x64dbg 注释命令完整指南:commentset/cmt/cmtset 的语法、底层实现与实战用法
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
导读
commentset(别名cmt、cmtset)是 x64dbg 用户数据库中用于"在指定地址上设置/编辑注释"的核心命令。注释是逆向工程中标记函数入口、解密循环、可疑调用点最常用的持久化标注手段,配合标签(label)、书签(bookmark)共同构成 x64dbg 的"用户数据库"体系。读完本文,你将掌握该命令的完整语法、地址表达式写法、底层存储机制(含 manual/auto 注释区分)、关联命令家族(删除/列出/清空)以及脚本 API 与测试用例的验证方式。
命令概览与语法
该命令在 x64dbg 的命令注册表中定义如下(见 src/dbg/x64dbg.cpp):
dbgcmdnew("commentset,cmt,cmtset", cbInstrCommentSet, true); //set/edit comment命令名:commentset,同时支持两个短别名cmt与cmtset,三者完全等价。
参数(arguments):
| 参数 | 含义 | 是否必填 |
|---|---|---|
arg1 | 要设置注释的地址(最好位于模块内部) | 必填 |
arg2 | 注释文本内容 | 必填 |
结果(result):该命令不设置任何结果变量,即执行后$result保持不变。
参数详解与实战示例
arg1:地址表达式
arg1并不是一个裸的十六进制字面量,它支持 x64dbg 完整的表达式求值系统。在命令处理器中,参数通过valfromstring(argv[1], &addr, false)解析(见 src/dbg/commands/cmd-user-database.cpp),这意味着你可以传入:
- 直接地址:
commentset 00401000, entry point - 表达式:
commentset eip, current instruction - 符号/标签:
commentset MessageBoxW, API hook target - 模块内地址表达式:
commentset ntdll:7C921000, LdrLoadDll call
推荐将地址定位在模块内部,这样注释会随模块基址重定位逻辑正确保存与恢复;跨模块的裸地址注释在下次加载、基址变化时可能失效。
arg2:注释文本与两个隐藏规则
arg2是注释文本,底层实现(见 src/dbg/comment.cpp)对文本有两个关键约束:
- 空字符串即删除:如果传入的文本以
\0开头(空文本),CommentSet不会写入注释,而是直接调用CommentDelete删除该地址的已有注释。这与执行commentdel的效果一致。 - 长度上限 512 字节:文本长度必须小于
MAX_COMMENT_SIZE - 1,即最多 511 个有效字符。MAX_COMMENT_SIZE在 src/bridge/bridgemain.h 中定义为512。超长文本会被拒绝,命令返回 "Error setting comment"。 - 首字符保留:以 ASCII
\1(0x01)开头的文本被禁止,因为该前缀在内部用于标记"自动注释"(见下文 manual/auto 区分),用户文本不得占用。
另外注意,命令行的参数分割以空格为界,含空格的注释文本建议用双引号包裹:commentset 00401000, "decrypt loop start"。
底层实现解析:从命令到存储
命令分发链路
整条调用链为:
命令行输入 commentset → cbInstrCommentSet(命令处理器) → valfromstring 解析地址表达式 → CommentSet(addr, text, true)(注释核心逻辑) → GuiUpdateAllViews()(刷新所有视图)cbInstrCommentSet的实现位于 src/dbg/commands/cmd-user-database.cpp:先检查参数数量(少于 3 个直接失败),再解析地址,成功后调用CommentSet并刷新 GUI 视图,失败则输出 "Error setting comment"。
注意第三个参数传的是true,即Manual标志——通过命令行设置的注释都是手动注释(manual)。
CommentSet 的核心逻辑
src/dbg/comment.cpp 中的CommentSet负责将注释写入内存中的Comments哈希表(AddrInfoHashMap),并为每条记录填充地址、模块哈希、manual 标志与文本:
bool CommentSet(duint Address, const char* Text, bool Manual) { if(!Text || Text[0] == '\1' || strlen(Text) >= MAX_COMMENT_SIZE - 1) return false; if(Text[0] == '\0') { CommentDelete(Address); return true; } COMMENTSINFO comment; if(!comments.PrepareValue(comment, Address, Manual)) return false; comment.text = Text; return comments.Add(comment); }每条注释通过Comments::VaKey(Address)以虚拟地址为键进行索引,COMMENTSINFO结构(见 src/dbg/comment.h)包含继承自AddrInfo的模块哈希、manual 标志,以及注释文本text。
manual 与 auto 注释的区分
x64dbg 的注释体系分为两类:
- 手动注释(manual):由
commentset命令或 GUI 右键菜单创建,持久保存并显示在反汇编视图中; - 自动注释(auto):由引擎/插件内部生成,例如
cmd-undocumented.cpp中为RUNTIME_FUNCTION生成的自动标注(见 src/dbg/commands/cmd-undocumented.cpp),此时CommentSet的Manual参数传false。
两者的读取差异体现在CommentGet中(src/dbg/comment.cpp):手动注释原样返回,自动注释则会被加上\1前缀,便于上层区分来源。在commentlist中也可以通过额外参数1选择是否同时列出自动注释。
持久化与数据库回调
注释属于"用户数据库"的一部分,随数据库保存/加载:
- 序列化键名为
"comments"(见 src/dbg/comment.cpp 的jsonKey()),通过CommentCacheSave/CommentCacheLoad在dbsave/dbload时写入或读回; - 每条记录写入数据库操作时标记为
DbItemTypeComment(src/dbg/comment.cpp),因此注释的增删会触发数据库回调(database callbacks),供插件与自动化测试监听。
关联命令家族
commentset不是孤立命令,它与另外三个注释命令共同构成完整的注释管理工具集(均在 src/dbg/x64dbg.cpp 注册):
| 命令 | 别名 | 功能 |
|---|---|---|
| commentset | cmt、cmtset | 设置/编辑注释 |
| commentdel | cmtc、cmtdel | 删除指定地址的注释 |
| commentlist | 无 | 在 Reference View 中列出注释($result返回条数) |
| commentclear | 无 | 清空所有模块的全部注释 |
其中commentlist的结果可通过脚本配合ref.addr(i)、ref.count()等表达式函数遍历(见 commentlist 文档 与 表达式函数)。
在 GUI 侧,反汇编视图(CPU 视图)会在指令行右侧直接显示注释文本,且手动注释会随数据库持久化;设置注释后GuiUpdateAllViews()会即时刷新所有相关视图。
脚本与插件 API
除命令行外,脚本与插件可以通过导出 API 完成相同操作:
- 脚本 API:
Script::Comment::Set(addr, text, manual)、Script::Comment::Get(addr, text)、Script::Comment::Delete(addr),实现在 src/dbg/_scriptapi_comment.cpp; Set还提供基于CommentInfo的重载:传入模块名与 RVA,自动换算为模块基址 + RVA 的绝对地址后再写入,适合插件按"模块 + 偏移"的稳定坐标管理注释。
测试用例验证
仓库中的数据库回调测试对注释的写入/删除做了端到端验证,见 src/tests/database_callbacks/test.comment-single.txt:
init tests/database_callbacks.exe _c1 = database_callbacks:VariableTarget commentset _c1, test assertlastop mod.hash(mod.base(_c1)), (_c1-mod.base(_c1)), c, a, 0, test commentdel _c1 assertlastop mod.hash(mod.base(_c1)), (_c1-mod.base(_c1)), c, r, 0该用例揭示了两个实用要点:
- 地址可以传标签/符号(
database_callbacks:VariableTarget),印证了 arg1 的表达式能力; - 注释写入与删除都会触发数据库回调(
assertlastop校验操作码a/r),回调参数中携带模块哈希、RVA 与注释文本——这正是插件监听用户数据库变更的标准通道。
实践建议与注意事项
- 用模块内地址:将注释设置在模块内部,配合
dbsave保存数据库后,下次调试同一模块时注释可正确恢复; - 配合标签使用:先
labelset命名关键位置,再用commentset补充说明,可读性最佳; - 注意文本长度:单条注释不要超过 511 字节,超长文本会被静默拒绝并报错;
- 区分自动注释:
commentlist默认只列手动注释,排查异常时可追加参数查看自动注释,避免与引擎生成的RUNTIME_FUNCTION等自动标注混淆; - 空文本即删除:
commentset addr, ""与commentdel addr等价,可用于脚本中条件化地清理注释。
相关命令的完整文档可在 user-database 命令索引 中查阅,用户数据库的批量管理(保存/加载/清空)参见 dbsave、dbload 与 dbclear。
【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考