- 前言
- 前置准備
- 目標分析
- 逆向加密邏輯
- 定位sign與key
- 解密luac
- 反編譯luajit的bytecode
- 開啟上帝模式
- 前言
看了fatezero的《陰陽師:一個非酋的逆向旅程》后,受益匪淺。特別是關於opcode映射關系一節,處理的很精妙。
對手頭上的游戲不思議迷宮,技癢的不行。於是上周周六花了整個下午的時間進行了研究。注意本文中針對安卓版本。
- 前置准備
如果讀者也想跟着步驟進行操作,需要准備這些工具:ApkIDE少月版、IDA、python、010editor
- 目標分析
APKIDE載入之后,首先看一下lib\armeabi\目錄下,發現了libcocos2dlua.so
從字面意思來看,應該是使用cocos2d引擎,並且使用lua腳本,再看assets\src\
發現大量的luac腳本,進一步確認了我們的想法。010editor打開Main.luac,從頭來看,並非luac文件的頭
正常的luaca的頭應該是1B 4C 75 61開頭,如下圖
所以不思議迷宮必然是對luac進行了加密。
- 逆向加密邏輯
通過IDA打開libcocos2dlua.so,一般情況下加密會出現cocos2dx_lua_loader->luaL_loadbuffer的某個過程中
源碼如下:
而在IDA中,luaL_loadbuffer之前出現了srcDecrypt函數,這可是源碼中沒有出現了。
數據的流向是:從文件讀入->v51->v25->luaL_loadbuffer,再分析srcDecrypt函數
當一個文件的頭為11 12 13 的時候,就用charMapList進行替換,而charMapList,通過引用查找
又是從buildEncrypyMap中初始化的,顯然這是一組“靜態”的置換表,完全可逆而且沒有任何難度。
但是回過頭來看apk中的luac文件,沒有一個的文件頭是11 12 13,文件頭全部是applicationWillEnterForeground
並沒有給我們帶來任何幫助,只能繼續分析luaLoadBuffer,看到了第二個加密的地方xxtea_decrypt
google,baidu之后,找到非常類似的一段源碼
通過sign對文件進行標記,符合條件用key進行解密,梳理一下luac的整體解密過程
由於沒有使用srcDecrypt的流程,所以實際上只有xxtea_decrypt,只要找到sign和key,問題就解決了。
- 定位sign與key
通過分析,可以確定加密的最終文件格式。文件頭都有固定長度的sign
xxtea_decrypt(buf+decode->m_xxteaSignLen, (xxtea_long)size -(xxtea_long)decode->m_xxteaSignLen, (unsigned char*)decode->m_xxteaKey, (xxtea_long)decode->m_xxteaKeyLen, &len);
再次打開另外一個luac文件,二者相同的文件頭如圖,所以sign為applicationWillEnterForeground
在IDA中,我們也找到了這個字符串
查找引用之后,這個字符串在initLuaStack中被調用了。
再通過資料搜索,發現一般使用xxtea算法的,都會使用setXXTEAKeyAndSign來設置sign和key,圖中v3就是setXXTEAKeyAndSign函數
stack->setXXTEAKeyAndSign("123", strlen("123"), "cloud", strlen("cloud"));
那么自然key就是:applicationDidEnterBackground
- 解密luac
首先pip install xxtea-py,安裝python的xxtea的庫
編寫腳本如下:
import xxtea import os sign = 'applicationWillEnterForeground' key = 'applicationDidEnterBackground' def decode(filename): luacdata = open(filename,'rb').read() decrypt_data = xxtea.decrypt(luacdata[len(sign):],key[:16]) open(filename.replace('.luac','.luacx'),'wb').write(decrypt_data)
解密后如下,出現的文件頭為1B 4C 4A 01,也並非luac標准頭,難道還有名堂?
經過一番資料查詢之后,這是luajit編譯的bytecode,並非標准lua。其頭為1B 4C 4A
- 反編譯luajit的bytecode
在github上找到用於反編譯luajit的項目:https://github.com/NightNord/ljd
由於該庫只能在python3上跑起來,而我懶得改腳本,於是直接通過命令執行方式,在kali中(同時存在python2、python3)運行了如下腳本
#coding=utf-8 import os sign = 'applicationWillEnterForeground' key = 'applicationDidEnterBackground' path = os.path.join(os.getcwd(),'src') def decode(filename): luacdata = open(filename,'rb').read() decrypt_data = xxtea.decrypt(luacdata[len(sign):],key[:16]) open(filename.replace('.luac','.luacx'),'wb').write(decrypt_data) def decomplie(filename): os.system('python3 ./ljdm/main.py '+filename+' > '+filename.replace('.luacx','.luacxs')) for path,d,filelist in os.walk(path): for filename in filelist: filename = os.path.join(path, filename) if filename.endswith('.luac') and not os.path.isfile(filename.replace('.luac','.luac')): print( filename) decode(filename) if filename.endswith('.luacx') and (not os.path.isfile(filename.replace('.luacx','.luacxs')) or os.path.getsize(filename.replace('.luacx','.luacxs'))==0): print(filename) decomplie(filename)
src目錄對應生成的luacxs就是最終反編譯好的lua源代碼了。
- 開啟上帝模式
通過大量的分析之后,我發現程序員在代碼中,沒有去掉測試用的上帝模式,只是簡單的隱藏
我在UIAccountBind.luacxs:380行中加入打開上帝模式的一行代碼openGdUI().
這樣當我點擊復制賬號id的時候就會彈出上帝模式窗口
local function onCopyClick(sender, eventType) if eventType == ccui.TouchEventType.ended then copyToClipBoard(rid, getLocStr("text_copied")) end openGdUI() return end
以及GDM.luacxs:24中的check_mode,讓其始終返回為true,保證上帝模式功能可正常開啟
function check_mode() return true end
然后通過luajit編譯,再進行xxtea加密,打包成APK放入手機。
最終效果圖為:全自動放置play,程序員已經實現了自動加天賦,自動撿東西,自動打怪,自動進入下一層等等