知物由学 | 如何对il2cpp进行加固保护?

发布者:网易易盾
发布于:2021-01-05 17:40

Unity3D是一款非常出名的游戏引擎,许多知名游戏就是基于该引擎进行开发的。它最大的一个特点是一次制作,多平台部署,而这一核心功能是靠Mono实现的。可以说Mono是Unity3D核心的核心,是Unity3D跨平台的根本。但是在2015年发布Unity5的时候,Unity3D官方推出了il2cpp。根据Unity官方给出的解释,推出il2cpp的原因主要是以下几点:

1. C#的运行效率还是远落后于C/C++

2. mono版本授权受限,无法使用.NET的许多新特性 

3. mono VM在各平台的移植和维护都非常耗时 

4. 更稳定可靠的垃圾回收机制

那么,在游戏代码的安全性上,mono和il2cpp两种模式会有哪些区别呢?使用mono编译后的游戏,会将C#脚本代码编译成IL中间语言后打包到客户端中。

以android端为例,相关脚本代码会放在apk里assets\bin\Data\Managed\目录下的Assembly-CSharp.dll中。使用il2cpp编译后的游戏,会将C#脚本代码编译成native代码,最后是在lib目录下对应架构的libil2cpp.so文件里。

可能有些开发者会觉得,代码放在so里面,反汇编分析起来也很累人,那么游戏的安全性应该能够大大增强了吧?

