【游戏开发进阶】教你使用IL2CppDumper从Unity il2cpp的二进制文件中获取类型、方法、字段等(反编译) |
您所在的位置:网站首页 › apk解密教程 › 【游戏开发进阶】教你使用IL2CppDumper从Unity il2cpp的二进制文件中获取类型、方法、字段等(反编译) |
文章目录
一、前言二、下载IL2CppDumper三、Unity Demo工程四、IL2CPP打包五、拿到libil2cpp.so与global-metadata.dat六、执行Il2CppDumper.exe七、查看反编译后的文件1、dump.cs2、il2cpp.h3、script.json4、stringliteral.json5、DummyDll/Assembly-CSharp.dll
八、拓展:使用IDA逆向il2cpp.so,得到函数体内部逻辑九、拓展补充:IL2CppDumper原理十、结束语
一、前言
点关注不迷路,持续输出Unity干货文章。 嗨,大家好,我是新发。 Unity使用Mono方式打出来的apk,我们可以直接从包内拿到Assembly-CSharp.dll,如果开发者没有对Assembly-CSharp.dll进行加密处理,那么我们可以很方便地使用ILSpy.exe对其进行反编译。 如果使用IL2CPP方式出包,则没有Assembly-CSharp.dll,不过,有一个IL2CppDumper工具,通过它,我们可以逆向得到Assembly-CSharp.dll,下面就教大家如何使用这个IL2CppDumper吧。 二、下载IL2CppDumperIL2CppDumper是一个开源工具,在GitHub上可以直接下载到。 地址:https://github.com/Perfare/Il2CppDumper 点击右边的Releases下面的版本进行下载即可。 为了进行测试,我们创建个Unity Demo工程,创建两个脚本,如下: 使用IL2CPP方式,打出apk包。 奖test.apk改为test.zip,解压进入目录中,拿到libil2cpp.so与global-metadata.dat。 \lib\armeabi-v7a\libil2cpp.so \assets\bin\Data\Managed\Metadata\global-metadata.dat 回到Il2CppDumper.exe所在的目录,创建input目录和output目录。 双击执行il2cpp_decompilation.bat,如下: 这个文件会把C#的dll代码的类、方法、字段列出来。比如我们写的Main.cs和Hello.cs。 // Namespace: public class Hello // TypeDefIndex: 1414 { // Methods // RVA: 0x39CB58 Offset: 0x39CB58 VA: 0x39CB58 public void .ctor() { } // RVA: 0x39CB60 Offset: 0x39CB60 VA: 0x39CB60 public void SayHello() { } // RVA: 0x39CBF0 Offset: 0x39CBF0 VA: 0x39CBF0 public void Say(string text) { } } // Namespace: public class Main : MonoBehaviour // TypeDefIndex: 1415 { // Fields public int test_int; // 0xC protected float test_float; // 0x10 private string test_string; // 0x14 public int[] test_int_array; // 0x18 protected List test_string_list; // 0x1C private Main.TEST_ENUM test_enum; // 0x20 private Hello hello; // 0x24 // Methods // RVA: 0x39CC78 Offset: 0x39CC78 VA: 0x39CC78 public void .ctor() { } // RVA: 0x39CD08 Offset: 0x39CD08 VA: 0x39CD08 private void Awake() { } // RVA: 0x39CD0C Offset: 0x39CD0C VA: 0x39CD0C private void Init() { } // RVA: 0x39CE7C Offset: 0x39CE7C VA: 0x39CE7C private void Start() { } [DebuggerHiddenAttribute] // RVA: 0x348B00 Offset: 0x348B00 VA: 0x348B00 // RVA: 0x39CF20 Offset: 0x39CF20 VA: 0x39CF20 private IEnumerator TestCoroutine() { } } 2、il2cpp.h生成的cpp的头文件,从头文件里我们也可以看到相关的数据结构。 struct Hello_Fields { }; struct Main_Fields : UnityEngine_MonoBehaviour_Fields { int32_t test_int; float test_float; struct System_String_o* test_string; struct System_Int32_array* test_int_array; struct System_Collections_Generic_List_string__o* test_string_list; int32_t test_enum; struct Hello_o* hello; }; 3、script.json以json格式显示类的方法信息: { "Address": 3787608, "Name": "Hello$$.ctor", "Signature": "void Hello___ctor (Hello_o* __this, const MethodInfo* method);" }, { "Address": 3787616, "Name": "Hello$$SayHello", "Signature": "void Hello__SayHello (Hello_o* __this, const MethodInfo* method);" }, { "Address": 3787760, "Name": "Hello$$Say", "Signature": "void Hello__Say (Hello_o* __this, System_String_o* text, const MethodInfo* method);" }, { "Address": 3787896, "Name": "Main$$.ctor", "Signature": "void Main___ctor (Main_o* __this, const MethodInfo* method);" }, { "Address": 3788040, "Name": "Main$$Awake", "Signature": "void Main__Awake (Main_o* __this, const MethodInfo* method);" }, { "Address": 3788044, "Name": "Main$$Init", "Signature": "void Main__Init (Main_o* __this, const MethodInfo* method);" }, { "Address": 3788412, "Name": "Main$$Start", "Signature": "void Main__Start (Main_o* __this, const MethodInfo* method);" }, { "Address": 3788576, "Name": "Main$$TestCoroutine", "Signature": "System_Collections_IEnumerator_o* Main__TestCoroutine (Main_o* __this, const MethodInfo* method);" }, 4、stringliteral.json以json的格式显示所有的字符串信息: { "value": "Hello, IL2CPP", "address": "0x52A0AC" }, { "value": "hello world", "address": "0x52A0B0" }, { "value": "il2cpp", "address": "0x52A0B4" }, { "value": "test", "address": "0x52A0B8" }, { "value": "Hi, I am linxinfa", "address": "0x52A0BC" }, { "value": "TestCoroutine, test_int: ", "address": "0x52A0C0" }, 5、DummyDll/Assembly-CSharp.dll进入DummyDll目录,可以看到很多dll,其中就有Assembly-CSharp.dll,我们可以使用ILSpy.exe对其进行反编译。 可以看到,与刚刚的dump.cs文件内容是一致的。 从上面IL2CppDumper我们可以发现,逆向得到的函数体都是空的,看不了内部逻辑。有没有办法可以逆向得到函数体内部逻辑呢? 有,需要借助另一个反编译神器:IDA,全称交互式反汇编器(Interactive Disassembler)。 IDA下载链接:https://pan.baidu.com/s/1NATDYzomBYiwrwdH6qBjUA 提取码:2dmy IDA官网:https://www.hex-rays.com/ IDA有32位的和64位两个exe,要根据你反编译的文件运行对应的exe。 Unity使用IL2CPP打包时,选择的CPU架构可以选择ARMv7和ARM64,由于我上面拿的libil2cpp.so是armeabi-v7a的,也就是32位的,所以我运行的32位的IDA。 在IDA中载入libil2cpp.so,耐心等待它的解析。 假设我们要查看Main脚本的Awake方法的函数体,我们从刚刚的dump.cs可以看到对应的函数地址: // RVA: 0x39CD08 Offset: 0x39CD08 VA: 0x39CD08 private void Awake() { }地址是:0x39CD08,我们在IDA中按G键,输入地址:0x39CD08,点击OK, 而在dump.cs中我们可以看到Init的函数地址是0x39CD0C: // RVA: 0x39CD0C Offset: 0x39CD0C VA: 0x39CD0C private void Init() { }我们再回过头看IDA,我们就可以对应起来了。 il2cpp将游戏 C#代码转换为C++代码,然后编译为各平台Native代码。 虽然游戏逻辑是以Native代码运行, 但依然要实现 C#某些语言特性(如GC、反射),il2cpp将所有的 C#中的类名、方法名、属性名、字符串等地址信息记录在 global-metadata.dat文件。il2cpp启动时会从这个文件读取所需要的类名、方法名、属性名、字符串等地址信息。 注:如果开发者对global-metadata.dat文件做了加密,那么在GlobalMetadata.cpp中加载global-metadata.dat前需要实现对应的解密逻辑。
global-metadata.dat文件是一个二进制文件,是按照一定的数据结构进行写入的。 我们可以下载个010Editor查看global-metadata.dat文件。 010Editor下载地址:https://www.sweetscape.com/download/010editor/ 运行010Editor,将global-metadata.dat文件拖入010Editor中,我们可以看到前四个字节是AF 1B B1 FA。 注意,IL2CppDumper是使用C#写的,C#在windows平台上是小端字节序,即数据的高字节保存在内存的高地址中,我们上面使用010Editor看二进制文件的时候,从左到右地址是升高的,所以上面的AF 1B B1 FA对应到C#代码中就是0xFAB11BAF,不要搞反了哦。
我们可以在IL2CppDumper源码中看到执行Dump方法需要传入metadata和il2Cpp。 完毕。 喜欢Unity的同学,不要忘记点击关注,如果有什么Unity相关的技术难题,也欢迎留言或私信~ |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |