You are on page 1of 18

Utilizando um debugger - OllyDbg http://www.guiadohardware.

net/comunidade/utilizando-debugger/785195/

Usuário Lembrar?
Senha

FórumGdH Registrar Calendário Pesquisar

Utilizando um debugger - OllyDbg


Programação, scripts, web e banco de dados

1. Utilizando um debugger - OllyDbg

Mais resultados? Use nossa pesquisa.

Compre lindos relógios da Timex e Dumont com super descontos de revenda autorizada, toda
linha a pronta entrega!

Página 1 de 3 1 23

23-09-2007 por Fergo


Utilizando um debugger - OllyDbg
Boa noite pessoal!
Ultimamente tenho visto uma certa
quantidade de posts relativos a programação
em linguagem assembly e ao uso de
debuggers (seja para depuração ou para fazer
adaptações). Resolvi criar uma pequena série
de tutoriais (comecei hoje) cobrindo alguns
conceitos interessantes sobre a utilização de
debuggers e um pouco de engenharia
reversa. A medida que eu vou escrevendo eu
vou postando aqui, em forma de post mesmo.

Por enquanto escrevi apenas uma introdução


e o esclarecimento de alguns conceitos. Estou
pensando ainda no aplicativo que vou
programar para usar como exemplo de
análise, mas provavelmente eu faça apenas
aquele programa clássico de "adivinhe o número" (escolha um número de 1 a 10...). Pretendo
utilizar o OllyDbg nos tutoriais, pois é o meu debugger predileto e o que estou mais
familiarizado (gratuito e pequeno).

Sumário:

1. Introdução e conceitos
2. Um pouco de assembly
3. Interface do Olly Debugger
4. Formas de aproximação
5. Re-assembly
6. Plugins

Mais capítulos em breve!

INTRODUÇÃO
Seguindo a minha linha de tutoriais voltados a programação, vou tratar sobre um assunto que
me interessa muito e talvez seja interessante para os programadores em geral: disassembler e

1 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

debuggers.

Primeiramente seria interessante esclarecer um pouco sobre o que é um debugger e o que é


um disassembler, pois apesar de andarem quase sempre juntos, possuem finalidades
diferentes.

Disassembler é algo que consegue transformar linguagem de máquina para a linguagem


assembly, transcrevendo as instruções enviadas ao processador para os seus mnemônicos em
assembly (asm). Não deve ser confundido com um descompilador, que procura converter o
código nativo em uma linguagem de mais alto nível como C, C++ ou Basic.

Debuggers são programas capazes de analisar, depurar e testar aplicações. Atualmente a


maioria das IDEs de programação contam com um debugger embutido (Visual Studio, por
exemplo). A principal utilidade deles é para a identificação e tratamento de erro, sendo que é
possível rodar o código linha por linha (ou instrução por instrução) e analisar a mudança das
variáveis e do comportamento do código. Os debuggers de binários já compilados - como os
executáveis do Windows (EXE) - seguem o mesmo conceito dos depuradores normais, mas
devido ao fato de o código já ter sido compilado, ele precisa ter um disassembler embutido no
debugger para decodificar as instruções.

Atualmente existem dezenas de debuggers e disassemblers por aí, dentre os quais os mais
famosos são: W32DASM, IDA, WinDbg, SoftICE e Ollydbg. Neste tutorial será utilizado o
OllyDbg, pois é um dos melhores e mais poderosos debuggers (incluindo um disassembler)
disponíveis no mercado. É também pequeno e gratuito

Site oficial do OllyDbg, com o link para download: http://www.ollydbg.de

QUAL A UTILIDADE UM DEBUGGER?


Muita gente se pergunta do porquê de usar um debugger, sendo que na maioria dos casos você
tem a acesso ao código fonte original (caso você tenha programado o aplicativo). Vou citar
abaixo algumas das maiores utilidades de um debugger:

Tratamento de erro. Certamente uma das principais. Às vezes durante a programação de


um aplicativo um pequeno erro passou despercebido, ocasionando mal funcionamento ou
gerando uma operação ilegal. Em muitos casos é mais fácil você analisar o binário já
compilado dentro de um debugger do que tentar encontrar o erro no código original.
Dentro desse mesmo item podemos citar a correção de bugs de aplicações já
descontinuadas (desde que com a autorização da empresa dona dos direitos).

Engenharia reversa. O processo de engenharia reversa de software não poderia ser feito
de forma eficiente sem a utilização de um debugger/disassembler. Muitas pessoas tendem
a confundir cracking com engenharia reversa, sendo que são conceitos diferentes. A
engenharia reversa por si só é uma atividade completamente legal, pois muito do que
vemos hoje só foi possível devido à engenharia reversa. A criação de drivers para Linux
de periféricos que antes só funcionavam com o Windows (WinModems) é um bom
exemplo de como a engenharia reversa traz coisas boas para nós.

Aprendizado. O uso de debuggers e engenharia reversa é uma das melhores formas de se


aprender a linguagem assembly. Você programa algo em uma linguagem de médio ou
alto nível e posteriormente analisa o resultado do binário compilado dentro de um
debugger. Com esse conhecimento é possível dominar melhor a linguagem e criar
algoritmos mais otimizados e eficientes.

CONCEITOS NECESSÁRIOS
Para entender o funcionamento de um debugger é preciso saber um pouco sobre alguns
conceitos ligados a informática, como o funcionamento da memória, processador, pilhas e
endereços. O conhecimento básico de assembly também é necessário, já que essa é a
linguagem que teremos de analisar. Caso não tenha experiência em assembly, fique tranqüilo,
pois nos capítulos seguintes darei uma visão geral sobre ela, o suficiente para entender o nosso
mini-aplicativo de estudo. Abaixo segue uma breve lista de conceitos:

2 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

Processador/CPU: É o cérebro de todo computador. É ele que decodifica as instruções e


