为什么"sub esp,96","lea eax,[esp + 16]"和"mov [esp + 4],eax"?(asm intel语法)

Lav*_*nen -3 x86 assembly

我正在尝试通过GNU在IDA的帮助下创建的逆向工程汇编代码来学习汇编(Intel语法).我很难理解汇编中对内存地址的引用,如果有人可以评论下面的代码并解释实际发生的事情,我将非常感激.

这是以下C程序:

#include <stdio.h>

int main(void)
{
    char *input[20];

    scanf("%s", &input);
    printf("%s", input);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

使用Intel语法进行汇编:

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
Run Code Online (Sandbox Code Playgroud)

问题1: 当变量包含20时,为什么编译器从ESP中减去96?什么是额外的字节以及编译器如何结束该数字?

sub     esp, 96
Run Code Online (Sandbox Code Playgroud)

问题2: 为什么编译器选择ESP + 16?为什么不,例如ESP + 5或ESP + 10?

lea     eax, [esp+16]
mov     [esp+4], eax
Run Code Online (Sandbox Code Playgroud)

非常感谢您的帮助.

Mar*_*oom 5

sizeof(input)因为input被声明为char *input[20]ie和指针的数组20(每个4字节),所以可能是80 .

这个特殊的编译器(GNU不是编译器)将堆栈对齐16个字节(即将堆栈指针保持在16的偏移倍数)并在prolog中同时为参数分配空间而不是push每次调用它们然后add esp清理堆栈(参见_cdecl调用约定).
编译器可以只使用8个字节作为参数(88分配的总字节数),但88不是16的倍数,16的下一个倍数是96.

esp是其中将第一个参数,esp+4所述第二,esp+8以及esp+12是填充用于对准,esp+16esp+96都是80个字节的数组.

esp+5或者esp+10不适合,因为这不是被调用者期望找到参数的地方.

在一张纸上绘制堆栈将大大澄清问题.