09-逆向信息保存方法
逆向信息保存方法——软件地图、知识图谱与找功能的手法
一句话
逆向里最值钱的不是地址和偏移,而是对象之间的关系,以及怎么证明这个关系存在。 单个软件的逆向信息用「软件地图」分十层保存;知识用「对象地图 / 发现过程 / 规律库」三种文档组织,记关系不记地址;找功能靠「功能发现策略库」记录为什么这样找,而不是找到了什么。
一、软件地图:单个软件逆向信息的十个保存层次
做单个软件的逆向分析,最重要的不是保存所有细节,而是建立一个能快速定位功能的知识库——软件地图(Software Map)。十个层次:
- 软件基本信息——名称/版本/编译时间/大小/MD5/开发商/保护方式(无壳/UPX/VMProtect/Themida)。
- 模块结构图——主程序 → 登录/配置/网络/数据库/升级/UI 模块,或 MainWindow → 菜单/工具栏/配置页。这是后面所有分析的索引。
- 功能列表(最重要)——表格:功能/菜单路径/入口函数/关键类/备注。每个功能记录 UI、入口、关键函数、调用链。
- 函数分类库——不要只记地址(
sub_140123450几天后全忘);建「地址 → 重命名 → 功能」表(sub_140123450 → VerifyUserPassword → 校验密码)。 - 类结构——C++ 程序尤其重要:类成员、虚表地址(VFTABLE)、成员偏移(
+0x08 m_UserInfo)。 - 关键数据结构——结构体:大小/字段偏移表(
+0x00 Magic / +0x04 Cmd / +0x08 User)。 - 消息流程——网络程序极其重要:点击登录 → 组包 → 加密 → 发送 → 接收 → 解析 → 界面。
- Hook 点和关键断点——后期最有价值:登录成功/发送网络包/AES 加密/MD5 等位置。
- 字符串索引——
"login failed" → sub_140123450,以后直接搜索。 - 最终软件地图——分析完成后形成总览树(Main → Login/Device/Update/Network),半年后重开 IDA/Ghidra/x64dbg 也能几分钟恢复上下文。
工具建议:IDA(重命名/结构体/注释)、Ghidra(Data Type Manager/Bookmark)、x64dbg(断点列表/注释库)。项目目录:
.i64 / notes.md / funcs.xlsx / structs.h / protocol.md / screenshots——notes.md 比 IDA 数据库更重要。
二、逆向知识图谱:记录关系而不是地址
三种文档(比「分析日志」价值高得多)
1 | 对象地图(最重要) Player 有哪些成员、怎么得到 Player(对象树) |
指针链怎么记
错误方式:[[[[base+20]+18]+30]+10]——版本更新全变。
正确方式:
1 | 访问路径:Module → GameManager → PlayerManager → Player |
偏移是编译结果(字段顺序/对齐/编译器优化/版本更新/继承都会改变),逻辑关系几年不变。 记语义位置不记数字。
为什么不要用 Excel
名称+地址表半年后基本废了;推荐 Obsidian/Markdown,目录:Objects / Analysis / Patterns / Structs。
三、数据视角与功能视角
- 数据视角:HP 在哪(
Player+0x10)。 - 功能视角:恢复生命有哪几种途径(改 HP / call 吃药 / hook 扣血 / 加防御 / 减伤 Buff / 无敌 / 改伤害计算)。
1 | 一个功能对应多个数据,一个数据对应多个功能 |
攻击力通常不是一个变量,会出现在人物面板/伤害计算/技能预览/装备评分/AI 评估/战斗日志等多处——记录引用点而不是单个地址:
1 | 攻击力引用点 |
终极结构是功能树形成的知识网络:功能 → 对象 → 数据 → 函数。
四、功能发现策略库:记录「为什么这样找」而不是「找到了什么」
很多逆向笔记最后变成 Attack = Player+0x1A8,但真正有价值的是我是怎么想到去找 Attack 的——地址会变,思路不会变。
记录格式:
1 | 攻击力 |
功能三层:显示值 / 计算值 / 最终值
1 | 显示值 角色面板攻击力(Attack = 100) |
很多时候找到的是 UI 显示,而不是真实值——笔记应写明类型。
各属性切入点表
1 | 血量 受伤 / 治疗 / 死亡 |
判别方法与功能定位套路库
攻击力为何总能搜到大量相似代码——因为 Attack 出现在面板/伤害计算/技能预览等多处。判别方法:
1 | 装备变化时触发、等级变化时触发、Buff变化时触发、攻击时不触发 → 属性重算函数 |
四类套路库(新游戏直接套):
1 | 属性类 攻击/防御/血量/蓝量 优先观察:装备 / Buff / 等级 |
人物状态切入点:出生/移动/攻击/受伤/死亡/复活/升级/装备变化/Buff 增删/地图切换/目标切换/选中——很多关键对象在这些事件附近暴露。
每个功能额外记录「最快路径」:
1 | 定位成功方案:✓ 装备变化 → 重算属性 → Attack |
几年后分析另一个 RPG,这种推理过程比地址还值钱。
五、找功能的实战手法
1. 难以搜索的数据:从对象和引用入手
「人物在=1、退出=0」的浮点,不要暴力搜几十万个 1.0f。五种方法:
1 | 方法一 先找 Player 对象,展开结构(+0x000 ~ +0x400 逐个标注) |
规律:
1 | 难以搜索的数据 |
2. 成倍增加数据的五种解释
「退出后全变 0」往往只是 Player 对象被清空/释放,整个结构体归零——不能证明是存在标志。数据 1,2,4,8,16 倍增的五种可能:
1 | ① 经验表 等级1→100 / 等级2→200 / 等级3→400 |
老逆向最常见的坑:浮点读成整数位掩码——内存 3F800000(显示 1.0f)实际上程序在当 uint32_t flag 用。检查清单:① 是否真被当作 float(movss vs mov)② 变化时是否对应状态开关 ③ 附近成员是否也是 1/2/4/8 模式。
3. 倍率字段的识别与验证
「默认 1.0、改成 2 就 2 倍攻击、5 就 5 倍」→ 大概率是 AttackMultiplier / DamageMultiplier / FinalDamageRate,而不是 Attack——因为正常伤害公式 Damage = Attack - Defense 或 Attack * SkillRate - Defense 下,攻击翻倍伤害不会精确翻倍。
验证方法:不要继续观察伤害,观察谁在读取它——断在该地址,若每次攻击触发 movss xmm0,[rcx+150] + mulss xmm1,xmm0(伤害 × 这个值),基本实锤是倍率。
常见误判:默认值刚好是 1.0 的 DamageMultiplier 被当成 OnlineFlag(改成 10 变 10 倍伤害才发现)。
4. 缩小搜索空间与记录失败路径
前人的优势不是「试得多」,而是缩小搜索空间:从对象关系出发(字段是否在 Player 附近)、从功能关联出发(哪些东西和攻击有关:装备/Buff/技能/等级/伤害结算)——建立 功能 → 对象 → 数据 而不是 数据 → 功能。
一改就崩溃:很多看起来像数值的内容实际是状态/索引/引用/内部控制数据。
值得记录的是失败路径(哪些路走不通):
1 | 功能:攻击倍率 |
5. 观察数据变化:快照对比与结构体展开
不打印几十万个地址,而是:
1 | ① 内存快照对比 状态A(攻击前)→ 状态B(攻击后)差分 |
隐藏属性大多通过结构体邻域分析被发现:HP 附近还有什么字段(DamageRate/CritRate/MoveSpeed 大概率也在附近),研究 Player 周围几百字节,而不是整个进程几百 MB。
6. 结构体重建:还原整个结构体
不能一键还原,但可部分还原。五种方法:
1 | ① 内存展开 直接 dump Player+0x000 ~ +0x1000 |
高手路线(拿到 Player* 之后优先做这个,而不是继续搜数值):
1 | 1. 连续 Dump 整个 Player 对象 |
六、核心结论
1 | 单个软件逆向信息 → 软件地图十层(功能列表最重要) |
真正要找的不是几十万个地址,而是少量对象、成员和访问关系;逆向文档里最值钱的不是地址和偏移,而是对象之间的关系以及如何证明这个关系存在。
与其他线的关系
- 与逆向工程论(07-工程控制/07):07 讲逆向工程是什么、记什么(对象关系>地址)、怎么证明(证据链)、怎么工程化;本文展开「记什么」的完整细节(软件地图十层、三种文档、功能发现策略库)与找功能的实战手法
- 与复刻软件(07-工程控制/04):复刻是产品层面的黑盒逆向,本文是二进制层面的信息保存;复刻的规格还原与本文的功能发现策略互补
- 与拆解程序(12-游戏/13):拆解程序是本文在游戏领域的应用(四层递进、软件地图、记录关系、功能树、数据/功能视角)