executa os códigos operacionais. É composto basicamente por uma unidade lógico-
aritmética (ALU), unidade de ponto flutuante (FPU), registradores, cachê, barramento e
gerador de clock.

Memória RAM: Local de armazenamento temporário de dados (são apagados ao desligar


o computador). Todo aplicativo se utiliza da memória para armazenar seus dados e estes
são buscados e gerenciados pelo processador.

Endereçamento de memória: É uma faixa de valores que apontam para uma


determinada posição de memória. Toda vez que você escreve ou lê algum dado da
memória é necessário indicar o endereço de onde está aquele valor, para que o
processador possa buscá-lo.

Pilha (Stack): É uma estrutura de dados. Sua principal característica é a forma de


funcionamento, onde você apenas coloca ou retira os valores, sem indicar um endereço
(LIFO – Last in, First Out – Último a entrar, primeiro a sair). Ela funciona de forma
semelhante a uma pilha de livros em que você vai os empilhando. Quando precisar
remover um deles, é necessário tirar todos os livros de cima.

Registradores: Pequenas partes de memória presentes dentro dos processadores (não


confundir com memória RAM). Extremamente rápidas, sendo que a CPU as utiliza como
forma temporária de armazenamento de dados e realização de operações. A quantidade
de dados que podem ser armazenados vai depender do tipo de processador. Os
processadores de 32 bits conseguem armazenar números de até 32 bits em cada
registrador, sem precisar de rotinas de conversão.

Parte 2 em breve. Provavelmente com imagens já.

Abraços,
Fergo
23-09-2007 por Fergo
Continuação...

UM POUCO DE ASSEMBLY
Para fazer o debug de binários compilados é necessário ter um conhecimento (ao menos básico)
da linguagem assembly, já que é para ela que a linguagem de máquina é traduzida.

Assembly (ou asm, com é abreviada) é uma linguagem de baixo nível que basicamente
interpreta os códigos operacionais (opcodes, veja abaixo) e os transcreve para seus
mnemônicos. É literalmente uma tradução da linguagem de máquina. O uso da linguagem
assembly pode ser bem variado, podendo fazer de tudo um pouco, mas é amplamente utilizada
na programação básica de Kernels e em algoritmos que precisam ser altamente otimizados,
onde asm é a linguagem ideal, já que é puramente linguagem de máquina traduzida.

Não pretendo agora explicar todo o funcionamento, estrutura e comandos da linguagem. Vou
dar apenas um apanhado geral sobre alguns termos e uma breve descrição sobre os comandos
mais básicos e corriqueiros que se encontra. Precisamos primeiramente definir o que são
mnemônicos e o que são os opcodes.

Opcodes (traduzido em operational code, ou código de operação) é a instrução que é enviada e


interpretada pelo processador. Cada opcode, ao ser interpretado pelo processador, vai realizar
uma operação. Mnemônicos são as palavras ou combinação de letras utilizadas para
representar um opcode, tornando a linguagem de máquina mais legível. Veja abaixo um
exemplo de um mnemônico do comando MOV:

Código:
MOV EAX,1

3 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

Esse comando em assembly apenas move o valor 1 para o registrador EAX (veremos isso logo
adiante na explicação dos comandos). Na hora de transformar isso em linguagem de máquina
(por um asssembler), esse comando é traduzido para um conjunto de números que possa ser
interpretado pelo processador:

Código:
B801000000

A teoria por trás de tradução de mnemônicos em opcode (e vice-versa) é um tanto complexa,


principalmente para a plataforma Intel na arquitetura IA32. É um processo que deve ser
realizado bit a bit e fugiria um pouco do contexto deste tutorial.

A principal dificuldade na linguagem assembly é certamente a sua estrutura, que foge do


padrão de linguagens de mais alto nível como C ou Pascal. Nada de Ifs com múltiplas
comparações, Switches, For ou While. Tudo é feito com comparações simples e saltos, perdendo
a sua linearidade (semelhante aos GoTo do BASIC).
Felizmente hoje temos debuggers muito inteligentes que conseguem estruturar e identificar
rotinas e repetições, facilitando muito o trabalho de interpretação. Mesmo com essas melhorias,
ainda acho importante ter um papel e uma caneta ao lado, onde você pode fazer anotações e ir
estruturando/convertendo o código na medida em que você os interpreta.

Para o assembly, a localização dos valores e variáveis é sempre baseada nos endereços que
elas ocupam na memória. O nome que você define para uma variável durante a programação é
substituído pelo endereço de memória que ela ocupa. Cada instrução também possui um
endereço, que é utilizado para controlar o fluxo e a estrutura do código. Sempre que você faz
um salto, é necessário indicar o endereço que o código deve ser direcionado, semelhante ao
que ocorria nas numerações de linhas dos BASICs mais antigos. Veja um exemplo abaixo de
como ficaria um código em C e o seu resultado compilado para assembly, utilizando apenas
registradores comuns:

Código:
void main() {
int a = 4;
int b = 6;
int c;

if((a == 4) && (b == 6)) {


c = 5;
}
}

O código acima quando compilado pode se transformar em algo semelhante a isso (boa parte do
código acima é inútil, estou utilizando somente para exemplificar):

Código:
00000000 MOV EAX,4h ;move o valor 4 para EAX
00000005 MOV EBX,6h ;move o valor 6 para EBX
0000000A CMP EAX,4h ;compara EAX com 4, se for verdadeiro: ZF = 1
0000000D JNE 00000019h ;se ZF != 1, pule para endereço 00000019h
0000000F CMP EBX,6h ;compara EBX com 6, se for verdadeiro: ZF = 1
00000012 JNE 00000019h ;se ZF != 1, pule para endereço 00000019h
00000014 MOV ECX,5h ;move o valor 5 para ECX
00000019 RETN ;finaliza execução e retorna

