Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
SILVERPICK — Framework di sviluppo di shellcode in modalità utente per Windows (WUMSDF) | Kitploit
Strumenti/GitHubGitHub/winterknife/silverpick
ExploitShellcodeRed TeamingGenerazione di ShellcodeSviluppo Payload
GitHubwinterknife/silverpick

SILVERPICK

Framework di sviluppo di shellcode in modalità utente per Windows (WUMSDF)

Vedi Repository
158171 mese faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

SILVERPICK

VERSIONE

  • v1.1

PANORAMICA

Il progetto SILVERPICK è un Windows User-Mode Shellcode Development Framework (WUMSDF) il cui unico scopo è consentire agli sviluppatori di capability di creare blob di Position Independent Code (PIC) per Windows x64 usando C/C++ in modo semplice, così da ridurre i costi di sviluppo di un'impresa del genere.

Deriva dal progetto WILDBEAST e, in quanto tale, si avvale di:

  1. Visual Studio Code come editor di codice
  2. toolchain MinGW-w64 come toolchain di compilazione
  3. GNU Make come sistema di build

CONFIGURAZIONE

Puoi trovare le istruzioni di configurazione qui: GCC-Clang-Setup-Windows

Nota: questo progetto utilizza MSYS2.

CARATTERISTICHE

Scrivere shellcode in linguaggi di programmazione ad alto livello non è una novità, e innumerevoli post di blog e articoli di ricerca sono stati pubblicati sull'argomento dal 2010. Quindi, cosa c'è di nuovo in SILVERPICK?

Beh, sono lieto che tu lo chieda.

SILVERPICK ha un bel po' di trucchi nascosti nella manica, ma soprattutto, questa è la mia interpretazione dell'argomento.

Quindi, senza ulteriori indugi, ti presento il mio primo trucco.

TRUCCO 01

Da quando Matt Graeber ha reso popolare la scrittura di shellcode in C, la maggior parte delle persone ha usato il suo stub di allineamento dello stack di 16 byte scritto in linguaggio Assembly.

Anche se questo non è un problema, visto che non siamo IKEA, l'assembly non dovrebbe essere necessario, e in effetti non lo è.

Esiste un GCC Function Attribute che emette per te lo stub di allineamento dello stack.

Ecco l'attributo di funzione force_align_arg_pointer sotto forma di un'utile macro ALIGN_STACK, che genera il seguente assembly:

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

«Che cos'è la sezione .init?», chiederai. Beh, è una perfetta transizione verso il mio secondo trucco.

TRUCCO 02

Matt Graeber potrebbe aver reso popolare la scrittura di shellcode in C a un certo punto, ma in realtà è stato Paul Ungur a far rivivere quest'arte nera con Stardust.

Ora, Stardust usa uno script di linker Binutils per controllare il posizionamento di funzioni e dati nella sezione PE appropriata nell'ordine corretto. Questa tecnica deriva a sua volta dal lavoro di Austin Hudson, e molte persone usano una variante dei suoi script di linker.

Sebbene gli script di linker siano ottimi per l'ordinamento delle sezioni del linker, se tutto ciò che serve è porre una determinata funzione all'inizio della sezione del codice, non sono necessari.

Entra in gioco l'attributo di funzione section con un nome di sezione speciale chiamato .init, che indica al linker che la funzione contiene codice di inizializzazione runtime pre-main() e deve essere per prima nell'ordine di collegamento.

A questo scopo, è stata creata la macro CODE_BEGIN.

TRUCCO 03

Per il mio terzo trucco, ti presento la macro STACK_STRING.

In C, puoi creare una stringa di stack (una stringa costruita dinamicamente sullo stack) dichiarando la stringa letterale come un array di caratteri ANSI:

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

In C++, puoi creare una stringa di stack semplicemente marcando un array char come constexpr:

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

Tuttavia, entrambe queste tecniche vengono rese inutili di fronte alle ottimizzazioni del compilatore se le stringhe letterali sono sufficientemente grandi, a differenza della nostra soluzione, che funzionerà indipendentemente dalla lunghezza della stringa e dal livello di ottimizzazioni del compilatore, grazie a un abile hack di metaprogrammazione con template C++ per gentile concessione di Can Bölük.

Usare questa macro è piuttosto semplice:

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

MessageBoxA(nullptr, sstrText.data(), sstrCaption.data(), MB_OK);

Questo genererà il seguente assembly:

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'

TRUCCO 04

Parlando di C++, per il mio quarto trucco ti presento l'hashing delle stringhe a tempo di compilazione.

Anche se non è un concetto nuovo, SILVERPICK offre alcuni miglioramenti rispetto alle implementazioni pubbliche esistenti.

In primo luogo, usiamo la variante a 64 bit della popolare funzione hash non crittografica FNV-1a per ridurre la probabilità di un attacco di collisione hash riuscito.

In secondo luogo, usiamo un parametro modificato per la funzione hash per difenderci dalle ricerche in tabelle hash precalcolate, come HashDB. Fondamentalmente, questo non cambia le proprietà della funzione hash.

Per calcolare l'hash di una stringa corta a runtime, basta usare la macro HASH_STRING_RUN_TIME.

Per calcolare l'hash di una stringa letterale corta a tempo di compilazione, basta usare la macro HASH_STRING_COMPILE_TIME. La valutazione esclusivamente a tempo di compilazione è garantita tramite consteval.

TRUCCO 05

Si scopre che puoi implementare un bel po' di funzioni della C Runtime Library (CRT) usando le istruzioni stringa x86. Quindi, ovviamente, ho dovuto implementarle usando un mix di intrinsic del compilatore e assembly inline.

