告别硬编码解析:Unity IL2CPP属性参数提取的自动化方案
你是否还在手动解析Unity IL2CPP(中间语言转C++)编译后的属性参数?面对VersionAttribute、ArrayLengthAttribute等自定义属性,是否因缺乏统一提取方案而反复编写解析代码?本文将系统讲解如何使用Il2CppDumper的属性解析框架,通过CustomAttributeDataReader实现属性值的自动化提取,让逆向工程效率提升80%。
读完本文你将掌握:
- 理解IL2CPP属性在内存中的存储结构
- 使用AttributeArgument处理复杂参数类型
- 定制VersionAttribute与ArrayLengthAttribute解析逻辑
- 构建通用属性提取器的完整流程
IL2CPP属性解析核心架构
Il2CppDumper的属性解析系统由属性定义、数据读取器和参数处理三部分组成,形成完整的解析流水线:
核心实现位于以下模块:
- 属性定义:Attributes/目录下的VersionAttribute.cs与ArrayLengthAttribute.cs
- 数据读取:Utils/CustomAttributeDataReader.cs
- 参数处理:Utils/AttributeArgument.cs
自定义属性定义解析
VersionAttribute版本控制实现
VersionAttribute用于标记字段的版本兼容性范围,定义在VersionAttribute.cs中:
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
class VersionAttribute : Attribute
{
public double Min { get; set; } = 0; // 最低支持版本
public double Max { get; set; } = 99; // 最高支持版本
}
该属性允许在单个字段上多次应用(AllowMultiple = true),这意味着同一字段可能需要处理多个版本区间的兼容性检查。
ArrayLengthAttribute数组长度约束
ArrayLengthAttribute用于指定数组字段的长度限制,定义在ArrayLengthAttribute.cs中:
[AttributeUsage(AttributeTargets.Field)]
class ArrayLengthAttribute : Attribute
{
public int Length { get; set; } // 数组固定长度
}
与VersionAttribute不同,数组长度属性不允许重复应用,每个字段只能有一个长度约束。
二进制数据读取流程
CustomAttributeDataReader是解析的核心引擎,其工作流程分为三个阶段:
- 元数据定位:通过ctorIndex找到属性构造函数定义
- 数据解析:读取参数、字段和属性值的二进制流
- 类型转换:将原始数据转换为C#对象表示
关键代码实现位于GetStringCustomAttributeData方法:
// 定位构造函数元数据
var ctorIndex = ReadInt32();
var methodDef = metadata.methodDefs[ctorIndex];
var typeDef = metadata.typeDefs[methodDef.declaringType];
// 读取参数数量
var argumentCount = this.ReadCompressedUInt32();
var fieldCount = this.ReadCompressedUInt32();
var propertyCount = this.ReadCompressedUInt32();
// 处理参数数据
for (var i = 0; i < argumentCount; i++)
{
argList.Add($"{AttributeDataToString(ReadAttributeDataValue())}");
}
属性参数类型处理
BlobValue类型系统
Il2CppDumper使用BlobValue类统一表示不同类型的属性参数值,支持字符串、数组、枚举等复杂类型:
在CustomAttributeDataReader.cs的AttributeDataToString方法中,实现了不同类型的格式化逻辑:
switch (blobValue.il2CppTypeEnum)
{
case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
return $"\"{blobValue.Value}\"";
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
var array = (BlobValue[])blobValue.Value;
// 数组元素递归处理
return $"new[] {{ {string.Join(", ", list)} }}";
case Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX:
return $"typeof({executor.GetTypeName(il2CppType)})";
}
AttributeArgument参数容器
AttributeArgument.cs定义了参数存储结构,将值与元数据索引关联:
public class AttributeArgument
{
public BlobValue Value; // 参数值
public int Index; // 元数据索引
}
这个简单的容器类在VisitCustomAttributeData方法中被批量使用,收集所有属性参数:
visitor.Arguments = new AttributeArgument[argumentCount];
for (var i = 0; i < argumentCount; i++)
{
var argument = visitor.Arguments[i] = new AttributeArgument();
argument.Value = ReadAttributeDataValue();
argument.Index = i;
}
实战:VersionAttribute解析实现
以下是提取VersionAttribute属性值的完整示例代码:
// 创建属性数据读取器
var reader = new CustomAttributeDataReader(executor, attributeData);
var visitor = reader.VisitCustomAttributeData();
// 处理版本属性参数
foreach (var arg in visitor.Arguments)
{
if (arg.Value.il2CppTypeEnum == Il2CppTypeEnum.IL2CPP_TYPE_R8)
{
if (arg.Index == 0) minVersion = (double)arg.Value.Value;
if (arg.Index == 1) maxVersion = (double)arg.Value.Value;
}
}
// 输出版本约束信息
Console.WriteLine($"版本范围: {minVersion} - {maxVersion}");
这段代码通过Index区分Min和Max参数,从BlobValue中提取double类型的版本数值。
通用属性提取器构建指南
基于Il2CppDumper的属性解析框架,可以构建适用于任何自定义属性的提取器,核心步骤如下:
- 定义属性模型:创建与目标属性匹配的C#类
- 实现类型转换器:扩展AttributeDataToString方法支持新类型
- 注册解析器:在CustomAttributeReaderVisitor中添加处理逻辑
- 测试验证:使用实际IL2CPP二进制文件验证提取结果
完整的实现模板可参考Utils/CustomAttributeDataReader.cs中的现有模式。
总结与进阶方向
Il2CppDumper的属性解析系统通过CustomAttributeDataReader和AttributeArgument提供了灵活的扩展机制,已支持VersionAttribute和ArrayLengthAttribute等常用属性。未来可进一步优化:
- 添加枚举类型自动解析(当前需手动处理)
- 支持嵌套属性对象的递归提取
- 实现属性值的可视化展示界面
掌握这些技术,你将能够轻松应对各种IL2CPP属性解析场景,大幅提升Unity逆向工程的效率和准确性。
提示:完整代码示例可在项目Utils/目录下找到,建议结合实际二进制文件进行调试学习。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



