聲明:
僅用於學習目的.
僅使用自己的微信聊天記錄.
寫在前面的廢話最近由於工作需要我又複習學習了一遍資料庫,是在極客時間上跟著陳暘博士學的,其中一節講微信聊天記錄的存儲方式,然後評論區有朋友就提到生成詞雲的點子。很開心,學以致用才是最好的方式嘛。但是呢,這課是2019年的,當我開始做的時候,坑早就埋好了。
朋友們,我折騰了快兩周,終於完整地完成了這個效果,先上圖!
效果還行吧?下面記錄下步驟和遇到的坑。PS:我使用的環境是win10+wsl2+Python3.9,使用mac/linux的朋友我覺得不需要看(狗頭
跟大象放進冰箱一樣簡單我們把聊天記錄生成詞雲也只需要三步對吧。
獲取聊天記錄
解密聊天記錄
解析文本生成詞雲效果
每一步都有很多坑,我們一個個看
Step1 獲取聊天記錄無非是從手機端或者電腦端獲取存放聊天記錄的資料庫文件對吧,當然我也見過在聊天窗口不斷選中聊天內容然後複製的,這種方式還挺開心的,我們不討論。說回正途。目前的微信版本,無論從電腦端(3.5.0.39)還是手機端(8.0.15),拿到資料庫文件還算簡單,但都是加密過的,解密才是最難的。對我來說電腦端解密似乎更難,我們就從手機端入手吧。
考慮到系統安全和保修問題,現在大家用的手機,無論 Android 還是 iPhone 很少去 root 或者越獄了對吧,所以我們可以用電腦端的安卓模擬器獲取 root 權限然後僅對微信做一些操作。對於安卓,我知道特定品牌的手機都有各自的獲取備份文件(包括各個APP的數據)的方式,以及 iPhone 也可以通過 iTunes 獲取備份,但是後面解密資料庫依然要靠安卓模擬器,而且考慮到通用性,使用安卓模擬器就是最佳選擇了。
PS: 寫作本文時發現 iPhone 通過 iTunes 似乎是可以直接獲取沒有加密的版本的Wx backup,由於我沒有用 iPhone ,我們還是繼續說 Android.
步驟如下:
備份聊天記錄至電腦
恢復聊天記錄至安卓模擬器
從安卓模擬器獲取資料庫文件
備份聊天記錄至電腦大家對電腦端的這個界面應該不陌生。需要注意的是要保證手機和電腦在同一區域網,然後電腦端設置項中發起備份。
恢復聊天記錄至安卓模擬器安裝模擬器並開啟root權限(一般默認都開啟了,可以在模擬器的設置中查看)。模擬器有很多種,常見的有網易MuMu https://mumu.163.com/, 夜遊神 https://www.yeshen.com/, 我用的是夜遊神,注意目前的大多數模擬器都要進 BIOS 開啟虛擬技術的功能,具體參看各模擬器的官方幫助。MuMu在後面安裝微信時會提示網絡問題導致失敗,大概是公司網絡限制,我沒有深究,所以換了夜遊神,當然夜遊神也有坑,後面再說。
通過模擬器自帶的應用商店安裝微信並登錄。注意這時候真正的手機及電腦端的微信都會被迫退出,電腦端重新登錄即可,手機端不要登錄。
電腦端再選擇「恢復聊天記錄」至手機(當然實際是恢復到模擬器中)。對於我們這次的需求,為加快速度,我們可以僅恢復文字消息。
從安卓模擬器獲取資料庫文件使用模擬器自帶的文件瀏覽器或者ES文件瀏覽器(若應用商店中沒有可在酷安之類的平臺上下載APK直接安裝),在類似 /data/data/com.tencent.mm/ 這樣的路徑下尋找 資料庫文件EnMicroMsg.db (僅針對目前8.0.15版本微信,不保證後續微信不會修改路徑、文件名甚至存儲方式,不過差別應該不大)。
通過模擬器文件共享的方式將 EnMicroMsg.db 放到電腦的文件系統中。微信終端的這個資料庫使用的是SQLite,我們可以用 DB Browser/navicat 等軟體可視化查看這個 EnMicroMsg.db ,顯然現在還打不開,因為需要解密。
Step2 解密聊天記錄首先說下,之前很多人提到的通過 IMEI 和 UIN 進行 MD5 計算並拿到密碼的方式已經沒有作用,微信在某個版本之後不再使用這種方式生成的密碼,所以我們還是要用其他辦法。
emmmm 那麼...
使用 frida 獲取密碼
sqlcipher 解密並生成未加密的資料庫
使用 frida 獲取密碼這是花費我時間和精力最多的一步(狗頭微笑
frida 的官網https://frida.re/是這麼介紹的:
Dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers.
Scriptable
Portable
Free
Battle-tested
簡單地說就是它簡單、多平臺、免費而且久經考驗。可用於逆向,可用於安全研究。
好的,我們還是看看它怎麼用吧。對於我們的應用場景,它的原理大致是這樣的:
手機端模擬器上打開微信,然後運行 frida-server ,再把模擬器的埠轉發到電腦端,電腦端運行 Python 腳本與之通信,通過 Python 代碼向模擬器中注入一段 Javascript 腳本,最後模擬器上的微信執行登錄步驟時會調用一些接口並傳遞打開資料庫用到的密碼,這個時候腳本就能獲取到密碼。
我們具體來看:
電腦端安裝 frida可通過 npm 安裝,也可以通過 Python 安裝,我選擇了通過 Python 來安裝。
首先,Python 儘量3.5以上的版本吧,Python 的包管理工具 pip 也要裝上。
然後繼續,終端輸入:
pip install frida
pip install frida-tools這裡有些坑:
在 python3.10 上安裝 frida 後無法 import,會報 DLL load failed while importing _frida , 換 Python3.9 可以,GitHub 上有人提到這個問題,應該是 frida 的兼容性沒做好。當然看到此文的時候也許最新版已經修復,你們可以試試。
目前都是使用 pip 進行安裝,由於眾所周知的網絡問題,需要設置國內鏡像,使用自己的代理會出現 SSLError, 稍微 google 一下沒有找到有用信息,還是設置國內鏡像比較快。
即使設置了國內鏡像,由於其中會從其他地方下載一些預編譯的包,終端還是會卡在那,耐心等會。
即使耐心等待了幾分甚至十幾分鐘,安裝完成後你依然要在終端輸入 frida --version 以及 frida-ps 看看有沒有輸出,有的話才安裝成功。否則卸載掉再多試幾次。
安裝 frida 折騰了我很久,祝你好運(狗頭微笑
模擬器安裝並運行 frida-server這裡說是安裝,其實僅僅是把 frida-server 放到模擬器中。從https://github.com/frida/frida/releases 下載 frida-server,有很多版本,應該下哪個呢?
終端輸入 adb shell getprop ro.product.cpu.abi 查看模擬器的 cpu 架構,我的是 x86。
此處還要注意一下,如果你安裝了 AndroidStudio 又或者其他什麼渠道僅僅裝了 adb ,你需要保證模擬器中的 adb 版本與你本來裝好的 adb 版本一致,否則後續使用 adb push 等命令時會失敗。若你沒有安裝,那就使用模擬器中的 adb 好了。特別的,夜遊神的 adb 是 %InstallPath%/Nox/bin/nox_adb.exe
上一步中我安裝的 frida 的版本是15.1.14,所以我這裡下載的是 frida-server-15.1.14-android-x86.xz
這是一個壓縮包,如果你有安裝 Git Bash 或者 Cygwin 之類的,你在下載路徑中打開終端輸入 xz -d -k ./frida-server-15.1.14-android-x86.xz 即可解壓, 沒有的話,安裝一下吧...
打開模擬器中「開發者選項」,再打開「USB調試」的功能。這樣電腦端輸入 frida-ps -U 就可以查看連接的USB設備的進程。
電腦終端依次輸入以下命令:
> adb push ./frida-server-15.1.14-android-x86 /data/local/tmp/
> adb shell
# su
# cd /data/local/tmp/
# chmod 777 ./frida-server-15.1.14-android-x86
# ./frida-server-15.1.14-android-x86這樣模擬器端的 frida-server 就運行起來了。
電腦端另開一個終端,輸入:
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043這樣進行埠轉發,然後輸入 frida-ps -R 就可以查看伺服器端(模擬器)進程的運行情況。
這裡我又遇到了一個坑:每次輸入命令查看進程,模擬器就會重啟,開始我以為是微信導致又或者是我不小心安裝了谷歌三件套導致的,最後重裝模擬器然後不裝任何APP還是不行。我恍惚了很久,快要放棄時突然想起來看看夜遊神的更新記錄,我用的V7.0.2.0版本的
支持安卓7(32位)使用Xposed框架
Xposed! 跟 frida 一樣的 Hook 框架,可能有點衝突或者其他的Bug吧,回退到V7.0.1.9版本就好了...
電腦端運行Python腳本獲取密碼最關鍵的時候來了,我們保證模擬器上的微信處於登錄界面(之前登錄並同步了聊天記錄,需要退出登錄先),模擬器的 frida-server 處於運行狀態.
然後電腦端運行如下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): # 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) # 加載進程號
# session = process.attach('com.android.phone') # 加載進程號
script = session.create_script(jscode) # 創建js腳本
script.on('message', on_message) # 加載回調函數,也就是js中執行send函數規定要執行的python函數
script.load() # 加載腳本
process.resume(pid) # 重啟app
sys.stdin.read()然後模擬器上點擊登錄。很快 Python 腳本就列印出密碼來。
密碼拿到!!!(開心
sqlcipher 解密並生成未加密的資料庫接下來相對來說就簡單了,由於 sqlcipher 沒有沒有免費版了,需要自己根據源碼https://github.com/sqlcipher/sqlcipher編譯,在wsl2下編譯時可能遇到的問題:- 除了需要安裝編譯c/c++用到的build-essential之外,還需要安裝 tcl tk openssl - configure 後提示 error, 在 config.log 中查看發現是 ld cannot find -lcrypto, 注意說的是libcrypto.so,不是libcrypt.so. 查找該庫,可能找到類似libcrypto.so.1.1.1這樣的,建一個軟連接就好了 - make 後報錯提示sqlite_int64之類未定義的話先查看上一步生成的sqlite3.h是否為空,是的話重新configure一下。
OK, sqlcipher 就編好了,運行 ./sqlcipher ./EnMicroMsg.db 再輸入以下指令,注意 yourkey 替換成上面獲取到的密碼
PRAGMA key = "yourkey";
PRAGMA cipher_use_hmac = OFF;
PRAGMA cipher_page_size = 1024;
PRAGMA kdf_iter = 4000;
PRAGMA cipher_compatibility = 1;
ATTACH 'decrypt.db' AS d KEY '';
SELECT sqlcipher_export('d');
DETACH DATABASE d;這樣就拿到了解密的資料庫文件 decrypt.db,從wls2的文件系統轉存到win10中,然後就可以用 DB Browser/navicat 之類的可視化方式查看。當然,當我費勁巴拉編好 sqlcipher 才發現 SQLiteStudio 這款免費軟體包含了 sqlcipher, 打開 SQLiteStudio 並選擇打開資料庫,數據類型選則 SQLCipher,然後密碼和參數如上就好啦。Hmmm...SQLiteStudio 效果不如 DB Browser ,我編 sqlcipher 沒有錯(狗頭微笑
還有一個坑就是wsl2和大部分安卓模擬器不能同時使用,解決方案是,win10底部搜索欄直接搜索「啟用或關閉 Windows 功能」並打開,使用安卓模擬器時就去掉「Hyper-V」和「虛擬機平臺」,使用wsl2時則相反。
Step3 解析文本生成詞雲效果花了這麼多時間和精力,終於到了我原本想做的事情(滑稽
我們直接上代碼吧,有些注釋也在代碼裡了
"""
根據微信資料庫文件創建聊天詞雲
"""
import sqlite3
import re
from wordcloud import WordCloud
from wordcloud import ImageColorGenerator
import matplotlib.pyplot as plt
import jieba
from PIL import Image
import numpy as np
# 這個 stop_words 可以根據你的實際情況再添加
stop_words = [
'什麼',
'呢',
'嗯',
'啊',
'的',
'了',
'吧',
'嗎',
'麼',
'哦',
'噢',
]
# 生成詞雲
def create_word_cloud(f):
print('根據微信聊天記錄,生成詞雲!')
mask = np.array(Image.open('./circle.jpg'))
maskImg = np.array(Image.open('./pikachu-character.jpg'))
maskColor = ImageColorGenerator(maskImg)
cut_text = " ".join(jieba.cut(f, cut_all=False, HMM=True))
wc = WordCloud(
font_path='simhei.ttf',
max_words=100,
# width=2000,
# height=2000,
background_color='white',
mask=mask,
collocations=False,
stopwords=stop_words,
# color_func=maskColor, # 可根據解析的圖片顏色生成相應顏色的文字效果
)
wordcloud = wc.generate(cut_text)
# 寫詞雲圖片
wordcloud.to_file("./data_view/wordcloud.jpg")
# 顯示詞雲文件
plt.imshow(wordcloud)
plt.axis("off")
plt.show()
def extract_content(f):
f = f[2 : len(f) - 3] # 去掉開頭的 (' 以及結尾的 ',)
sub_pattern = re.compile(r'^.+:\\n')
f = sub_pattern.sub('', f)
return f
def get_content_from_weixin():
# 創建資料庫連接
# 這裡需要把 找到的weixin.db 放到根目錄,具體方法之前文稿講過
conn = sqlite3.connect(r"D:\src_code_release\test_code\python_\data_view\weixin.db")
# 獲取遊標
cur = conn.cursor()
# 創建數據表
# 查詢當前資料庫中的所有數據表
sql = "SELECT content FROM message;" # 這樣是解析全部聊天內容
# sql = "SELECT content FROM message WHERE talker = '666666@chatroom' ORDER BY msgId;" # 解析特定的群
# sql = "SELECT content FROM message WHERE talker = 'wxid_888888';" # 解析你與某個朋友的聊天內容
cur.execute(sql)
contents = ''
rets = cur.fetchall()
for temp in rets:
contents = contents + extract_content(str(temp))
# 提交事務
conn.commit()
# 關閉遊標
cur.close()
# 關閉資料庫連接
conn.close()
return contents
content = get_content_from_weixin()
# 去掉HTML標籤裡的內容
pattern = re.compile(r'<[^>]+>', re.S)
content = pattern.sub('', content)
# 將聊天記錄生成詞雲
create_word_cloud(content)如果你不能運行,注意查看裡面用到的庫,使用 pip 安裝即可。
如果你的聊天內容非常多,這個腳本可能會運行很久,耐心等待...
再看幾張效果圖
好開心噢。
所以我哪裡是在學資料庫,是在搞逆向安全研究?
我要繼續學習了,下期再見,祝大家開心~