C函数堆栈布局

我有一个看起来像这样的function:

int bof(char *str) { char buffer[12]; strcpy(buffer, str); return 1; } 

我试图覆盖其返回地址。 我发现我可以使用例如memcpy(buffer+24, "\x15\xf1\xff\xbf", 4) 。 我不明白的是为什么我需要访问buffer + 24 。 我对C内存模型的理解告诉我,执行这个函数时的堆栈应该是这样的

 bottom of top of memory memory buffer(12) sfp(4) ret(4) str(4)  top of bottom of stack stack 

这表明我应该将ret地址从buffer + 16开始。 额外的8个字节在哪里?

顺便说一下,我在32位系统上运行它。

检查ISO C 99和更早的标准,你会发现没有C内存模型这样的东西。 每个编译器都可以自由使用它喜欢的任何模型来满足function规范。 一个简单的例子是编译器可以使用或省略帧指针。 它还可以为返回值分配空间或使用寄存器。

当我在我的机器上编译这段代码时,我得到了

 _bof: pushl %ebp movl %esp, %ebp subl $40, %esp movl 8(%ebp), %eax movl %eax, 4(%esp) leal -20(%ebp), %eax movl %eax, (%esp) call _strcpy movl $1, %eax leave ret 

leal指令计算缓冲区起始位置为bp-20 。 在序幕之后,堆栈框架看起来像:

 [ bp+8: str ] [bp+4: rtn address ] [bp: saved bp] [ bp-20: buf ] ... 

所以看起来你必须在这里写28个字节来改变返回地址。 我的猜测是编译器试图在段落边界上对齐堆栈帧。 这里的完整帧包括strcpy arg设置是48个字节,这是3个段落。 段落对齐有助于总线和缓存性能。

这不是C内存模型或C函数调用堆栈布局。 它只是您对特定编译器和硬件架构所了解的实现。 在ARM 32位CPU中,函数调用参数1~4不会推送到堆栈,它使用r0~r3传递参数,然后如果参数超过4则将其他参数推送到堆栈。函数返回地址可能也没有必要推送到堆栈,因为有一个特殊的寄存器LR来保存返回地址。