picoCTF Buffer Overflow 2:栈覆盖传递win函数参数原理咨询
picoCTF Buffer Overflow 2:栈传参的底层原理
要搞懂这个机制,得先吃透x86平台cdecl调用约定和栈帧切换的核心逻辑,这是整个溢出操作的基础。
1. 先明确两个关键规则
- cdecl调用约定:调用函数时,参数要从右往左压入栈;被调用函数用
ebp(栈帧基址寄存器)作为基准取参数,第一个参数在ebp+8,第二个在ebp+12(因为ebp本身占4字节,加上返回地址的4字节,所以第一个参数偏移是8)。 - 栈帧结构:每个函数执行时会创建自己的栈帧,结构大概是(地址从高到低,栈向下生长):
[高地址] → 函数参数 → 返回地址(当前函数执行完要跳回的位置) → 保存的旧ebp(上一个栈帧的基址) → 局部变量(比如题目里的缓冲区buf) [低地址]
2. 漏洞函数的栈布局与溢出过程
假设题目里的漏洞函数是vuln,它有一个缓冲区buf,存在ebp-0x40的位置(偏移0x40)。当我们输入超长数据时:
- 先填满
buf(用0x40字节的垃圾数据); - 接着覆盖
ebp位置的旧ebp值(再填4字节,内容无所谓); - 然后覆盖
ebp+4位置的返回地址,把它改成win函数的地址; - 继续输入的内容,就会存在返回地址的上方(更高地址),也就是
vuln栈帧里的ebp+8、ebp+12位置。
3. 跳转到win函数后的参数读取逻辑
当vuln执行到ret指令时,会做两件事:
- 把栈顶的
win地址弹出到EIP(程序计数器),让程序跳到win函数执行; esp(栈顶寄存器)加4,指向我们填在返回地址后面的第一个数据。
接下来win函数启动,执行栈帧初始化(prologue):
push ebp:把当前ebp(也就是我们刚才覆盖的旧ebp值)压入栈,esp减4;mov ebp, esp:把ebp设置成当前esp,作为自己栈帧的基址。
此时win的栈帧结构就和正常调用时完全一致了:
ebp+4:win的返回地址(就是我们填在返回地址后面的第一个垃圾数据,无所谓是什么);ebp+8:第一个参数(我们填在vuln的ebp+8位置的数据);ebp+12:第二个参数(我们填在vuln的ebp+12位置的数据)。
这样win函数就能通过ebp+8和ebp+12正确读取到我们传入的参数,完成执行。
简单说,我们就是通过溢出,在栈上伪造了一个win函数被正常调用时的参数布局,让win以为自己是被合法调用的,从而读取我们塞进去的参数。
内容的提问来源于stack exchange,提问作者יאיר צפוני
相关产品推荐
相关产品推荐

