1. 程式人生 > >MacOS微信逆向分析-Frida

MacOS微信逆向分析-Frida

## MacOS微信逆向分析-Frida ### 0.前言 ------ PC下的微信二次開發相信大家都會了,那麼本篇文章將帶領大家使用Frida框架對Mac下微信來進行**二次開發**! PS:還有一種靜態注入的方式也不錯,但是考慮到大家xcode安裝包太大就不在這裡展開啦。 PS:frida如何去使用大家得自己去學,本文不過多展開。 主要功能涉及如下: 1. 微信訊息傳送 2. 微信訊息監聽 ### 1.微信版本 ------ ![image-20210215202805857](https://images.cnblogs.com/cnblogs_com/Huerye/1068060/o_210215144110image-20210215202805857.png) ### 2.工具 ------ 預先善其事,必先利其器!請先準備如下分析工具 1. [*Hopper* Disassembler](https://www.hopperapp.com/) 2. [Class-dump](http://stevenygard.com/projects/class-dump/) 3. [Frida](https://frida.re/) 4. [Pycharm](https://www.jetbrains.com/)(可選) 5. [Vscode](https://code.visualstudio.com/)(可選) ### 3.**Dump 出頭檔案** ------ 首先利用Class-Dump拿到微信的標頭檔案,開啟終端執行: ```shell class-dump -H /Applications/WeChat.app ``` 成功執行之後會生成很多的標頭檔案了,如下所示 ```shell -rw-r--r-- 1 n staff 927B 2 15 19:19 WXCPbQcwxtalkPackage.h -rw-r--r-- 1 n staff 975B 2 15 19:19 WXCPbReportItem.h -rw-r--r-- 1 n staff 1.7K 2 15 19:19 WXCPbSCAddVoiceGroupMemberResp.h -rw-r--r-- 1 n staff 772B 2 15 19:19 WXCPbSCCancelCreateVoiceGroupResp.h -rw-r--r-- 1 n staff 7.2K 2 15 19:19 WXCPbSCCreateVoiceGroupResp.h -rw-r--r-- 1 n staff 6.9K 2 15 19:19 WXCPbSCEnterVoiceRoomResp.h -rw-r--r-- 1 n staff 1.1K 2 15 19:19 WXCPbSCExitVoiceRoomResp.h -rw-r--r-- 1 n staff 1.2K 2 15 19:19 WXCPbSCModifyVoiceGroupInfoResp.h -rw-r--r-- 1 n staff 872B 2 15 19:19 WXCPbSCSubscribeLargeVideoResp.h -rw-r--r-- 1 n staff 867B 2 15 19:19 WXCPbSCSubscribeVideoResp.h -rw-r--r-- 1 n staff 2.0K 2 15 19:19 WXCPbSCVoiceClientSceneReportResp.h -rw-r--r-- 1 n staff 864B 2 15 19:19 WXCPbSCVoiceGetGroupInfoBatchResp.h -rw-r--r-- 1 n staff 637B 2 15 19:19 WXCPbSCVoiceMemberWhisperResp.h -rw-r--r-- 1 n staff 5.9K 2 15 19:19 WXCPbSCVoiceRedirectResp.h -rw-r--r-- 1 n staff 1.1K 2 15 19:19 WXCPbSCVoiceRoomHelloResp.h -rw-r--r-- 1 n staff 904B 2 15 19:19 WXCPbSKBuiltinBuffer_t.h -rw-r--r-- 1 n staff 686B 2 15 19:19 WXCPbSubscribeVideoMember.h -rw-r--r-- 1 n staff 2.7K 2 15 19:19 WXCPbSwitchVideoGroupResp.h -rw-r--r-- 1 n staff 1.4K 2 15 19:19 WXCPbVideoGroupMember.h -rw-r--r-- 1 n staff 671B 2 15 19:19 WXCPbVoiceClientScene.h -rw-r--r-- 1 n staff 1.2K 2 15 19:19 WXCPbVoiceClientSceneExt.h -rw-r--r-- 1 n staff 2.9K 2 15 19:19 WXCPbVoiceConf.h ``` ### 4.分析 ------ 首先那麼多的檔案我們肯定不能一個個的去看,那樣效率太低。相信大家做開發為了自己好維護程式碼,肯定不會給物件隨便命名為abc這種吧!不會吧!不會吧!真的有這種人啊!!!但是我相信騰訊的程式設計師肯定不會這麼做!!微信核心的功能是啥?是發訊息哦,那麼訊息的英文是啥?**Message** !對就是他。所以我們就先塞選下這個**Message**! ```shell # n @ localhost in ~/vscodewsp/wechat/dump [20:58:22] $ ll |wc -l 4922 # n @ localhost in ~/vscodewsp/wechat/dump [20:58:29] $ ll -l |grep Message|wc -l 157 # n @ localhost in ~/vscodewsp/wechat/dump [20:58:42] ``` 執行如上命令我們把檔案數從4922個轉變到157了。這樣就縮小了範圍啦!如何再次縮小範圍尼!那麼就得是看大家的開發習慣啦,我一般做業務我都喜歡寫service,controller,這種業務類名,於是我再次.... ```shell # n @ localhost in ~/vscodewsp/wechat/dump [20:58:42] $ ll -l |grep Message|grep Service|wc -l 9 # n @ localhost in ~/vscodewsp/wechat/dump [21:02:13] $ ll -l |grep Message|grep Service -rw-r--r-- 1 n staff 5.1K 2 15 19:19 FTSFileMessageService.h -rw-r--r-- 1 n staff 382B 2 15 19:19 IMessageServiceAppExt-Protocol.h -rw-r--r-- 1 n staff 980B 2 15 19:19 IMessageServiceFileExt-Protocol.h -rw-r--r-- 1 n staff 381B 2 15 19:19 IMessageServiceFileReTransferExt-Protocol.h -rw-r--r-- 1 n staff 755B 2 15 19:19 IMessageServiceImageExt-Protocol.h -rw-r--r-- 1 n staff 780B 2 15 19:19 IMessageServiceVideoExt-Protocol.h -rw-r--r-- 1 n staff 407B 2 15 19:19 IMessageServiceVideoReTransferExt-Protocol.h -rw-r--r-- 1 n staff 3.1K 2 15 19:19 MMFTSMessageService.h -rw-r--r-- 1 n staff 20K 2 15 19:19 MessageService.h # n @ localhost in ~/vscodewsp/wechat/dump [21:02:25] $ ``` 哎呦哎呦,就剩9個檔案啦???那麼這個一個個看也不礙事!!有時間就是任性!!!哼。最終定位到**MessageService.h** 開啟一看,果然尼!真是運氣好! ```swift - (id)SendLocationMsgFromUser:(id)arg1 toUser:(id)arg2 withLatitude:(double)arg3 longitude:(double)arg4 poiName:(id)arg5 label:(id)arg6; - (id)SendNamecardMsgFromUser:(id)arg1 toUser:(id)arg2 containingContact:(id)arg3; - (id)SendStickerStoreEmoticonMsgFromUsr:(id)arg1 toUsrName:(id)arg2 md5:(id)arg3 productID:(id)arg4; - (id)SendEmoticonMsgFromUsr:(id)arg1 toUsrName:(id)arg2 md5:(id)arg3 emoticonType:(unsigned int)arg4; - (id)SendImgMessage:(id)arg1 toUsrName:(id)arg2 thumbImgData:(id)arg3 midImgData:(id)arg4 imgData:(id)arg5 imgInfo:(id)arg6; - (id)SendTextMessage:(id)arg1 toUsrName:(id)arg2 msgText:(id)arg3 atUserList:(id)arg4; - (id)SendAppMusicMessageFromUser:(id)arg1 toUsrName:(id)arg2 withTitle:(id)arg3 url:(id)arg4 description:(id)arg5 thumbnailData:(id)arg6; - (id)SendAppURLMessageFromUser:(id)arg1 toUsrName:(id)arg2 withTitle:(id)arg3 url:(id)arg4 description:(id)arg5 thumbnailData:(id)arg6; - (id)SendAppURLMessageFromUser:(id)arg1 toUsrName:(id)arg2 withTitle:(id)arg3 url:(id)arg4 description:(id)arg5 thumbUrl:(id)arg6 sourceUserName:(id)arg7 sourceDisplayName:(id)arg8; ``` 你看這功能不就來了嘛?Send開頭的都是傳送訊息的函式啊。OK完事。那麼就開始搞它! PS:其實分析時候還是挺費事的,但是大家自己多動手肯定能找到的! ### 5.FridaHook驗證 ------ 為了驗證自己的分析是不是正確的,我們得進行驗證啊,怎麼驗證?frida大法好!執行以下命令: `frida-trace -m "-[MessageService Send*]" 微信` ```shell $ frida-trace -m "-[MessageService Send*]" 微信 Instrumenting... -[MessageService SendTextMessageWithString:toUser:]: Auto-generated handler at "/Users/n/vscodewsp/wechat/__handlers__/MessageService/SendTextMessageWithString_toUser_.js" -[MessageService SendAppURLMessageFromUser:toUsrName:withTitle:url:description:thumbUrl:sourceUserName:sourceDisplayName:]: Auto-generated handler at "/Users/n/vscodewsp/wechat/__handlers__/MessageService/SendAppURLMessageFromUser_toUsrN_eaefd0af.js" ------------------------------------------------------------------------------ -[MessageService SendNamecardMsgFromUser:toUser:containingContact:]: Auto-generated handler at "/Users/n/vscodewsp/wechat/__handlers__/MessageService/SendNamecardMsgFromUser_toUser_c_b5899e8d.js" Started tracing 18 functions. Press Ctrl+C to stop. ``` 然後會在當前目錄生成__handlers__資料夾,裡面是frida為我們自動生成的hook指令碼檔案。我們使用微信傳送一條訊息試試。 然後終端會輸出一條資訊: `195323 ms -[MessageService SendTextMessage:0x600000b6fae0 toUsrName:0x6503cfa934d442eb msgText:0x6000002ec860 atUserList:0x600000a73570]` 這個就是觸發了傳送訊息的hook資訊啦。**SendTextMessage** 是不是跟我們在標頭檔案資訊裡面看到的一樣。 我們找到handles資料夾下**SendTextMessage**這個js檔案,試試修改log輸出然後再執行 `frida-trace -m "-[MessageService Send*]" 微信` 我們可以看到輸出變啦 `2908 ms -[我的訊息測試 SendTextMessage:0x600000b6fae0 toUsrName:0x6503cfa934d442eb msgText:0x6722df8306c2767b atUserList:0x6000009c2760]` 如此可以確定我們找到的函式就是傳送訊息的函式。那麼看看能不能打印出自己傳送的訊息內容 `- (id)SendTextMessage:(id)arg1 toUsrName:(id)arg2 msgText:(id)arg3 atUserList:(id)arg4;` 可以看到這個函式一共有4個引數:引數一:暫時不知道。引數二:toUsrName,我們可以知道是訊息傳送給誰的。引數三:msgText 訊息內容,訊息四:暫時不知道 分別把這四個引數給打印出來試試!修改js檔案 ```javascript onEnter(log, args, state) { console.log(`-[我的訊息測試 SendTextMessage:${args[2]} toUsrName:${args[3]} msgText:${args[4]} atUserList:${args[5]}]`); console.log("arg[1] -> " + new ObjC.Object(args[2])) console.log("arg[2] -> " + new ObjC.Object(args[3])) console.log("arg[3] -> " + new ObjC.Object(args[4])) console.log("arg[4] -> " + new ObjC.Object(args[5])) }, ``` 然後執行 `frida-trace -m "-[MessageService Send*]" 微信` 傳送一條訊息 ```shell arg[1] -> wxid_*****63i822 arg[2] ->
filehelper arg[3] -> 這個是訊息測試 arg[4] -> /* TID 0x307 */ 14534 ms -[我的訊息測試 SendTextMessage:0x600000b6fae0 toUsrName:0x6503cfa934d442eb msgText:0x600000adefd0 atUserList:0x600000add470] ``` 我們可以看到終端正確響應了,輸出的正是我們傳送的訊息。那麼我修改傳送內容試試??新增如下程式碼: ​ `args[4] = ObjC.classes.NSString.stringWithString_("MacOS微信分析")` 然後微信傳送任何訊息,對方都將收到的是**MacOS微信分析**
這樣我們就確定了傳送文字訊息的函式就是這個。那麼我們如何主動呼叫它呢? ### 6.Hopper分析程式程式碼 ------ 從上面的分析我們看到傳送訊息需要四個引數。第一個:通過分析應該是我們自己的微信id,第二個:對方的微信id,第三個:訊息內容,第四個:可以為null 那麼就開啟hopper拖入微信具體分析分析吧 應用程式->微信->顯示包內容->Contents->MacOS->WeChat 拖進hopper然後預設選項即可 在左邊輸入**SendTextMessage**搜尋我們可以看到上面四個應該是我們所需要的,都開啟看下虛擬碼。(我們分析需要找到函式呼叫的地方就能知道傳參,然後再去分析引數是如何而來。那麼除了函式定義地方程式碼,其餘的都可以找到。 **MMMessageSendLogic** : ```swift /* @class MMMessageSendLogic */ -(unsigned char)sendTextMessageWithString:(void *)arg2 mentionedUsers:(void *)arg3 { r14 = self; r15 = [arg2 retain]; r12 = [arg3 retain]; r13 = [[CUtility filterStringForTextMessage:r15] retain]; [r15 release]; if ([r13 length] != 0x0) { stack[-64] = r12; rax = [r13 lengthOfBytesUsingEncoding:0x4]; rbx = rax; if (rax >
= 0x4001) { rax = [[NSString alloc] initWithFormat:@"ERROR: Text too long, length: %lu, utf8 length: %lu", [r13 length], rbx]; stack[0] = "-[MMMessageSendLogic sendTextMessageWithString:mentionedUsers:]"; [MMLogger logWithMMLogLevel:0x2 module:"ComposeInputView" file:0x103e0e162 line:0x112 func:stack[0] message:rax]; [rax release]; rax = [NSBundle mainBundle]; rax = [rax retain]; stack[-72] = rax; r15 = [[rax localizedStringForKey:@"Message.Input.Too.Long.Title" value:@"" table:0x0] retain]; rax = [NSBundle mainBundle]; rax = [rax retain]; r14 = rax; rax = [rax localizedStringForKey:@"Message.Input.Too.Long.Content" value:@"" table:0x0]; rax = [rax retain]; [NSAlert showAlertSheetWithTitle:r15 message:rax completion:0x0]; [rax release]; [r14 release]; [r15 release]; [stack[-72] release]; r14 = 0x0; r12 = stack[-64]; } else { rax = [WeChat sharedInstance]; rax = [rax retain]; r15 = [[rax CurrentUserName] retain]; [rax release]; rax = [r14 currnetChatContact]; rax = [rax retain]; r14 = [[rax m_nsUsrName] retain]; [rax release]; r12 = [[MMServiceCenter defaultCenter] retain]; objc_unsafeClaimAutoreleasedReturnValue([[[r12 getService:[MessageService class]] retain] SendTextMessage:r15 toUsrName:r14 msgText:r13 atUserList:stack[-64]]); [rax release]; [r12 release]; [r14 release]; [r15 release]; r14 = 0x1; r12 = stack[-64]; r13 = r13; } } else { rax = [[NSString alloc] initWithFormat:@"ERROR: Text is empty, can't send"]; stack[0] = "-[MMMessageSendLogic sendTextMessageWithString:mentionedUsers:]"; [MMLogger logWithMMLogLevel:0x2 module:"ComposeInputView" file:0x103e0e162 line:0x10c func:stack[0] message:rax]; [rax release]; r14 = 0x0; } [r13 release]; [r12 release]; rax = r14 & 0xff; return rax; } ``` 這個虛擬碼看的就比較清楚了, `objc_unsafeClaimAutoreleasedReturnValue([[[r12 getService:[MessageService class]] retain] SendTextMessage:r15 toUsrName:r14 msgText:r13 atUserList:stack[-64]]);` 我們可以看到第一個引數是r15,網上追溯r15, `r15 = [[rax CurrentUserName] retain];` r15是這裡賦值的,那麼再看看CurrentUserName方法內容。 ```swift -(void *)CurrentUserName { if ([self isLoggedIn] != 0x0) { rdi = [[CUtility GetCurrentUserName] retain]; } else { rdi = 0x0; } rax = [rdi autorelease]; return rax; } ``` 可以看到是先判斷是不是已經登入,然後呼叫CUtility類裡面的GetCurrentUserName方法獲得的。那麼第一個引數我們就知道了。其餘三個引數我們也很容易的可以手動構造。我們編寫js指令碼程式碼 ### 7.編寫frida指令碼 ------ ```js console.log("init success"); function SendTextMessage(wxid, msg) { var message = ObjC.chooseSync(ObjC.classes.MessageService)[0] var username = ObjC.classes.CUtility.GetCurrentUserName(); console.log(username) console.log("Type of arg[0] -> " + message) var toUsrName = ObjC.classes.NSString.stringWithString_(wxid); var msgText = ObjC.classes.NSString.stringWithString_(msg); message["- SendTextMessage:toUsrName:msgText:atUserList:"](username, toUsrName, msgText, null); } SendTextMessage("filehelper","主動呼叫傳送資訊!") ``` 將以上文字儲存js檔案,然後執行以下命令: `frida 微信 --debug --runtime=v8 --no-pause -l test.js` 我們就可以看到微信上傳送了一條訊息 ![image-20210215221032552](https://images.cnblogs.com/cnblogs_com/Huerye/1068060/t_210215144138image-20210215221032552.png?a=1613400169856) ### 8.訊息監聽 ------ 未完