[Azurlane]Ver 3.0.12.1

发布于 2019-08-26  8.15k 次阅读


0x00 前言

[+]本文作者:Floeice

碧蓝航线近期终于用上了il2cpp,原来通过修改Assembly-CSharp.dll来实现读取自定义scripts文件的方式不好使了,但是该改的还是能改啊...下文中将简述两种绕过scripts文件检测的方式。出于个人喜好,我会把hook的方式尽可能写的详细一些,也算是对自己近些日子摸鱼的总结吧..

0x01 分析老版本的Assembly-CSharp.dll

定位到LuaScriptMgr.Load(Action):

重点看红框和橙框中的代码,可以给出两个思路:

一是通过hook WWWLoader.Inst.LoadFile()函数,来读取任意指定的luaName,达到自定义读取scripts文件的目的,同时规避后续的文件Hash检测;

WWWLoader.Inst.LoadFile(string,UnityAction)

二是通过修改条件判断,确保text.Equals(this.luaHash)始终为true,或者直接修改程序跳转,都可以达到相同的目的,是比较简单的一种做法,直接在libil2cpp.so中修改字节码即可

0x02 方案一:修改字节码

用ida打开libil2cpp.so,定位到0xA243A0_Load_c__AnonStorey1$$__m__0Nop掉以下三处跳转即可:

0xA244AC
0xA24568
0xA24624

0x03 方案二:注入lib并hook string

使用aapt dump badging命令查看碧蓝航线的launchable-activity

入口为com.manjuu.azurlane.MainActivity

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

onCreate()
loadLibrary("secshell")

接下来就要开始写hook string的代码了,在这之前有些知识点是必须知道的,说多了都是泪..

1.c#中字符编码是unicode,使用il2cpp生成的游戏当然也一样,但linux下默认的字符编码是utf-8;

2.在windows下进行unicode编码操作,一般都会推荐使用wchar_t,但这玩意在windows下长度是为2字节,在linux下长度为4字节。

牢记上述之后,在dump.cs中找找关于String的定义吧:

32位下String的定义

对着抄,写一个String结构体:

typedef struct String
{
    void* Empty;
    void* WhiteChars;
    int32_t length;
    char start_char[1];
} String;

为了验证这个结构体是否正确,可以选择将内存中的lengthstart_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文件修改工具

Azurlane-LuaHelper

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

0x05 成品

blhx_3.0.12.1_floeice.apk

scripts_mod(@平凡,内含使用说明)