Давайте сделаем несколько асм раньше:
Код
$ cat gets.c
int main(int argc, char **argv) {
char array[512];
gets(array);
}
Асм
$ gcc gets.c -o getsA.s -S -fverbose-asm
$ cat gets.s
....
.globl main
.type main, @function
main:
leal 4(%esp), %ecx #,
andl $-16, %esp #,
pushl -4(%ecx) # (1)
pushl %ebp # 2
movl %esp, %ebp #,
pushl %ecx # 3
subl $516, %esp #,
leal -516(%ebp), %eax #, tmp60
movl %eax, (%esp) # tmp60,
call gets # << break here
addl $516, %esp #, << or here to see the stack picture
popl %ecx # (3')
popl %ebp # (2')
leal -4(%ecx), %esp # (1')
ret
.size main, .-main
Пролог и эпилог (они с кодом выравнивания) подробно описаны здесь Понимание назначения некоторых операторов сборки
Макет стека:
(char) array[0]
...
(char) array[511]
(32bit) $ecx - pushed by 3 - it was the address on the stack of the eip which main will return to
(32bit) $ebp - pushed by 2
(32bit) $esp - pushed by 1 - change the $esp to the original value
Итак, если вы хотите изменить адрес возврата main, вам не следует менять адрес в стеке, который будет использоваться ret
, но также повторять значения, сохраненные в стеке, с помощью (1), (2) (3) толкает. Или вы можете встроить новый адрес возврата в сам массив и перезаписать только (3) новым адресом стека + 4. (используйте 516-байтовую строку)
Я предлагаю вам использовать этот исходный код, чтобы взломать его:
$ cat getss.c
f()
{
char array[512];
gets(array);
}
int main(int argc, char **argv) {
f();
}
потому что у f нет проблем с перестройкой стека
.globl f
.type f, @function
f:
pushl %ebp #
movl %esp, %ebp #,
subl $520, %esp #,
leal -512(%ebp), %eax #, tmp59
movl %eax, (%esp) # tmp59,
call gets #
leave
ret
.size f, .-f
Макет стека для f()
:
(char) array[0]
...
(char) array[511]
(32bit) old ebp
(32bit) return address
Точка останова при команде ret в f () с 520 байтами "A"
(gdb) x/w $sp
0xXXXXXa3c: 0x41414141