x86 asm - 12 байт, вычтенных из esp. Нужно всего 8
Я скомпилировал этот код с помощью gcc (gcc -ggdb -mpreferred-stack-border =2 -o demo demo.c) и декомпилировал его, чтобы посмотреть на сборку (я знаю, что он использует небезопасные функции, это было для упражнения в буфер переполнение):
#include<stdio.h>
CanNeverExecute()
{
printf("I can never execute\n");
exit(0);
}
GetInput()
{
char buffer[8];
gets(buffer);
puts(buffer);
}
main()
{
GetInput();
return 0;
}
Вот сборка для функции GetInput():
(gdb) disas GetInput
Dump of assembler code for function GetInput:
0x08048432 <+0>: push ebp
0x08048433 <+1>: mov ebp,esp
0x08048435 <+3>: sub esp,0xc
=> 0x08048438 <+6>: lea eax,[ebp-0x8]
0x0804843b <+9>: mov DWORD PTR [esp],eax
0x0804843e <+12>: call 0x8048320 <gets@plt>
0x08048443 <+17>: lea eax,[ebp-0x8]
0x08048446 <+20>: mov DWORD PTR [esp],eax
0x08048449 <+23>: call 0x8048340 <puts@plt>
0x0804844e <+28>: leave
0x0804844f <+29>: ret
End of assembler dump.
Вот сборка для функции Main():
(gdb) disas main
Dump of assembler code for function main:
0x08048450 <+0>: push ebp
0x08048451 <+1>: mov ebp,esp
0x08048453 <+3>: call 0x8048432 <GetInput>
0x08048458 <+8>: mov eax,0x0
0x0804845d <+13>: pop ebp
0x0804845e <+14>: ret
End of assembler dump.
Я установил точку останова в строке 13 (получает (буфер))
Из Main() я вижу, что значение ebp помещается в стек. Затем, когда вызывается функция GetInput(), адрес возврата также помещается в стек. После входа в функцию GetInput значение ebp снова помещается в стек. Теперь вот где я запутался:
0x08048435 <+3>: sub esp,0xc
Буферная переменная составляет всего 8 байтов, поэтому 8 байтов следует вычесть из esp, чтобы учесть локальную переменную буфера.
Стек:
(gdb) x/8xw $esp
0xbffff404: 0x08048360 0x0804847b 0x002c3ff4 0xbffff418
0xbffff414: 0x08048458 0xbffff498 0x00147d36 0x00000001
(gdb) x/x &buffer
0xbffff408: 0x0804847b
0x08048458 - это адрес возврата, 0xbffff418 - старое значение ebp, а 4 байта буферной переменной - в 0x0804847b, поэтому я предполагаю, что остальные 4 байта равны 0x002c3ff4. Но в стеке, похоже, есть еще 4 байта.
Итак, мой вопрос: почему вычитается 12 байтов, если требуется только 8 байтов? Зачем нужны 4 дополнительных байта?
Спасибо
1 ответ
Это из-за
mov DWORD PTR [esp],eax
Видимо, твой puts
а также gets
реализации требуют, чтобы аргумент был помещен в стек.
Значение [ebp-0xc]
на самом деле [esp]
вот почему dword
зарезервировано впереди.
Почему это так? Делать это таким образом более эффективно, так как вам не нужно pop
а также push
, но просто двигайся eax
на [esp]
Таким образом, вы сэкономите хотя бы одну инструкцию. Тем не менее, я думаю, что этот код прошел некоторую оптимизацию, потому что этот код является умным.