Você está na página 1de 4

Bom, j viram que eu meio que abandonei isso aqui n Resolvi que escrever em ingls seria mais vantajoso

pra mim (estou indo embora do BR em breve e seria uma forma de expor minhas habilidades/curriculo). De qualquer forma, hoje o meu camaradinha tuxtrack l do #dclabs na FreeNode me pe diu um help sobre stack overflow Como eu sei alguma coisa sobre o conhecimento dele, eu preferi explicar o EIP Ov erwrite (stack overflow) de uma forma diferente, talvez seja util pra mais algum ento a est: Explicando melhor as dvidas que surgiram: - Porque estamos vendo instrues como: 008048383 : mov %eax,04(%esp) 0x0804838a : mov %eax,(%esp) - Ao invs dos clssicos: push %eax (calcula um novo eax aqui) push %eax ??????? Resposta: Porque a cada push, na verdade, o ciclo de instrues executa um: movl %eax, (%esp) sub $-4, %esp Ento podemos ver claramente que isso ai MAIS lento do que eu fazer um nico mov l usando um endereamento de referencia, como acontece aqui: 008048383 : mov %eax,04(%esp)

Isso so otimizaes do gcc, tem opes especficas do compilador que produzem o codigo com pushs normal Hoje em dia a realidade j otimizao, ento importante trabalhar com elas.

Outra pergunta: - Porque voc falou que a call empurra o valor EIP+5 (eip == propria call)?? Resposta: - EIP no momento da execuo da instruo call, obviamente, aponta para o proprio en dereo de memria da instruo call. A instruo call tem 5 bytes (quando desviando para um endereo de memria (label), sendo 1 opcode + 4 bytes do endereo sendo chamado), ento o endereo da proxima instruo depois da CALL exatamente o Endereo da CALL + 5. Mais sobre isso no final do post UPDATE: <shinku> Nem toda call tem 5 bytes ;P (Conversa no IRC) verdade, quando uma call usa um registrador, ela tem outro tamanho, exemplo: 0: ff d0 call *%eax Num caso desse, obviamente, o endereo empurrado vai ser EIP_ATUAL+Tamanho_da_In str_CALL Mais info sobre CALL: http://home.comcast.net/~fbui/intel_c.html#call Agora sim, segue o que construi para minha explicao pro tuxtrack: 001 waKKu@0xcd80$ cat tux.c 002 #include <stdio.h> 003 004 void blah_function(char *buffer, int bleh) { 005 printf("%d\n", bleh); 006 } 007 008 int main(void) { 009 char *buf; 010 int nada;

011 012 blah_function(buf, nada); // Imagina que a funcao "blah_function " tem uma falha de overflow 013 return(1); 014 } 015 waKKu@0xcd80$ gcc -O0 -fno-stack-protector -z execstack -o tux tux.c 016 waKKu@0xcd80$ gdb ./tux 017 GNU gdb 6.4.90-debian 018 Copyright (C) 2006 Free Software Foundation, Inc. 019 GDB is free software, covered by the GNU General Public License, and you are 020 welcome to change it and/or distribute copies of it under certain condit ions. 021 Type "show copying" to see the conditions. 022 There is absolutely no warranty for GDB. Type "show warranty" for detai ls. 023 This GDB was configured as "i486-linux-gnu"...Using host libthread_db li brary "/lib/tls/i686/cmov/libthread_db.so.1". 024 025 (gdb) disass main 026 Dump of assembler code for function main: 027 0x0804836f <main+0>: lea 0x4(%esp),%ecx 028 0x08048373 <main+4>: and $0xfffffff0,%esp 029 0x08048376 <main+7>: pushl 0xfffffffc(%ecx) 030 031 # Inicio do prologo assembly (salvando um stack frame) 032 0x08048379 <main+10>: push %ebp 033 # Joga o valor atual do ebp pra pilha pra guardar 034 035 0x0804837a <main+11>: mov %esp,%ebp 036 # Copia o frame atual pro ebp, pra definir um novo frame "local" 037 038 0x0804837c <main+13>: push %ecx 039 # Empurra o valor de ecx que foi gerado antes do prologo para a pilha 040 # Nesse momento o inicio do nosso frame local foi alterado de ebp para e bp-4 041 # Aqui tambem termina o prologo 042 043 0x0804837d <main+14>: sub $0x24,%esp // Reservando nosso espao na st ack 044 0x08048380 <main+17>: mov 0xfffffff8(%ebp),%eax // ebp == base do n osso stack-frame 045 # (gdb) print /d (int)0xfffffff8 046 # $2 = -8 047 # Ou seja, (base stackframe) - 8 = posio da primeira variavel (char *buf), -8 pq o primeiro valor o "ecx" q foi salvo no prologo 048 # Lembra do layout da stack agora: [EBP][ECX][VAR1][VAR2] (ebp - 8 = VAR 1) 049 050 0x08048383 <main+20>: mov %eax,0x4(%esp) 051 # Copia o valor na posicao VAR1 (q ta em eax) para a posio do ESP+4 (segun do argumento pra funcao blah_function = int bleh) 052 053 0x08048387 <main+24>: mov 0xfffffff4(%ebp),%eax 054 # (gdb) print /d (int)0xfffffff4 055 # $3 = -12 056 # EBP - 12 => VAR2 057 058 0x0804838a <main+27>: mov %eax,(%esp) 059 # Copia a posicao do VAR2 para a pilha (isso o mesmo que push eax, mas o

mov mais rapido) 060 061 0x0804838d <main+30>: call 0x8048354 <blah_function> 062 # Aqui entra o conhecimento de "calling convention". O calling conventio n do linux diz que os argumentos das funes devem 063 # estar dispostos no ESP, de ordem inversa. ESP => ultimo argumento, ESP +4 => penultimo, ESP+8 => ante-penultimo .... 064 # Aqui tambem tem TODA a sacada do EIP Overwrite em Stack Overflows, lem bra como a instruo "call" funciona? 065 # Ela antes de direcionar o fluxo para a funo sendo chamada, ela EMPURRA ( push) o valor EIP+5 (eip == propria call), pra servir 066 # como base para a instruo "ret" quando a funo sendo chamada for retornar. E esse valor que queremos sobrescrever. 067 068 ######################################################## 069 # Vamos pular direto para a funo blah_function, como se estivessemos segui ndo o fluxo de execuo 070 ######################################################## 071 (gdb) disass blah_function 072 Dump of assembler code for function blah_function: 073 # Inicio do prologo 074 0x08048354 <blah_function+0>: push %ebp 075 0x08048355 <blah_function+1>: mov %esp,%ebp 076 # Fim do prologo 077 078 0x08048357 <blah_function+3>: sub $0x8,%esp // criando espao pro nos so stackframe local 079 0x0804835a <blah_function+6>: mov 0xc(%ebp),%eax 080 # Aqui onde a funo recupera os argumentos da pilha (calling convention), e la sabe que 081 # o argumento na posio ESP+12 => ARG2 (int bleh), ento ela copia esse ender eo para o eax 082 083 0x0804835d <blah_function+9>: mov %eax,0x4(%esp) 084 # Mas agora ela precisa chamar o "printf" pra exibir esse valor 085 # entao de novo a gente ve o calling convention em ao, e ela empurra o val or 086 # do ARG2 para a posio ESP+4 (segundo argumento do printf) 087 088 0x08048361 <blah_function+13>: movl $0x80484a8,(%esp) 089 # Aqui ela empurra o primeiro argumetno do printf (format string) 090 # (gdb) x/s 0x80484a8 091 # 0x80484a8 <_IO_stdin_used+4>: "%d\n" 092 093 0x08048368 <blah_function+20>: call 0x8048290 <printf@plt> 094 # Aqui ela chama o printf pra executar.. 095 096 0x0804836d <blah_function+25>: leave 097 # Leave um substituto para o epilogo, o mesmo que o contrario do prologo (recupera o stackframe): 098 # movl %ebp, %esp 099 # pop %ebp 100 101 0x0804836e <blah_function+26>: ret 102 # Aqui onde a magica acontece... Depois do "leave" ajustar o stackframe de volta, o valor atualmente no ESP 103 # o endereo que o "call" botou na pilha LAAAA antes de entrar na funo "blah_ function", e o que o "ret" 104 # faz o mesmo que uma instruo "pop %eip", ou seja, ele pega o valor que ES P aponta e poe como endereo

105 # da proxima instruo a ser executada. Numa condio normal isso devolveria o f luxo para a funo que chamou 106 # a "blah_function", que no nosso caso a main e a execuo continuaria norma l. 107 # O que fazemos pra conseguir um EIP Overwrite estourar a nossa area res ervada na stack at sobrescrevermos 108 # esse valor que o "ret" confia que ser a proxima instruo. Fazendo isso qua ndo o ret for executado 109 # o EIP vai ter o valor que nos colocamos l, e direcionar o fluxo pra ond e desejarmos... 110 111 End of assembler dump. 112 (gdb) 113 114 # Numa condio NORMAL, o EIP apontaria pra c (0x08048392), esse foi o valor que a "call" colocou na pilha 115 # antes entrar na "blah_function". 116 0x08048392 <main+35>: mov $0x1,%eax 117 0x08048397 <main+40>: add $0x24,%esp 118 0x0804839a <main+43>: pop %ecx 119 0x0804839b <main+44>: pop %ebp 120 0x0804839c <main+45>: lea 0xfffffffc(%ecx),%esp 121 0x0804839f <main+48>: ret 122 End of assembler dump. 123 (gdb) Mais sobre o funcionamento CALL + RET 01 <waKKu> 0x08048380 <main+17>: mov 0xfffffff8(%ebp),%eax 02 <waKKu> 0x08048383 <main+20>: mov %eax,0x4(%esp) 03 <waKKu> 0x08048387 <main+24>: mov 0xfffffff4(%ebp),%eax 04 <waKKu> 0x0804838a <main+27>: mov %eax,(%esp) 05 <waKKu> 0x0804838d <main+30>: call 0x8048354 <blah_function> 06 <waKKu> 0x08048392 <main+35>: mov $0x1,%eax 07 <waKKu> o CALL vai desviar o fluxo, mas antes vai salvar o endereo dela + 5 para a pilha 08 <waKKu> q seria exatamente aquele main+35 ali 09 <waKKu> quando a blah_function estiver terminando, ela executa: 10 <waKKu> 0x0804836d <blah_function+25>: leave 11 <waKKu> 0x0804836e <blah_function+26>: ret 12 <waKKu> ou seja, o "ret" vai pegar o endreo que ta na pilha (que aponta p ara main+35) e vai por no EIP 13 <waKKu> ai o EIP vai voltar pra main e continuar o fluxo depois da call Se tiverem mais dvidas, me avisem ai nos comentrios ou IRC que eu vou dando update no post ;) . Cya folks

Você também pode gostar