0x0 前言
用Unity做的视觉小说基本上没啥会上高强度的加密,顶多搞点自定义封包乱七八糟的,对Unity底层动刀的几乎没有,和国内外二游比起来简直是眉清目秀(乐
0x1 Mono
以二分之一为例,这样的目录结构就是典型的mono打包方式,具体表现为只有UnityPlayer.dll和启动器,没有GameAssembly.dll:
要逆向这个游戏用dnspyEX反编译二分之一_Data\Managed文件夹下面的Assembly-CSharp.dll即可,非常明了的C#代码:

0x2 il2cpp
这玩意用一张图来解释大概是这样
C#代码由C#编译器编译成托管代码,然后切割掉没有使用的托管代码,再转换成C++代码,和libil2cpp一起编译成Native DLL。
尽管被编译成了Native代码,但是C#的一些语言特性(反射GC等)依然要实现,所以 C# 中的类名、方法名、属性名、字符串等东西会额外写入global-metadata.dat
以Clover Days + Steam版本为例,这样的目录结构就是典型的il2cpp打包方式:
要逆向il2cpp首先得把函数名,结构体,字符串等全部解析出来,可以用Il2CppDumper或Il2CppInspectorRedux来完成全部流程
然后把在反编译器里面跑一下生成的脚本就能恢复全部符号和结构体以及字符串:
后续只要注意常量一般在.cctor里面填充就行
比如我要逆Clover Days + Steam的system.dat文件的加密,只需要搜system.dat,定位到反编译器里面对应的位置,然后查交叉引用抠出整个调用链即可
C
System_IO_Stream_o *PkMain__OpenInternal(PkMain_o *this, System_String_o *fpath, const MethodInfo *method)
{
int (*v3)(void); // eax
int v4; // ebx
System_IO_FileStream_o *v5; // esi
int32_t v6; // ecx
int v8; // eax
int v9; // eax
System_IO_FileNotFoundException_o *v10; // esi
int v11; // eax
if ( !byte_A2B20B )
{
sub_B6D00(&System_IO_FileStream_TypeInfo);
byte_A2B20B = 1;
}
v3 = dword_A2B7B8;
if ( !dword_A2B7B8 )
{
v3 = sub_B8B20("UnityEngine.SystemInfo::GetPhysicalMemoryMB()");
if ( !v3 )
{
v8 = sub_B5CF0("UnityEngine.SystemInfo::GetPhysicalMemoryMB()");
sub_B43F0(v8, 0);
}
dword_A2B7B8 = v3;
}
v4 = v3();
if ( !System_IO_File__Exists(fpath, 0) )
{
v9 = sub_F0E70(&System_IO_FileNotFoundException_TypeInfo);
v10 = sub_F1070(v9);
System_IO_FileNotFoundException___ctor(v10, 0);
v11 = sub_F0E70(&Method_PkMain_OpenInternal__);
sub_F1080(v10, v11);
}
v5 = sub_BD210(System_IO_FileStream_TypeInfo);
v6 = 0x100000;
if ( v4 <= 0xA00 )
v6 = 0x1000;
System_IO_FileStream___ctor_276732752(v5, fpath, 3, 1, 5, v6, 0, 0x10000000, 0);
return v5;
}
C
void PRead___ctor(PRead_o *this, System_IO_Stream_o *fs, System_String_o *fn, const MethodInfo *method)
{
bool v4; // zf
int v5; // eax
unsigned int v6; // edx
int v7; // ecx
System_String_o *v8; // eax
System_Collections_Generic_Dictionary_string__PRead_fe__o *ti; // eax
if ( !byte_A2B3B5 )
{
sub_B6D00(&Method_System_Collections_Generic_Dictionary_string__PRead_fe__Remove__);
sub_B6D00(&str_adult_dat); // adult.dat
sub_B6D00(&StringLiteral_795); // def/version.txt
byte_A2B3B5 = 1;
}
v4 = dword_A27CC4 == 0;
this->fields.fs = fs;
if ( !v4 )
{
do
LOBYTE(v5) = sub_D61C0();
while ( v5 == 0xFF );
dword_A4FE90[v6 >> 5] |= 1 << (v6 & 0x1F);
byte_A27898 = 0;
}
PRead__Init(this, 0);
if ( !fn )
goto LABEL_12;
v8 = System_String__ToLower(fn, 0);
if ( !v8 )
goto LABEL_12;
if ( !System_String__EndsWith_274357920(v8, str_adult_dat, 0, 0) )
return;
ti = this->fields.ti;
if ( !ti )
LABEL_12:
sub_F1090(v7);
System_Collections_Generic_Dictionary_string__PRead_fe___Remove(ti, StringLiteral_795, Method_System_Collections_Generic_Dictionary_string__PRead_fe__Remove__);// def/version.txt
}
C
void PRead__Init(PRead_o *this, const MethodInfo *method)
{
struct System_IO_Stream_o *fs; // ecx
int *v3; // edi
bool v4; // zf
int v5; // eax
unsigned int v6; // edx
int v7; // eax
System_Byte_array *v8; // ebx
int v9; // edi
int32_t i; // esi
int32_t v11; // eax
System_Byte_array *v12; // esi
uint32_t v13; // eax
System_Byte_array *v14; // edi
uint32_t v15; // eax
int v16; // eax
int32_t v17; // esi
System_String_o *v18; // eax
void (__cdecl **v19)(struct System_Collections_Generic_Dictionary_string__PRead_fe__o *, System_String_o *, uint32_t, uint32_t, uint32_t, int, _DWORD); // ecx
int v20; // edx
void (__cdecl **v21)(int *, _DWORD, _DWORD, _DWORD); // [esp-4h] [ebp-44h]
int32_t startIndex; // [esp+10h] [ebp-30h]
System_Byte_array *value; // [esp+14h] [ebp-2Ch]
int v24; // [esp+18h] [ebp-28h]
System_String_o *v25; // [esp+18h] [ebp-28h]
int L; // [esp+1Ch] [ebp-24h]
int32_t La; // [esp+1Ch] [ebp-24h]
int v28; // [esp+20h] [ebp-20h]
struct System_Collections_Generic_Dictionary_string__PRead_fe__o **p_ti; // [esp+24h] [ebp-1Ch]
uint32_t v30; // [esp+28h] [ebp-18h]
uint32_t v31; // [esp+2Ch] [ebp-14h]
uint32_t v32; // [esp+30h] [ebp-10h]
if ( !byte_A2B3B6 )
{
sub_B6D00(&System_BitConverter_TypeInfo);
sub_B6D00(&byte___TypeInfo);
sub_B6D00(&Method_System_Collections_Generic_Dictionary_string__PRead_fe__Add__);
sub_B6D00(&Method_System_Collections_Generic_Dictionary_string__PRead_fe___ctor__);
sub_B6D00(&System_Collections_Generic_Dictionary_string__PRead_fe__TypeInfo);
byte_A2B3B6 = 1;
}
v3 = sub_BD210(System_Collections_Generic_Dictionary_string__PRead_fe__TypeInfo);
if ( !v3 )
goto LABEL_35;
v21 = **(*(Method_System_Collections_Generic_Dictionary_string__PRead_fe___ctor__ + 0xC) + 0x60);
(*v21)(v3, 0, 0, v21);
v4 = dword_A27CC4 == 0;
p_ti = &this->fields.ti;
this->fields.ti = v3;
if ( !v4 )
{
do
LOBYTE(v5) = sub_D61C0();
while ( v5 == 0xFF );
dword_A4FE90[v6 >> 5] |= 1 << (v6 & 0x1F);
byte_A27898 = 0;
}
fs = this->fields.fs;
if ( !fs )
goto LABEL_35;
(fs->klass->vtable._12_unknown.methodPtr)(fs, 0, 0, fs->klass->vtable._12_unknown.method);
v7 = sub_AE200(byte___TypeInfo, 0x400);
fs = this->fields.fs;
v8 = v7;
if ( !fs )
goto LABEL_35;
(fs->klass->vtable._22_unknown.methodPtr)(fs, v7, 0, 0x400, fs->klass->vtable._22_unknown.method);
v9 = 0;
for ( i = 0x10; i < 0x3FC; i += 4 )
{
if ( (System_BitConverter_TypeInfo->_2.bitflags2 & 4) != 0 && !System_BitConverter_TypeInfo->_2.cctor_finished )
sub_C32F0(System_BitConverter_TypeInfo);
v11 = System_BitConverter__ToInt32(v8, i, 0);
v9 += v11;
}
v28 = v9;
v12 = sub_AE200(byte___TypeInfo, 0x10 * v9);
value = v12;
if ( !v12 )
goto LABEL_35;
fs = this->fields.fs;
if ( !fs )
goto LABEL_35;
(fs->klass->vtable._22_unknown.methodPtr)(fs, v12, 0, v12->max_length, fs->klass->vtable._22_unknown.method);
if ( (System_BitConverter_TypeInfo->_2.bitflags2 & 4) != 0 && !System_BitConverter_TypeInfo->_2.cctor_finished )
sub_C32F0(System_BitConverter_TypeInfo);
v13 = System_BitConverter__ToUInt32(v8, 0xD4, 0);
PRead__dd(v12, 0x10 * v9, v13, 0, 0, 0);
L = System_BitConverter__ToInt32(v12, 0xC, 0) - 0x10 * (v9 + 0x40);
v14 = sub_AE200(byte___TypeInfo, L);
if ( !v14 )
goto LABEL_35;
fs = this->fields.fs;
if ( !fs )
goto LABEL_35;
(fs->klass->vtable._22_unknown.methodPtr)(fs, v14, 0, v14->max_length, fs->klass->vtable._22_unknown.method);
v15 = System_BitConverter__ToUInt32(v8, 0x5C, 0);
PRead__dd(v14, L, v15, 0, 0, 0);
v24 = 0;
La = 0;
if ( v28 > 0 )
{
v16 = 8;
for ( startIndex = 8; ; startIndex += 0x10 )
{
if ( (System_BitConverter_TypeInfo->_2.bitflags2 & 4) != 0 && !System_BitConverter_TypeInfo->_2.cctor_finished )
{
sub_C32F0(System_BitConverter_TypeInfo);
v16 = startIndex;
}
v31 = System_BitConverter__ToUInt32(v12, v16 - 8, 0);
v17 = System_BitConverter__ToInt32(v12, startIndex - 4, 0);
v32 = System_BitConverter__ToUInt32(value, startIndex, 0);
v30 = System_BitConverter__ToUInt32(value, startIndex + 4, 0);
while ( v17 < v14->max_length && sub_3060(v17) )
++v17;
if ( !System_Text_Encoding__get_UTF8(0) )
break;
v18 = sub_315A0(v14, v24, v17 - v24);
if ( !v18 )
break;
v25 = System_String__ToLower(v18, 0);
if ( !*p_ti )
break;
v19 = *(*(*(Method_System_Collections_Generic_Dictionary_string__PRead_fe__Add__ + 0xC) + 0x60) + 0x48);
(*v19)(*p_ti, v25, v30, v31, v32, 2, v19);
v20 = v17 + 1;
v12 = value;
v16 = startIndex + 0x10;
v24 = v20;
if ( ++La >= v28 )
return;
}
LABEL_35:
sub_F1090(fs);
}
}
C
void PRead__dd(System_Byte_array *b, int32_t L, uint32_t k, int64_t ro, int32_t wo, const MethodInfo *method)
{
_DWORD *v6; // edi
unsigned int v7; // ecx
int v8; // esi
int i; // edx
uint32_t v10; // ecx
int32_t v12; // eax
int64_t v13; // kr00_8
int v14; // eax
int *v15; // eax
uint8_t v16; // [esp+Fh] [ebp-11h]
char v17; // [esp+Fh] [ebp-11h]
int32_t v18; // [esp+10h] [ebp-10h]
char v19; // [esp+1Ch] [ebp-4h]
if ( !byte_A2B3B7 )
{
sub_B6D00(&byte___TypeInfo);
sub_B6D00(&Method_PRead_dd__);
byte_A2B3B7 = 1;
}
v6 = sub_AE200(byte___TypeInfo, 0x100);
v7 = 0x1CDF * k + 0xA74C;
v8 = v7 ^ (v7 << 0x11);
for ( i = 0; i < 0x100; ++i )
{
v10 = v8 - k + v7;
v8 = v10 + 0x38;
v7 = ((v10 + 0x38) & 0xEF) * v10;
if ( !v6 )
goto LABEL_14;
if ( i >= v6[3] )
goto LABEL_16;
*(v6 + i + 0x10) = v7;
v7 >>= 1;
}
if ( L > 0 )
{
v12 = 0;
v18 = 0;
if ( b )
{
while ( wo < b->max_length )
{
v13 = ro + v12;
if ( v13 % 0xFD > 0x7FFFFFFF || (v16 = b->m_Items[wo], v17 = sub_3060(v6, v13 % 0xFD) ^ v16, v13 % 0x3B > 0x7FFFFFFF) )
{
v14 = sub_F10C0();
sub_F1080(v14, Method_PRead_dd__);
}
v19 = (v17 + sub_3060(v6, v13 % 0x3B)) ^ 0x99;
sub_1BD30(b, wo++, v19);
v12 = v18 + 1;
v18 = v12;
if ( v12 >= L )
return;
}
LABEL_16:
v15 = sub_F11D0();
sub_F1080(v15, 0);
}
LABEL_14:
sub_F1090(v7);
}
}