Pra entender o código acima é necessário entender sobre aquilo que compõe a linguagem

4 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

assembly. Ela é basicamente composta por registradores, endereços e instruções


(mnemônicos).

Os registradores foram explicados no capítulo anterior, mas vamos agora saber quem são eles.
Os processadores da arquitetura Intel de 32 bits possuem basicamente nove registradores de
32 bits comuns: EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI e EIP. Teoricamente cada um desses
registradores possui uma determinada “função padrão”, mas devido a sua escassez, muitas
vezes eles são utilizados como registradores para qualquer propósito. Você pode criar um
código usando livremente os oito primeiros registradores que não haverá muitos problemas
(desde que saiba o que está fazendo/modificando). O último registrador, EIP, é quase sempre
mantido intacto, pois ele é o responsável por contar as instruções e informar o endereço da
próxima instrução. Alterar o seu valor pode desviar completamente o fluxo do aplicativo e
provavelmente vai gerar uma falha de segmentação ou uma operação ilegal.

Esses registradores apresentados são todos de 32 bits. No entanto também é possível utilizar
apenas 8 ou 16 bits, como mostra a tabela abaixo utilizando o EAX como exemplo (a teoria
vale para os outros registradores também):

Para o caso da porção de 8 bits, o registrador terminado em L são os 8 bits menos significantes
de AX e o terminado em H são os 8 bits mais significantes de AX. Para a porção de 16 bits, são
utilizados os 16 bits menos significantes da porção de 32 bits.

Além dos registradores, também existem as Flags, que são bits utilizados como resultado de
operações (verdadeiro ou falso, por exemplo). Elas são usadas principalmente para análise
condicional em instruções como CMP e TEST. Dentre as diversas flags, as mais corriqueiras são:
ZF (Zero Flag), CF (Carry Flag) e SF (Signal Flag). A ZF é setada sempre que uma operação
resulta em zero (uma comparação entre dois números através do comando CMP subtrai seus
operandos sem alterar valores e seta a ZF caso o resultado da subtração seja zero, indicando
valores iguais). A flag CF é setada quando o resultado de uma operação estoura o valor
máximo comportado pelo registrador/local sem considerar o sinal (overflow). Por último,
tempos a SF, que é ativada sempre que o bit mais significativo de um operando for 1, indicando
um valor negativo (pesquise sobre complemento de dois).

Os endereços na linguagem assembly são a base para o fluxo do aplicativo e para o


armazenamento de dados. As variáveis que você usa durante a programação são substituídas
por endereços que apontam para uma área da memória paginada com acesso a leitura e a
escrita. Os destinos dos saltos (Jumps) também dependem dos endereços das instruções, pois é
através deles que você informa o destino do salto.
O código abaixo demonstra apenas uma linha de assembly onde é possível ver o endereço da
instrução (00401000) e o endereço para um byte de memória (00403000) para o qual o
número nove está sendo movido:

Código:
00401000 MOV BYTE PTR DS:[00403000], 09h

Por último temos as instruções, que nada mais são do que os opcodes traduzidos em um
mnemônico, como demonstrado e exemplificado alguns parágrafos acima.
Abaixo eu vou por uma pequena lista mostrando algumas das instruções mais utilizadas, pois
seria inviável colocar todas elas (são aproximadamente 130 instruções bases para a arquitetura
Intel).

MOV destino, origem


Move o valor do campo origem para o destino. Essa instrução possui diversas variações,

5 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

por isso ela pode aparecer de diversas formas diferentes (pode-se trabalhar com
constantes, memória, pilha, etc). Alguns exemplos:

MOV EAX, 10h


MOV AX, WORD PTR DS:[00403000]
MOV BYTE PTR DS:[00403002], 1Ch

CMP arg1, arg2


Realiza uma comparação entre os dois operandos. A comparação é feita simplesmente
subtraindo os dois operandos e caso o resultado for zero (valores iguais), ele seta a ZF
para 1. Vale lembrar que essa operação não altera os valores dos operandos, apenas as
flags.

CMP EAX, 04h

JMP endereço
Faz um salto incondicional e obrigatório para o endereço indicado.

JMP 00401008h

JZ endereço / JE endereço
Faz um salto condicional. Caso o valor da zero flag seja 1, ele realiza o salto.
Normalmente utilizado junto com um CMP para realizar um desvio caso a comparação
seja verdadeira.

JE 0040101Ah

JNZ endereço / JNE endereço


Semelhante ao item acima, mas realiza o salto somente quando a zero flag não foi setada
(ZF = 0).

JNZ 0040102Ch

ADD destino, arg1


Adiciona o valor de arg1 ao destino. Também possui diversas variações, pelas mesmas
razões do comando MOV. Se o resultado estourar o limite do destino, a CF é setada.

ADD EBX, 04h


ADD EBX, DWORD PTR DS:[00403032]

SUB destino, arg1


Realiza uma subtração dos operandos. As variações e características são as mesmas do
comando ADD.

SUB ECX, 2Ah

PUSH valor
Coloca o valor no topo da pilha (Stack). O comando PUSH é amplamente utilizado nas
chamadas de funções (CALL), pois é através da pilha que a função busca seus
argumentos.

PUSH 08h

POP destino
Remove o valor do topo da pilha e o armazena no destino.

POP EAX

CALL local
Faz chamada a uma função. É possível passar o local de diversas formas para o comando
CALL, desde uma constante, registrador ou até mesmo uma função externa dentro de
uma DLL. O comando CALL usa a pilha para indicar o endereço para o qual a função deve
retornar depois de finalizada a sua execução.

6 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

CALL User32!GetDlgItemTextA
CALL 0040115Fh

