整理总结一波il2cpp攻防的知识点,希望能从插件脚本小子稍微变强一点点

Unity il2cpp由来已久,由于il2cpp本身是开源的,自然也就诞生了很多针对未加密Unity项目的自动化破解工具,Hack工具,比较为人熟知的有:

对于一些较为基础的概念和说明,网上也有很多文章分享,这里推荐大家去逛逛看雪论坛,贴几篇文章在这:

一般对IL2CPP的反编译流程是:

  1. ida强行分析加载流程,从内存Dump出global-metadata.dat,可以无视加密,并存储成文件,例如:IL2CPP 逆向初探
  2. 使用上述工具对global-metadata.dat进行反编译,得到dummy dll以及各种反编译需要的数据,里面包含了所有方法,字段的地址偏移
  3. ida加载可执行文件,根据地址和第二步拿到的数据,查看指定函数,字段的反汇编代码,分析逻辑
  4. 使用Hook工具,对相应函数Hook,强制更改逻辑

相对应的,就有很多防御方案

  • global-metadata.dat魔法数字加密
  • 更改Il2CppMetadataRegistration字段顺序,使自动Dump失败,不过Il2CppInspector已经做了处理
  • 针对自动化反编译工具魔改il2cpp源码,使其自动化失败
  • 针对动态dump工具隐藏il2cpp的系统api,或者检测相应进程名称,直接闪退游戏
  • 对可执行文件(GameAssembly.dll,il2cpp.so)进行混淆加密

推荐阅读:https://www.lfzxb.top/il2cpp-code-gen/ 了解IL2CPP的基础知识

加密头

位于Unity.IL2CPP.AssemblyConversion.SecondaryWrite.Steps.Global.WriteGlobalMetadataDat.WriteFinalDat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private static void WriteFinalDat(
WorkItemData<GlobalWriteContext, ReadOnlyCollection<ResultData<BaseSectionWriter, ReadOnlyCollection<DatSection>>>, object> data)
{
using (data.Context.Services.TinyProfiler.Section("Write Metadata Dat"))
{
SourceWritingContext sourceWritingContext = data.Context.CreateSourceWritingContext();
DatSection[] array = data.Item.SelectMany<ResultData<BaseSectionWriter, ReadOnlyCollection<DatSection>>, DatSection>((Func<ResultData<BaseSectionWriter, ReadOnlyCollection<DatSection>>, IEnumerable<DatSection>>) (r => (IEnumerable<DatSection>) r.Result)).ToArray<DatSection>();
using (FileStream fileStream = new FileStream(sourceWritingContext.Global.InputData.MetadataFolder.MakeAbsolute().CreateDirectory().Combine("global-metadata.dat").ToString(), FileMode.Create, FileAccess.Write))
{
int capacity = array.Length * 2 + 2;
List<uint> headerData = new List<uint>(capacity);
// 对应AF 1B B1 FA
headerData.Add(4205910959U);
// 对应1F 00 00 00
headerData.Add(31U /*0x1F*/);
fileStream.Seek((long) (capacity * 4), SeekOrigin.Begin);
foreach (DatSection datSection in array)
{
WriteGlobalMetadataDat.WriteStreamAndRecordHeader(sourceWritingContext, datSection.Name, (Stream) fileStream, (Stream) datSection.Stream, headerData, datSection.SectionAlignment);
datSection.Dispose();
}
fileStream.Seek(0L, SeekOrigin.Begin);
foreach (uint num in headerData)
fileStream.WriteUInt(num);
}
}
}

模板文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
typedef int32 TypeIndex;
typedef int32 TypeDefinitionIndex;
typedef int32 FieldIndex;
typedef int32 DefaultValueIndex;
typedef int32 DefaultValueDataIndex;
typedef int32 CustomAttributeIndex;
typedef int32 ParameterIndex;
typedef int32 MethodIndex;
typedef int32 GenericMethodIndex;
typedef int32 PropertyIndex;
typedef int32 EventIndex;
typedef int32 GenericContainerIndex;
typedef int32 GenericParameterIndex;
typedef int16 GenericParameterConstraintIndex;
typedef int32 NestedTypeIndex;
typedef int32 InterfacesIndex;
typedef int32 VTableIndex;
typedef int32 InterfaceOffsetIndex;
typedef int32 RGCTXIndex;
typedef int32 StringIndex;
typedef int32 StringLiteralIndex;
typedef int32 GenericInstIndex;
typedef int32 ImageIndex;
typedef int32 AssemblyIndex;
typedef int32 InteropDataIndex;


