Escolar Documentos
Profissional Documentos
Cultura Documentos
programateste.exe -r filename.txt
argc = 3
argv[0] = programateste.exe
argv[1] = -r
argv[2] = filename.txt
Agora a listagem abaixo mostra como o código C acima fica quando é compilado, ou seja,
na linguagem assembly.
Cada linha do código, isto é, cada endereço de memória que é mostrado à esquerda é
chamado de offset. O argc é comparado com 3 em , e o argv[1] é comparado com -r em isto é
feito com o uso da função strncmp.
Note como argv[1] é acessado: primeiramente a localização do início do array é carregada
em EAX, e então 4 (offset) é adicionado ao EAX para obter o argv[1]. O número 4 é utilizado
porque cada entrada no array argv é um endereço para uma string e cada endereço possui 4 bytes de
tamanho em sistemas 32-bit. Se -r é fornecido na linha de comando, o código que se inicia em
será executado, que é quando nós vemos o argv[2] sendo acessado através do offset 8 do argv,
ou seja, assim como fez com o 4, ele utiliza o início do array argv e adiciona 8 bytes. Dessa forma é
fornecido um argumento para a função DeleteFileA.
O foco do curso não é cobrir todo o conteúdo da arquitetura x86 e da linguagem assembly. A
intenção nesse capítulo foi de prover informações essenciais para lidar com a engenharia reversa de
malware.
OYS Academy – Ronaldo Pinheiro de Lima
CAPÍTULO 2 – ESTRUTURAS INTERNAS DOS S OFTWARES | 34
Para conteúdo adicional existem fontes que discutem com detalhes esses assuntos, seguem
algumas recomendações de materiais:
Manuais Intel
Download: http://www.artofasm.com/index.html.
Nesse Lab iremos ver na prática como uma pilha é formada na memória, praticaremos os
conceitos discutidos nesse capítulo como o layout da pilha, formação do stack frame, passagem de
parâmetros, prólogo, epílogo, acesso a variáveis locais, endereço de retorno, uso dos registradores,
etc. O Lab também será útil para apresentar a ferramenta OllyDbg, muito utilizada na engenharia
reversa de malware. O executável estudado será um trojan-banker real.
Material necessário:
- Máquina virtual com Windows XP 32-bit
- OllyDbg
- Arquivo: Lab-02-01.exe
Passo a Passo
1- Abra o OllyDbg.
1. Código disassembly: essa janela exibe o código do programa que está sendo debugado. A
linha marcada é a próxima instrução que será executada.
2. Registradores: essa janela exibe o estado atual dos registradores do programa que está
sendo debugado. Quando o código vai sendo executado, esses registradores vão mudando da
cor preta para a cor vermelha caso a instrução executada modifique seu valor.
3. Memory dump: essa janela exibe o dump da memória que está sendo utilizada pelo
programa.
4. Pilha: essa janela exibe o estado atual da pilha na memória. Ela sempre exibirá o topo da
pilha a menos que você a trave clicando com o botão direito do mouse em cima dela e
escolhendo a opção “Lock address”.
Clicando com o botão do mouse em cima em uma dessas janelas ela passará a ser o foco e os
comandos e atalhos de teclado terão efeitos sobre ela.
4- Clique na janela referente ao código, pressione o atalho Ctrl + G (G de "Go") e insira o endereço
453E58. Isso nos levará diretamente ao offset que queremos, com o trecho de código abaixo.
Obs.: Para o Olly destacar CALLs e JUMPs com cores diferentes, clique na janela de código,
clique com o botão direito e selecione Appearance – Highlighting – Jumps and calls.
Destacado em azul vemos uma chamada a função que está no endereço 00444230. Antes
dessa chamada vemos quatro PUSHs. Como discutimos, a instrução PUSH serve para colocarmos
itens na pilha e quando isso é feito antes de uma chamada de função provavelmente esses valores
são os argumentos (ou parâmetros) passados para a função. Com isso já podemos deduzir que essa
função possui quatro parâmetros.
5- Agora vamos observar a execução do programa para vermos como a pilha é formada na
memória. Clique em cima do primeiro PUSH que está no offset 00453E45. A linha será destaca,
agora pressione F2. Isso faz com que seja colocado um breakpoint nessa instrução. Quando o
programa executar irá parar nessa linha, o que nos permitirá observar com cuidado os valores da
memória e dos registradores.
6- Iremos executar o programa, para isso pressione o F9. Vemos que o offset ficou em vermelho e
preto e no rodapé da janela do OllyDbg há uma indicação de que o programa realmente parou em
nosso breakpoint.
7- Podemos debugar o programa pressionando F7 (step into) ou F8 (step over), isto é, executar cada
instrução linha a linha. A diferença do step into para o step over é que quando encontrar uma
chamada de função o step into entrará nessa função e executará linha a linha todas as suas
instruções. Já o step over pulará o código interno da função e sua execução irá para a instrução logo
abaixo do CALL, ou seja, o retorno da função. No nosso caso utilizaremos o F7 já que queremos
debugar o código interno da função.
OYS Academy – Ronaldo Pinheiro de Lima
CAPÍTULO 2 – ESTRUTURAS INTERNAS DOS S OFTWARES | 38
8- Seguimos pressionando F7 até chegar à instrução CALL, a cada instrução executada repare
cuidadosamente nos efeitos que elas provocam nas janelas dos registradores e principalmente da
pilha. A pilha terá a aparência abaixo.
No Olly vemos que ela vai crescendo de baixo para cima, porém podemos reparar que é do
endereço de memória maior para o menor, top-down. Vemos então que os quatro parâmetros para a
função já estão na pilha.
Nas duas primeiras linhas observamos o epílogo da função, onde o EBP é salvo na pilha e
em seguida recebe o valor do ESP para servir como base pointer, assim como discutimos o EBP será
utilizado para acessar variáveis locais através da expressão [EBP-valor] e parâmetros através de
[EBP+valor]. Na terceira linha vemos o uso da instrução SUB que subtrai do ESP 0x0C bytes, isso
é para reservar no stack frame espaço para as variáveis locais, que vemos sendo acessadas a partir
do offset 00444252.
10- Seguindo com o F7 até o offset 00444236, instrução abaixo do SUB, teremos o seguinte layout
da pilha.
Vemos nesse stack frame tudo o que já discutimos até aqui. Os quatro valores abaixo
(endereços mais altos) são os quatro parâmetros passados para a função. Acima há o endereço de
retorno do programa, o OllyDbg já facilita a nossa vida no comentário em vermelho dizendo
justamente isso, esse valor é colocado na pilha automaticamente quando a instrução CALL é
executada, assim quando terminar a execução da função o programa saberá para onde voltar no
código que a chamou.
Acima do endereço de retorno há o EBP que foi salvo na pilha e será usado como ponteiro
base.
A última instrução executada até aí foi a SUB ESP, 0C, o que ela fez? Subtraindo o ESP que
é o topo da pilha ela reservou 12 bytes (0x0C) para variáveis locais nos endereços 0012F8CC,
0012F8C8 e 0012F8C4. Esse último é agora o topo da pilha, o valor que está no ESP.
E porque esses três endereços reservados estão apresentando um conteúdo estranho? Lixo
deixado por outras funções, assim que forem utilizados serão sobrescritos.
É importante entender o que foi discutido até aqui, pois é essencial para a engenharia reversa
de malware, caso tenha alguma dúvida sinta-se à vontade para perguntar, revisar, reler, até não
restar dúvidas.