Cursos de Informática Grátis www.megainforcursos.com

20 de maio de 2012

Quarta parte saindo do forninho.

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


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


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 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,

0 comentários:

Postar um comentário

 
Design by Wordpress Theme | Bloggerized by Free Blogger Templates | coupon codes