逆向WeChat(四)

bbqz007發表於2024-05-28

本篇在部落格園地址https://www.cnblogs.com/bbqzsl/p/18209439

mars

先回顧一下,在上兩篇《WeUIEngine》《EventCenter》。我對wechat如何使用chrome::base框架的分析中需要更正補充。首先要指出,逆向分析是一個過程。需要經過不斷假設,推斷,求證,驗證。花費時間一步一步將結果改進完善的循序漸進的過程。當前的內容只代表當前分析的過程跟結果,當前的分析是以當前侷限的結果作為前提的。還在逆向分析初期,我只執行少量功能場景,忽略了TaskScheduler除了Pump外。還有就是wechat在初始化時,已經將主執行緒的TaskRunner儲存到了全域性變數,用作主執行緒的dispatch_main_queue,供全域性使用。但是EventCenter仍然是中心。

回到本篇內容,mars。

在逆向分析,我發現了StartTask的實際程式碼,將stn::Task進行了十次copy構造。一個320位元組的結構,有10多個string,幾個vector<string>,一個map<string,string>。佐證在本篇後面。

作為network模組,先來看SocketSelect。windows基於WASSelect,macos基於kevent,linux android它也看作unix,這個有點偷懶。對於看熱鬧的人來說linux是類unix系統,但是對於unix系統使用者就不買這個帳,unix跟linux就是兩個東西。macos是基於freebsd,是unix分支,核心用kqueue,macos的MachPort就是基於kqueue。linux有它獨用的epoll,好像也始於2.6核心的時代,核心並沒kqueue。mars在非macos的系統中通通由poll來解決。這不是問題的關鍵。因為每一個tcp連線開一個執行緒,沒有事件分離器複用。我也是驚了個呆。難怪低配置的安卓手機直接用不起,記得N年前28nm的曉龍425還可以執行微信6,微信7後因為舊版登陸介面不支援,強制升級微信後只能卡死在登陸進行轉進入主介面。一個28nm的4cores1.4g的舊cpu且只帶2GB運存的破手機,在2024年還可以執行最新版淘寶,大部分功能。不知道這東西,是不是如專案描述那樣,在它自家的產品中跨平臺使用了。freebsd,darwin-xnu,linux-kernel在github都有原始碼。

window需要vc2015,那麼姑且需要c++14,clang3.4於2013年就支援了,xcode跟ndk同樣適用。c++14已經有不少boost庫的東西,而且lambda也很成熟了。用boost::bind看著真的頭暈。年輕時我也曾非boost不夠cool。boost::bind(method, this, _1, _2, _3, _4, _5, _6),用python言語就是lambda a,b,c,d,e,f: this.method(a, b, c, d, e, f),用c++11的lambda就是[=](auto& a, auto& b, auto& c, auto& d, auto& e, auto& f) { return this->method(a, b, c, d, e, f); },以及std::function<auto(, , , , , , )>。boost::bind也就等同lambda的一種實現方式。除錯有boost的專案時有個無盡長的名字我卻不知道你是什麼的惡夢。

在mars的設計中,mars既是namespace也是目錄,mars下一級就是子模組,同樣既是namespace也是目錄,如mars::comm,mars::sdt,mars::stn。這裡不能不說是在參考了java的包層級。

stn,我認為就是簡單的首字命名,Strategy,Task,Network。重點就是network,這是一個network模組。至於Strategy就簡單理解成ShortLink,LongLink。Task,原本咋一看還以為comm::mq分派Task,這個stn是一個分派中心,包括Task還有socket操作,Strategy類似於scheduler。實質卻不是這樣。Task就是在一個ShortLink或LongLink上完成一輪Request-Response。這個Task的設計就類似於js的XMLHttpRequest。js中,xhr = new XMLHttpRequest; xhr.onreadystatechange= function() {} ; xhr.open(); xhr.send(request_packet)。再來看mars,tsk = new Task(request_packet); tsk.OnEndTask = lambda; mars::stn::StartTask(tsk); 同一配方。所以我認為stn就是mars核心的network模組。

