獲取安卓中加密資料庫的金鑰

WXjzc發表於2024-08-01

帶大家過一遍如何分析常見java層的資料庫加密金鑰

確定資料庫是否加密,找到資料庫路徑,對於有度這個樣本,其資料庫位於/data/data/im.xinda.youdu/files/youdu/db/路徑下,該路徑會存在1個或多個資料夾,用於儲存不同使用者的資料,其格式為buin_使用者uin_user_使用者gid,其中uingid是解密的關鍵,請看後續分析

截圖

進行逆向分析前,先看一下應用的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()其實就是uinyDAccountInfo.a()其實就是gid

截圖

而前面那一段,其實是獲取了裝置的deviceID,通常返回的是IMEI或者MEID

截圖

那麼金鑰實際上就是SHA1(deviceID的hashcode+uin+601216000547603300+gid)的結果

如果是跟第3個,則是直接傳入了uingid,從哪來的呢?

截圖

跟一下a2其實就知道了,是將資料庫目錄名進行拆分,分別獲取到uingid,也就是我開頭所說的

截圖

現在的問題是,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裡了

相關文章