Introdução
Olá pessoal! Este é mais um artigo da série onde falo sobre técnicas de anti-engenharia reversa. Neles compartilho um pouco dos tutoriais práticos que desenvolvi ao longo das minhas pesquisas e estudos. No primeiro artigo, falei sobre a categoria de anti-debugging TLS Callback. Agora vou falar um pouco sobre as técnicas da categoria que utilizam flags de depuração do próprio processo para detectar o debugger. Antes de mais nada, vale entender o que são essas flags.
O que são flags de depuração?
Estas flags são valores presentes dentro do espaço de endereço do processo, permanecendo na memória até o encerramento deste programa. A presença e/ou determinado valor destes bits podem indicar que o processo está em um ambiente de depurador. Como estas técnicas são válidas apenas para o ambiente Windows, seu significado e valores são definidos na documentação oficial do SO (WinAPI) [1], entretanto algumas flags podem não ser documentadas e, neste caso, somente se encontra informação em livros como o Windows Internals [2].
A primeira que vamos analisar é a NtGlobalFlag, desenvolvida na época do antigo Windows NT. Esta flag é definida no cabeçalho do Process Environment Block (PEB) de todos processos no sistema e é usada especificamente para indicar se o processo está executando ou não em depurador.
Antes de aprofundar nas particularidades desta flag, precisamos entender o que é a PEB, que foi citada anteriormente. De forma resumida, é uma estrutura que existe em cada processo do Windows e é apontada por outra estrutura chamada TEB (Thread Environment Block), que por sua vez contém informações em relação às threads do processo.
O código da PEB é composto por uma struct e sua versão para 32 bits no header windows.h ou winternl.h é a seguinte:
typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID Reserved4[3]; PVOID AtlThunkSListPtr; PVOID Reserved5; ULONG Reserved6; PVOID Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; PVOID Reserved9[45]; BYTE Reserved10[96]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved11[128]; PVOID Reserved12[1]; ULONG SessionId; } PEB, *PPEB;
*O 3º byte desta estrutura é o campo BeingDebugged e este é definido pelo Windows para todo binário que está sendo executado, sendo 0 (padrão) para indicar que o processo não está sendo depurado e 1 para depurado. Este byte é avaliado na técnica abordada no Curso de Engenharia Reversa Online (CERO) através da função IsDebuggerPresent() e por conta disto esta técnica não será abordada neste artigo.
A Aula 24 - Anti-debug, do CERO, aborda a PEB de forma prática. Uma referência completa sobre a PEB também pode ser acessada em [3].
NtGlobalFlag
Esta flag também é um campo da PEB e se encontra no offset 0x68 em ambientes 32 bits, ou offset 0xBC em 64 bits, contados a partir do seu início. Esta flag é usada para rastreamento e depuração de programas. Seu valor é 0 por padrão, indicando que não há depurador no ambiente.
Caso se inicie o programa primeiro e depois de já iniciado, se analise seu processo no depurador, os campos da NtGlobalFlag não são alterados, deixando esta abordagem sem efeito. No entanto, caso se execute o programa diretamente em um depurador, as seguintes flags serão definidas:
FLG_HEAP_ENABLE_TAIL_CHECK (0x10) FLG_HEAP_ENABLE_FREE_CHECK (0x20) FLG_HEAP_VALIDATE_PARAMETERS (0x40)
A presença de um depurador no ambiente é confirmada ao se verificar o valor 0x70 resultante da operação OR entre estas flags.
Disassemble de processo 32 Bits:
mov eax, fs:[30h] // acessa a PEB no registrador de segmento mov al, [eax+68h] // a partir da PEB acessa a flag NtGlobalFlag and al, 70h // soma as flags FLG_HEAP_* cmp al, 70h // verifica se o valor da soma é 0x70 call being_debugged // caso positivo, avisa sobre depuração
Disassemble de processo 64 Bits:
mov rax, gs:[60h] // acessa a PEB no registrador de segmento mov al, [rax+BCh] // a partir da PEB acessa a flag NtGlobalFlag and al, 70h cmp al, 70h call being_debugged
Disassemble de processo WOW64 (64 Bits que aceita também 32 bits):
mov eax, fs:[30h] // acessa a PEB no registrador de segmento mov al, [eax+10BCh] // a partir da PEB acessa a flag NtGlobalFlag and al, 70h cmp al, 70h call being_debugged
Código de exemplo:
#include <windows.h> #include <iostream> using namespace std; // define o valor das flags #define FLG_HEAP_ENABLE_TAIL_CHECK 0x10 #define FLG_HEAP_ENABLE_FREE_CHECK 0x20 #define FLG_HEAP_VALIDATE_PARAMETERS 0x40 // executa a operação OR entre o valor de todas as flags #define NT_GLOBAL_FLAG_DEBUGGED (FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS) // se não for 64 bits #ifndef _WIN64 PVOID pPeb = (PVOID)__readfsdword(0x30); // PEB DWORD dwNtGlobalFlag = *(PDWORD)((PBYTE) pPeb + 0x68); // flag #else // o mesmo procedimento para 64 bits PVOID pPeb = (PVOID)__readgsqword(0x60); DWORD dwNtGlobalFlag = *(PDWORD)((PBYTE)pPeb + 0xBC); #endif // _WIN64 // função que acusa depurador e sai void being_debugged() { std::cout << "Stop debugging program!" << std::endl; exit(-1); } int main() { // se em ambiente de depurador chama função de saída if (dwNtGlobalFlag & NT_GLOBAL_FLAG_DEBUGGED) being_debugged(); eles // senão segue normalmente std::cout << "Hello World!\n"; return 0; }
Desabilitando a Técnica
Para desviar desta verificação, basta definir o campo NtGlobalFlag da estrutura PEB com 0 antes que este valor seja verificado pela proteção anti-depuração. Abaixo são alguns exemplos de como zerar esta flag:
No x64dgb com o plugin ScyllaHide:
Plugins → ScyllaHide → Options -> NtGlobalFlag [x]
Manualmente no x64dbg:
1. Ctrl+G → peb()+0x68 (32 bits) ou Ctrl+G → peb()+0xBC (64 bits)
2. Trocar o valor do byte de 0x70 para zero
No OllyDbg com o plugin Command Line:
1. Plugins → Command Line → Command Line
2. dump fs:[30]+0x68 (32 bits) ou dump gs:[60]+0xBC (64 bits)
3. No painel dump de memória selecionar o 1º byte → Binary → Fill with 00's
*Também é possível usar o plugin Olly-Advanced
Bom, iniciei aqui uma nova categoria de técnicas Anti-Debug, a das flags de depuração, iniciando com a flag NtGlobalFlag. No próximo artigo vou falar sobre uma técnica que usa 2 flags em conjunto, a Heap Flags e a ForceFlags.
Espero que tenham gostado.
Forte abraço & até o próximo!
Referências:
[1] https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list
[2] https://docs.microsoft.com/en-us/sysinternals/resources/windows-internals
[3] https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm