前言
本篇文章将接着之前的21-自动抢红包UI这个项目,继续实现自动抢红包
的功能。根据以往所掌握的逆向知识,要在WeChat
中实现自定义的功能,有2种方式:
-
越狱机
创建tweak插件
,插入代码。 -
非越狱机
先重签名
,再代码注入
。
其实以上2种方式,都可以通过MonkeyDev
插件实现,我们可以使用logos
语法,实现Hook
红包功能相关的一系列方法,那么越狱
和非越狱
这2个环境就可以自由切换
,从而达到自动抢红包的目的。
一、编写Tweak插件
接下来,我们就以越狱机
的方式,编写Tweak插件
来实现自动抢红包的功能。
- 首先创建Tweak插件工程
XFWeChatDemo
如何获取WeChat的BundleID呢?直接使用OpenSSL端口链接(可参考22-越狱 & OpenSSH)
然后使用cy脚本,打印APPID(可参考19-Cycript)
有个很实用的cy脚本 mjcript
- 添加自定义的代码(包括抢红包的2个自定义的cell)
- 修改Monkey的配置
- 编译
- 越狱机运行查看
如果碰到上述问题 说明你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连接手机设备
- 搜索出
WeChat
的包路径(需先启动WeChat
)
ps -A | grep WeChat
地址 /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/
- 再次运行就有图片了
至此,UI方面就基本完成了。
二、定位聊天界面消息接收
接下来就是实现抢红包
功能了,在抢红包之前,我们得先理清WeChat
接收消息的流程。最直观的当然是 在聊天界面定位消息的接收,触发了哪些方法的调用?
准备工作
在定位之前,我们还要做些准备工作
- 对
WeChat
包进行砸壳class-dump
可参考23-应用砸壳 -
info.plist
中删除UIDevice
的配置,否则无法重签名
- 定位聊天界面的VC 看UI层级 找到聊天信息的VC
有2种方式定位VC
- attach to process,然后查看UI层级
- cycript命令查看
我们定位到聊天的界面VC
BaseMsgContentViewController
- hook VC中所有的方法 锁定【接收红包信息】的方法
- logos hook所有方法
pl
脚本(在theos
的目录下面) 输出xm
文件
pl
脚本位置
- logos hook所有方法
来到dump的文件夹,执行脚本输出
logify.pl BaseMsgContentViewController.h > ../logMethod.xm
- 将
logMethod.xm
直接拖到工程去使用
上图可见,就是通过%log
查看调用方,参数
等信息,再%orig
回到原有流程。
查看源码
编译前改下配置项
编译生成.mm
将logMethod.mm
拖进工程
然后再次编译,会报错
.mm文件过大,查看时会很卡,我们直接看.xm文件。
报错的方法我们就注释掉,直至编译成功
⚠️注意:此时是没有安装的,在安装之前,先
杀掉
当前的微信进程!
修改配置 编译即安装
- 在
控制台
中查看信息
很明显,这里打印的信息,就是我们之前通过logify
脚本添加的代码所输出的。
- 定位
收到消息
所触发的方法
- 首先接收一个消息,看看控制台的输出
我们只看前面触发的几个方法,有onNewSyncStart
--> findNodeDataByLocalId
-->addMessageNode
,其中onNewSyncStart
和findNodeDataByLocalId
的参数没有什么实质性内容,我们来到addMessageNode
上图可见,addMessageNode
方法中的入参,就有我们想要的,例如:
m_nsFromUsr
发消息的用户Id,m_nsToUsr
接收消息的用户Id,type
可能是消息类型等。
此时,我们定位到方法addMessageNode
,但是,你仔细想想,如果我们定位的是聊天页面的addMessageNode
,再hook它抢红包,这时的时机是不是太晚了?
- 接着,我们退出聊天页面,切换到【发现】tab,再接收消息,看看控制台输出
就2个方法onNewSyncStart
和onNewSyncfinish
。当然我们只看onNewSyncStart
,明显它时机更早,继续,看看谁调用它?
- 下
onNewSyncStart
断点,看函数调用栈等信息
◦ 首先attach process
附加进程WeChat
,断住, lldb中methods
指令查看方法地址
⚠️注意:如果没有
methods
指令,可参考18-lldb(下)chisel & 插件中LLDB指令集插件的安装。
我们搜索出onNewSyncStart
和addMessageNode
的地址
- (void) onNewSyncStart; (0x117f32394)
- (void) addMessageNode:(id)arg1 layout:(BOOL)arg2 addMoreMsg:(BOOL)arg3; (0x117f3b850)
◦ b
指令下断点
断住后,c
指令continue继续执行。接收一条消息,验证是否成功
◦ 接着,sbt
指令查看调用栈信息
frame#1
和frame#2
这两项没有恢复,我们继续执行一次看看
frame#1
0x10e7f4418 WeChat
-[MMExtensionCenter callExtension:selector:block:]`
frame#2
还是没获取到,我们可以记录一下地址
frame #2 : 0x10e773584 WeChat`___lldb_unnamed_symbol958446$$WeChat ... unresolved womp womp + 56
然后image list查看首地址,计算偏移地址
那么0x10e773584
- 0x0000000104f78000
= 0x97FB584
,该地址应该在Text常量区
,然后我们用Hopper打开Mach-O文件,查看该地址是
没有什么有价值的信息。
三、定位全局消息接收
接下来定位addMesssageNode
的调用栈信息,还是一样,sbt
查看
第二次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:
这两个方法
-
%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的三个方法
仅编译
编译,生成.mm
- 退出聊天界面,回到【发现】tab首页,然后接收消息,看看控制台的输出
此时,我们还未进入到聊天VC,红框处的都是CMessageMgr
类的MainThreadNotifyToExt
方法输出的日志。
再一次,我们去到聊天VC,看看控制台的输出
此时就有了OnAddMsg
和DidAddMsg
方法的打印。经过分析,最终我们得到的方向就是
MainThreadNotifyToExt
方法不是我们研究抢红包的唯一方法,类CMessageMgr
才是重点的研究对象。
四、定位消息管理者
接下来重点研究CMessageMgr
消息管理者类。
- pl脚本``hook所有方法
logify.pl CMessageMgr.h > ../logMethodCMessageMgr.xm
- 将
logMethodCMessageMgr.xm
文件拖入Monkey注入工程中,查看源码
近300行。
- 编译,产出.mm,拖入工程
然后注释一些编译不过的方法
- 编译运行,重新接收消息,打开控制台查看
得到GetMsg:
CheckMessageStatus:
AsyncOnPreAddMsg:
和 onNewSyncAddMessage
OnNewSyncAddMsgSessionArray
等等很多关于消息的方法打印
-[ GetMsg:Aron1101 LocalID:16 hasError:0x16f1c6d70]
-[ 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="745337625 "} ]
-[ 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="745337625 "} ]
-[ 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=\"745337625 \"} ";
} withUsers:{(
Aron1101
)}]
这么多方法触发,关键我们是要对消息
本身进行研究,看看有哪些信息,才能准确定位
红包消息。所以,最终我们锁定到CheckMessageStatus
它的参数才是我们想要的
-[ 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="745337625 "} ]
- 接下来,我们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进程),然后接收一条消息,查看控制台输出
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="745337629 "
}
arg2 Class:CMessageWrap
可以看到,普通的文本消息type=1
,消息的类class是CMessageWrap
。
- 然后再接收个
红包
消息,查看输出
红包消息的type=49
。那么我们接下来,就能在CheckMessageStatus:Msg
方法中,做一个hook
如果接收的消息
type=49
(即红包),那么就执行拆开红包
。
五、动态分析拆红包
接下来就是拆
红包了,还是一样,需要定位所调用的方法。
需要使用工具
IDA
静态分析
抢红包的方法。
-
attach
附加viewDebug
查看UI
层级
上图可见,红包的view类是WCRedEnvelopesReceiveHomeView
,同时拆
是个UIButton
。
- 定位到
拆
按钮的方法
拆button所对应的方法是OnOpenRedEnvelopes
。接下来就是静态分析 如何模拟触发OnOpenRedEnvelopes
方法,这个我们在后面的文章中继续讲解。(30-项目实战(2)
)
总结
本篇文章,通过Tweak
插件的方式,在越狱机中演示,如何一步步地定位 接收红包
消息所触发的方法,hook并打印这些方法的参数,找到我们想要的消息model类的具体信息,进而为我们下一步,分析拆
红包做准备。