如何优雅地管理微信数据库?
最近每天在隔離點蹲著,發現隔離點的護士小姐姐每天兩次在群里扒聊天記錄統計一兩百號人的體溫真是太南了,所以想寫個程序幫小姐姐自動收集,今天剛好隔離期滿,也算是給這段特殊的經歷留個紀念。
這篇文章主要內容是:
- 如何找到微信本地緩存數據庫存放地址
- Mac OS 關閉 SIP 系統完整性保護
- lldb 斷點調試得到緩存數據庫地址
- 如何打開數據庫
- lldb 斷點調試得到數據庫密碼
- 使用 DB Browser for SQLite 打開數據庫并重設密碼
- 微信本地緩存數據庫的結構介紹
- Contact - wccontact_new2.db - 好友信息
- Group - group_new.db - 群聊和群成員信息
- Message - {msg_0.db - msg_9.db} - 聊天記錄和公眾號文章
- Favorites - favorites.db - 收藏
- 如何解析數據庫并提取目標信息
找到微信本地緩存數據庫存放地址并獲取數據庫密碼
捷徑
對于Mac OS 系統,一個 short answer 是
/Users/xxx/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/
打開后,可以看到:
這里需要重點關注的是看起來很像 md5 碼形式的文件,每個文件都代表一個曾經在你的電腦上登陸過并留下緩存的微信賬號,有了下面將會介紹的解碼方法,你可以逐個打開解析,確認到底哪個賬號是你要找的。
總體來說,Windows 系統同理。
LLDB 調試
在沒有任何信息的情況下,我們如何找到一個獲取數據庫地址的系統性方法?答案在于LLDB斷點調試。
什么是 LLDB?
LLDB is a next generation, high-performance debugger. It is built as a set of reusable components which highly leverage existing libraries in the larger LLVM Project, such as the Clang expression parser and LLVM disassembler.
LLDB is the default debugger in Xcode on Mac OS X and supports debugging C, Objective-C and C++ on the desktop and iOS devices and simulator.
All of the code in the LLDB project is available under the standard LLVM License, an open source “BSD-style” license.
簡言之,LLDB是一個有著 REPL(交互式解析器) 的特性和 C++ |Python 插件的開源調試器新一代高性能調試器。隨著Xcode5的發布,LLDB調試器成為macOS系統調試的基礎部分。對于開源和其他非基于GUI的應用程序調試的開發,可以將終端窗口中的LLDB用作傳統的命令行調試器。
這里的主要的信息是:
- LLDB 是一個內建于 OS 終端窗口的命令行調試器
- LLDB 可以和系統運行的進程進行交互調試
這意味著,使用 LLDB 我們可以通過在命令行打斷點來獲得正在運行的進程的后臺信息。這也是我們可以通過 LLDB 來尋找微信數據庫地址以及獲取訪問密碼的主要原因。
準備工作:關閉SIP系統完整性保護
系統完整性保護(SIP)是 OS X El Capitan 及更高版本所采用的一項安全技術,旨在幫助防止潛在惡意軟件修改 Mac 上受保護的文件和文件夾,但這也造成了安裝某些特殊版本軟件的或者做特殊修改的時候權限不足。在這里就體現在使用 LLDB 調試時候,所有的調試語句都會被系統拒絕,因此在正式進行調試之前,一個重要的準備工作就是檢查系統完整性保護(SIP)的開啟狀態,如果開啟的話,要把它關閉。
-
檢查 SIP 的開啟狀態
在終端里輸入 csrutil status 回車,如果看到:System Integrity Protection status: enabled.
這說明的 SIP 已經開啟,如果要繼續調試的話,需要關閉。如果是 System Integrity Protection status: disabled. 則說明 SIP 已經處于關閉狀態,可以直接進行調試。
-
關閉 SIP
-
重啟,并在開機的時候長按 Command 和 R
-
進入系統恢復狀態
-
點擊屏幕頂部工具欄上的 實用工具,選擇終端
-
在終端中輸入 csrutil disable 回車,會出現
Successfully disabled System Integrity Protection. Please restart the machine for the changes to take effect.
-
再次重啟生效
-
-
記得在調試后按照同樣的步驟輸入 csrutil enable 重新開啟 SIP 惹
LLDB 獲取微信數據庫地址
-
打開微信,但是不要登錄
-
在命令行里輸入 lldb -p $(pgrep WeChat)
(lldb) process attach --pid 77855 Process 77855 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOPframe #0: 0x00007fff6e878dfa libsystem_kernel.dylib`mach_msg_trap + 10 libsystem_kernel.dylib`mach_msg_trap: -> 0x7fff6e878dfa <+10>: retq 0x7fff6e878dfb <+11>: nop libsystem_kernel.dylib`mach_msg_overwrite_trap:0x7fff6e878dfc <+0>: movq %rcx, %r100x7fff6e878dff <+3>: movl $0x1000020, %eax ; imm = 0x1000020 Target 0: (WeChat) stopped.Executable module set to "/Applications/WeChat.app/Contents/MacOS/WeChat". Architecture set to: x86_64h-apple-macosx-. -
這時候微信的進程被我們暫停了,需要在命令行中輸入 c 回車,可以看到:
Process 77855 resuming
-
掃碼登陸微信
-
輸入 br set -n '[WCTDatabase initWithPath:]
Breakpoint 1: where = WCDB`-[WCTDatabase(Database) initWithPath:], address = 0x000000010e54120a -
進入 聊天備份與恢復 頁面點擊恢復聊天記錄到手機 觸發斷點
Process 78136 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1frame #0: 0x000000010e54120a WCDB`-[WCTDatabase(Database) initWithPath:] WCDB`-[WCTDatabase(Database) initWithPath:]: -> 0x10e54120a <+0>: pushq %rbp0x10e54120b <+1>: movq %rsp, %rbp0x10e54120e <+4>: pushq %r150x10e541210 <+6>: pushq %r14 Target 0: (WeChat) stopped. -
在命令行輸入 po $arg3
/Users/xxxx/Library/Containers/com.tencent.xinWeChat/Data/Library/ApplicationSupport/com.tencent.xinWeChat/2.0b4.0.9/Backup/d9381f8bfa1ab8fa0f5e54b2858dffc3/EA2CC6CD-FCD2-4570-9CAC-9C95FF7E348B/Backup.db
-
以上可以得到微信數據庫的本地存儲地址,確切地來說是微信備份文件的存儲地址,往上一層文件夾就可以找到微信好友和聊天記錄數據庫
LLDB 獲取微信數據庫密碼
-
接著上面的操作在命令行里輸入 br set -n sqlite3_key
Breakpoint 2: 2 locations. -
輸入 memory read --size 1 --format x --count 32 $rsi
0x7fff78a12bc9: 0x69 0x6e 0x59 0x74 0x57 0x69 0x74 0x68 0x7fff78a12bd1: 0x50 0x61 0x34 0x68 0x3a 0x00 0x73 0x65 0x7fff78a12bd9: 0x74 0x53 0x70 0x65 0x65 0x64 0x4d 0x75 0x7fff78a12be1: 0x6c 0x74 0x69 0x70 0x6c 0x69 0x65 0x72 -
按照以下的步驟處理上面的輸出即可得到 64 位密碼:
-
只保留 : 右邊的數據
0x69 0x6e 0x59 0x74 0x57 0x69 0x74 0x68 0x50 0x61 0x34 0x68 0x3a 0x00 0x73 0x65 0x74 0x53 0x70 0x65 0x65 0x64 0x4d 0x75 0x6c 0x74 0x69 0x70 0x6c 0x69 0x65 0x72 -
刪掉所有的 0x
69 6e 59 74 57 69 74 68 50 61 34 68 3a 00 73 65 74 53 70 65 65 64 4d 75 6c 74 69 70 6c 69 65 72 -
刪掉所有的空格和換行
696e597457697468506134683a0073657453706565644d756c7469706c696572 -
以上就是打開數據庫的 64 位密碼啦,該密碼適用于聊天記錄,好友信息,群聊成員等各個數據庫
-
打開數據庫并重設密碼
微信存儲數據用的是輕量級的數據庫工具 SQLite ,有很多軟件可以打開,這里以 DB Browser for SQLite 為例。比如我雙擊聊天記錄數據庫 msg_0.db,會出現以下界面:
注意:
- Encryption settings 選擇 SQLClipher 3 defaults
- 右邊的密碼形式選擇 Raw key
- 密碼填寫 0x 加上之前獲取的 64 位密碼,所以一共是 66 位
如果正確操作的話,這里應該可以打開數據庫了。這里我們來看下數據庫的結構,這里可以看出這個數據庫里一共有 144 張表,每張表對應一個微信好友/群聊/公眾號的聊天記錄。
一個典型的表的屬性如下:
最重要的幾個屬性為:
- msgCreateTime 聊天時間戳
- msgContent 聊天內容
- messageType 聊天類型 文字為1
點擊瀏覽數據可以進行預覽:
為了避免每次打開都輸入密碼,我們可以移除數據庫的密碼。在 DB Browser for SQLite 中的具體操作是 工具 - 設置加密 - OK,即直接重設為空密碼,這樣也方便我們進一步提取數據。
本地存儲的微信數據庫里都有什么?
我翻了翻幾個文件夾,認為以下四項最有分析意義,當然還有其他的小伙伴們可以自行發掘。
- Contact - wccontact_new2.db - 好友信息
- Group - group_new.db - 群聊和群成員信息
- Message - {msg_0.db - msg_9.db} - 聊天記錄和公眾號文章
- Favorites - favorites.db - 收藏
微信好友/公眾號
首先來看聯系人數據庫 wccontact_new2.db,這里主要就是一張表 WCContact,這里面存儲了我們加的微信好友和關注的公眾號的信息,主要是昵稱和微信號 m_nsUsrName。一般公眾號以 gh_ 開頭。這里的 m_nsUsrName 非常重要,因為聊天數據庫里的表名都是 md5 編碼后的 m_nsUsrName。比如公眾號 廣發證券研究 的 m_nsUsrName 為 gh_24e4252623cf,md5 編譯后即為 41cbc56f1e10ab139339a40a4df2132d,因此聊天記錄數據庫里的 Chat_41cbc56f1e10ab139339a40a4df2132d 即為這個公眾號的全部歷史信息。
群聊/群成員
group_new.db 數據庫里有兩張表,分別是 GroupContact 和 GroupMember。其中 GroupContact 和微信好友數據庫很像,只不過里面存儲的是群聊名稱和群聊 m_nsUsrName。用法和聯系人數據庫一樣,都是通過對 m_nsUsrName 進行 md5 編譯索引到聊天記錄數據庫里的表。
GroupMember 表里包含了你加群里所有群成員的微信號和昵稱,不管有沒有加過好友。所以一般如果聯系人數據庫里有幾百個的話,這個表里往往有幾千上萬條記錄。
收藏
favorites.db 里面有 8 張表,其中最重要的是兩個,FavoriteItemTable 和 FavoriteSearchTable。
FavoriteSearchTable 給了收藏的標題和 localID。
FavoriteItemTable 給了收藏時間戳,收藏內容鏈接,以及收藏內容來源用戶等,并且可以和 FavoriteSearchTable 通過 localID 互相索引。
聊天記錄
見上文
如何解析數據庫并提取目標信息?
使用解密腳本打開數據庫:
from pysqlcipher import dbapi2 as sqlite output = 'output_db_whole.db' key = 'a3c77a9' conn = sqlite.connect(db) c = conn.cursor() c.execute("PRAGMA key = '" + key + "';") c.execute("PRAGMA cipher_use_hmac = OFF;") c.execute("PRAGMA cipher_page_size = 1024;") c.execute("PRAGMA kdf_iter = 4000;") c.execute("SELECT name FROM sqlite_master WHERE type='table'") c.execute("ATTACH DATABASE '" + output + "' AS db KEY '';") c.execute("SELECT sqlcipher_export('db');") c.execute("DETACH DATABASE db;") conn.close()從群聊數據庫里提取群聊列表,使用 transmd5 函數獲得群聊索引序號 md5 編碼,并寫入文本文件中:
import sqlite3 import time, datetime import hashlibconn = sqlite3.connect('group_new.db') print("Opened database successfully")def transmd5(string):m = hashlib.md5(string.encode(encoding='UTF-8')).hexdigest()return(m)def transfertime(timeStamp):timeArray = time.localtime(timeStamp)otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)return(otherStyleTime)cursor = conn.execute("SELECT m_nsUsrName, nickname, m_nsFullPY, m_nsChatRoomMemList from GroupContact") f = open('groupcontact.txt','w') for row in cursor:for i in range(3):f.write(row[i]+';')f.write(transmd5(row[0]))f.write('\n')print("Operation done successfully") conn.close()從以上結果里面,我們可以得到我們想要的群聊在數據庫中的 md5 編碼 Chat_aa309112204d5fd125c5a8bad609ff25。同時提取群聊成員列表,準備與聊天記錄進行合并:
conn = sqlite3.connect('group_new.db')cursor = conn.execute("SELECT m_nsUsrName, nickname from GroupMember") f = open('groupmember.txt','w') for row in cursor:for i in range(2):f.write(row[i]+';')f.write('\n')conn.close()由于聊天記錄被自動拆分到了 10 個數據庫文件 msg_0.db - msg_9.db 里,需要遍歷所有的數據庫文件獲取每個數據庫里所有的表名才能確定我們要的聊天記錄到底存儲在哪個文件里。
def sheetname(i):conn = sqlite3.connect('msg_%s.db'%i)return(conn)def getdata(conn):cursor = conn.execute("select name from sqlite_master where type='table'")tab_name=cursor.fetchall()tab_name=[line[0] for line in tab_name]return(tab_name)f = open('sheetname.txt','a') for j in range(10):conn = sheetname(j)tab_name = getdata(conn)for i in tab_name:f.write(i+','+'sheet_%s'%j+'\n')conn.close()經過這一步,我們可以精確定位想要的群聊到底在哪個數據庫文件里的哪張表里。于是可以從聊天數據庫里提取指定群聊信息的聊天記錄,轉換時間戳,使用正則表達式篩選符合指定信息的聊天記錄,并寫入文本文件中:
conn = sqlite3.connect('msg_0.db')def transfertime(timeStamp):timeArray=time.localtime(timeStamp)otherStyleTime=time.strftime("%Y-%m-%d %H:%M:%S",timeArray)return(otherStyleTime)cursor=conn.execute("SELECT msgCreateTime,messageType,msgContent from Chat_aa309112204d5fd125c5a8bad609ff25 where messageType=1")f = open('chatrecord.txt','w')for row in cursor:a = re.findall('[\d]+.*[\d]+\.[\d]',row[2].split(':')[-1])if a != []:f.write("%s;%s;%s;\n"%(transfertime(row[0]),row[2].split(':')[0],a))conn.close()
如果不通過正則表達式篩選的話,得到的就是文本格式的聊天記錄:
這里存在一個問題是,聊天記錄里只有每個人的微信號,沒有昵稱,因此我們需要把這張表和群聊成員表合并,并導出最終的結果:
import pandas as pd import csvdf1 = pd.read_table('/Users/mengjiexu/PycharmProjects/wx/chatrecord.txt',sep = ';') df1.columns = ['timestamp','wxindex','record',''] df2 = pd.read_table('/Users/mengjiexu/PycharmProjects/wx/groupmember.txt',sep = ';',error_bad_lines=False,quoting=csv.QUOTE_NONE) df2.columns = ['wxindex','nickname',''] df = pd.merge(df1,df2,on = 'wxindex',how='left') df.to_csv('resultspd.csv',mode ='w',encoding='gb18030')如果對自己的好友分布感興趣,還可以導出自己的好友和公眾號列表,并進行進一步的分析:
conn = sqlite3.connect('wccontact_new2.db')cursor = conn.execute("SELECT m_nsUsrName, nickname, m_nsFullPY, m_nsAliasName from WCContact") #df = pd.DataFrame(cursor, columns=['username','nickname','fullpy','aliasname']) #df.to_csv('contact.csv', sep=',', mode='a', encoding='utf8') f = open('contact.txt','a') for row in cursor:for i in range(3):f.write(row[i]+',')f.write('\n')conn.close()參考鏈接
總結
以上是生活随笔為你收集整理的如何优雅地管理微信数据库?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于Python SimpleHTTPS
- 下一篇: unity 模拟水下场景 水下冒泡