IOS平台Unity引擎的IL2CPP机制分析及安全性评估

发表于2016-07-19
评论0 8.2k浏览

研究背景
  Iphone5S以上(包括5S以上的机型)的机器中都采用了64位的CPU,苹果为了更好发挥64位CPU运行速度,APP Store发布规定:全新App必须在15年2月1日支持64位CPU,已经上架的游戏必须在15年6月1日更新的时候支持64位,否则不能通过苹果官方的审核。
  Unity引擎4.6.2之前的版本采用Mono的AOT机制提前将C#代码编译为机器识别的二进制代码,Unity官方为了支持IOS平台下64位的APP游戏,4.6.2之后的版本采用IL2CPP机制编译和处理IOS平台下的游戏。Unity的IL2CPP并不是对整个.NET或者Mono工具进行重新改写,依然支持继续使用Mono的C#编译过程,唯一不同点为:IOS平台的Unity在4.6.2之后的版本不在使用Mono的AOT编译机制,重新实现一套预编译程序,可在预编译将中间层的IL指令转换为C++代码,然后在利用标准的C++编译程序来产生原生二进制文件。IL2CPP同时实现虚拟机,虚拟机负责处理函数之间调度、管理调度所需内存等。
  本文主要分析如下几个点:Unity引擎 IOS版本中C#代码和转换之后的C++代码对应关系、以三剑豪IOS为例分析其中的C#逻辑函数调用方式和关键信息分析、IL2CPP机制的游戏安全性评估。
 
实现原理
  Unity引擎作为目前最为主流的3D游戏开发引擎,游戏平台移植性非常好,Unity引擎4.6.2之后的版本采用了IL2CPP机制支持IOS平台64位游戏编译,针对IL2CPP机制进行深入分析之后有利于评估IOS平台的Unity游戏安全性。

一、IOS平台Unity引擎IL2CPP机制生成代码对比
  Unity引擎提供了支持苹果Mac系统的版本,测试过程中下载了Unity引擎的最新版本(5.1.2f1版本),安装好Mac版的Unity引擎之后便可创建游戏工程。
  Unity引擎通过File菜单Build&run项可编译IOS版的Unity游戏代码,点击之后会弹出下图所示的框:


      
需要点击Pl
ayerSetting选项设置编译方式和代码框架,对应设置选项内容如下图所示:

                         

  上图界面信息中需要重点关注两项设置,首先需要将“Scripting BackEnd”设置为IL2CPP方式,之后需要将“Architecture”设置为Universal(表示同时支持ArmV7Arm64两种指令集)。设置完之后便可点击Build按钮编译工程,编译成功之后会生成一个XCode工程,以供游戏开发方在Mac平台的XCode工具中编译生成最终的APP文件。
  Unity引擎采用IL2CPP机制在Mac平台成功编译之后,会生成一个完整的XCode工程,Unity游戏逻辑代码采用C#编写,游戏开发方的C#代码最终会生成在/Classes/Native目录中,对应文件结构如下图所示:

              

Unity会将使用的C#库代码编译为Bulk_Name.cpp的文件,对应截图如下:

                

同时Unity会生成IL2CPP转换之后的相关结构、初始化、调用等信息,文件名以L2CPP命令,对应截图如下所示:

                               

  以上为UnityMac平台中针对IOS版本利用IL2CPP机制编译生成C#代码。
  下面针对UnityIL2CPP机制,对比并分析C#逻辑代码经过IL2CPP机制生成的CPP文件代码,测试原始的C#代码如下所示:


     

测试代码中C#代码的ActivateTrigger类有一个OnTriggerEnter函数和几个成员变量,对应生成的代码如下:

        

        

  其中C#类的成员变量被定位为结构体成员,类的成员函数被定义为C导出的函数,例如ActivateTrigger::OnTriggerEnter 成员函数被IL2CPP机制编译    为ActivateTrigger_OnTriggerEnter_m409函数名,同时增加了两个参数,两个参数为:所属的类结构指针(类似每个类的This指针)、MethodInfo结构体指针,方便函数内部调用类的成员变量。通过以上方法可知Unity所采用的IL2CPP机制实际利用C语言的方式表示C#对象的成员变量和成员函数。其中ActivateTrigger::OnTriggerEnter函数内部调用了DoActivateTrigger函数,由于DoActivateTrigger上层由C#调用,所以该函数在编译编译生成XCode工程时以直接调用方式编入ActivateTrigger_OnTriggerEnter_m409函数代码中。
 
二、逆向分析IL2CPP机制中C#函数调用方式
  第一部分以正向的方式分析Unity引擎IL2CPP机制将IL指令转换为CPP的方式及代码结构。该部分以逆向的方式分析Unity3D游戏(三剑豪游戏采用Unity引擎的4.6.2f版本)中所采用的IL2CPP处理机制。
1、IL2CPP机制中C#函数调用处理方式
  三剑豪游戏采用Unity引擎开发,IOS平台中采用UnityIL2CPP机制支持64系统,其中Android平台依然采用UnityMono Jit编译机制。
