You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

函数尾声中的LEA/POP指令作用是什么?基于《Hacking: The Art of Exploitation》汇编示例

结论

你的猜想完全正确,这段地址80491b8之后的代码是main函数的尾声部分,核心作用就是设置返回值、恢复调用前的寄存器和栈状态,最终返回上层调用者。


80491b8后代码逐行解析

  • 80491b8: b8 00 00 00 00 mov eax,0x0
    x86 32位C语言调用约定规定,整型返回值默认存放在EAX寄存器中。C标准明确要求如果main函数没有写显式的return语句,末尾会隐式返回0,这行就是实现这个规则。
  • 80491bd: 8d 65 f8 lea esp,[ebp-0x8]
    对应函数序言里的压栈操作:你在序言中先后push ebx、push ecx,因此当前栈帧布局为ebp指向旧ebp值,ebp-0x4存旧ebx,ebp-0x8存旧ecx。这里直接把栈指针ESP指向旧ecx的存储位置,为后续批量恢复寄存器做准备,比逐次调整ESP效率更高。
  • 80491c0: 59 pop ecx
    弹出栈顶的旧ecx值恢复到ecx寄存器,ESP自动+4,此时栈顶指向旧ebx的存储位置。
  • 80491c1: 5b pop ebx
    弹出栈顶的旧ebx值恢复到ebx寄存器,ESP自动+4,此时栈顶指向旧ebp的存储位置。
  • 80491c2: 5d pop ebp
    弹出栈顶的旧ebp值恢复到ebp寄存器,还原上层函数的栈基址,ESP自动+4,此时栈顶指向序言最开始保存的返回地址上方位置。
  • 80491c3: 8d 61 fc lea esp,[ecx-0x4]
    序言最开头做了16字节栈对齐操作and esp,0xfffffff0,修改了原始栈指针,这里用最开始保存在ecx里的原始栈相关值恢复原始ESP,确保返回上层调用者时栈指针完全正确,不会因为对齐操作出现栈偏移错误。
  • 80491c6: c3 ret
    弹出栈顶的返回地址赋值给EIP寄存器,程序跳回main的调用者(通常是libc的__libc_start_main函数)继续执行。

补充说明(对应你的TODO)

你标注的80491ab: 83 c4 10 add esp,0x10逻辑很明确:调用puts前你执行了sub esp,0xc + push eax,总共让ESP减了16字节(0xc+0x4=0x10),cdecl调用约定要求调用者负责清理参数栈,所以调用完加16字节把栈恢复到调用前状态。

内容的提问来源于stack exchange,提问作者Duderus Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 23:45:02