【游戏开发进阶】教你使用IL2CppDumper从Unity il2cpp的二进制文件中获取类型、方法、字段等(反编译)

您所在的位置:网站首页 gg修改器在哪个目录文件夹 【游戏开发进阶】教你使用IL2CppDumper从Unity il2cpp的二进制文件中获取类型、方法、字段等(反编译)

【游戏开发进阶】教你使用IL2CppDumper从Unity il2cpp的二进制文件中获取类型、方法、字段等(反编译)

2024-07-11 01:50:19| 来源: 网络整理| 查看: 265

文章目录 一、前言二、下载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吧。

二、下载IL2CppDumper

IL2CppDumper是一个开源工具,在GitHub上可以直接下载到。 地址:https://github.com/Perfare/Il2CppDumper 点击右边的Releases下面的版本进行下载即可。 在这里插入图片描述 在这里插入图片描述 下载后解压,文件如下,可以看到Il2CppDumper.exe。 在这里插入图片描述

三、Unity Demo工程

为了进行测试,我们创建个Unity Demo工程,创建两个脚本,如下: 在这里插入图片描述 Main.cs脚本:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Main : MonoBehaviour { enum TEST_ENUM { E1, E2, E3 } public int test_int = 0; protected float test_float = 0.0f; private string test_string = "hello world"; public int[] test_int_array; protected List test_string_list; private TEST_ENUM test_enum; private Hello hello = new Hello(); private void Awake() { Init(); } private void Init() { test_int = 1; test_float = 2.0f; test_int_array = new int[3] { 4, 5, 6 }; test_string_list = new List() { "il2cpp", "test" }; test_enum = TEST_ENUM.E2; } private void Start() { hello.SayHello(); test_string = "Hi, I am linxinfa"; hello.Say(test_string); StartCoroutine(TestCoroutine()); } private IEnumerator TestCoroutine() { for (int i = 0; i public void SayHello() { Debug.Log("Hello, IL2CPP"); } public void Say(string text) { Debug.Log(text); } } 四、IL2CPP打包

使用IL2CPP方式,打出apk包。 在这里插入图片描述 打出的apk如下。 在这里插入图片描述

五、拿到libil2cpp.so与global-metadata.dat

奖test.apk改为test.zip,解压进入目录中,拿到libil2cpp.so与global-metadata.dat。 \lib\armeabi-v7a\libil2cpp.so \assets\bin\Data\Managed\Metadata\global-metadata.dat 在这里插入图片描述

六、执行Il2CppDumper.exe

回到Il2CppDumper.exe所在的目录,创建input目录和output目录。 在这里插入图片描述 将libil2cpp.so与global-metadata.dat拷贝到input目录中。 在这里插入图片描述 创建一个il2cpp_decompilation.bat文件。 在这里插入图片描述 il2cpp_decompilation.bat文件内容如下:

..\Il2CppDumper.exe libil2cpp.so global-metadata.dat ..\output

双击执行il2cpp_decompilation.bat,如下: 在这里插入图片描述 进入output目录,可以看到生成了如下文件: 在这里插入图片描述

七、查看反编译后的文件 1、dump.cs

这个文件会把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文件内容是一致的。 在这里插入图片描述 在这里插入图片描述

八、拓展:使用IDA逆向il2cpp.so,得到函数体内部逻辑

从上面IL2CppDumper我们可以发现,逆向得到的函数体都是空的,看不了内部逻辑。有没有办法可以逆向得到函数体内部逻辑呢? 有,需要借助另一个反编译神器:IDA,全称交互式反汇编器(Interactive Disassembler)。 在这里插入图片描述在这里插入图片描述 关于IDA的使用,我之前写过一篇文章:《新发的日常学习——IDA的入门使用,反编译so/dll文件(反编译神器)》,感兴趣的同学可以看看。

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, 在这里插入图片描述 可以看到跳过来是这样的,这里对应的是汇编代码,这个sub_39CD08就是我们的Awake函数。 在这里插入图片描述 源代码中,Awake函数中调用了Init函数:

private void Awake() { Init(); }

而在dump.cs中我们可以看到Init的函数地址是0x39CD0C:

// RVA: 0x39CD0C Offset: 0x39CD0C VA: 0x39CD0C private void Init() { }

我们再回过头看IDA,我们就可以对应起来了。 在这里插入图片描述 我们可以直接按F5将汇编转成对应的c代码,这个sub_39CD08就是Awake函数,可以看到,这里代码被优化了,Awake函数内部实际上就是Init中的内容,不过看起来也是挺晦涩难懂的,感兴趣的同学可以深入研究研究。

signed int __fastcall sub_39CD08(_DWORD *a1) { _DWORD *v1; // r4 int v2; // r5 int v3; // r5 int v4; // r5 int v5; // r0 int v6; // r0 signed int result; // r0 v1 = a1; if ( !byte_52A52B ) { sub_39688C(&elf_hash_chain[89]); byte_52A52B = 1; } v1[3] = 1; v1[4] = 0x40000000; v2 = dword_524724; sub_38CFC4(dword_524724); v3 = il2cpp_array_new_specific_0(v2, 3); sub_773C4(0, v3, dword_5277E8, 0); v1[6] = v3; v4 = sub_3CCA3C(dword_524EF4); v5 = sub_303278(v4, dword_5262D8); if ( v4 ) { sub_304144(v4, dword_52A0B4, dword_5262E0); } else { ((void (__fastcall *)(int))loc_3BF764)(v5); v6 = sub_304144(0, dword_52A0B4, dword_5262E0); ((void (__fastcall *)(int))loc_3BF764)(v6); } sub_304144(v4, dword_52A0B8, dword_5262E0); result = 1; v1[7] = v4; v1[8] = 1; return result; } 九、拓展补充:IL2CppDumper原理

il2cpp将游戏 C#代码转换为C++代码,然后编译为各平台Native代码。 在这里插入图片描述

虽然游戏逻辑是以Native代码运行, 但依然要实现 C#某些语言特性(如GC、反射),il2cpp将所有的 C#中的类名、方法名、属性名、字符串等地址信息记录在 global-metadata.dat文件。il2cpp启动时会从这个文件读取所需要的类名、方法名、属性名、字符串等地址信息。 在这里插入图片描述 我们可以在Unity安装目录的Editor目录中找到il2cpp虚拟机的源码:vm目录。 在这里插入图片描述 在里面的GlobalMetadata.cpp中,就可以看到加载global-metadata.dat文件的逻辑。

注:如果开发者对global-metadata.dat文件做了加密,那么在GlobalMetadata.cpp中加载global-metadata.dat前需要实现对应的解密逻辑。

在这里插入图片描述 IL2CppDumper正是利用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。 在这里插入图片描述 AF 1B B1 FA是global-metadata.dat文件的标识,如果你去看IL2CppDumper源码,你就会看到这个标识的判断:

注意,IL2CppDumper是使用C#写的,C#在windows平台上是小端字节序,即数据的高字节保存在内存的高地址中,我们上面使用010Editor看二进制文件的时候,从左到右地址是升高的,所以上面的AF 1B B1 FA对应到C#代码中就是0xFAB11BAF,不要搞反了哦。

在这里插入图片描述 我们这样看global-metadata.dat文件是看不出啥东西的,我们需要告诉010Editor如何解析这个global-metadata.dat文件。 这个时候,就需要下载UnityMetadata.bt模板文件了。 UnityMetadata.bt模板文件下载:https://www.sweetscape.com/010editor/repository/files/UnityMetadata.bt 下载完之后,在010Editor中点击菜单Templates / Open Template...,选择刚刚下载的UnityMetadata.bt。 在这里插入图片描述 然后点击这个三角形按钮运行模板。 在这里插入图片描述 运行成功,可以看到global-metadata.dat解析出数据来了。 在这里插入图片描述 不过我们光从global-metadata.dat是看不出具体的字符串的,需要依赖libil2cpp.so进行寻址才行。

我们可以在IL2CppDumper源码中看到执行Dump方法需要传入metadata和il2Cpp。 在这里插入图片描述 因为libil2cpp.so是UNIX派系的二进制文件,所以它的前4个字节是7F 45 4C 46,对应到C#小端的十六进制表达就是0x464c457f。 在这里插入图片描述 我们可以在Init方法中看到读取libil2cpp.so的逻辑, 在这里插入图片描述 再往里走,就是Elf格式文件的解析了,这里就不展开了。执行完Dump即可生成逆向文件了。 感兴趣的同学可以继续深入研究下。

十、结束语

完毕。 喜欢Unity的同学,不要忘记点击关注,如果有什么Unity相关的技术难题,也欢迎留言或私信~



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