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 分析获得。

  • 先打开新的朋友界面。

// 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逆向实战与工具使用(微信添加好友自动确认)的更多相关文章

  1. 逆向实战干货,植物大战僵尸快速定位自动捡阳光Call,或者标志

    逆向实战干货,快速定位自动捡阳光Call,或者标志 注意: 关于CE和OD的使用,这里不再多说,快速定位,默认大家已经有了CE基础,或者OD基础. 第一种方法,找Call 第一步,打开CE,搜索阳光值 ...

  2. iOS - 逆向调试自用工具(reveal 14 Hopper Go2Shell ifunboxmac MachOView Alfred Go2Shell iTerm)

    研究了挺长一段时间的逆向感觉没啥可弄的了,再深处对我也用处不大,渐渐兴趣有些掉头了.最近有问我要工具的朋友,分享一些常用工具给用到的朋友. 用法自己百度吧,这里不再赘述. 图例(希望看到你想要的): ...

  3. iOS逆向开发(1):基础工具 | ssh | scp | socat

    小白:小程,我一直想问,什么是逆向来着?是逆向行驶吗? 小程:理解为逆向行驶也没错.一般的项目是从无到有,而逆向是从已有的状态入手,分析出已有的流程与结构的手段. iOS上的逆向开发,是一件有趣的事情 ...

  4. iOS逆向之一 工具的安装和使用

    iOS逆向之一-工具的安装和使用 最近在学习iOS安全方面的技术,有些东西就记录下来了,所有有了这篇文章.顺便也上传了DEMO,可以再这里找到这些DEMO的源码:dhar/iOSReProject 越 ...

  5. iOS逆向分析app

    适合有一定的逆向编程基础的人看. 背景:自动抢红包的脚本工具:cyscript,reveal,class-dump,越狱的pod等. 这里先上一张reveal的分析图: 小结:获取到了真个软件的整体结 ...

  6. iOS逆向+越狱

    感觉本文涉及内容有点多的,但是自己不愿意写太多,就简单的谢谢关于ios上手的东西吧 初级入手不免要用到,pp助手,i4 tools等 iOS逆向-ipa包重签名及非越狱手机安装多个应用 1.常识 我们 ...

  7. iOS 逆向之ARM汇编

    最近对iOS逆向工程很感兴趣. 目前iOS逆向的书籍有: <Hacking and Securing IOS Applications>, <iOS Hacker's Handboo ...

  8. ios逆向过程中lldb调试技巧

    在ios逆向过程中,善于运用lldb,会给逆向带来很大的方便 一般的命令: 1.image list -o -f  看看各个模块在内存中的基址 2.register read r0  读取寄存器r0的 ...

  9. 逆向实战第一讲,寻找OllyDbg调试工具的Bug并修复

    逆向实战第一讲,寻找OllyDbg调试工具的Bug并修复 首先我们要知道这个OD的Bug是什么. 我们调试一个UNICODE的窗口,看下其窗口过程. 一丶查看OllyDbg 的Bug 1.1spy++ ...

随机推荐

  1. 【NOIP2014】 联合权值

    [题目链接] 点击打开链接 [算法] 如果(u,v)的距离为2,那么有两种可能 : 1.u和v为祖孙关系 2.u和v为兄弟关系 树形DP即可,详见代码 [代码] #include<bits/st ...

  2. [yii2]Module的Namespace和控制器位置

    namespace和目录对应,否则无法找到控制器类,module文件在根路径 使用gii生成Module为\app\admin,那么 namespace app; class admin extend ...

  3. 超实用的JavaScript技巧及最佳实践给

    1.数组创建一个随机项 var items = [12,548,'a',2,5478,'foo',8852,,'Doe',2145,119]; var randomItem = items[Math. ...

  4. python学习笔记7-异常处理

    1 写弄成了读 1 try: fh = open("testfile", "r") fh.write("This is my test file fo ...

  5. UVa 1336 Fixing the Great Wall (区间DP)

    题意:给定 n 个结点,表示要修复的点,然后机器人每秒以 v 的速度移动,初始位置在 x,然后修复结点时不花费时间,但是如果有的结点暂时没修复, 那么每秒它的费用都会增加 d,修复要花费 c,坐标是 ...

  6. 【WIP】iOS Xcode基础

    创建: 2018/04/18 Xcode基本操作  创建项目处的填空  Product Name 应用名 英语字母  Organization Name 公司/组织/个人名 英语字母  Organiz ...

  7. poj1015【DP.......无奈了】

    首先,读题,真是一窍不通.后来看完程序的意思,才明白吧.. 题意: n个人中选m个,条件是取sum|D-P|最小,当有|D-P|相同的时候取|D+P|最大的.然后输出那些m个人的sumD,sumP,最 ...

  8. sublime text3 注册码

    —– BEGIN LICENSE —– TwitterInc 200 User License EA7E-890007 1D77F72E 390CDD93 4DCBA022 FAF60790 61AA ...

  9. poj 2774 Long Long Message【SA】

    把两个串接到一起求一个SA,然后找最大的sa[i]和sa[i-1]不是一个串的he[i] #include<iostream> #include<cstdio> #includ ...

  10. oatu2.0认证原理(转)

    今天有时间总结一下: 一.OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版. 在详细讲解OAuth 2.0之前,需要了解几个专用名词,理 ...