Android逆向分析實例(三)-解密微信EnMicroMsg.db數據庫


 

1.簡介

首先介紹下EnMicroMsg.db數據庫:這個數據庫是存放在Android手機本地的用來保存微信聊天記錄的一個數據庫,是一個Sqlite數據庫,且手機必須要有root權限才能獲取到,而且是被加密過的,必須先找到密鑰才能打開。我們現在要做的就是找到這個密鑰。

網上關於該數據庫的解密方法幾乎都一樣:首先通過微信的 system_config_prefs.xml 文件獲取uin值,然后獲取手機的IMEI值,最后 (IMEI值+uin值)取MD5的前七位就是數據庫的密碼。

 

說下這個方法的缺陷:

  1. 該方法只能在微信沒有修改加密算法的前提下使用,一旦新版本微信修改加密算法,此方法就會立刻失效。
  2. 而且前兩天自己在微信8.0.2版本嘗試過這種方法,生成的密鑰並不正確。

下面將介紹通過frida hook方法獲取數據庫密鑰。

 

實驗准備:

  1. 微信(版本任意,我的版本是8.0.2)
  2. frida環境配置(沒有配置環境的可以看這篇文章:Frida框架配置)
  3. 一台擁有root權限的手機或者模擬器
  4. SqlCipher工具(用來打開數據庫,關注下面公眾號回復sqlcipher獲取下載鏈接)

 

2.實驗

首先將EnMicroMsg.db文件從模擬器(手機)復制到電腦上(手機或者模擬器要登陸微信):

adb pull /data/data/com.tencent.mm/MicroMsg/8e1435ec4ddf157ca48ec73b4fc108ac/EnMicroMsg.db C:\Users\lxh\Desktop\WeiXin\EnMicroMsg
#8e1435ec4ddf157ca48ec73b4fc108ac這個文件夾名稱可能不一樣,要注意一下。

然后可以這樣想:微信在啟動時,肯定會連接到這個數據庫,連接后需要一個函數以及密碼來打開數據庫讀取其中的內容,我們要做的就是找到這個打開數據庫的函數,然后用frida hook 這個函數,打印出密碼。

 微信.apk拖入jadx,打開騰訊微信官方api文檔:https://tencent.github.io/wcdb/references/android/reference/com/tencent/wcdb/database/SQLiteDatabase.html

因為涉及到數據庫的操作函數肯定帶有database字段,所以我們直接在頁面搜索"database“,經過搜索與分析后可以發現一個openOrCreateDatabase函數:(可以看到第一個參數file應該就是需要打開的數據庫文件,第二個參數password就是打開數據庫的密碼)

於是在jadx中搜索"openOrCreateDatabase",可以得到如下結果:

點進去:

可以看到這個y就是數據庫文件名,而str2就是打開數據庫的密碼,這兩個參數傳給了openOrCreateDatabase函數,接下來查看"openOrCreateDatabase"函數的調用:

可以看到這個函數是個重載函數,其中有一個調用了openDatabase函數,查看其調用:

這里的大概意思是:如果str3字符串以"EnMicroMsg.db"結尾,也就是說如果數據庫名為EnMicroMsg.db,則調用openDatabase(str3, bytes,sQLiteCipherSpec, null, i, fVar, 8)打開這個數據庫。接着查找該函數的聲明:

到這里我們就可以直接用frida來hook這個函數了,並打印前兩個參數(數據庫名和密碼):

hook.js:

import frida  
import sys    

jscode = """
    Java.perform(function(){  
        var utils = Java.use("com.tencent.wcdb.database.SQLiteDatabase"); // 類的加載路徑
        
        utils.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(a,b,c,d,e,f,g){   
            console.log("Hook start......");
            var JavaString = Java.use("java.lang.String");
            var database = this.openDatabase(a,b,c,d,e,f,g);
            send(a);
            console.log(JavaString.$new(b));
            send("Hook ending......");
            return database;
        };
        
    });
"""


def on_message(message,data): #js中執行send函數后要回調的函數
    if message["type"] == "send":
        print("[*] {0}".format(message["payload"]))
    else:
        print(message)
    
process = frida.get_remote_device()
pid = process.spawn(['com.tencent.mm']) #spawn函數:進程啟動的瞬間就會調用該函數
session = process.attach(pid)  # 加載進程號
script = session.create_script(jscode) #創建js腳本
script.on('message',on_message) #加載回調函數,也就是js中執行send函數規定要執行的python函數
script.load() #加載腳本
process.resume(pid)  # 重啟app
sys.stdin.read() 

執行代碼可以得到如下結果:

 這個95af940就是我EnMicroMsg.db數據庫的密碼,用sqlcipher.exe打開EnMIcroMsg.db,輸入密碼:

成功打開了數據庫,然后可以在message表中看到所有的聊天記錄。

 

3.總結

本次實例的難點就是找那個打開EnMicroMsg.db數據庫的函數,以及js中重載函數部分代碼的編寫,只要函數找對了,找出密碼也就近在咫尺了。

 

關注公眾號[人人皆是程序猿],回復sqlcipher獲取工具下載鏈接

曾經發生過的事情不可能忘記,只不過是想不起而已。

——《千與千尋》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM