1. 被动分析到底在分析什么1.1 从“不动代码”说起很多人第一次听到“被动分析”这四个字脑子里冒出来的画面是坐在电脑前盯着屏幕发呆。其实恰恰相反被动分析是逆向工程里信息密度最高、最考验耐心和逻辑的一类工作。它的核心原则只有一条不修改目标程序的任何字节不改变它的运行逻辑只通过观察、记录、比对来还原程序的设计意图。你可能会问不修改代码怎么理解程序打个比方你面前有一台正在运转的钟表主动分析相当于把表拆开、拨动齿轮、看每个零件怎么咬合被动分析则是坐在旁边听它走时的声音、看指针的节奏、记录它每天误差几秒然后反推出内部结构。前者直接但破坏性强后者温和但需要更强的推理能力。在游戏逆向这个场景里被动分析的价值尤其突出。游戏程序往往有反调试、完整性校验、内存加密等保护机制你稍微动一下代码就可能触发检测轻则崩溃重则封号。而被动分析全程只读不触碰任何敏感操作是安全系数最高的切入点。1.2 被动分析能拿到哪些关键信息具体来说被动分析能帮你拿到几类硬通货调用关系谁调用了谁调用频率如何调用时的参数长什么样。这相当于给程序画一张人际关系图。交叉引用某个数据地址被哪些指令读写过某个函数被哪些地方跳转进入。这是定位关键逻辑的指南针。数据流一个值从产生到消费经过了哪些寄存器、哪些内存地址、哪些运算。这是理解算法的心脏。时序特征函数执行耗时、调用间隔、循环次数。这能帮你区分主逻辑和辅助逻辑。这四类信息组合起来基本就能在不改一行代码的前提下把程序的核心骨架摸清楚。热搜词里提到的“调用关系分析”“交叉引用分析”“数据流分析”正是被动分析的三大支柱。1.3 适合谁来学这套方法如果你刚接触逆向建议先把被动分析练熟。因为它不需要你懂汇编优化、不需要你会写注入器、不需要你绕过反调试只需要你会用调试器和分析工具的基本观察功能。门槛低但天花板很高——真正的高手往往是被动分析做得最扎实的那批人。如果你已经有一定基础被动分析能帮你建立系统化的分析习惯。很多人逆向时东一榔头西一棒子看到哪算哪效率极低。被动分析强迫你先观察、先记录、先画图再动手验证这种工作流一旦养成分析任何程序都能事半功倍。2. 调用关系分析把程序的人际网络画出来2.1 调用关系为什么重要一个游戏程序动辄几万个函数你不可能一个一个看。但如果你知道某个函数被谁调用、它又调用了谁就能像顺藤摸瓜一样从入口点一路摸到核心逻辑。调用关系就是程序的骨架图有了它你才知道哪些函数是主干、哪些是枝叶、哪些是死代码。举个实际例子。假设你想找到游戏里“计算伤害”的函数。直接搜索字符串“damage”可能一无所获因为游戏可能用加密字符串或者根本不出现这个词。但如果你先找到“攻击按钮点击”的处理函数然后看它调用了哪些子函数再逐层往下跟大概率能在三四层调用之内找到伤害计算逻辑。这就是调用关系分析的价值用已知推未知用外围推核心。2.2 静态调用图与动态调用栈调用关系分析分两条路静态和动态。静态调用图是在不运行程序的情况下通过反汇编工具扫描所有指令找出所有call指令及其目标地址然后构建一张有向图。优点是覆盖全能发现冷门分支缺点是遇到间接调用比如call eax、call [edx0x10]就抓瞎因为目标地址在运行时才能确定。动态调用栈则是在程序运行时通过调试器或插桩工具记录每次函数调用的现场。优点是准确能抓到间接调用的真实目标缺点是只能覆盖你实际触发的路径没走到的分支就看不到。我的习惯是两者结合先用静态调用图圈定大范围再用动态调用栈验证关键路径。这样既不会漏也不会被间接调用卡死。2.3 实操用调试器抓取调用栈以常见的调试器为例抓取调用栈的操作流程大致如下在目标函数入口下断点。如果你还不知道目标函数在哪可以先在某个已知的外围函数比如按钮响应下断。断点触发后查看调用栈窗口。大多数调试器都会显示当前线程的调用链从最内层函数一直列到线程入口。逐层点击调用栈中的每一帧观察对应的汇编代码和寄存器状态。重点关注调用前的参数压栈操作。记录下每一层的函数地址、调用指令地址、传入参数。这些数据就是你后续分析的原材料。注意有些游戏会使用栈混淆技术故意破坏调用栈的可读性。遇到这种情况不要硬跟先退回到上一层从更稳定的调用点重新切入。2.4 调用频率与调用间隔的隐藏信息除了调用关系本身调用的频率和间隔也是重要线索。比如一个函数每帧都被调用那它大概率是渲染或物理更新相关的如果一个函数只在特定事件发生时被调用一次那它可能是事件处理逻辑。我习惯在抓调用栈的同时用脚本记录每个函数的调用次数和时间戳。积累一段时间后就能画出一张“函数活跃度热力图”。这张图能帮你快速区分主循环函数、事件回调函数和工具函数。工具函数通常调用频繁但耗时极短主循环函数调用规律但耗时较长事件回调则是不规律出现。3. 交叉引用分析定位关键数据的指南针3.1 交叉引用的两种类型交叉引用Cross Reference简称XREF分两种代码引用和数据引用。代码引用是指某条指令跳转到了某个地址或者调用了某个函数。比如“函数A被函数B、C、D调用过”这就是代码交叉引用。数据引用是指某条指令读写了一个内存地址。比如“地址0x123456被指令1读取、被指令2写入”这就是数据交叉引用。在逆向游戏时数据交叉引用往往比代码交叉引用更有价值。因为游戏的核心数据——血量、金币、坐标、背包物品——都存放在内存里找到这些数据的读写点就等于找到了操作这些数据的代码。3.2 从数据地址反推代码逻辑假设你已经通过内存扫描找到了金币数量的地址。接下来要做的就是查看这个地址被哪些指令访问过。在调试器的数据窗口里右键点击该地址选择“查找写入引用”或“查找读取引用”。调试器会列出所有访问过这个地址的指令地址。通常写入引用比读取引用更关键因为写入意味着数据被修改而修改逻辑往往就是游戏的核心逻辑。拿到写入指令地址后反汇编查看该指令所在的函数。这个函数大概率就是“增加金币”“扣除金币”或“同步金币显示”的逻辑。再结合调用关系分析就能顺藤摸瓜找到金币的完整生命周期。3.3 实操用交叉引用定位背包逻辑背包系统是游戏逆向的经典练手目标。具体操作步骤如下在游戏中获得一件新物品记录物品数量变化前后的内存值。用内存扫描工具找到物品数量的地址。对该地址下硬件写入断点触发一次物品数量变化。断点触发后查看当前指令和所在函数。这个函数就是物品数量更新逻辑。查看该函数的调用者以及它调用的其他函数。通常你会看到“添加物品”“移除物品”“检查背包容量”等一组相关函数。对背包数组的基地址做交叉引用分析找到背包的遍历逻辑和渲染逻辑。提示硬件断点数量有限通常只有4个所以优先对最关键的地址下断。如果地址是动态的可以先下内存访问断点等拿到稳定地址后再换硬件断点。3.4 交叉引用分析中的常见陷阱第一个陷阱是地址复用。游戏内存中同一个地址在不同时间可能存放不同数据。比如一个临时缓冲区上午存的是玩家坐标下午存的是伤害数值。如果你不确认当前上下文交叉引用分析就会把你带到完全无关的代码里。第二个陷阱是多级指针。很多游戏数据不是直接存放在固定地址而是通过“基址偏移”的多级指针访问。你找到的地址可能只是某一级指针的值真正的数据在更深层。这时候需要做指针扫描找到稳定的基址和偏移链。第三个陷阱是加密存储。有些游戏会对关键数据做简单加密比如金币数值实际存储的是“真实值异或0x1234”。你扫描到的地址存的是加密后的值直接下断点看到的写入逻辑也是加密逻辑。这时候需要先逆向出加密算法才能理解数据流。4. 数据流分析追踪一个值的完整旅程4.1 数据流分析的目标数据流分析要回答的问题是一个值从哪里来经过了哪些运算最终到哪里去。在游戏逆向中这通常意味着追踪某个关键数值比如伤害、血量、坐标的完整计算链路。举个例子。你想知道游戏里的伤害是怎么算出来的。通过交叉引用你找到了“写入伤害值”的指令。但这条指令只是把计算结果存到内存真正的计算逻辑在更早的地方。数据流分析就是沿着这条写入指令往回追看伤害值是由哪些寄存器、哪些内存值、经过哪些运算得到的。4.2 寄存器级追踪与内存级追踪数据流分析可以在两个层面进行寄存器级和内存级。寄存器级追踪关注的是值在CPU寄存器之间的流转。比如“eax来自ebxebx来自[ebp-4][ebp-4]来自函数参数”。这种追踪精度高但工作量大因为寄存器会被反复覆盖。内存级追踪关注的是值在内存地址之间的流转。比如“伤害值先存在0x1000然后复制到0x2000最后写入0x3000”。这种追踪更宏观适合理解数据的整体流向但容易漏掉中间的寄存器运算。我的做法是先用内存级追踪画出大流程图再用寄存器级追踪填充关键节点的计算细节。这样既有全局视野又不失精度。4.3 实操追踪一次伤害计算的完整链路假设你已经找到了“写入最终伤害值”的指令。接下来按以下步骤回溯查看该指令的源操作数。如果源操作数是寄存器查看该寄存器上一条被写入的指令。重复步骤1直到源操作数变成内存地址或常量。如果是内存地址对该地址做交叉引用分析找到它的写入点。如果是常量记录下来这可能是伤害公式中的系数。把整条链路画成图常量→内存→寄存器→运算→寄存器→内存。在关键运算指令处下断点观察实际运行时的值验证你的推断。注意游戏伤害计算往往涉及浮点运算和随机数。浮点寄存器xmm系列的追踪比通用寄存器更麻烦因为浮点指令的源操作数格式不同。建议先追踪整数部分再单独处理浮点部分。4.4 数据流分析中的简化技巧数据流分析很容易陷入细节泥潭。我的经验是先抓主干再补枝叶。具体来说先只追踪关键值的直接来源忽略中间的工具函数和辅助计算。比如伤害计算可能调用了“取随机数”“查暴击表”“应用防御减免”等多个子函数你先不要进这些子函数只记录“伤害值 基础伤害 × 随机系数 × 暴击系数 × 防御系数”这个主干公式。等主干清楚了再逐个进入子函数看具体实现。另一个技巧是用颜色标记。在画数据流图时用不同颜色标记不同来源的值红色表示来自玩家输入蓝色表示来自配置文件绿色表示来自随机数黄色表示来自其他系统。这样一眼就能看出哪些值是可控的、哪些是固定的、哪些是随机的。5. 被动分析的完整工作流5.1 从入口到核心的渐进式分析被动分析不是一上来就直奔核心逻辑而是从程序入口开始一层一层往里剥。典型的渐进式分析流程如下确定入口点游戏主模块的入口通常是WinMain或main但实际游戏逻辑的入口可能是某个初始化函数或消息循环。观察主循环找到游戏的主循环通常是PeekMessage/DispatchMessage循环或自定义的帧循环观察它每帧调用了哪些函数。标记关键系统在主循环的调用列表中识别出渲染、物理、输入、网络、UI等子系统。深入目标系统选择你最关心的系统比如战斗系统深入它的调用链。定位核心数据在目标系统中找到关键数据的内存地址做交叉引用分析。追踪数据流对关键数据做完整的数据流追踪还原算法逻辑。这个流程的好处是每一步都有明确的产出不会迷失方向。即使中途卡住也可以退回到上一步重新选择路径。5.2 工具链的选型与配置被动分析常用的工具分几类工具类型代表工具主要用途注意事项反汇编器IDA、Ghidra静态分析、交叉引用、调用图加载时选对处理器架构和基址调试器x64dbg、WinDbg动态追踪、断点、调用栈注意反调试检测优先用只读操作内存扫描器Cheat Engine数据定位、指针扫描扫描时注意数据类型和范围追踪工具API Monitor、Procmon系统调用、文件注册表监控过滤无关进程减少噪音脚本引擎Python、Lua自动化记录、数据处理脚本只读不写避免触发保护工具选型的原则是静态工具看全局动态工具看细节脚本工具做记录。三者配合才能既快又准。5.3 记录与归档让分析结果可复用被动分析会产生大量中间数据函数地址、调用关系、数据地址、数据流图、断点记录。如果不及时归档过两天你自己都看不懂。我的归档习惯是每个函数一个条目记录地址、功能推测、调用者、被调用者、关键参数。每个关键数据一个条目记录地址、类型、含义、读写点、数据流图。每次断点触发记录时间、线程、寄存器快照、调用栈。所有条目用统一格式的文本文件保存方便搜索和比对。提示不要依赖调试器的保存功能很多调试器的会话文件格式不通用。用纯文本或Markdown记录虽然原始但最可靠。6. 常见问题与排查技巧实录6.1 断点不触发怎么办断点不触发是被动分析中最常见的问题。原因通常有几类地址不对你下的地址是静态地址但程序实际加载基址变了。解决方法是使用模块基址偏移的方式下断。代码没执行你下断的函数在当前场景下根本没被调用。解决方法是先确认触发条件比如先执行某个操作再下断。断点被绕过游戏有反调试检测到断点后跳过了该指令。解决方法是换用硬件断点或内存断点。线程不对断点只在特定线程生效但你当前停在别的线程。解决方法是切换线程或对所有线程下断。排查顺序建议先确认地址再确认执行路径最后怀疑反调试。6.2 交叉引用结果太多怎么筛有时候一个数据地址有几百条交叉引用根本看不过来。这时候需要筛选策略优先看写入引用忽略读取引用。优先看条件跳转附近的引用忽略无条件跳转。优先看循环体内的引用忽略循环外的。优先看调用指令附近的引用忽略独立指令。如果还是太多可以先用动态断点触发一次看实际命中的是哪个引用。以实际命中点为中心向周围扩展分析。6.3 数据流追到一半断了怎么办数据流追踪断掉通常是因为遇到了间接寻址、加密运算或系统调用。应对方法间接寻址在运行时下断观察实际地址。静态分析看不到的动态能看到。加密运算先识别加密算法异或、加减、查表逆向出解密逻辑再继续追踪。系统调用数据可能传入了系统API这时候需要结合API监控工具看数据传给了哪个系统调用。经验数据流断掉的地方往往就是游戏保护最严密的地方。不要硬闯先绕过去从其他路径逼近。6.4 常见问题速查表问题现象可能原因排查方法解决技巧断点不触发地址错误/未执行/反调试检查基址、确认路径、换断点类型用模块偏移先触发再下断交叉引用过多数据被广泛使用筛选写入引用、动态验证以实际命中点为中心扩展数据流中断间接寻址/加密/系统调用动态下断、识别算法、API监控绕路逼近不硬闯调用栈混乱栈混淆/多线程切换线程、查看原始栈内存从稳定调用点重新切入内存地址变化动态分配/多级指针指针扫描、基址定位用偏移链代替绝对地址7. 被动分析的心法与进阶方向7.1 耐心比技术更重要被动分析的技术门槛其实不高会用调试器和反汇编器就能开始。真正拉开差距的是耐心。同一个函数有人看一遍就过有人反复看十遍每遍都有新发现。我见过太多人卡在“这个函数我好像看过了”的错觉里其实根本没看懂。我的习惯是每个关键函数至少看三遍。第一遍看整体结构第二遍看指令细节第三遍看数据流和边界条件。三遍下来这个函数基本就刻在脑子里了。7.2 从被动到主动的平滑过渡被动分析做扎实了向主动分析过渡就是水到渠成的事。因为你已经知道了函数地址、数据结构、调用关系这时候再写注入器、打补丁、做Hook就是按图施工风险可控。但切记被动分析阶段绝对不要修改任何东西。哪怕你发现了一个明显的bug也不要顺手改掉。一旦你改了后续所有观察结果都可能失真而且可能触发游戏的完整性校验。保持只读是 passive 分析的生命线。7.3 进阶方向自动化与可视化当你手动分析过几个游戏后可以考虑把重复劳动自动化。比如写脚本自动抓取调用栈、自动记录交叉引用、自动生成数据流图。Python配合调试器的脚本接口能大幅提升效率。可视化也是进阶方向。把调用关系画成图、把数据流画成管道、把内存布局画成地图这些视觉化表达能帮你更快发现模式和异常。我常用Graphviz画调用图用自定义的HTML页面画数据流效果比纯文本好得多。最后分享一个小技巧定期回顾你的分析笔记。很多当时没看懂的线索过几天再看就豁然开朗了。被动分析是一场持久战笔记就是你的弹药库。