作者: 許夢潔 (Frankfurt School of Finance and Management)
E-mail: m.xu@fs.de
Stata連享會 課程主頁 || 直播視頻 || 知乎推文
掃碼查看連享會最新專題、公開課視頻和 100 多個碼雲計量倉庫連結。
主講嘉賓:司繼春 || 遊萬海
連享會-文本分析與爬蟲專題-4天直播Note: 本課程為直播/錄播課,直播後任何時間都可以購買課程,獲取所有電子課件。
目錄
最近每天在隔離點蹲著,發現隔離點的護士小姐姐每天兩次在群裡扒聊天記錄統計一兩百號人的體溫真是太南了,所以想寫個程序幫小姐姐自動收集,今天剛好隔離期滿,也算是給這段特殊的經歷留個紀念。
這篇文章主要內容是:
使用 DB Browser for SQLite 打開資料庫並重設密碼Contact - wccontact_new2.db - 好友信息Group - group_new.db - 群聊和群成員信息Message - {msg_0.db - msg_9.db} - 聊天記錄和公眾號文章Favorites - favorites.db - 收藏1. 如何找到微信本地緩存資料庫存放地址並獲取資料庫密碼1.1 捷徑對於Mac OS 系統,一個 short answer 是
/Users/xxx/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/
打開後,可以看到:
這裡需要重點關注的是看起來很像 md5 碼形式的文件,每個文件都代表一個曾經在你的電腦上登陸過並留下緩存的微信帳號,有了下面將會介紹的解碼方法,你可以逐個打開解析,確認到底哪個帳號是你要找的。
總體來說,Windows 系統同理。
1.2 LLDB 調試在沒有任何信息的情況下,我們如何找到一個獲取資料庫地址的系統性方法?答案在於LLDB斷點調試。
1.2.1 什麼是 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 是一個內建於 ISO 終端窗口的命令行調試器這意味著,使用 LLDB 我們可以通過打斷點來獲得正在運行的進程的後臺信息。這也是我們可以通過 LLDB 來尋找微信資料庫地址以及獲取訪問密碼的主要原因。
1.2.2 關閉SIP系統完整性保護系統完整性保護(SIP)是 OS X El Capitan 及更高版本所採用的一項安全技術,旨在幫助防止潛在惡意軟體修改 Mac 上受保護的文件和文件夾,但這也造成了安裝某些特殊版本軟體的或者做特殊修改的時候權限不足。在這裡就體現在使用 LLDB 調試時候,所有的調試語句都會被系統拒絕,因此在正式進行調試之前,一個重要的準備工作就是檢查系統完整性保護(SIP)的開啟狀態,如果開啟的話,要把它關閉。
檢查 SIP 的開啟狀態
在終端裡輸入 csrutil status 回車,如果看到:
System Integrity Protection status: enabled.
這說明的 SIP 已經開啟,如果要繼續調試的話,需要關閉。如果是 System Integrity Protection status: disabled. 則說明 SIP 已經處於關閉狀態,可以直接進行調試。
在終端中輸入 csrutil disable 回車,會出現
Successfully disabled System Integrity Protection. Please restart the machine for the changes to take effect.
記得在調試後按照同樣的步驟輸入 csrutil enable 重新開啟 SIP 惹
1.2.3 LLDB 獲取微信資料庫地址在命令行裡輸入 lldb -p $(pgrep WeChat)
(lldb) process attach --pid 77855
Process 77855 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #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, %r10
0x7fff6e878dff <+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.1
frame #0: 0x000000010e54120a WCDB`-[WCTDatabase(Database) initWithPath:]
WCDB`-[WCTDatabase(Database) initWithPath:]:
-> 0x10e54120a <+0>: pushq %rbp
0x10e54120b <+1>: movq %rsp, %rbp
0x10e54120e <+4>: pushq %r15
0x10e541210 <+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
以上可以得到微信資料庫的本地存儲地址,確切地來說是微信備份文件的存儲地址,往上一層文件夾就可以找到微信好友和聊天記錄資料庫
image-202004011836303941.2.4 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 位密碼啦,該密碼適用於聊天記錄,好友信息,群聊成員等各個資料庫
1.3 打開資料庫並重設密碼微信存儲數據用的是輕量級的資料庫工具 SQLite ,有很多軟體可以打開,這裡以 DB Browser for SQLite 為例。比如我雙擊聊天記錄資料庫 msg_0.db,會出現以下界面:
image-20200401184709226注意:
Encryption settings 選擇 SQLClipher 3 defaults密碼填寫 0x 加上之前獲取的 64 位密碼,所以一共是 66 位如果正確操作的話,這裡應該可以打開資料庫了。這裡我們來看下資料庫的結構,這裡可以看出這個資料庫裡一共有 144 張表,每張表對應一個微信好友/群聊/公眾號的聊天記錄。
image-20200401184925323一個典型的表的屬性如下:
最重要的幾個屬性為:
點擊瀏覽數據可以進行預覽:
image-20200401185305159為了避免每次打開都輸入密碼,我們可以移除資料庫的密碼。在 DB Browser for SQLite 中的具體操作是 工具 - 設置加密 - OK,即直接重設為空密碼,這樣也方便我們進一步提取數據。
image-202004011854435952. 本地存儲的微信資料庫裡都有什麼?
我翻了翻幾個文件夾,認為以下四項最有分析意義,當然還有其他的小夥伴們可以自行發掘。
Contact - wccontact_new2.db - 好友信息Group - group_new.db - 群聊和群成員信息Message - {msg_0.db - msg_9.db} - 聊天記錄和公眾號文章Favorites - favorites.db - 收藏2.1 微信好友/公眾號首先來看聯繫人資料庫 wccontact_new2.db,這裡主要就是一張表 WCContact,這裡面存儲了我們加的微信好友和關注的公眾號的信息,主要是暱稱和微信號 m_nsUsrName。一般公眾號以 gh_ 開頭。這裡的 m_nsUsrName 非常重要,因為聊天資料庫裡的表名都是 md5 編碼後的 m_nsUsrName。比如公眾號 廣發證券研究 的 m_nsUsrName 為 gh_24e4252623cf,使用 32 位 md5 編譯後即為 dec04a5f755c79d776498462a9f92292,因此聊天記錄資料庫裡的 Chat_dec04a5f755c79d776498462a9f92292 即為這個公眾號的全部歷史信息。
image-202004011859596932.2 群聊/群成員group_new.db 資料庫裡有兩張表,分別是 GroupContact 和 GroupMember。其中 GroupContact 和微信好友資料庫很像,只不過裡面存儲的是群聊名稱和群聊 m_nsUsrName。用法和聯繫人資料庫一樣,都是通過對 m_nsUsrName 進行 md5 編譯索引到聊天記錄資料庫裡的表。
image-20200401195131067GroupMember 表裡包含了你加群裡所有群成員的微信號和暱稱,不管有沒有加過好友。所以一般如果聯繫人資料庫裡有幾百個的話,這個表裡往往有幾千上萬條記錄。
image-202004011952590142.3 收藏favorites.db 裡面有 8 張表,其中最重要的是兩個,FavoriteItemTable 和 FavoriteSearchTable。
image-20200401195806062FavoriteSearchTable 給了收藏的標題和 localID。
image-20200401200058596FavoriteItemTable 給了收藏時間戳,收藏內容連結,以及收藏內容來源用戶等,並且可以和 FavoriteSearchTable 通過 localID 互相索引。
image-202004012002295622.4 聊天記錄見上文
3. 如何解析資料庫並提取目標信息?使用解密腳本打開資料庫:
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 hashlib
conn = 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 csv
df1 = 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()
關於我們Stata連享會 由中山大學連玉君老師團隊創辦,定期分享實證分析經驗。🎦 直播間 http://lianxh.duanshu.com 有很多視頻課程,可以隨時觀看 (🆓 公開課-「直擊面板數據」已經被觀看 1000+ 次)。🌺 你的頸椎還好嗎? 為了保護頸椎,您可以將 🍎 連享會-主頁 lianxh.cn 和 知乎專欄 (在知乎中搜索「連享會」即可) 添加到您的收藏夾,以便隨時查看往期推文。📁 公眾號推文分類查看: 歷史推文分為 內生性 | 空間計量 | 時序面板 | 結果輸出 | 交乘調節 五類,主流方法介紹一目了然:DID, RDD, IV, GMM, FE, Probit 等。只需進入公眾號,點擊菜單即可查看。💥 - 公眾號關鍵詞搜索/回復 功能已經上線。大家可以在公眾號左下角點擊鍵盤圖標,輸入簡要關鍵詞,以便快速呈現歷史推文,獲取工具軟體和數據下載。常見關鍵詞:課程, 直播, 視頻, 客服, 模型設定, 研究設計,stata, plus,Profile, 手冊, SJ, 外部命令, profile, mata, 繪圖, 編程, 數據, 可視化DID,RDD, PSM,IV,DID, DDD, 合成控制法,內生性, 事件研究交乘, 平方項, 缺失值, 離群值, 縮尾, R2, 亂碼, 結果Probit, Logit, tobit, MLE, GMM, DEA, Bootstrap, bs, MC, TFP面板, 直擊面板數據, 動態面板, VAR, 生存分析, 分位數空間, 空間計量, 連老師, 直播, 爬蟲, 文本, 正則, pythonMarkdown, Markdown幻燈片, marp, 工具, 軟體, Sai2, gInk, Annotator, 手寫批註歡迎加入Stata連享會(公眾號: StataChina)