Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
SILVERPICK — Framework de développement de shellcode en mode utilisateur Windows (WUMSDF) | Kitploit
Outils/GitHubGitHub/winterknife/silverpick
ExploitationShellcodeRed TeamingGénération de ShellcodeDéveloppement de Charges Utiles
GitHubwinterknife/silverpick

SILVERPICK

Framework de développement de shellcode en mode utilisateur Windows (WUMSDF)

Voir le dépôt
158175il y a 1 moisVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

SILVERPICK

VERSION

  • v1.1

DESCRIPTION

Le projet SILVERPICK est un Windows User-Mode Shellcode Development Framework (WUMSDF) dont l'unique objectif est de permettre aux développeurs de capacités de créer des blobs de Position Independent Code (PIC) pour Windows x64 en C/C++ de manière simple, afin de réduire les coûts de développement d'une telle entreprise.

Il dérive du projet WILDBEAST et, à ce titre, s'appuie sur :

  1. Visual Studio Code comme éditeur de code
  • La chaîne d'outils MinGW-w64 comme chaîne de compilation
  • GNU Make comme système de construction
  • INSTALLATION

    Vous trouverez les instructions d'installation ici : GCC-Clang-Setup-Windows

    Veuillez noter que ce projet utilise MSYS2.

    FONCTIONNALITÉS

    Écrire du shellcode dans des langages de programmation de haut niveau n'a rien de nouveau, et d'innombrables articles de blog et documents de recherche ont été publiés sur le sujet depuis 2010. Alors, qu'y a-t-il de nouveau dans SILVERPICK ?

    Eh bien, je suis ravi que vous posiez la question.

    SILVERPICK cache un joli petit sac d'astuces dans sa manche, mais surtout, c'est ma vision du sujet.

    Sans plus attendre, je vous présente ma première astuce.

    ASTUCE 01

    Depuis que Matt Graeber a popularisé l'écriture de shellcode en C, la plupart des gens utilisent son stub d'alignement de pile de 16 octets écrit en langage Assembly.

    Bien que cela ne soit pas un problème, étant donné que nous ne sommes pas IKEA, l'assembleur ne devrait pas être nécessaire, et il ne l'est effectivement pas.

    Il existe un attribut de fonction GCC qui émet le stub d'alignement de pile pour vous.

    Voici l'attribut de fonction force_align_arg_pointer sous la forme d'une macro pratique ALIGN_STACK, qui génère l'assembleur suivant :

    root@kitploit:~
    Disassembly of section .init:
    
    <PicEntry>:
    	push   rbp
    	mov    rbp,rsp
    	and    rsp,0xfffffffffffffff0
    	sub    rsp,0x20
    	call   <PicEntry+0x11>	IMAGE_REL_AMD64_REL32	.text$payload
    	leave
    	ret
    

    Qu'est-ce que la section .init, demanderez-vous ? Eh bien, cela constitue une belle transition vers ma deuxième astuce.

    ASTUCE 02

    Matt Graeber a peut-être popularisé l'écriture de shellcode en C à un moment donné, mais c'est en réalité Paul Ungur qui a ravivé cet art noir avec Stardust.

    Stardust utilise un script de liaison Binutils pour contrôler le placement des fonctions et des données dans la section PE appropriée, dans le bon ordre. Cette technique est elle-même dérivée des travaux d'Austin Hudson, et de nombreuses personnes utilisent une variante de ses scripts de liaison.

    Bien que les scripts de liaison soient parfaits pour ordonner les sections du lieur, ils sont inutiles si tout ce dont vous avez besoin est de placer une fonction donnée au début de la section de code.

    C'est ici qu'intervient l'attribut de fonction section avec un nom de section spécial appelé .init, qui indique au lieur que la fonction contient du code d'initialisation d'exécution pré-main() et qu'elle doit être première dans l'ordre de liaison.

    À cette fin, la macro CODE_BEGIN a été créée.

    ASTUCE 03

    Pour ma troisième astuce, je vous présente la macro STACK_STRING.

    En C, vous pouvez créer une chaîne de pile (une chaîne construite dynamiquement sur la pile) en déclarant la chaîne littérale comme un tableau de caractères ANSI :

    root@kitploit:~
    char charrHelloKitty[] = { 'H', 'e', 'l', 'l', 'o', 'K', 'i', 't', 't', 'y', '\0' };
    

    En C++, vous pouvez créer une chaîne de pile en marquant simplement un tableau char comme constexpr :

    root@kitploit:~
    constexpr char charrHelloKitty[]{ "HelloKitty" };
    

    Cependant, ces deux techniques deviennent inutiles face aux optimisations du compilateur si les chaînes littérales sont suffisamment grandes, contrairement à notre solution, qui fonctionnera quelle que soit la longueur de la chaîne et le niveau d'optimisation du compilateur, grâce à une astuce intelligente de métaprogrammation de templates C++ offerte par Can Bölük.

    L'utilisation de cette macro est assez simple :

    root@kitploit:~
    STACK_STRING(sstrText, "an extra long hello world!");
    STACK_STRING(sstrCaption, "Demo");
    
    MessageBoxA(nullptr, sstrText.data(), sstrCaption.data(), MB_OK);
    

    Cela générera l'assembleur suivant :

    root@kitploit:~
    mov     [rsp+58h+var_23], 61h ; 'a'
    mov     [rsp+58h+var_22], 6Eh ; 'n'
    mov     [rsp+58h+var_21], 20h ; ' '
    mov     [rsp+58h+var_20], 65h ; 'e'
    mov     [rsp+58h+var_1F], 78h ; 'x'
    mov     [rsp+58h+var_1E], 74h ; 't'
    mov     [rsp+58h+var_1D], 72h ; 'r'
    mov     [rsp+58h+var_1C], 61h ; 'a'
    mov     [rsp+58h+var_1B], 20h ; ' '
    mov     [rsp+58h+var_1A], 6Ch ; 'l'
    mov     [rsp+58h+var_19], 6Fh ; 'o'
    mov     [rsp+58h+var_18], 6Eh ; 'n'
    mov     [rsp+58h+var_17], 67h ; 'g'
    mov     [rsp+58h+var_16], 20h ; ' '
    mov     [rsp+58h+var_15], 68h ; 'h'
    mov     [rsp+58h+var_14], 65h ; 'e'
    mov     [rsp+58h+var_13], 6Ch ; 'l'
    mov     [rsp+58h+var_12], 6Ch ; 'l'
    mov     [rsp+58h+var_11], 6Fh ; 'o'
    mov     [rsp+58h+var_10], 20h ; ' '
    mov     [rsp+58h+var_2F], 0
    mov     [rsp+58h+var_F], 77h ; 'w'
    mov     [rsp+58h+var_E], 6Fh ; 'o'
    mov     [rsp+58h+var_D], 72h ; 'r'
    mov     [rsp+58h+var_C], 6Ch ; 'l'
    mov     [rsp+58h+var_B], 64h ; 'd'
    mov     [rsp+58h+var_A], 21h ; '!'
    mov     [rsp+58h+var_33], 44h ; 'D'
    mov     [rsp+58h+var_32], 65h ; 'e'
    mov     [rsp+58h+var_31], 6Dh ; 'm'
    mov     [rsp+58h+var_30], 6Fh ; 'o'
    

    ASTUCE 04

    En parlant de C++, je vous présente le hachage de chaînes à la compilation pour ma quatrième astuce.

    Bien que ce ne soit pas un concept nouveau, SILVERPICK offre quelques améliorations par rapport aux implémentations publiques existantes.

    Premièrement, nous utilisons la variante 64 bits de la célèbre fonction de hachage non cryptographique FNV-1a afin de réduire la probabilité d'une attaque par collision de hachage réussie.

    Deuxièmement, nous utilisons un paramètre modifié pour la fonction de hachage afin de nous défendre contre les recherches de tables de hachage précalculées, comme HashDB. Point crucial, cela ne change pas les propriétés de la fonction de hachage.

    Pour hacher une chaîne courte au moment de l'exécution, utilisez simplement la macro HASH_STRING_RUN_TIME.

    Pour hacher une chaîne littérale courte à la compilation, utilisez simplement la macro HASH_STRING_COMPILE_TIME. L'évaluation uniquement à la compilation est garantie via consteval.

    ASTUCE 05

    Il s'avère que l'on peut implémenter pas mal de fonctions de la C Runtime Library (CRT) à l'aide des instructions de chaîne x86. Alors, bien sûr, j'ai dû les implémenter en utilisant un mélange d'intrinsèques du compilateur et d'assembleur inline.

    Vous voulez utiliser la fonction msvcrt!memset dans votre code ? Utilisez plutôt la macro ZERO_MEMORY, qui utilise l'instruction rep stosb émise via un intrinsèque du compilateur.

    Et pour la fonction msvcrt!memcpy ou msvcrt!memmove, demanderez-vous ? Voici la macro COPY_MEMORY comme remplaçant, qui utilise l'instruction rep movsb émise via un intrinsèque du compilateur.

    Mais qu'en est-il d'une alternative à la fonction msvcrt!memcmp ? Il s'avère qu'il n'existe pas vraiment d'intrinsèque du compilateur permettant d'émettre l'instruction repe cmpsb. Nous écrivons donc une fonction compare_memory utilisant de l'assembleur inline à la place.

    Enfin, si vous cherchez un remplaçant pour la fonction msvcrt!memchr, voici la fonction scan_memory, qui utilise là encore de l'assembleur inline, car aucun intrinsèque du compilateur n'est disponible pour émettre l'instruction repne scasb.

    Oh, et ai-je oublié de mentionner que vous pouvez écrire votre propre version plus sûre de la fonction msvcrt!strlen en utilisant la routine scan_memory comme ceci :

    root@kitploit:~
    DWORD_PTR dwptrExportNameLength = std::min(BIT_CAST(DWORD_PTR, scan_memory(strExportName, 0x00, MAX_EXPORTED_SYMBOL_NAME_LEN)) - BIT_CAST(DWORD_PTR, strExportName), MAX_EXPORTED_SYMBOL_NAME_LEN);
    

    Veuillez noter que ces implémentations ne produisent pas forcément le code le plus performant, selon la microarchitecture du CPU cible. Cependant, elles sont garanties pour faire le travail.

    ASTUCE 06

    Intéressé par d'autres tours de passe-passe ?

    Il existe beaucoup d'autres petites macros dans Common.h qui servent à abstraire certaines complexités liées au contrôle du compilateur.

    Une implémentation sans dépendance de la fonction GetModuleHandle est fournie dans UserModuleBase.cpp. Pour simplifier l'utilisation, une macro pratique nommée GET_USER_MODULE_BASE a été créée.

    De même, une implémentation sans dépendance de la fonction GetProcAddress est fournie dans PEParse.cpp, puis encapsulée dans une macro pratique judicieusement nommée GET_EXPORTED_SYMBOL_ADDRESS. De plus, deux autres macros sont fournies pour faciliter la liaison dynamique à l'exécution : INITIALIZE_FUNCTION_POINTER pour déclarer et initialiser un pointeur de fonction à 0, et RESOLVE_FUNCTION_POINTER pour résoudre ce pointeur de fonction.

    L'intégration de Visual Studio Code est intégrée au projet afin que les développeurs puissent utiliser le raccourci clavier Ctrl+Shift+B pour un processus de compilation sans tracas.

    L'intégration de GitHub Actions est également intégrée au projet pour permettre les builds CI.

    Le projet tire une certaine fierté de sa structure bien organisée, ainsi que de son code abondamment commenté et relativement propre.

    Enfin, jetez un œil au Makefile du projet, qui contient la sélection la plus fine d'options de compilation et d'édition de liens générant un code petit, sécurisé et respectueux de l'OPSEC. Parallèlement, une journalisation verbeuse et un fichier de carte de liaison généré offriront une visibilité sur le processus de compilation pour faciliter une compréhension plus approfondie de la chaîne d'outils. De plus, chaque unité de traduction produit également un fichier de désassemblage qui, à l'inspection, vous fera souvent dire des choses comme « le compilateur a fait quoi, là ? », etc.

    UTILISATION

    Si vous êtes convaincu par ce framework, cette section décrit comment l'utiliser.

    L'extrait suivant provient de PicMain.cpp :

    root@kitploit:~
    /// @brief PIC start function
    /// @param None
    /// @return None
    EXTERN_C NO_INLINE VOID __stdcall payload(
        VOID
    ) {
        // Init local variables
        PVOID pKernel32 = nullptr;
        INITIALIZE_FUNCTION_POINTER(LoadLibraryA);
        HMODULE hUser32 = nullptr;
        STACK_STRING(sstrUser32, "user32.dll");
        INITIALIZE_FUNCTION_POINTER(MessageBoxA);
        STACK_STRING(sstrText, "an extra long hello world!");
        STACK_STRING(sstrCaption, "Demo");
    
        // Get the image base address of kernel32.dll
        pKernel32 = GET_USER_MODULE_BASE("kernel32.dll");
        if (pKernel32 == nullptr)
            goto cleanup;
    
        // Resolve kernel32!LoadLibraryA
        RESOLVE_FUNCTION_POINTER(pKernel32, LoadLibraryA);
        if (LoadLibraryA == nullptr)
            goto cleanup;
    
        // Load User32.dll into the process VAS
        hUser32 = LoadLibraryA(sstrUser32.data());
        if (hUser32 == nullptr)
            goto cleanup;
    
        // Resolve user32!MessageBoxA
        RESOLVE_FUNCTION_POINTER(hUser32, MessageBoxA);
        if (MessageBoxA == nullptr)
            goto cleanup;
    
        // Display a message box
        MessageBoxA(nullptr, sstrText.data(), sstrCaption.data(), MB_OK);
    
        // Cleanup
    cleanup:
        return;
    }
    

    Ça a l'air assez facile, non ?

    Lorsque vous écrivez du PIC en C/C++ avec le framework SILVERPICK, vous devez prendre en considération les règles suivantes :

    1. Traitez la fonction payload comme vous le feriez avec la fonction main d'un programme traditionnel, c'est-à-dire comme le (pseudo) point d'entrée.
    2. Toutes les chaînes littérales doivent être déclarées comme des chaînes de pile.
    3. Les variables globales ne peuvent être utilisées nulle part dans le code.
    4. Les fonctions Windows API ou Native API ne peuvent être utilisées que via la liaison dynamique à l'exécution, après avoir vérifié que le prototype de la fonction est disponible dans le fichier d'en-tête correspondant.

    AMÉLIORATIONS FUTURES

    Cette section contient une liste non exhaustive d'améliorations planifiées qui seront intégrées dans un futur projet.

    • Passer à la chaîne d'outils Clang/LLVM.
    • Passer à un autre système de construction.
    • Obfuscation de chaînes à la compilation compatible avec les chaînes de pile et résistante à FLOSS.
    • Hachage de chaînes à la compilation utilisant une fonction de hachage personnalisée non cryptographique avec graine.
    • Méthode alternative pour récupérer l'adresse de base du segment GS.
    • Capacité à contourner l'atténuation des exploits Export Address Filtering (EAF).

    RÉFÉRENCES

    Voici une liste de références classées par ordre chronologique qui m'ont été d'une valeur inestimable au cours de mes recherches et m'ont largement servi d'inspiration pour ce projet :

    1. Writing Shellcode with a C Compiler par Nick Harbour (2010)

    2. Shellcode with a C-compiler par Didier Stevens (2010)

    3. Writing Optimized Windows Shellcode in C par Matt Graeber (2013)

    4. Shellcode the better way, or how to just use your compiler par Justin Fisher (2016)

    5. ShellcodeStdio par Jack Ullrich (2016)

    6. Shellcode: A Windows PIC using RSA-2048 key exchange, AES-256, SHA-3 par Odzhan (2016)

    7. Writing Optimized Windows Shellcode par Dimitri Fourny (2017)

    8. Writing and Compiling Shellcode in C par Aleksandra Doniec et Mantvydas Baranauskas (2021)

    9. Creating Shellcode from any Code Using Visual Studio and C++ par Hamid Memar (2021)

    10. Writing Optimized Windows Shellcode in C par Philip Woldhek (2021)

    11. From C, with inline assembly, to shellcode par Steve Salinas (2023)

    12. How To Craft Your Own Windows x86/64 Shellcode with Visual Studio par Yazid Benjamaa (2023)

    13. Modern implant design: position independent malware development par Paul Ungur (2024)

    14. From C to shellcode (simple way) par Print3M (2024)

    15. relocatable par Tijme Gommers (2025)

    16. PIC Development Crash Course par Raphael Mudge (2025)

    17. scfw par Petr Beneš (2026)

    ERRATA

    VirusTotal

    Ce qui m'a paru étrange, c'est la classification VirTool:Win64/Silepesz.A par Microsoft Defender.

    Voici les octets couverts par la signature :

    root@kitploit:~
    [+] Target file size: 2560 bytes
    [+] Analyzing...
    [!] Identified end of bad bytes at offset 0x4CB
    000003CB   44 24 2F 32 C6 44 24 30  2E C6 44 24 31 64 C6 44   D$/2�D$0.�D$1d�D
    000003DB   24 32 6C C6 44 24 33 6C  C6 44 24 35 61 C6 44 24   $2l�D$3l�D$5a�D$
    000003EB   36 6E C6 44 24 37 20 C6  44 24 38 65 C6 44 24 39   6n�D$7 �D$8e�D$9
    000003FB   78 C6 44 24 3A 74 C6 44  24 3B 72 C6 44 24 3C 61   x�D$:t�D$;r�D$<a
    0000040B   C6 44 24 3D 20 C6 44 24  3E 6C C6 44 24 3F 6F C6   �D$= �D$>l�D$?o�
    0000041B   44 24 40 6E C6 44 24 41  67 C6 44 24 42 20 C6 44   D$@n�D$Ag�D$B �D
    0000042B   24 43 68 C6 44 24 44 65  C6 44 24 45 6C C6 44 24   $Ch�D$De�D$El�D$
    0000043B   46 6C C6 44 24 47 6F C6  44 24 48 20 C6 44 24 29   Fl�D$Go�D$H �D$)
    0000044B   00 C6 44 24 49 77 C6 44  24 4A 6F C6 44 24 4B 72   .�D$Iw�D$Jo�D$Kr
    0000045B   C6 44 24 4C 6C C6 44 24  4D 64 C6 44 24 4E 21 C6   �D$Ll�D$Md�D$N!�
    0000046B   44 24 25 44 C6 44 24 26  65 C6 44 24 27 6D C6 44   D$%D�D$&e�D$'m�D
    0000047B   24 28 6F E8 55 00 00 00  48 85 C0 74 4B 48 BA 58   $(o�U...H.AtKH�X
    0000048B   D0 CC C6 F8 E7 BF 0A 48  89 C1 E8 86 FD FF FF 48   DI�o��.H.A�.y��H
    0000049B   85 C0 74 34 48 8D 4C 24  2A FF D0 48 85 C0 74 28   .At4H.L$*�DH.At(
    000004AB   48 BA D9 92 FB 55 9A AC  70 E0 48 89 C1 E8 63 FD   H�U.�U.�p�H.A�cy
    000004BB   FF FF 48 85 C0 74 11 48  8D 54 24 35 45 31 C9 4C   ��H.At.H.T$5E1�L
    

    Ceci correspond au code source suivant :

    root@kitploit:~
    pKernel32 = GET_USER_MODULE_BASE("kernel32.dll");
    if (pKernel32 == nullptr)
        goto cleanup;
    
    RESOLVE_FUNCTION_POINTER(pKernel32, LoadLibraryA); // mov rdx, 0x0ABFE7F8C6CCD058 (FNV-1a hash of "LoadLibraryA" with modified offset basis)
    if (LoadLibraryA == nullptr)
        goto cleanup;
    
    hUser32 = LoadLibraryA(sstrUser32.data());
    if (hUser32 == nullptr)
        goto cleanup;
    
    RESOLVE_FUNCTION_POINTER(hUser32, MessageBoxA); // mov rdx, 0xE070AC9A55FB92D9 (FNV-1a hash of "MessageBoxA" with modified offset basis)
    if (MessageBoxA == nullptr)
        goto cleanup;
    
    MessageBoxA(nullptr, sstrText.data(), sstrCaption.data(), MB_OK); // arg setup only
    

    Inutile de dire qu'il s'agit d'une détection extrêmement fragile qui ne détectera que l'exemple de code exact présenté ci-dessus. Elle souligne néanmoins l'importance d'utiliser des hachages API polymorphes.

    Télécharger l’outil