使用frida获取unity il2cpp符号信息

本次主角是米X游的《原X》,原由照样在网上看到了一篇关于崩3的符号信息还原的文章,就想看看原神上有没有转变。

0x1 靠山

本次剖析选取的是android平台的,游戏接纳unity开发,使用il2cpp模式编译。

使用 IL2CPP 模式编译,游戏中使用的字符串都被保存在了一个global-metadata.dat的资源文件里,只有在动态运行时才会将这些字符串读入内存。一样平常使用Il2CppDumper就可以读取global-metadata.dat文件中的信息,辅助反编译。

有破解就有珍爱,可能会遇到无法dump或者global-metadata.dat文件结构被修改的情形,无法使用Il2CppDumper,这个时刻就需要自己着手了。

0x2 IL2CPP 加载历程

github上随便找一个il2cpp的源码,搜索global-metadata.dat,发现只有函数MetadataCache::Initialize()处使用。

void MetadataCache::Initialize()
{
    s_GlobalMetadata = vm::MetadataLoader::LoadMetadataFile("global-metadata.dat");
    s_GlobalMetadataHeader = (const Il2CppGlobalMetadataHeader*)s_GlobalMetadata;
    ...

 }

查看LoadMetadataFile代码:

void* MetadataLoader::LoadMetadataFile(const char* fileName)
{
    std::string resourcesDirectory = utils::PathUtils::Combine(utils::Runtime::GetDataDir(), utils::StringView<char>("Metadata"));

    std::string resourceFilePath = utils::PathUtils::Combine(resourcesDirectory, utils::StringView<char>(fileName, strlen(fileName)));

    int error = 0;
    FileHandle* handle = File::Open(resourceFilePath, kFileModeOpen, kFileAccessRead, kFileShareRead, kFileOptionsNone, &error);
    if (error != 0)
        return NULL;

    void* fileBuffer = utils::MemoryMappedFile::Map(handle);

    File::Close(handle, &error);
    if (error != 0)
    {
        utils::MemoryMappedFile::Unmap(fileBuffer);
        fileBuffer = NULL;
        return NULL;
    }

    return fileBuffer;
}

很明显,就是Initialize时挪用LoadMetadataFile将global-metadata.dat映射到内存中。

还需要稍微领会一下global-metadata.dat文件结构。

struct Il2CppGlobalMetadataHeader
{
    int32_t sanity;
    int32_t version;
    int32_t stringLiteralOffset; // string data for managed code
    int32_t stringLiteralCount;
    int32_t stringLiteralDataOffset;
    int32_t stringLiteralDataCount;
    int32_t stringOffset; // string data for metadata
    int32_t stringCount;
    int32_t eventsOffset; // Il2CppEventDefinition
    int32_t eventsCount;
    int32_t propertiesOffset; // Il2CppPropertyDefinition
    int32_t propertiesCount;
    int32_t methodsOffset; // Il2CppMethodDefinition
    int32_t methodsCount;
    ...
}

0x3 某手游加载历程

反编译原神的il2cpp.so,loadmetadataFile()函数,对比原始loadmetadataFile,可以清晰的看到将文件映射进内存后进行了一次解密操作。(在MetadataCache::Initialize中另有一次解密)

usdt钱包(caibao.it):使用frida获取unity il2cpp符号信息 安全技术 移动安全 第1张

,

皇冠体育APP下载

(www.huangguan.us)是一个提供皇冠 *** APP下载、皇冠会员APP下载、皇冠体育最新登录线路、新2皇冠网址的的体育平台。也只有皇冠APP可以真正地带给你顶级体育赛事的娱乐体验感。立马一键皇冠体育开户,世界体育赛事等你欣赏。

,

再查看MetadataCache::Initialize(),凭据红框,对比原始代码,可以发现global-metadata.dat文件结构已经被修改,纵然乐成dump文件也不能使用Il2CppDumper。

usdt钱包(caibao.it):使用frida获取unity il2cpp符号信息 安全技术 移动安全 第2张

0x4 hook SetupMethodsLocked

为什么选择在SetupMethodsLocked hook,有其他大佬已经讲得很仔细了,可以参考还原使用IL2CPP编译的unity游戏的symbol

阅读源码,通过il2cpp_class_get_methods->Class::GetMethods->Class::SetupMethods跟踪到SetupMethodsLocked:

