ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

GEF `pie` 命令组实战指南:让 PIE 二进制的动态断点自动就位

GEF `pie` 命令组实战指南:让 PIE 二进制的动态断点自动就位 网络安全开发工具【免费下载链接】gefGEF (GDB Enhanced Features) - a modern experience for GDB with advanced debugging capabilities for exploit devs reverse engineers on Linux项目地址https://gitcode.com/gh_mirrors/gef/gef点击查看免费下载在 GDB 中调试位置无关可执行文件PIE, Position-Independent Executable时断点地址无法在加载前静态确定——每次运行都会因 ASLR 而变化。GEF 的pie命令组正是为此而生它引入PIE 虚拟断点机制允许你先声明基于基址偏移的断点再在pie run/pie attach/pie remote真正接管目标进程的瞬间自动把基址解析出来并把所有虚拟断点实例化为真实 GDB 断点。读完本文你将掌握pie breakpoint、pie info、pie delete、pie run、pie attach、pie remote六个子命令的完整用法并理解 GEF 在 gef.py 中实现这一机制的底层数据结构与调用链。一、为什么 PIE 二进制需要专门的断点命令对于开启 PIE 的可执行文件其.text段在链接时的虚拟地址通常从0x0或很小的值开始运行时由动态链接器在随机基址上加载。这意味着你在静态分析如反汇编器里看到的偏移量并不是可调试的绝对地址若直接用break按偏移下断点GDB 会把它当成符号或绝对地址处理行为不可靠而一旦进程跑起来真实的加载基址才从/proc/pid/maps中可查。GEF 的解法是把断点声明与断点生效解耦。pie breakpoint只登记一个待解析的断点描述一个给我基址我还你一个 GDB 命令的闭包真正执行b *baseoffset的时机推迟到pie run、pie attach或pie remote拿到基址之后。需要注意的前提要支持 PIE 断点必须使用完整的pie命令系列尤其是那些会拉起进程的pierun 类命令pie attach、pie run等。如果你混用了原生attach/runGEF 没有机会去解析基址并实例化这些虚拟断点。二、命令总览pie是一个前缀命令prefix command其语法为gef➤ pie (breakpoint|info|delete|run|attach|remote)对应源码中的 PieCommandregister class PieCommand(GenericCommand): PIE breakpoint support. _cmdline_ pie _syntax_ f{_cmdline_} (breakpoint|info|delete|run|attach|remote) def __init__(self) - None: super().__init__(prefixTrue) returnprefixTrue意味着pie本身不执行动作只负责把子命令分发到下面六个具体实现。三、pie breakpoint声明一个 PIE 断点用法gef➤ pie breakpoint OFFSET参数是相对于目标二进制基址的偏移量或者一个符号名。注意执行该命令后断点不会立即设置而是在你后续使用pie attach、pie run或pie remote真正接管进程时才解析出正确的基址并落地为真实断点。实现细节PieBreakpointCommand 的核心逻辑只有两行addr parse_address(args.offset) self.set_pie_breakpoint(lambda base: fb *{base addr}, addr)parse_addressgef.py负责把参数解析成整数如果是十六进制串直接按0x解析否则交给gdb.parse_and_eval因此符号名如main同样可用真正被保存的是一个lambda 闭包lambda base: fb *{base addr}——给我一个基址 base我返回一条 GDB 命令b *baseaddr。这个闭包连同偏移量一起被封进PieVirtualBreakpoint对象登记到会话状态中。源码里还有一段值得注意的分支如果此刻进程已经在运行is_alive()为真基址已经可以从gef.memory.maps中查到于是 GEF 会立即对所有 PIE 断点调用instantiate(base_address)落地不必等下一次pie run# When the process is already on, set real breakpoints immediately if is_alive(): vmmap gef.memory.maps base_address [x.page_start for x in vmmap if x.path get_filepath()][0] for bp_ins in gef.session.pie_breakpoints.values(): bp_ins.instantiate(base_address)也就是说对一个已 attach 的 PIE 进程追加pie breakpoint时断点是即时生效的。四、pie info查看 PIE 断点状态由于 PIE 断点在实例化之前不是真实断点info breakpoint看不到它们。pie info提供了专门的观察窗口行为类似 GDB 的info breakpointgef➤ pie info VNum Num Addr 1 N/A 0xdeadbeef三个列的含义列含义VNumVirtual NumberPIE 断点自身的编号用于枚举是pie delete的操作句柄Num关联的真实 GDB 断点编号尚未实例化时显示N/AAddr该 PIE 断点声明的偏移地址十六进制用法gef➤ pie info [VNum]VNum参数可省略省略时列出全部PIE 断点。实现见 PieInfoCommand当参数缺省时取gef.session.pie_breakpoints.values()全量输出Num列通过str(x.bp_num) if x.bp_num else N/A渲染——bp_num为0即表示尚未实例化。五、pie delete删除 PIE 断点gef➤ pie delete [VNum]按VNum删除指定的 PIE 虚拟断点不传参数时删除全部。实现见 PieDeleteCommand其delete_bp静态方法体现了删除的原子性——它先处理真实断点、再清理虚拟记录两者不会脱节for bp in breakpoints: # delete current real breakpoints if exists if bp.bp_num: gdb.execute(fdelete {bp.bp_num}) # delete virtual breakpoints del gef.session.pie_breakpoints[bp.vbp_num]即若该虚拟断点已经实例化过bp_num非零会先执行delete Num把真实 GDB 断点删掉然后才从会话字典中移除虚拟断点记录。六、pie run/pie attach/pie remote带 PIE 断点支持的进程接管这三个命令的行为分别与 GDB 的run、attach、remote一致但都在进程接管完成后多走一步解析基址并实例化所有 PIE 断点。只要存在 PIE 断点就应当始终用pie系列代替原生命令。pie run用法与run相同。其实现 PieRunCommand 的执行序列是通过get_filepath()确认已file加载了可执行文件且该文件具有可执行权限否则给出 warning 并返回临时set stop-on-solib-events 1并隐藏 context 输出执行run args拉起进程从gef.memory.maps中筛选出属于该可执行文件x.path get_filepath()的映射项取其page_start作为基址并打印base address 0x...对每个 PIE 虚拟断点调用bp_ins.instantiate(base_address)把闭包生成的b *baseoffset命令真正执行从而落地真实断点最后continue让程序直接跑向第一个断点。pie attach用法与attach相同。PieAttachCommand 先执行原生attach PIDattach 成功后 GDB 处于停止状态此时立即读取gef.memory.maps拿到基址逐个实例化 PIE 断点最后刷新一次context输出。pie remote用法与remote相同。PieRemoteCommand 在底层是调用 GEF 自己的gef-remote REMOTE而非裸target remote建立远程会话随后同样停止→取基址→实例化断点→刷新 context。从源码结构看远程场景下基址匹配用的是x.realpath get_filepath()而非本地x.path以适应远程目标与本地文件路径不一致的情况。七、底层机制PieVirtualBreakpoint与实例化流程PIE 断点的核心载体是 PieVirtualBreakpoint 类class PieVirtualBreakpoint: PIE virtual breakpoint (not real breakpoint). def __init__(self, set_func: Callable[[int], str], vbp_num: int, addr: int) - None: # set_func(base): given a base address return a # set breakpoint gdb command string self.set_func set_func self.vbp_num vbp_num # breakpoint num, 0 represents not instantiated yet self.bp_num 0 self.bp_addr 0 ... def instantiate(self, base: int) - None: if self.bp_num: self.destroy() try: res gdb.execute(self.set_func(base), to_stringTrue) or ... res_list res.split() self.bp_num res_list[1] self.bp_addr res_list[3]几个关键设计点set_func是闭包而非存储的命令。pie breakpoint把lambda base: fb *{base addr}存进去实例化时才拼接出真实命令并执行——这就是声明与生效解耦的具体落地bp_num 0表示尚未实例化实例化成功后从 GDB 的返回文本Breakpoint N at ADDR: ...中解析出真实编号与地址重复实例化是安全的instantiate开头会先destroy()旧的真实断点再重建所以进程重启如再次pie run后基址变化断点会被自动迁移到新基址上全局状态挂在会话管理器上。GefSessionManager 持有pie_breakpoints: dict[int, PieVirtualBreakpoint]VNum → 虚拟断点和pie_counter: int 1VNum 从 1 开始自增。旧 APIgef_get_pie_breakpoint(num)已被标记废弃gef.py推荐直接访问gef.session.pie_breakpoints[num]。完整生命周期可以概括为pie breakpoint OFFSET → parse_address(OFFSET) → PieVirtualBreakpoint(set_funclambda base: b *{baseoffset}, vbp_num, addr) → 登记进 gef.session.pie_breakpoints pie run / pie attach / pie remote → 接管进程并停在启动点 → 从 vmmap 中按可执行文件路径匹配 page_start 得到基址 → 对每个虚拟断点 instantiate(base) → 执行 b *baseoffset解析返回的 Num 与 Addr → continue / context程序在真实断点处停住八、测试用例印证仓库自带针对pie命令的测试模块 tests/commands/pie.py覆盖了三类关键行为pie breakpointpie info联动对测试目标default取符号main的偏移pie breakpoint offset后断言pie info输出的最后一行首列是1、尾列正是该偏移的十六进制pie delete有效性删除pie delete 1后pie info中不再出现该偏移pie run后断点真实命中pie run后断言程序确实以reason: BREAKPOINT停在main中且断点地址满足address offset offset——这个位掩码断言正好验证了真实断点地址是随机基址 固定偏移正是 PIE 断点语义的体现。测试目标default.out的编译参数可在 tests/binaries/Makefile 中看到default.out: EXTRA_FLAGS : -fstack-protector-all -fpie -pie即默认测试二进制确实是以 PIE 方式构建的测试结论可直接作为该机制在真实 PIE 程序上可用的依据。九、使用注意与适用限制必须成套使用有 PIE 断点时拉进程请始终走pie run/pie attach/pie remote而不是裸run/attach/target remote否则虚拟断点永远不会被实例化pie remote走的是gef remote它内部调用gef-remote建立连接因此 GEF 远程调试相关依赖如 gef-remote 说明中的工具链需要可用基址来源是内存映射表三个 run 类命令都依赖gef.memory.maps中属于该可执行文件的那条映射的page_start。若进程内找不到与已加载文件匹配的映射例如文件路径解析异常从源码结构看会直接抛出索引错误——因此请确保 GDB 里加载的是与目标进程实际运行的同一份二进制断点参数是偏移或符号pie breakpoint的参数经parse_address解析支持0x401234形式的偏移与符号名两种写法适用前提该机制面向目标二进制可执行文件本身是 PIE的场景基址解析针对的是可执行文件映射不包含对共享库内偏移断点的支持。小结GEF 的pie命令组用虚拟断点 延迟实例化的模式把 PIE 调试中最繁琐的查 maps、算基址、重下断点手工流程自动化了pie breakpoint负责声明偏移pie info/pie delete负责管理与清理pie run/pie attach/pie remote在接管进程的瞬间自动完成基址解析与断点落地。对于本地 attach、本地运行或 GEF 远程会话三种调试路径这套命令提供了一致的 PIE 断点体验其实现细节均可在 gef.py 的PieVirtualBreakpoint与Pie*Command系列类中逐行对照验证。赞分享网络安全开发工具【免费下载链接】gefGEF (GDB Enhanced Features) - a modern experience for GDB with advanced debugging capabilities for exploit devs reverse engineers on Linux项目地址https://gitcode.com/gh_mirrors/gef/gef点击查看免费下载相关推荐GEF entry-break 命令实战指南:自动定位最佳断点,让动态分析一步到位GEF entry break 命令实战指南:自动定位最佳断点,让动态分析一步到位 GEF 的 entry break 别名 start 命令解决的是动态调试中网络安全开发工具GEF name-break 命令详解为 GDB 断点命名让 stripped 二进制调试不再靠猜GEF name break 命令详解为 GDB 断点命名让 stripped 二进制调试不再靠猜 本篇基于 GEF 官方命令文档 name break网络安全开发工具GEF patch 命令实战在 GDB 中实时改写内存、注入 Shellcode 与动态 Patch 二进制GEF patch 命令实战在 GDB 中实时改写内存、注入 Shellcode 与动态 Patch 二进制 GEFGDB Enhanced Feature网络安全开发工具上一篇终极指南3步搞定node-fetch用户代理字符串配置轻松应对反爬虫挑战下一篇前端开发创新思维imagesLoaded启发的开发理念创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表