Essas são as instruções mais comuns dentro de um binário compilado. Claro que existe mais de
uma centena delas, mas eu procurei colocar aqui apenas aquelas que serão utilizadas no
aplicativo de aprendizado. Para uma lista completa com uma explicação mais profunda dos
opcodes, recomendo ver a lista apresentada neste endereço:
http://www.numaboa.com.br/informatic...ncias/opcodes/

O próximo capítulo cobrirá a parte da apresentação da interface do OllyDbg para depois


podermos realmente colocar a mão na massa e analisar um binário.

Comentários/críticas/sugestões são bem vindas, claro (não me importo que as partes não
fiquem em sequência).

Abraços!
Fergo
23-09-2007 por philix
Ô cara!! Para que vc faz um tutorial tão bom?! Vou ter que parar meu serviço para ler agora!
24-09-2007 por Etinin
Nota 10 como praticamente todos seus tutos anteriores, Fergo!
25-09-2007 por Fergo
Valeu pelo suporte pessoal! Aí vai mais um pedaço

INTERFACE DO OLLY DEBUGGER


Após uma boa parte teórica, chegou a hora de por em prática aquilo que acabamos de estudar.
Neste capítulo vou apresentar um pouco da interface do OllyDbg, que apesar de intuitiva,
merece esclarecimentos.

Para o nosso estudo, eu criei um simples aplicativo que iremos depurar mais a frente. Ele é
necessário neste já neste capítulo (não para depuração, mas para a apresentação dos itens do
Olly). Você pode baixar o arquivo executável juntamente com o seu código fonte (programado
em assembly na sintaxe MASM32 utilizando o WinAsm Studio como IDE) no link abaixo:
http://www.fergonez.net/files/adivinhe.rar

O OllyDbg pode ser baixado gratuitamente através do site http://www.ollydbg.de. É bem


pequeno e não necessita instalação, basta extrair o conteúdo para uma pasta qualquer.

Após extraído, abra o Olly, vá em “File->Open” e abra o nosso arquivo de estudo


(adivinhe.exe). Rapidamente o Olly vai interpretar o arquivo e mostrar o disassembly na janela
principal. Vamos deixar essa parte mais frente, já que o objetivo deste capítulo é apenas
mostrar a interface, sem depurar o aplicativo por enquanto. A tela deve ser semelhante a essa:

7 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

A interface do aplicativo é composta por poucos botões. O segredo do Olly é o botão direito do
mouse. A maioria das funções existentes no aplicativo pode ser acessada através do botão
direito do mouse, sendo que os itens exibido no menu de popup variam de acordo com o local
onde foi dado o clique (dependendo da coluna e da região).

Eu numerei as principais regiões da tela de 1 a 4.

Região 1

Esta é a tela principal do programa, onde é apresentado o disassembly do aplicativo. Ela é


dividida em quatro colunas:

Coluna 1 – Address. Ela nos mostra o endereço virtual das instruções (para saber mais
sobre esse endereçamento, veja meu artigo sobre o funcionamento dos executáveis).
Você pode reparar que os endereços não são em intervalos iguais para cada instrução.
Isso ocorre devido ao fato de que o tamanho das instruções ser variável, como podemos
observar na segunda coluna.

Coluna 2 – Hex Dump. Aqui temos o código da instrução no seu formato hexadecimal (a
cada 2 caracteres, temos 1 byte). São esses valores que ficam armazenados dentro do
arquivo executável e que são passados para o processador. Como mencionado no
parágrafo anterior, as instruções variam de tamanho, sendo que o endereço da próxima
instrução é dado pelo endereço da instrução atual mais a soma dos bytes da instrução.
Veja o exemplo do nosso aplicativo de exemplo. Ele começa no endereço 00401000
(padrão do Windows) e a sua primeira instrução é composta por 2 bytes (6A 00). O
endereço da próxima instrução (na linha de baixo) vai ser o endereço atual somado com
o tamanho da instrução (00401000+2) = 00401002.

Coluna 3 – Disassembly. Essa coluna nada mais é do que a interpretação e a tradução

8 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

para assembly das instruções presentes na segunda coluna. A análise do aplicativo é feita
quase que inteiramente nela.

Coluna 4 – Comments. Essa coluna não influencia no aplicativo, ela é utilizada apenas
para comentários e informações. O Olly a utiliza para identificar as chamadas de função
juntamente com os seus argumentos (você pode ver que ele identifica as chamadas da
API do Windows em vermelho e lhe mostra os argumentos, facilitando e muito a
interpretação).

Região 2

Essa área mostra todos os registradores e flags que nós vimos anteriormente (juntamente com
diversos outros valores). A cada instrução essa tela é atualizada, mostrando o estado atual de
cada um dos itens. Caso algum desses itens tenha sido modificado de uma instrução para outra,
o Olly as colore com outra cor (nesse caso é o vermelho). As flags são mostradas logo abaixo
dos registradores, abreviadas com a letra C (Carry Flag), Z (Zero Flag) e S (Signal Flag)

Região 3

Essa região nos mostra a memória física (RAM) destinada ao aplicativo. É possível observar o
valor de cada byte de memória dentro do espaço reservado ao aplicativo. É composta por três
colunas:

Coluna 1 – Address. Mostra os endereços virtuais de memória.

Coluna 2 – Hex Dump. Este espaço contém o valor de cada byte da memória. Por padrão
o Olly coloca 8 bytes por linha e por essa razão a coluna de endereços cresce de 8 em 8
bytes.

Coluna 3 – ASCII. Essa coluna pode ser utilizada para exibir de formas diferentes os
valores contidos na memória. Por padrão o Olly opta por exibir a representação ASCII
desses valores. O modo de representação pode ser alterado utilizando o botão direito do
mouse.

Região 4

Mostra o estado atual da pilha (stack). Como visto anteriormente, a pilha é amplamente
utilizada durante as chamadas de função. O VisualBasic é uma linguagem que faz um uso
muito grande da pilha, principalmente pela quantidade de funções que são utilizadas pelo
aplicativo. Também é dividida em 3 colunas :

