dnSpy逆向一款不知名黃油的經(jīng)驗(yàn)(二)

緊接上篇文章中介紹的存檔解密過程,這次將講述封呪姫這款游戲的資源解密過程.

在解資源解密前,通常需要確定哪些文件是游戲的主要資源文件,對(duì)于一款黃油來說資源通常包含:游戲文本,游戲貼圖,CG,音頻以及一些游戲內(nèi)部的物品,技能,屬性等.對(duì)于unity開發(fā)的游戲來說資源文件通常都打包在.assets的文件中.當(dāng)然也有些作者會(huì)自己打包.
查看封呪姫這款游戲的目錄,并通過文件以及文件的大小能夠大致判斷出資源文件所在的目錄,對(duì)于游戲來說資源占的容量絕對(duì)是最大的,這沒的爭(zhēng)議,代碼啥的終究只是文本能有多大.

在目錄中src文件夾就是游戲的主要資源文件所存放的位置,


隨便截了兩張src文件夾中文件的信息,什么.dx文件 .dml文件 都是沒見過的文件格式,一般來說這些都是作者自己搞得.用UE隨便打開一個(gè)文件看看:

對(duì)于unity資源,先不管別的直接上AssetStudio,看看資源能不能打開,結(jié)果不出意外是打不開的.

在用AssetStudio,無解的情況下,最終還是要回歸到代碼層面,畢竟游戲的一切終歸是由代碼構(gòu)筑的,總不可能資源自己會(huì)跑會(huì)跳.
老套路dnSpy啟動(dòng),這次是要定位資源的解密代碼.如果作者不耍什么小動(dòng)作,搜索一些關(guān)于資源的關(guān)鍵詞應(yīng)該能夠找到.經(jīng)過一番搜索和對(duì)比之后,大致定位到了GSys這個(gè)類中LoadByte這個(gè)方法.
具體是為什么做出這個(gè)判斷,一是經(jīng)驗(yàn),二是感覺,三是瞎猜,四是它太像了.
其實(shí)主要是通過dnSpy的分析得來的.
在看到LoadByte這個(gè)方法的時(shí)候,為了能夠找到是什么方法調(diào)用了它,就可以用右鍵"分析"這個(gè)功能,此時(shí)方法就會(huì)進(jìn)入到分析器中

在展開后,[被使用]標(biāo)簽就是這個(gè)方法被調(diào)用的位置.
通過圖中的展開,可以看出LoadByte被LoadString調(diào)用,LoadString又被三個(gè)(Load×××)方法調(diào)用這三個(gè)方法所傳入的參數(shù)都是文件名的字符串,顯然是要對(duì)文件下手

于是點(diǎn)開LoadByte這個(gè)方法:

代碼看上去挺多了,其實(shí)主要還是一大堆亂碼搞得,看過上次文章應(yīng)該能夠?qū)@個(gè)代碼有似曾相識(shí)的感覺,前面的代碼就不解釋了.
直接看到:int?encMode?=?GSys.GetEncMode(sFilename);

這里定義了一個(gè) encMode的變量,字面理解是加密模式(當(dāng)然加密模式是啥,一開始我也不知道是啥,不看看GetEncMode這個(gè)方法的具體操作了什么是很難猜的).不過這里可以先不考慮GetEncMode這個(gè)方法到底給encMode這個(gè)變量賦值什么,畢竟它就是個(gè)int,再?gòu)?fù)雜也就是個(gè)數(shù)字.
往下看到這個(gè)if判斷,可以看出就是判斷這個(gè)encMode是否為0,
再看if的內(nèi)容,內(nèi)容里也很簡(jiǎn)單,一個(gè)定義了一個(gè)num2(之前忽略了),補(bǔ)充內(nèi)容
###這里num2有兩種情況:
###1:全長(zhǎng)????????2:長(zhǎng)度為100
###總結(jié)起來是encMode = 0,數(shù)據(jù)沒有經(jīng)過加密,不需要解密過程直接跳過
###encMode != 1, 整個(gè)數(shù)組都經(jīng)過加密(src文件夾中.dxt? .dml? .dsv文件的加密模式)
###encMode =1, 數(shù)組的前100個(gè)元素經(jīng)過加密(src文件夾中.dx文件加密模式)
###總結(jié)一下就是不同資源文件的加密模式存在略微差別,并且游戲兼容解密后資源的直接讀取,所以解密后的文件改一下后綴直接放在src文件夾就能被游戲讀取。其中.dxt??.dml? .dsv? .dx解密后改為.txt? .xml? .csv? .tx當(dāng)然也可以隨便改后綴只要不與原始的4種后綴一樣就行(名字保持一樣只改后綴)。
,然后就是for循環(huán)
看過上期的人應(yīng)該知道這個(gè)for循環(huán)是啥吧,就是本篇文章所要尋找的解密代碼

array[i]?=?(byte)((int)array[i]?^?i?*?num);
這條代碼就是加密的關(guān)鍵,解析一下就是,array這個(gè)數(shù)組的每一個(gè)元素和(i*num)進(jìn)行異或后賦值回去,num這么變量是前面代碼中賦過值的,

看上去一大串其實(shí)就是int num = 28;別問我怎么來的,算出來的
簡(jiǎn)化再把num用28替換掉:
array[i]?=?(byte)((int)array[i]?^?i?*?28);
清晰明了了,接下來就是自己寫一下python代碼實(shí)現(xiàn)這個(gè)功能了.
但是要注意的是這過程中都是字符進(jìn)行運(yùn)算的,如果沒有注意的byte類型大小的話直接算會(huì)溢出的.
前面還提到過加密模式的問題,其實(shí)這個(gè)加密模式就是個(gè)幌子,與其說是加密模式,不如說就是個(gè)判斷資源有沒有加密,沒加密就不同跑下面的解密代碼了.
接下來上一下解密成果,就拿技能的文件展示一下.






這里注意的是文本是經(jīng)過簡(jiǎn)體漢化的.日文原版也是一樣的.

還有一點(diǎn)注意的是一些CG的資源包解密之后,是unity官方的包,需要用AssetBundleExtractor繼續(xù)解包才行,不過這之后的解包直接用工具干就行了,沒什么技術(shù)含量就略去一萬字了.

接下來我介紹游戲"解碼"的兩個(gè)方法(解碼太危險(xiǎn)盡量不上圖了)
代碼層面的"解碼"介紹
資源層面的"解碼"介紹
后續(xù)可能還會(huì)有一些代碼魔改的介紹................