归龙潮程序逆向笔记 (停更)

Unity游戏啊,先分析一下文件,Unity2021.3,AB包没加密,Lua看着像异或加密,还有HybridCLR的dll看不出特征

看到了libNetHTProtect.solibmsaoaidsec.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

其他的不敢写了…溜了溜了

posted @ 2024-11-05 20:36  星如雨yu  阅读(40)  评论(0编辑  收藏  举报