整理总结一波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);
}
}
}