获取安卓中加密数据库的密钥

带大家过一遍如何分析常见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里了

posted @ 2024-08-01 08:00  WXjzc  阅读(384)  评论(0编辑  收藏  举报