我正在尝试通过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
非常感谢您的帮助。
答案 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+4
和esp+8
的填充是对齐方式,esp+12
到{{1 }}是数组的80个字节。
esp+16
或esp+96
不适合,因为这不是被调用者期望找到参数的地方。
在纸上画一叠纸可以大大澄清问题。