Programação

Um cheatsheet de como preparar bons shellcodes x86/64 para Windows

Tempo de leitura: 3 minutos

Esse é um guia prático sobre como montar pequenos snippets PIC (position independent code) e que sejam cross-platform: x86/x64 de autoria da pesquisadora e desenvolvedora de exploits hh86.

Snippets para conseguir o base address do PEB e kernel32.dll

A trick está em usar o prefixo DEC EAX/REX e CMOVcc para conseguir os dados que precisamos de forma condicional: em x86 conseguimos o endereço do struct PEB em EBX, enquanto em x64 isso não tem efeito algum.

Em x86 CMOVS move ESP para EDX, mas não em x64, RDX continua 60h. GS:DEC EAX e CMOVNS não tem efeito também, enquanto em x64 conseguimos o endereço do PEB em RBX.

Em x64 SETZ seta CL = 1. Usamos IMUL para ajustar dinamicamente os offsets para ler Ldr e InLoadOrderModuleList.

PUSH/POP não precisam do prefixo REX, compatível para ambos os modos e é uma ótima trick de otimização. A mesma coisa acontece com SETZ/IMUL que pode ser usado para fazer o parse do PE quando estamos procurando endereços de APIs numa DLL.

Como chamar APIs

W64 se utiliza das chamadas FASTCALL, então algumas APIs vão precisar de slots de 4 QWORD para preencher os registradores, o que chamamos de “shadow space”. Montaremos esses slots utilizando PUSH que em uma STDCALL em W32 tem o efeito de fazer push de um parâmetro. Ficaria assim em W64:

quando encontramos os endereços das APIs que precisamos, damos um push na stack, porém para pegarmos o endereço da API da stack precisamos novamente calcular o offset correto.

A ideia aqui é utilizar o offset da arquitetura x86 e multiplicar por 2 em um código trampoline nomeado no exemplo como jump2api. Então EAX = offset da API, ESI é um ponteiro para os endereços da API na stack:

Esse is64bit é uma espécie de “detection gem” desenvolvido pelo qkumba para uma base de código para malwares autorreplicadores conhecida como beautifulsky (codebase disponível no github):

Aqui XOR define ZF = 1 em ambos os modos. ARPL define ZF = 0 em x86 mas aqui está a pegadinha: em x64, o opcode ARPL é reatribuído como MOVSXD que não altera nenhuma flag.

Bônus: exception handling

Utilizando Vectored Exception Handling é possível criar um handler compatível para ambos os modos. Vejamos como começa nosso handler:

Aqui usamos a trick do prefixo REX/CMOVNS para obter o ponteiro em EXCEPTION_POINTERS em EBX/RBX, onde em x64 é passado para o handler via RCX, e em x86 pela stack.

Usamos CALL para fazer o push na stack para o endereço que usamos para prosseguir a execução substituindo EIP/RIP em CONTEXT, do contrário a execução continuaria onde a exceção aconteceu.

Então, set_newIP representa esse trecho de código:

Aqui obtemos o ponteiro para CONTEXT e calculamos o offset para EIP/RIP e com o POP substituímos o ponteiro com o endereço que foi dado push, então retornamos EXCEPTION_CONTINUE_EXECUTION e a execução prossegue após “call set_newIP”

Leave a Comment

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *