Pergunta

void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   ret = buffer1 + 12;
   (*ret) += 8;//why is it 8??
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}

Acima de demonstração aqui:

http://insecure.org/stf/smashstack.html

Mas ele não está funcionando aqui:

D:\test>gcc -Wall -Wextra hw.cpp && a.exe
hw.cpp: In function `void function(int, int, int)':
hw.cpp:6: warning: unused variable 'buffer2'
hw.cpp: At global scope:
hw.cpp:4: warning: unused parameter 'a'
hw.cpp:4: warning: unused parameter 'b'
hw.cpp:4: warning: unused parameter 'c'
1

E eu não entendo o por que do 8 que o autor pensa:

Um pouco de matemática nos diz que a distância é 8 bytes.

Meu gdb despejo chamado:

Dump of assembler code for function main:
0x004012ee <main+0>:    push   %ebp
0x004012ef <main+1>:    mov    %esp,%ebp
0x004012f1 <main+3>:    sub    $0x18,%esp
0x004012f4 <main+6>:    and    $0xfffffff0,%esp
0x004012f7 <main+9>:    mov    $0x0,%eax
0x004012fc <main+14>:   add    $0xf,%eax
0x004012ff <main+17>:   add    $0xf,%eax
0x00401302 <main+20>:   shr    $0x4,%eax
0x00401305 <main+23>:   shl    $0x4,%eax
0x00401308 <main+26>:   mov    %eax,0xfffffff8(%ebp)
0x0040130b <main+29>:   mov    0xfffffff8(%ebp),%eax
0x0040130e <main+32>:   call   0x401b00 <_alloca>
0x00401313 <main+37>:   call   0x4017b0 <__main>
0x00401318 <main+42>:   movl   $0x0,0xfffffffc(%ebp)
0x0040131f <main+49>:   movl   $0x3,0x8(%esp)
0x00401327 <main+57>:   movl   $0x2,0x4(%esp)
0x0040132f <main+65>:   movl   $0x1,(%esp)
0x00401336 <main+72>:   call   0x4012d0 <function>
0x0040133b <main+77>:   movl   $0x1,0xfffffffc(%ebp)
0x00401342 <main+84>:   mov    0xfffffffc(%ebp),%eax
0x00401345 <main+87>:   mov    %eax,0x4(%esp)
0x00401349 <main+91>:   movl   $0x403000,(%esp)
0x00401350 <main+98>:   call   0x401b60 <printf>
0x00401355 <main+103>:  leave
0x00401356 <main+104>:  ret
0x00401357 <main+105>:  nop
0x00401358 <main+106>:  add    %al,(%eax)
0x0040135a <main+108>:  add    %al,(%eax)
0x0040135c <main+110>:  add    %al,(%eax)
0x0040135e <main+112>:  add    %al,(%eax)
End of assembler dump.

Dump of assembler code for function function:
0x004012d0 <function+0>:        push   %ebp
0x004012d1 <function+1>:        mov    %esp,%ebp
0x004012d3 <function+3>:        sub    $0x38,%esp
0x004012d6 <function+6>:        lea    0xffffffe8(%ebp),%eax
0x004012d9 <function+9>:        add    $0xc,%eax
0x004012dc <function+12>:       mov    %eax,0xffffffd4(%ebp)
0x004012df <function+15>:       mov    0xffffffd4(%ebp),%edx
0x004012e2 <function+18>:       mov    0xffffffd4(%ebp),%eax
0x004012e5 <function+21>:       movzbl (%eax),%eax
0x004012e8 <function+24>:       add    $0x5,%al
0x004012ea <function+26>:       mov    %al,(%edx)
0x004012ec <function+28>:       leave
0x004012ed <function+29>:       ret

No meu caso, a distância deve ser - = 5,certo?Mas parece que não está funcionando..

Por function necessidades 56 bytes para variáveis locais?( sub $0x38,%esp )

Foi útil?

Solução

Como joveha apontou, o valor da EIP salvo na pilha (endereço de retorno) pelo call instrução precisa ser incrementada por 7 bytes (0x00401342 - 0x0040133b = 7) a fim de ignorar a x = 1; instrução (movl $0x1,0xfffffffc(%ebp)).

Você está correto, 56 bytes são reservados para variáveis locais (sub $0x38,%esp), para a peça que falta é quantos bytes passado buffer1 na pilha está a salvo PEI.


Um pouco de código de teste e de assembly embutido me diz que o valor é magia 28 para o meu teste.Eu não posso dar uma resposta definitiva a razão por que não é de 28, mas eu diria que o compilador é a adição de preenchimento e/ou pilha canárias.

O seguinte código foi compilado com o GCC 3.4.5 (MinGW) e testado no Windows XP SP3 (x86).


unsigned long get_ebp() {
   __asm__("pop %ebp\n\t"
           "movl %ebp,%eax\n\t"
           "push %ebp\n\t");
}

void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   /* distance in bytes from buffer1 to return address on the stack */
   printf("test %d\n", ((get_ebp() + 4) - (unsigned long)&buffer1));

   ret = (int *)(buffer1 + 28);

   (*ret) += 7;
}

void main() {
   int x;

   x = 0;
   function(1,2,3);
   x = 1;
   printf("%d\n",x);
}

Eu poderia ter facilmente usado gdb para determinar este valor.

(compilado com/ -g para incluir símbolos de depuração)

(gdb) break function
...
(gdb) run
...
(gdb) p $ebp
$1 = (void *) 0x22ff28
(gdb) p &buffer1
$2 = (char (*)[5]) 0x22ff10
(gdb) quit

(0x22ff28 + 4) - 0x22ff10 = 28

(ebp valor + tamanho da palavra) - endereço do buffer1 = número de bytes


Além de Esmagamento De Pilha Para Diversão E Lucro, Eu gostaria de sugerir a leitura de alguns dos artigos que eu mencionei no a minha resposta a uma questão prévia de seu e/ou outro material sobre o assunto.Ter um bom entendimento de como exatamente este tipo de exploração de obras deve ajudar a você escrever mais código de segurança.

Outras dicas

É difícil prever o que buffer1 + 12 realmente aponta.O compilador pode colocar buffer1 e buffer2 em qualquer local na pilha de desejos, mesmo indo tão longe a ponto de não poupar espaço para buffer2 em tudo.A única maneira de saber realmente onde buffer1 vai é olhar para o montador de saída do seu compilador, e há uma boa chance de que ele iria saltar com as diferentes configurações de otimização ou diferentes versões do mesmo compilador.

Eu não testar o código na minha própria máquina, mas você tem tomado memória de alinhamento em consideração?Tente desmontagem o código com o gcc.Eu acho que um código de assembly pode dar-lhe uma maior compreensão do código.:-)

Este código imprime 1, bem como no OpenBSD e FreeBSD, e dá uma falha de segmentação no Linux.

Este tipo de exploração é fortemente dependente do conjunto de instruções da máquina em particular, e as convenções de chamada do compilador e do sistema operacional.Tudo sobre o layout da pilha é definido pela implementação, e não a linguagem C.O artigo assume o Linux x86, mas parece que você está usando o Windows, e seu sistema pode ser de 64 bits, mas você pode mudar o gcc para 32-bits com -m32.

Os parâmetros que você vai ter para afinar são 12, que é o deslocamento da ponta da pilha o endereço de retorno, e 8, que é de quantos bytes de main você quer pular.Como o artigo diz, você pode usar o gdb para inspecionar a desmontagem da função para ver (um) a distância que a pilha é empurrado quando você chamar function, e (b) o byte deslocamentos de instruções main.

A +8 bytes parte é o quanto ele quer a salvo PEI para o incrementada com.O PEI foi salvo, então o programa pode retornar para a última atribuição após o function está feito, agora ele quer ignorá-la adicionando 8 bytes para os salvos PEI.

Então, tudo o que ele tenta é para "pular" o

x = 1;

No seu caso é a salvo EIP irá apontar para 0x0040133b, a primeira instrução após function retorna.Pular a atribuição de que você precisa para fazer a salvo EIP ponto para 0x00401342.7 bytes.

É realmente uma "bagunça com RET PEI", ao invés de incluir um estouro de buffer exemplo.

E quanto a 56 bytes para variáveis locais vai, que poderia ser qualquer coisa que o seu compilador vem com o preenchimento, a pilha de canárias, etc.

Editar:

Isso mostra o quão difícil é fazer com que as sobrecargas da memória intermédia exemplos em C.O deslocamento de 12 de buffer1 pressupõe uma certa preenchimento de estilo e opções de compilação.GCC serão felizes para inserir a pilha de canárias hoje em dia (o que torna-se uma variável local que "protege" a salvo EIP), a menos que você diga a ele, não.Além disso, o novo endereço que ele quer pular a (o início da instrução para o printf chamada) realmente tem que ser resolvido manualmente a partir da assemblagem.No seu caso, na sua machie, com o seu sistema operacional, com o seu compilador, no dia....ele tinha 8 anos.

Você está compilando um programa em C com o compilador C++.Mudar o nome hw.cpp para hw.c e você vai encontrá-lo irá compilar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top