Anda di halaman 1dari 25

c 

 

Ultimamente tenho visto 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. À 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).




1.V untrodução e conceitos


2.V Um pouco de assembly
3.V unterface do OllyDebugger
4.V Formas de aproximação
5.V Re-assembly
6.V Plugins

Mais capítulos em breve!

u c

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
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 uDEs 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, uDA,
WinDbg, SoftuCE 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

âccuucc

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:

yV rratamento 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).

yV 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.

yV 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.

å åu åu

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:

yV ï !åïc É 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.

yV 
"  Local de armazenamento temporário de dados
(são apagados ao desligar o computador). rodo aplicativo se
utiliza da memória para armazenar seus dados e estes são
buscados e gerenciados pelo processador.

yV   #
   

" É uma faixa de valores que
apontam para uma determinada posição de memória. roda 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.

yV ï$ % &' É 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 (LuFO ±
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.

yV     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.


cïcå(
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 utilizad a 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 operationalcode, 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, torna ndo a
linguagem de máquina mais legível. Veja abaixo um exemplo de um
mnemônico do comando MOV:

Código:
MOV EAX,1

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
untel na arquitetura uA32. É 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 ufs com múltiplas comparações, Switches,
For ou While. rudo é feito com comparações simples e saltos,
perdendo a sua linearidade (semelhante aos Go ro do BASuC).
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 va riá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 BASuCs 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:
voidmain() {
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:
00000000MOVEAX,4h ;move o valor 4 para EAX
00000005MOVEBX,6h ;move o valor 6 para EBX
0000000ACMP EAX,4h ;compara EAX com 4, se for verdadeiro: ZF = 1
0000000DJNE 00000019h ;se ZF != 1, pule para endereço 00000019h
0000000FCMP EBX,6h ;compara EBX com 6, se for verdadeiro: ZF = 1
00000012JNE 00000019h ;se ZF != 1, pule para endereço 00000019h
00000014MOVECX,5h ;move o valor 5 para ECX
00000019RErN ;finaliza execução e retorna
ï     c   cm
 css    s 
   cm     m ssm  
 scm
cm s   s  s çss ç s(mmcs)

s  s  s  m cs  c     ms
ms    s   m s s s  css s    
  32 s ss m scm   s  s  32 s
cm s:   C  ï ï    ï
 cmc msss  s  sss  m m
 ç    ms   s  scss! m s !s s s
!s cm  s  s       "s #c  
c   m c"  s  m s   m s  s  s
  $ % m s   ms (s  s    s%
!/mc)  &m  s   ï
 s sm 
m c s 
  ss%   c  s s ç s 
 m    ç   "m s ç     s    
s  cmm     c   m 
   m$s mç  m ç 

sss  s  s  ss s s  32 s '
m
m
ss ! s8 16 scmms 
   !cmm(  s
  s  s  sm
m):

ï cs ç8 s  s   mm(


ss8 smss cs mm)ss8
smss csï  ç16 ss !s
s16 smss cs ç32 s





m s  s  s m
m sm s F s  s
s !scm s  çs(   s 
m) s s ss  cm
   %s cc
m s ç s cm CMï      s  ss  s s ms
c   ss:*F (* F ) CF (C F )  F( F ) 
*F
 s sm   m  ç s  m !  ( m
cm ç   s &m s  
s  cm CMï s  
s s  s sm     s  s  *F cs  s  
s  ç s+ !  c  s  s)    CF
 s
  s   m  ç s     m%m
cm    s  /c sm cs    s ( ,)
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:
00401000MOV BYrE PrR 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 untel).

yV ) destino, origem

Move o valor do campo origem para o destino. Essa instrução


possui diversas variações, 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 PrR DS:[00403000]

MOV BYrE PrR DS:[00403002], 1Ch

yV åï 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

yV ®ï endereço

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


indicado.

JMP 00401008h

yV ® endereço / ® 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.

JE0040101Ah

yV ® endereço / ®  endereço

Semelhante ao item acima, mas realiza o salto somente


quando a zero flag não foi setada (ZF = 0).

JNZ0040102Ch

yV  destino, arg1

Adiciona o valor de arg1 ao destino. rambé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 PrR DS:[00403032]

yV c destino, arg1

Realiza uma subtração dos operandos. As variações e


características são as mesmas do comando ADD.

SUB ECX, 2Ah

yV ïc* valor

Coloca o valor no topo da pilha (Stack). O comando PUS) é


amplamente utilizado nas chamadas de funções (CALL), pois é
através da pilha que a função busca seus argumentos.

PUS) 08h
yV ïï destino

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

POP EAX

yV å local

Faz chamada a uma função. É possível passar o local de


diversas formas para o comando CALL, desde uma cons tante,
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.

CALL User32!GetDlgutemrextA

CALL0040115Fh

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.

