0x00 前言
[+]本文作者:Floeice
碧蓝航线近期终于用上了il2cpp
,原来通过修改Assembly-CSharp.dll
来实现读取自定义scripts文件的方式不好使了,但是该改的还是能改啊...下文中将简述两种绕过scripts文件检测的方式。出于个人喜好,我会把hook的方式尽可能写的详细一些,也算是对自己近些日子摸鱼的总结吧..
0x01 分析老版本的Assembly-CSharp.dll
定位到LuaScriptMgr.Load(Action):

重点看红框和橙框中的代码,可以给出两个思路:
一是通过hook WWWLoader.Inst.LoadFile()
函数,来读取任意指定的luaName,达到自定义读取scripts文件的目的,同时规避后续的文件Hash检测;

二是通过修改条件判断,确保text.Equals(this.luaHash)
始终为true
,或者直接修改程序跳转,都可以达到相同的目的,是比较简单的一种做法,直接在libil2cpp.so中修改字节码即可
0x02 方案一:修改字节码
用ida打开libil2cpp.so,定位到0xA243A0
,_Load_c__AnonStorey1$$__m__0
,Nop
掉以下三处跳转即可:



0x03 方案二:注入lib并hook string
使用aapt dump badging
命令查看碧蓝航线的launchable-activity
:

打开之,找到onCreate()
,在下方插入loadLibrary
的代码,确保用于注入的lib第一时间加载:


接下来就要开始写hook string的代码了,在这之前有些知识点是必须知道的,说多了都是泪..:
1.c#中字符编码是unicode,使用il2cpp生成的游戏当然也一样,但linux下默认的字符编码是utf-8;
2.在windows下进行unicode编码操作,一般都会推荐使用wchar_t,但这玩意在windows下长度是为2字节,在linux下长度为4字节。
牢记上述之后,在dump.cs中找找关于String的定义吧:

对着抄,写一个String结构体:
typedef struct String
{
void* Empty;
void* WhiteChars;
int32_t length;
char start_char[1];
} String;
为了验证这个结构体是否正确,可以选择将内存中的length和start_char打印出来作进一步分析:
void showMemoryHex(void* ptr, int size) {
unsigned char* bytes = (unsigned char*)ptr;
for (int i = 0; i < size; i++) {
LOGI(" %02x", bytes[i]);
}
}
int new_WWWLoader__LoadFile(int a1, String * path ,void * onLoaded)
{
int32_t i = path->length;
LOGI("length is : %d", i);
showMemoryHex((void *)path->start_char, i * 2);
return old_WWWLoader__LoadFile(a1, path, onLoaded);
}
运行游戏,查看log输出:

由于安卓手机内存中存储的方式为小端模式,所以实际字符串为:
U0073,U0063,U0072,U0069,U0070,U0074,U0073,U0033,U0032(Unicode)
等同于scripts32,长度为9
上述证明了我们的String结构体是正确的,接下来就很明朗了:修改start_char即可,可以使用memcpy(不要使用strcpy,会被截断)来达成我们的目的。因为path中仅包含字母、数字和一些基本符号( U0000~U007F ),在写转换函数的时候只需针对高位的0作处理,当然这肯定是一种偷懒的做法,不要学我
unsigned char *memoryHexToUtf8(void *ptr, int size) {
int i, j;
unsigned char* bytes = (unsigned char*)ptr;
unsigned char *temp = (unsigned char*)malloc(size / 2);
for(i = 0, j = 0; i < size; i++) {
if (bytes[i] != '\0') //U0000~U007F,直接删除高位地址的0,转为单字节UTF8字符(英文字母、数字)
temp[j++] = bytes[i];
}
temp[j] = '\0';
return temp;
}
int Utf8ToUnicode(char* pInput, char* pOutput) {
int outputSize = 0; //记录转换后的Unicode字符串的字节数
while (*pInput) { //处理单字节UTF8字符(英文字母、数字)
if (*pInput > 0x00 && *pInput <= 0x7F) {
*pOutput = *pInput;
pOutput++;
*pOutput = 0; //小端法表示,在高地址填补0
} else { //对于其他字节数的UTF8字符不进行处理
return -1;
}
pInput++; //处理下一个utf8字符
pOutput++;
outputSize += 2;
}
*pOutput = 0;
pOutput++;
*pOutput = 0; //unicode字符串后面,有两个\0
return outputSize;
}
假设我们要指定加载一个名为“floe--ice”
的scripts文件,那么只需要将原有的unicode字符串“scripts32”
替换为“floe--ice”
即可:
int new_WWWLoader__LoadFile(int a1, String * path ,void * onLoaded)
{
int32_t i = path->length;
LOGI("length is : %d", i);
if (strcmp((char *)memoryHexToUtf8((void*)path->start_char, i * 2), "scripts32") == 0) {
LOGI("coincide");
char myPath[9];
strcpy(myPath, "floe--ice");
char *myPathBuf = (char *)malloc(i * 2);
int ret = Utf8ToUnicode(myPath, myPathBuf);
memcpy(path->start_char, myPathBuf, i * 2);
}
LOGI("path is : %s", memoryHexToUtf8((void*)path->start_char, i * 2));
return old_WWWLoader__LoadFile(a1, path, onLoaded);
}
测试的时候,将“floe--ice”
文件置于/files/AssetBundles
下即可
0x04 推荐的lua文件修改工具
Usage: Azurlane.exe <option> <path-to-file(s) or path-to-directory(s)>
>!You can input multiple files or directory, and lua & assetbundle are the only acceptable file!<
Options:
-u, --unlock Decrypt Lua
-l, --lock Encrypt Lua
-d, --decompile Decompile Lua
-r, --recompile Recompile Lua
--decrypt Decrypt AssetBundle
--encrypt Encrypt AssetBundle
--unpack Unpack all lua from AssetBundle
--repack Repack all lua from AssetBundle
Comments | 11 条评论
fbnb,我tm舔爆
@数字

nbnb{滑稽}
#该评论为私密评论#
#该评论为私密评论#
大佬我能否问个比较弱智的问题。。。old_WWWLoader__LoadFile这个函数哪里来的
@卖报纸的
这个其实就是orig_WWWLoader_LoadFile,在il2cpp.so里
#该评论为私密评论#
#该评论为私密评论#
#该评论为私密评论#
大佬 请问现版本改了客户端 SC改法已经失效了 大佬有更好的解决办法吗