iOS逆向实战与工具使用(微信添加好友自动确认)

2022-11-19,,,,

iOS逆向实战与工具使用(微信添加好友自动确认

原文链接

源码地址

WeChatPlugin-iOS

Mac OS 版微信小助手(远程控制、消息防撤回、自动回复、微信多开)

一、前言

本篇主要实现在微信上自动添加好友,从而熟悉 iOS 逆向分析的过程。


二、 工具

2.1 MacBook 软件

theos

制作 Tweak 的工具
hopper disassembler
用于静态分析
usbmuxd
端口转发,可以让我们通过usb连接手机进行ssh、lldb调试等。主要使用python-client目录下的文件
class-dump
dump 目标对象的 class 信息的工具.
lldb
调试神器,用过的都说好。默认自带,在/Applications/Xcode.app/Contents/Developer/usr/bin/lldb 中。

2.2 越狱iPhone 软件

以下软件在 Cydia 中即可下载

OpenSSH

实现在越狱手机上远程进行 ssh 服务

iOS 工具大部分都需要在 ssh 环境中使用

Cycript

脚本语言,用于 hook 正在运行的进程,并实时注入代码。
ondeviceconsole
用于在 Terminal 中查看手机的 log
debugserver
用于连接手机进行 lldb 调试的工具。用 Xcode 在手机上进行 app 调试即可生成,在手机目录的 /Developer/usr/bin/ 中。
使用 debugserver 需要先进行处理。因为缺少task_for_pid权限,所以调试不了其他的 app。

debugserver + lldb环境搭建


三、 分析

思路:想要实现自动添加好友,则要拿到获取好友请求的方法,以及添加好友的方法。hook 获取好友请求的方法,在接收到好友请求的时候,执行添加好友的方法。
而这些主要逻辑在“新的朋友”界面。

3.1 定位好友请求的方法


3.1.1 UI 分析

想要拿到好友请求的方法,要先拿到方法实现的 ViewController。而这时候可以通过 UI 分析获得。

先打开新的朋友界面。

使用 usbmuxd 进行端口的转发
usbmuxd进行ssh连接-iOS逆向工程

// 1. 端口的转发,(手机22 端口转到Mac 2222 端口)
python tcprelay.py -t 22:2222 // 2. 再使用ssh连接至手机(举例 手机ip为 192.168.31.94)
ssh root@192.168.31.94 -p 2222

查看微信的进程信息

ps -e |grep WeChat

cycript 注入

cycript -p WeChat

查看当前 UI 布局(或者用Reveal工具)

UIApp.keyWindow.recursiveDescription().toString()

因为知道当前的视图有tableview,所以找到tableview的对象。从上图可以看到该对象的地址为0x18c4be00。
在使用 nextResponder()根据响应者往上找当前的控制器。

找到当前的控制器,为SayHelloViewController

3.1.2 Log 分析

使用class-dump dump 出微信的 class 信息。

class-dump -H WeChat -o ~/Document/headers/
// 保存在 ~/Document/headers/ 目录中

再使用 theos 的 logify 工具,该工具用来注入NSLog来打印方法的入参和出参。(就是在所有的方法里面加 log)

logify.pl  ~/Document/headers/SayHelloViewController.h > 	~/Desktop/Tweak.xm

注意:一般该Tweak.xm仍然无法执行,需要进行修改:
去掉.cxx destruct 方法
将HBLogDebug 改为NSLog
去掉所有的delegate
将所有的参数对象类型改成id
去掉所有的weak

在ssh中使用ondeviceconsole打印手机的 log。ondeviceconsole使用

这时操作添加自己好友。触发好友请求的方法。可以看到以下的 log

说明有好友添加请求的时候,会调用
[SayHelloViewController OnSayHelloDataChange]


3.1.3 动态分析

既然已经知道了当前控制器会调用OnSayHelloDataChange,那么我们可以想如何查看是哪些方法触发了OnSayHelloDataChange,这时候就要用到 lldb + hopper 神器了。

先用 hopper 打开微信的二进制文件。搜索-[SayHelloViewController OnSayHelloDataChange]方法。

可以看到当前方法在微信中的偏移地址0x14a4824。

再使用 usbmuxd 转换端口

python tcprelay.py -t 1234:1234

在 ssh 到手机上,开启 debugserver

debugserver *:1234 -a "WeChat"

使用新的 terminal 窗口,打开 lldb,连接1234端口,并查看当前所有进程。

// 打开lldb
/Applications/Xcode.app/Contents/Developer/usr/bin/lldb
// 连接端口调试
(lldb) process connect connect://localhost:1234
// 打印所有进程
(lldb) image list -o -f

找到微信在当前手机上的进程内存基地址为0x000b2000(这个值不是不变的)

通过以上可以找到 [SayHelloViewController OnSayHelloDataChange]方法在手机上的内存地址。即

内存地址 = 进程内存基地址 + 方法偏移地址

使用 br 打断点查看

br s -a "0x000b2000 + 0x14a4824"

接着输入 c 继续运行,重新使用另一微信账号添加好友,会触发该断点。

使用 bt 查看调用栈信息,即哪些方法调用了当前的方法,找到方法的上游。(异步调用的话没办法查看)

第一个表示当前的方法,可以看到在调用此方法前,该进程总共调用了3个方法。

分别计算出这三个方法在微信中的偏移量。

将这三个地址在 hopper 中查看,找到了对应的方法为

// 调用的顺序为从下到上
[SayHelloViewController OnSayHelloDataChange]
[SayHelloDataLogic onFriendAssistAddMsg:]
[FriendAsistSessionMgr OnAddMsgForSpecialSession:MsgList:]
[CMessageMgr MainThreadNotifyToExt:]

从以上方法名可以猜测

[FriendAsistSessionMgr OnAddMsgForSpecialSession:MsgList:]
//是用来接收添加好友消息的函数处理,其中MsgList:后面的参数可能为消息的数组,为了证明我们可以打个断点查看下。

看出r3确实是个数组,同时也得到了消息的对象为CMessageWrap 证明我们是对的。

解释下为什么要看r3,因为在 armv7 中,一个方法的调用,一般寄存器都是这么存储的。前四个参数放在r0~r3,剩下的存放在堆栈中。查看堆栈的话使用x/10 $sp 查看前10个堆栈里的对象地址。(在 armv64 中 ,前八个参数放在r0~r7,剩下的存放在堆栈中。)

然而FriendAsistSessionMgr这个类可能需要一些初始化,且放在SayHelloViewController中,而我们想要的是不管在哪个控制器里都可以 hook 住上面的消息数组对象。因此我们往上找,[CMessageMgr MainThreadNotifyToExt:],然而里面并没有我们需要的信息。而根据类名我们推测CMessageMgr是用来管理消息的。有可能是在异步执行了消息数组的获取。

因此,重复以上步骤,使用 logify 对CMessageMgr进行 Log 分析。最终锁定了

CMessageMgr MessageReturn: MessageInfo:Event:


3.2 定位 通过好友请求的方法

3.2.1 动态分析

我们知道,通过好友请求的方法,是在新的朋友界面,点击查看的时候触发的。(可以通过 Log 分析,然而这里还有另一个比较快速的方法)找到“查看”按钮的对象

而我们知道 UIButton 是继承 UIControl 的,而 UIControl 的话可以通过allTargets 与 allControlEvents查看所有的对象与事件,再使用actionsForTarget:forControlEvent:从而找到触发的方法。

看出所触发的方法为[ContactsItemView onRightBtnAction]

3.2.2 静态分析

既然拿到了方法名,那我们怎么看他具体的实现呢?

接下来就是大名鼎鼎的hopper 登场了。

用 hopper 打开微信的二进制文件,并进行汇编与伪代码的转换。

由于汇编读起来比较晦涩,所以还是进行伪代码的转换,这样效率比较快。点击该按钮进行转换

上图我们看到了

r10 = self;
r5 = r10 + *0x33befe8;
r4 = objc_loadWeakRetained(r5);
r8 = @selector(onContactsItemViewRightButtonClick:);
r11 = [r4 respondsToSelector:r8];

可以得出,r11 = [r5 onContactsItemViewRightButtonClick:btn],

而 r5 我们判断为 self 的代{过}{滤}理,这个我们也可以通过在之前用 class-dump 的头文件里面搜索onContactsItemViewRightButtonClick,会发现在ContactsItemViewDelegate中。

也就是[ContactsItemView onRightBtnAction]内部调用了[self.delegate onContactsItemViewRightButtonClick:].

而 ContactsItemView 的delegate为 SayHelloViewController。

再用 hopper 定位onContactsItemViewRightButtonClick。

看到这里估计会很蒙不知道从何下手。这时候只要加以推测就可以了。

上图中进行了两个if判断,第一个为

	r10 = @selector(class);
r2 = loc_1c099bc(@class(CPushContact), r10);
r1 = @selector(isKindOfClass:);
r5 = loc_1c099bc(r4, r1, r2);
loc_1c099d4(r4);
if ((r5 & 0xff) != 0x0) {

可以得出其实是执行了 if([r4 isKindOfClass:[CPushContact class]]);

而r4是什么呢?可以肯定是 CPushContact 对象,不然下面的代码都不执行了。我们可以根据3.1的动态分析,通过lldb打断点,并查看r3寄存器的对象类型,可以看到该对象为 CPushContact 对象。因此r4就是 CPushContact 对象,根据字面意思可以得到就是联系人对象。

继续看下面的代码,可以看到也进行了一次判断if (((loc_1c099bc(r6, @selector(m_bSuspiciousUser)) & 0xff) != 0x0) && ((loc_1c099bc(r6, @selector(isMMContact)) & 0xff) == 0x0)),看到了MMUIAlertView。推测是弹窗的 view ,推测如果是可疑的用户或者当前申请的好友已经是自己的好友,那就进行弹窗。而另一部分为verifyContactWithOpCode:opcode:,推测该部分为添加好友的方法。

可以通过Log 分析或者通过 lldb 打断点,会看到都会进入该方法。且参数分别为 CPushContact 对象与 3。

接着继续分析verifyContactWithOpCode:opcode:方法。主要的部分如下所示。

通过分析,我们可以得到,确认好友申请,显示构造了CContactVerifyLogic对象。再构造了一个CVerifyContactWrap对象,并设置了相关属性,比如m_nsUsrName m_uiScene m_nsTicket.然后通过添加到数组中,通过CContactVerifyLogic对象的startWithVerifyContactWrap:opCode:parentView:fromChatRoom:方法发送。

代码如下:

CContactVerifyLogic *verifyLogic = [[CContactVerifyLogic alloc] init];
CVerifyContactWrap *wrap = [[CVerifyContactWrap alloc] init];
[wrap setM_nsUsrName:contact.m_nsEncodeUserName];
[wrap setM_uiScene:contact.m_uiFriendScene];
[wrap setM_nsTicket:contact.m_nsTicket];
[wrap setM_nsChatRoomUserName:contact.m_nsChatRoomUserName];
wrap.m_oVerifyContact = contact; AutoSetRemarkMgr *mgr = [[MMServiceCenter defaultCenter] getService:[AutoSetRemarkMgr class]];
id attr = [mgr GetStrangerAttribute:contact AttributeName:1001]; if([attr boolValue]) {
[wrap setM_uiWCFlag:(wrap.m_uiWCFlag | 1)];
}
[verifyLogic startWithVerifyContactWrap:[NSArray arrayWithObject:wrap] opCode:3 parentView:[UIView new] fromChatRoom:NO];

这样我们就得到了 获取好友请求的方法与添加好友的方法。
而这里还有一个问题,就是添加好友的对象是CPushContact,而获得好友请求的对象的CMessageWrap。这里需要进行转换,而转换的方法也在SayHelloViewController中,可以重复上面的分析方法获得。


五、编写Tweak

通过以上的分析,将代码合并起来

%hook CMessageMgr
- (void)MessageReturn:(unsigned int)arg1 MessageInfo:(NSDictionary *)info Event:(unsigned int)arg3 {
%orig;
if (arg1 == 332) { // 收到添加好友消息
NSString *keyStr = [info objectForKey:@"5"];
if ([keyStr isEqualToString:@"fmessage"]) {
NSArray *wrapArray = [info objectForKey:@"27"];
[self addAutoVerifyWithArray:wrapArray];
}
}
} %new
- (void)addAutoVerifyWithArray:(NSArray *)ary {
[ary enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
CPushContact *contact = [%c(SayHelloDataLogic) getContactFrom:obj];
if (![contact isMyContact] && [contact.m_nsDes isEqualToString:autoVerifyKeyword]) {
CContactVerifyLogic *verifyLogic = [[%c(CContactVerifyLogic) alloc] init];
CVerifyContactWrap *wrap = [[%c(CVerifyContactWrap) alloc] init];
[wrap setM_nsUsrName:contact.m_nsEncodeUserName];
[wrap setM_uiScene:contact.m_uiFriendScene];
[wrap setM_nsTicket:contact.m_nsTicket];
[wrap setM_nsChatRoomUserName:contact.m_nsChatRoomUserName];
wrap.m_oVerifyContact = contact; AutoSetRemarkMgr *mgr = [[%c(MMServiceCenter) defaultCenter] getService:%c(AutoSetRemarkMgr)];
id attr = [mgr GetStrangerAttribute:contact AttributeName:1001]; if([attr boolValue]) {
[wrap setM_uiWCFlag:(wrap.m_uiWCFlag | 1)];
}
[verifyLogic startWithVerifyContactWrap:[NSArray arrayWithObject:wrap] opCode:3 parentView:[UIView new] fromChatRoom:NO];
}
}];
}

五、总结

由于整个逆向流程有点繁琐,有时候也不是只要分析一次就可以成功的,需要反反复复的进行UI分析、LOG分析、lldb分析。因此可能在过程中,有些知识没有漏掉,希望大家可以指出。

iOS逆向实战与工具使用(微信添加好友自动确认)的相关教程结束。

《iOS逆向实战与工具使用(微信添加好友自动确认).doc》

下载本文的Word格式文档,以方便收藏与打印。