前言
本篇文章将接着之前的21-自动抢红包UI这个项目,继续实现自动抢红包
的功能。根据以往所掌握的逆向知识,要在WeChat
中实现自定义的功能,有2种方式:
-
越狱机
👉🏻 创建tweak插件
,插入代码。 -
非越狱机
👉🏻 先重签名
,再代码注入
。
其实以上2种方式,都可以通过MonkeyDev
插件实现,我们可以使用logos
语法,实现Hook
红包功能相关的一系列方法,那么越狱
和非越狱
这2个环境就可以自由切换
,从而达到自动抢红包的目的。
一、编写Tweak插件
接下来,我们就以越狱机
的方式,编写Tweak插件
来实现自动抢红包的功能。
- 首先创建Tweak插件工程
XFWeChatDemo
👇🏻
![](https://img.haomeiwen.com/i3444487/d8fc0fda15e7e9a1.png)
![](https://img.haomeiwen.com/i3444487/8a2a7fc29dc5a7cc.png)
![](https://img.haomeiwen.com/i3444487/ca6152e059f33d7c.png)
如何获取WeChat的BundleID呢?直接使用OpenSSL端口链接(可参考22-越狱 & OpenSSH)👇🏻
![](https://img.haomeiwen.com/i3444487/f4d907806a7b863f.png)
然后使用cy脚本,打印APPID(可参考19-Cycript)👇🏻
有个很实用的cy脚本 👉🏻 mjcript
![](https://img.haomeiwen.com/i3444487/cfec63fbf7217c1c.png)
- 添加自定义的代码(包括抢红包的2个自定义的cell)
![](https://img.haomeiwen.com/i3444487/abc56e875582115d.png)
![](https://img.haomeiwen.com/i3444487/d6ed9c800b222978.png)
- 修改Monkey的配置👇🏻
![](https://img.haomeiwen.com/i3444487/820209dc1e447fa6.png)
- 编译
![](https://img.haomeiwen.com/i3444487/fd828ddd2ba3f2fc.png)
![](https://img.haomeiwen.com/i3444487/630b2862c5b06680.png)
- 越狱机运行查看
![](https://img.haomeiwen.com/i3444487/155a651d0706c5bc.png)
如果碰到上述问题 👉🏻 说明你mac的RSA密钥对不存在,需要重新生成。解决方案👇🏻
// 1. 生成公钥 (直接回车回车回车生成)
ssh-keygen
// 2. copy到你的越狱手机中
ssh-copy-id -i $HOME/.ssh/id_rsa.pub root@你的手机ip地址
⚠️ 确保你的手机已经
越狱
,并且和mac是在同一个wifi
下!
重新生成新的密钥对后,再次运行,手机会自动重启
,打开微信设置
页面👇🏻
上图可见,2个cell已经展示出来了!🍺🍺🍺🍺🍺🍺
图片的处理
之前21-自动抢红包UI时,是将图片复制到app包
里面,再重签名
安装即可。那么现在越狱状态下怎么将图片copy过去呢?不难👇🏻
- OpenSSH连接手机设备👇🏻
![](https://img.haomeiwen.com/i3444487/533b289ac12e091a.png)
- 搜索出
WeChat
的包路径(需先启动WeChat
)👇🏻
ps -A | grep WeChat
![](https://img.haomeiwen.com/i3444487/cd9447b336de4d41.png)
地址 👉🏻 /var/containers/Bundle/Application/27D7F5E7-E36C-435F-B1B8-8267FECF58E6/WeChat.app/
,只需要到.app
这个路径即可。
- 逐个将图片copy到手机的
WeChat
的包路径之中(先去到图片的文件夹之中)👇🏻
scp -p 12345 ./xxx.png root@localhost:/var/containers/Bundle/Application/27D7F5E7-E36C-435F-B1B8-8267FECF58E6/WeChat.app/
![](https://img.haomeiwen.com/i3444487/8116e26ac4378247.png)
- 再次运行就有图片了👇🏻
![](https://img.haomeiwen.com/i3444487/e1d620ae13b13ac3.png)
至此,UI方面就基本完成了。
二、定位聊天界面消息接收
接下来就是实现抢红包
功能了,在抢红包之前,我们得先理清WeChat
接收消息的流程。最直观的当然是 👉🏻 在聊天界面定位消息的接收,触发了哪些方法的调用?
准备工作
在定位之前,我们还要做些准备工作👇🏻
- 对
WeChat
包进行砸壳class-dump
👉🏻 可参考23-应用砸壳 -
info.plist
中删除UIDevice
的配置,否则无法重签名👇🏻
![](https://img.haomeiwen.com/i3444487/0934a0d61af5c0a6.png)
- 定位聊天界面的VC 看UI层级 👉🏻 找到聊天信息的VC
有2种方式定位VC👇🏻
- attach to process,然后查看UI层级👇🏻
![](https://img.haomeiwen.com/i3444487/3d522d1c5e194c12.png)
![](https://img.haomeiwen.com/i3444487/ebd7cf6cb9e905e3.png)
- cycript命令查看 👇🏻
![](https://img.haomeiwen.com/i3444487/d928639a5321ac33.png)
我们定位到聊天的界面VC
👉🏻 BaseMsgContentViewController
- hook VC中所有的方法 👉🏻 锁定【接收红包信息】的方法
- logos hook所有方法
pl
脚本(在theos
的目录下面) 输出xm
文件👇🏻
pl
脚本位置👇🏻
- logos hook所有方法
来到dump的文件夹,执行脚本输出👇🏻
logify.pl BaseMsgContentViewController.h > ../logMethod.xm
![](https://img.haomeiwen.com/i3444487/946dc33369099e6e.png)
![](https://img.haomeiwen.com/i3444487/e50f7c1fb5504a92.png)
- 将
logMethod.xm
直接拖到工程去使用👇🏻
![](https://img.haomeiwen.com/i3444487/8f4527f2a0cc7693.png)
上图可见,就是通过%log
查看调用方,参数
等信息,再%orig
回到原有流程。
查看源码👇🏻
![](https://img.haomeiwen.com/i3444487/d8492c03ac446a32.png)
编译前改下配置项👇🏻
![](https://img.haomeiwen.com/i3444487/4eca1199db0a6be2.png)
编译生成.mm👇🏻
![](https://img.haomeiwen.com/i3444487/8f778092e8457d65.png)
将logMethod.mm
拖进工程👇🏻
![](https://img.haomeiwen.com/i3444487/e15f9e387efa3997.png)
然后再次编译,会报错👇🏻
![](https://img.haomeiwen.com/i3444487/385ba6bfd7983a69.png)
.mm文件过大,查看时会很卡,我们直接看.xm文件。
报错的方法我们就注释掉,直至编译成功👇🏻
![](https://img.haomeiwen.com/i3444487/bb3380bf60a64817.png)
![](https://img.haomeiwen.com/i3444487/d13ef2eab9f422cf.png)
⚠️注意:此时是没有安装的,在安装之前,先
杀掉
当前的微信进程!
修改配置 👉🏻 编译即安装👇🏻
![](https://img.haomeiwen.com/i3444487/42e909ebbd0ee4f9.png)
- 在
控制台
中查看信息
![](https://img.haomeiwen.com/i3444487/8834c84e8f06457f.png)
![](https://img.haomeiwen.com/i3444487/a0a2becd91ccf031.png)
很明显,这里打印的信息,就是我们之前通过logify
脚本添加的代码所输出的。
- 定位 👉🏻
收到消息
所触发的方法
- 首先接收一个消息,看看控制台的输出👇🏻
![](https://img.haomeiwen.com/i3444487/c82238c927ebd402.png)
我们只看前面触发的几个方法,有onNewSyncStart
--> findNodeDataByLocalId
-->addMessageNode
,其中onNewSyncStart
和findNodeDataByLocalId
的参数没有什么实质性内容,我们来到addMessageNode
👇🏻
![](https://img.haomeiwen.com/i3444487/1e820f8e5d3cef82.png)
上图可见,addMessageNode
方法中的入参,就有我们想要的,例如:
m_nsFromUsr
发消息的用户Id,m_nsToUsr
接收消息的用户Id,type
可能是消息类型等。
此时,我们定位到方法addMessageNode
,但是,你仔细想想,如果我们定位的是聊天页面的addMessageNode
,再hook它抢红包,这时的时机是不是太晚了?
- 接着,我们退出聊天页面,切换到【发现】tab,再接收消息,看看控制台输出 👇🏻
![](https://img.haomeiwen.com/i3444487/93489ae3d16cade4.png)
就2个方法onNewSyncStart
和onNewSyncfinish
。当然我们只看onNewSyncStart
,明显它时机更早,继续,看看谁调用它?
- 下
onNewSyncStart
断点,看函数调用栈等信息
◦ 首先attach process
附加进程WeChat
,断住, lldb中methods
指令查看方法地址👇🏻
![](https://img.haomeiwen.com/i3444487/a431b43b848e39e0.png)
⚠️注意:如果没有
methods
指令,可参考18-lldb(下)chisel & 插件中LLDB指令集插件的安装。
我们搜索出onNewSyncStart
和addMessageNode
的地址👇🏻
- (void) onNewSyncStart; (0x117f32394)
- (void) addMessageNode:(id)arg1 layout:(BOOL)arg2 addMoreMsg:(BOOL)arg3; (0x117f3b850)
◦ b
指令下断点
![](https://img.haomeiwen.com/i3444487/652b548f5d467ccd.png)
断住后,c
指令continue继续执行。接收一条消息,验证是否成功👇🏻
![](https://img.haomeiwen.com/i3444487/53d1f5c20952fff2.png)
◦ 接着,sbt
指令查看调用栈信息👇🏻
![](https://img.haomeiwen.com/i3444487/6a6a48cbe9741100.png)
frame#1
和frame#2
这两项没有恢复,我们继续执行一次看看👇🏻
![](https://img.haomeiwen.com/i3444487/9215c79d5d236558.png)
frame#1
👉🏻 0x10e7f4418 WeChat
-[MMExtensionCenter callExtension:selector:block:]`
frame#2
还是没获取到,我们可以记录一下地址
frame #2 : 0x10e773584 WeChat`___lldb_unnamed_symbol958446$$WeChat ... unresolved womp womp + 56
然后image list查看首地址,计算偏移地址👇🏻
![](https://img.haomeiwen.com/i3444487/546306ddc562848c.png)
那么0x10e773584
- 0x0000000104f78000
= 0x97FB584
,该地址应该在Text常量区
,然后我们用Hopper打开Mach-O文件,查看该地址是👇🏻
![](https://img.haomeiwen.com/i3444487/721a55ab8e8698a7.png)
![](https://img.haomeiwen.com/i3444487/0f6d265606c248af.png)
没有什么有价值的信息。
三、定位全局消息接收
接下来定位addMesssageNode
的调用栈信息,还是一样,sbt
查看👇🏻
![](https://img.haomeiwen.com/i3444487/50f2eebc555ea17a.png)
第二次sbt
恢复了调用栈的前面几个方法的信息,得到几个类,包括BaseMsgContentLogicController
MMExtensionCenter
MMContext
和CMessageMgr
👇🏻
frame #1 : 0x10343b614 WeChat`-[BaseMsgContentLogicController DidAddMsg:] + 852
frame #2 : 0x10341ae24 WeChat`-[BaseMsgContentLogicController OnAddMsg:MsgWrap:] + 728
frame #3 : 0x109968418 WeChat`-[MMExtensionCenter callExtension:selector:block:] + 204
frame #4 : 0x1098fc244 WeChat`-[MMContext callExtension:selector:block:] + 168
frame #5 : 0x10135e4b8 WeChat`-[CMessageMgr MainThreadNotifyToExt:] + 836
接着逐个分析这些类中定位的方法, logos语法%log
查看信息。
- 首先是
BaseMsgContentLogicController
-
%log
打印DidAddMsg:
和OnAddMsg:MsgWrap:
这两个方法👇🏻
-
![](https://img.haomeiwen.com/i3444487/acde1a9b5ac8154d.png)
%hook BaseMsgContentLogicController
- (void)DidAddMsg:(id)arg1 {
%log;
%orig;
}
- (void)OnAddMsg:(id)arg1 MsgWrap:(id)arg2 {
%log;
%orig;
}
%end
- 同理,再看
MMExtensionCenter
和MMContext
这2个类的方法,貌似和message的关系不大,所以,我们继续hookCMessageMgr
的方法👇🏻
%hook CMessageMgr
- (void)MainThreadNotifyToExt:(id)arg1{
%log;
%orig;
}
%end
- 只保留上面要hook的三个方法👇🏻
![](https://img.haomeiwen.com/i3444487/8be89893bd1ac72d.png)
仅编译👇🏻
![](https://img.haomeiwen.com/i3444487/5ce0dc3cf1fd9ec9.png)
编译,生成.mm 👇🏻
![](https://img.haomeiwen.com/i3444487/f87f63f09b8e20c3.png)
- 退出聊天界面,回到【发现】tab首页,然后接收消息,看看控制台的输出👇🏻
![](https://img.haomeiwen.com/i3444487/acbabec119f43b9b.png)
此时,我们还未进入到聊天VC,红框处的都是CMessageMgr
类的MainThreadNotifyToExt
方法输出的日志。
再一次,我们去到聊天VC,看看控制台的输出👇🏻
![](https://img.haomeiwen.com/i3444487/08e2f5edd80b2f62.png)
此时就有了OnAddMsg
和DidAddMsg
方法的打印。经过分析,最终我们得到的方向就是👇🏻
MainThreadNotifyToExt
方法不是我们研究抢红包的唯一方法,类CMessageMgr
才是重点的研究对象。
四、定位消息管理者
接下来重点研究CMessageMgr
消息管理者类。
- pl脚本``hook所有方法 👇🏻
logify.pl CMessageMgr.h > ../logMethodCMessageMgr.xm
![](https://img.haomeiwen.com/i3444487/5122e48eb2d23909.png)
- 将
logMethodCMessageMgr.xm
文件拖入Monkey注入工程中,查看源码👇🏻
![](https://img.haomeiwen.com/i3444487/334410f39b27bd08.png)
![](https://img.haomeiwen.com/i3444487/3cf8570622156aa5.png)
近300行。
- 编译,产出.mm,拖入工程👇🏻
![](https://img.haomeiwen.com/i3444487/23b18abb8d5e82c2.png)
然后注释一些编译不过的方法👇🏻
![](https://img.haomeiwen.com/i3444487/ab2219e9511b4bb9.png)
- 编译运行,重新接收消息,打开控制台查看👇🏻
![](https://img.haomeiwen.com/i3444487/b0ee19a15d5496cc.png)
得到GetMsg:
CheckMessageStatus:
AsyncOnPreAddMsg:
和 onNewSyncAddMessage
OnNewSyncAddMsgSessionArray
等等很多关于消息的方法打印👇🏻
-[<CMessageMgr: 0x13fe6d300> GetMsg:Aron1101 LocalID:16 hasError:0x16f1c6d70]
-[<CMessageMgr: 0x13fe6d300> CheckMessageStatus:Aron1101 Msg:{m_uiMesLocalID=16, m_ui64MesSvrID=7818316956601146902, m_nsFromUsr=Aro*101~8, m_nsToUsr=wxi*o12~19, m_uiStatus=3, type=1, createTime=1629794366 msgSource="<msgsource><sequence_id>745337625</sequence_id></msgsource>"} ]
-[<CMessageMgr: 0x13fe6d300> onNewSyncAddMessage:{m_uiMesLocalID=16, m_ui64MesSvrID=7818316956601146902, m_nsFromUsr=Aro*101~8, m_nsToUsr=wxi*o12~19, m_uiStatus=3, type=1, createTime=1629794366 msgSource="<msgsource><sequence_id>745337625</sequence_id></msgsource>"} ]
-[<CMessageMgr: 0x13fe6d300> onNewSyncAddMsgSessionArray:{
Aron1101 = "{m_uiMesLocalID=16, m_ui64MesSvrID=7818316956601146902, m_nsFromUsr=Aro*101~8, m_nsToUsr=wxi*o12~19, m_uiStatus=3, type=1, createTime=1629794366 msgSource=\"<msgsource><sequence_id>745337625</sequence_id></msgsource>\"} ";
} withUsers:{(
Aron1101
)}]
这么多方法触发,关键我们是要对消息
本身进行研究,看看有哪些信息,才能准确定位
红包消息。所以,最终我们锁定到CheckMessageStatus
它的参数才是我们想要的👇🏻
-[<CMessageMgr: 0x13fe6d300> CheckMessageStatus:Aron1101 Msg:{m_uiMesLocalID=16, m_ui64MesSvrID=7818316956601146902, m_nsFromUsr=Aro*101~8, m_nsToUsr=wxi*o12~19, m_uiStatus=3, type=1, createTime=1629794366 msgSource="<msgsource><sequence_id>745337625</sequence_id></msgsource>"} ]
- 接下来,我们hook
CheckMessageStatus:Msg:
方法👇🏻
%hook CMessageMgr
- (void)CheckMessageStatus:(id)arg1 Msg:(id)arg2 {
%log;
%orig;
}
%end
也可以自定义打印格式👇🏻
%hook CMessageMgr
- (void)CheckMessageStatus:(id)arg1 Msg:(id)arg2 {
NSLog(@"arg1:%@\narg1 Class:%@\narg2:%@\narg2 Class:%@", arg1, [arg1 class], arg2, [arg2 class]);
%orig;
}
%end
- 编译安装(记得
手动
杀掉WeChat App进程),然后接收一条消息,查看控制台输出👇🏻
![](https://img.haomeiwen.com/i3444487/4b79604608e05d52.png)
arg1是字符串 agr2是CMessageWrap
,这个就是消息
model类👇🏻
arg2:
{
m_uiMesLocalID=17,
m_ui64MesSvrID=6443875340523928360,
m_nsFromUsr=Aro*101~8,
m_nsToUsr=wxi*o12~19,
m_uiStatus=3,
type=1,
createTime=1629854565
msgSource="<msgsource><sequence_id>745337629</sequence_id></msgsource>"
}
arg2 Class:CMessageWrap
可以看到,普通的文本消息type=1
,消息的类class是CMessageWrap
。
- 然后再接收个
红包
消息,查看输出👇🏻
![](https://img.haomeiwen.com/i3444487/801183985a15b69b.png)
红包消息的type=49
。那么我们接下来,就能在CheckMessageStatus:Msg
方法中,做一个hook👇🏻
如果接收的消息
type=49
(即红包),那么就执行拆开红包
。
五、动态分析拆红包
接下来就是拆
红包了,还是一样,需要定位所调用的方法。
需要使用工具
IDA
👉🏻静态分析
抢红包的方法。
-
attach
附加viewDebug
查看UI
层级👇🏻
![](https://img.haomeiwen.com/i3444487/a24754f216c5592e.png)
上图可见,红包的view类是WCRedEnvelopesReceiveHomeView
,同时拆
是个UIButton
。
- 定位到
拆
按钮的方法 👇🏻
![](https://img.haomeiwen.com/i3444487/b64696d9931a0244.png)
拆button所对应的方法是OnOpenRedEnvelopes
。接下来就是静态分析 👉🏻 如何模拟触发OnOpenRedEnvelopes
方法,这个我们在后面的文章中继续讲解。(30-项目实战(2)
)
总结
本篇文章,通过Tweak
插件的方式,在越狱机中演示,如何一步步地定位 👉🏻 接收红包
消息所触发的方法,hook并打印这些方法的参数,找到我们想要的消息model类的具体信息,进而为我们下一步,分析拆
红包做准备。
网友评论