为什么" sub esp, 96"、"lea eax, [esp+16]"和"mov [esp+4], eax"?(ASM 英特尔语法)



我正试图通过在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+8esp+12是用于对齐的填充,esp+16esp+96是阵列的80个字节。

CCD_ 14或CCD_。

在一张纸上画出这一叠将大大澄清这件事。

最新更新