基于/proc/pid/maps与ptrace追踪子进程函数调用的问题求助
C程序函数调用Tracer问题解答
背景
我正在开发一款可追踪任意C程序函数调用的tracer,期望实现运行后显示被调用函数的进入信息及程序输出的效果。目前已完成以下工作:
- 解析
/proc/[child_pid]/maps文件 - 通过公式「可执行段地址 - 段偏移 + 符号本地地址(
Elf64_Sym->st_value)」计算符号全局映射地址 - 列出ELF64头中的可执行段符号列表
但在使用PTRACE_PEEKTEXT检测call指令(opcode 0xe8)、跳转后对比RIP与符号地址时遇到异常:跳转后的RIP未匹配对应函数地址,却仍能输出函数内信息,且toto和tutu函数跳转后RIP指向同一未知地址。
问题1:计算的全局地址是否正确?
公式本身是正确的,等价于内存基址 + (符号st_value - 段偏移)(可执行段地址即内存基址),但需要验证几个关键点确保结果正确:
- 确认选取的是可执行段:检查
/proc/[pid]/maps中对应段的权限包含x,同时匹配ELF程序头中p_flags含PF_X的段,取其p_offset和内存起始地址 - 校验符号归属:通过
Elf64_Sym->st_shndx找到对应段表项,确认段的sh_flags包含SHF_EXECINSTR,且段的sh_offset与程序头的p_offset一致,确保符号属于可执行段 - 手动交叉验证:用
objdump -d <目标程序>查看函数的本地偏移(即st_value),加上maps中可执行段的内存基址,手动计算全局地址,和程序计算结果对比,确认是否一致
问题2:若地址正确,为何未处于对应函数时仍能输出函数内信息?
核心原因是call指令检测逻辑存在漏洞:
- 仅检测
0xe8(相对地址call)会漏掉其他call指令格式:比如ff 15(RIP间接调用)、ff d0(寄存器间接调用)等,这些指令同样会进入函数,但不会被你的tracer捕获,导致你误以为是“未处于对应函数时输出”,实际程序已通过其他call方式进入函数 - 检测时机错误:
PTRACE_PEEKTEXT读取的是当前RIP指向的指令,若未在正确时机读取(比如进程停止时RIP已跳过call指令),会误读后续指令,导致未检测到call却看到函数内输出
问题3:为何toto和tutu函数跳转后RIP会指向main、toto、tutu之前的同一地址?
这种情况通常和以下因素有关:
- PLT链接表影响:如果目标程序是动态链接,即使是自定义函数,若编译时启用了延迟绑定或链接优化,调用可能先跳转到PLT条目,再转向实际函数地址。若你误将PLT入口当成函数实际入口,就会看到指向同一区域的地址
- call目标地址计算错误:
0xe8是相对地址call,目标地址计算公式为:目标地址 = 当前RIP + 4 + rel32(0xe8占1字节,后续rel32占4字节,当前RIP指向0xe8时,执行call后RIP会指向0xe8+5,而rel32是相对于RIP+4的偏移)。若你未加4或字节序处理错误(x86_64是小端序,需将读取的4字节rel32转为有符号整数),会得到错误的目标地址,若toto和tutu的rel32计算后指向同一地址,就会出现该现象
内容的提问来源于stack exchange,提问作者Julian52575
相关产品推荐
相关产品推荐

