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

时间:2018-11-28 14:06:33

标签: assembly x86

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

这是下面的C程序:

#include <stdio.h>

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

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

    return 0;
}

使用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

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

sub     esp, 96

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

lea     eax, [esp+16]
mov     [esp+4], eax

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:5)

4-2=2可能为80,因为sizeof(input)被声明为input,即,指针数组为20(每个4字节)。

这个特殊的编译器(GNU不是编译器)将栈对齐为16个字节(即,将栈指针保持在16的偏移倍数),并且在序言中一次为所有参数分配空间< / em>而不是char *input[20]每次调用它们,然后push清理堆栈(请参阅add esp调用约定)。
编译器仅可以使用8个字节作为参数(总共分配了_cdecl个字节),但是88并非16的倍数,下一个16的倍数是96。

88是第一个参数的位置,第二个esp是第二个参数,esp+4esp+8的填充是对齐方式,esp+12到{{1 }}是数组的80个字节。

esp+16esp+96不适合,因为这不是被调用者期望找到参数的地方。

在纸上画一叠纸可以大大澄清问题。