-
-
[原创]解决Il2cppapi混淆,通杀DumpUnityCs文件
-
发表于: 6小时前 113
-
没想到上一篇文章有这么多师傅观看,因此有了这一篇。
上一篇讲解了纯静态解决global-metadata.dat的各类问题,并且提到了利用il2cpp_api(未混淆)动态dump CS文件这一方法。
也就是下方这个项目。
3dfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6b7k6i4u0X3j5i4u0W2i4K6u0r3h3Y4W2Y4K9i4y4C8i4K6u0V1d9h3H3J5b7%4m8H3c8s2g2E0M7r3g2J5
所以这一篇就分享一下我解决il2cpp_api混淆的解决方法。
通过源码了解Zygisk-Il2CppDumper做了什么,因代码过长,所以后续仍旧截取片段。
打开il2cpp_dump.cpp,找到il2cpp_dump函数。
我们可以看到几个由il2cpp命名的函数,il2cpp_domain_get il2cpp_domain_get_assemblies il2cpp_assembly_get_image等等。
索引il2cpp_domain_get查看它的实现,会来到il2cpp-api-functions.h。
会发现整个头文件是一个规范由DO_API宏定义的函数。
跳转到DO_API看看它都做了什么。
r:返回的类型
n:函数的名字
p:指针地址
通过xdl_sym去获取符号名n的地址并定义成返回类型为r的函数。
DO_API宏遍历了所有的API声明,并为每个API解析函数地址。
既然如此,我们打开任意一个il2cpp.so来查看一下。

打开导出表,搜索il2cpp_,可以看见大量的函数名。
而这些函数有什么作用呢?
我们截取dump_method,cs文件中的方法是怎么导出的。
所以我们可以知道unity底层就是通过这些api去获取各类class field等。
因此我们也可以通过这些api去获取。
拿出例子,观看混淆后的样子。

我们去导出表搜索il2cpp字符串没有一个相对应的api函数,函数名被混淆为长度11的乱序字符。
1.特征码。
2.api的顺序是否规律。
3.找到游戏上个未混淆的版本,进行对比。
这是作者最先想到,并且尝试的方法,先说第一种。
看到以上两个函数,它们仅仅只有ret,除了这两个还有其余许多汇编一摸一样的函数。因此特征码是不行的。
第二种api的顺序是否规律。
这里作者直接给出答案,大致是规律的。例如,如果你确认某个混淆函数是il2cpp_property_get_name,那么它的下一个混淆函数大概率就是il2cpp_property_get_get_method,以此类推。
这个方法的隐患就是如果有变动了,你也不确定,当某个重要api地址错误时,程序会崩溃。
第三个方法找到游戏上个未混淆的版本,进行对比。
这个方法当版本更新越接近越好用。
所以将以上三个方法一起搭配使用,应该是最美味的。
不知道你否注意到过这个so呢?
任意将这个libunity.so名字丢入ai,说明它的作用。
它应该都会给你诸如此类的回答。
我们可以特别注意到这两行回答。
提供 Unity C# 脚本与底层原生代码之间的桥梁和包含游戏的核心逻辑(特别是使用 IL2CPP 时)。
我们任意打开一个未混淆il2cpp_api包的libunity.so,发现导出表也没有跟il2cpp相关的函数啊?
那试试字符串呢?

我们赶紧索引字符串,看看谁在调用它们。
可以发现所有的il2cpp_api相关的字符串都只索引了这一个函数,并且观察规律,这些api是按照一定顺序进行初始化的。
按照这个思路,马萨卡?难道说?赶紧打开混淆的libunity.so
ohhhhhhhhhh,通过比较我们可以知道GdnPlVZEdxf对应的就是il2cpp_init,接着往下的函数都是一样。
众所周知,我是一个懒狗。
既然有这么好用的映照函数,当然是赶紧编写一些idapython脚本做一套映射表。
1.获取il2cpp_api对照表
通过汇编观察规律,字符串不是在ADRL就是ADRP语句上。
众所周知,我是一个懒狗,懒狗就要有懒狗的样子!
我都发现这个规律了,凭什么还要写脚本?派出AI大将。

