还原使用IL2CPP编译的unity游戏的symbol(二)

0x00 背景

还原使用IL2CPP编译的unity游戏的symbol(一)中介绍了如何还原il2cpp里的字符串,这篇文章将介绍如何还原函数名类名等符号。

0x01 IL2CPP代码分析

首先仔细看一下vm/MetadataCache.cppMetadataCache::GetMethodInfoFromMethodDefinitionIndex

//vm/MetadataCache.cpp
const MethodInfo* MetadataCache::GetMethodInfoFromMethodDefinitionIndex (MethodIndex index)
{
    assert(index >= 0 && static_cast<uint32_t>(index) <= s_GlobalMetadataHeader->methodsCount / sizeof (Il2CppMethodDefinition));

    if (!s_MethodInfoDefinitionTable[index])
    {
        const Il2CppMethodDefinition* methodDefinition = GetMethodDefinitionFromIndex (index);
        Il2CppClass* typeInfo = GetTypeInfoFromTypeDefinitionIndex (methodDefinition->declaringType);
        Class::SetupMethods (typeInfo);
        s_MethodInfoDefinitionTable[index] = typeInfo->methods[index - typeInfo->typeDefinition->methodStart];
    }

    return s_MethodInfoDefinitionTable[index];
}

这段代码先根据index获取对象class,然后将class传给SetupMethods,使用这个函数将class中相应的method的名字与指针匹配起来。

我们看一下vm/Class.cpp里的SetupMethodsLocked这个函数具体是怎么做的:

// passing lock to ensure we have acquired it. We can add asserts later
void SetupMethodsLocked (Il2CppClass *klass, const FastAutoLock& lock)
{
    ...
    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);
        ...
    }
    ...
}

可以看到这个函数几乎做了所有我们想要的事,把method的名字和地址全匹配起来了。

其中GetMethodPointerFromIndex的代码如下:

//vm/MetadataCache.cpp
Il2CppMethodPointer MetadataCache::GetMethodPointerFromIndex (MethodIndex index)
{
    if (index == kMethodIndexInvalid)
        return NULL;

    assert (index >= 0 && static_cast<uint32_t>(index) < s_Il2CppCodeRegistration->methodPointersCount);
    return s_Il2CppCodeRegistration->methodPointers[index];
}

它仅仅是返回了s_Il2CppCodeRegistration->methodPointers[index]的值。我们来看一下s_Il2CppCodeRegistration->methodPointers是个什么东西。

这里我们根据还原使用IL2CPP编译的unity游戏的symbol(一),可以知道s_Il2CppCodeRegistration其实就是g_CodeRegistration

static void s_Il2CppCodegenRegistration()
{
    il2cpp_codegen_register (&g_CodeRegistration, &g_MetadataRegistration, &s_Il2CppCodeGenOptions);
}

g_CodeRegistration的定义是:

const Il2CppCodeRegistration g_CodeRegistration = 
{
    16812,
    g_MethodPointers,
    2,
    g_ReversePInvokeWrapperPointers,
    35,
    g_DelegateWrappersManagedToNative,
    195,
    g_MarshalingFunctions,
    0,
    NULL,
    4831,
    g_Il2CppGenericMethodPointers,
    1909,
    g_Il2CppInvokerPointers,
    3138,
    g_AttributeGenerators,
    0,
    NULL,
};

那么s_Il2CppCodeRegistration->methodPointers其实就是g_MethodPointers了。注意其中的g_MethodPointers是函数的地址表,它将会出现在生成的可执行文件中,我们如果能将这个表中的函数按顺序命名即可大功告成。也就是说,我们只要循环调用SetupMethods,事实上就可以把g_MethodPointers所指向的函数和它们的名字匹配起来了。

g_MethodPointers的定义如下:

extern const Il2CppMethodPointer g_MethodPointers[16812] = 
{
    Locale_GetText_m1954433032,
    Locale_GetText_m2553164138,
    SafeHandleZeroOrMinusOneIsInvalid__ctor_m3340306667,
    SafeHandleZeroOrMinusOneIsInvalid_get_IsInvalid_m2033528032,
    SafeWaitHandle__ctor_m1710231470,
    SafeWaitHandle_ReleaseHandle_m634725016,
    ...
    VignetteAndChromaticAberration__ctor_m3270745889,
    VolumeHandler_Start_m3226079559,
    VolumeHandler_SetVolume_m3613034220,
    VolumeHandler_OnDestroy_m1170460248,
    VolumeHandler__ctor_m818831955,
};

0x02 IDA插件

如果你是iOS的binary,与还原使用IL2CPP编译的unity游戏的symbol(一)一样,其实在g_MetadataUsages的结束之后紧接着就是g_MethodPointers了。在g_MethodPointers起始处按顺序给这些地址命名即可。

0x03 代码

https://github.com/nevermoe/unity_metadata_loader

0x04 其他

我使用了这个插件尝试对某pkg进行了分析,然后做了一个作弊工具,使得无论你怎么扔精灵球,总是100%能砸中小精灵(注意,不是100%抓住)。
视频链接:
https://youtu.be/tj_2cEI5qlQ
https://youtu.be/l8avwEdaBnY

发表时的PPT:
avtokyo_jp_re
avtokyo_en_re