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

C程序栈帧返回地址是否指向.text段及内存段范围问题

关于函数栈帧返回地址与32位Linux内存布局的解答

对返回地址所属段的说明

常规编译运行的x86 32位C程序中,call指令压入栈的返回地址,本质是call指令下一条待执行指令的地址,绝大多数场景下属于可执行代码段范围,但不一定是主程序的.text段:

  • 如果程序编译时未开启位置无关选项(-no-pie),且关闭ASLR,主程序自身的代码会固定加载,函数调用返回到主程序自身代码时,返回地址确实落在主程序的.text段范围内,该段权限为可读、可执行、不可写。
  • 如果程序开启PIE、或调用动态链接库函数、或做ROP/ret2libc利用时,返回地址可能落在动态链接器、libc等其他模块的可执行代码段,不属于主程序.text段范围。
  • 如果你做栈上shellcode注入,把返回地址改成栈上shellcode的起始地址,那返回地址就落在栈段,不属于任何.text段。

你遇到的覆盖异常现象,恰恰说明你找错了返回地址的位置:

  • 覆盖栈上保存的旧EBP值立刻崩溃是正常现象:函数收尾执行leave指令时,会把保存的EBP值加载回EBP寄存器,后续栈寻址、ret取返回地址都依赖EBP的值,EBP被篡改后会直接访问非法内存触发段错误。
  • 你覆盖自认为是返回地址的位置后,程序仅多执行一次循环就崩溃,说明你改到的是栈上存储的循环计数器、临时变量之类的数据,根本没到返回地址的存储位置——x86 32位栈帧中,保存的旧EBP位于[EBP]位置,返回地址在旧EBP的高地址方向,即[EBP+4]的位置,你大概率是往低地址方向找错了偏移。另外你探测到的0x1f7ffd属于极低地址范围,32位Linux下该区域是未映射的保留区域,根本不可能存储合法代码地址,这是你格式化字符串读内存时偏移计算错误,把栈上的普通数值(比如局部变量、短整数)误判成了地址。

32位Linux各段地址范围说明

你之前的记忆完全混淆了32位和64位架构的地址布局:0x7fffffff是64位用户态地址空间的上限,0x55555555是64位PIE程序的默认加载基址,和32位场景完全不匹配。32位Linux用户态地址空间总大小为3G,范围是0x00000000 ~ 0xbfffffff,高于0xc0000000的地址属于内核空间,用户态无法访问。各段范围根据安全机制开启状态分为两类:

场景1:关闭ASLR、编译为非PIE程序(入门CTF题常用编译配置)

内存布局从低地址到高地址固定:

  • .text段(主程序代码段):从0x08048000起始,后续紧跟.rodata、.data段
  • BSS段:位于主程序数据段后,大致范围在0x0804a000 ~ 0x080b0000区间
  • 堆:从BSS段结束位置开始,向高地址增长
  • 动态链接库(libc、ld-linux等):从0x40000000起始映射,各库的代码段、BSS段都落在该区间
  • 栈:从0xbfffffff开始向低地址增长,栈上所有地址都落在0xbfxxxxxx区间

场景2:开启ASLR、编译为PIE程序(现代Linux默认配置)

各段基址会随机化,仅能给出大致浮动范围:

  • 主程序.text、BSS、data段:浮动在0x56555000附近的随机偏移位置
  • 动态链接库:浮动在0xb7xxxxxx附近的随机范围
  • 栈:浮动在0xbfxxxxxx区间,仍从高地址向低地址增长
  • 堆、mmap匿名映射区域:分布在堆到动态库之间的随机地址范围

调试建议

定位返回地址最准确的方式是用gdb附加进程,在目标函数入口处下断点,等push ebp; mov ebp, esp两条栈帧建立指令执行完后,直接执行x/wx $ebp+4查看该地址存储的值,就是当前函数真实的返回地址,对照/proc/[进程pid]/maps里的内存映射表,就能直接确认该地址所属的内存段。

内容的提问来源于stack exchange,提问作者Nikolai Savulkin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:36:26