書接上文,如果我們沒有找到原版的Decrypt.js該如何進行分析
前置要求
按照思路一,分析到遊戲讀寫文件的函數被DecrypterPlayer.js
重寫了
思路
之前有提到能用瀏覽器運行遊戲會給我們逆向遊戲帶來巨大的便利性,那我們該如何使用呢?假設作者沒有寫註釋,我們沒有辦法根據註釋查到最原始版本的加密腳本,我們又該如何分析
首先我們用Edge打開遊戲(準備篇有講如何用Edge打開遊戲)
F12打開開發者模式,在資源中找到DecrypterPlayer.js
,並選擇優質打印(就是格式化代碼)圖中紅圈
找到我們分析出來的進行解密的地方,設置斷點
刷新頁面,當斷點被觸發,選擇單步步入
步入兩次之後,我們發現代碼執行到了YEP_KeyCore.js
也就是我們上一篇通過各種分析得到的祕鑰文件
選擇優質打印,可以看到,這與我們上篇的分析結果完全符合
使用Debug的方式也可以找到祕鑰文件,但我們不關心解密函數具體如何實現,我們只需要在上層調用
我們使用編輯器打開DecrypterPlayer.js
文件,並進行格式化,添加一行控制行輸出代碼
取消斷點,刷新頁面,切換到控制檯頁,可以看到文件確實明文輸出了
我們當然可以在解密函數這裏插入代碼,直接將解密文件輸出,不過注意要依賴node.js
那可能會有人有疑問,加密腳本是如何運行的呢?加密腳本要把源文件加密,輸出,並且刪除源文件。這是因爲RPG Maker MV使用了NW.js
搜索之
NW.js由node-webkit項目發展而來 , 通過使用頁面相關技術開發桌面應用 , 同時能夠使用DOM調用Node.js的所有模塊 .
簡而言之,如果你使用RPG Maker MV來啓動遊戲,那麼遊戲是可以調用讀寫文件庫的
好的,那麼至此,一次完整的RPG Maker MV遊戲逆向過程就分享完畢了,感謝閱讀
一些擴展
- 加密後的判斷語句如何理解
if (xhr.status < 400) {
window[name] = JSON.parse(Decrypter.decryptText(xhr.response));
DataManager.onLoad(window[name]);
}
經過加密後
400 > h.status && (window[a] = JSON.parse(Decrypter.decryptText(h.response)), DataManager.onLoad(window[a]))
加密後的語句該如何理解
實際上這是運用了邏輯運算的短路特性
什麼是短路呢?
舉個例子:
條件1 && 條件2
如果條件1爲假,那麼條件2無論真假最終結果都是假,所以沒有必要判斷條件2
再看看上面的語句是不是豁然開朗
- 我們的思路只適用於RPG Maker MV嗎
先來回憶一下我們做了什麼
我們拿到一款數據文件加密過了的遊戲,根據分析,找到了數據的解密函數,並據此寫出新的函數,獲得所有未加密的文件
那我們的思路是不是僅對RPG Maker MV遊戲有效果呢?
誠然,本篇所講到的手段都是針對RPG Maker MV遊戲的,但是僅僅是手段,分析問題的思路是通用的
舉個例子:
想必很多人修改遊戲入門都是通過修改植物大戰殭屍的陽光數,那我就通過這個來講一講
首先,給大家補充一點基礎知識
我們使用的計算機架構被稱爲“馮諾依曼結構”
我們所有的程序包括操作系統都是加載在內存上運行的,這也是你爲什麼能通過CE搜索內存來修改遊戲數據
直接改內存值就是操作內存地址對應的值,很好理解
那有些功能比如若種植不減陽光,植物無CD,種植物陽光不減反而增加其背後的原理又是什麼?
再給各位開個小竈
計算機只認識字節碼,而我們根本記不住字節碼,就算有神人記住了所有的字節碼,那編程效率也是極低的,那怎麼辦呢?我們發明了一種最基礎的編程語言,叫做彙編。彙編和字節碼有一一對應的關係,所以人們只需要記住有意義的單詞,轉換的工作就交給計算機(也就是編譯),在此之上人們進行了更高層的抽象,誕生了高級編程語言,比如C, C++, JAVA等等
但程序最終還是要加載在內存中執行的,我們用CE瀏覽彙編代碼區域的時候就是從內存中把字節碼取出來並且翻譯成人類可閱讀的彙編代碼,我們使用“是什麼修改了這個內存地址”來找到修改數值的函數,這和我們之前的思路是一樣的,要找到目標函數,再在目標函數上進行修改以實現相應的功能。
好了,那我們就知道了,所有的破解和修改器,實現思路無非就是找到目標函數(比如權限驗證函數,加減錢函數),然後理清函數是如何實現的,我們在對函數進行修改以達到我們的功能。
- 再再講一點,什麼是HOOK
嗨呀,講的停不下來了
修改遊戲的時候我們我們經常能聽到一個名詞HOOK,翻譯過來是鉤子,那他究竟是什麼意思呢?
結合上面講到的,任何程序都是加載在內存裏運行的,我們如果在原來的內存斷上隨意的修改,會造成內存的混亂,進而導致進程崩潰
那我們如何才能把自己的代碼附加進去呢?
我們可以申請一片新的內存空間,將原來的函數某一處改爲jmp(跳轉命令,參數是目標內存地址),我們在新的內存空間寫上原來被替換掉的內容再加上我們新的內容,最後在跳轉回原來的地址,這樣就實現了新增我們自己的處理邏輯
上面所說的這種操作手段就是HOOK了,大部分複雜的修改器功能都是通過HOOK來實現的
- 再再再來一波
那網絡遊戲爲什麼不能修改呢?按道理說也運行在本地的內存上
網絡遊戲對於重要的數據是在服務器上有存儲的,你的每一個相關操作都會重新請求一次服務器來更新本地數據,所以你即使修改了本地數據,但是你下一次請求發生之後,服務器返回的數據會覆蓋你修改的數據
但所有數據都在服務器處理無疑會給服務器帶來巨大的壓力,因此有一部分數據在本地修改了是可以生效的,但現在計算機性能越來越強,基本上所有會影響到遊戲平衡的數據都在服務器處理了
針對網遊,除了內存式修改器,還可以通過模擬發包的方式
什麼是發包呢?
這裏需要對網絡有一定的概念
A和B兩臺計算機之間如果連上網線就可以互相通信了(假設沒有防火牆,且有通信協議支持,實際想讓兩臺計算機通信是很複雜的)
A給B發送:“hi” B收到A發送的消息
這個過程對A來說叫發包,B是收包
現代操作系統都會提供網絡通信的函數,因此,我們直接在這些函數上附加斷點,分析調試,就能找到遊戲發送數據包的函數以及數據包內容加密的函數。之後再僞造請求,比如自動吃藥功能,通過內存檢測血量不足了,程序模擬一個吃藥的包發送給服務器,我們就實現了自動吃藥功能。(倘若服務器沒有驗證剩餘藥數量那就可以實現即使沒藥也能加血的功能,當然這種基本在code review階段就被幹死了)
好啦好啦,這下真就結束了,兩天三篇文章,真是爆肝
所以呢,無論是寫代碼的,搞逆向的,其實思路都是一樣的,只是一種是正着想,一種是反着想
但我們的基本功是不變的,語言和工具都是細枝末節(我不是說他們不重要),我們更應該深挖的是原理層面的,你如果熟悉網絡,熟悉計算機組成,瞭解編程思路,那麼無論是做編程還是做逆向都會事半功倍,DEBUG的時候無疑有更多的思路。
加油,朋友們,共勉