x64dbg serun/sego 命令详解:吞掉异常并继续运行调试器
2026/9/19 14:48:34 网站建设 项目流程

x64dbg serun/sego 命令详解:吞掉异常并继续运行调试器

【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg

导读

serun(别名sego)是 x64dbg 调试器专用于异常场景的"运行"命令。当调试器因异常而中断(停锁)时,serun会在不通知被调试程序的情况下吞掉当前异常、跳过调试对象内的异常分发,并立即释放调试锁让程序继续运行。本文结合 关联文档 与 命令实现源码,完整讲解其用法、底层实现、与run/erun的区别,以及在反调试与异常分析中的典型实战场景。

一、命令速览

项目说明
命令名serun
别名sego
所属类别debug-control(调试控制)
注册位置src/dbg/x64dbg.cpp#L152:dbgcmdnew("serun,sego", cbDebugSerun, true); //run + swallow exception
功能定位吞掉当前异常 + 放行程序继续运行
是否设置结果变量

serun与同族的seStepInto(src/dbg/x64dbg.cpp#L157)、seStepOver(src/dbg/x64dbg.cpp#L160)共享相同的"se"(swallow exception,吞异常)前缀语义,是异常中断场景下"继续执行"一族命令的成员。

二、参数与结果变量

参数

serun [arg1] sego [arg1]
  • [arg1](可选):指定一个地址或表达式。当提供该参数时,x64dbg 会在该位置放置一个一次性(single-shot)断点,然后才开始运行。程序运行到该地址时自动停下,相当于"运行到此处"。
  • 若省略参数,则直接释放调试锁,程序无条件下继续运行,直到遇到下一个断点或异常。

结果变量

根据 serun.md 的说明,该命令不设置任何结果变量(如$result等),因此不能在脚本中依赖其返回值判断执行结果;判断执行是否成功应依靠后续的调试状态(如是否停到目标断点)或检查dbgisrunning()之类的状态。

参数内部处理

arg1参数并不是独立实现的"运行到地址"逻辑,而是复用断点命令:

  • 在 cbDebugRunInternal 中,当argc >= 2时执行:DbgCmdExecDirect(StringUtils::sprintf("bp \"%s\", ss", argv[1]).c_str())
  • 即内部调用bp <地址>, ss,设置一个一次性(single-shot,ss)软件断点后再放行程序。因此arg1支持 x64dbg 断点命令所接受的地址表达式语法。

三、底层实现:两步走

serun的命令处理函数 cbDebugSerun 实现非常简洁,仅两行:

bool cbDebugSerun(int argc, char* argv[]) { cbDebugContinue(argc, argv); return cbDebugRunInternal(argc, argv, history_clear); }

可以看出serun两步操作的组合

第一步:设置异常处理状态(吞掉异常)

调用 cbDebugContinue(即continue命令的处理函数)。由于serun通常不带参数调用,此时走argc < 2分支:

dbgsetcontinuestatus(DBG_CONTINUE); dputs(QT_TRANSLATE_NOOP("DBG", "Exception will be swallowed"));

dbgsetcontinuestatus(DBG_CONTINUE)将当前异常的继续状态设置为DBG_CONTINUE,表示"异常已被处理",调试器后续会把该异常当作已处理事件返回给系统,不再向被调试程序分发。这也正是文档中"swallowing the current exception, skipping exception dispatching in the debuggee"(吞掉当前异常、跳过调试对象内的异常分发)的实现位置。

第二步:释放锁并放行

调用 cbDebugRunInternal:

  1. 清空/记录历史(此处传history_clear,即清空历史);
  2. 若提供了arg1,先执行bp "<arg1>", ss设置一次性断点;
  3. 若程序已在运行则直接返回;
  4. 调用GuiSetDebugStateAsync(running)将 GUI 状态切为"运行中";
  5. 调用unlock(WAITID_RUN)释放调试等待锁,唤醒调试循环让程序继续执行;
  6. 触发CB_RESUMEDEBUG插件回调,通知各插件调试已恢复。

四、与 run、erun 的差异对比

在 cmd-debug-control.cpp 中,三个"运行"命令并排实现,语义差异非常清晰:

命令处理函数异常处理适用场景
run/go/r/gcbDebugRun不做特殊处理,仅跳过 INT3 单步(skipInt3Stepping普通运行,交由异常过滤器决定是否中断
eruncbDebugErun未运行时调用dbgsetskipexceptions(true)开启"跳过所有异常"模式想连续跨过后续所有异常直到目标
serun/segocbDebugSerun将当前这一异常标记为已处理(DBG_CONTINUE只吞掉当前这次异常,立即继续

关键区别在于:

  • erun是"跳过未来所有异常":通过dbgsetskipexceptions(true)设置全局跳过标志bSkipExceptions(见 debugger.cpp#L61、debugger.cpp#L2311),后续异常在(bSkipExceptions || filter.breakOn != ExceptionBreakOn::FirstChance) && (!maxSkipExceptionCount || ++skipExceptionCount < maxSkipExceptionCount)条件下不再停表,直到再次遇到显式断点。
  • serun是"只处理当前这一次":它只修改当前异常的继续状态,异常循环回到 debugger.cpp#L2310 的dbgsetcontinuestatus(...)时,已设置的DBG_CONTINUE会生效——注意 debugger.cpp#L2301-L2313 中首次异常会按过滤器重置继续状态,若过滤器配置为"中断所有首次异常",则下一次异常仍会照常中断。这正是serunerun行为差异的本质。

提示:还有无前缀的continue命令(cbDebugContinue),它只设置异常继续状态、不释放运行锁,必须配合run族命令使用。serun则是把"设状态"和"放行"合并为一条命令。

五、异常中断循环中的执行流程

当程序在异常处被 x64dbg 停住时,执行serun的完整流程为:

  1. 调试器当前处于异常中断态(调试循环停在异常处理处,WAITID_RUN等待锁被占用);
  2. 用户执行serun(或不带参数的sego);
  3. cbDebugContinue(argc, argv)argc < 2进入,dbgsetcontinuestatus(DBG_CONTINUE)把当前异常继续状态设为"已处理";
  4. cbDebugRunInternal检查是否提供了目标地址(有则下一次性断点)、检查程序是否已在运行;
  5. unlock(WAITID_RUN)释放等待锁,调试循环从 debugger.cpp 的等待点恢复;
  6. 调试器把DBG_CONTINUE返回给 Windows 调试 API,异常被标记为已处理,不会进入被调试程序的 SEH 异常处理链
  7. 程序继续运行,直到下一次断点或异常。

正是因为第 6 步"异常不再进入程序自身的处理链",serun成为反调试场景与异常陷阱绕过的常用手段。

六、典型实战场景

场景一:跳过反调试异常陷阱

许多保护壳/反调试代码会在执行路径上主动触发异常(如int3int 2d),并用结构化异常处理(SEH)检测异常是否被"正常"处理,从而判断是否处于调试状态。此时若直接run,异常处理链会被触发、程序可能走反调试分支;而执行serun后异常被调试器吞掉、不进入程序处理链,可让程序按"未发生异常"的路径继续执行。注意:serun吞异常是"代为处理"而非"转发给程序",若程序依赖该异常被自身 SEH 捕获,行为会与真实环境不同,需结合具体样本判断。

场景二:快速跨过频繁的良性异常

某些程序在正常运行中会高频产生良性异常(如未处理的内存访问探测)。在异常中断后逐个按run效率低下,可:

  • 执行serun立即放行当前这一次;
  • 若此类异常会反复触发,改用erun一次性跳过后续所有异常(可通过 Engine/MaxSkipExceptionCount 配置 限制跳过的异常数量上限,避免完全失控);
  • 更精细的做法是在 异常过滤器 中将该异常类型配置为"不中断",从源头减少停表。

场景三:运行到目标位置

serun 00401000

先设置一次性断点于0x00401000再放行,等价于"运行到该地址",常用于从异常点快速跳到关键代码段。

场景四:脚本化控制

在 x64dbg 脚本(脚本命令参考)中:

serun ; 吞掉当前异常继续跑 serun 7FF700000000 ; 或一路跑到指定地址

由于serun不设置结果变量,脚本中如需判断是否到达目标,可配合在目标地址预置断点并检查是否停在该地址来验证。

七、相关命令对照

命令别名行为文档
rungorg普通运行run.md
erun-跳过异常运行erun.md
serunsego吞异常运行serun.md
StepIntostisi单步步入StepInto.md
seStepIntosesti吞异常单步seStepInto.md
continue-仅设置异常继续状态-

八、源码索引

  • 命令注册:src/dbg/x64dbg.cpp#L152
  • 处理函数声明:src/dbg/commands/cmd-debug-control.h#L18
  • 处理函数实现:src/dbg/commands/cmd-debug-control.cpp#L386-L390
  • 运行内部逻辑(含一次性断点与解锁):src/dbg/commands/cmd-debug-control.cpp#L46-L65
  • 异常继续状态设置:src/dbg/commands/cmd-debug-control.cpp#L483-L496
  • 异常循环与继续状态生效点:src/dbg/debugger.cpp#L2301-L2313
  • 异常过滤器配置说明:docs/gui/settings/Exceptions.md

结语

serun/sego的核心价值在于把"吞掉当前异常"与"继续运行"两个动作原子化:一条命令即可完成continuerun的组合。理解其底层对DBG_CONTINUE的设置逻辑,以及它相对于run(不干预异常)与erun(跳过全部异常)的差异,是准确使用异常控制命令、高效绕过反调试陷阱的关键。在调试会话中,根据"只处理这一次"还是"跳过后续所有"的意图在serunerun之间做选择,能显著提升异常密集场景下的调试效率。

【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询