u å(c

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 u tilizando o WinAsm
Studio como uDE) no link abaixo:

http://www.fergonez.net/files/adivinhe.rar
    s    m  
s  s
$://,,,  - m     css sç
s  c&  ms   

"s  %› › ›


 ss
 s ($).m   
   ms   sssm  + c #ms 
ss   ms   +%    + s c 
 s
ms     c sm     c      
s sm$ss:

  c  c


 cms    cs /s 
s   
      m s  m  s  ç/s
sscs css 
s  
m s s  s s    m      m 
c  cm  c     c  (  c  
  )

  m s cs  /s14



s
    c    m 
  s 
sssm c
m  c s:

yV

å 
 ±  ss  s ms    ç    s
s ç/s (  s   ms s  ss  çm +
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. usso ocorre devido ao fato de que o tamanho
das instruções ser variável, como podemos observar na
segunda coluna.

yV å + ± )exDump. Aqui temos o código da instrução no seu


formato hexadecimal (a cada 2 caracteres, temos 1 byte). São
esses valores que fic am 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 byt es 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.

yV å  , ± Disassembly. Essa coluna nada mais é do que a


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

yV å  - ± 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 APu do Windows em
vermelho e lhe mostra os argumentos, facilitando e muito a
interpretação).

 .+

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 (CarryFlag), Z (Zero Flag) e S (SignalFlag).

 .,

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
   sç s   c - cms    0s
c s:

yV

å 
± ssMs s çs  smm" 

yV

å 
 ± ) m s sç c
m     c
 mm" ï     cc 8 s  $ 
 ss !c  çsc sc8m8 s

yV

å 
±CIIssc s  !   
 ms ss scsmm" ï  
         sç CII sss  s 
m   sç  s    !  
 m s




Ms   s    $ (sc ) Cm s


  m  $
 mm !   s c$ms
  ç  #s sc
 m   m  ! m s m 
  $  cm     ç/s  s

!sc m
m
m3c s:

yV

å 
 ±  ss C m   msm  s   s
c s ç'-s  çc sc  
m   sscsç$
c   m
1.(4 s)

yV

å 
 ± #  #   m!    ç 
$

yV

å 
 ± Cmm 2!   cm% s  ms  
 mç/s ss   çc
 sss$(cm çs  )c
sss mç/sc cm% s


m s  /s "s ms      ms:



  msc çc s    

yV /- c %  ç;


yV ±.c  c ;
yV å ±Fc$cçc  ;
yV ï  ± Ic  c ç   ç  c Cs
$ m 3 $ s sc ( ms )
  ms %c  mm;
yV ï ±ï scmm;
yV  u  ± 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;
yV   0  ± 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;
yV  u  ± 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;
yV   0  ± Semelhante ao item acima, mas não faz o
registro do conteúdo das chamadas;

yV 1     ± Executa as instruções até encontrar o


primeiro comando de retorno (RErN);

yV   ± 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:

yV 2$3   43: exibe um log, no qual o Olly registra


algumas ações como carregamento de plugins, etc.
yV  2 $3   43: 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 APus do Windows, facilitando a
aproximação em determinada região do código.
yV 2$3
43: 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.
yV 2$3 $  : 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.
yV 4 2 $3 43 : 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).
yV *2$3* : 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.
yV å 2 $3 åïc: 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).
yV !$3ï $ : as modificações feitas no executável ficam
registradas nessa janela, facilitando a modificação ou o retorno
à instrução original.
yV ö2$3å &: mostra uma pilha de todas as chamadas
de função até então feitas pelo aplicativo.
yV  2 $3  &   43: exibe todos os breakpoints
setados no programa alvo.
yV 2$3 5   : exibe todas as referências encontradas
durante uma busca (seja ela uma constante, instrução, string).
Veremos mais sobre elas adiante.
yV  2    : nessa janela é mostrado o resultado da
operação de tracing (mencionada quando falamos de rrace
unto/Over). rracing é 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).
yV  2 $3   : 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.

yV      ± 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.
yV     ± Permite configurar o esquema de cores.
Recomendo alterar essas configurações para algo que lhe
agrade, fazendo um ³syntaxhighlighting´ do código, facilitando
a leitura.
yV *   ± Mostra a janela de ajuda do Olly. A ajuda está em
unglê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 SoftuCE.

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.

ï6u

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 stringtable (tabela de strings), cada um a 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. usso é
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.

)á 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:

.  
cms  m    m 4  
 c  s
 ss sc  sss s      ï 
 + cs sssm  c" c cm
    % m Ü $
 
 5
Iss %cm ms  m+csss  s
 s  cs    m cm    c"  '
c&+ c m 0ss:




ïms  s    ms  0s  0cs  


ms m c  m  s  çs C sm  
s  Ms m 
 ms m  c ms
    cm m     Iss s c  c ms 3
