Introdução
DLL,
Dynamic Link Library, é a forma que o Windows utiliza para compartilhar
bibliotecas de funções entre múltiplas aplicações. Internamente uma DLL
é bem semelhante a um EXE, utiliza o formato PE e somente uma simples
flag diferencia que é uma DLL e não um EXE (essa flag está no
IMAGE_FILE_HEADER no campo Characteristics do PE Header). Além disso
DLLs geralmente têm mais Exports que os executáveis.
DLL
não é um arquivo standalone, se clicarmos duas vezes nela ela não vai
executar, necessita de um processo host. Esse processo é o responsável
por carregar a DLL para seu espaço de memória através da função
LoadLibrary.
Assim
como os executáveis, as DLLs também possuem seu entry point (é chamado
de DllMain ou DllEntryPoint), que na teoria é o endereço da primeira
instrução que será executada pela DLL assim que ela for carregada na
memória do processo host. Assim que a LoadLibrary carrega uma DLL na
memória, automaticamente ela executa a
DllMain.
Para
os malwares, as DLLs são ótimos lugares de armazenamento de códigos
maliciosos, inclusive por sua análise ser um pouco mais complicada que a
de um executável comum. Imagine uma DLL com um packer, se pretendemos
retirar a proteção através de debugging temos que carregá-la na memória
de um processo e colocar um breakpoint bem no entry point da DLL. Como
fazer isso sabendo que um processo pode ter inúmeras DLLs em seu
contexto?
É o que veremos agora através de um passo a passo.
Ambiente de testes
Foi utilizado o Immunity Debugger para injetar uma DLL em um processo e
colocaremos um Breakpoint no entry point da DLL injetada para realizar o
debugging desde a primeira instrução como se fosse um executável
normal.
Apenas para título de demonstração o autor utilizou arquivos comuns do Windows, o processo host será o
IEXPLORE.EXE e a DLL será a
p2p.dll, localizada em “C:\windows\system32\”. O software utilizado é o Immunity Debugger que pode ser
baixado aqui, e também é necessária a instalação do Python 2.7 que pode ser
obtido aqui.
Injeção de DLL em um processo
Segue o passo a passo do procedimento:
1- Execute normalmente o programa que servirá como processo host da DLL, nesse caso foi utilizado o Internet Explorer.
2- Abra o Immunity Debugger e clique em File – Attach
para selecionar o processo no qual o ImmDbg irá anexar para debugar.
Será exibida uma janela com todos os processos em execução. Selecione o
IEXPLORE e clique em Attach.
3-
O ImmDbg irá abrir o processo IEXPLORE.exe em seu ambiente e irá pausar
a execução. Queremos injetar uma DLL no processo e parar a execução
exatamente no Entry Point da DLL.
Aqui
temos um problema, a função que faz a injeção da DLL é a LoadLibrary() e
sabemos que quando ela carregar a DLL na memória do processo ela
automaticamente executará o entry point da DLL não dando tempo de
colocar um breakpoint aí.
Para lidar com isso o ImmDbg possui uma opção, clique em Options – Debugging options e selecione a aba Events. Marque a opção “Break on new module (DLL)”
e clique em OK. Assim o debugger irá parar a execução bem após ter
carregado a DLL na memória e antes de executar o entry point.
4- Estamos prontos para injetar a DLL. Clique no segundo botão da barra de tarefas do ImmDbg para a abrir a Python Shell.
5-
A shell que se abriu nos permite executar comando Python e ter acesso a
API Python do debugger. Para obter a documentação da API há o menu ImmLib – Help no próprio ImmDbg ou ainda na pasta de instalação do programa: Immunity Debugger\Documentation\Ref\toc.html.
6-
No momento que injetamos a DLL no processo é criada uma nova Thread
para essa DLL, então vamos injetar nossa DLL e recuperar e exibir o
número da Thread. Para isso digite os seguintes comandos na shell:
>>>thread = imm.injectDll("c:\\p2p.dll")
>>>
>>>print "Thread ID: 0x%X" % thread
Thread ID: 0x134
Injetamos a DLL e já recuperamos o número da nova Thread em um variável, depois exibimos esse número no formato hexadecimal.
7-
A DLL foi injetada na memória do processo mas o módulo ainda não foi
carregado pela LoadLibrary(). Precisamos executar o programa para que
nosso módulo seja chamado. Pode ser que ele não seja o próximo módulo a
ser carregado, talvez precisemos executar o programa (F9) mais de uma
vez.
Quando pressionarmos F9 para a execução, teremos que ficar de olho na janela de módulos carregados (Window – 3 Executable modules)
para descobrir se o nosso está lá. Às vezes o ImmDbg nos apresenta essa
janela assim que o módulo é carregado e ele estará destacado em
vermelho.
8- Pressione F9
e observe os resultados, caso não tenha carregado a DLL pressione
novamente F9 até atingi-la. No momento que carregar o nosso módulo
injetado será exibida na janela de módulos essa linha em vermelho:
9-
Nosso módulo foi carregado e o entry point da DLL ainda não foi
executado. Agora vamos voltar para a Python Shell para colocar um
breakpoint no entry point da DLL carregada. Utilize os comandos abaixo.
>>>mod = imm.getModule("p2p.dll")
>>>
>>>print "Module ImageBase: 0x%X" % mod.getBase()
Module ImageBase: 0x4EFB0000
>>>
>>>print "Module EntryPoint: 0x%X" % mod.getEntry()
Module EntryPoint: 0x4EFC22E4
>>>
>>>imm.setBreakpoint(mod.getEntry())
0
>>>
Primeiro
atribuímos para uma variável o módulo carregado, em seguida apenas por
questões de estudo imprimimos os endereços do ImageBase e EntryPoint do
módulo, com as funções getBase() e getEntry()
respectivamente. Por fim colocamos o breakpoint exatamente no EntryPoint
do módulo. Se olharmos na janela de breakpoint do ImmDbg ele estará lá.
10- Agora retire aquela opção de parar a execução nos módulos e execute o programa novamente com F9 ou com o comando imm.run() no Python Shell. A execução irá parar em nosso breakpoint e a partir daí é só debugar a DLL normalmente.
Conclusão
Essa
injeção de DLL com o uso do ImmDebugger não é muito conhecida, é
difícil encontrar documentação a respeito e até em uma fonte mais
confiável como um livro não apresenta o procedimento de forma
precisa, são necessários vários testes até atingir os resultados
esperados.
Postado por:
Ronaldo Lima