Vuoi usare la funzione msvcrt!memset nel tuo codice? Usa invece la macro ZERO_MEMORY, che usa l'istruzione rep stosb emessa tramite un intrinsic del compilatore.

E per quanto riguarda la funzione msvcrt!memcpy o la funzione msvcrt!memmove, chiederai? Ecco la macro COPY_MEMORY come sostituto, che usa l'istruzione rep movsb emessa tramite un intrinsic del compilatore.

Ma che dire di un'alternativa alla funzione msvcrt!memcmp? Si dà il caso che non esista esattamente un intrinsic del compilatore disponibile per emettere l'istruzione repe cmpsb. Quindi, scriviamo invece una funzione compare_memory usando assembly inline.

Infine, se cerchi un sostituto per la funzione msvcrt!memchr, ecco la funzione scan_memory, che ancora una volta usa assembly inline poiché non è disponibile alcun intrinsic del compilatore per emettere l'istruzione repne scasb.

Oh, e ho forse dimenticato di menzionare che potresti scrivere la tua versione più sicura della funzione msvcrt!strlen usando la routine scan_memory in questo modo:

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);

Tieni presente che queste implementazioni potrebbero non produrre il codice più performante, a seconda della microarchitettura CPU di destinazione. Tuttavia, è garantito che facciano il lavoro.

TRUCCO 06

Interessato ad altri giochi di prestigio?

Ci sono molte altre piccole macro in Common.h che servono ad astrarre alcune delle complessità legate alla manipolazione del compilatore.

Un'implementazione senza dipendenze della funzione GetModuleHandle è fornita in UserModuleBase.cpp. Per semplificarne l'uso, è stata creata un'utile macro chiamata GET_USER_MODULE_BASE.

Analogamente, un'implementazione senza dipendenze della funzione GetProcAddress è fornita in PEParse.cpp, che viene poi racchiusa in una comoda macro opportunamente chiamata GET_EXPORTED_SYMBOL_ADDRESS. Inoltre, sono state fornite altre due macro per agevolare il collegamento dinamico a runtime: INITIALIZE_FUNCTION_POINTER per dichiarare e inizializzare un puntatore a funzione a 0, e RESOLVE_FUNCTION_POINTER per risolvere tale puntatore a funzione.

L'integrazione con Visual Studio Code è inclusa nel progetto, così gli sviluppatori possono usare la scorciatoia da tastiera Ctrl+Shift+B per un processo di build senza complicazioni.

Anche l'integrazione con GitHub Actions è inclusa nel progetto per abilitare le build CI.

Il progetto va piuttosto fiero della sua struttura ben organizzata, oltre che del codice accuratamente commentato e relativamente pulito.

Infine, dai un'occhiata al Makefile del progetto, che contiene la migliore selezione di flag del compilatore e del linker per generare codice piccolo, sicuro e OPSEC-friendly. Nel frattempo, la registrazione dettagliata e un file di mappa del linker generato forniranno visibilità sul processo di build per facilitare una comprensione più profonda della toolchain. Inoltre, ogni unità di traduzione produce anche un file di disassembly che, a un'ispezione, spesso ti farà dire cose come "il compilatore ha fatto cosa?", ecc.

UTILIZZO

Se il framework ti ha convinto, questa sezione descrive come usarlo.

Quello che segue è un estratto tratto da 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;
}

Sembra abbastanza facile, no?

Quando scrivi PIC in C/C++ usando il framework SILVERPICK, devi tenere in considerazione le seguenti regole:

  1. Tratta la funzione payload come faresti con la funzione main di un programma tradizionale, cioè come il (pseudo) punto di ingresso.
  2. Tutte le stringhe letterali devono essere dichiarate come stringhe di stack.
  3. Le variabili globali non possono essere usate in nessun punto del codice.
  4. Le funzioni Windows API o Native API possono essere usate solo tramite collegamento dinamico a runtime dopo essersi assicurati che il prototipo della funzione sia disponibile nel corrispondente file di intestazione.

MIGLIORAMENTI FUTURI

La sezione contiene un elenco non esaustivo di miglioramenti pianificati che saranno integrati in un progetto futuro.

  • Passare alla toolchain Clang/LLVM.
  • Passare a un sistema di build diverso.
  • Offuscamento delle stringhe a tempo di compilazione che funziona con le stringhe di stack ed è resistente a FLOSS.
  • Hashing delle stringhe a tempo di compilazione tramite una funzione hash non crittografica personalizzata con seed.
  • Metodo alternativo per recuperare l'indirizzo base del segmento GS.
  • Capacità di bypassare la mitigazione degli exploit Export Address Filtering (EAF).

RIFERIMENTI

Quello che segue è un elenco di riferimenti in ordine cronologico che si sono rivelati preziosissimi durante la mia ricerca e sono stati ampiamente usati come ispirazione per questo progetto:

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

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

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

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

  5. ShellcodeStdio di Jack Ullrich (2016)

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

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

  8. Writing and Compiling Shellcode in C di Aleksandra Doniec e Mantvydas Baranauskas (2021)

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

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

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

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

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

ERRATA

VirusTotal

Quello che mi ha colpito come strano è stata la classificazione VirTool:Win64/Silepesz.A da parte di Microsoft Defender.

Questi sono i byte coperti dalla firma:

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

Questo si traduce nel seguente codice sorgente:

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 dirlo, questa è una rilevazione estremamente fragile e rileverà solo l'esatto codice di esempio mostrato nell'esempio. Tuttavia, evidenzia l'importanza di usare hash di API polimorfici.

Scarica lo strumento
  • From C to shellcode (simple way) di Print3M (2024)

  • relocatable di Tijme Gommers (2025)

  • PIC Development Crash Course di Raphael Mudge (2025)

  • scfw di Petr Beneš (2026)