0x00 背景
还原使用IL2CPP编译的unity游戏的symbol(一)中介绍了如何还原il2cpp里的字符串,这篇文章将介绍如何还原函数名类名等符号。
0x01 IL2CPP代码分析
首先仔细看一下vm/MetadataCache.cpp
的MetadataCache::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