归龙潮程序逆向笔记 (停更)
Unity游戏啊,先分析一下文件,Unity2021.3,AB包没加密,Lua看着像异或加密,还有HybridCLR的dll看不出特征
看到了libNetHTProtect.so
和libmsaoaidsec.so
两位老朋友,易盾众所周知可以上frida一把梭!
果不其然一开frida就闪退,看闪退的时机大概率在il2cpp前就已经检测了…
搜一下msaoaidsec,果然在java层有一个initDeviceFinger
,干掉后就能愉快的上frida了~
直接dump内存得到解密后的il2cpp和global-metadata,还原符号,但是导出表怎么没了.jpg 反正不影响分析,可以用特征再找出来
这Lua是自己手搓了一个包?和当年的ylq一样自制了一个Bundle,好在解开了
调用链: GameEntry.Awake -> DeepClient.Engine.InitFPArchive -> ... -> FPArchive.LuaArchiveFile.ReadArchiveFile
这是…趣加(FunPlus)??原来是趣加子工作室啊
opcode被混淆了,搜索"C stack overflow"定位到0x32A138,根据lua源码判断0x33E6CC即为LuaV_execute,使用Ghidra成功分析出case分支(ida竟然直接无视了case…离谱),根据这篇文章的参考写出魔改版的unluac
更简便且可行的方法是看雪大佬的Opcode自吐,分析C#层可知程序会加载PrismLua/目录下的lua脚本,满足注入自定义脚本的需求(不用手动call DoString,好耶!)
看了眼HybridCLR的介绍,大概是将.Net DLL和il2cpp混合使用实现C#热更,先贴一段尝试dump Dll的代码,竟然成功了〣( ºΔº )〣,直接把C#代码还原了:
// 2024.11.03 归龙潮DLL dump
var basePath = '/sdcard/Android/data/com.tong.lcgame/' //files/Patch/Lua/'
var is_load=false;
//var sleep = new NativeFunction(Module.findExportByName('libc.so', 'sleep'), 'int', ['int']);
var WriteFile = function(fileName, data){
// 使用frida的自带api
// 输入文件地址和Array
var file = new File(fileName, 'w');
file.write(data);
file.close();
}
function buf2hex(arrayBuffer) {
return Array.prototype.map.call(new Uint8Array(arrayBuffer), x => ('00' + x.toString(16)).slice(-2)).join('');
}
var readSystemByteArray = function (systemByteArrayPtr) {
// 读取max_length字段
var maxLengthPtr = systemByteArrayPtr.add(0x18); // 偏移量为0x18
var maxLength = maxLengthPtr.readU64();
console.log("Array Length: " + maxLength);
// 读取m_Items字段, 即可拿到数据
var itemsPtr = systemByteArrayPtr.add(0x20); // 偏移量为0x20
var data = itemsPtr.readByteArray(maxLength);
return data;
};
var hook = function() {
// System_Byte_array *__fastcall HybridLoad_HybridLoadMgr_DecodeBytes(this, bytes, encryptKey, method)
//
// 这就....AES解密了?
var str_name_so = "libil2cpp.so"; //要hook的so名
let n_addr_func_offset = false; //要hook的函数在函数里面的偏移
//加载到内存后 函数地址 = so地址 + 函数偏移
let n_addr_so = Module.findBaseAddress(str_name_so);
let n_addr_func = parseInt(n_addr_so, 16) + n_addr_func_offset;
var ptr_func = new NativePointer(n_addr_func);
console.log('Base: ', ptr_func);
var add_method = new NativeFunction(ptr_func, 'pointer', ['pointer', 'pointer']);
if (ptr_func.toString() == '0x0') {
var exit_func = new NativeFunction(Module.findExportByName("libc.so", "exit"), 'int', ['int']);
// 没hook住, 重启程序
exit_func(0);
}
// il2cpp_string_new
// 没导出 自己找 找一个2021的il2cpp查交叉引用
var n_addr_func_offset_2 = false;
var n_addr_func_2 = parseInt(n_addr_so, 16) + n_addr_func_offset_2;
var il2cpp_new_string = new NativeFunction(new NativePointer(n_addr_func_2), 'pointer', ['pointer']);
var StringChars = function(arg) {
return Memory.readUtf16String(arg.add(20));
};
var new_string = function(arg) {
let cString = Memory.allocUtf8String(arg);
return il2cpp_new_string(cString);
};
var do_write = function(msg, retval) {
var data = readSystemByteArray(retval);
WriteFile(basePath+msg.replaceAll('/','+')+'.dll', data);
console.log(msg+' OK!')
}
Interceptor.attach(ptr_func, {
onEnter: function(args) {
console.log("hook on enter");
is_load = args[1].toString();
},
onLeave: function(retval) {
if (is_load != false) {
var msg=is_load;
is_load = false;
do_write(msg, retval);
}
//console.log("hook on Leave");
}
});
}
setTimeout(hook, 1000); // waiting for load
在代码里可以找到AES密钥,然后还原其他模块dll
其他的不敢写了…溜了溜了
--------------
你已经看完这篇博文了!
本文来自博客园,作者:星如雨yu,转载请注明原文链接:https://www.cnblogs.com/tianpanyu/p/18528750
另,建议转载手动看一眼,把代码块转过去呗(超小声嘀咕)