最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

Unity 應(yīng)用解包與資源文件解密

2023-08-16 17:31 作者:花開半夏Nemo  | 我要投稿

本文僅供學(xué)習(xí)參考,請(qǐng)勿將其用于非法用途

前言

近日,“世界大明星”動(dòng)畫的衍生音游“世界大明星 夢(mèng)想的群星(App 名 ユメステ)”上線于 Google Play Store。作為世界計(jì)劃的玩家之一,看到出了一款同樣包含大小鍵的新音游,于是毫不猶豫地下載了下來。

由于最近才完成了 sonolus-hwpl-engine 的重構(gòu),于是我在想能不能將這個(gè)游戲也搬到 Sonolus 上去。

由于是新出的一款音游,在網(wǎng)上找不到任何的資源。沒辦法,我只好參考世界計(jì)劃的解密方法,自行解密其譜面文件來分析。這是筆者第一次進(jìn)行資源文件的解密,走了不少的彎路,所以寫個(gè)文章記錄一下,萬一以后還有要用的呢。

目錄

本文章將分為以下幾個(gè)板塊進(jìn)行記錄:

  • apktool 解包與回編

  • il2cppdumper 逆向

  • frida 初探

  • ida 逆向分析

apktool 解包與回編

由于我們想要獲取到加密后的譜面文件,對(duì)應(yīng)用網(wǎng)絡(luò)資源的訪問是必不可少的,這里我們采用 anyproxy 來進(jìn)行抓包。

抓包有一個(gè)前提,需要將抓包軟件提供的證書安裝在 Android 用戶 CA 證書目錄下。但是由于高版本 Android 的限制,用戶 CA 證書同樣不受信任,只有系統(tǒng) CA 證書才受信任,就要求手機(jī)必須 root。但是如果應(yīng)用主動(dòng)信任用戶 CA 證書,那么該應(yīng)用就能被順利抓包。

apktool 解包

首先把應(yīng)用安裝包下載到電腦上后,利用 apktool 這個(gè)工具,我們就可以將安裝包解開,看到部分的配置文件。

java -jar ./apktool.jar d yumesute.apk

執(zhí)行以上命令后,將在同目錄下新建一個(gè) yumesute?的文件夾,里面就是安裝包內(nèi)的所有內(nèi)容了。

進(jìn)入該文件夾,新增一個(gè) res/xml/network_security_config.xml?文件,內(nèi)容如下:

<?xml version="1.0" encoding="utf-8"?>

<network-security-config>

? ? <base-config cleartextTrafficPermitted="true">

? ? ? ? <trust-anchors>

? ? ? ? ? ? <certificates src="user" />

? ? ? ? ? ? <certificates src="system" />

? ? ? ? </trust-anchors>

? ? </base-config>

</network-security-config>

接下來,打開根目錄下的 AndroidManifest.xml 文件,在 application 元素里加一個(gè) android:networkSecurityConfig="@xml/network_security_config"?屬性,這個(gè)軟件發(fā)出的所有網(wǎng)絡(luò)請(qǐng)求都可以被我們的 anyproxy 給捕捉到了。

為了方便,我們先把應(yīng)用的包名也改一下,避免出現(xiàn)各種沖突問題導(dǎo)致需要卸載原應(yīng)用才能安裝的上去。

同樣在 AndroidManifest.xml?中,搜索所有的 com.kms.worlddaistar(這是原應(yīng)用的包名),然后將除了出現(xiàn)在 activity?元素里的全部改為 com.kms.worlddaistar2(這是新包名),這樣一來這個(gè)應(yīng)用的包名就是 com.kms.worlddaistar2?了。

apktool 回編與應(yīng)用安裝

退出 yumesute 目錄,使用如下指令編譯新應(yīng)用安裝包:

java -jar ./apktool.jar b yumesute -o yumesute.apk

要想安裝在實(shí)機(jī)上還需要對(duì)安裝包進(jìn)行簽名才能安裝:

keytool -genkey -v -keystore abc.keystore -alias abc.keystore -keyalg RSA -validity 365

jarsigner -verbose -keystore abc.keystore -signedjar yumesute_signed.apk yumesute.apk abc.keystore

生成的 yumesute_signed.apk 通過 adb?安裝到手機(jī)上就行了。

如果不換包名的話,會(huì)導(dǎo)致我們的簽名與原應(yīng)用的簽名不同,從而導(dǎo)致無法安裝。所以在上一步我們才需要換掉包名,目的就是為了避免簽名沖突。

