使用x64dbg分析微信並獲取所有聯絡人資訊
*本文原創作者:lant1e,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載
引言
上回我們完成了 微信中對發信息的自動呼叫 ,這次我們接著分析微信的聯絡人資訊,完成對聯絡人、群資訊的獲取。完整程式碼會放在github上: https://github.com/15pb/wechat_tools 。
藍鐵在逆向分析時的三種分析思路
我們在逆向工程中,分析方法一般是多種方法嘗試。以分析微信聯絡人列表為例,我們可以從三方面入手來分析:
自上而下分析法此方法是從介面開始分析,也就是從視窗回撥函式開始分析介面相關的程式碼,從介面中獲取到聯絡人資訊,這種分析方法如果對UI庫(微信用的是DuiLib庫)比較熟悉的話,分析起來會比較好一些。
自下而上分析法此方法是從API的呼叫開始,向上棧回溯分析,一般在我們破解一些有註冊彈窗的程式會比較好下斷點,而分析微信聯絡人列表沒有彈窗資訊,所以這塊分析起來需要猜測API,再分析。
字串分析法此方法可以說在任何時候都可以進行分析,比如分析聯絡人列表,可以從 Contact
或 ContactList
入手開始分析。
在分析微信聯絡人資訊時,我採用了三種方法相結合的方式,起初使用的是自下而上分析法,從API入手,因為在上一節對微信發信息的函式分析時,是一步一步向上棧回溯分析的,想的也可以採用類似的方式,但在跟蹤過程中耗費了大量時間找資訊,並沒有找到穩定的資料列表。所以最終放棄這種方法,而從UI層分析,需要先對UI庫熟悉一下,而現在一心只想獲取資料,不想學UI,所以只能用字串分析法了。而使用字串分析,發現也是沒有結果。回過頭來,依然使用API下斷的方式,這時由於前面分析和跟蹤的過程中,已經對微信的程式碼熟悉了,發現微信中所有的資訊分為兩種方式儲存,第一種是UI上會有儲存,第二種是資料庫儲存,第一種PASS,選擇第二種最終找到了關鍵函式。下面就是我嘗試的兩種方式。
使用字串分析法分析無果
首先使用x64dbg附加正在執行的微信,在符號選項卡中選擇微信的通訊邏輯模組 wechatwin.dll
:
然後搜尋當前模組的字串,在字串中過濾
Contact
:
會發現
Contact
字串還是很多的,換另一個 ContactList
過濾,發現少了一些。
仔細觀察過濾出的字串,有幾個看起來可能有關,我試了兩個,最終並沒找到相關程式碼。寫出分析的過程主要就是告訴大家逆向本不是一帆風順,有時在分析時會無功而返,在這個時候需要耐心,總會有出路的!
巧用sql語句執行函式找到獲取聯絡人資訊函式
在分析發信息的函式時,底層找到了執行sql語句的函式,由於執行這個函式會多次呼叫,經過下斷觀察,發現有和聯絡人相關的sql語句。(藍鐵提示:注意獲取聯絡人資訊的程式碼是在微信掃碼之後執行的,所以需要在微信掃碼之前使用x64dbg附加程式並下斷點)
在動態跟蹤的過程中,在上面程式碼斷下後的上兩層程式碼中,又一次呼叫了Sql執行函式,sql語句和上圖基本一致,再繼續跟蹤的過程中,發現了獲取微信ID的函式。
繼續向下跟蹤時,找到了獲取微訊號資訊的函式。
經過跟蹤,發現這段程式碼是一個迴圈,每次獲取到微訊號資訊不一樣,並且還有群資訊。如果我們能自己實現這個迴圈就能完成獲取聯絡人以及群資訊。但看上下文函式呼叫比較多。所以採用hook的方式對上圖中呼叫的函式進行Hook是一種比較好的方法。hook的點在此:
0CCBA0C6| E8 45 0D 00 00| call <wechatwin.sub_CCBAE10>| 獲取微訊號資訊,hook點
巧用inline hook提取聯絡人以及群名稱
我們知道一般 inline hook
是在函式的開始處進行Hook,而在這個程式碼中發現我們要Hook的這個CALL引用的地方是比較多的,也就是說有很多地方都呼叫了這個函式,所以我們採用的方式是隻hook我們呼叫的這個點。(藍鐵提示:在x64dbg中,選中函式開始處,使用快捷鍵x,可以檢視所有引用這個函式的程式碼
)我們hook的程式碼是將
0CCBA0C6
處程式碼 call 獲取微信資訊
改成我們自己的函式,然後在我們自己的函式中儲存微信資訊,之後再跳過來。原理圖如下:
原理有了剩下的就是寫程式碼,不過寫程式碼前需要先分析一下函式呼叫的引數以及我們儲存的引數的結構,即微信聯絡人資訊的結構。經過多次呼叫觀察堆疊資訊,得出了以下結果。
call引數: 第一個是傳出緩衝區,獲取微信聯絡人資訊 第二個是sql執行返回的物件,即查詢物件或結果集物件 我們關注的是第一個,呼叫之後會得到聯絡人資訊 傳出緩衝區即為傳出物件 +0x8微信ID +0x1C 微訊號 +0x48 標誌位,1代表聯絡人,2代表群,3代表服務號 +0x4C 0x0 代表個人,0x8代表普通訂閱號,0x18 代表訂閱、服務號,0x1C 代表系統服務 +0x50 微信備註 +0x64 服務名稱或微信名稱() +0xA4 拼音簡稱(全大寫) +0xB8 拼音名稱(全拼小寫)
Hook的關鍵程式碼:
// 1. 計算虛擬地址,模組基地址+RVA地址 HMODULE hMod = ::GetModuleHandle(L"wechatwin.dll"); DWORD dwEIPAddr = (DWORD)hMod + 0x3AA0C6; DWORD dwCallAddr = (DWORD)hMod + 0x3AAE10; // 2. 修改程式碼屬性 DWORD dwOldType = 0; ::VirtualProtect((LPVOID)dwEIPAddr, 5, PAGE_EXECUTE_READWRITE, &dwOldType); // 3. 讀取程式碼 DWORD dwRead; HookData stcReadData; ::ReadProcessMemory((HANDLE)-1, (LPVOID)dwEIPAddr, &stcReadData, 5, &dwRead); // 4. 寫入程式碼 HookData stcWriteData(0xE9, dwEIPAddr, (DWORD)NakeHookCall); ::WriteProcessMemory((HANDLE)-1, (LPVOID)dwEIPAddr, &stcWriteData, 5, &dwRead); // 5. 恢復屬性 ::VirtualProtect((LPVOID)dwEIPAddr, 5, dwOldType, &dwOldType); // 6. 記錄原函式返回地址 g_dwRetAddr = dwEIPAddr + 5;
從原函式跳轉到的Hook程式碼
DWORD g_dwRetAddr; // 用於儲存返回到原函式的地址 void _declspec(naked) NakeHookCall() { _asm { mov g_OutObj, esi// 儲存引數1,從這個值中獲取微信資訊 mov eax, g_hMod;// add eax, 0x3AAE10;// 計算呼叫函式 call eax// 呼叫獲取微信聯絡人資訊函式 mov eax, SaveInfo call eax// 呼叫自己的函式儲存微信聯絡人資訊 push g_dwRetAddr// 跳轉回原函式 ret } }
儲存聯絡人的程式碼:
vector<MyWXContactInfo> g_vector; // 儲存微信聯絡人 void SaveInfo() { // 執行到Hook點,每次都會呼叫這個函式儲存微信聯絡人 MyWXContactInfo obj(g_OutObj); g_vector.push_back(obj); }
自己定義的微信聯絡人類:
class MyWXContactInfo { public: // +0x8微信ID // +0x1C 微訊號 // +0x48 標誌位,1代表聯絡人,2代表群,3代表服務號 // +0x4C 0x0 代表個人,0x8代表普通訂閱號,0x18 代表訂閱、服務號,0x1C 代表系統服務 // +0x50 微信備註 // +0x64 服務名稱或微信名稱() // +0xA4 拼音簡稱(全大寫) // +0xB8 拼音名稱(全拼小寫) MyWXContactInfo(DWORD dwObj) { m_wxID = *(wchar_t**)(dwObj + 0x8); m_wxNum = *(wchar_t**)(dwObj + 0x1C); m_wxFlag1 = *(DWORD*)(dwObj + 0x48); m_wxFlag2 = *(DWORD*)(dwObj + 0x4C); m_wxBeizhu = *(wchar_t**)(dwObj + 0x50); m_wxName = *(wchar_t**)(dwObj + 0x64); } wchar_t* PrintString() { CString strObj; strObj.Format(L"ID:%s NUM:%s f1:%p f2 %p,Beizhu:", m_wxID, m_wxNum, m_wxFlag1, m_wxFlag2); if (m_wxBeizhu) { strObj += m_wxBeizhu; strObj += " "; } if (m_wxName) { strObj += m_wxName; strObj += " "; } OutputDebugString(strObj.GetBuffer()); return strObj.GetBuffer(); } public: wchar_t* m_wxID;// +0x8微信ID wchar_t* m_wxNum; // +0x1C 微訊號 intm_wxFlag1;// +0x48 標誌位 intm_wxFlag2;// +0x4C 0x0 wchar_t* m_wxBeizhu;// +0x50 wchar_t* m_wxName;// +0x64 };
除此之外,當然獲取資訊之後,還需要進行過濾,經過人肉分析之後,發現微信聯絡人結構中 0x48
和 0x47
是兩個標誌,可以使用這兩個標誌過濾群列表和聯絡人列表。
測試結果
測試程式碼還是寫在了DLL中,所以測試的順序是:
1.開微信
2.注入DLL
3.在測試對話方塊中點選按鈕 Hook獲取聯絡人
4.掃碼登入微信
5.開啟DebugView
6.在測試對話方塊中點選按鈕 獲取群列表
7.在測試對話方塊中點選按鈕 獲取聯絡人列表
總結
在分析微信聯絡人過程中,我們會發現,從一個商業軟體中獲取一組資訊還是比較麻煩的,最終我們使用了Hook這種方式完成了功能,而寫程式碼和分析兩者其實做好都比較難,所以合格的逆向工程師,兩者能力都應該具備!
*本文原創作者:lant1e,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載