轉載,鏈接:解密安卓微信聊天信息存儲 | Greycode’s Blog
目錄
準備工作
(當前微信版本是:8.0.18)
- 一台 Root 的手機(手機不能 Root 的話用安卓模擬器,然後安卓模擬器獲取 Root 應該也是可以的,不過我沒試過)
- DB Browser for SQLite
- SQLCipher
- silk-v3-decoder
收集數據
需要收集的數據有:
- image2 文件夾:裡面存放着所有的微信聊天圖片,位置在:
/data/data/com.tencent.mm/MicroMsg/[32位字母]/image2
- voice2 文件夾:裡面存放着所有的微信語音,位置在:
/sdcard/Android/data/com.tencent.mm/MicroMsg/[32位字母]/voice2
- voide 文件夾:裡面存放着所有的微信視頻,位置在:
/sdcard/Android/data/com.tencent.mm/MicroMsg/[32位字母]/voide
- avatar 文件夾:裡面存放着所有的微信頭像,位置在:
/data/data/com.tencent.mm/MicroMsg/[32位字母]/avatar
- Download 文件夾: 微信的聊天發送的文件存放在這裡,位置在:
/sdcard/Android/data/com.tencent.mm/MicroMsg/Download
- EnMicroMsg.db: 微信的數據庫文件,位置在:
/data/data/com.tencent.mm/MicroMsg/[32位字母]/EnMicroMsg.db
- WxFileIndex.db: 微信的文件索引數據庫文件,位置在:
/data/data/com.tencent.mm/MicroMsg/[32位字母]/WxFileIndex.db
在上面的這些文件中,需要注意的是路徑中有個 32 位字母的路徑,這個是微信通過某種算法生成的,每個號的路徑都不一樣。 其中 voice2、voide、Download 這三個文件夾在 /sdcard
目錄下,其他的在系統目錄 /data
下。 Download 文件夾存放着當前手機上所有微信聊天時發送的文件,這裡文件例如:文檔,安裝包、壓縮包等。需要通過 WxFileIndex.db 來索引到這個文件夾。
把上面收集的所有文件放在電腦的同一個文件夾中,接下來對這些數據進行處理。
獲取 DB 訪問密碼
在上面獲取到的 EnMicroMsg.db、WxFileIndex.db 是經過加密的,所以我們需要獲得這個的訪問密碼,通過這個密碼來解密數據庫。
方法一
可以直接通過 MD5(IMEI+uin) 取前 7 位即是訪問密碼,如果是大寫的要轉換成小寫字母。(注意:拼接兩個數據時不需要用 +
號) 其中IMEI 是手機的 IMEI 碼,可以查詢手機的設置,在設置中可以查看到。如果你手機刷過機,那麼 IMEI 有可能是空白的。或者像 MIUI 系統一樣,應用無法真正獲取到手機的 IMEI。這時就可以用 1234567890ABCDEF 這個字符串來代替 IMEI。 uin 可以通過 adb 來查看當前登陸微信的 uin 數據:
## 進入 adb
$ adb shell
# 進入 root 用戶
$ su
# 查看文件
$ cat /data/data/com.tencent.mm/shared_prefs/auth_info_key_prefs.xml
文件內容:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<boolean name="auth_info_prefs_use_new_ecdh" value="true" />
<int name="_auth_uin" value="272136722" />
<boolean name="key_auth_info_prefs_created" value="true" />
<int name="key_auth_update_version" value="xxx" />
<string name="server_id">xxxx</string>
<string name="_auth_key">xxxxx</string>
</map>
其中 _auth_uin 的 value 值就是 uin。
方法二
還可以通過 Frida 來獲取訪問密碼,如果你電腦上有 python 環境的話,建議用這個方法,因為這個方法可以直接得到密碼,而不用一個一個的去試拼接出來的密碼,並且絕對正確。 首先電腦通過下面命令安裝 Frida 包:
$ pip install frida
$ pip install frida-tools
然後使用 adb 查看手機架構:
$ adb shell getprop ro.product.cpu.abi
arm64-v8a
得到的是 arm64-v8a,然後去 https://github.com/frida/frida/releases 頁面下載對應的 frida-server-<版本號>-arm64.xz 包,然後解壓。 注意:這邊的 frida-server 版本號要和上面電腦安裝的 frrida 的版本號一致,否則可能會出現額外的錯誤。 通過 adb 把 frida-server 傳到手機:
$ adb push frida-server-<版本號>-android-arm /data/local/tmp
然後在手機上運行 frida-server:
$ adb shell
$ su
$ cd /data/local/tmp
$ chmod 777 frida-server-<版本號>-android-arm
$ ./frida-server-<版本號>-android-arm
運行後,這個終端界面不要關閉,另外在啟動一個終端,然後在終端中輸入:
$ adb forward tcp:27042 tcp:27042
$ adb forward tcp:27043 tcp:27043
$ frida-ps -U
如果終端輸出了一些進程,那麼就表示環境搭建成功了。搭建成功後,在電腦運行下面的 Python 腳本:
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):
if message["type"] == "send":
print("[*] {0}".format(message["payload"]))
else:
print(message)
process = frida.get_remote_device()
pid = process.spawn(['com.tencent.mm'])
session = process.attach(pid)
script = session.create_script(jscode)
script.on('message',on_message)
script.load()
process.resume(pid)
sys.stdin.read()
腳本運行後,然後在手機直接打開微信,這時電腦控制台會輸出一個 7 位字母,這個就是訪問密碼。
解密 DB
微信加密使用的是開源的 SqlCipher,所以我用這個工具加上上面得到的密碼來進行解密。 在 https://github.com/sqlcipher/sqlcipher/tags 這個頁面上,可以找到最新的版本,然後下載。 下載後進行解壓,解壓後進入文件夾進行編譯,編譯前先檢查本地有沒有安裝 GCC 和 OpenSSL,如果沒有安裝,需要先安裝。
# 進行編譯
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
LDFLAGS="-lcrypto"
$ make
Mac 電腦可以直接通過 brew 來進行直接安裝:
$ brew install sqlcipher
Windows 建議使用 WSL 來進行編譯。 測試有沒有安裝成功,在終端輸入 sqlcipher,如果出現以下信息,則表示安裝成功了。(注意:如果後面括號中沒有出現 SQLCipher 4.5.1 community,那麼表示只安裝了 SQLite,沒有安裝 SQLCipher,那麼就不能進行解密操作了)。
$ sqlcipher 127 ↵
SQLite version 3.37.2 2022-01-06 13:25:41 (SQLCipher 4.5.1 community)
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite>
安裝完成後,打開一個終端,進入 EnMicroMsg.db 文件存放的位置,然後在終端進行數據庫的解密:
$ sqlcipher EnMicroMsg.db
SQLite version 3.37.2 2022-01-06 13:25:41 (SQLCipher 4.5.1 community)
Enter ".help" for usage hints.
sqlite> PRAGMA key = '上面得到的密碼';
ok
sqlite> PRAGMA cipher_use_hmac = off;
sqlite> PRAGMA kdf_iter = 4000;
sqlite> PRAGMA cipher_page_size = 1024;
sqlite> PRAGMA cipher_hmac_algorithm = HMAC_SHA1;
sqlite> PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;
sqlite> ATTACH DATABASE 'plaintext.db' AS plaintext KEY '';
sqlite> SELECT sqlcipher_export('plaintext');
sqlite> DETACH DATABASE plaintext;
執行完上面這些命令後,會在終端輸出一個 plaintext.db,這個文件就是解密後的數據庫文件。對 WxFileIndex.db 進行同樣的操作來進行解密。(進行第二次解密時,注意 plaintext.db 文件名衝突,所以建議使用一個新的文件名)
EnMicroMsg.db 解析
數據表
這個數據庫中有許多的表,但是真正有用的就下面這幾張表:
- userinfo 表:存儲個人信息,其中 id 為 2 的 value 是個人的微信 id。
- message 表:存儲所有的聊天記錄。
- chatroom 表:存儲所有群聊信息。
- img_flag 表:存儲所有用戶的在線頭像的信息。如果本地 avatar 文件夾沒有頭像時,可以用這個表的地址來訪問用戶的頭像,其中 reserved2 是縮略圖,reserved1 是高清圖。
- rcontact 表:存放所有的好友信息。
消息內容解析
在 message 表中,type 字段表示着當前消息的類型,一般有如下類型:
- 1:文本消息
- 3:圖片消息
- 34:語音消息
- 43:視頻消息
- 47:大表情消息
- 49:分享卡片信息
- 1000:撤回消息提醒
- 436207665:微信紅包
- 419430449:微信轉賬
- 1090519089:文件消息 上面的一些媒體類型的消息,例如圖片、語音、視頻等,都會可以用 msgId 字段去 WxFileIndex.db 數據庫中的 WxFileIndex2 表中查找到對應的文件路徑。 除了通過去 WxFileIndex2 表查詢媒體文件的路徑,還可以通過某些字段的拼接和加密直接獲取媒體文件的路徑。
圖片地址獲取
圖片消息的地址有兩個,一個是圖片縮略圖,一個是圖片原圖。
- 縮略圖獲取: 在 message 表中,如果當前消息為圖片消息時,imgPath 字段會有值,值類似於:
THUMBNAIL_DIRPATH://th_5a24c5d362dae72b0ad52d78767ba883
,其中 5a24 代表/5a/24
文件夾下的,th_5a24c5d362dae72b0ad52d78767ba883 是圖片文件名。圖片的父目錄就是一開始的/image2
文件夾。 - 原圖獲取: 如果要獲取原圖,則是通過另外一種拼接規則來得到圖片地址的。一般有兩種情況:
- 發送的圖片:文件名是:
自己的wxid+_+當前的talker值+_+當前msgSvrid+_backup
,路徑是文件名的前兩個字母,每兩個字母代表一個文件夾層級。 - 接收的圖片:文件名是:
當前的talker值+_+自己的wxid_+當前msgSvrid+_backup
,路徑是文件名的前兩個字母,每兩個字母代表一個文件夾層級。
- 發送的圖片:文件名是:
視頻地址獲取
直接通過 message 表後的 imgPath 查找到 video 文件夾查找對應的視頻,封面圖後綴為 .jpg
,視頻後綴為:.mp4
。
語音地址獲取
message 的 imgPath 字段通過 MD5 加密後,前 4 個字母代表兩級文件夾名,然後最終文件名是:msg_imgPath的值.amr
文件地址獲取
在微信聊天時發送的文件都存放在 /sdcard/Android/data/com.tencent.mm/MicroMsg/Download
文件夾下,只能通過當前的 msgId 字段去 WxFileIndex.db 數據庫中的 WxFileIndex2 表中查找到對應的文件路徑。
本地頭像獲取
微信的頭像都存放在 /data/data/com.tencent.mm/MicroMsg/[32位字母]/avatar
文件夾下,微信 ID 通過 MD5 加密後,前 4 個字母代表兩級文件夾名,每兩位代表一個文件夾名,文件名格式:user_md5字符串.png
例如微信id:weixin 經過 MD5 加密後是:C196266F837D14E0B693F961BEE37B66,那麼這個微信的頭像地址是:avatar/c1/96/user_c196266f837d14e0b693f961bee37b66.png
語音文件處理
由於微信語音使用了 SILK v3 編碼,一般播放器都不放不了,所以需要進行手動解碼。這裡直接使用開源的 silk-v3-decoder 工具來進行解碼。需要先安裝 GCC、ffmpeg 等工具,具體查看開源工具說明。 轉碼後,在獲取語音文件地址時,記得把後綴改為你轉碼後的後綴,例如轉碼成 mp3
格式,後綴就是 mp3
,不是 amr
。