Coluna 1 – Address. Cumpre o mesmo papel das outras colunas de endereço. Nota-se
que o endereço cresce de quatro em quatro bytes, pois cada posição da pilha é ocupada
por um tipo DWORD (4 bytes)

Coluna 2 – Value. Valor armazenado naquele endereço da pilha

Coluna 3 – Comment. Utilizado para comentários e mostrar informações relevantes


sobre aquele endereço. O Olly identifica diversos itens da pilha (como endereços de
retorno) e adiciona essas informações na coluna de comentários.

Além das regiões nós temos a barra de ferramentas:

Abaixo uma descrição de cada botão, da esquerda para a direita.

Open - Abre o executável para depuração


Restart – Recarrega o aplicativo atual
Close – Fecha a aplicação carregada

9 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

Play – Inicia a execução e depuração do aplicativo. Caso nenhum breakpoint tenha sido
posicionado (veremos adiante), o programa será executado normalmente.
Pause – Pausa o aplicativo em andamento

Step Into – Caso um breakpoint tenha sido colocado em uma chamada de função, esse
botão lhe permite fazer a depuração do conteúdo dessa função.
Step Over – O contrário do item anterior. Ele simplesmente não “entra” dentro da
chamada (mas ainda assim a executa), continuando a depuração na próxima instrução.
Trace Into – Utilizado apenas quando está se faz backtracing. Ele registra as ações e
endereços em um log, registrando também o conteúdo das funções chamadas.
Trace Over – Semelhante ao item acima, mas não faz o registro do conteúdo das
chamadas.

Execute Till Return – Executa as instruções até encontrar o primeiro comando de


retorno (RETN).

Go to Address – Permite ao usuário especificar um endereço do código para visualizar.

Após os comandos básicos de depuração, temos os botões das janelas:

L – Show Log Window: exibe um log, no qual o Olly registra algumas ações como
carregamento de plugins, etc.
E – Show Modules Window: exibe todos os módulos e funções externas utilizados pelo
programa (DLLs). Com o menu direito é possível acessar uma gama de opções dentro
dessa janela (o mesmo vale para todas as outras janelas que forem mencionadas). Essa
janela de módulos é muito importante para configurar breakpoints nas APIs do Windows,
facilitando a aproximação em determinada região do código.
M – Show Memory Window: mostra o estado da memória que está sendo utilizado pelo
aplicativo, incluindo as seções do executável e tabelas de importação/exportação. Para
um detalhamento byte a byte da memória, deve se utilizar a região de memória física
mostrada na janela principal do aplicativo.
T – Show Threads: exibe o estado de cada thread contida no aplicativo. Em aplicações
multi-threading é possível, através dessa janela, ter um controle sobre cada uma das
threads.
W – Show Windows: mostra a estrutura e configuração das janelas carregadas pelo
aplicativo (definida pelo WinProc). Os dados só são mostrados com o programa em
execução e precisa ser atualizada manualmente pelo usuário (através do botão direito do
mouse).
H – Show Handles: exibe uma informação detalhada sobre os handles (referência a um
objeto) que estão sendo utilizados pelo aplicativo. Quando o aplicativo abre um arquivo,
é retornado um handle, que é utilizado para fazer a leitura e escrita, por exemplo.
C – Show CPU: janela padrão do aplicativo, que é aberta automaticamente na hora de
carregar o alvo. O seu conteúdo já foi explicado nos itens anteriores (onde as regiões
foram numeradas de 1 a 4).
/ - Show Patches: as modificações feitas no executável ficam registradas nessa janela,
facilitando a modificação ou o retorno à instrução original.
K – Show Call Stack: mostra uma pilha de todas as chamadas de função até então
feitas pelo aplicativo.
B – Show Breakpoints Window: exibe todos os breakpoints setados no programa alvo.
R – Show references: exibe todas as referências encontradas durante uma busca (seja
ela uma constante, instrução, string). Veremos mais sobre elas adiante.
... – Run Trace: nessa janela é mostrado o resultado da operação de tracing
(mencionada quando falamos de Trace Into/Over). Tracing é um processo um pouco
complicado, por isso a sua explicação detalhada será apresentada mais a frente (é
possível encontrar uma boa explicação na própria ajuda do Olly).
S – Show Source: quando o aplicativo alvo é compilado com as informações de debug,
normalmente o código assembly resultante também é armazenado. Nesse caso essa
janela exibe esse código e mostra em tempo real o local no código fonte original que está
sendo executado no momento. Muito útil para comparar o código assembly escrito e o
compilado.

Por último temos os botões de configuração e ajuda.

10 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

Debugging Options – Exibe a janela de configuração do Olly. A princípio não é


necessário alterar nada para fazer a depuração, a não ser que você tenha noção de onde
está mexendo.
Appearence – Permite configurar o esquema de cores. Recomendo alterar essas
configurações para algo que lhe agrade, fazendo um “syntax highlighting” do código,
facilitando a leitura.
Help – Mostra a janela de ajuda do Olly. A ajuda está em Inglês.

Estas foram as opções contidas na barra de ferramentas. O Olly também conta com um menu
tradicional, que contém basicamente as mesmas funções da barra de ferramentas. Um dos itens
do menu que vale a pena mencionar é o de plugins. O Olly suporta a criação de plugins, sendo
alguns deles muito úteis. Por padrão ele vêm apenas com dois plugins, um de bookmarks e
outro de comandos, que adiciona uma caixa de texto no rodapé do programa onde você pode
entrar com ações e comandos de forma semelhante ao SoftICE.

No rodapé do Olly fica uma pequena barra de status, que além de mostrar o estado atual do
alvo (finalizado, pausado ou em execução), serve como um informativo.

No próximo capítulo pretendo colocar alguns conceitos importantes de depuração.

