这只是显示由 C 函数生成的汇编代码。如果您注释掉 C 行,并在其间添加一些换行符,那么发生的事情就会变得更加清晰。
// void myfun1(char *str) {
push ebp
mov ebp,esp
// char buffer[16];
sub esp,0x18 // Allocate space for buffer and function args.
// strcpy(buffer, str);
mov eax,DWORDPTR [ebp+8] // Load the str parameter into eax.
mov DWORD PTR [esp+4],eax // Set str as the second argument to strcpy.
lea eax,[ebp-16] // Load the address of the buffer into eax.
mov DWORD PTR [esp],eax // Set the address as the first argument to strcpy.
call 0x80482c4 <strcpy@plt> // Call strcpy.
// myfun2(buffer);
lea eax,[ebp-16] // Load the address of the buffer into eax.
mov DWORD PTR [esp],eax // Set the address as the first argument to myfunc.
call 0x80483b4 <myfun2> // Call myfunc.
// }
leave
ret
这与我通常期望生成的 C 代码有点不同。您通常会在调用函数之前将参数压入堆栈,而此代码已提前在堆栈上腾出空间,然后将参数移入堆栈。
为了更好地理解 ebp 和 esp 引用,它有助于构建堆栈的样子。
ebp+08 The str parameter
ebp+04 The return address
ebp+00 The saved copy of ebp <- ebp
ebp-04 Space for buffer
ebp-08 Space for buffer
ebp-12 Space for buffer
ebp-16 Space for buffer
esp+04 ebp-20 Second function argument
esp+00 ebp-24 First function argument <- esp
当函数被调用时,str参数被压入堆栈,加上调用代码的返回地址。然后该函数保存 ebp 的副本,并将 ebp 设置为指向堆栈上的该位置。最后,它为缓冲区(16 个字节)加上函数调用中使用的两个参数(另外 8 个字节)腾出空间。