接下來,只需要安裝 anyproxy 的證書到用戶 CA 證書目錄中,啟動(dòng) anyproxy 后,設(shè)置好網(wǎng)絡(luò)代理,再打開我們才安裝好的新應(yīng)用。進(jìn)入 anyproxy 的管理頁面,我們就會(huì)發(fā)現(xiàn),所有的網(wǎng)絡(luò)請(qǐng)求信息都被我們的 anyproxy 捕捉到了,至此這一步就完成了。

(好像 anyproxy 給的鏈接有點(diǎn)問題,使用腳本修正一下就好了。)

anyproxy 管理界面

il2cppdumper 逆向

注意到有兩個(gè)鏈接(上面的圖上沒有):

https://assets.wds-stellarium.com/production/Notations/4/music_config.enc

https://assets.wds-stellarium.com/production/Notations/4/5.enc

一個(gè)是音樂配置文件的下載鏈接,另一個(gè)是譜面文件的下載鏈接。

下載下來后你會(huì)發(fā)現(xiàn)全都是些亂碼,而且后綴名還是 .enc,肯定是加了密的。要想知道怎么解密,肯定需要知道源代碼才行。

在此,我們使用 il2cppdumper 來逆向,將程序使用的函數(shù)全部挖出來,再使用 dnSpy 來查找他可能用到的函數(shù)。

于是,打開 dnSpy,你驚奇地發(fā)現(xiàn):

dnSpy 界面

好家伙,這么多程序集,程序集下面又有一堆名字空間,名字空間下面又有一堆類,類下面又有一堆方法,這無疑是大海撈針啊。

找了半天,發(fā)現(xiàn)了下面這個(gè)東西:

dnSpy 界面

哦原來用的是 AES 來加密嗎,參考世界計(jì)劃對(duì)譜面的處理,好像是這么一回事,那接下來只需要獲取到 AES 的密鑰和 iv 不就可以解出來了嗎。

frida 初探

程序在 CreateWithInitialize 這個(gè)方法里面?zhèn)魅肓思用芩枰?iv 和 password,因此我們只需要獲取到這個(gè)函數(shù)的參數(shù)就行了。

這個(gè)時(shí)候就要請(qǐng)上我們 Hook 的工具 frida 了。

frida 安裝

官方建議是把 frida-server?傳到手機(jī)上啟動(dòng),但是很不幸的是,沒有 root 權(quán)限,意味著沒法改變可執(zhí)行文件的運(yùn)行權(quán)限,這條路算是徹底寄了。

但是你會(huì)發(fā)現(xiàn)還有個(gè)東西 frida-gadget,這東西只要包到應(yīng)用里就可以了,根本不需要 root。正巧的是,在第一個(gè)板塊,我們就使用了 apktool?來拆包與回編。

于是下載 frida-gadget.so?后,重命名為 libfrida-gadget.so,扔到 yumesute/lib/arm64-v8a/?下面去。

要想程序能夠調(diào)用這個(gè)庫(kù)文件,我們需要修改一下應(yīng)用,打開 AndroidManifest.xml,我們不是還有個(gè)沒改的 com.kms.worlddaistar.UnityPlayerActivityOverride?嗎,說明這就是程序的入口文件。

打開 yumesute/smali/com/kms/worlddaistar/UnityPlayerActivityOverride.smali,在構(gòu)造函數(shù) .method public constructor <init>()V?中新增以下內(nèi)容:

const-string v0, "frida-gadget"

invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

為了能夠盡早調(diào)用 frida,我們才需要將這句加在構(gòu)造函數(shù)中。

而且這里有很多的坑點(diǎn),如果你把這句話放在正常函數(shù)中,v0?后面的那個(gè)數(shù)字不能超過 15,否則即使能夠回編成功,應(yīng)用也無法啟動(dòng);而如果放在構(gòu)造函數(shù)中,這個(gè)數(shù)字甚至不能超過 1,否則也不能正常啟動(dòng)。

按照第一個(gè)板塊中的方法回編并安裝后,打開應(yīng)用,只要應(yīng)用能夠暫停在黑色界面不閃退,那么說明 frida 就注入成功了。

接下來的就是在電腦端上安裝 frida?和 frida-tools?就行了。

python -m pip install frida frida-tools

frida 使用

frida 其實(shí)挺好用的,網(wǎng)上的教程一抓一大把,我就說一些需要注意的點(diǎn):

  1. 由于我們使用的是 frida-gadget,官網(wǎng)也說了,包名并不是我們打包的那個(gè)包名,而是固定的 Gadget,因此我們需要使用 session = frida.get_usb_device().attach("Gadget")?才能正常連接到應(yīng)用。

  2. 網(wǎng)上有很多關(guān)于函數(shù)在內(nèi)存中的地址的爭(zhēng)論,有說直接 Module.findBaseAddress("libil2cpp.so").add(address)?的,還有需要再加 1?的,甚至還有有理有據(jù)的分析說什么應(yīng)該要再加 1。但實(shí)際上這個(gè)跟設(shè)備的架構(gòu)相關(guān),arm?架構(gòu)的設(shè)備不需要加 1?就能獲取到真實(shí)地址。

