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的工具(https://github.com/Perfare/Il2CppDumper),可以直接将il2cpp.so还原为dll文件,并且还能够自动生成il2cpp.so对应的ida.py脚本,进一步方便攻击者进行分析和修改。
下面我们演示下该工具的使用过程和最终效果。首先,按照工具的使用说明和提示,运行该工具,下图是运行结果。
从上图可知,工具已经成功生成了对应的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引擎保护方案的研究。