Abraços,
Fergo
26-09-2007 por luishoc
Fergo parabéns por criar um tutorial tão exepcional como esse.
Tópico bem explicativo.
Falou!
[]'s
luishoc
26-09-2007 por rhfpt
voce tem algum site para que possamos baixar esse tutorial?
26-09-2007 por philix

Citação:

voce tem algum site para que possamos baixar esse tutorial?

O site dele que está na assinatura dele.


26-09-2007 por Fergo
Valeu pelas respostas pessoal, fico grato.
Por enquanto estou colocando o tutorial apenas aqui no GdH, talvez quando eu terminar eu
lanço um PDF dele. Mas como o philix mencionou, você encontra meus outros tutoriais no meu
site (www.fergonez.net).

Abraços!
Fergo
27-09-2007 por rhfpt
ops... nao havia percebido..

vlw. Muito bom esse tutorial. Parabéns


27-09-2007 por Fergo
Valeu rhfpt.
Amanhã tenho prova na faculdade então não sei se consigo escrever uma continuação do guia
hoje, mas tentarei.

Abraços,
Fergo
27-09-2007 por intruso
Muito bom o tópico, parabéns. :-)
28-09-2007 por Fergo
Valeu galera!

11 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

Quarta parte saindo do forninho. Desta vez fala de como encontrar aquilo que você quer no
meio de tanta instrução.

FORMAS DE APROXIMAÇÃO
Uma das maiores dificuldades em debugging de baixo nível (em assembly) é identificar o local
onde se encontra aquele trecho de código de deseja analisar. Existem diversas manhas para
convergir ao local correto, sendo que vou citar duas das mais utilizadas.

A primeira delas consiste em buscar em buscar por strings. Na maioria dos casos todo o texto
presente em um aplicativo fica armazenado em uma string table (tabela de strings), cada uma
com seu número identificador. Normalmente quem decide o que vai para a tabela de strings ou
o que é referenciado diretamente no código é o compilador, por isso esse método nem sempre
é totalmente funcional, mas costuma ter bons resultados.

Certo, mas que strings devemos procurar? Nós queremos é encontrar o local onde é feita a
comparação do número digitado com o número correto. Se o número não for aquele que você
digitou, ele vai exibir uma mensagem, que contém um título e um texto. Isso é bastante
interessante, pois a provável lógica do programa é verificar pelo número digitado e caso ele
seja incorreto, nos mostrar a MessageBox. Se nós encontrarmos o local onde o texto é utilizado
pela MsgBox, sabemos que estamos pertos e um pouco adiante de onde foi feita a verificação.

Há uma maneira bem direta de descobrir o local onde está a chamada para a MsgBox, mas vou
focar mais no sistema de busca por string. Vamos lá. Entre com um valor qualquer (maior que
0 e menor que 21) e mande verificar. Provavelmente você recebeu uma mensagem semelhante
a essa:

Repare que ela é composta por um título e um texto. Que tal verificar se é possível buscar
esses textos dentro do OllyDbg. Para tal, na janela principal, sobre o disassembly do código,
clique com o botão direito e vá em “Search For -> All referenced text strings”. Isso fará
com que o Olly mostre uma janela contendo todas as strings que são referenciadas por algum
comando dentro do código. Note que no conteúdo da janela apareceram três itens:

Podemos observar que temos três referências ao texto mensagem, ocorrendo em diferentes
endereços. “Curiosamente” a string “Mensagem.” é o título da mensagem de texto que
recebemos ao entrar com um valor errado. Isso significa que encontramos 3 possíveis locais
onde a caixa de texto é exibida. Uma maneira fácil de descobrir qual das três é a verdadeira
(mais pra frente veremos que na realidade nenhuma delas é “falsa”, são apenas mensagens de
texto diferentes) é setando um breakpoint sempre a mensagem de texto for referenciada. Para
tal, clique com o botão direito em qualquer uma das linhas e selecione “Set Breakpoint on
every command”. Sempre que a mensagem for utilizada, o Olly vai pausar a execução e lhe
mostrar onde a execução foi congelada.

Com o breakpoint configurado, apenas digite novamente o número no aplicativo de teste (sem
fechar ou reiniciar o Olly). Assim que você clicar no botão, ao invés de exibir a mensagem de
número incorreto, o Olly vai pausar a execução e lhe mostrar o local onde a referência de texto
foi utilizada. Você deve ter parado aqui (linha marcada em cinza):

12 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

Foi bem como queríamos. Paramos bem no local onde o endereço da mensagem é colocado na
pilha para ser utilizada pela função MessageBoxA:

Por curiosidade, note que temos 3 chamadas para a função MessageBox. Pelo texto de cada
uma é possível identificar que a primeira é referente ao texto de quando você acerta o número,
a segunda (que nós estamos) é de quando você erra e a última é para quando você entra com
um valor fora do intervalo especificado. Isso explica também o fato de termos três referências a
string “Mensagem.”, pois ela é utilizada pelas três chamadas.

Como mencionado anteriormente, esse método nos faz convergir para um local além de onde
foi feita a comparação (pois a mensagem de texto é exibida somente depois que o valor é
verificado). Para encontrar a comparação a partir do local atual podem-se utilizar diversos
métodos. Alguns preferem simplesmente ir analisado o código acima da MsgBox “na mão” ou
fazer um backtrace, que consiste em analisar o código asm inversamente. Como esse aplicativo
é bem pequeno, fica fácil achar o local na marra, mas vou dar uma visão sobre o backtracing. O
Olly felizmente possui várias funções que ajudam na interpretação do código, sendo que vamos
utilizar as referências de salto para essa situação. Para chegar até a mensagem de texto, muito
provavelmente foi feito um salto, já que a provável lógica seria:

1. Adquire os dados digitados pelo usuário


