type
status
date
slug
summary
tags
category
icon
password
背景
分析il2cpp架构生成的游戏的字符串信息时发现了新老unity版本的差异,沿着差异审查代码合理推测确定核心修改点
旧版实现:字符串解析
metadataUsagesCount保存在global-metadata头结构中,读取之,并循环index从0~metadataUsagesCount,传递进入InitializeMethodMetadata
InitializeMethodMetadata实际上就是原始的il2cpp的函数,但是将除了字符串处理外的其他case分支全部注释,最终得以对所有的字符串index获取对应的字符串数据,然后按照destindex的顺序输出到string_literal.txt文本中
旧版实现:函数名解析
从header头结构中获取typeCount,然后从0~typeCount循环调用GetTypeInfoFromTypeDefinitionIndex拿到g_classes再把这个传递给SetupMethods函数获取函数名
新版
新版中既没有metadataUsagesCount也没有typeCount,所以没法直接获取到index然后遍历。这就是最大的问题,也是最大的差异。
- 首先分析新版的InitializeMethodMetadata函数,发现另一个遍历index的for循环,但是这里传递的不是从0到xxcount这样的index,而是s_Il2CppMetadataRegistration->metadataUsages[i],向上追溯这个结构体在哪里赋值的,最终在il2cppmetadataregistration.c中发现g_MetadataRegistration中的各项元素被赋值,但如果现在是以二进制的形式保存在gameassembly中的话如何定位这样的数据就有些困难了
- 回过头来看InitializeAllMethodMetadata函数,他依然是从g_MetadataRegistration中拿到该metadataUsages元素,这个元素即metadataPointer,即[type + index + inited]数值,之前分析过。但是新版的InitializeMethodMetadata函数只初始化TypeInfo/MethodDef/MethodRef三种类型(通过调用InitializeRuntimeMetadata函数),搜索case kIl2CppMetadataUsageStringLiteral:没有发现调用,说明新版本中字符串的元数据信息在初始化期间不再预调用,根据之前的分析可以推测字符串的初始化都在第一次访问的时候由gameassembly中的C#转成的cpp文件发起初始化请求
- 新版函数名获取流程与旧版一样,问题还是在index到底是啥。传递给GetTypeInfoFromTypeDefinitionIndex函数的参数是methodDefinition->declaringType,而methodDefinition是通过传递index给GetMethodDefinitionFromIndex获取的,这个函数的获取倒不是问题,因为也是纯粹的依赖global-metadata结构即可,关键是这次换递给他的index不是那个元数据文件可以拿到的,逆向跟踪回溯调用者回溯到这样的函数GetMethodInfoFromEncodedIndex,发现index是从[type + index + inited]结构数值中取出来的,这个数值是对传递给InitializeRuntimeMetadata函数的第一个参数解引用拿到的 而传递给InitializeRuntimeMetadata函数的第一个参数要么是刚刚说的g_MetadataRegistration中定义的(但Demo此处为空),要么是脚本文件转成的CPP中直接调用过来的,也就是原始的定义 之前分析过,在il2cppmetadatausage.c中直接定义的数值
- 从il2cppmetadatausage.c定义中试图找规律,发现同类的index解析出来后一定是单调递增排布的,但是不连续且开始不是0,所以之前的从0~count的方法已经不行了
总结
可以看出新老版本其实大部分都是一样的,思路也是一样的只要有index那么直接复用il2cpp的解析代码打印就好了,但是问题就是这样的index在旧版中可以直接通过元数据文件拿到,在新版中被编译进了gameassembly二进制中,难度是上升了的