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

一、前言
点关注不迷路,持续输出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<string> 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<string>() { "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 < 100; ++i){++test_int;Debug.Log("TestCoroutine, test_int: " + test_int);yield return new WaitForSeconds(1);}}
}Hello.cs脚本```csharp
using UnityEngine;public class Hello
{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: 0x39CB58public void .ctor() { }// RVA: 0x39CB60 Offset: 0x39CB60 VA: 0x39CB60public void SayHello() { }// RVA: 0x39CBF0 Offset: 0x39CBF0 VA: 0x39CBF0public void Say(string text) { }
}// Namespace:
public class Main : MonoBehaviour // TypeDefIndex: 1415
{// Fieldspublic int test_int; // 0xCprotected float test_float; // 0x10private string test_string; // 0x14public int[] test_int_array; // 0x18protected List<string> test_string_list; // 0x1Cprivate Main.TEST_ENUM test_enum; // 0x20private Hello hello; // 0x24// Methods// RVA: 0x39CC78 Offset: 0x39CC78 VA: 0x39CC78public void .ctor() { }// RVA: 0x39CD08 Offset: 0x39CD08 VA: 0x39CD08private void Awake() { }// RVA: 0x39CD0C Offset: 0x39CD0C VA: 0x39CD0Cprivate void Init() { }// RVA: 0x39CE7C Offset: 0x39CE7C VA: 0x39CE7Cprivate void Start() { }[DebuggerHiddenAttribute] // RVA: 0x348B00 Offset: 0x348B00 VA: 0x348B00// RVA: 0x39CF20 Offset: 0x39CF20 VA: 0x39CF20private 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; // r4int v2; // r5int v3; // r5int v4; // r5int v5; // r0int v6; // r0signed int result; // r0v1 = 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的逻辑

教你使用IL2CppDumper从Unity il2cpp的二进制文件中获取类型、方法、字段等(反编译)相关推荐

  1. Unity在XR设备中获取手柄的按键信息

    我们在平常的XR设备开发中,尤其适用VR设备的时候,会用到手柄的操作. 我们知道Oculus SDK提供了OVRInput,能够获取得到手柄的按键信息. // public variable that ...

  2. 如何快速掌握oracle,教你如何快速从 Oracle 官方文档中获取需要的知识

    以下链接可查看 11g 到 20c 的在线官方文档. https://docs.oracle.com/en/database/oracle/oracle-database/index.html 如图, ...

  3. Unity IL2Cpp

    为什么80%的码农都做不了架构师?>>>    编者注 由于之前引入的log4net,在IL2Cpp下无法正常运行,导致没有日志库使用.还需要解决protobuf或thrift是否支 ...

  4. 反编译用unity打包的资源文件

    如何反编译破解别人家的游戏包,美术资源是维权和侵权一直杠下去的话题,如果作为商业用途,我是反对破坏原作者的创意,侵害作者的劳动果实行为.但是如果是仅仅为了学习,实验,不妨参考我的文章,我相信你可以从我 ...

  5. win7html文件,教你win7浏览器打不开本地html文件格式的解决方法

    最近有用户反馈,遇到了html文件打不开,一直显示主页而不是html页面,其实很大部分是浏览器设置问题.有什么办法可以解决?针对此疑问,下面小编教你win7浏览器打不开本地html文件格式的解决方法. ...

  6. unity片元着色器中获取屏幕坐标_Unity踩坑笔记(持续更新)

    1.error CS0104: 'MinAttribute' is an ambiguous reference between 'UnityEngine.Rendering.PostProcessi ...

  7. 一步一步教你抓数据——用.net精确提取网站数据的通用方法 [转]

    一步一步教你抓数据--用.net精确提取网站数据的通用方法 [转] 2008年02月23日 星期六 16:53 具体实现思路: 1 首先用WebClient类下载网页源码 public static ...

  8. Unity导入3D模型的过程与方法

    一.介绍 资源是游戏开发中的原材料,也就是组成游戏的模块. Unity只是一个游戏开发引擎,而并不是一个资源开发软件.这就意味着在游戏中需要的资源通常是由一些设计者使用其他软件开发出来的,然后设计者会 ...

  9. Unity C# 反编译

    前言 结合前篇:[反编译U3D]Decompile Unity Resources 修正 本篇说说如何查看unity项目(apk) 的源代码,前提是这个apk的代码未经过加密. 写这篇的目地就是看看别 ...

  10. 【100个 Unity踩坑小知识点】| Unity调用API ,动态获取Android权限,附带所有Android权限表格

    Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 . 包括游戏开发.美术.建筑.汽车设计.影视在内的所有创作者,借助 Unity 将创意 ...

最新文章

  1. debian,ubuntu下安装MariaDB,并设置密码,修改端口,允许外网访问
  2. 基于React与Redux的留言墙的实现
  3. 云网融合 — 云网之争
  4. mybatis获取mysql存储过程out参数的值_mybatis接受mysql存储过程out的值
  5. Linux expr命令、Linux wc命令、Linux let 命令
  6. 情人节福利,用JAVA做个QQ机器人,帮我提醒女神按时喝水和陪她聊天~(开源)
  7. android白圈闪现动画,Android基于Shader的图像处理(9)-仿抖音闪白特效
  8. 百面机器学习—6.PCA与LDA要点总结
  9. XE5 搭建DataSnap服务
  10. 利用python提取SWAT模型output.rch中固定sub编号的逐月径流到Excel
  11. 通过修改dat文档对植物大战僵尸关卡数进行修改
  12. vue聊天功能模块(十二)消息转发
  13. 服务器usb驱动安装系统安装失败怎么办,USB3.0驱动无法安装失败怎么办?USB驱动失败失败的解决方法...
  14. vue createElement后删除这个元素 the node to be removed is not a child of this node
  15. 极客日报:腾讯下一步或减持美团和拼多多的股份;iPhone 13连续6周成中国最畅销智能手机;Linux 5.16 开发者统计
  16. CSP201609-3炉石传说
  17. O2O、C2C、B2B、B2C是什么意思 有什么区别
  18. (2013.05.05)N枚硬币找1枚假币
  19. 2022-2028年中国工业机器人行业产销需求预测与转型升级分析报告
  20. Android 实时监听耳机拔除

热门文章

  1. CSS 相邻兄弟选择器
  2. steam饥荒存档备份_如何手动备份您的Steam游戏文件
  3. macos high sierra
  4. 入行数据分析要知道什么是概括性度量
  5. 产品设计七大黄金定律
  6. ThreadPoolExecutor线程池 —————— 开开开山怪
  7. 阿里面试官没想到,一个Volatile,我都能跟他吹半小时
  8. 优矿-获取商品期权数据
  9. aix oracle 10.2.0.1 升级 10.2.0.4,【江枫 】AIX平台升级到Oracle10.2.0.4的几个问题
  10. 权力的游戏——读《原则2 :应对变化中的世界秩序》(中)