首页
社区
课程
招聘
[原创]解决Il2cppapi混淆,通杀DumpUnityCs文件
发表于: 4小时前 96

[原创]解决Il2cppapi混淆,通杀DumpUnityCs文件

4小时前
96

没想到上一篇文章有这么多师傅观看,因此有了这一篇。

上一篇讲解了纯静态解决global-metadata.dat的各类问题,并且提到了利用il2cpp_api(未混淆)动态dump CS文件这一方法。

也就是下方这个项目。

d22K9s2c8@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";
    }
// domain
DO_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));
// domain
DO_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_API
 
static 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_API
 
static 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_0
1. 引擎运行时核心
包含 Unity 游戏引擎的核心功能代码
 
处理游戏循环、内存管理、渲染管线等基础系统
 
提供 Unity C# 脚本与底层原生代码之间的桥梁
 
包含游戏的核心逻辑(特别是使用 IL2CPP 时)
1. 引擎运行时核心
包含 Unity 游戏引擎的核心功能代码
 

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 6
支持
分享
最新回复 (1)
雪    币: 17
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
666
4小时前
0
游客
登录 | 注册 方可回帖
返回