但是,il2cpp编译后会生成一个global-metadata.dat的文件,这个文件包含了大量的符号信息,有了这些符号信息,可以大幅降低逆向成本。更有热心开发者,开发了一个名为Il2CppDumper的工具(eb2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6b7k6i4u0X3j5i4u0W2i4K6u0r3d9h3H3J5b7%4m8H3c8s2g2E0M7r3g2J5i4K6t1&6i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1#2i4K6S2r3i4@1q4r3i4@1f1@1i4@1u0n7i4@1p5#2i4@1f1%4i4K6W2n7i4@1t1@1i4@1f1$3i4K6S2q4i4@1p5#2i4@1f1#2i4@1t1H3i4K6R3$3K9h3H3J5j5%4m8H3i4K6u0W2M7$3!0Q4c8e0S2Q4b7V1k6Q4z5e0S2Q4c8e0g2Q4z5p5g2Q4z5f1k6Q4c8e0c8Q4b7U0S2Q4b7V1q4V1L8r3I4Q4c8e0k6Q4z5e0k6Q4z5o6N6Q4c8e0c8Q4b7V1u0Q4b7U0k6Q4c8f1k6Q4b7V1y4Q4z5p5y4Q4c8e0g2Q4b7U0W2Q4b7U0k6Q4c8e0c8Q4b7U0S2Q4z5e0c8Q4c8e0S2Q4b7V1k6Q4z5e0S2Q4c8e0S2Q4z5o6y4Q4b7V1c8Q4c8e0g2Q4b7e0c8Q4z5f1k6Q4c8e0S2Q4z5o6N6Q4b7f1q4Q4c8e0g2Q4z5p5q4Q4b7e0S2Q4c8e0N6Q4z5e0c8Q4z5f1k6Q4c8e0k6Q4z5o6S2Q4z5e0m8A6L8o6u0U0M7s2m8Q4x3X3g2K6L8#2!0q4y4g2!0m8c8W2!0n7z5g2!0q4y4g2!0n7b7g2)9&6y4q4!0q4y4#2)9&6b7g2)9^5y4r3W2V1j5g2)9J5k6i4m8&6i4@1f1^5i4K6R3@1i4K6W2m8i4@1f1$3i4K6W2o6i4@1q4o6i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1^5i4@1u0r3i4K6W2n7i4@1f1@1i4@1t1^5i4K6R3H3i4@1f1$3i4@1q4p5i4@1p5#2i4@1f1$3i4K6V1$3i4@1t1&6i4@1f1@1i4@1u0q4i4@1u0r3i4@1f1$3i4K6V1@1i4@1u0n7i4@1f1#2i4K6R3%4i4@1u0n7i4@1f1^5i4K6R3H3i4K6R3#2i4@1f1^5i4@1u0r3i4K6W2n7i4@1f1^5i4@1p5I4i4K6S2o6i4@1f1#2i4K6R3^5i4K6R3$3i4@1f1$3i4K6W2q4i4K6V1H3i4@1f1#2i4K6V1J5i4K6S2o6i4@1f1@1i4@1u0r3i4@1q4q4i4@1f1$3i4K6V1@1i4@1t1&6i4@1f1K6i4K6R3H3i4K6R3J5

下面我们演示下该工具的使用过程和最终效果。首先,按照工具的使用说明和提示,运行该工具,下图是运行结果。


从上图可知,工具已经成功生成了对应的dll文件,我们使用dnspy对dll文件进行解析,得到的结果如下图所示。


从上图可以看到,该游戏所有的函数名都可以完整的解析出来,攻击者可以根据函数的名称判断该函数的功能,然后通过IDA和IL2CppDumper.exe生成的ida.py,对函数进行分析和修改,从而实现破解版的功能,比如下图,是MainCityResidenceStrategy类的GatherTempMoney函数的内容。


那么,我们应该如何防范这类工具呢?从工具的使用来看,它需要2个文件,一个是libil2cpp.so,一个是对应的global-metadata.dat。如果对任意一个文件做处理,它是不是就无法正常工作了呢?为了验证这个问题,我们首先只对libil2cpp.so进行加固,再用该工具进行验证,结果如下图所示。


从图里可知,so加固之后,工具是没法正常工作的。然后我们只对global-metadata.dat进行保护,这时候工具的输出结果如下图所示。


从上图可知,单独对dat文件做保护,工具也是无法正常运行的。

因此,我们可以得出结论,要防范IL2CppDumper.exe这类工具,有2方面的工作可以进行,一是对libil2cpp.so文件做保护;二是对global-metadata.dat文件做保护。对于so保护,网易易盾已经有成熟的保护方案,这里不再详细介绍,主要讲下global-metadata.dat文件保护方案。

若要对global-metadata.dat进行保护,那么在引擎加载该文件的时候,我们必须将文件恢复到原始状态,否则游戏会加载报错。那么很自然的可以想到,只要我们在引擎加载该文件之前,给它解密回去,不就搞定了吗?该方案确实可行,市面上很多保护方案也是这么做的,我们可以将该方案称为第一代global-metadata.dat文件保护方案。对global-metadata.dat加载过程了解的同学都知道,该文件的加载入口是MetadataLoader::LoadMetadataFile函数,只要我们对该函数进行一定的处理,就完成对global-metadata.dat的保护了。但是,该方案还是有一个明显的缺点,就是解密之后,会有完整的global-metadata.dat文件内容暴露在内存中,攻击者还是可以借助一些工具,将解密后的文件dump到本地中。

还有一种隐藏global-metadata.dat文件的方案,它是将该文件加密后隐藏在apk内部,改变文件的原始存储路径,然后通过hook的方式在加载的时候恢复回去,本质上还是属于第一代globalmetadata.dat文件保护方案。

针对第一代保护方案的缺陷,易盾通过对il2cpp的深入探索,研究出了第二代global-metadata.dat保护方案。该方案相对第一代的优势是,能杜绝文件在内存中全部的暴露,可以做到边运行边解密,而非一次性全部解密。使用该方案后,即使攻击者dump出内存中的global-metadata.dat文件,它也不是完整的解密后的文件,安全性得到很大的提升。有了该方案,再配合上对libil2cpp.so文件的加固,游戏防破解能力能得到明显的提升。下面用一张图展示这2种方案的区别。


从mono的dll整体加密、方法加密以及畸形化保护,再到il2cpp的global-metadata.dat整体加密和方法加密,易盾一致致力于对Unity3D引擎保护方案的研究。



声明:该文观点仅代表作者本人,转载请注明来自看雪