2. Compara com o valor real
3. É igual?
4. Caso não seja igual, pule para ...
5. Caso seja igual continue/pule para outro local

Sabendo onde foi realizado o salto nos deixa mais próximo ainda do local da comparação. Como
o pulo foi realizado para mostrar a mensagem de texto, destino mais provável para o salto é
quando os dados da mensagem de texto começam a ser empilhados. Selecione a linha logo
acima da atual, onde tem o comando PUSH 0 (primeiro valor colocado na pilha). Note que o
Olly identifica esse local como sendo o alvo de um salto (veja na parte de baixo da região do
disassembly):

13 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

Basicamente ele está te dizendo que para chegar ao local atual, foi feito um salto no endereço
00401061. Podemos ir até esse local e verificar se esse salto realmente existe. Clique com o
botão direito sobre essa linha (no endereço 00401079) e vá em “GoTo -> JNZ from
00401061”. Isso nos levará diretamente para o local do salto:

Fomos levados até o endereço 00401061 onde realmente existe um salto (JNZ SHORT
adivinhe.00401079) e provavelmente estamos bem próximo do local da comparação.
Realmente estamos. Analise as linhas que antecedem o salto. Temos uma chamada a função
GetDlgItemInt (busca um inteiro contido dentro de um item da janela, que nesse caso é uma
caixa de texto) e o armazena em EAX (isso é padrão, todo retorno de função é em EAX). Em
sequida temos:

1. Compare EAX com 1


2. Se for menor, pule para 0040108F
3. Compare EAX com 14 (os números são em hex, logo 14h = 20 decimal)
4. Se for maior, pule para 0040108F
5. Compare EAX com 4
6. Se forem diferentes (JNZ/JNE), pule para 00401079

Creio que você já tenha sacado o que está ocorrendo. Ele está primeiramente verificando se o
número digitado está dentro do intervalo (20 >= X >= 1). Se eles estão no intervalo, nenhum
salto foi realizado, logo ele continua a execução. Logo após o valor digitado é comparado com o
número 4, e se eles forem diferentes, o programa pula para aquela mensagem de texto que
estávamos anteriormente. Que tal experimentar colocar o número 4 na caixa de texto do
programinha de estudo e ver o resultado? Bingo, encontramos o local da comparação e por
conseqüência, o número com o qual ele compara o valor digitado.

Esse código asm seria gerado basicamente por uma estrutura semelhante a esta, em um
pseudocódigo:

Código:
Declara variável inteira X;
X = Número contido na caixa de texto;
Se X < 1 ou X > 20 Então
Exibe mensagem de texto “Número Inválido”
Fim Se
Se X = 4 Então
Exibe mensagem de texto “Parabéns”
Caso Contrário
Exibe mensagem de texto “Você Errou”
Fim Se

Essa é uma das maneiras para localizar trechos de código em um debugger. É usado por muita
gente, sendo que esse exemplo que apresentei é um “clássico”. Outra forma, muito mais direta,
mas que exige um conhecimento da API do Windows é buscar pelas chamadas das funções das
APIs do Windows.

Supondo que o usuário tenha certa experiência em programação (seja em asm ou em C), ele
provavelmente conhece algumas funções do Windows, já que elas são necessárias para
qualquer aplicativo visual. Como a lógica desse programa se baseia em buscar e comparar um
dado digitado em uma caixa de texto, um usuário que já conheça um pouco da API sabe que é
necessário usar uma função do Windows para realizar esse processo. As duas funções mais
famosas que pegam dados de controles são: GetDlgItemText e GetDlgItemInt.

O Olly possui uma janela que mostra todas as funções utilizadas pelo programa, então podemos

14 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

verificar se existe uma dessas duas funções no aplicativo alvo. Para isso, clique no botão E (ou
use o atalho ALT+E) para abrir a janela de módulos. Vai ter uma breve lista, contendo na
primeira linha o próprio programa e nas outras as DLLs dependentes. Clique sobre a linha que
contém o nosso aplicativo (adivinhe) e vá em “View Names”. Isso exibirá uma lista com todas
as funções utilizadas pelo aplicativo.

Eis a lista de funções utilizadas:

Note que a função GetDlgItemInt foi utilizada, como buscávamos. Para descobrir o local onde
ela é usada pode-se utilizar o mesmo método de antes, clicando com o botão direito e
selecionando “Set breakpoint on every referecence”. Daí basta continuar a execução do
programa, digitar um número e clicar no botão. Quando o alvo for chamar a função, o Olly
congela e exibe o local onde será feita a chamada, que é logo acima de onde é feita a
verificação, como vimos anteriormente.

Eu particularmente prefiro este método sobre o das referências, por algumas razões:

Ele normalmente nos leva para uma região bem mais próxima da verificação e antes
dela. Utilizando as referências, você pode ser levado para um local muito além,
necessitando de muito backtracing.
Algumas referências de texto não aparecem na lista da string table, o que torna esse
método mais prático.
Não depende de mensagem de texto ou MessageBox, que nem sempre estão presentes
em todos os aplicativos.

Em breve a continuação!
Abraços,
Fergo
03-10-2007 por emtudo
cara nao posso deixar de te agradecer por este tutorial, eu queria ate viajar para aprender isso,
mas nunca sobrou "$"

esta de parabens!
05-10-2007 por junior Fire
Primeiramente quero dar parabéns pelo tutorial.

15 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

Estou acompanhando ele dês do inicio e quero sabe se você ira com ele até o fim.

T+
05-10-2007 por Fergo
Valeu pessoal!
Pretendo ir postando sim (até não sei se vai ter um final, posso ir sempre postando dicas sobre
o Olly e tudo mais). Tive 2 semanas corridas de prova na faculdade e não sobrou tempo para
muita coisa.

Fergo
05-10-2007 por emtudo

Citação:

Postado Originalmente por Fergo


