首發地址: https://mp.weixin.qq.com/s/Nik8fBF3hxH5FPMGNx3JFw
前言
最近想寫一個免費的微信公眾號自動採集的工具,我看公眾號文章下載需求還挺多的。搜了下github,免費的工具思路大多都是使用瀏覽器開啟公眾號主頁獲取到需要的請求引數,例如key、uin等,然後再用引數請求歷史。
最佳化
這些工具都需要自己複製公眾號主頁的連結發給檔案傳輸助手然後開啟,才能攔截到請求引數,那麼能不能讓這一步也讓程式來完成呢?
簡單的方式可以使用模擬點選,這個有興趣的自行實現。這篇文章我說下基於逆向的方式在微信內建瀏覽器開啟某個連結,這樣實現可以更自動化一點,下面是具體的逆向和分析過程。
其他採集方案
在之前的一篇文章裡我也說了其他一些採集方案,例如微信公眾平臺、微信讀書等,有興趣的可以看【Python微信機器人】寫一個監控採集公眾號文章的外掛。不過聽說微信讀書已經開始加密了,而且採集的歷史也不是很全,較早的文章可能獲取不到。
對於微信公眾平臺來採集的可以參考aardio爬蟲) 實戰篇:採集自己的公眾號粉絲列表,原理基本類似,都是先掃碼獲取到cookie,請求對應的介面,公眾平臺的介面都沒加密。
還有基於逆向hook的採集方案,這個暫不公開。如果只是平時下載一下公眾號文章的話,上面兩種足夠用了
方案優缺點
攔截引數對比逆向來說優點在於不需要固定微信的版本,更適合普通使用者。因為逆向hook只能適用於特定版本,想不限版本,需要為每個版本做一遍適配,這明顯不太可能。
而且基於逆向的方式採集還有可能有封號的風險,對於只想下幾篇公眾號文章的人來說,這個代價還是挺大的。當然逆向採集優點也很明顯:穩定、無需人工操作、採集的資料量更多等。
逆向過程
下面逆向的版本選擇的是3.9.6.32
,選這個版本主要是之前的寫的機器人用的這個版本,很多東西有現成的。最新的也沒變,想看64位最新版的翻到最後,也會提幾句。
日誌定位
微信定位一個功能call最好也最方便的方式就是日誌(肯定不是因為我只會這個)。開啟連結的地方有多處,比如以下兩個:
還有就是點選聊天記錄裡的連結來開啟,這三種方式都試一下,看看日誌之間有什麼共同點,這樣更容易定位到關鍵日誌點。
前兩個都有openUrlWithExtraWebview
,看名稱應該就是微信在呼叫開啟瀏覽器,但是這裡還處於比較上層的位置,如果你在這裡打斷點的話,會觸發多次,不止開啟瀏覽器才觸發,並且看了下引數比較複雜,有一堆控制代碼和回撥不好處理,所以這裡先不考慮了。
WebViewMgr:user setting
這個日誌比較重要,因為三個日誌裡都有它,而且正好處於openUrlWithExtraWebview
和AddTab
的中間位置,看上去像是開啟瀏覽器時初始化配置。
感覺在這裡打上斷點應該能在呼叫堆疊裡找到開啟瀏覽器的call。先在IDA裡搜尋user setting
(x64dbg裡搜尋這個字串也可以,我比較喜歡看IDA,有偽C程式碼看比彙編直觀一點),然後定位到下面的位置:
接著翻到函式頭,在x64dbg裡函式頭的位置打上斷點,接著開啟一個連結讓斷點斷下
右鍵右下角堆疊的返回地址選擇在反彙編中轉到指定DWORD
(也就是函式呼叫的地方),然後在IDA中檢視這個地址,翻到函式頭的位置看到了比較關鍵的日誌資訊(showWebView
),在函式頭繼續打上斷點
檢視呼叫的位置,先看看IDA裡這裡所在的函式在做些什麼操作,看上去是在解析json,而且看到了日誌有OpenUrlWithExtraWebviewHandler
,看名稱這裡就是處理OpenUrlWithExtraWebview
事件的回撥,那前面解析json就是在解析之前日誌裡的json
在頭部打上斷點,看看傳入的引數是不是就是之前的json。如下圖,雖然引數不是之前看到的json字串,但是和json內容基本一樣,估計上層函式又對json做了解析,那這裡肯定是處理OpenUrlWithExtraWebview
事件的回撥函式,也就是在這個函式里開啟的連結
那關鍵位置基本就是上面提到的showWebView
函式了,下面開始分析函式的引數和幾個需要呼叫的call。
分析引數
這裡只有ecx是未知的,看了下ebp-0x90C
在上面就有,估計是上面賦值的,打上斷點看一下,一般複製的結構體都有函式可以生成,不需要去關心怎麼構造。當然,如果構造的call離的太遠且結構體也不負責的話也可以自己構造。
分析的結果如上,這裡沒什麼複雜的引數,只需要傳一個url就可以。前面在檢視函式引用的時候,看到有的地方呼叫6CC52610
時不是push的這幾個值,也就是說這四個值並不是固定的,而是用於控制某些變數。
比如用系統預設瀏覽器開啟則是下面的引數:
可能的組合(每行一種,最後一位是edx)
0 1 0 0 4
0 1 1 0 4
0 1 0 0 2
0 1 0 1 4
0 1 0 0 0
0 1 0 1 2
1 1 0 0 4
0 1 1 0 5
0 1 1 0 8
1 1 0 0 0
1 1 0 0 4
可以自己都測試一遍,不過看日誌有的可能是開啟小程式相關的,引數不一樣也許會崩潰,具體案例具體分析吧。還有一個點,呼叫這四個call,堆疊不平衡,需要加上add esp, 0x10;
(不一定這麼處理,也可能是少call,單步走看看到哪個call平棧了),我看其他地方呼叫這個call都是使用的add esp, 0x10;
,所以猜測這裡加上這個也能執行。下面還有一個call是釋放ebp-0x90C
結構體裡的記憶體,也需要呼叫一下。
完整程式碼
DWORD ShowVebView(wchar_t* url) {
size_t urlLen = wcslen(url);
DWORD WeChatWinBase = GetWeChatWinBase();
DWORD dwCall1 = WeChatWinBase + 0x77A430;
DWORD dwCall2 = WeChatWinBase + 0xF67310;
DWORD dwCall3 = WeChatWinBase + 0x76CC70;
DWORD dwCall4 = WeChatWinBase + 0x1602610;
DWORD dwCall5 = WeChatWinBase + 0x77A790;
DWORD ebp_0x90C[0x500] = { 0 };
__asm {
pushad;
lea ecx, ebp_0x90C;
call dwCall1;
push urlLen;
push url;
lea eax, ebp_0x90C;
lea ecx, [eax + 0x56C];
call dwCall2;
call dwCall3;
push 0x0;
push 0x1;
push 0x0;
push 0x0;
xor edx, edx;
lea ecx, ebp_0x90C;
call dwCall4;
add esp, 0x10;
lea ecx, ebp_0x90C;
call dwCall5;
popad;
}
return 0;
}
編譯成dll注入到程序,呼叫ShowVebView
正常開啟連結。
64位
以能下載到的最新版為例(3.9.10.27
),其實32位和64位微信邏輯是一樣的,同樣在x64dbg裡搜尋user setting
,在引用的函式頭打斷點找到呼叫點,然後接著在函式頭打斷點找到呼叫點,關鍵位置如下圖(基址是00007FF86A160000
):
邏輯一模一樣,只是64位無法內聯彙編,可以用函式指標來呼叫。大概程式碼如下:
typedef UINT64 (*dwCall1Ptr)(UINT64);
dwCall1Ptr dwCall1 = (dwCall1Ptr)0x7FF86BD82C70;
DWORD rbp_0x100[0x500] = {0};
UINT64 addr = dwCall1(&rbp_0x100);
typedef UINT64 (*dwCall2Ptr)(UINT64,wchar_t*,UINT64);
dwCall2Ptr dwCall2 = (dwCall2Ptr)0x7FF86C840C10;
wchar_t* url = (wchar_t*)L"";
dwCall2(addr+0x***, url, wcslen(url));
typedef UINT64 (*dwCall3Ptr)();
dwCall3Ptr dwCall3 = (dwCall3Ptr)0x7FF86BD72AE0;
dwCall3();
typedef UINT64 (*dwCall4Ptr)(UINT64,UINT64,UINT64,UINT64,UINT64,UINT64);
dwCall4Ptr dwCall4 = (dwCall4Ptr)0x7FF86D24E9B0;
dwCall4(addr, 0,0,0,1,0);
typedef UINT64 (*dwCall5Ptr)(UINT64);
dwCall5Ptr dwCall5 = (dwCall5Ptr)0x7FF86BD82EA0;
dwCall5(addr);
上面只是虛擬碼,需要自己除錯改成能執行的程式碼。
本文由部落格一文多發平臺 OpenWrite 釋出!