Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
Ferramentas/GitHubGitHub/winterknife/silverpick
ExploraçãoShellcodeRed TeamingGeração de ShellcodeDesenvolvimento de Payloads
GitHubwinterknife/silverpick

SILVERPICK

Framework de Desenvolvimento de Shellcode em Modo de Usuário do Windows (WUMSDF)

Ver Repositório
158175há 1 mêsRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

SILVERPICK

VERSÃO

  • v1.1

RESUMO

O projeto SILVERPICK é um Windows User-Mode Shellcode Development Framework (WUMSDF) cujo único propósito é capacitar desenvolvedores de capacidades a construir blocos de Position Independent Code (PIC) para Windows x64 usando C/C++ de maneira fácil, de modo a reduzir os custos de desenvolvimento de tal empreendimento.

Ele deriva do projeto WILDBEAST e, como tal, utiliza:

  1. Visual Studio Code como editor de código
  • MinGW-w64 como toolchain de compilação
  • GNU Make como sistema de build
  • CONFIGURAÇÃO

    Você pode encontrar as instruções de configuração aqui: GCC-Clang-Setup-Windows

    Por favor, note que este projeto está usando MSYS2.

    FUNCIONALIDADES

    Escrever shellcode em linguagens de programação de alto nível não é novidade, e inúmeros posts de blog e artigos de pesquisa foram publicados sobre o assunto desde 2010. Então, o que há de novo no SILVERPICK?

    Bem, fico feliz que você tenha perguntado.

    SILVERPICK tem um belo repertório de truques escondidos na manga, mas, acima de tudo, esta é a minha visão sobre o assunto.

    Então, sem mais delongas, apresento a vocês meu primeiro truque.

    TRUQUE 01

    Desde que Matt Graeber popularizou escrever shellcode em C, a maioria das pessoas tem usado seu stub de alinhamento de pilha de 16 bytes escrito em linguagem Assembly.

    Embora isso não seja um problema, já que não somos a IKEA, assembly não deveria ser necessário, e de fato não é.

    Existe um GCC Atributo de Função que emitirá o stub de alinhamento de pilha para você.

    Conheça o atributo de função force_align_arg_pointer na forma de uma útil macro ALIGN_STACK, que gera o seguinte 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
    

    O que é a seção .init, você pergunta? Bem, isso serve como uma boa transição para o meu segundo truque.

    TRUQUE 02

    Matt Graeber pode ter popularizado escrever shellcode em C em algum momento, mas, na verdade, foi Paul Ungur quem reviveu essa arte negra com Stardust.

    Agora, Stardust usa um script de linker do Binutils para controlar o posicionamento de funções e dados na seção PE apropriada na ordem correta. Essa técnica em si é derivada do trabalho de Austin Hudson, e muitas pessoas usam uma variante de seus scripts de linker.

    Embora scripts de linker sejam ótimos para ordenar seções do linker, se tudo o que você precisa é colocar uma determinada função no início da seção de código, eles são desnecessários.

    Entre o atributo de função section com um nome de seção especial chamado .init, que indica ao linker que a função contém código de inicialização em tempo de execução pré-main() e deve estar primeiro na ordem de link.

    Para esse efeito, a macro CODE_BEGIN foi criada.

    TRUQUE 03

    Para o meu terceiro truque, apresento a vocês a macro STACK_STRING.

    Em C, você pode criar uma stack string (uma string construída dinamicamente na pilha) declarando o literal de string como um array de caracteres ANSI:

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

    Em C++, você pode criar uma stack string simplesmente marcando um array char como constexpr:

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

    No entanto, ambas as técnicas se tornam inúteis diante de otimizações do compilador se os literais de string forem suficientemente grandes, ao contrário da nossa solução, que funcionará independentemente do comprimento da string e do nível de otimizações do compilador, graças a um engenhoso hack de metaprogramação com templates C++ cortesia de Can Bölük.

    Usar essa macro é bastante simples:

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

    Isso gerará o seguinte 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'
    

    TRUQUE 04

    Falando em C++, apresento a vocês hashing de strings em tempo de compilação para o meu quarto truque.

    Embora este não seja um conceito novo, SILVERPICK oferece algumas melhorias em relação às implementações públicas existentes.

    Primeiramente, usamos a variante de 64 bits da popular função hash não criptográfica FNV-1a para reduzir a probabilidade de um ataque bem-sucedido de colisão de hash.

    Em segundo lugar, usamos um parâmetro modificado para a função hash para nos defendermos contra consultas de tabelas hash pré-calculadas, como o HashDB. Crucialmente, isso não altera as propriedades da função hash.

    Para fazer hash de uma string curta em tempo de execução, basta usar a macro HASH_STRING_RUN_TIME.

    Para fazer hash de um literal de string curta em tempo de compilação, basta usar a macro HASH_STRING_COMPILE_TIME. A avaliação somente em tempo de compilação é garantida via consteval.

    TRUQUE 05

    Acontece que você pode implementar um bom punhado de funções da C Runtime Library (CRT) usando instruções de string x86. Então, é claro, eu tive que implementá-las usando uma mistura de intrínsecos do compilador e assembly inline.

    Você quer usar a função msvcrt!memset no seu código? Use a macro ZERO_MEMORY em vez disso, que usa a instrução rep stosb emitida por meio de um intrínseco do compilador.

    E quanto à função msvcrt!memcpy ou à função msvcrt!memmove, você pergunta? Conheça a macro COPY_MEMORY como substituta, que usa a instrução rep movsb emitida por meio de um intrínseco do compilador.

    Mas e uma alternativa para a função msvcrt!memcmp? Acontece que não existe exatamente um intrínseco do compilador disponível para emitir a instrução repe cmpsb. Então, escrevemos uma função compare_memory usando assembly inline.

    Finalmente, se você procura um substituto para a função msvcrt!memchr, conheça a função scan_memory, que mais uma vez usa assembly inline, já que não há intrínseco do compilador disponível para emitir a instrução repne scasb.

    Ah, e eu esqueci de mencionar que você pode escrever sua própria versão mais segura da função msvcrt!strlen usando a rotina scan_memory da seguinte forma:

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

    Por favor, observe que essas implementações podem não produzir o código com melhor desempenho, dependendo da microarquitetura do CPU alvo. No entanto, elas têm a garantia de fazer o trabalho.

    TRUQUE 06

    Interessado em mais truques de salão?

    Há muitas outras macros pequenas em Common.h que existem para abstrair algumas das complexidades de manipular o compilador.

    Uma implementação sem dependências da função GetModuleHandle é fornecida em UserModuleBase.cpp. Para simplificar a facilidade de uso, uma macro útil denominada GET_USER_MODULE_BASE foi criada.

    Da mesma forma, uma implementação sem dependências da função GetProcAddress é fornecida em PEParse.cpp, que é então envolvida em uma macro prática apropriadamente chamada de GET_EXPORTED_SYMBOL_ADDRESS. Além disso, duas outras macros foram fornecidas para auxiliar na vinculação dinâmica em tempo de execução - INITIALIZE_FUNCTION_POINTER para declarar e inicializar um ponteiro de função em 0, e RESOLVE_FUNCTION_POINTER para resolver o referido ponteiro de função.

    A integração com Visual Studio Code está embutida no projeto para que os desenvolvedores possam usar o atalho de teclado Ctrl+Shift+B para um processo de build sem complicações.

    A integração com GitHub Actions também está embutida no projeto para permitir builds de CI.

    O projeto tem certo orgulho em sua estrutura bem organizada, bem como no código totalmente comentado e relativamente limpo.

    Por fim, dê uma olhada no Makefile do projeto, que contém a melhor seleção de flags do compilador e do linker que irão gerar código pequeno, seguro e amigável ao OPSEC. Enquanto isso, o log detalhado e um arquivo de mapa do linker gerado fornecerão visibilidade sobre o processo de build para facilitar uma compreensão mais profunda da toolchain. Além disso, cada unidade de tradução também produz um arquivo de desmontagem que, após inspeção, muitas vezes fará você dizer coisas como "o que o compilador fez agora?", etc.

    USO

    Se você está convencido sobre o framework, esta seção descreve como você pode utilizá-lo.

    O seguinte é um trecho retirado 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;
    }
    

    Parece fácil o suficiente, não?

    Ao escrever PIC em C/C++ usando o framework SILVERPICK, você precisa levar em consideração as seguintes regras:

    1. Trate a função payload como você trataria a função main de um programa tradicional, ou seja, como o (pseudo) ponto de entrada.
    2. Todos os literais de string devem ser declarados como stack strings.
    3. Variáveis globais não podem ser usadas em nenhum lugar do código.
    4. As funções Windows API ou Native API só podem ser usadas por meio de vinculação dinâmica em tempo de execução após garantir que o protótipo da função esteja disponível no arquivo de cabeçalho correspondente.

    MELHORIAS FUTURAS

    Esta seção contém uma lista não exaustiva de melhorias planejadas que serão integradas em um projeto futuro.

    • Mudar para a toolchain Clang/LLVM.
    • Mudar para um sistema de build diferente.
    • Ofuscação de strings em tempo de compilação que funcione com stack strings e seja resistente a FLOSS.
    • Hashing de strings em tempo de compilação usando uma função hash personalizada não criptográfica com semente.
    • Método alternativo para obter o endereço base do segmento GS.
    • Capacidade de contornar a mitigação de exploração Export Address Filtering (EAF).

    REFERÊNCIAS

    A seguir está uma lista de referências organizadas em ordem cronológica que se mostraram inestimáveis para mim durante minha pesquisa e foram amplamente usadas como inspiração para este projeto:

    1. Escrevendo Shellcode com um Compilador C por Nick Harbour (2010)

    2. Shellcode com um Compilador C por Didier Stevens (2010)

    3. Escrevendo Shellcode Otimizado para Windows em C por Matt Graeber (2013)

    4. Shellcode do melhor jeito, ou como simplesmente usar seu compilador por Justin Fisher (2016)

    5. ShellcodeStdio por Jack Ullrich (2016)

    6. Shellcode: Um PIC para Windows usando troca de chaves RSA-2048, AES-256, SHA-3 por Odzhan (2016)

    7. Escrevendo Shellcode Otimizado para Windows por Dimitri Fourny (2017)

    8. Escrevendo e Compilando Shellcode em C por Aleksandra Doniec e Mantvydas Baranauskas (2021)

    9. Criando Shellcode a partir de qualquer Código Usando Visual Studio e C++ por Hamid Memar (2021)

    10. Escrevendo Shellcode Otimizado para Windows em C por Philip Woldhek (2021)

    11. De C, com assembly inline, a shellcode por Steve Salinas (2023)

    12. Como Criar Seu Próprio Shellcode Windows x86/64 com Visual Studio por Yazid Benjamaa (2023)

    13. Design moderno de implantes: desenvolvimento de malware independente de posição por Paul Ungur (2024)

    14. Do C ao shellcode (maneira simples) por Print3M (2024)

    15. relocatable por Tijme Gommers (2025)

    16. Curso Intensivo de Desenvolvimento de PIC por Raphael Mudge (2025)

    17. scfw por Petr Beneš (2026)

    ERRATA

    VirusTotal

    O que me pareceu estranho foi a classificação VirTool:Win64/Silepesz.A pelo Microsoft Defender.

    Estes são os bytes que a assinatura cobre:

    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
    

    Isso se traduz de volta para o seguinte código-fonte:

    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
    

    Escusado será dizer que esta é uma detecção extremamente frágil e só detectará o código de exemplo exato mostrado no exemplo. No entanto, ela destaca a importância de usar hashes polimórficos de API.

    Baixar ferramenta