Valeu pessoal!
Pretendo ir postando sim (até não sei se vai ter um final, posso ir sempre postando dicas
sobre o Olly e tudo mais). Tive 2 semanas corridas de prova na faculdade e não sobrou
tempo para muita coisa.

Fergo

Se for falta de agradecer e dizer que o tutorial esta muito bom, vai ter um fim sim, pq vamos
sempre agradecer por este excelente trabalho

Mais uma vez o trabalho esta excelente, estamos aguardando com paciencia vc ter um tempo
para postar mais, mas nao tenha presa, aguardandos assim vc pode postar com paciencia e o
trabalho vai ficar excelente como ja esta ficando...
20-10-2007 por Fergo
Valeu pelo apoio pessoal.
Finalmente arranjei um tempo livre e dei continuidade do estudo

RE-ASSEMBLY
O Olly além de ser um ótimo debugger, é um ótimo assembler. Com ele também é possível
editar o código em tempo real e observar as mudanças de comportamento. Todas as mudanças
que você realiza no código ficam salvas já janela de “Show Patches” (/). Vamos agora ver
como podemos modificar o código e re-salvar o nosso executável (muito útil para correção de
bugs).

Reabra o nosso arquivo no Olly, caso ele esteja fechado. Nos capítulos anteriores discutimos
um pouco sobre o funcionamento do nosso aplicativo de teste, então vamos agora fazer uma
pequena modificação no mesmo para que ele sempre exiba a mensagem de “Você acertou”.

Nós tínhamos uma seqüência de comparações seguidas pelos seus respectivos saltos. A
primeira verificava se o número era menor que 1, a segunda se ele era maior que 20 e a
terceira se ele era em si o número escondido (4). Existem diversas formas de fazer com que o
número seja sempre o correto. Vou listar algumas:

Alterar o código forçando que o valor 4 seja movido para EAX antes da comparação.
Anular o último salto (após o CMP EAX, 4), fazendo com que o fluxo do aplicativo siga
direto para a mensagem correta.
Desviando e forçando um salto para a mensagem correta logo na primeira comparação
realizada.

Existem outras formas ainda, mas vamos ficar com essas três, pois são as mais óbvias. Eu vou
utilizar o último método neste tutorial para exemplificar o processo.

16 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

O que basicamente nós vamos fazer é forçar um salto logo após a função GetDlgItemInt
diretamente para a região que chama a nossa mensagem de texto “Você acertou...”, como
mostra a imagem abaixo:

Para modificar o disassembly, basta clicar sobre a linha que deseja alterar e apertar a tecla
“Espaço”. Isso fará com que uma janela se abra com um local onde você possa indicar a
instrução que deseja colocar no local.

O nosso objetivo aqui é substituir o CMP EAX, 1 por um salto não-condicional até o local onde
os argumentos da nossa mensagem de texto começam a ser selecionados (no PUSH 0,
localizado no endereço 00401063).

Clique sobre a linha que contém o CMP EAX, 1 (no endereço 00401054) e pressione “Espaço”.
A seguinte janela se abrirá:

Na caixa de texto é onde você entra com a instrução em assembly que deseja substituir. Caso a
instrução que nós inserirmos seja menor (em bytes) que a instrução anterior, o Olly substitui
esses bytes restantes pela instrução NOP, que não realiza nenhuma operação, evitando assim
que fiquem resquícios e “lixos” do comando anterior (caso a caixa de seleção “Fill with NOP’s”
esteja selecionada, claro).

Altere o CMP EAX, 1 por JMP 00401063, como mostra a figura:

Em seguida, basta clicar em Assemble para confirmar a modificação. Você deve ter notado que
o Olly coloriu com vermelho aquilo que foi modificado. Note também que ele inseriu um
comando NOP após o salto, indicando que o nosso opcode do salto era 1 byte menor que o
comando anterior.

Você pode rodar o nosso aplicativo dentro do Olly e observar a modificação. Agora, com

17 de 18 19/08/2010 18:31
Utilizando um debugger - OllyDbg http://www.guiadohardware.net/comunidade/utilizando-debugger/785195/

qualquer valor que você entre (mesmo aqueles fora do intervalo), o programa vai exibir a
mensagem que desejávamos.

Como mencionado anteriormente, todas as modificações ficam armazenadas na janela de


patches, que pode ser acessada clicando no botão “\” ou através o atalho CTRL+P. Para alternar
entre a instrução modificada e a original, basta selecionar a modificação desejada na janela de
patches e apertar “Espaço” (ou através do botão direito -> “Restore original code”).

Para salvar o novo executável é bem simples. Na janela de dissassembly, clique com o botão
direito e vá para “Copy to Executable -> All modifications”. Uma pequena janela se abrirá
perguntando se você deseja copiar o código modificado. Selecione “Copy All”. Uma nova
janela, contendo todo o código modificado, será exibida. Clique com o botão direito sobre ela e
selecione “Save File”. Basta escolher o local e você terá um novo executável, contendo a
modificação realizada.

Espero dar continuidade em breve.

Abraços e bom final de semana,


Fergo
20-10-2007 por emtudo

Citação:

Postado Originalmente por Fergo


Valeu pelo apoio pessoal.
Finalmente arranjei um tempo livre e dei continuidade do estudo

RE-ASSEMBLY

Valeu, esta cada vez melhor, nao posso deixar de sempre agradecer
22-10-2007 por Fergo
Valeu emtudo!

Já foi liberada a versão 2.0 pré-alpha do Ollydbg, pode ser conferida no link:
http://www.ollydbg.de/

Ainda não é boa para ser utilizada em debugging, pois quase todas as funcionalidades ainda
estão desativadas, mas já da para perceber uma certa melhoria na análise do código.

Fergo

Guia do Hardware Melhores Tópicos

18 de 18 19/08/2010 18:31

You might also like