ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

dnSpy 拆解 Unity 程序集:Mono 与 IL2CPP 下的逆向分析实战

2026/9/26 21:54:25 拓冰建站 浏览量
dnSpy 拆解 Unity 程序集:Mono 与 IL2CPP 下的逆向分析实战 简介这份资源是面向 Unity 游戏开发与逆向分析学习者的 dnSpy 反编译工具完整包适合需要查看、调试与修改 .NET 程序集的中高级开发者使用。压缩包共收录 1736 个文件以 1583 个 dll 程序集为核心辅以 76 个 pdb 调试符号、26 个 json 与 24 个 xml 配置数据、8 个 dntheme 界面主题及 6 个 exe 可执行程序整体约 134.32MB结构完整可直接用于反编译环境搭建。借助该工具读者能够把 Unity 项目中的托管代码还原为可读的 C# 逻辑配合调试符号定位关键方法进而分析游戏运行机制、排查异常或学习优秀项目的代码组织方式。资源配套有详细的操作方法参考文档便于快速上手。目前已有 3929 人学习下载适合希望深入理解 Unity 底层实现与 .NET 逆向流程的开发者参考使用。1. dnSpy 拆 Unity 程序集先搞清楚它到底能看什么、不能看什么拿到一个 Unity 打包出来的游戏或工具想看看某个逻辑是怎么写的很多人第一反应是找 dnSpy。这个思路本身没错但前提是你得先明白 Unity 的代码到底以什么形态躺在安装目录里。Unity 用 C# 写逻辑编译产物是 IL 中间语言最终落在Managed文件夹下的Assembly-CSharp.dll、Assembly-CSharp-firstpass.dll以及各种第三方库 dll 里。dnSpy 能做的是把这些 IL 反编译回接近原始 C# 的可读代码还能直接改 IL、改方法体、重新保存 dll。它不能做的是把已经用 IL2CPP 后端转成 C 再编译成机器码的逻辑还原成 C#——那种情况你打开GameAssembly.dll只会看到一堆汇编。所以判断能不能用 dnSpy第一步永远是看Managed目录还在不在。这篇内容面向的是需要排查线上逻辑、做兼容适配、研究第三方 SDK 行为的 Unity 开发者以及刚接触程序集分析、想找一条可复现路径的工程师。下面从目录结构判断讲到实际操作再到参数和踩坑尽量让你照着能跑通一遍。2. 判断目标程序集形态与 dnSpy 的适用边界2.1 先看 Managed 目录Mono 与 IL2CPP 的分水岭Unity 的脚本后端有两种常见选择Mono 和 IL2CPP。Mono 后端会把 C# 编译成 IL打包成 dll 放在*_Data/Managed/下IL2CPP 后端则先把 IL 转成 C再编译成平台原生代码Managed目录里通常只剩少量元数据 dll核心逻辑全在GameAssembly.dllWindows或libil2cpp.soAndroid里。dnSpy 只对前者有效。判断方法很直接进到安装目录找*_Data文件夹# Windows 下查看 Managed 目录内容 dir 你的游戏名_Data\Managed # macOS 下 ls /Applications/你的游戏名.app/Contents/Resources/Data/Managed # Android 解包后apk 本质是 zip unzip -l yourgame.apk | grep -i managed\|assembly如果看到Assembly-CSharp.dll体积在几百 KB 到几 MB基本可以确定是 Mono 后端dnSpy 能直接吃。如果Managed里只有mscorlib.dll、System.dll这类基础库而Assembly-CSharp.dll只有几 KB 甚至不存在同时存在一个几十 MB 的GameAssembly.dll那就是 IL2CPPdnSpy 帮不上核心逻辑的忙。这里有个容易翻车的点有些项目为了减小体积或做混淆会把Assembly-CSharp.dll改名或拆分成多个 dll。这时候不要只盯着文件名用 dnSpy 打开Managed下所有 dll 逐个看命名空间和类型往往能找到被改名的逻辑程序集。2.2 dnSpy 能改什么、改完怎么生效dnSpy 的核心能力有三层查看反编译后的 C#、编辑方法体或 IL 指令、保存回 dll。很多人以为改完保存就完事实际上 Unity 加载 dll 的时机和路径决定了改动是否生效。对于 Mono 后端Unity 在启动时从Managed目录加载 dll。你用 dnSpy 改完Assembly-CSharp.dll并保存覆盖原文件下次启动就会加载新版本。但要注意如果游戏有完整性校验比如比对 dll 哈希改完可能直接启动失败。常见做法是先在副本上改确认逻辑无误后再考虑绕过校验而不是一上来就动原文件。编辑时优先改方法体而不是签名。改签名会牵连调用方容易导致MissingMethodException。如果只是想看某个值怎么算出来的用 dnSpy 的调试功能附加到进程在目标方法下断点比直接改代码更安全。// 反编译后常见的 Unity 方法形态dnSpy 里看到的大致是这样 private void Update() { // 这里可能是被混淆过的变量名但控制流通常还看得懂 if (this.isActive Time.time this.nextTick) { this.nextTick Time.time 0.5f; this.CheckState(); } }上面这段不是让你抄而是说明 dnSpy 反编译出来的代码结构字段访问、方法调用、条件分支基本保留混淆主要影响命名。看到num、text、flag这类变量名不要慌顺着调用链和字符串常量往往能还原意图。2.3 混淆与去混淆的现实预期Unity 项目常用的混淆手段包括改名、控制流平坦化、字符串加密。dnSpy 本身不带去混淆功能它只是把 IL 翻译成 C#。遇到改名混淆你能看到逻辑但读起来费劲遇到控制流平坦化反编译出来的代码可能是一大坨 switch-case需要手动或借助其他工具还原。一个实用技巧先找字符串。dnSpy 里按CtrlShiftK可以搜索字符串常量很多关键逻辑附近会有 URL、错误提示、配置键名。从字符串反查引用它的方法比从头读混淆代码快得多。如果字符串也被加密那就得先定位解密方法在解密后的位置下断点看运行时值。提示dnSpy 对 .NET Framework 和 .NET Core 程序集的支持有差异。Unity 老版本多用 .NET Framework 兼容层新版本开始转向 .NET Standard。如果打开 dll 报错先确认目标框架版本必要时换用对应版本的 dnSpy 分支。3. 用 dnSpy 定位 Unity 关键逻辑的完整操作路径3.1 加载程序集与建立符号索引打开 dnSpy把Managed目录下所有 dll 拖进去或者用File - Open逐个加载。加载完成后左侧程序集列表会列出所有命名空间和类型。这时候不要急着点开Assembly-CSharp先做一件事在View - Assembly Explorer里确认所有依赖 dll 都已解析。如果有黄色感叹号说明缺少引用反编译出来的代码会有大量unknown类型影响阅读。建立索引的常用操作# 如果 dll 很多可以先用命令行工具列出所有类型快速定位目标 # 这里用 monodis 或 ilspycmd 做批量导出dnSpy 本身没有命令行批量模式 ilspycmd -l c 你的游戏名_Data\Managed\Assembly-CSharp.dll types.txt # 然后搜索关键词 findstr /i player health damage types.txtilspycmd是 ILSpy 的命令行版本和 dnSpy 同源适合做批量类型列表。拿到类型名后再回 dnSpy 精读。这一步能省掉在 GUI 里翻半天的时间。3.2 从字符串和 MonoBehaviour 入口反查逻辑Unity 的逻辑入口通常是MonoBehaviour的Awake、Start、Update。在 dnSpy 里搜索: MonoBehaviour可以列出所有挂载脚本。但大型项目里这类类型成百上千更高效的方式是从字符串入手。假设你想找某个弹窗的触发条件先搜弹窗上的文案// dnSpy 搜索字符串 每日奖励已领取 后找到引用它的方法 // 反编译结果可能类似 private void OnDailyRewardClaimed() { this.rewardPanel.SetActive(false); this.claimButton.interactable false; PlayerPrefs.SetInt(daily_claimed, 1); PlayerPrefs.Save(); }从这段就能看出状态存在PlayerPrefs的daily_claimed键里。接着搜daily_claimed的其他引用就能找到判断是否可领取的逻辑。这种顺藤摸瓜的方式比盲目读代码可靠得多。参数说明PlayerPrefs是 Unity 的本地持久化方案键名和值类型在反编译代码里通常保留原样是定位逻辑的强线索。如果键名也被混淆成str_0x1a2b那就需要结合运行时调试看实际写入的值。3.3 编辑 IL 与重新保存的注意事项dnSpy 支持直接编辑 C# 方法体右键方法选Edit Method (C#)改完点编译它会生成对应 IL。保存时选File - Save Module覆盖原 dll 或另存为新文件。几个必须注意的点第一改完的方法如果引用了原程序集里不存在的类型或方法编译会失败。dnSpy 的编辑器不是完整编译器它只做局部替换所以尽量只改方法内部逻辑不要新增外部依赖。第二保存后的 dll 可能丢失强名称签名。如果原 dll 有强名称Unity 加载时可能校验失败。解决办法是用sn -Vr跳过验证或者用工具重新签名。这一步在 Mono 后端下不一定触发但遇到StrongNameException就要往这个方向查。第三改完先备份原 dll。血泪经验有一次改完直接覆盖结果游戏启动黑屏原文件也没了只能重新解包。养成Assembly-CSharp.dll.bak的习惯。# 保存前先备份 copy Assembly-CSharp.dll Assembly-CSharp.dll.bak # 改完后对比文件大小和修改时间确认确实写入了 dir Assembly-CSharp.dll*3.4 附加调试与运行时验证静态看代码只能确认逻辑意图实际值还得靠调试。dnSpy 的Debug - Attach to Process可以附加到正在运行的 Unity 进程。附加后在目标方法下断点触发条件时就能看到调用栈和局部变量。常见问题Unity 的 Mono 运行时默认可能不允许调试器附加需要在启动参数里加--debug或确保没有开启代码优化。如果断点显示空心圆说明符号没加载或方法被内联。这时候可以尝试在方法入口加[MethodImpl(MethodImplOptions.NoInlining)]但改这个需要重新编译 dll不如直接在调用方下断点。调试时关注三个窗口Locals看局部变量Call Stack看调用链Watch手动加表达式。对于混淆过的变量名可以在Watch里输入this展开所有字段对照反编译代码里的字段顺序来对应。4. dnSpy 处理 Unity 程序集时的避坑与排查4.1 打开 dll 报 “Invalid method” 或类型加载失败现象dnSpy 加载某个 dll 时弹出错误部分类型显示为红色或无法展开。原因dll 被裁剪过比如 Unity 的 managed stripping或者依赖的另一个 dll 没加载导致元数据不完整。IL2CPP 项目里残留的元数据 dll 也容易出现这种情况。解决先把Managed下所有 dll 一次性全部加载不要只拖一个。如果仍然报错用ilspycmd或monodis检查 dll 的元数据表是否完整。对于裁剪过的 dll能看的逻辑有限考虑从 IL2CPP 的global-metadata.dat方向另找方案。4.2 反编译出来的代码全是乱码变量名现象方法体逻辑能看懂但变量名是num1、text2、flag3字段名是m_0、m_1。原因项目用了混淆工具如 Obfuscar、ConfuserEx做了重命名。dnSpy 不做去混淆只能原样展示。解决从字符串、API 调用、控制流结构反推含义。优先看有字符串常量的方法这些地方混淆工具通常不会改字符串内容。另外Unity 的序列化字段名在 Inspector 里可能保留原样可以对照场景文件或 Prefab 里的字段名来映射。4.3 改完 dll 游戏启动崩溃或行为没变化现象保存 dll 后启动游戏要么闪退要么改动的方法似乎没被执行。原因三种可能。一是改错了 dll项目有多个Assembly-CSharp变体二是游戏从其他路径加载了 dll比如从StreamingAssets或热更新目录三是改动被完整性校验拦截。解决先确认游戏实际加载的 dll 路径。用Process Monitor过滤CreateFile操作看进程启动时读了哪个 dll。如果是热更新项目逻辑可能在Assets/StreamingAssets下的 AssetBundle 里需要先解包再改。完整性校验的话搜MD5、SHA、File.ReadAllBytes相关调用定位校验点。4.4 调试附加后断点不命中现象dnSpy 显示已附加到进程但下断点后一直不触发。原因方法被内联、JIT 优化跳过了调试信息或者附加的进程不是实际执行逻辑的进程比如 Unity 有多个子进程。解决在Debug - Options里关闭“仅我的代码”开启“显示所有断点”。如果方法短小且被频繁调用JIT 可能内联改在调用方下断点。多进程情况下用Debug - Attach to Process时看清楚进程名和 PIDUnity 的编辑器进程和打包后的可执行文件进程是两回事。4.5 保存时提示 “Cannot save module”现象编辑完方法后保存dnSpy 报错无法写入。原因dll 被其他进程占用比如游戏还在运行或者文件只读或者 dnSpy 没有写权限。解决关闭占用 dll 的进程检查文件属性去掉只读必要时以管理员身份运行 dnSpy。如果 dll 在系统保护目录下先复制到工作目录再改。5. 从 dnSpy 静态分析到 IL2CPP 场景的衔接技巧dnSpy 在 Mono 后端下是利器但越来越多 Unity 项目默认用 IL2CPP。这时候直接硬刚GameAssembly.dll不现实更务实的路径是先用 dnSpy 分析残留的元数据 dll拿到类型和方法签名再用 IL2CPP 专用工具如 Il2CppDumper导出结构两者对照。具体做法把Managed下所有 dll 用 dnSpy 打开导出类型列表和方法签名。然后用 Il2CppDumper 处理GameAssembly.dll和global-metadata.dat得到dump.cs。两份数据按方法名或 RVA 对齐就能在 IL2CPP 的汇编层面定位到对应逻辑。dnSpy 在这里的角色是提供“语义地图”不是直接改代码。另一个技巧是关注UnityEngine和mscorlib的版本。dnSpy 打开 dll 时会在底部显示目标框架如果显示.NET 4.x而项目实际用.NET Standard 2.1反编译结果可能有细微差异。遇到 API 对不上时先核对框架版本。我自己的习惯是拿到任何 Unity 项目先花五分钟判断后端类型Mono 就直接上 dnSpyIL2CPP 就转 IL2CPP 工具链不在这上面浪费时间。改 dll 之前一定备份调试优先于硬改。这套流程跑顺了大部分逻辑排查都能在一两个小时内出结果。希望帮到你。本文还有配套的精品资源点击获取