三剑豪游戏中C#函数FDynamicSceneTrigger::OnTriggerEnter对应代码如下图所示:

        

  FDynamicSceneTrigger::OnTriggerEnter函数C#代码经过Unity的IL2CPP机制转换并在XCode工程编译之后生成的汇编代码如下所示:

 

  _FDynamicSceneTrigger_OnTriggerEnter_m25168为IL2CPP机制转换之后C#函数对应的函数名,_FDynamicSceneTrigger_OnTriggerEnter_m25168函数对应地址为:0x43A824,通过交叉引用发现该函数并无直接调用的函数,交叉引用只有一处,及属于该函数对应的MethodInfo结构,该函数对应的MethodInfo结构如下所示:

 

  其中FDynamicSceneTrigger::OnTriggerEnter函数经过IL2CPP机制转换之后对应生成的MethodInfo变量名为:_FDynamicSceneTrigger_OnTriggerEnter_m25168_MethodInfo,MethodInfo结构中详细记录函数相关所有信息,其中包括C#函数上层调用函数。MethodInfo定义如下所示:

          

  通过上图MethodInfo结构可知C#函数方法对应的函数名字,所属类信息,方法对应地址、上层调用等信息,其中几个重要的变量解释如下:
 Name:为C#函数名;
 Method:为C#函数代码地址;
 Declaring_type:为C#函数所属类信息,其中包括类名、命名空间等信息。
 Invoker_method:为C#函数对应的上层调用函数地址,Unity的IL2CPP机制按照不同C#函数参数定义了不同的上层C调用函数(其中调用方式为间接调用)。
通过_FDynamicSceneTrigger_OnTriggerEnter_m25168_MethodInfo结构可知_FDynamicSceneTrigger_OnTriggerEnter_m25168函数的上层调用RuntimeInvoker_Void_t2689_Object_t(MethodInfo const*, void *, void **),其中对应的间接调用代码如下图所示:

  

  上图中R3为MethodInfo 结构变量,R3+4对应C#函数经过编译处理之后的函数代码地址,上层通过间接的方式调用C#函数。

  其中RuntimeInvoker_Void_t2689_Object_t函数的上层调用也是通过间接方式实现,对应调用接口函数为:il2cpp::vm::Runtime::Invoke(该函数定义为:il2cpp::vm::Runtime::Invoke(MethodInfo const*, void *, void **, Il2CppObject **)),该函数为Unity为实现IL2CPP机制而添加的VM虚拟机,该函数负责调度处理C#函数,其中il2cpp::vm::Runtime::Invoke函数间接调用RuntimeInvoker_Void_t2689_Object_t函数代码如下图所示:

  其中R6为MethodInfo变量,MethodInfo+0x10偏移对应C#函数的上层调用函数地址,在获取每个C#函数对应的RuntimeInvoker函数之后,便通过间接的方式进行调用。
  Unity引擎采用的IL2CPP机制为每个C#函数生成上层的RuntimeInvoker间接调用函数,生成的函数规则为:RuntimeInvoker_+函数返回类型+_函数编号_+函数参数信息,通过为每类C#函数生成上层调用函数并填入对应C#函数的MethodInfo结构中,从而实现间接动态动用方式。
Unity引擎的IL2CPP机制中VM虚拟机针对C#函数调用方式的调用如下所示:
il2cpp::vm::Runtime::Invoke  -> RuntimeInvoker 函数 -> C#函数代码(C#函数经过IL2CPP转换之后的C++代码函数)
 
三、IOS平台Unity IL2CPP机制安全性分析
  通过以上两节的分析可了解到Unity引擎采用IL2CPP机制编译代码方式及调度处理C#函数代码方式,分析过程中提到的重点结构为MethodInfo,每个C#函数编译之后会生成MethodInfo结构,该结构中揭露详细的函数信息,包括函数名、参数名、所属类名、函数地址、命名空间等信息。IOS平台Unity引擎采用的IL2CPP为每个C#函数编译生成MethodInfo结构,通过逆向方法可推测出每个C#函数对应的函数名、类型等信息,如下图所示为部分函数名信息:


    

  通过函数名交叉引用可确认C#函数名对应MethodInfo结构,通过MethodInfo可获取C#函数所有相关重要信息,包括函数所属类名、函数地址、函数参数等信息。

  IL2CPP为每个C#函数生成的Methodinfo结构相当于为逆向分析者提供一份Map文件,逆向分析者可根据字符串搜索快速定位到函数地址,从而大大降低了游戏分析难度。通过以上分析得出结论:IOS平台中Unity引擎采用的IL2CPP机制会大大降低游戏逆向的分析难度,给游戏带来较大的安全隐患。

总结
  以上对IOS平台Unity引擎IL2CPP新机制进行相关分析,其中包括C#IL指令转换为CPP的代码生成规则、IL2CPPVM虚拟机针对C#函数的调用方式,同时针对Unity引擎的IL2CPP机制安全性进行分析,最终得出结论为:Unity所采用的IL2CPP机制会大大降低逆向分析的难度,通过搜索关键字便可快速找到函数名和函数地址对应关系,从而分析清楚相关功能处理方式。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引