x64dbg 文件菜单(File Menu)完全指南:从打开程序、附加进程到数据库导入导出与补丁管理
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
导读
x64dbg 的File(文件)菜单是逆向工程的起点——几乎所有调试会话都从这里开始:打开目标程序、附加到正在运行的进程、管理最近调试记录,以及维护与调试会话配套的用户数据库(注释、标签、断点、函数等)。本文基于 File.rst 官方文档,逐项剖析该菜单的全部条目,并结合仓库源码揭示其底层命令实现与配置存储机制,帮助你不仅会用界面,还能理解每个动作背后发生了什么,甚至可以直接用命令行完成同样操作。
菜单总览
File 菜单自上而下包含以下条目:
| 菜单项 | 作用 | 对应命令 |
|---|---|---|
| Open | 打开可执行文件(EXE/DLL)开始调试 | InitDebug(init) |
| Recent Files | 最近调试过的文件子菜单 | —(由 MRUList 管理) |
| Attach | 附加到运行中的进程 | AttachDebugger(attach) |
| Detach | 分离调试器,让调试对象自由运行 | DetachDebugger(detach) |
| Import database | 导入用户数据库 | dbload |
| Export database | 导出用户数据库 | dbsave |
| Patch file | 打开补丁对话框,查看并应用补丁 | — |
| Restart as Admin | 以管理员权限重启 x64dbg 与当前调试目标 | restartadmin(内部命令) |
| Exit | 退出调试器并终止被调试进程 | — |
下面逐项展开。
Open:开始一个调试会话
Open动作用于打开一个可执行文件进行调试,文件类型可以是EXE,也可以是DLL。这是 x64dbg 最常用的入口动作,其 UI 实现位于 MainWindow.cpp:
void MainWindow::openFileSlot() { auto filename = QFileDialog::getOpenFileName(this, tr("Open file"), mMRUList->getEntry(0), tr("Executables (*.exe *.dll);;All files (*.*)")); if(!filename.length()) return; filename = QDir::toNativeSeparators(filename); //convert to native path format (with backlashes) openRecentFileSlot(filename); }几个值得注意的实现细节:
- 文件过滤器默认限定为
*.exe和*.dll,但允许切换到All files (*.*)手动选择任意文件(例如无扩展名的驱动或固件镜像)。 - 初始目录取自最近文件列表(MRU)的第一项所在位置(
mMRUList->getEntry(0)),方便连续调试同一目录下的多个样本。 - 选定文件后,路径会通过
QDir::toNativeSeparators转换为 Windows 原生反斜杠格式,再交给openRecentFileSlot,最终拼成命令init "路径"执行。
底层命令:init
Open 动作的界面背后执行的正是init(全名 InitDebug,别名initdbg)。该命令完成一次完整的初始化流程:
- 加载可执行文件并做基本检查;
- 若存在 TLS 回调(TLS callbacks),为其设置断点;
- 在进程入口点(entry point)设置断点;
- 在系统断点(system breakpoint)处停下,把控制权交还给用户。
命令语法:
init <arg1> [arg2] [arg3]| 参数 | 含义 | 说明 |
|---|---|---|
arg1 | 要调试的可执行文件路径 | 如果未给出完整路径,会调用GetCurrentDirectoryAPI 补全为完整路径;路径含空格时必须使用引号包裹 |
arg2(可选) | 创建进程时附带的命令行参数 | 直接传给CreateProcess |
arg3(可选) | 当前工作目录 | 即所谓的 working directory / current directory,也传给CreateProcess |
命令返回后,会设置$pid(进程 ID)和$hp/$hProcess(进程句柄)变量,方便后续脚本直接引用。例如:
init "C:\malware\sample.exe" "--flag1 --flag2" "C:\malware"在 GUI 层面,除了菜单 Open,你还可以把文件直接拖拽到主窗口上触发同样的init流程——MainWindow.cpp 的dropEvent会把拖入的文件路径拼成init "路径"命令执行,两者完全等价。
Recent Files:最近调试记录
Recent Files子菜单列出了你此前调试过的文件。文档特别说明:它不会包含任何程序无法调试的文件(例如已不存在的路径或非可执行文件)。
该子菜单的数据存放在配置 INI 文件的Recent Files小节中,你可以直接编辑该 INI 文件来增删条目。
源码级实现:MRUList
Recent Files 由MRUList类实现,见 MRUList.cpp。关键行为:
- 存储格式:条目以两位数字序号作为键写入 INI,例如
Recent Files\01、Recent Files\02……每个键对应一个文件路径。BridgeSettingSet/BridgeSettingGet负责读写(见save()与load())。 - 默认容量:
MRUList构造函数的maxItems参数默认为16(见 MRUList.h),超出上限的旧条目会被裁剪(addEntry中mMRUList.erase(...))。 - 去重:加载与保存时都会
removeDuplicates(),同一个文件只会出现一次;再次打开某文件时,它会被提升到列表最前(insert(mMRUList.begin(), entry))。 - 有效性校验:
load()与save()都会检查QFile(path).exists(),不存在的文件不会进入列表——这正是文档所说"不包含无法调试的文件"的机制来源。 - 点击行为:
appendMenu为每个条目创建一个 QAction,触发后发出openFile信号,由主窗口openRecentFileSlot接收并执行init "路径"(见 MainWindow.cpp)。
主窗口在初始化时创建该列表(MainWindow.cpp):
mMRUList = new MRUList(this, "Recent Files"); connect(mMRUList, SIGNAL(openFile(QString)), this, SLOT(openRecentFileSlot(QString))); mMRUList->load();值得注意的是,MRUList是一个通用组件,同一机制还被复用于Recent Scripts(ScriptView.cpp)和Recent Trace Files(TraceManager.cpp),只是 INI 小节名不同。另外,主窗口的restartDebugging()(对应actionRestart)也依赖 MRU 列表第一项来重新启动上次调试的目标(MainWindow.cpp)。
Attach:附加到运行中的进程
Attach用于附加到一个正在运行的进程。点击后会弹出一个对话框,列出当前系统正在运行的进程,你可以选择其一进行附加。
有两个必须牢记的限制(官方文档明确):
- 架构必须一致:只能附加与调试器同架构的可执行文件。例如用 x32dbg 无法附加 64 位进程,反之亦然。
- 附加会终止当前调试对象:如果你正在调试某个可执行文件,再附加另一个进程会终止之前的调试对象(debuggee)。
底层命令:attach
Attach 动作对应 AttachDebugger(别名attach):
attach <arg1> [arg2] [arg3]| 参数 | 含义 | 说明 |
|---|---|---|
arg1 | 目标进程的 PID | 必填 |
arg2(可选) | 事件对象句柄 | 仅内部使用(internal use only) |
arg3(可选) | 附加后要恢复的线程 TID | 仅内部使用(internal use only) |
附加成功后,命令同样会在系统断点处把控制权交回用户,并设置$pid与$hp/$hProcess变量。日常使用只需attach 1234这样的形式即可。
Detach:分离调试器
Detach将调试器与调试对象分离,让被调试进程脱离调试器的控制继续独立运行。
使用限制:未处于调试状态时无法执行此操作。对应命令 DetachDebugger(别名detach)非常简洁——它没有任何参数,也不会设置任何结果变量,直接输入detach即可。
典型场景:当逆向分析完成、不再需要单步跟踪,但又希望目标进程(比如一个被附加分析的服务器程序)继续对外提供服务时,用 Detach 比直接终止更加温和。
Import / Export database:用户数据库的导入与导出
Import database允许你导入一个数据库文件;Export database则允许你把当前数据库导出为未压缩(uncompressed)的数据库文件。
这里所说的"数据库"(database)是 x64dbg 的用户数据库——它集中存放与当前调试目标关联的一切分析成果:注释(comments)、标签(labels)、书签(bookmarks)、函数定义(functions)、参数(arguments)、断点(breakpoints)等。用户数据库的底层存储逻辑集中在 database.cpp,相关类型定义见 database.h。
对应的两个命令
dbload(别名loaddb),见 dbload:
dbload [arg1]- 若给出
arg1(数据库文件路径):执行的是导入(import)语义,当前数据不会被自动清空; - 若不给出参数:会清空所有数据,并从磁盘重新加载当前程序的数据库。
其命令处理函数位于 cmd-user-database.cpp:无参数时先DebugRemoveBreakpoints()再DbClear(),随后DbLoad(...)、DebugSetBreakpoints()并刷新所有视图——也就是说加载数据库的同时会同步恢复其中的断点设置。
dbsave(别名savedb),见 dbsave:
dbsave [arg1]- 若给出
arg1:将数据库保存到指定路径; - 若不给出参数:保存到当前程序的默认数据库。
实现上直接调用DbSave(DbLoadSaveType::All, ...)(cmd-user-database.cpp),按"全部类型"导出。
这两个命令在命令系统中注册于 x64dbg.cpp:
dbgcmdnew("dbsave,savedb", cbInstrDbsave, true); //save program database dbgcmdnew("dbload,loaddb", cbInstrDbload, true); //load program database实战建议:在分析一个大型样本前,先用dbsave备份干净的初始数据库;导入他人分享的分析成果时用dbload <path>,这样不会覆盖你当前已有的数据。
Patch file:补丁对话框
Patch file打开补丁对话框(Patch Dialog),你可以在此查看已记录的补丁(patches),并将补丁直接应用到文件上。
补丁功能允许你在内存中修改指令后,把修改固化到磁盘上的原始文件里——例如 patch 掉反调试的jz跳转、改写条件分支等。GUI 侧由 PatchDialog 实现,主窗口的patchWindow()槽负责将其显示出来(MainWindow.cpp),并通过actionPatches连接到菜单项(MainWindow.cpp)。此外,当你在反汇编视图中通过右键"show patches"时也会触发同一个对话框(MainWindow.cpp)。
补丁的底层记录与读写逻辑位于 patches.cpp / patches.h。对话框内会列出所有待定补丁,你可以逐个审查后选择"应用"来把修改写回文件。
Restart as Admin:以管理员权限重启
Restart as Admin会用管理员权限重启 x64dbg,并同时以管理员权限重启当前正在调试的目标进程。
该菜单项在 MainWindow.cpp 中被绑定到内部命令restartadmin:
makeCommandAction(ui->actionRestartAdmin, "restartadmin");命令在 x64dbg.cpp 中注册:
dbgcmdnew("restartadmin,runas,adminrestart", cbInstrRestartadmin, false);核心实现是dbgrestartadmin(),见 debugger.cpp:
bool dbgrestartadmin() { wchar_t wszProgramPath[MAX_PATH] = L""; if(GetModuleFileNameW(GetModuleHandleW(nullptr), wszProgramPath, _countof(wszProgramPath))) { std::wstring file = wszProgramPath; auto last = wcsrchr(wszProgramPath, L'\\'); if(last) *last = L'\0'; std::wstring params = L"\"" + gInitExe + L"\""; if(!gInitCmd.empty()) params += L" -- " + gInitCmd; if(!gInitDir.empty()) params = L"-workingDir \"" + gInitDir + L"\" " + params; auto result = ShellExecuteW(NULL, L"runas", file.c_str(), params.c_str(), wszProgramPath, SW_SHOWDEFAULT); return INT_PTR(result) > 32 && GetLastError() == ERROR_SUCCESS; } return false; }机制要点:
- 通过
ShellExecuteW的runas动词触发 Windows UAC 提权,重新启动调试器; - 重启时会保留初始调试目标:
gInitExe(当前调试的可执行文件)、gInitCmd(启动命令行参数,通过--分隔传递)、gInitDir(工作目录,通过-workingDir参数传递)都会被透传给新进程; - 如果调试器在调试过程中检测到需要管理员权限(例如附加到高权限进程失败),也会走同样的提权重启路径(debugger.cpp)。
典型应用:许多恶意样本、加壳程序或受保护进程运行在高权限下,普通权限的调试器无法附加或读写其内存,此时用此功能一次性提升整个调试环境(包括被调试进程)的权限。
Exit:退出调试器
Exit终止调试器。特别注意官方文档的警告:如果当前有进程正被本程序调试,退出时这些进程也会被一并终止(terminated)。
因此,在退出前请确认:
- 是否已经保存好数据库(
dbsave/ Export database); - 是否已经应用或导出补丁(Patch file);
- 如果只想让目标进程继续运行而不再调试,应优先使用Detach而非 Exit。
小结与实战速查
File 菜单虽然只是 x64dbg 的入口,却串联起了调试生命周期中最核心的几个动作。一张表速查:
| 你的需求 | 操作路径 | 等价命令 |
|---|---|---|
| 打开 EXE/DLL 开始调试 | File → Open | init "path" [cmdline] [workdir] |
| 重新调试上次的目标 | File → Recent Files 或 Restart | init(由 MRU 首项驱动) |
| 附加同架构运行进程 | File → Attach | attach <pid> |
| 让目标脱离调试器继续运行 | File → Detach | detach |
| 备份/分享分析成果 | File → Export database | dbsave [path] |
| 载入他人/旧的分析成果 | File → Import database | dbload [path] |
| 把内存修改写回文件 | File → Patch file | —(GUI 对话框) |
| 提权后继续调试 | File → Restart as Admin | restartadmin |
| 结束调试并终止目标 | File → Exit | — |
如需更深入地理解每一项背后的命令行为,可继续阅读 debug-control 与 user-database 两个命令手册目录,或直接查看 MainWindow.cpp、MRUList.cpp、cmd-user-database.cpp 等源码文件。
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考