29-项目实战(1)

前言

本篇文章将接着之前的21-自动抢红包UI这个项目,继续实现自动抢红包的功能。根据以往所掌握的逆向知识,要在WeChat中实现自定义的功能,有2种方式:

  1. 越狱机 创建tweak插件,插入代码。
  2. 非越狱机重签名,再代码注入

其实以上2种方式,都可以通过MonkeyDev插件实现,我们可以使用logos语法,实现Hook红包功能相关的一系列方法,那么越狱非越狱这2个环境就可以自由切换,从而达到自动抢红包的目的。

一、编写Tweak插件

接下来,我们就以越狱机的方式,编写Tweak插件来实现自动抢红包的功能。

  1. 首先创建Tweak插件工程XFWeChatDemo

如何获取WeChat的BundleID呢?直接使用OpenSSL端口链接(可参考22-越狱 & OpenSSH)

然后使用cy脚本,打印APPID(可参考19-Cycript)

有个很实用的cy脚本 mjcript

  1. 添加自定义的代码(包括抢红包的2个自定义的cell)
  1. 修改Monkey的配置
  1. 编译
  1. 越狱机运行查看

如果碰到上述问题 说明你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过去呢?不难

  1. OpenSSH连接手机设备
  1. 搜索出WeChat的包路径(需先启动WeChat
ps -A | grep WeChat

地址 /var/containers/Bundle/Application/27D7F5E7-E36C-435F-B1B8-8267FECF58E6/WeChat.app/,只需要到.app这个路径即可。

  1. 逐个将图片copy到手机的WeChat的包路径之中(先去到图片的文件夹之中)
scp -p 12345 ./xxx.png root@localhost:/var/containers/Bundle/Application/27D7F5E7-E36C-435F-B1B8-8267FECF58E6/WeChat.app/
  1. 再次运行就有图片了

至此,UI方面就基本完成了。

二、定位聊天界面消息接收

接下来就是实现抢红包功能了,在抢红包之前,我们得先理清WeChat接收消息的流程。最直观的当然是 在聊天界面定位消息的接收,触发了哪些方法的调用?

准备工作

在定位之前,我们还要做些准备工作

  1. WeChat包进行砸壳class-dump 可参考23-应用砸壳
  2. info.plist中删除UIDevice的配置,否则无法重签名
  1. 定位聊天界面的VC 看UI层级 找到聊天信息的VC
    有2种方式定位VC
  • attach to process,然后查看UI层级
  • cycript命令查看

我们定位到聊天的界面VC BaseMsgContentViewController

  1. hook VC中所有的方法 锁定【接收红包信息】的方法
    • logos hook所有方法 pl脚本(在theos的目录下面) 输出xm文件
      pl脚本位置

来到dump的文件夹,执行脚本输出

logify.pl BaseMsgContentViewController.h > ../logMethod.xm

  • logMethod.xm直接拖到工程去使用

上图可见,就是通过%log查看调用方,参数等信息,再%orig回到原有流程。

查看源码

编译前改下配置项

编译生成.mm

logMethod.mm拖进工程

然后再次编译,会报错

.mm文件过大,查看时会很卡,我们直接看.xm文件。

报错的方法我们就注释掉,直至编译成功

⚠️注意:此时是没有安装的,在安装之前,先杀掉当前的微信进程!

修改配置 编译即安装

  • 控制台中查看信息

很明显,这里打印的信息,就是我们之前通过logify脚本添加的代码所输出的。

  1. 定位 收到消息所触发的方法
  • 首先接收一个消息,看看控制台的输出

我们只看前面触发的几个方法,有onNewSyncStart --> findNodeDataByLocalId -->addMessageNode,其中onNewSyncStartfindNodeDataByLocalId 的参数没有什么实质性内容,我们来到addMessageNode

上图可见,addMessageNode方法中的入参,就有我们想要的,例如:
m_nsFromUsr发消息的用户Id,m_nsToUsr接收消息的用户Id,type可能是消息类型等。

此时,我们定位到方法addMessageNode,但是,你仔细想想,如果我们定位的是聊天页面的addMessageNode,再hook它抢红包,这时的时机是不是太晚了?

  • 接着,我们退出聊天页面,切换到【发现】tab,再接收消息,看看控制台输出

就2个方法onNewSyncStartonNewSyncfinish。当然我们只看onNewSyncStart,明显它时机更早,继续,看看谁调用它?

  • onNewSyncStart断点,看函数调用栈等信息
    ◦ 首先attach process附加进程WeChat,断住, lldb中methods指令查看方法地址

⚠️注意:如果没有methods指令,可参考18-lldb(下)chisel & 插件中LLDB指令集插件的安装。

我们搜索出onNewSyncStartaddMessageNode的地址

- (void) onNewSyncStart; (0x117f32394)

- (void) addMessageNode:(id)arg1 layout:(BOOL)arg2 addMoreMsg:(BOOL)arg3; (0x117f3b850)

b指令下断点

断住后,c指令continue继续执行。接收一条消息,验证是否成功

◦ 接着,sbt指令查看调用栈信息

frame#1frame#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 MMContextCMessageMgr

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查看信息。

  1. 首先是BaseMsgContentLogicController
    • %log打印DidAddMsg:OnAddMsg:MsgWrap:这两个方法
%hook BaseMsgContentLogicController
- (void)DidAddMsg:(id)arg1 {
    %log;
    %orig;
}

- (void)OnAddMsg:(id)arg1 MsgWrap:(id)arg2 {
    %log;
    %orig;
}
%end
  1. 同理,再看MMExtensionCenterMMContext这2个类的方法,貌似和message的关系不大,所以,我们继续hookCMessageMgr的方法
%hook CMessageMgr
- (void)MainThreadNotifyToExt:(id)arg1{
    %log;
    %orig;
}
%end
  1. 只保留上面要hook的三个方法

仅编译

编译,生成.mm

  1. 退出聊天界面,回到【发现】tab首页,然后接收消息,看看控制台的输出

此时,我们还未进入到聊天VC,红框处的都是CMessageMgr类的MainThreadNotifyToExt方法输出的日志。

再一次,我们去到聊天VC,看看控制台的输出

此时就有了OnAddMsgDidAddMsg方法的打印。经过分析,最终我们得到的方向就是

MainThreadNotifyToExt方法不是我们研究抢红包的唯一方法,类CMessageMgr才是重点的研究对象。

四、定位消息管理者

接下来重点研究CMessageMgr消息管理者类。

  1. pl脚本``hook所有方法

logify.pl CMessageMgr.h > ../logMethodCMessageMgr.xm

  1. logMethodCMessageMgr.xm文件拖入Monkey注入工程中,查看源码

近300行。

  1. 编译,产出.mm,拖入工程

然后注释一些编译不过的方法

  1. 编译运行,重新接收消息,打开控制台查看

得到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"} ]
  1. 接下来,我们hookCheckMessageStatus: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
  1. 编译安装(记得手动杀掉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

  1. 然后再接收个红包消息,查看输出

红包消息的type=49。那么我们接下来,就能在CheckMessageStatus:Msg方法中,做一个hook

如果接收的消息type=49(即红包),那么就执行拆开红包

五、动态分析拆红包

接下来就是红包了,还是一样,需要定位所调用的方法。

需要使用工具IDA 静态分析抢红包的方法。

  1. attach附加 viewDebug查看UI层级

上图可见,红包的view类是WCRedEnvelopesReceiveHomeView,同时是个UIButton

  1. 定位到按钮的方法

拆button所对应的方法是OnOpenRedEnvelopes。接下来就是静态分析 如何模拟触发OnOpenRedEnvelopes方法,这个我们在后面的文章中继续讲解。(30-项目实战(2)

总结

本篇文章,通过Tweak插件的方式,在越狱机中演示,如何一步步地定位 接收红包消息所触发的方法,hook并打印这些方法的参数,找到我们想要的消息model类的具体信息,进而为我们下一步,分析红包做准备。

你可能感兴趣的:(29-项目实战(1))