const MethodInfo* il2cpp_class_get_methods(Il2CppClass *klass, void* *iter)//导出函数很好找
{
    return Class::GetMethods(klass, iter);
}

 const MethodInfo* Class::GetMethods(Il2CppClass *klass, void* *iter)
    {
     ...
        if (!*iter)
        {
            Class::SetupMethods(klass);
            if (klass->method_count == 0)
                return NULL;
            *iter = &klass->methods[0];
            return klass->methods[0];
        }
   ...
   }

void Class::SetupMethods(Il2CppClass *klass)
{
    if (klass->method_count || klass->rank)
    {
        FastAutoLock lock(&g_MetadataLock);
        SetupMethodsLocked(klass, lock);
    }
}
void SetupMethodsLocked(Il2CppClass *klass, const FastAutoLock& lock)
    {
        if ((!klass->method_count && !klass->rank) || klass->methods)
            return;

        if (klass->generic_class)
        {
            InitLocked(GenericClass::GetTypeDefinition(klass->generic_class), lock);
            GenericClass::SetupMethods(klass);
        }
        else if (klass->rank)
        {
            InitLocked(klass->element_class, lock);
            SetupVTable(klass, lock);
        }
        else
        {
            if (klass->method_count == 0)
            {
                klass->methods = NULL;
                return;
            }

            klass->methods = (const MethodInfo**)IL2CPP_CALLOC(klass->method_count, sizeof(MethodInfo*));
            MethodInfo* methods = (MethodInfo*)IL2CPP_CALLOC(klass->method_count, sizeof(MethodInfo));
            MethodInfo* newMethod = methods;

            MethodIndex start = klass->typeDefinition->methodStart;
            IL2CPP_ASSERT(start != kFieldIndexInvalid);
            MethodIndex end = start + klass->method_count;

            for (MethodIndex index = start; index < end; ++index)
            {
                const Il2CppMethodDefinition* methodDefinition = MetadataCache::GetMethodDefinitionFromIndex(index);

                newMethod->name = MetadataCache::GetStringFromIndex(methodDefinition->nameIndex);
                newMethod->methodPointer = MetadataCache::GetMethodPointerFromIndex(methodDefinition->methodIndex);
                newMethod->invoker_method = MetadataCache::GetMethodInvokerFromIndex(methodDefinition->invokerIndex);
                newMethod->declaring_type = klass;
                newMethod->return_type = MetadataCache::GetIl2CppTypeFromIndex(methodDefinition->returnType);

                ParameterInfo* parameters = (ParameterInfo*)IL2CPP_CALLOC(methodDefinition->parameterCount, sizeof(ParameterInfo));
                ParameterInfo* newParameter = parameters;
                for (uint16_t paramIndex = 0; paramIndex < methodDefinition->parameterCount; ++paramIndex)
                {
                    const Il2CppParameterDefinition* parameterDefinition = MetadataCache::GetParameterDefinitionFromIndex(methodDefinition->parameterStart + paramIndex);
                    newParameter->name = MetadataCache::GetStringFromIndex(parameterDefinition->nameIndex);
                    newParameter->position = paramIndex;
                    newParameter->token = parameterDefinition->token;
                    newParameter->customAttributeIndex = parameterDefinition->customAttributeIndex;
                    newParameter->parameter_type = MetadataCache::GetIl2CppTypeFromIndex(parameterDefinition->typeIndex);

                    newParameter++;
                }
                newMethod->parameters = parameters;

                newMethod->customAttributeIndex = methodDefinition->customAttributeIndex;
                newMethod->flags = methodDefinition->flags;
                newMethod->iflags = methodDefinition->iflags;
                newMethod->slot = methodDefinition->slot;
                newMethod->parameters_count = static_cast<const uint8_t>(methodDefinition->parameterCount);
                newMethod->is_inflated = false;
                newMethod->token = methodDefinition->token;
                newMethod->methodDefinition = methodDefinition;
                newMethod->genericContainer = MetadataCache::GetGenericContainerFromIndex(methodDefinition->genericContainerIndex);
                if (newMethod->genericContainer)
                    newMethod->is_generic = true;

                klass->methods[index - start] = newMethod;

                newMethod++; //在这里hook,就可以轻松拿到所有MethodInfo
            }
        }
    }