typedef struct Il2CppGlobalMetadataHeader
{
int32 sanity;
int32 version;
int32 stringLiteralOffset; // string data for managed code
int32 stringLiteralSize;
int32 stringLiteralDataOffset;
int32 stringLiteralDataSize;
int32 stringOffset; // string data for metadata
int32 stringSize;
int32 eventsOffset; // Il2CppEventDefinition
int32 eventsSize;
int32 propertiesOffset; // Il2CppPropertyDefinition
int32 propertiesSize;
int32 methodsOffset; // Il2CppMethodDefinition
int32 methodsSize;
int32 parameterDefaultValuesOffset; // Il2CppParameterDefaultValue
int32 parameterDefaultValuesSize;
int32 fieldDefaultValuesOffset; // Il2CppFieldDefaultValue
int32 fieldDefaultValuesSize;
int32 fieldAndParameterDefaultValueDataOffset; // uint8_t
int32 fieldAndParameterDefaultValueDataSize;
int32 fieldMarshaledSizesOffset; // Il2CppFieldMarshaledSize
int32 fieldMarshaledSizesSize;
int32 parametersOffset; // Il2CppParameterDefinition
int32 parametersSize;
int32 fieldsOffset; // Il2CppFieldDefinition
int32 fieldsSize;
int32 genericParametersOffset; // Il2CppGenericParameter
int32 genericParametersSize;
int32 genericParameterConstraintsOffset; // TypeIndex
int32 genericParameterConstraintsSize;
int32 genericContainersOffset; // Il2CppGenericContainer
int32 genericContainersSize;
int32 nestedTypesOffset; // TypeDefinitionIndex
int32 nestedTypesSize;
int32 interfacesOffset; // TypeIndex
int32 interfacesSize;
int32 vtableMethodsOffset; // EncodedMethodIndex
int32 vtableMethodsSize;
int32 interfaceOffsetsOffset; // Il2CppInterfaceOffsetPair
int32 interfaceOffsetsSize;
int32 typeDefinitionsOffset; // Il2CppTypeDefinition
int32 typeDefinitionsSize;
int32 imagesOffset; // Il2CppImageDefinition
int32 imagesSize;
int32 assembliesOffset; // Il2CppAssemblyDefinition
int32 assembliesSize;
int32 fieldRefsOffset; // Il2CppFieldRef
int32 fieldRefsSize;
int32 referencedAssembliesOffset; // int32_t
int32 referencedAssembliesSize;
int32 attributeDataOffset;
int32 attributeDataSize;
int32 attributeDataRangeOffset;
int32 attributeDataRangeSize;
int32 unresolvedIndirectCallParameterTypesOffset; // TypeIndex
int32 unresolvedIndirectCallParameterTypesSize;
int32 unresolvedIndirectCallParameterRangesOffset; // Il2CppMetadataRange
int32 unresolvedIndirectCallParameterRangesSize;
int32 windowsRuntimeTypeNamesOffset; // Il2CppWindowsRuntimeTypeNamePair
int32 windowsRuntimeTypeNamesSize;
int32 windowsRuntimeStringsOffset; // const char*
int32 windowsRuntimeStringsSize;
int32 exportedTypeDefinitionsOffset; // TypeDefinitionIndex
int32 exportedTypeDefinitionsSize;
} Il2CppGlobalMetadataHeader;


typedef struct Il2CppStringLiteralInfoDefinition
{
uint32 Length;
uint32 Offset;
} Il2CppStringLiteralInfoDefinition;