ssscsc
 2mm %c
sc   s  0s
   (ms    ms 
  $ m s
 s  s s ms s 
  s)
 s m 3 sm   ms m 
    c ï   c  cm     m
   m s $s  sc Ü 
/ 

  m    ms m   !  


 s c ç$ms  c çc 

Cm  3 c  s   m 


&m cs(smc$   c )ssm
c0cc    
s   ms m&m 
c      s   c ç  $ ms    c  
 0c    ! #c0        ($
m cmc!):


F m cm  ms ï ms m  c  
 ç  ms m
 cc  $   s  ! 
 çMss :


ï  c s   ms 3 c$ms     ç
Mss  ï   c m
 ss c   
 m 
       c0 c   &m  
s ( "ssms)
 c0 &m
 
c0 cm m       scc Iss
c m
m     ms  0s  0cs s  
Ms m s
 !s 0sc$ms
Cm mc   m ss m
 s !
c    mc
m   cm ç(s
ms m  
   sm s    

 c) ï  c    cm ç      c  
m-s !  ssm
s s  msmsm 
s  c"  cm  Ms   m    !  m
c3 c  css m s   c"  sm  sm
Cm ss c
 m   c %c c$   c 
m ms   mss  c3 c !m
ss  % s  ç/s + m    çc" s
 ms !  s  0cs  s   ss s ç ï 
c$   
  ms m   m   m   m
s+%  %" cs :

1V   ss s s % 


2V Cm cm  
3V - ?
4V Css+   
5V Css+ c /    c

    !  s s  ms  "m


ccm çCm  ! ms  
 ms m   s ms  %    s
 
s s  ms m   cmçm  s  m$s
c  $   cm     m  cm ï2) 0
( m    cc  $) '    c ss
c cm s    m s (+      
 sssm ):

scm  s%  !    c$    c
   ms  ç00401061ïms 
ss
c   c  s ss s m s C  cm  
 s  ss $ ( ç 00401079) % mÜ 


 
5 
!!! !"  Iss s  %  m    c 
s:


Fms s 
    ç 00401061  m
s m s ('* ).  $00401079)   m
sms m  "m  c  cm ç .m sms
s s $s  cm  s ms m c$m 
 ç#  ( sc m c  mm
 +  ss cs
 m c  )    m! m
 (ss
        ç
 m ) m s 
ms:

1V Cm cm1


2V  m   0040108F
3V Cm cm14(s&m ssm$ 14$520
cm)
4V  m   0040108F
5V Cm cm4
6V  m s('*/')  00401079

C  c0+%$sc s%c s%


 m m  c s  &m    s%   
  (20 65  65 1)  s s    $ m s
 ! c c ç( "s  

 cm  cm  &m  4  s s  m  s 
  m       ms m    s%ms
  m4  m cc &m 4c
    m$  s      s ?  
c ms  c  cm ç    cs 70c  &m 
cm cm   
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 unvá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 APu do Windows é buscar pelas chamadas
das funções das APus 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 APu 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:   u
 1
e   u
u .

O Olly possui uma janela que mostra todas as funções


utilizadas pelo programa, então podemos verificar se existe uma
dessas duas funções no aplicativo alvo. Para isso, clique no botão E
(ou use o atalho ALr+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 7) 3 
8. usso exibirá uma lista
com todas as funções utilizadas pelo aplicativo.


ss ç/s !s:



'    ç 8 ImI  ! cm
sc%msï sc  c
 s-s ! 
 msm m
  s cc cm     
sc  3    cc   s
c    c ç    m    m &m   cc  
 4      c$m    ç   c     
cs % c$m 
 cm 

 cçcmms  m
   c  m    s m
 s   s
 0cs  ms !/s:

yV   mm s    m   m ms  "m


 cçs2!s  0csc0
s     m c m  
m css  m 
c3 c 
yV  ms  0cs     cm  s 
s     ssm
ms %c
yV 'ms m Mss  m
sm s ssmsscs
  

  
m  s  m "m   
 m "m
ssm  Cmm
m
ss c" mm
! 
 s  sm çscm m ssm çs 
c0 !c" cmss+%+ $ $
ï$ (/)
#ms      cm ms mc   c"   -s  
ssc %(m & c ç s)

.   ss     cs  s+ c$ 's
c s   s sc ms m  c s    cm 
ss c  s  ms    !  m  
mcç  msm     sm     ms m 
#c0c  

'"s $ms m s 70c  cm ç/s s s s


s s scsss m  cs&m  m 
1s s m  20 c s m
s&m sc(4)sm ss ms! cm 
&m s+sm c # s  ms:

yV     c"   ç     4 s+ m   


scm ç
yV   &ms("sCMï4)!cm 
 cs   ms mc 
yV s   ç m s    ms m c   
 m cm ç !

sm  s msmsmsc cmsss 0s


s s s ms " s     !   &m m
 s
   mc  css

  scm "s ms ! 


  ç  m s  
"s   ç #  " " m       c$m
 ss ms m   #c0 c    cm ms  
m m :


ï  mc   sssm  s cc  s   $ 
s+         c Ü/%  Iss  % cm  m
+s cm mcc0ssc s ç 
s+cc c

ss  + 
s s   CMï 1  ms
-cc
cs msssms m
  cmçm  s  scs ( ï2) 0 c! 
 ç00401063)

C  s   $  c


m  CMï  1 (  ç
00401054) ssÜ/% s +s  %:


' c  
  c0   cm  s ç m
ssm   s+s s   Css ç  "s s  ms
s+m (m s) s ç  s s sss
s ss  s ç 'ï   ! $ m
 çssm  m s css cm
  (cscsç# $$9ï s+sc
c )

 CMï1 9Mï00401063cmms  :


m s  s cc  m ssm    c m  
mcç #c0        c   cm  m$
  mc'm
m s   mcm
'ï"ssc sscs  1 
m  cm  




Você pode rodar o nosso aplicativo dentro do Olly e observar a
modificação. Agora, com 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 s er acessada clicando
no botão ³\´ ou através o atalho CrRL+P. Para alternar entre a
instrução modificada e a original, basta selecionar a modificação
desejada na janela de patches e apertar 7 $#8 (ou através do
botão direito ->7    8).