一通批评ai后,哐当!我们得到一个ida插件(顺便让它帮忙写了一个ui,不然每次都要点加载脚本,累死哥们了),运行一下DumpIl2cppFuction.py
得到了一个按照正确顺序的il2cpp函数表。注意unity的版本不同,这里的函数表也不一样,大家做游戏的时候,可以收集一下各个版本的il2cpp_functions表,其实也没几个版本差距特别大,也就4-5个。
顺手再dump一个混淆的表。
最后再映射成一个表。
怎么将我们的混淆函数名和代码中的函数名映射上呢?
你问我?我怎么知道?
肯定是继续让ai写啊!
后续可以让dump脚本映射成{"il2cpp_init", "EEHqqEpNwom"},的样式。但是我太懒了,连让ai修改一下的动力都没有了...
最后在game.h填上你最爱的游戏包名,编译导入手机面具加载模块即可。
额...看了一眼上一篇文章,有师傅在底下问我问题,然后我记得我好像扣了几百字回复了,然后就...发现竟然没有发出!!!
然后就不管了...一想到要重新扣那么多字就心累~
因此,我在这里留一个球球好了,OTYyNTY0ODM5。
如果添加第一时间没通过...本人虽然真得很懒,但那一定是在睡觉。
还有如果没有帮助到师傅你完成通杀的话...我是个标题党哈哈哈哈
最后,临近元旦,祝师傅们一定要多笑,苹果肌尽量不要保持扁平,我知道这很困难!但这是命令!
毕竟,搞技术身体最重要!
嗯@-@
void il2cpp_dump(const char *outDir) { LOGI("dumping..."); size_t size; auto domain = il2cpp_domain_get(); auto assemblies = il2cpp_domain_get_assemblies(domain, &size); std::stringstream imageOutput; for (int i = 0; i < size; ++i) { auto image = il2cpp_assembly_get_image(assemblies[i]); imageOutput << "// Image " << i << ": " << il2cpp_image_get_name(image) << "\n"; }void il2cpp_dump(const char *outDir) { LOGI("dumping..."); size_t size; auto domain = il2cpp_domain_get(); auto assemblies = il2cpp_domain_get_assemblies(domain, &size); std::stringstream imageOutput; for (int i = 0; i < size; ++i) { auto image = il2cpp_assembly_get_image(assemblies[i]); imageOutput << "// Image " << i << ": " << il2cpp_image_get_name(image) << "\n"; }// domainDO_API(Il2CppDomain*, il2cpp_domain_get, ());DO_API(const Il2CppAssembly*, il2cpp_domain_assembly_open, (Il2CppDomain * domain, const char* name));DO_API(const Il2CppAssembly**, il2cpp_domain_get_assemblies, (const Il2CppDomain * domain, size_t * size));// domainDO_API(Il2CppDomain*, il2cpp_domain_get, ());DO_API(const Il2CppAssembly*, il2cpp_domain_assembly_open, (Il2CppDomain * domain, const char* name));DO_API(const Il2CppAssembly**, il2cpp_domain_get_assemblies, (const Il2CppDomain * domain, size_t * size));#define DO_API(r, n, p) r (*n) p#include "il2cpp-api-functions.h"#undef DO_APIstatic uint64_t il2cpp_base = 0;void init_il2cpp_api(void *handle) {#define DO_API(r, n, p) { \ n = (r (*) p)xdl_sym(handle, #n, nullptr); \ if(!n) { \ LOGW("api not found %s", #n); \ } \}#include "il2cpp-api-functions.h"#undef DO_API}#define DO_API(r, n, p) r (*n) p#include "il2cpp-api-functions.h"#undef DO_APIstatic uint64_t il2cpp_base = 0;void init_il2cpp_api(void *handle) {#define DO_API(r, n, p) { \ n = (r (*) p)xdl_sym(handle, #n, nullptr); \ if(!n) { \ LOGW("api not found %s", #n); \ } \}#include "il2cpp-api-functions.h"#undef DO_API}std::string dump_method(Il2CppClass *klass) { std::stringstream outPut; outPut << "\n\t// Methods\n"; void *iter = nullptr; while (auto method = il2cpp_class_get_methods(klass, &iter)) { //TODO attribute if (method->methodPointer) { outPut << "\t// RVA: 0x"; outPut << std::hex << (uint64_t) method->methodPointer - il2cpp_base; outPut << " VA: 0x"; outPut << std::hex << (uint64_t) method->methodPointer; } else { outPut << "\t// RVA: 0x VA: 0x0"; } /*if (method->slot != 65535) { outPut << " Slot: " << std::dec << method->slot; }*/ outPut << "\n\t"; uint32_t iflags = 0; auto flags = il2cpp_method_get_flags(method, &iflags); outPut << get_method_modifier(flags); //TODO genericContainerIndex auto return_type = il2cpp_method_get_return_type(method); if (_il2cpp_type_is_byref(return_type)) { outPut << "ref "; } auto return_class = il2cpp_class_from_type(return_type); outPut << il2cpp_class_get_name(return_class) << " " << il2cpp_method_get_name(method) << "("; auto param_count = il2cpp_method_get_param_count(method); for (int i = 0; i < param_count; ++i) { auto param = il2cpp_method_get_param(method, i); auto attrs = param->attrs; if (_il2cpp_type_is_byref(param)) { if (attrs & PARAM_ATTRIBUTE_OUT && !(attrs & PARAM_ATTRIBUTE_IN)) { outPut << "out "; } else if (attrs & PARAM_ATTRIBUTE_IN && !(attrs & PARAM_ATTRIBUTE_OUT)) { outPut << "in "; } else { outPut << "ref "; } } else { if (attrs & PARAM_ATTRIBUTE_IN) { outPut << "[In] "; } if (attrs & PARAM_ATTRIBUTE_OUT) { outPut << "[Out] "; } } auto parameter_class = il2cpp_class_from_type(param); outPut << il2cpp_class_get_name(parameter_class) << " " << il2cpp_method_get_param_name(method, i); outPut << ", "; } if (param_count > 0) { outPut.seekp(-2, outPut.cur); } outPut << ") { }\n"; //TODO GenericInstMethod } return outPut.str();}std::string dump_method(Il2CppClass *klass) { std::stringstream outPut; outPut << "\n\t// Methods\n"; void *iter = nullptr; while (auto method = il2cpp_class_get_methods(klass, &iter)) { //TODO attribute if (method->methodPointer) { outPut << "\t// RVA: 0x"; outPut << std::hex << (uint64_t) method->methodPointer - il2cpp_base; outPut << " VA: 0x"; outPut << std::hex << (uint64_t) method->methodPointer; } else { outPut << "\t// RVA: 0x VA: 0x0"; } /*if (method->slot != 65535) { outPut << " Slot: " << std::dec << method->slot; }*/ outPut << "\n\t"; uint32_t iflags = 0; auto flags = il2cpp_method_get_flags(method, &iflags); outPut << get_method_modifier(flags); //TODO genericContainerIndex auto return_type = il2cpp_method_get_return_type(method); if (_il2cpp_type_is_byref(return_type)) { outPut << "ref "; } auto return_class = il2cpp_class_from_type(return_type); outPut << il2cpp_class_get_name(return_class) << " " << il2cpp_method_get_name(method) << "("; auto param_count = il2cpp_method_get_param_count(method); for (int i = 0; i < param_count; ++i) { auto param = il2cpp_method_get_param(method, i); auto attrs = param->attrs; if (_il2cpp_type_is_byref(param)) { if (attrs & PARAM_ATTRIBUTE_OUT && !(attrs & PARAM_ATTRIBUTE_IN)) { outPut << "out "; } else if (attrs & PARAM_ATTRIBUTE_IN && !(attrs & PARAM_ATTRIBUTE_OUT)) { outPut << "in "; } else { outPut << "ref "; } } else { if (attrs & PARAM_ATTRIBUTE_IN) { outPut << "[In] "; } if (attrs & PARAM_ATTRIBUTE_OUT) { outPut << "[Out] "; } } auto parameter_class = il2cpp_class_from_type(param); outPut << il2cpp_class_get_name(parameter_class) << " " << il2cpp_method_get_param_name(method, i); outPut << ", "; } if (param_count > 0) { outPut.seekp(-2, outPut.cur); } outPut << ") { }\n"; //TODO GenericInstMethod } return outPut.str();}.text:00000000019047F8.text:00000000019047F8.text:00000000019047F8 EXPORT YMqyW_Qt_Ld.text:00000000019047F8 YMqyW_Qt_Ld ; DATA XREF: LOAD:0000000000007578↑o.text:00000000019047F8 ; __unwind {.text:00000000019047F8 RET.text:00000000019047F8 ; } // starts at 19047F8.text:00000000019047F8 ; End of function YMqyW_Qt_Ld.text:00000000019047F8.text:00000000019047FC EXPORT yG_WSutBToN.text:00000000019047FC yG_WSutBToN ; DATA XREF: LOAD:0000000000003B88↑o.text:00000000019047FC ; __unwind {.text:00000000019047FC RET.text:00000000019047FC ; } // starts at 19047FC.text:00000000019047F8.text:00000000019047F8.text:00000000019047F8 EXPORT YMqyW_Qt_Ld.text:00000000019047F8 YMqyW_Qt_Ld ; DATA XREF: LOAD:0000000000007578↑o.text:00000000019047F8 ; __unwind {.text:00000000019047F8 RET.text:00000000019047F8 ; } // starts at 19047F8.text:00000000019047F8 ; End of function YMqyW_Qt_Ld.text:00000000019047F8.text:00000000019047FC EXPORT yG_WSutBToN.text:00000000019047FC yG_WSutBToN ; DATA XREF: LOAD:0000000000003B88↑o.text:00000000019047FC ; __unwind {.text:00000000019047FC RET.text:00000000019047FC ; } // starts at 19047FC.text:0000000001279314 EXPORT il2cpp_property_get_name.text:0000000001279314 il2cpp_property_get_name ; DATA XREF: LOAD:00000000000048F0↑o.text:0000000001279314 ; __unwind {.text:0000000001279314 B il2cpp_field_get_type_0.text:0000000001279318.text:0000000001279318 EXPORT il2cpp_property_get_get_method.text:0000000001279318 il2cpp_property_get_get_method ; DATA XREF: LOAD:0000000000005178↑o.text:0000000001279318 ; __unwind {.text:0000000001279318 B il2cpp_class_get_name_0.text:000000000127931C EXPORT il2cpp_property_get_set_method.text:000000000127931C il2cpp_property_get_set_method ; DATA XREF: LOAD:000000000000A788↑o.text:000000000127931C ; __unwind {.text:000000000127931C B il2cpp_class_get_namespace_0.text:0000000001279320.text:0000000001279320 EXPORT il2cpp_property_get_parent.text:0000000001279320 il2cpp_property_get_parent ; DATA XREF: LOAD:000000000000E0E8↑o.text:0000000001279320 ; __unwind {.text:0000000001279320 B il2cpp_assembly_get_image_0.text:0000000001279320 ; } // starts at 1279320.text:0000000001279328 EXPORT il2cpp_object_get_class.text:0000000001279328 il2cpp_object_get_class ; DATA XREF: LOAD:000000000000B928↑o.text:0000000001279328 ; __unwind {.text:0000000001279328 B il2cpp_assembly_get_image_0.text:0000000001279314 EXPORT il2cpp_property_get_name.text:0000000001279314 il2cpp_property_get_name ; DATA XREF: LOAD:00000000000048F0↑o.text:0000000001279314 ; __unwind {.text:0000000001279314 B il2cpp_field_get_type_0.text:0000000001279318.text:0000000001279318 EXPORT il2cpp_property_get_get_method.text:0000000001279318 il2cpp_property_get_get_method ; DATA XREF: LOAD:0000000000005178↑o.text:0000000001279318 ; __unwind {.text:0000000001279318 B il2cpp_class_get_name_0.text:000000000127931C EXPORT il2cpp_property_get_set_method.text:000000000127931C il2cpp_property_get_set_method ; DATA XREF: LOAD:000000000000A788↑o.text:000000000127931C ; __unwind {.text:000000000127931C B il2cpp_class_get_namespace_0.text:0000000001279320.text:0000000001279320 EXPORT il2cpp_property_get_parent.text:0000000001279320 il2cpp_property_get_parent ; DATA XREF: LOAD:000000000000E0E8↑o.text:0000000001279320 ; __unwind {.text:0000000001279320 B il2cpp_assembly_get_image_0.text:0000000001279320 ; } // starts at 1279320.text:0000000001279328 EXPORT il2cpp_object_get_class.text:0000000001279328 il2cpp_object_get_class ; DATA XREF: LOAD:000000000000B928↑o.text:0000000001279328 ; __unwind {.text:0000000001279328 B il2cpp_assembly_get_image_01. 引擎运行时核心包含 Unity 游戏引擎的核心功能代码处理游戏循环、内存管理、渲染管线等基础系统提供 Unity C# 脚本与底层原生代码之间的桥梁包含游戏的核心逻辑(特别是使用 IL2CPP 时)1. 引擎运行时核心包含 Unity 游戏引擎的核心功能代码