frida-il2cpp-bridge

經(jīng)歷了六七個(gè)小時(shí)的 hook 后,我放棄了。由于 Unity 中 Byte 結(jié)構(gòu)的特殊性,我獲取到的參數(shù)地址總是會(huì)與真實(shí)的地址出現(xiàn)偏差。

那就沒辦法了嗎?這時(shí)我無意中發(fā)現(xiàn)了一個(gè)工具 frida-il2cpp-bridge,它可以自動(dòng)解析出 Unity 中的 Byte 結(jié)構(gòu)。

我心想,我不就是我需要的東西嗎?于是開始光速開始配置 frida-il2cpp-bridge?的環(huán)境。

新建一個(gè) package.json 文件,寫入以下內(nèi)容

{

? ? "name": "playground",

? ? "main": "index.ts",

? ? "version": "1.0.0",

? ? "private": true,

? ? "type": "module",

? ? "scripts": {

? ? ? ? "build": "frida-compile -o _.js index.ts",

? ? ? ? "attach": "frida -U \"Gadget\" -l _.js --runtime=v8",

? ? ? ? "spawn": "frida -U -f \"Gadget\" -l _.js --no-pause --runtime=v8"

? ? },

? ? "devDependencies": {

? ? ? ? "@types/frida-gum": "^18.3.1",

? ? ? ? "frida-compile": "^16.2.2",

? ? ? ? "frida-il2cpp-bridge": "*"

? ? }

}

新建 tsconfig.json 文件,寫入以下內(nèi)容

{

? ? "compilerOptions": {

? ? ? ? "target": "esnext",

? ? ? ? "lib": [ "es2022" ],

? ? ? ? "experimentalDecorators": true,

? ? ? ? "module": "esnext",

? ? ? ? "allowJs": false,

? ? ? ? "noEmit": false,

? ? ? ? "esModuleInterop": false,

? ? ? ? "moduleResolution": "nodenext",

? ? ? ? "strict": true,

? ? ? ? "sourceMap": true

? ? },

? ? "files": [ "index.ts" ]

}

使用以下指令開始配置 frida-il2cpp-bridge?的環(huán)境:

npm install

環(huán)境安裝好后,新建 index.ts?文件,寫入以下內(nèi)容:

import "frida-il2cpp-bridge";


Il2Cpp.perform(() => {

? ? const e = Il2Cpp.domain.assembly("Sirius.Security").image.class("Plugins.Sirius.Security.Cryptography.CustomAesEncoder");

? ? Il2Cpp.trace(true).classes(e).and().attach();

});

手機(jī)先打開應(yīng)用,然后連接電腦,輸入以下指令開始 Hook

npm run build & npm run attach

這時(shí)候,應(yīng)用成功啟動(dòng),隨便點(diǎn)擊一個(gè)譜面來玩。在加載譜面的時(shí)候,AES 加密的 password 和 iv 就打印在我們控制臺(tái)上了。

raw data

興沖沖地拿著 password?和 iv?去控制臺(tái)里嘗試,但是報(bào)錯(cuò) bad magic number。

我不知道是我電腦的問題還是什么,反正我就是解不了。

沒辦法,再挖一下,發(fā)現(xiàn) Plugins.Sirius.Security.Cryptography.CustomAesEncoder?還調(diào)用了個(gè) System.Security.Cryptography。

經(jīng)過一段時(shí)間的尋找,又發(fā)現(xiàn)了這個(gè)類:

dnSpy 界面

里面的 createDecryptor?函數(shù)不是正好就給了我 key 和 iv 嗎。于是修改 index.ts,追蹤一下這個(gè)類:

import "frida-il2cpp-bridge";


Il2Cpp.perform(() => {

? ? const e = Il2Cpp.domain.assembly("System.Core").image.class("System.Security.Cryptography.AesCryptoServiceProvider");

? ? Il2Cpp.trace(true).classes(e).and().attach();

});

再次打開游戲,加載譜面的時(shí)候,這次不僅將 key 和 iv 吐出來了,還告訴我們使用的 AES-256-CBC?進(jìn)行加密的,這一點(diǎn)和世界計(jì)劃是不一樣的。

raw data2

這次再拿著 key 和 iv 去控制臺(tái)里嘗試,成功解密。

