我了解堆栈框架。但如果我是对的,在调用函数之前,该函数的参数将被推入堆栈框架。
例如,
int main(void)
{
printf("hi everyone %d \n", 3);
return 0;
}
在这种情况下,在主堆栈框架中,“Hi everyone%d \ n”的地址和3应该被推,然后必须调用printf,如果我是对的。
但是当我使用gdb时没有这样的指令。
我正在研究字符串格式漏洞。但我上面写的内容并没有发生。我怎么了?
答案 0 :(得分:2)
对于您的简单程序:
#include <stdio.h>
int main(void)
{
printf("hi everyone %d \n", 3);
return 0;
}
编译为gcc -g -ansi -pedantic -Wall test.c -o test
(在使用gcc版本4.8.4的Ubuntu 14.04系统上),似乎printf
的参数正在寄存器中传递。在printf
命令上设置断点并进行反汇编会产生以下结果:
Dump of assembler code for function main:
0x000000000040052d <+0>: push rbp
0x000000000040052e <+1>: mov rbp,rsp
=> 0x0000000000400531 <+4>: mov esi,0x3
0x0000000000400536 <+9>: mov edi,0x4005d4
0x000000000040053b <+14>: mov eax,0x0
0x0000000000400540 <+19>: call 0x400410 <printf@plt>
0x0000000000400545 <+24>: mov eax,0x0
0x000000000040054a <+29>: pop rbp
0x000000000040054b <+30>: ret
End of assembler dump.
我们可以看到值3(在这种情况下作为文字被编码到指令中)开始移入%esi
寄存器,你的字符串的地址被移动到{{ 1}}注册。您可以通过查看内存来验证这一点:
%edi
另外,你可以检查堆栈和基本指针,你会发现在这个简单的程序中没有使用堆栈:
(gdb) x/16cb 0x4005d4
0x4005d4: 104 'h' 105 'i' 32 ' ' 101 'e' 118 'v' 101 'e' 114 'r' 121 'y'
0x4005dc: 111 'o' 110 'n' 101 'e' 32 ' ' 37 '%' 100 'd' 32 ' ' 10 '\n
由于(gdb) print $rbp
$4 = (void *) 0x7fffffffe460
(gdb) print $rsp
$5 = (void *) 0x7fffffffe460
和$rpb
都具有相同的值。
希望这会有所帮助。 -T。
答案 1 :(得分:1)
这篇文章介绍了GCC compiler can optimize code to replace some types of call with equivalent, but not identical, operations如何提供帮助。
您提供的示例将是此类优化的主要目标。
答案 2 :(得分:-1)
遵循__cdecl
调用约定(大多数编译器的默认值)https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl,我们可以看到编译器可能产生的内容。
首先,调用者创建一个堆栈帧。
push ebp
mov ebp, esp
上述代码的作用很简单。首先,它将ebp的当前值推送到堆栈,然后将堆栈指针的值移动到ebp。
现在它将函数的参数从右向左推送到堆栈。
push 3; The number
push ?; Placeholder for the pointer to the string
现在它调用该函数。
call ?; Placeholder for the address of printf
当函数返回时,我们通过简单地重置堆栈框架来清理堆栈
mov esp, ebp
并恢复ebp
pop ebp
只是看一下visual studio 2015为我制作的一些代码,我可以确认这个调用约定有变化,但这是堆栈帧的一般概念。