Desenvolvimento

30 ago, 2012

KERNEL32.DLL Fake – um sandbox amador

Publicidade

Há alguns dias, notei uma busca no meu site por uma maneira de carregar e usar KERNEL32.dll falsos e percebi que eu ainda não tinha abordado essa informação. Não há nenhum código-fonte para este artigo, já que estou um pouco sem tempo para escrevê-lo, mas vou fazer o meu melhor para fornecer o máximo de informações, assim, aqueles que querem tentar não terão problemas.

KERNEL32.DLL

A primeira coisa interessante sobre KERNEL32.dll é que ele está sempre carregado, independentemente se um executável em funcionamento importa qualquer coisa a partir dele. O mesmo que NTDLL.DLL (bem, Kernel32.dll importa a partir dela). Essa biblioteca fornece interfaces para interação com os níveis mais profundos da parte “user land” do sistema operacional para o arquivo executável em execução e algumas outras bibliotecas de ligação dinâmica carregadas na memória do processo.

Sabendo de tudo isso, o primeiro pensamento pode ser: “como vamos falsificar se todo o resto depende disso?”. A solução é mais fácil do que se pode imaginar à primeira vista. No entanto, devemos ter em mente que alguns programas podem importar de NTDLL.DLL diretamente, ignorando o KERNEL32.dll (o que costumava acontecer muitas vezes no mundo do malware), o que significa que uma vez que você falsificou KERNEL32.dll, você pode ter que falsificar NTDLL também.

Os preparativos

Devemos começar escrevendo um bom e velho DLL/injetor de código simples. É mais fácil dissecar o processo vítima de dentro. Essa é a parte mais simples. Falando a grosso modo, o injetor deve ser capaz de criar um processo vítima em estado suspenso, passando o processo de criação flag CREATE_SUSPENDED para CreateProcess API.

Escrever o código ou a DLL que vamos injetar é uma tarefa difícil, porque esse código é destinado a executar as tarefas descritas abaixo, em ordem de execução.

Carregue o KERNEL32.dll falso

Vamos supor que tenhamos um KERNEL32.dll falso pronto para usar (vamos voltar à criação de dll falso um pouco depois). Isso é muito simples – chame a função LoadLibrary a partir do seu código. Uma coisa que merece destaque é que MSDN não está sugerindo usar LoadLibrary em sua função DllMain. Portanto, se você decidir usar injeção de DLL, em vez de injeção de código, então é melhor usar a abordagem descrita no artigo “Advanced DLL Injection“.

O falso KERNEL32.dll deve simplesmente importar todas as APIs do original. Não se engane – importar, e não enviar as exportações, pelo menos enquanto estamos falando de funções de API, mas você pode seguramente enviar objetos exportados e variáveis para o original.

Resolver importação do processo vítima

Até o momento, temos nosso código/DLL em execução dentro do processo vítima suspenso, e todas as suas importações deveriam ter sido resolvidas. O que ainda temos que fazer é substituir todos os endereços de API exportados do KERNEL32.dll original por endereços correspondentes em nosso falsificado.

Aqui está um link com as especificações da Microsoft de MS PE e formatos de arquivos do MS COFF – seria útil fuçar nas importações e na exportação.

Ocultar o KERNEL32.dll original

Enquanto executar as ações acima mencionadas pode ser suficiente no caso de uma aplicação regular, devemos tomar algumas precauções em caso de um código malicioso. Minha sugestão é esconder o KERNEL32.dll original, substituindo a sua entrada na lista de estruturas LDR_MODULE no PEB com aquele a descrever nosso KERNEL32.dll falso.

Criação de KERNEL32.dll falso

Isso pode parecer assustador, mas não há necessidade de se preocupar (pelo menos não muita). Tudo o que precisamos para criar um é um compilador C (ou uma linguagem de alto nível que você preferir) e qualquer assembler (eu uso FASM).

Dump KERNEL32.dll to ASM Source

Não, claro que não temos que desmontar o conjunto DLL e despejá-lo em uma fonte de Assembly correspondente. Em vez disso, o que temos que fazer é escrever um pequeno aplicativo em linguagem de alto nível (você pode tentar fazer isso em Assembly se quiser) que iria analisar a tabela de exportação do KERNEL32.dll original e criar um conjunto de arquivos fonte em Assembly: um para o código, um para dados (se necessário), um para a importação e um para as seções de exportação.

Querendo ou não, o aplicativo tem que gerar um pouco de código Assembly para pelo menos, transferir o fluxo de execução para uma função API no KERNEL32.dll original. Por exemplo, se não temos interesse em, digamos, ExitProcess, então o nosso ExitProcess falso deve ser semelhante a este:

fake_ExitProcess:
; As we are not tracing/logging this function, we simply let the
; original ExitProcess shoot
jmp dword [real_ExitProcess]
No entanto, o código seria diferente para APIs de interesse. Por exemplo, a API CreateFileA seria implementada como:
fake_CreateFileA:
; We pass control to a fake CreateFileA, which is implemented in
; a separate DLL imported by our fake KERNEL32.dll
; Parameters are already on the stack, so we simply jump.
; Don't forget to declare the fake function as STDCALL
; (remember #define WINAPI __declspec(stdcall) ? )
jmp dword [our_CreateFileA]

O arquivo fonte Assembly contendo o código para a seção de importação conteria, então, o seguinte:

section '.idata' import data readable writable
library original, 'kernel32.dll',\ ;Import original KERNEL32.dll
fake, 'our_dll_with_fake_api.dll' ;Import a DLL with fake APIs

import original,\
real_ExitProcess, 'ExitProcess'

import fake,\
our_CreateFileA, 'whatever you call it here'

Finalmente, agora chegamos ao código da seção de exportação:

section '.edata' export data readable
export 'KERNEL32.dll',\ ;FASM does not care about what you type here,
;so let's be fake to the end and pretend
;to be KERNEL32.dll
fake_ExitProcess, 'ExitProcess',\
fake_CreateFileA, 'CreateFileA'

Finalmente, o arquivo de origem principal, aquele que iria reunir todos os demais juntos:

format PE DLL at 0x10000000

include 'code.asm'
include 'import.asm'
include 'export.asm'

section '.reloc' fixups data discardable
compile com FASM e você terá seu KERNEL32.dll falso.

Implementação de API falsa

Como já foi mencionado acima, existem algumas funções que gostaríamos de seguir. Elas devem possuir alguma implementação personalizada, de preferência em uma DLL separada (que seria carregada pelo carregador do Windows no momento em que resolve as nossas dependências de KERNEL32.dll falso). Logo abaixo está um diagrama das interações entre todos os módulos:

E aqui está um exemplo dessa API falsa:

HANDLE WINAPI fake_CreateFileA(
LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSA,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
fprintf(log_file, "CreateFileA(list params here)\n", params);
return CreateFileA(lpFileName,
dwDesiredAccess,
dwShareMode,
lpSA,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
}

Claro, você pode implementar mecanismos adicionais dentro dessa DLL, por exemplo, deixá-la se comunicar com outro aplicativo via sockets ou pipes, mas isso merece um artigo separado.

P.S.: Minha sugestão pessoal é inserir mais códigos em cada função dentro do KERNEL32.dll falso, de modo que pareceria mais realista para o processo vítima (ele deve tentar fazer algo com ele).

Espero que este artigo tenha sido útil.

Vejo vocês na próxima.

***

Texto original disponível em http://syprog.blogspot.com.br/2012/03/faking-kernel32dll-amateur-sandbox.html