关于 rev-struct
rev-struct 是用于逆向分析的技能,通过函数内存访问和调用链推断数据结构布局。它可借助 IDA Pro MCP 查询,也支持分析导出的反编译文件。
主要功能
- 01收集字段偏移和访问类型
- 02追踪调用者与被调用者
- 03推断字段类型和结构大小
- 04汇总证据生成结构分析
适用场景
- 01根据目标函数重建数据结构
- 02检查字段在函数间的传递方式
MCP 相关
连接 IDA Pro MCP 时可直接查询 IDA;也可分析 IDA-NO-MCP 导出的数据。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ p4nda0s/ reverse-skills --skill rev-struct安装后在 Claude Code 中输入/rev-struct确认已加载。
详细介绍
rev-struct - 结构恢复
通过分析函数及其调用链中的内存访问模式,恢复数据结构定义。
预检查
确定可用的 IDA 访问方式:
选项 A — IDA Pro MCP(已连接时优先):
检查 IDA Pro MCP 服务是否已连接(查找活动的 ida-pro 或等效 MCP 连接)。如果已连接,可通过 MCP 工具直接查询 IDA,无需导出文件。使用 MCP 进行分析。
选项 B — IDA-NO-MCP 导出数据: 如果 MCP 未连接,检查当前目录中是否存在 IDA-NO-MCP 导出数据:
- 检查是否存在
decompile/目录 - 检查其中是否有
.c文件
如果既没有 MCP,也没有导出数据,请提示用户:
No IDA access method detected. Choose one of the following:
Option A — IDA Pro MCP (recommended):
Connect the IDA Pro MCP server so Claude can query IDA directly.
Option B — IDA-NO-MCP export:
1. Download plugin: https://github.com/P4nda0s/IDA-NO-MCP
2. Copy INP.py to IDA plugins directory
3. Press Ctrl-Shift-E in IDA to export
4. Open the exported directory with Claude Code
导出目录结构
./
├── decompile/ # Decompiled C code directory
│ ├── 0x401000.c # One file per function, named by hex address
│ ├── 0x401234.c
│ └── ...
├── decompile_failed.txt # Failed decompilation list
├── decompile_skipped.txt # Skipped functions list
├── strings.txt # String table (address, length, type, content)
├── imports.txt # Import table (address:function_name)
├── exports.txt # Export table (address:function_name)
└── memory/ # Memory hexdump (1MB chunks)
函数文件格式(decompile/*.c)
每个 .c 文件都包含函数元数据注释和反编译代码:
/*
* func-name: sub_401000
* func-address: 0x401000
* callers: 0x402000, 0x403000 // List of functions that call this function
* callees: 0x404000, 0x405000 // List of functions called by this function
*/
int __fastcall sub_401000(int a1, int a2)
{
// Decompiled code...
}
结构恢复步骤
步骤 1:读取目标函数
- 根据用户提供的地址,读取
decompile/<address>.c - 解析函数元数据,提取调用者和被调用者列表
- 识别函数中的指针参数(可能是结构体指针)
步骤 2:收集内存访问模式
在目标函数中搜索以下模式:
直接偏移访问:
*(a1 + 0x10) // offset 0x10
*(_DWORD *)(a1 + 8) // offset 0x8, DWORD type
*(_QWORD *)(a1 + 0x20) // offset 0x20, QWORD type
*(_BYTE *)(a1 + 4) // offset 0x4, BYTE type
数组访问:
*(a1 + 8 * i) // array, element size 8 bytes
a1[i] // array access
嵌套结构:
*(*a1 + 0x10) // first field of struct pointed by a1 is a pointer
记录格式:
offset=0x00, size=8, access=read/write, type=QWORD
offset=0x08, size=4, access=read, type=DWORD
...
步骤 3:遍历调用者进行分析
读取每个调用者函数并分析:
-
参数传递:调用时传入了什么?
sub_401000(v1); // v1 might be a struct pointer sub_401000(&v2); // v2 is a struct sub_401000(malloc(64)); // struct size is ~64 bytes -
调用前后的操作:
v1 = malloc(0x40); // allocate 0x40 bytes *v1 = 0; // offset 0x00 initialization *(v1 + 8) = callback; // offset 0x08 is a function pointer sub_401000(v1); -
收集更多偏移访问
步骤 4:遍历被调用者进行分析
读取每个被调用者函数并分析:
-
参数的使用方式:
// In callee int callee(void *a1) { return *(a1 + 0x18); // accesses offset 0x18 } -
传递给其他函数:
another_func(a1 + 0x20); // offset 0x20 might be a nested struct
步骤 5:汇总并推断
- 合并所有偏移信息,并按偏移排序
- 计算结构体大小:max(offset) + last_field_size
- 推断字段类型:
- 作为函数指针调用 → 函数指针
- 传递给
strlen/printf→ 字符串指针 - 与常量比较 → 枚举/标志
- 执行递增/递减操作 → 计数器/索引
- 识别常见模式:
- 偏移 0 是函数指针表 → 虚函数表(C++ 对象)
- next/prev 指针 → 链表节点
- 引用计数字段 → 引用计数对象
输出格式
/*
* Structure Recovery Analysis
* Source function: <func_address>
* Analysis scope: <number of callers/callees analyzed>
*
* Functions using this struct:
* - 0x401000 (initialization)
* - 0x401100 (field access)
* - 0x401200 (destruction)
*/
// Estimated size: 0x48 bytes
// Confidence: High / Medium / Low
struct suggested_name {
/* 0x00 */ void *vtable; // vtable pointer, called: (*(*this))()
/* 0x08 */ int refcount; // reference count, has ++/-- operations
/* 0x0C */ int flags; // flags, AND with 0x1, 0x2
/* 0x10 */ char *name; // string, passed to strlen/printf
/* 0x18 */ void *data; // data pointer
/* 0x20 */ size_t size; // size field
/* 0x28 */ struct node *next; // linked list next pointer
/* 0x30 */ struct node *prev; // linked list prev pointer
/* 0x38 */ callback_fn handler; // callback function
/* 0x40 */ void *user_data; // user data
};
// Field access examples:
// 0x401000: *(this + 0x08) += 1; // refcount++
// 0x401100: printf("%s", *(this + 0x10)); // print name
评论
支持 Markdown