帶大家過一遍如何分析常見java層的資料庫加密金鑰
確定資料庫是否加密,找到資料庫路徑,對於有度這個樣本,其資料庫位於/data/data/im.xinda.youdu/files/youdu/db/
路徑下,該路徑會存在1個或多個資料夾,用於儲存不同使用者的資料,其格式為buin_使用者uin_user_使用者gid
,其中uin
和gid
是解密的關鍵,請看後續分析
進行逆向分析前,先看一下應用的lib目錄裡都有些什麼東西,可以很明顯地看到libwcdb.so
這個依賴,顯然這個應用大機率是使用了wcdb來完成資料庫操作,我們只需要跟一下wcdb的資料庫開啟操作,即可找到金鑰計算方式
使用jadx進行反編譯,找到wcdb的com.tencent.wcdb.database.SQLiteOpenHelper
類,在這裡可以看到封裝了一個獲取資料庫的方法,其中呼叫了openOrCreateDatabase
去開啟資料庫,第3個引數就是金鑰
跟一下就能看到,實際是要呼叫com.tencent.wcdb.database.SQLiteDatabase.openDatabase
這個方法去開啟資料庫
這個方法中的第二個引數就是金鑰
在com.tencent.wcdb.database.SQLiteDatabase
下又有很多基於openDatabase
封裝的方法,都是用於接收不同的引數去開啟資料庫,所以只需要檢視這些方法(包括com.tencent.wcdb.database.SQLiteOpenHelper
類中的一些方法)的引用即可
這樣可以找到疑似的引用
咱們一步步往上跟,這裡的第3個引數是金鑰
這個方法有2個引用
第一個跟下去是直接拿已經計算好的金鑰,可以暫時不去管
第二個跟下去,第4個引數是金鑰
繼續跟,這邊第3個引數是金鑰
這個跟一下,第一個實際上就是之前直接獲取金鑰的那個,不去管他,後面兩個跟哪個都能拿到結果
先跟第二個吧,這邊直接看金鑰計算的方法
跟一層其實就比較清晰了,傳入YDAccountInfo
,獲取其中的兩個值,然後和其他內容一起算sha1得到金鑰
yDAccountInfo.b()
其實就是uin
,yDAccountInfo.a()
其實就是gid
而前面那一段,其實是獲取了裝置的deviceID
,通常返回的是IMEI或者MEID
那麼金鑰實際上就是SHA1(deviceID的hashcode+uin+601216000547603300+gid)
的結果
如果是跟第3個,則是直接傳入了uin
和gid
,從哪來的呢?
跟一下a2
其實就知道了,是將資料庫目錄名進行拆分,分別獲取到uin
和gid
,也就是我開頭所說的
現在的問題是,deviceID
如何獲取?實際上應用都會將資料進行儲存,這裡就是儲存的操作
每次獲取前,實際都是先從檔案讀取看看有沒有,沒有才再次獲取
那麼從這裡往下跟,就能找到資料所在目錄是global
這裡有account_info和deviceId,可以拿到金鑰計算所需要的內容
接下來計算金鑰
from hashlib import sha1
def java_string_hashcode(s):
h = 0
for char in s:
h = (31 * h + ord(char)) & 0xFFFFFFFF
if h > 0x7FFFFFFF:
h -= 0x100000000
return h
hashcode = java_string_hashcode('863640031394918')
salt = '601216000547603300'
uin = '14104680'
gid = '100022'
_sha1 = sha1()
_sha1.update((str(hashcode)+uin+salt+gid).encode('utf8'))
print(_sha1.hexdigest())
#6d2ac6f687955b27373cdeb422b4737d1f7b7c92
在im.xinda.youdu.lib.xutils.DbUtils
中還定義了資料庫採用預設的SQLCipher3
引數進行加密
成功解密資料庫
逆向分析就到此結束,此外還能透過對wcdb的資料庫開啟方法進行hook,從而達到對該框架的金鑰通殺(要想hook已經提取出的應用的金鑰,可以使用模擬
的方式,即在模擬器中安裝目標應用,將提取出的資料覆蓋到模擬器中,大多數情況下都可以正常開啟(斷網狀態))
setImmediate(function () {
Java.perform(function () {
function bytesToString(arr) {
var str = '';
arr = new Uint8Array(arr);
for (var i in arr) {
str += String.fromCharCode(arr[i]);
}
return str;
}
let SQLiteDatabase = Java.use('com.tencent.wcdb.database.SQLiteDatabase');
SQLiteDatabase['openDatabase'].overload('java.lang.String', '[B', 'com.tencent.wcdb.database.SQLiteCipherSpec', 'com.tencent.wcdb.database.SQLiteDatabase$CursorFactory', 'int', 'com.tencent.wcdb.DatabaseErrorHandler', 'int').implementation = function (str, bArr, sQLiteCipherSpec, cursorFactory, i, databaseErrorHandler, i2) {
console.log(`SQLiteDatabase.openDatabase is called: str=${ str }, bArr=${ bytesToString(bArr) }, sQLiteCipherSpec=${ sQLiteCipherSpec }, cursorFactory=${ cursorFactory }, i=${ i }, databaseErrorHandler=${ databaseErrorHandler }, i2=${ i2 }`);
let result = this['openDatabase'](str, bArr, sQLiteCipherSpec, cursorFactory, i, databaseErrorHandler, i2);
console.log(`SQLiteDatabase.openDatabase result=${ result }`);
return result;
};
});
});
easyFrida已經支援wcdb的hook
這種非常用的(內部通訊工具),我就不加到ForensicsTool裡了