x64dbg LoadTypes 命令详解:从 JSON 文件加载自定义类型定义
2026/9/19 20:30:25 网站建设 项目流程

x64dbg LoadTypes 命令详解:从 JSON 文件加载自定义类型定义

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

LoadTypes是 x64dbg 调试器类型系统(Types)中最核心的批量导入命令:它从一份 JSON 文件中一次性加载 typedef、结构体/联合体、函数原型与枚举定义,供反汇编视图、Struct 视图与脚本表达式使用。本文以官方命令文档为主线,结合仓库源码(cmd-types.cpp、types.cpp)完整讲解命令语法、JSON 格式规范、owner 替换语义、底层加载流程与 GUI 入口,读完即可亲手编写并加载属于自己的类型定义文件。

LoadTypes 命令概览

LoadTypes位于 x64dbg 命令系统的类型操作(Types)命令组中,与AddTypeAddStructParseTypesEnumTypes等命令并列(见 types/index.rst)。其功能官方定义为:

Load types from a JSON file.

命令注册于 x64dbg.cpp:

dbgcmdnew("LoadTypes", cbInstrLoadTypes, false); //LoadTypes

语法

LoadTypes arg1
项目说明
arg1待加载 JSON 文件的路径。加载后类型的owner(所有者)为该 JSON 的文件名;此前由该 owner 定义的所有类型都会被移除
结果变量本命令不设置任何结果变量(如$result

SizeofType这类会写入$result的命令不同(参见 cmd-types.cpp 中的varset("$result", size, false)),LoadTypes的执行结果只能通过命令回显判断:加载成功打印Types loaded,失败打印LoadTypes failed

参数 arg1:路径与 owner 语义

命令处理器首先校验参数个数(少于 2 个参数直接失败),随后执行三件事(cmd-types.cpp):

bool cbInstrLoadTypes(int argc, char* argv[]) { if(IsArgumentsLessThan(argc, 2)) return false; auto owner = FileHelper::GetFileName(argv[1]); ClearTypes(owner); if(!LoadTypesFile(argv[1], owner)) { dputs(QT_TRANSLATE_NOOP("DBG", "LoadTypes failed")); return false; } GuiTypeListUpdated(); dputs(QT_TRANSLATE_NOOP("DBG", "Types loaded")); return true; }

owner 从何而来

owner直接取自FileHelper::GetFileName(argv[1]),即传入路径的文件名部分(含扩展名)。例如:

  • LoadTypes C:\types\win32.json→ owner 为win32.json
  • LoadTypes "D:\My Types\ntdll types.json"→ owner 为ntdll types.json

路径中包含空格时必须用双引号包裹——GUI 内部的调用就是如此构造命令的(详见下文 GUI 集成一节)。

替换语义:先清除,再加载

ClearTypes(owner)会在加载前删除该 owner 名下的全部已有类型,然后LoadTypesFile再写入新定义,从而实现"全量替换"而非增量合并。底层对应TypeManager::Clear(owner)(types.h),它通过filterOwnerMap只清理 owner 匹配的条目:

template<typename K, typename V> void filterOwnerMap(std::unordered_map<K, V> & map, const std::string & owner) { for(auto j = map.begin(); j != map.end();) { if(j->second.owner.empty()) j++; // skip builtin types else if(owner.empty() || j->second.owner == owner) j = map.erase(j); else j++; } }

从这段实现可以推断出两个关键行为:

  • 内置类型不受影响:owner 为空(内置类型)的条目在清理时会被跳过;
  • owner 隔离:不同 owner 的类型互不干扰,多次LoadTypes加载不同文件可以共存。对同一文件重复加载则表现为"重新加载/刷新"。

JSON 文件格式详解(源码级)

LoadTypes底层由LoadTypesJson完成解析(types.cpp),使用仓库自带的 jansson 库(src/dbg/jansson)解析 JSON:

bool LoadTypesJson(const std::string & json, const std::string & owner) { EXCLUSIVE_ACQUIRE(LockTypeManager); json_error_t err; auto root = json_loads(json.c_str(), 0, &err); if(root) { Model model; loadTypes(json_object_get(root, "types"), model.types); loadTypes(json_object_get(root, ArchValue("types32", "types64")), model.types); loadStructUnions(json_object_get(root, "structUnions"), model.structUnions); loadFunctions(json_object_get(root, "functions"), model.functions); loadFunctions(json_object_get(root, ArchValue("functions32", "functions64")), model.functions); loadEnums(json_object_get(root, "enums"), model.enums); loadEnums(json_object_get(root, ArchValue("enums32", "enums64")), model.enums); LoadModel(owner, model); json_decref(root); } else return false; return true; }

顶层结构支持五类字段,其中三类支持按架构区分(见ArchValue宏,src/dbg/_global.h:64 位构建取xxx64,32 位构建取xxx32):

顶层字段含义是否区分架构
typestypedef 别名定义支持types32/types64
structUnions结构体 / 联合体定义
functions函数原型定义支持functions32/functions64
enums枚举定义支持enums32/enums64

LoadTypesFile(types.cpp)负责把文件读成字符串再交给LoadTypesJson

bool LoadTypesFile(const std::string & path, const std::string & owner) { std::string json; if(!FileHelper::ReadAllText(path, json)) return false; return LoadTypesJson(json, owner); }

types 段:typedef 别名

解析函数loadTypes(types.cpp)只读取两个字段:

{ "types": [ { "type": "uint32_t", "name": "DWORD" }, { "type": "uint8_t", "name": "BYTE" } ] }
  • type:被别名的基类型名(字符串);
  • name:新类型名。

任一字段缺失或为空字符串,该条目会被跳过。

structUnions 段:结构体与联合体

解析函数loadStructUnions(types.cpp)是格式最丰富的一段:

{ "structUnions": [ { "name": "MY_STRUCT", "isUnion": false, "size": 8, "members": [ { "type": "uint32_t", "name": "field1" }, { "type": "uint8_t", "name": "data", "arrsize": 4 }, { "type": "uint32_t", "name": "flags", "bitfield": true, "sizeBits": 4, "bitOffset": 32 } ] } ] }

成员字段与单位换算规则(注意size/offset字节为单位,加载时自动 ×8 换算为内部使用的单位):

字段含义单位
name结构体/联合体名
isUnion是否为联合体(缺省 false)布尔
size结构体总大小字节(加载时 ×8 转位)
sizeBits结构体总大小的备选写法位(与size二选一)
members[].type成员类型名
members[].name成员名
members[].arrsize数组元素个数(缺省 0,即非数组)
members[].size成员大小(兼容旧格式)字节(×8 转位)
members[].sizeBits成员大小备选写法
members[].offset成员偏移(兼容旧格式)字节(×8 转位)
members[].bitOffset成员位偏移
members[].bitfield是否为位域成员布尔

源码注释明确写着// NOTE: Attempt to keep support for the previous JSON format,即size/offset(字节)与sizeBits/bitOffset(位)两套写法会同时被接受,优先读取字节单位版本。

functions 段:函数原型

解析函数loadFunctions(types.cpp):

{ "functions": [ { "name": "MyApi", "rettype": "NTSTATUS", "callconv": "stdcall", "noreturn": false, "args": [ { "type": "HANDLE", "name": "hObject" }, { "type": "uint32_t", "name": "dwFlags" } ] } ] }

字段说明:

字段含义
name函数名
rettype返回类型
callconv调用约定,取值cdecl/stdcall/thiscall/delphi
noreturn函数是否不返回(如ExitProcess),缺省 false
args[].type参数类型
args[].name参数名

调用约定的匹配使用大小写不敏感的scmp(基于_stricmp,见 _global.cpp),因此StdCallSTDCALL均可识别;遇到未知值则回退为Cdecl。注意callconv为空或缺失时也默认Cdecl

enums 段:枚举

解析函数loadEnums(types.cpp):

{ "enums": [ { "name": "MY_FLAGS", "size": 4, "isFlags": true, "members": [ { "name": "FLAG_A", "value": 1 }, { "name": "FLAG_B", "value": 2 } ] } ] }

字段说明:

字段含义
name枚举名
size/sizeBits枚举宽度(字节 / 位两种写法)
isFlags是否为位标志枚举,缺省 false
members[].name枚举成员名
members[].value枚举成员值(整数)

需要注意一个边界条件:value == -1的成员条目会被跳过(源码中if(value == -1 || !name || !*name) continue;),因此-1不能作为合法的枚举值使用。

底层加载流程与错误处理

LoadModel(types.cpp)是数据落地的核心,它刻意安排了加载顺序以避免前向引用问题:

  1. 先添加全部结构体/联合体空壳(AddStruct/AddUnion),失败则打印Failed to add struct/union并计数,同时清空该条目的名字标记错误;
  2. 再添加全部函数空壳(AddFunction),同样对失败条目打印错误并清名;
  3. 随后填充结构体成员(AddStructMember)、枚举(AddEnum+AddEnumMember)、函数返回值与参数(AddFunctionReturn/AddArg),错误逐条打印到日志。

需要指出的实现细节是:LoadTypesJson的返回值只反映 JSON 是否解析成功json_loads是否返回非空根节点),LoadModel内部的逐条错误仅打印到日志而不会导致整个命令失败。也就是说:

  • JSON 语法错误、文件不存在 →LoadTypes failed(命令失败);
  • JSON 合法但个别类型定义冲突/引用未定义类型 → 命令仍报告Types loaded,具体错误条目在日志中逐条列出。

类型加载全程受LockTypeManager互斥锁保护(EXCLUSIVE_ACQUIRE/SHARED_ACQUIRE),避免与LookupTypeByNameSizeofType等其他类型查询并发冲突。

GUI 集成:Struct 视图的 Load JSON 入口

除了命令行,x64dbg 的 Struct 视图(结构体窗口)也内置了加载入口。在 StructWidget.cpp 中,loadJsonSlot弹出文件选择对话框后,正是通过命令桥执行LoadTypes

void StructWidget::loadJsonSlot() { auto filename = QFileDialog::getOpenFileName(this, tr("Load JSON"), QString(), tr("JSON files (*.json);;All files (*.*)")); if(!filename.length()) return; filename = QDir::toNativeSeparators(filename); DbgCmdExec(QString("LoadTypes \"%1\"").arg(filename)); }

从这段代码可以看到两层信息:其一,GUI 用双引号包裹带空格的路径后交给DbgCmdExec,这与命令行手输规则一致;其二,文件对话框默认过滤*.json,提示 JSON 是官方推荐的类型交换格式。加载成功后 GUI 通过GuiTypeListUpdated刷新类型列表,随后即可在 Struct 视图中用VisitType展示具体地址处的类型布局(见 cmd-types.cpp)。

完整实战示例

下面是一份可直接保存为example.json并执行LoadTypes example.json的完整示例,涵盖四类定义,并演示了架构区分字段的用法:

{ "types": [ { "type": "uint32_t", "name": "DWORD" }, { "type": "uint8_t", "name": "BYTE" } ], "structUnions": [ { "name": "EXAMPLE_HEADER", "isUnion": false, "size": 16, "members": [ { "type": "DWORD", "name": "Signature" }, { "type": "uint16_t", "name": "Version" }, { "type": "BYTE", "name": "Reserved", "arrsize": 8 }, { "type": "uint32_t", "name": "Flags", "bitfield": true, "sizeBits": 16, "bitOffset": 32 } ] } ], "functions": [ { "name": "ExampleInitialize", "rettype": "BOOL", "callconv": "stdcall", "args": [ { "type": "EXAMPLE_HEADER*", "name": "pHeader" }, { "type": "DWORD", "name": "dwSize" } ] } ], "enums": [ { "name": "EXAMPLE_STATE", "size": 4, "isFlags": false, "members": [ { "name": "STATE_IDLE", "value": 0 }, { "name": "STATE_RUNNING", "value": 1 }, { "name": "STATE_DONE", "value": 2 } ] } ], "types64": [ { "type": "uint64_t", "name": "QWORD" } ], "functions64": [ { "name": "ExampleInit64", "rettype": "QWORD", "callconv": "cdecl", "args": [ { "type": "QWORD", "name": "qwParam" } ] } ] }

加载后可用EnumTypes命令验证结果——它逐条输出owner: kind name, sizeof(name) = N(见 cmd-types.cpp),例如example.json: struct EXAMPLE_HEADER, sizeof(EXAMPLE_HEADER) = 16。若加载的是 64 位构建,types64functions64段也会被一并合并进来。

与相关命令的配合使用

LoadTypes是类型系统命令组(完整清单见 types/index.rst)中的批量导入环节,常与以下命令协同:

命令作用与 LoadTypes 的关系
ParseTypes从 C/C++ 头文件解析类型另一条批量导入路径(文本头文件 vs JSON),内部同样以文件名作 owner 并先ClearTypes
ClearTypes清除指定 owner 的类型LoadTypes内部隐式调用;也可手动清除后重新加载
EnumTypes列出当前全部类型加载后的验证手段
AddType / AddStruct逐条手动添加类型单条定义的手动替代方案,owner 固定为cmd
SizeofType查询类型大小验证结构体布局是否与目标进程一致
VisitType在指定地址实例化展示类型加载后最重要的消费方式:结合内存地址解析真实数据

在逆向分析实践中,典型工作流是:先用ParseTypes从 SDK 头文件快速导入,再用LoadTypes加载社区分享的 JSON 类型库,最后用VisitType在 Struct 视图中逐字段核对目标进程内存——LoadTypes正是这条链路中"复用他人类型成果、持久化自有类型"的关键一环。

注意事项小结

  1. owner 即文件名:同一 JSON 文件重复LoadTypes会整体替换;两个不同文件若碰巧同名(如不同目录下的types.json),owner 相同也会互相覆盖,命名时建议使用有区分度的文件名。
  2. 命令无结果变量:脚本中需要判断成功与否时,依赖日志文本或配合其他手段,而非$result
  3. JSON 解析失败才报错:类型条目本身的错误(如引用了未定义类型)只进日志,命令仍会提示Types loaded,加载后务必用EnumTypes或日志核对。
  4. 字节与位单位并存size/offset按字节、sizeBits/bitOffset按位,二者混用时注意单位换算(加载器按 ×8 转换字节版本)。
  5. 架构隔离:需要区分 32/64 位定义时使用types32/types64functions32/functions64enums32/enums64字段,当前构建只会读取与自身架构匹配的那一份;structUnions不区分架构。

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

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

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

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

立即咨询