推送和更改%esp帧指针

Jul*_*ien 4 c assembly buffer-overflow

我有一个用C编写的小程序echo():

/* Read input line and write it back */
void echo() {
    char buf[8];  /* Way too small! */
    gets(buf);
    puts(buf);
}
Run Code Online (Sandbox Code Playgroud)

相应的汇编代码:

1 echo:
2 pushl %ebp                //Save %ebp on stack
3 movl  %esp, %ebp          
4 pushl %ebx                //Save %ebx
5 subl  $20, %esp           //Allocate 20 bytes on stack
6 leal  -12(%ebp), %ebx     //Compute buf as %ebp-12
7 movl  %ebx, (%esp)        //Store buf at top of stack
8 call  gets                //Call gets
9 movl  %ebx, (%esp)        //Store buf at top of stack
10 call puts                //Call puts
11 addl $20, %esp           //Deallocate stack space
12 popl %ebx                //Restore %ebx
13 popl %ebp                //Restore %ebp
14 ret                      //Return
Run Code Online (Sandbox Code Playgroud)

我有几个问题.

  1. 为什么%esp会分配20个字节?buf是8个字节,为什么额外12个?

  2. 返回地址就在我们推动%ebp正确的位置上方吗?(假设我们将堆栈颠倒,它向下增长)旧%ebp的目的是什么(当前%ebp指向的是第3行的结果)?

  3. 如果我想更改返回地址(通过输入超过12个字节的任何内容),它将改变echo()返回的位置.更改旧的%ebp(返回地址前的4个字节)的后果是什么?是否有可能通过更改旧的%ebp来更改返回地址或echo返回的位置?

  4. %ebp的目的是什么?我知道它的帧指针,但那是什么?

  5. 编译器是否有可能将缓冲区放在旧%ebp存储位置旁边的某个位置?就像我们声明buf [8]但它在第6行将它存储在-16(%ebp)而不是-12(%ebp)一样?

*c代码和程序集从计算机系统复制 - 程序员的Perspective第2版.

**使用gets()因为做缓冲区溢出

Dol*_*000 6

分配20个字节的原因是为了堆栈对齐.GCC 4.5+生成的代码可确保被调用者的本地堆栈空间与16字节边界对齐,以确保已编译的代码可以以明确定义的方式对堆栈执行对齐的SSE加载和存储.因此,在这种情况下,编译器需要丢弃一些堆栈空间,以确保gets/ puts获得正确对齐的帧.

本质上,这就是堆栈的外观,其中每一行是一个4字节的字,除了---表示16字节地址边界的行:

...
Saved EIP from caller
Saved EBP
---
Saved EBX       # This is where echo's frame starts
buf
buf
Unused
---
Unused
Parameter to gets/puts
Saved EIP
Saved EBP
---
...             # This is where gets'/puts' frame starts
Run Code Online (Sandbox Code Playgroud)

正如你可以从我梦幻般的ASCII图形中看到的那样,如果不是"未使用"的部分,gets/ puts会得到一个未对齐的帧.但请注意,未使用12个字节; 其中4个是为参数保留的.

编译器是否有可能将缓冲区放在旧%ebp存储位置旁边的某个位置?就像我们声明buf [8]但它在第6行将它存储在-16(%ebp)而不是-12(%ebp)一样?

当然.编译器可以自由组织堆栈,但感觉就像.为了可预测地执行缓冲区溢出,您必须查看程序的特定编译二进制文件.

至于EBP的目的是什么(因此回答你的问题2,3和5),请参阅任何有关如何组织调用堆栈的介绍性文本,例如维基百科文章.

  • @LeibnizMan:不,它是当前函数的*frame*指针. (2认同)