大家好,我是一名Unity开发者,正在为一款小型独立游戏工作。我的同事经常告诉我,Unity在保护项目代码和资产方面做得不够好。我知道使用Mono是一个错误的选择,因为这相当于把项目交给陌生人。但我相信IL2Cpp脚本后端能够提供足够的保护。

但是有一天,我对如何反汇编我的项目感兴趣。 我将Il2CppDumper指向我的构建的global-metadata.dat文件,它吐出了一个DummyDll文件夹,我在Rider中打开了它。没有任何麻烦,我就能查看命名空间、方法、类名和字段名。 我不喜欢这样,因为我的构建是泄密的,我的私人GitHub存储库也毫无用处。

我开始寻找隐藏我的代码的方法。 我开始在Asset Store和GitHub中查找能够混淆我的构建的选项。 说干就干,唯一合适的资产是GuardingPearSoftware的Obfuscator Free。 它做得非常好,但免费版本不能与Unity方法、序列化字段、属性或命名空间一起工作,且完全忽略了MonoBehaviour、ScriptableObject和[Serializable]类。 有一个付费版本可以做到这一点,但80美元对于独立团队来说是一个很大的开支,因此我决定尝试制作一个自己的替代方案来作为Obfuscator Free插件的扩展。

为什么混淆器会忽略序列化类型?

每个脚本在构建中都有一个MonoScript条目在玩家数据中(level0sharedassets*.assetsresources.assetsglobalgamemanagers文件)。 它包含三个条目:m_ClassNamem_Namespacem_AssemblyName。 每个场景对象和每个预设都引用了这个条目。 如果你重命名一个类的DLL,Unity将无法绑定到该类型 - 且组件将变成一个丢失的脚本。

解决方案

我意识到这一点后,我开始寻找绕过它的方法。 我发现了一个既聪明又荒谬的方法:我生成混淆后的名称与原始名称相同的字符数。这让我避免了调整长度前缀、重新计算偏移量以及开发重写序列化文件的工具。 由于这个意外发现的技巧,我肯定可以节省一周的失眠时间! 但是重要的是要注意,即使使用这个方法,你仍然需要存储一大堆带有混淆名称的文件。 所有项目名称都在预先保留,因此你无法生成一个已经存在的名称。

纯C#类型没有MonoScript条目,因此它们的命名空间被缩短到我想使用的任何短、无意义的字符串(_Project.Code.AssetManagementpqmpqu)。 反之,序列化类型被迫维持长度一致性(_Project.Code.Sounddis4nAw5EW74Z6wLxDN)。

找到条目而不破坏文件

你不能简单地在文件中搜索“PlayerController”并覆盖它。 那个同样字符串可能是一个GameObject名称、一个字符串字面量、一个可寻址键 - 如果你击中了错误的键,你就不会喜欢调试。

所以我在三个长度前缀的连续条目上锚定:[len][m_ClassName][len][m_Namespace][len][m_AssemblyName],它们之间有4字节的对齐。 三个字符串的连续匹配以及正确的对齐是一个足够强的信号。 我还将m_Name几字节更早的位置进行了修补 - 它是类名的第二次出现,且它会很乐意泄露你刚刚隐藏的一切。

堆栈跟踪问题

我还必须编写自己的堆栈跟踪反混淆器,因为任何异常都会使跟踪对人类来说完全不可读,例如hdektk.uHN1wAaEGHkYq6ul.qoaktw()。 每个构建都会写入JSON映射文件(_Project.Code.AssetManagementpqmpqu),并且有一个编辑器窗口,我可以在其中粘贴从Player.log文件中的原始堆栈跟踪。 它有四个阶段,反向顺序:GUPS反混淆器 → 我的序列化类型名称 → 我的命名空间 → 剩余的成员名称。

什么不需要重命名?

Unity的消息方法:AwakeOnTriggerEnter2DOnBecameInvisible和大约六十个其他方法 - 引擎会通过名称调用它们。 虚拟、接口和覆盖的成员,因为重命名实现但不重命名声明会破坏虚表条目。 任何在运行时通过名称序列化的 - 我的保存是JSON,重命名一个属性会使所有现有的保存文件都无法使用。

当前构建的结果

40个命名空间被重命名。82个MonoBehaviour/ScriptableObject类型被重命名并在玩家数据中修补。848个成员。

是否值得做?

混淆是一个障碍,而不是一个防御。 anyone决心要解决它的人仍然会实现目标,使用调试器,且我更喜欢坦白这一点而不是假装不存在。 对于一名 solo开发者来说,一旦建立了一次构建步骤并且忘记了它,这是我的意见,是值得花费时间和精力。

我没有把我的扩展发布到任何地方 - 它是我的自己的构建管道中的一部分,是standalone-only的,并且有很多我不想在公开中辩护的角落。 但是你不需要我的代码就可以开始了,这才是这个帖子的真正目的。

做这个而不是做其他的。 运行Il2CppDumper或AssetRipper在自己的构建上 - 五分钟,观看自己的架构滑过会完全不同于知道它在那里。 然后安装Obfuscator Free,列出你的程序集,构建。 免费,一个晚上,会保护你的纯C#类型和方法。

这只会让你比大多数今天发布的Unity构建更强大。 所有上述内容都是第二步 - 你会知道什么时候需要它。

我为Peak or Die开发了这一切 - 一款基于扑克牌的生存策略游戏。 如果你感兴趣,请在Steam wishlist中添加它:https://store.steampowered.com/app/4273370