我正试图通过在IDA的帮助下对GNU创建的汇编代码进行逆向工程来学习汇编(英特尔语法(。我很难理解assembly中对内存地址的引用,如果有人能对下面的代码发表评论并解释实际发生的事情,我将不胜感激。
它是以下C程序:
#include <stdio.h>
int main(void)
{
char *input[20];
scanf("%s", &input);
printf("%s", input);
return 0;
}
使用英特尔语法的程序集:
push ebp
mov ebp, esp
and esp, -16
sub esp, 96 ; char *input[20]
; scanf("%s", &input)
lea eax, [esp+16] ; move the effective address of [esp+16] into EAX
mov [esp+4], eax ; &input
mov dword ptr [esp], offset aS ; %s
call _scanf
; printf("%s", input)
lea eax, [esp+10] ; move the effective address of [esp+10] into EAX
mov [esp+4], eax ; input
mov dword ptr [esp], offset aS ; %s
call _printf
mov eax, 0
leave
ret
问题1:当变量包含20时,为什么编译器从ESP中减去96?所有额外的字节是什么?编译器是如何得出这个数字的?
sub esp, 96
问题2:编译器为什么选择ESP+16?为什么不,例如,ESP+5或ESP+10?
lea eax, [esp+16]
mov [esp+4], eax
真的很感谢你的帮助。
sizeof(input)
可能是80,因为input
被声明为char *input[20]
,即指针数组20(每个4字节(。
这个特定的编译器(GNU不是编译器(在16个字节上对齐堆栈(即,将堆栈指针保持在16的偏移倍数(,并在prolog中同时为参数分配空间,而不是每次调用push
,然后add esp
来清理堆栈(请参阅_cdecl
调用约定(
编译器可以只使用8个字节作为参数(总共分配了88
个字节(,但88不是16的倍数,16的下一个倍数是96。
esp
是放置第一个参数的位置,esp+4
是第二个参数,esp+8
和esp+12
是用于对齐的填充,esp+16
到esp+96
是阵列的80个字节。
CCD_ 14或CCD_。
在一张纸上画出这一叠将大大澄清这件事。