typedef struct (uint infoSize, uint stringLiteralDataOffset, Il2CppStringLiteralInfoDefinition StringLiteralInfos[])
{
typedef struct (uint stringLiteralDataOffset, uint index, Il2CppStringLiteralInfoDefinition StringLiteralInfos[])
{
local uint infoOffset = StringLiteralInfos[index].Offset;
local uint infoLength = StringLiteralInfos[index].Length;
FSeek(stringLiteralDataOffset + infoOffset);
if (infoLength > 0) char data[infoLength] <optimize=false>;
} StringLiteralDefinition <read=(infoLength > 0 ? data : "null")>;
local uint index = 0;
while (index + 1 < infoSize) StringLiteralDefinition StringLiteralDefinitions(stringLiteralDataOffset, index++, StringLiteralInfos);
} Il2CppStringLiteralDefinition;


typedef struct Il2CppImageDefinition
{
StringIndex nameIndex;
AssemblyIndex assemblyIndex;

TypeDefinitionIndex typeStart;
uint32 typeCount;

TypeDefinitionIndex exportedTypeStart;
uint32 exportedTypeCount;

MethodIndex entryPointIndex;
uint32 token;

CustomAttributeIndex customAttributeStart;
uint32 customAttributeCount;
} Il2CppImageDefinition;


#define PUBLIC_KEY_BYTE_LENGTH 8
typedef struct Il2CppAssemblyNameDefinition
{
StringIndex nameIndex;
StringIndex cultureIndex;
StringIndex hashValueIndex;
StringIndex publicKeyIndex;
uint32 hash_alg;
int32 hash_len;
uint32 flags;
int32 major;
int32 minor;
int32 build;
int32 revision;
ubyte public_key_token[PUBLIC_KEY_BYTE_LENGTH];
} Il2CppAssemblyNameDefinition;

typedef struct Il2CppAssemblyDefinition
{
ImageIndex imageIndex;
uint32 token;
int32 referencedAssemblyStart;
int32 referencedAssemblyCount;
Il2CppAssemblyNameDefinition aname;
} Il2CppAssemblyDefinition;

typedef struct Il2CppMethodDefinition
{
StringIndex nameIndex;
TypeDefinitionIndex declaringType;
TypeIndex returnType;
ParameterIndex parameterStart;
GenericContainerIndex genericContainerIndex;
GenericMethodIndex methodIndex;
CustomAttributeIndex customAttributeStart;
uint32 token;
uint16 flags;
uint16 iflags;
uint16 slot;
uint16 parameterCount;
} Il2CppMethodDefinition;

typedef struct Il2CppParameterDefinition
{
StringIndex nameIndex;
uint32 token;
CustomAttributeIndex customAttributeStart;
TypeIndex typeIndex;
uint32 attrs;
} Il2CppParameterDefinition;

typedef struct Il2CppFieldDefinition
{
StringIndex nameIndex;
TypeIndex typeIndex;
uint32 attrs;
DefaultValueDataIndex defaultValueIndex;
CustomAttributeIndex customAttributeStart;
uint32 token;
} Il2CppFieldDefinition;

typedef struct Il2CppPropertyDefinition
{
StringIndex nameIndex;
MethodIndex get;
MethodIndex set;
uint32 attrs;
CustomAttributeIndex customAttributeStart;
uint32 token;
} Il2CppPropertyDefinition;

typedef struct Il2CppEventDefinition
{
StringIndex nameIndex;
TypeIndex type;
MethodIndex add;
MethodIndex remove;
MethodIndex raise;
CustomAttributeIndex customAttributeStart;
uint32 token;
} Il2CppEventDefinition;

