Você está na página 1de 9

Aula 03

2.12. Exemplo de código em C e Assembly ......................................................................................32


2.13. Mais informações: linguagem assembly e arquitetura Intel x86 ..............................................33
2.14. Lab-02-01 Entendo na prática o funcionamento da pilha com o OllyDbg ...............................35
CAPÍTULO 2 – ESTRUTURAS INTERNAS DOS S OFTWARES | 32

2.12. Exemplo de código em C e Assembly

É importante sabermos como um programa em uma linguagem de alto-nível se traduz para


uma de baixo-nível. Nesse caso iremos utilizar as linguagens C e assembly. Isso irá ajudá-lo a
entender a diferença nas suas construções.
Um programa padrão em C possui dois argumentos no método main, tipicamente dessa
forma:

int main(int argc, char ** argv)

Os parâmetros argc e argv são determinados em tempo de execução. O parâmetro argc é um


inteiro que contém o número de argumentos da linha de comando, incluindo o nome do programa.
O argv é um ponteiro para um array de strings que contém os argumentos da linha de comando. O
exemplo abaixo mostra um programa de linha de comando e os conteúdos do argc e argv quando o
programa é executado.

programateste.exe -r filename.txt

argc = 3
argv[0] = programateste.exe
argv[1] = -r
argv[2] = filename.txt

A listagem abaixo mostra o código C para um simples programa.

Agora a listagem abaixo mostra como o código C acima fica quando é compilado, ou seja,

OYS Academy – Ronaldo Pinheiro de Lima


CAPÍTULO 2 – ESTRUTURAS INTERNAS DOS S OFTWARES | 33

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.

2.13. Mais informações: linguagem assembly e arquitetura Intel x86

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

Volume 1: Basic Architecture


Esse manual descreve a arquitetura x86 e o ambiente de programação. Ela é útil por ajudar a
entender como a memória trabalha, incluindo registradores, layout de memória, endereçamento e a
pilha. No manual também contém detalhes sobre os grupos de instruções.

Volume 2: Instruction Set Reference


Esse é o manual mais útil para analistas de malware. Ele coloca todas as instruções assembly em
ordem alfabética e discute todos os aspectos de cada uma, incluindo o formato da instrução,
opcodes e como a instrução impacta o sistema.

Volume 3: System Programming Guide


Adicionalmente aos registradores de uso geral, a arquitetura x86 possui muitos registradores de uso
especial e instruções que impactam e auxiliam o sistema operacional, incluindo debugging,
gerenciamento de memória, proteção, gerenciamento de tarefas, interrupções e manipulação de
erros, suporte a multiprocessadores e muito mais. Caso encontre um registrador de uso especial
recorra a esse guia para descobrir como ele impacta a execução do programa.

Download dos três volumes combinados em um único PDF: http://intel.ly/u7ZHpu.

Optimization Reference Manual


Esse manual da Intel descreve técnicas de otimização de código para aplicações. Ele oferece
detalhes adicionais sobre o código gerado pelos compiladores e possui muitos exemplos de como as
instruções podem ser utilizadas de modos não convencionais.
Download: http://intel.ly/x1cLz5.

The Art of Assembly Language Programming (AoA) - Randy Hyde


É o livro mais popular da Internet sobre a linguagem assembly. Ótimo para ser consultado como
referência. Possui conteúdo completo e detalhado sobre a linguagem. Está disponível online para
ser baixado gratuitamente.

OYS Academy – Ronaldo Pinheiro de Lima


CAPÍTULO 2 – ESTRUTURAS INTERNAS DOS S OFTWARES | 35

Download: http://www.artofasm.com/index.html.

2.14. Lab-02-01 Entendo na prática o funcionamento da pilha com o OllyDbg

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.

2- Clique no menu File – Open e selecione o arquivo Lab-02-01.exe.

3- Será apresentada a tela abaixo.

OYS Academy – Ronaldo Pinheiro de Lima


CAPÍTULO 2 – ESTRUTURAS INTERNAS DOS S OFTWARES | 36

Notamos quatro janelas diferentes, que correspondem à:

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.

OYS Academy – Ronaldo Pinheiro de Lima


CAPÍTULO 2 – ESTRUTURAS INTERNAS DOS S OFTWARES | 37

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.

9- Siga pressionando o F7 e observando as alterações nas janelas, a execução do programa entrará


na função.

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.

OYS Academy – Ronaldo Pinheiro de Lima


CAPÍTULO 2 – ESTRUTURAS INTERNAS DOS S OFTWARES | 39

O valor atual do registrador EBP é 0012F8D0 e do ESP é 0012F8C4.

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.

OYS Academy – Ronaldo Pinheiro de Lima

Você também pode gostar