MethodInfo、Il2CppClass的结构也需要领会一下:

struct Il2CppClass
{
    const Il2CppImage* image;
    void* gc_desc;
    const char* name;
    const char* namespaze;
    ... 
}
struct MethodInfo
{
    Il2CppMethodPointer methodPointer;
    InvokerMethod invoker_method;
    const char* name;
    Il2CppClass *declaring_type;
    const Il2CppType *return_type;
    const ParameterInfo* parameters;
  ...
};

反编译获取hook地址:

usdt钱包(caibao.it):使用frida获取unity il2cpp符号信息 安全技术 移动安全 第3张

编写frida js:

// hook SetupMethodsLocked
var module = Process.findModuleByName("libil2cpp.so");
var p_size = 8;
Interceptor.attach(ptr(module.base).add(0x72F09EC).add(0x204),{
    onEnter:function(args){
    var newMethod = this.context.x20
    var pointer = newMethod.readPointer(); //MethodInfo
    var name = newMethod.add(p_size * 2).readPointer().readCString();
    var klass = newMethod.add(p_size * 3).readPointer();//Il2CppClass
    var klass_name = klass.add(p_size * 2).readPointer().readCString();
    var klass_paze = klass.add(p_size * 3).readPointer().readCString();
    send(klass_paze+"."+klass_name+":"+name+"    -> "+pointer.sub(module.base));
    }
});

乐成hook,也乐成获取了符号信息与地址。但人人也能看出,本次主角做了混淆。关于混淆又是另一个故事了。

usdt钱包(caibao.it):使用frida获取unity il2cpp符号信息 安全技术 移动安全 第4张

参考

  1. [unity]Real-time Dump
  2. [Honkai 3rd]v3.5符号还原


Allbet Gaming声明:该文看法仅代表作者自己,与本平台无关。转载请注明:usdt钱包(caibao.it):使用frida获取unity il2cpp符号信息
发布评论

分享到:

usdt官网下载(www.caibao.it):喜事近?玄彬孙艺珍被曝搬入婚房同居,经纪公司战战兢兢回应
8 条回复
  1. 电银付APP安装教程
    电银付APP安装教程
    (2020-12-26 00:06:10) 1#

    因全球对这神出鬼没病毒的泉源至今照样一筹莫展 无可奈何 好看得不得了,快来

  2. 卡利开户
    卡利开户
    (2021-01-02 00:02:29) 2#

    皇冠新现金网平台www.huangguan.us是一个提供皇冠代理APP下载、皇冠会员APP下载、皇冠体育最新登录线路、新2皇冠网址的的体育平台。新皇冠体育官网是多年来值得广大客户信赖的平台,我们期待您的到来!难得的好文

    1. 环球UG
      环球UG
      (2021-01-02 09:04:21)     

      各地疫情爆发时点不太一样,对股市的影响也不尽相同。考虑到本轮行情的下跌,更多的是由于意大利疫情爆发引起,且目前为止意大利疫情的情况仍是最严重的,对股市影响也最大。因此,衡量疫情对股市的影响(石油价格战对股市的影响,相对较为次要),以意大利指数近期高点,2月19日开始计算,据此评估疫情导致的股市市值损失。可以的哈,眼熟我

      1. AllbetGaming下载
        AllbetGaming下载
        (2021-01-18 11:37:19)     

        USDT支付菜包钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台。免费提供Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜包Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。大家还记得我不

    2. 卡利充值
      卡利充值
      (2021-01-14 22:20:09)     

      USDT充值接口菜包钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台。免费提供Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜包Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。很强大

  3. UG环球官网
    UG环球官网
    (2021-02-13 00:04:55) 3#

    欧博开户www.sunbet.us欢迎进入欧博开户平台(Allbet Game),欧博开户平台开放欧博Allbet开户、欧博Allbet代理开户、欧博Allbet电脑客户端、欧博AllbetAPP下载等业务。特点挺多的

  4. 皇冠即时比分
    皇冠即时比分
    (2021-03-06 00:00:00) 4#

    制造勉勉强强,可以的

  5. 新2网址大全
    新2网址大全
    (2021-09-10 00:04:45) 5#

    欧博亚洲客户端www.aLLbetgame.us),欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

    一直在看哦

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。