PC下的微信二次开发相信你们都会了,那么本篇文章将带领你们使用Frida框架对Mac下微信来进行二次开发!javascript
PS:还有一种静态注入的方式也不错,可是考虑到你们xcode安装包太大就不在这里展开啦。java
PS:frida如何去使用你们得本身去学,本文不过多展开。git
主要功能涉及以下:程序员
预先善其事,必先利其器!请先准备以下分析工具shell
首先利用Class-Dump拿到微信的头文件,打开终端执行:macos
class-dump -H /Applications/WeChat.app
成功执行以后会生成不少的头文件了,以下所示swift
-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
首先那么多的文件咱们确定不能一个个的去看,那样效率过低。相信你们作开发为了本身好维护代码,确定不会给对象随便命名为abc这种吧!不会吧!不会吧!真的有这种人啊!!!可是我相信腾讯的程序员确定不会这么作!!微信核心的功能是啥?是发消息哦,那么消息的英文是啥?Message !对就是他。因此咱们就先塞选下这个Message!xcode
# 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,这种业务类名,因而我再次....sass
# 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 打开一看,果真尼!真是运气好!微信
- (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:其实分析时候仍是挺费事的,可是你们本身多动手确定能找到的!
为了验证本身的分析是否是正确的,咱们得进行验证啊,怎么验证?frida大法好!执行如下命令:
frida-trace -m "-[MessageService Send*]" 微信
$ 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文件
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*]" 微信
发送一条消息
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微信分析
这样咱们就肯定了发送文本消息的函数就是这个。那么咱们如何主动调用它呢?
从上面的分析咱们看到发送消息须要四个参数。第一个:经过分析应该是咱们本身的微信id,第二个:对方的微信id,第三个:消息内容,第四个:能够为null
那么就打开hopper拖入微信具体分析分析吧
应用程序->微信->显示包内容->Contents->MacOS->WeChat 拖进hopper而后默认选项便可
在左边输入SendTextMessage搜索咱们能够看到上面四个应该是咱们所须要的,都打开看下伪代码。(咱们分析须要找到函数调用的地方就能知道传参,而后再去分析参数是如何而来。那么除了函数定义地方代码,其他的均可以找到。
MMMessageSendLogic :
/* @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方法内容。
-(void *)CurrentUserName { if ([self isLoggedIn] != 0x0) { rdi = [[CUtility GetCurrentUserName] retain]; } else { rdi = 0x0; } rax = [rdi autorelease]; return rax; }
能够看到是先判断是否是已经登陆,而后调用CUtility类里面的GetCurrentUserName方法得到的。那么第一个参数咱们就知道了。其他三个参数咱们也很容易的能够手动构造。咱们编写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
咱们就能够看到微信上发送了一条消息
未完待续。