Things you will need.
1. rooted android phone.
2. install python on your pc and then install Frida-tools (pip install frida-tools)
3. Frida server on your android phone.
4. Ida pro or ghidra to find the functions you want.
Step1.
load your unity games libil2cpp.so file into your disassembler of choice and then apply names to the functions using Il2CppDumper.
https://github.com/Perfare/Il2CppDumper
just load it into ida or ghidra and after it is finished analyzing it run the script that Il2CppDumper gave you.
Step2.
Find the function that is doing the decryption.
This can be found in unity games by looking at the LoadAssetBundle functions.
Lets look at this example.
com.tencent.hlfish
https://anonfiles.com/r3Ebcf35p5/com.te ... 711f9d_apk
So if I search for LoadAssetBundleSync in the function window I get 1 result
and if i look at the pseudo code generated i see this function.
HLArcade_HLUtils__CreateABFromEncryptFile
Now we go into this function we will notice 2 important things.
The first thing is the load from memory function.
UnityEngine_AssetBundle__LoadFromMemory_20449300
All unity games decrypt their files into memory then load them from memory so we can see its taking in v7.
And if we go up a little bit we see v7 being created from HLArcade_HLFileCryptNew__DecryptFileNew
So lets hop into this function.
looks like its taking a string (file path) and then decrypting it for us so lets see what it does when it runs.
you can see in the bottom left the function offset 00683014
so we are going to use Frida to see what its doing
Step3.
Install the apk onto your phone.
now use adb to setup frida server (Magisk can do this part as a plugin for you if you have it installed).
https://github.com/frida/frida/releases frida-server-14.2.2-android-arm64.xz (extract this with 7z)
adb push c:\frida-server-14.2.2-android-arm64 /data/local/tmp/frida-server
now do adb shell
then type su to change to root mode
now change to the directory /data/local/tmp
cd /data/local/tmp
and make Frida server executable
chmod 777 ./frida-server
now run Frida server
./frida-server
Step4
test and make sure you can see your device in Frida
(Frida installs itself into the scripts directory in your python install)
frida-ps.exe -U
and you should see the running programs on your phone.
Step5
lets launch or game and attach to it with Frida.
frida.exe -U -f com.tencent.hlfish
and it should show a black screen on your phone and 'your command prompt should look like this.
if you typed %resume the game would start up like normal with Frida attached but we want o write our monitor code first.
Code: Select all
var moduleName = "libil2cpp.so";
var nativeFuncAddr = 0x00683014; //
Interceptor.attach(Module.findExportByName(null, "dlopen"), {
onEnter: function(args) {
this.lib = Memory.readUtf8String(args[0]);
console.log("dlopen called with: " + this.lib);
},
onLeave: function(retval) {
if (this.lib.endsWith(moduleName)) {
console.log("ret: " + retval);
var baseAddr = Module.findBaseAddress(moduleName);
Interceptor.attach(baseAddr.add(nativeFuncAddr), {
onEnter: function(args) {
console.log("[-] hook invoked");
//var keyDump = Memory.readByteArray(args[0], 0x300);
//var keyDump2 = Memory.readByteArray(args[1], 0x300);
//console.log(hexdump(keyDump));
//console.log(hexdump(keyDump2));
console.log(JSON.stringify({
a0: args[0],
a1: args[1],
a2: args[2]
}, null, '\t'));
},
onLeave: function (retval) {
console.log(retval);
//var keyDump2 = Memory.readByteArray(retval, 0x300);
//console.log(hexdump(keyDump2));
}
});
}
}
});
if you paste this in it should look like this
if you see any red text you did something wrong and check your commands again.
This code is hooking when libil2cpp.so is loaded then hooking the function at address 0x00683014 in that .so and printing its arguments and return value.
so lets type %resume and see if we get a hit.
We found the module loading.
and we hooked the function.
so the parameters passed to the function are.
"a0": "0x0",
"a1": "0xb1a845f0",
"a2": "0xc62c3b84"
so the first parameter is 0
the 2nd is a pointer to the file name
and the 3rd is a pointer to the file size that this function writes there.
and the return value is a pointer to the decrypted asset bundle in memory.
we can look at these using this command
Memory.readByteArray(new NativePointer(0xb1a845f0),0x100);
hey look the first file it decrypts
and if we look at the next part
its just all 0's where it wants to write the size to.
and if we look at the return address.
bingo a unity header
so now we just need a function to create our string for us for the file we want to decrypt.
if we search for CreateString it comes right up.
System_String_o *__cdecl System_String__CreateString(System_String_o *this, int8_t *value, const MethodInfo *method)
so lets try creating a string.
You will get an error if you do this outside the hook code but it will still work
Code: Select all
var moduleName = "libil2cpp.so";
var moduleBase = Module.findBaseAddress(moduleName);
var careate_str = ptr(parseInt(moduleBase) + 0x00FCA590 )
var careate_str_f = new NativeFunction(careate_str, 'pointer' , [ 'pointer' , 'pointer' ]);
var root = '/storage/emulated/0/Android/data/com.tencent.hlfish/files/AssetBundles/'
var path = 'kernel'
var path_str_utf8 = Memory.allocUtf8String(root + path)
var path_str = careate_str_f(new NativePointer(path_str_utf8), path_str_utf8);
Memory.readByteArray(new NativePointer(path_str), 0x100);
so lets try decrypting that file.
So restart the game with Frida and just type %resume till you are at the logon page.
now lets decrypt this file
we see the file is 0x14F2AC in size so les dump it
Code: Select all
//Construction parameter 2 path parameter
var moduleName = "libil2cpp.so";
var moduleBase = Module.findBaseAddress(moduleName);
var careate_str = ptr(parseInt(moduleBase) + 0x00FCA590 )
var careate_str_f = new NativeFunction(careate_str, 'pointer' , [ 'pointer' , 'pointer' ]);
var root = '/storage/emulated/0/Android/data/com.tencent.hlfish/files/AssetBundles/'
var path = 'kernel'
var path_str_utf8 = Memory.allocUtf8String(root + path)
var path_str = careate_str_f(new NativePointer(path_str_utf8), path_str_utf8);
Memory.readByteArray(new NativePointer(path_str), 0x100);
//Construction parameter 3
var arg3_ptr = Memory.alloc( 0x4 )
Memory.writeInt(arg3_ptr, 0x0 )
//decrypt
var decrypt = parseInt(moduleBase) + 0x00683014;
var decrypt_f = new NativeFunction(ptr(decrypt), 'pointer', ['int','pointer', 'pointer']);
var retval = decrypt_f(0,path_str, arg3_ptr)
var file_bgein = retval.add(0x10)
//dump
var dump = Memory.readByteArray(new NativePointer(file_bgein), 0x14F2AC);
var file = new File("/storage/emulated/0/Android/data/com.tencent.hlfish/files/AssetBundles/kernel.unity3d","w");
file.write(dump);
file.close();
now lets grab our dumped file
now I have all the games logic lua files.