MacOS微信逆向分析-Frida

0.前言


PC下的微信二次开发相信大家都会了,那么本篇文章将带领大家使用Frida框架对Mac下微信来进行二次开发

PS:还有一种静态注入的方式也不错,但是考虑到大家xcode安装包太大就不在这里展开啦。

PS:frida如何去使用大家得自己去学,本文不过多展开。

主要功能涉及如下:

  1. 微信消息发送
  2. 微信消息监听

1.微信版本


2.工具


预先善其事,必先利其器!请先准备如下分析工具

  1. Hopper Disassembler
  2. Class-dump
  3. Frida
  4. Pycharm(可选)
  5. Vscode(可选)

3.Dump 出头文件


首先利用Class-Dump拿到微信的头文件,打开终端执行:

class-dump -H /Applications/WeChat.app

成功执行之后会生成很多的头文件了,如下所示

-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

# 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,这种业务类名,于是我再次....

# 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:其实分析时候还是挺费事的,但是大家自己多动手肯定能找到的!

5.FridaHook验证


为了验证自己的分析是不是正确的,我们得进行验证啊,怎么验证?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微信分析

这样我们就确定了发送文本消息的函数就是这个。那么我们如何主动调用它呢?

6.Hopper分析程序代码


从上面的分析我们看到发送消息需要四个参数。第一个:通过分析应该是我们自己的微信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脚本代码

7.编写frida脚本


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

我们就可以看到微信上发送了一条消息

8.消息监听


未完待续。

MacOS微信逆向分析-Frida的更多相关文章

  1. Android微信逆向--实现发朋友圈动态

    0x0 前言 最近一直在研究Windows逆向的东西,想着快要把Android给遗忘了.所以就想利用工作之余来研究Android相关的技术,来保持对Android热情.调用微信代码来发送朋友圈动态一直 ...

  2. 20145307陈俊达_安卓逆向分析_Xposed的hook技术研究

    20145307陈俊达_安卓逆向分析_Xposed的hook技术研究 引言 其实这份我早就想写了,xposed这个东西我在安卓SDK 4.4.4的时候就在玩了,root后安装架构,起初是为了实现一些屌 ...

  3. IM通信协议逆向分析、Wireshark自定义数据包格式解析插件编程学习

    相关学习资料 http://hi.baidu.com/hucyuansheng/item/bf2bfddefd1ee70ad68ed04d http://en.wikipedia.org/wiki/I ...

  4. 技术分享:逆向分析ATM分离器

    文章内容仅供技术交流,请勿模仿操作! 背景(作者) 每一次外出时, Elizabeth和我总是格外的小心,同时把我们身上的钱藏在特殊的皮带上面,这样还不够,我们还采取了“狡兔三窟”的方式来藏身上带的银 ...

  5. C++反汇编与逆向分析技术揭秘

    C++反汇编-继承和多重继承   学无止尽,积土成山,积水成渊-<C++反汇编与逆向分析技术揭秘> 读书笔记 一.单类继承 在父类中声明为私有的成员,子类对象无法直接访问,但是在子类对象的 ...

  6. TI(德州仪器) TMS320C674x逆向分析之一

    一.声明 作者并不懂嵌入式开发,整个逆向流程都是根据自身逆向经验,一步一步摸索出来,有什么错误请批评指正,或者有更好的方法请不吝赐教.个人写作水平有限,文中会尽量把过程写清楚,有问题或是写的不清楚的地 ...

  7. 一文了解安卓APP逆向分析与保护机制

    "知物由学"是网易云易盾打造的一个品牌栏目,词语出自汉·王充<论衡·实知>.人,能力有高下之分,学习才知道事物的道理,而后才有智慧,不去求问就不会知道."知物 ...

  8. Android逆向分析(2) APK的打包与安装背后的故事

    前言 上一次我们反编译了手Q,并遇到了Apktool反编译直接crash的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次先插入一下,正所谓知其然知其所以然,授之 ...

  9. 逆向分析一款国外Blackjack Card Counter软件并附上License生成脚本

    没有学过逆向,一时兴起,搞了一下这个小软件,名为“逆向分析”,其实过程非常简单,难登大雅之堂,就当段子看吧.首先介绍一下背景吧.这是一款国外的Blackjack也就是21点算牌软件,我从来不玩牌的,机 ...

随机推荐

  1. JVM有哪些垃圾回收器

    JVM 的垃圾回收器 目录 JVM 的垃圾回收器 经典垃圾收集器 Serial 收集器 ParNew 收集器 Parallel Scavenge 收集器 Serial Old 收集器 Parallel ...

  2. Scrapy———反爬蟲的一些基本應對方法

    1. IP地址驗證 背景:有些網站會使用IP地址驗證進行反爬蟲處理,檢查客戶端的IP地址,若同一個IP地址頻繁訪問,則會判斷該客戶端是爬蟲程序. 解決方案: 1. 讓Scrapy不斷隨機更換代理服務器 ...

  3. Kubernetes TensorFlow 默认 特定 集群管理器 虚拟化技术

    Our goal is to foster an ecosystem of components and tools that relieve the burden of running applic ...

  4. 洛谷P3501

    Description 对于一个 \(0/1\) 串,如果取反后再将整个串反过来和原串一样,就称作「反对称」字符串 给出一个长度为 \(n\) 的 \(0/1\) 串,求它有多少个反对称子串 Solu ...

  5. LOJ10141染色

    SDOI 2011 染色 给定一棵有 n 个节点的无根树和 m 个操作,操作共两类. 将节点 a 到节点 b 路径上的所有节点都染上颜色: 询问节点 a 到节点 b 路径上的颜色段数量,连续相同颜色的 ...

  6. (G)I-DLE—화(火花) (HWAA)

    闲来无事又来推歌了/cy 我这博客好像只能用来更日记+推歌了/kk 到今天(G)I-DLE已经获得九个一位啦~ 歌真的挺不错的 特别是,一个韩国女团出了这首歌的中文版 就觉得很有好感 music 韩文 ...

  7. MarkDown学习笔记 Typora

    快捷方式篇 新建 ctrl + N 新建窗口 ctrl + shift + N 打开md文件 ctrl + O 快速打开 ctrl + P 保存 ctrl + S 另存为 ctrl + shift + ...

  8. Java——break,continue,return语句

    break语句: break:用于改变程序控制流 用于do-while.while.for中时,可跳出循环而执行循环后面的语句. break的作用:终止当前循环语句的执行. break还可以用来终止s ...

  9. juniper srx系列配置端口映射 转载

    http://www.cnblogs.com/pinpin/p/9895815.html

  10. Spring boot 自定义注解标签记录系统访问日志

    package io.renren.common.annotation; import java.lang.annotation.Documented; import java.lang.annota ...