至於comm目錄下的東西。socket跟network子目錄只是些底層socket相關的api跟資料結構的封裝,Facade模式。thread子目錄轉到平臺對應的子目錄。也是同步,TSS等相關的Facade封裝。messagequeue子目錄,我原來以為會是mars的重要的東西,跟它的執行緒模型會的密切關聯。這個messagequeue的設計,就是在模仿Java的RunLoop。概念是一個執行緒對應一個messagequeue,執行緒id作為佇列id。由繫結的MessageHandler處理message,只能用MessageHandler投遞訊息。在java中,handler = new Handler(Loop()); handler.SendMessage();handler.post();。回到mars上。messagequeue必須加上一個RunLoop構成一個Pump線上程中pump。MessageQueueCreater是一個自帶執行緒以及Pump的類,它存在於stn::NetCore,comm::GetDefMessageQueue,還有comm::GetDefTaskQueue。如果你沒有InstanllAsyncHandler去安裝一個MessageHandler專門將message作為runnable處理,就是有Pump線上程執行也不分派runnable。幾乎stn內每個功能類都有一個預設到comm::GetDefMessageQueue的MessageHandler,用來進行AsyncInvoke,但又似乎stn都基本上沒有怎麼用。(分別只這幾處使用:Alarm.cc, BroadcastMessage。signalling_keeper.cc, AsyncInvokeAfter。zombi_task_manager.cc SingletonMessage。netsource_timechecker.cc, AsyncInvokePeriod。ActiveLogic::OnForeground, AsyncInvoke。)。並非,comm::mq::Callback,是可以繫結MessageHandler,用來Handler2Queue投遞AsyncInvoke。還有SYNC2ASYNC宏,所以shortlink跟longlink的OnResponse是AsyncInvoke進行的。還有ASYNC_BLOCK_START宏,StartTask也是AsyncInvoke進行的。整個設計有一個核心關鍵字,就是RunLoop,所有東西都要繞進來,什麼類幾乎都有一個RunLoop函式。還有就是Breadker,它為Condition又起了一個名。所以有這麼一個函式BreakMessageQueueRunloop,從字面上還以為要中斷一個執行緒的RunLoop,但實質是在做相反的事,breaker->notify(),快喚醒那個執行緒。這個RunLoop跟CFRunLoop,CFSocket的程式設計好像一個性子。

還有就是,所有執行緒跟messagequeue共用一個mutex。

* 兩個執行緒的runloop居然還要競爭一個鎖,然後才能處理自己的mq。
* mqA的runloop居然還要跟mqB的postmessage競爭一個鎖。
* mqA的postmessage居然還要跟mqB的postmessage競爭一個鎖。

既然mars是重度使用boost的,不如直接用asio。檢視它的boost版本原來是1.60,boost在1.66才將asio重構成我們今天熟悉的executor。我寫過一下關於1.66的boost.asio框架設計

逆向,我扯遠了。我只關心應用層的介面就好了。

下面就是應用層的主要介面,還有互動流程。

現在逆向看看閉源的部分有哪些

先來解讀一下目錄結構

目錄參考了java包目錄結構。

mars是一級包名。下面的子包app,cdn,magicbox,mmdns,sdt,smc,stn。

每個子包,有一個同名的標頭檔案,定義類結構。有一個同名的logic標頭檔案,匯出子包的介面,包括模組生命週期hooking (onCreate,onDestroy),logic.cc初始模組。子包裡的功能單元稱為Manager。所有功能的實現體單元在子包的src子目錄。

目錄 mars stn src
namespace mars:: mars::stn
StnManager ShortLink LongLink ShortLinkManager LongLinkManager
檔案 stn.h, stn_logic.h, stn_maanger.h ShortLink.cc LongLink.cc ShortLinkManager.cc LongLinkManager.cc

再整理一下模組的設計思想,logic是包的介面層,manager是承接層,core是服務層。應用層透過logic呼叫包的服務,經過manager下達core。包內服務透過core呼叫應用層的服務,經由manager的callback集回撥應用層安置的服務。實現過程是否嚴格貫徹是另一回事。應用層跟包內都有直接使用manager。

我在逆向分析StartTask時,發現了一個問題,mars::stn::Task總計做了10次copy構造,WTF。

在NetCore::StartTask入口準備AsyncInvoke時,一次

在Message構建過程,總計八次,其中有一個any_cast出來的AysncInvovkeFunction賦值函式無意思地進行了七次。typedef boost::function<void()> AsyncInvokeFunction;

在執行體裡面,再一次,就是第十次。

你沒有逆向過,也不知道原來這麼funny。他們到底有沒有跟蹤過自己的程式碼,問號?似乎單靠xlog發現不了問題。沒經過逆向分析誰也不清楚any或boost會編譯出來什麼,也不會在意這些小事的。傳參用引用也不是萬事大吉的。

本期先到這裡,下期再見。

相關文章