typedef struct Il2CppTypeDefinition
{
StringIndex nameIndex;
StringIndex namespaceIndex;
TypeIndex byvalTypeIndex;
TypeIndex byrefTypeIndex;

TypeIndex declaringTypeIndex;
TypeIndex parentIndex;
TypeIndex elementTypeIndex; // we can probably remove this one. Only used for enums

RGCTXIndex rgctxStartIndex;
int32 rgctxCount;

GenericContainerIndex genericContainerIndex;

uint32 flags;

FieldIndex fieldStart;
MethodIndex methodStart;
EventIndex eventStart;
PropertyIndex propertyStart;
NestedTypeIndex nestedTypesStart;
InterfacesIndex interfacesStart;
VTableIndex vtableStart;
InterfacesIndex interfaceOffsetsStart;

uint16 method_count;
uint16 property_count;
uint16 field_count;
uint16 event_count;
uint16 nested_type_count;
uint16 vtable_count;
uint16 interfaces_count;
uint16 interface_offsets_count;

// bitfield to portably encode boolean values as single bits
// 01 - valuetype;
// 02 - enumtype;
// 03 - has_finalize;
// 04 - has_cctor;
// 05 - is_blittable;
// 06 - is_import_or_windows_runtime;
// 07-10 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128)
uint32 bitfield;
uint32 token;
} Il2CppTypeDefinition;

Il2CppGlobalMetadataHeader metadataHeader <comment="metadata header information">;

local uint infoSize = metadataHeader.stringLiteralSize / sizeof(Il2CppStringLiteralInfoDefinition);

FSeek(metadataHeader.stringLiteralOffset);
Il2CppStringLiteralInfoDefinition StringLiteralInfoDefinitions[infoSize] <comment="metadata define StringLiteralInfo">;
Il2CppStringLiteralDefinition StringLiteralDefinitions(infoSize, metadataHeader.stringLiteralDataOffset, StringLiteralInfoDefinitions) <comment="metadata define StringLiteralDefinitions">;

// 正确计算images数组大小
local uint imagesCount = metadataHeader.imagesSize / sizeof(Il2CppImageDefinition);
FSeek(metadataHeader.imagesOffset);
Il2CppImageDefinition imagesDefinitions[imagesCount] <comment="metadata define images">;

// 正确计算assemblies数组大小
local uint assembliesCount = metadataHeader.assembliesSize / sizeof(Il2CppAssemblyDefinition);
FSeek(metadataHeader.assembliesOffset);
Il2CppAssemblyDefinition assemblyDefinitions[assembliesCount] <comment="metadata define assemblys">;

// 正确计算typeDefinitions数组大小
local uint typeDefinitionsCount = metadataHeader.typeDefinitionsSize / sizeof(Il2CppTypeDefinition);
FSeek(metadataHeader.typeDefinitionsOffset);
Il2CppTypeDefinition typeDefinitions[typeDefinitionsCount] <comment="metadata define types">;

// 正确计算methods数组大小
local uint methodsCount = metadataHeader.methodsSize / sizeof(Il2CppMethodDefinition);
FSeek(metadataHeader.methodsOffset);
Il2CppMethodDefinition methodDefinitions[methodsCount] <comment="metadata define methods">;

// 正确计算parameters数组大小
local uint parametersCount = metadataHeader.parametersSize / sizeof(Il2CppParameterDefinition);
FSeek(metadataHeader.parametersOffset);
Il2CppParameterDefinition parameterDefinitions[parametersCount] <comment="metadata define parameters">;

// 正确计算fields数组大小
local uint fieldsCount = metadataHeader.fieldsSize / sizeof(Il2CppFieldDefinition);
FSeek(metadataHeader.fieldsOffset);
Il2CppFieldDefinition fieldDefinitions[fieldsCount] <comment="metadata define fields">;

// 正确计算properties数组大小
local uint propertiesCount = metadataHeader.propertiesSize / sizeof(Il2CppPropertyDefinition);
FSeek(metadataHeader.propertiesOffset);
Il2CppPropertyDefinition propertyDefinitions[propertiesCount] <comment="metadata define properties">;

// 正确计算events数组大小
local uint eventsCount = metadataHeader.eventsSize / sizeof(Il2CppEventDefinition);
FSeek(metadataHeader.eventsOffset);
Il2CppEventDefinition eventDefinitions[eventsCount] <comment="metadata define events">;

// 注意:在当前版本的IL2CPP中,metadataUsagePairs和metadataUsageList已被移除
// 如果需要解析其他部分的数据,请参考IL2CPP源码中的GlobalMetadataFileInternals.h