但是如果你多開幾個(gè)譜面,你會(huì)發(fā)現(xiàn),music_config.enc?的 key 和 iv 與 5.enc 都不同,而不同歌曲間的 key 雖然相同,但 iv 也不同。仔細(xì)觀察,你會(huì)發(fā)現(xiàn),其實(shí) iv 就是 *.enc 文件的前 16 位。

還有個(gè)問題,解出來的文件與你控制臺(tái)里打出來的對(duì)比一下,你會(huì)發(fā)現(xiàn)你解出來的會(huì)多了前 16 位。顯然這是沒有意義的,在程序里甚至是直接舍掉了。因此這前 16 位我們直接舍掉就好了。

至此,所有解包解密過程結(jié)束。本文完。

ida 逆向分析

你不會(huì)真的以為就完了吧。

music_config.enc?解出來是沒有問題的,但是 5.enc?解出來還是個(gè)二進(jìn)制文件,看來還用了其他的加密手段。沒辦法,只有看看用 ida 來看看了。

用 ida 打開 libil2cpp.so,使用 il2cppdumper?里的 ida_py3.py?將函數(shù)導(dǎo)入進(jìn)去。

先利用 frida 找到函數(shù) Plugins.Sirius.Security.Cryptography.CustomAesEncoder.Decrypt?的調(diào)用棧。新建 script.py,寫入如下內(nèi)容:

import frida

import sys


rdev = frida.get_usb_device()

session = rdev.attach("Gadget")


def on_message(message, data):

? ? if message['type'] == 'send':

? ? ? ? print("[*] {0}".format(message['payload']))

? ? else:

? ? ? ? print(message)


scr = """

Java.perform(function(){

? ? // Hook Constructor

? ? var initPointer = Module.findBaseAddress("libil2cpp.so").add(0x5F4A074); // 函數(shù)地址,在 dnSpy 里面去看

? ? console.log("initPointer: " + initPointer);

? ? Interceptor.attach(initPointer, {

? ? ? ? onEnter: function(args) {

? ? ? ? ? ? console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n"));

? ? ? ? ? ? console.log(Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join("\\n"));

? ? ? ? },

? ? });

});

"""


script = session.create_script(scr)

script.on('message', on_message)

print('[*] Running CTF')

script.load()

sys.stdin.read()

手機(jī)打開應(yīng)用,連接電腦,python script.py?就可以啟動(dòng)了。

同樣在打開譜面的時(shí)候,會(huì)輸出當(dāng)前的調(diào)用棧中所有函數(shù)的地址值。

已知當(dāng)前函數(shù)的地址為 0x5F4A074,那調(diào)用這個(gè)函數(shù)的地址就理應(yīng)是下一行的 0x445DEAC。

在 ida 中按下 G 鍵,輸入 0x445DEAC?后就會(huì)跳轉(zhuǎn)到一個(gè)地方。

ida 界面

看不懂?直接按下 F5 反編譯,你會(huì)發(fā)現(xiàn)給了我們一連串的 C 代碼。

ida 界面

說實(shí)話我其實(shí)不怎么能看得懂,畢竟 v1, v2, v3?這樣的神仙命名看得懂就有鬼了。

但是你會(huì)發(fā)現(xiàn),我們目前的光標(biāo)位置是在調(diào)用 Sirius_Compress_BrotliCompressHelper__DecompressToString?這個(gè)函數(shù),而上面一行就是我們剛剛的 Plugins_Sirius_Security_Cryptography_CustomAesEncoder__Decrypt?函數(shù)。

那么結(jié)果就出來了,應(yīng)用先通過 AES 將譜面文件解出來后,再利用這個(gè)叫 brotli?的方法來解壓縮的。

于是網(wǎng)上隨便搜了個(gè)解 brotli?的工具,一解,你驚奇地發(fā)現(xiàn),出來的正是我們想要的文本文件了。

至此,所有解包解密過程正式宣告結(jié)束。本文完。

Unity 應(yīng)用解包與資源文件解密的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
涞源县| 鱼台县| 土默特右旗| 高清| 巴青县| 伽师县| 临清市| 车险| 鄂州市| 西林县| 东至县| 江油市| 双鸭山市| 天气| 孟州市| 普洱| 余姚市| 姚安县| 于都县| 顺义区| 灵寿县| 简阳市| 大丰市| 宾阳县| 大兴区| 岗巴县| 乌拉特后旗| 临夏市| 洛隆县| 神农架林区| 沈阳市| 温州市| 呼和浩特市| 陆河县| 光泽县| 瑞金市| 怀柔区| 西华县| 崇义县| 九龙城区| 海丰县|