关于工具的用法不会讲的太细,如果有什么不懂的可以留言一起探讨
需要准备的工具:
1.IDA
2.dnSpy
3.Il2CppDumper
4.frida
5.一个root的安卓手机
将目标apk修改后缀为rar并解压
image.png (31.99 KB, 下载次数: 0)
下载附件
2022-12-5 17:44 上传
我们随意打开一个资源文件发现它是经过加密的
image.png (18.75 KB, 下载次数: 0)
下载附件
2022-12-5 18:17 上传
image.png (52.48 KB, 下载次数: 0)
下载附件
2022-12-5 18:17 上传
正常的unity资源文件是以Unity开头的
image.png (40.16 KB, 下载次数: 0)
下载附件
2022-12-5 18:18 上传
用Il2CppDumper工具导出dll(你的手机是64位的就选arm64的so,否则选32位的so)
image.png (63.28 KB, 下载次数: 0)
下载附件
2022-12-5 17:53 上传
image.png (43.11 KB, 下载次数: 0)
下载附件
2022-12-5 17:54 上传
导出后会在Il2CppDumper工具的目录下DummyDll文件夹
image.png (54.12 KB, 下载次数: 0)
下载附件
2022-12-5 17:58 上传
接下来使用dnSpy打开Assembly-CSharp.dll
image.png (69.2 KB, 下载次数: 0)
下载附件
2022-12-5 18:00 上传
使用IDA打开前面提到的libil2cpp.so并导入符号表,且等待ida分析完成(这里时间可能需要比较久,分析没完成之前,可能无法按F5转到C源码分析)
image.png (112.66 KB, 下载次数: 0)
下载附件
2022-12-5 18:03 上传
OK! 准备工作已经完成,接下里分析代码
Unity的资源加载主要是依靠Assetbundle
我们在dnspy里正好看到一个
[color=]AssetBundleUtil的类
image.png (34.11 KB, 下载次数: 0)
下载附件
2022-12-5 18:14 上传
这个类里有多个加载函数
image.png (58.45 KB, 下载次数: 0)
下载附件
2022-12-5 18:21 上传
而看起来LoadAsset 和 LoadAssetPrefab 比较像是加载资源的(猜的...)
接下来我们去IDA查看 直接搜索LoadAsset
image.png (31.57 KB, 下载次数: 0)
下载附件
2022-12-5 18:24 上传
通过查看源码发现不管是LoadAsset还是LoadAssetPrefab都是调用的GetOrLoadAssetBundleByAssetPath
LoadAsset代码
image.png (71.83 KB, 下载次数: 0)
下载附件
2022-12-5 18:27 上传
LoadAssetPrefab代码
image.png (24.08 KB, 下载次数: 0)
下载附件
2022-12-5 18:27 上传
image.png (71.69 KB, 下载次数: 0)
下载附件
2022-12-5 18:28 上传
而GetOrLoadAssetBundleByAssetPath最终调用的是AssetBundleUtil__Load
image.png (27.8 KB, 下载次数: 0)
下载附件
2022-12-5 18:32 上传
继续分析AssetBundleUtil__Load函数,经过分析发现Unity自身的API,UnityEngine_AssetBundle__LoadFromMemoryAsync和UnityEngine_AssetBundle__LoadFromMemory这两个函数都是从内存中加载资源,那么说明v25就是解密过的数据,而v25是通过CryptUtil__DecryptToBytes函数返回的!
那么DecryptToBytes就是我们要的解密函数
而GetByteArrayFromFile函数可以让我们获取到资源的路径
image.png (43.37 KB, 下载次数: 0)
下载附件
2022-12-5 19:25 上传
回到dySpy 查看GetByteArrayFromFile 和 DecryptToBytes的函数偏移地址
image.png (9.75 KB, 下载次数: 0)
下载附件
2022-12-5 19:27 上传
image.png (8.83 KB, 下载次数: 0)
下载附件
2022-12-5 19:27 上传
开始写frida代码 decode.js
主要功能就是Hook GetByteArrayFromFile 和 DecryptToBytes函数 通过参数和返回值来获取资源目录以及解密后的bytes并发送给python代码直接写入电脑的文件夹下
需要注意的是,64位手机和32位手机获取字符串和bytes数组的偏移量不一样.如var str = args[0].add(20).readUtf16String(-1) 在32位手机中就不是add(20)了
[JavaScript] 纯文本查看 复制代码"use strict";
Java.perform(function () {
console.log("==============================================");
//这里libil2cpp.so还没有加载完成,所以下个定时器循环获取直到libil2cpp.so加载完成
var i = setInterval(function () {
var addr = Module.findBaseAddress('libil2cpp.so');
if (addr) {
let path;
console.log("libil2cpp 已加载~~~~~~~~~~~~");
//GetByteArrayFromFile
Interceptor.attach(addr.add("0xF9F68C"), {
onEnter(args) {
var str = args[0].add(20).readUtf16String(-1);
if (str) {
str = str.replace("/storage/emulated/0/Android/data/com.xxx.xxx.android/files/Assets/", "Assets2/")
path = "/sdcard/" + str
console.log(path);
}
},
onLeave(retval) {
//console.log(retval);
}
});
let ret;
//static byte[] DecryptToBytes(byte[] bytes)
Interceptor.attach(addr.add("0xF1C244"), {
onEnter(args) {
ret = args[0];
},
onLeave(retval) {
ret = retval
let size = (ret.add(24).readLong());
let data = ret.add(32).readByteArray(size);
send(path, data);
//以下方法可以直接保存在手机的sdcard目录
// let file = new File(path, "wb");
// file.write(data);
// file.flush();
// file.close();
}
});
clearInterval(i);
}
}, 100);
});
main.py[Python] 纯文本查看 复制代码import frida, sysimport struct
import os
def on_message(message, data):
if message['type'] == 'send':
strPath = message['payload']
print(" {0}".format(strPath))
if isinstance(data, bytes):
n = strPath.rindex("/")
dir = "H:" + strPath[0:n]
if not os.path.exists(dir):
print("创建目录:"+dir)
os.makedirs(dir)
fw = open('H:'+ strPath, 'wb')
fw.write(data)
fw.close()
else:
print(message)
device = frida.get_usb_device()
pid = device.spawn(["com.xxx.xxx.android"])//这里是apk的包名
device.resume(pid)
session = device.attach(pid)
# session = device.attach(pid, realm='emulated') #模拟器专用
f = open("decode.js",'r+')
script = session.create_script(f.read())
script.on('message', on_message)
print(' Running CTF')
script.load()
sys.stdin.read()
开启手机的frida-server
image.png (8.75 KB, 下载次数: 0)
下载附件
2022-12-5 19:37 上传
转发端口
image.png (7.18 KB, 下载次数: 0)
下载附件
2022-12-5 19:39 上传
执行main.py
image.png (5.95 KB, 下载次数: 0)
下载附件
2022-12-5 19:44 上传
我们就可以在指定目录下看到解密后的资源了
image.png (14.26 KB, 下载次数: 0)
下载附件
2022-12-5 19:45 上传
将sdcard文件夹拖入AssetStudioGUI就可以得到正确的资源文件啦
image.png (106.28 KB, 下载次数: 0)
下载附件
2022-12-5 19:47 上传
这个方法的缺点是,只能获取游戏当前加载的资源,如果这个资源没有加载过就获取不到
可以通过自己构造路径然后手动调用AssetBundleUtil__Load方法来解决这个问题
注:第一次写文章写的不好的地方请大家见谅,欢迎大家留言探讨~