Para salvar o novo executável é bem simples. Na janela de


dissassembly, clique com o botão direito e vá para
7å $ 1     :
5  8. Uma pequena janela se
abrirá perguntando se você deseja copiar o código modificado.
Selecione 7å $8. Uma nova janela, contendo todo o código
modificado, será exibida. Clique com o botão direito sobre ela e
selecione 70   8. Basta escolher o local e você terá um novo
executável, contendo a modificação realizada.

Após um tempo sem updates, resolvi criar u ma lista com os


plugins que considero indispensáveis para o Olly.

ïcu 

u 

* Ar4RE

Um dos melhores plugins já criados para o Olly. Ele traz para o


OllyDbg muitas das facilidades e funcionalidades disponíveis no uDA
(unteractiveDisassembler).


%

* Gigapede

Plugin extremamente útil quando está se trabalhando com


descompressão de executáveis, em que é necessário realizar um
dump do processo na memória (buscar os dados da memória e jogar
para o disco, simplificadamente). Funciona muito b em juntamente
com o umpRec (umportReconstructor), que re-alinha todo o
executável novamente.
 &

* S)aG

undispensável.Com esse plugin você pode rodar scripts dentro


do debugger, automatizando diversos processos e lhe salvando
tempo. É fácil encontrar sites contendo centenas de scripts, mas
recomendo este aqui:

http://www.tuts4you.com/download.php?list.53

ï 
V
*  Joe Stewart

Outra forma de automatizar o Olly, escrevendo scripts na


linguagem Perl. Para utilizar o plugin é preciso ter o ActivePerl
instalado.

å



* Gigapede

Quem já utilizou o bom e velho (e infelizmente extinto =()


SoftuCE certamente vai gostar desse plugin. rrata -se de uma caixa de
texto na qual você pode praticamente controlar o Olly através de
comandos que seguem a mesma sintaxe utilizada no SoftuCE (bpx,
bd, cpu, etc).

 

* arjuns

Com esse plugin você pode adicionar itens na barra de


ferramentas do OllyDbg. Muito bom para colocar atalhos para
Notepad, Calculadora, editor )exadecimal, editor de recursos. Dessa
forma você não precisa ficar perdendo tempo e navegar até a pasta
(ou menu iniciar) onde estão os aplicativos. Recomendo muito.

3

* henryouly

ralvez um dos plugins mais interessantes. O OllyFlow gera


gráficos e fluxogramas do disassembly para facilitar a identificação e
a interpretação do fluxo do aplicativo (funcionalidade presente no
uDA).
Por enquanto são esses. Mais para frente eu adiciono
eventuais plugins e deixo um aviso aqui neste tópico, notificando da
atualização.

Vou aproveitar esse tópico para colocar o meu scheme de


cores que criei e utilizo no Olly. Para utilizar, modifique o arq uivo

V adicionando as seguintes entradas (verifique pela seção
correta e altere o asterisco para o índice desejado):

Código:
[Colours]
Scheme[*]=0,12,8,18,7,8,7,13
Scheme name[*]=Fergo

[Syntax]
Commands[*]=15,4,10,10,9,10,112,13,111,8,12,0,0,0
Operands[*]=1,11,11,11,2,2,4,4,0,0,0,0,0,0
Schemename[*]=Fergo

Anda mungkin juga menyukai