ARTICLE DETAIL

资讯详情

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

pwndbg nextproginstr 命令详解:单步追踪回到程序自身代码

pwndbg nextproginstr 命令详解:单步追踪回到程序自身代码 pwndbg nextproginstr 命令详解单步追踪回到程序自身代码【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg导读nextproginstr是 pwndbg 在 GDB 与 LLDB 调试器上提供的步进至指定位置系列命令之一其核心能力是让程序继续运行直到 PC程序计数器落入正在运行程序自身的可执行代码页为止。在分析恶意样本、追踪库函数调用链或做漏洞利用开发时你经常需要快速跳出 libc、vdso 等外部代码回到主程序二进制本身的代码段nextproginstr正是为此场景设计的一键式方案。读完本文你将掌握该命令的精确语义、底层实现原理、边界行为以及与之配套的兄弟命令体系。一、命令概览用法与参数nextproginstr是 pwndbg 命令体系中step_next_continue步进 / 下一步 / 继续类别下的命令定义于 pwndbg/commands/next.py。其官方命令文档位于 docs/commands/step_next_continue/nextproginstr.md。用法usage: nextproginstr [-h]该命令不接受任何位置参数唯一可用的选项是标准的帮助参数ShortLongHelp-h--helpshow this help message and exit命令语义官方描述为Breaks at the next instruction that belongs to the running program.即在下一条属于正在运行程序的指令处停下。所谓属于正在运行程序的指令从源码实现看指的是 PC 落入主程序二进制objfile的可执行execute内存页——而不是动态库如 libc、内核映射vdso/vvar等其它可执行区域。与同族的nextcall下一个 call、nextjmp下一个跳转、nextret下一个返回等按指令类型停靠的命令不同nextproginstr的判定依据是指令所属的内存映射归属与具体指令形态无关因此它在离开库代码、回到主程序这一场景下不可替代。二、核心语义什么算程序自身的代码要正确使用nextproginstr必须先理解它对程序代码的判定标准。判定逻辑集中在 pwndbg/aglib/next.py 的break_on_program_code函数中exe pwndbg.aglib.proc.exe() binary_exec_page_ranges tuple( (p.start, p.end) for p in pwndbg.aglib.vmmap.get() if p.objfile exe and p.execute )这里的关键条件有两个p.objfile exe内存页所属的映射对象必须是主程序可执行文件本身pwndbg.aglib.proc.exe()返回当前被调试进程对应的可执行文件路径。libc.so、ld-linux.so、vdso 等映射页不满足此条件。p.execute该内存页必须具有可执行权限在 vmmap 输出中表现为r-xp一类的权限位。只有同时满足以上两个条件的页区间start, end构成的闭区间集合才会被纳入binary_exec_page_ranges作为停靠目标。换句话说即使某个动态库的代码段也是可执行的它也不是程序自身的代码同理主程序二进制中不可执行的页如只读数据段.rodata也不在停靠范围内。这种设计使得命令的语义非常精确回到主程序二进制可执行代码段而不是笼统的回到任何可执行内存。三、底层原理break_on_program_code 的工作流程nextproginstr的完整调用链为nextproginstr() └─ dispatch_execution_controller(_nextproginstr) [pwndbg/commands/next.py] └─ break_on_program_code(ec) [pwndbg/aglib/next.py] ├─ 计算 binary_exec_page_ranges ├─ 检查当前 PC 是否已在程序代码中 ├─ 循环: break_next_ret single_step └─ 检查 PC 是否落入程序代码页3.1 命令入口与执行控制器在 pwndbg/commands/next.py 中命令注册如下async def _nextproginstr(ec: pwndbg.dbg_mod.ExecutionController): Execution controller for the nextproginstr command. await pwndbg.aglib.next.break_on_program_code(ec) pwndbg.commands.Command( Breaks at the next instruction that belongs to the running program., categoryCommandCategory.NEXT, ) pwndbg.commands.OnlyWhenRunning def nextproginstr() - None: pwndbg.dbg.selected_inferior().dispatch_execution_controller(_nextproginstr)值得注意的细节pwndbg.commands.OnlyWhenRunning命令要求被调试进程正在运行。如果程序尚未启动执行时会得到提示nextproginstr: The program is not being run.该行为由测试用例 tests/library/gdb/tests/test_commands_next.py 显式验证。执行控制器ExecutionController机制命令并不直接操作 GDB/LLDB 原语而是通过selected_inferior().dispatch_execution_controller(...)分发到异步控制器由 pwndbg 的调试后端统一调度单步与临时断点操作。这也是 pwndbg 支持 GDB 与 LLDB 双调试器的架构基础。3.2 前置检查PC 已在程序代码中在开始奔跑之前break_on_program_code会先检查当前 PCpc pwndbg.aglib.regs.pc for start, end in binary_exec_page_ranges: if start pc end: print(message.error(The pc is already at the binary objfile code. Not stepping.)) return False如果 PC 已经落在主程序二进制可执行页内命令不会执行任何步进而是打印红色错误提示The pc is already at the binary objfile code. Not stepping.并立即返回。这一保护性设计避免了两类问题无意义的空转如果 PC 已经在目标区域继续步进只会浪费时间防止误步进到错误位置测试 test_command_nextproginstr 中专门验证在main断点处执行nextproginstr输出正是这条错误信息且 PC 保持不变。3.3 主循环穿越 ret 与信号检查当 PC 不在程序代码中时命令进入主循环while pwndbg.aglib.proc.alive(): # Break on signal as it may be a segfault if pwndbg.aglib.proc.stopped_with_signal(): return False await break_next_ret(ec, including_currentTrue) await ec.single_step() for start, end in binary_exec_page_ranges: if start regs.pc end: return True return False每一步的含义存活检查proc.alive()为假进程退出则结束循环并返回False。信号检查stopped_with_signal()为真时立即返回False——程序可能在等待时触发了段错误segfault等信号此时不应继续盲目步进。这也是崩溃保护设计对应测试 test_next_command_doesnt_freeze_crashed_binary。break_next_ret(ec, including_currentTrue)这是提速的关键。它会在下一个返回类指令ret 族处临时停下见 pwndbg/aglib/next.py利用 ret 指令一步跨越整个函数栈帧的特性快速穿越库代码中的函数体including_currentTrue表示若当前指令本身就是 ret 则不额外步进。该函数内部还会循环调用break_next_branch在两个分支指令之间逐步跳跃直到遇到CS_GRP_RETcapstone 反汇编分组标记为止。ec.single_step()在 ret 之后执行一次单步真正越过返回指令PC 随即指向调用者caller的下一条指令——这一步往往就是程序自身代码的开始。落点检查每次单步后立即检查 PC 是否落入binary_exec_page_ranges命中即返回True。循环终止于三种情形成功回到程序代码返回 True、进程不再存活、发生信号异常。3.4 为什么使用 ret 而非逐指令单步从性能与正确性两个角度看性能从库函数内部逐指令sisingle step回到主程序可能要执行成千上万条指令而库函数总是以 ret 返回调用者直接跑到下一个 ret → 单步越过只需很少的步数。正确性ret 一定会把控制流交还给栈上的返回地址。对于回到主程序这一目标而言ret 是最短且最确定的路径。若中间经过多次嵌套调用break_next_ret的循环会逐层处理每次break_next_branch都会跳过中间的 call 体。四、典型使用场景4.1 在库函数内快速跳出到主程序这是该命令最直接的用途。假设你在puts位于 libc内部中断pwndbg bt #0 __GI__puts (str0x402004 hello) at ./stdio-common/puts.c:33 #1 main () at reference-binary.c:12此时执行pwndbg nextproginstr程序会继续运行穿越 libc 内部的全部代码直到回到main的调用点之后的第一条指令停下——期间无需手工设置断点也不会在沿途的其它库代码中停留。测试 test_command_nextproginstr 给出的完整验证流程与此一致在main断点停下执行nextproginstr→ 得到错误提示PC 已在程序代码中在puts断点位于 libc停下再次执行nextproginstr→ PC 回到main所在的内存页再次执行nextproginstr→ 再次得到已在程序代码中的错误提示PC 不动。4.2 追踪混淆 / 间接调用链在逆向工程中如果目标程序大量使用函数指针、PLT间接跳转或callback机制控制流会频繁在程序代码与库代码之间切换。nextproginstr让你可以只关心程序自身执行了什么指令自动过滤掉库内部的执行细节是分析reference-binary测试用参考二进制这类含有 libc 调用的样本时的常用手法。4.3 与其它next 系列命令的分工nextproginstr属于 pwndbg 的 step/next 命令族相关文档均位于 docs/commands/step_next_continue完整列表包括命令停靠条件nextcall下一个 call 指令可加 symbol 正则过滤nextjmp下一个跳转指令nextret下一个返回类指令nextproginstr下一条属于主程序二进制可执行页的指令nextsyscall下一个系统调用不跨越分支stepret通过步进方式抵达下一个返回指令stepsyscall下一个系统调用跨越分支stepover下一条指令越过当前调用stepuntilasm下一个匹配指定助记符 / 操作数的指令它们共享同一套执行控制器基础设施与测试框架见 tests/library/gdb/tests/test_commands_next.py 中的NEXT_COMMANDS参数化测试但判定逻辑各不相同可根据调试目标按需组合使用。五、边界行为与注意事项结合源码与测试使用nextproginstr时需要注意以下边界行为进程未运行输出nextproginstr: The program is not being run.OnlyWhenRunning装饰器触发见 test_next_commands_binary_not_running。PC 已在程序代码中打印The pc is already at the binary objfile code. Not stepping.不步进、不移动 PC。主程序 PIE / ASLR 场景由于判定基于 vmmap 中的objfile exe条件而非固定地址PIE 程序在不同加载基址下同样适用——只要内存页归属于主程序 objfile 且可执行即可。崩溃保护若在步进过程中进程收到信号如 segfault命令会停止并返回不会陷入死循环或冻结调试器。测试 test_next_command_doesnt_freeze_crashed_binary 还验证了把 PC 强制设置到不可反汇编地址如0x1234时命令不会卡死。多线程场景命令作用于selected_inferior()当前选中线程在多线程调试时停靠位置以当前线程的 PC 为准。不存在程序代码停靠点如果主程序二进制中不存在可执行页极端情况binary_exec_page_ranges为空元组命令会在进程存活期间持续步进直至进程退出或收到信号返回False。六、小结nextproginstr以内存页归属 可执行权限作为判定标准通过跑到下一个 ret → 单步越过 → 检查 PC的循环快速将控制流带回主程序二进制代码是 pwndbg step/next 命令族中专注于代码归属场景的实用工具。其实现位于 pwndbg/commands/next.py 与 pwndbg/aglib/next.py行为由 tests/library/gdb/tests/test_commands_next.py 与 tests/library/dbg/tests/test_commands_next.py 覆盖验证同时兼容 GDB 与 LLDB 后端。理解它的关键三句话不按指令类型停靠而按代码归属停靠使用 ret 加速穿越库代码PC 已在目标区域时不动作。掌握它与nextcall/nextret/nextsyscall的组合用法即可在复杂控制流的逆向与利用开发中高效定位程序自身代码。【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表