iOS逆向-微信自動添加好友

前言

上次完成了 macOS 版微信小助手,如今終於有(xian)時(de)間(huang)來講說 iOS 逆向了。本篇主要實如今微信上自動添加好友(即自動驗證新的朋友申請),從而熟悉 iOS 逆向分析的過程,可能總結的有點粗糙,若是有不懂的地方歡迎探討。python

github地址: iOS 版微信小助手(防撤回、修改微信運動、羣管理、好友請求管理)ios

工具

如下工具的詳細使用方法能夠查看iOS應用逆向工程 第2版 第二部分 工具篇。git

macbook 軟件

  • theosgithub

    製做 Tweak 的工具數組

  • hopper disassemblersass

    用於靜態分析bash

  • usbmuxd微信

    端口轉發,可讓咱們經過 usb 鏈接手機進行 ssh、lldb 調試等。主要使用python-client目錄下的文件app

  • class-dumpssh

dump 目標對象的 class 信息的工具.

  • lldb

調試神器,用過的都說好。默認自帶,在/Applications/Xcode.app/Contents/Developer/usr/bin/lldb 中。

越獄 iPhone 軟件

如下軟件在 Cydia 中便可下載(debugserver 除外)

  • OpenSSH

實如今越獄手機上遠程進行 ssh 服務,經過 ssh,便可以經過終端鏈接 iPhone 進行控制。

**iOS 工具大部分都須要在 ssh 環境中使用**
複製代碼
  • Cycript

腳本語言,用於 hook 正在運行的進程,並實時注入代碼。

  • ondeviceconsole

用於在 Terminal 中查看手機的 log

  • debugserver

用於鏈接手機進行 lldb 調試的工具。用 Xcode 在手機上進行 app 調試便可在iPhone目錄的 /Developer/usr/bin/ 中生成。

使用 debugserver 須要先進行處理。由於缺乏task_for_pid權限,因此調試不了其餘的 app。 先經過 ssh 拷貝 debugserver

scp root@iOSIP:/Developer/usr/bin/debugserver ~/debugserver // iOSIP 爲手機的ip地址
複製代碼

下載 ldident,進行

ldid - Sent.xml debugserver
複製代碼

在使用 ssh 拷貝至手機,完成。

分析

思路:想要實現自動驗證好友請求,則要拿到獲取好友請求的方法,以及經過好友請求的方法。hook 獲取好友請求的方法,在接收到好友請求的時候,執行添加好友的方法。 而這些主要邏輯在「新的朋友」界面。

定位好友請求的方法

UI 分析

咱們知道,根據 MVC 模式,通常的方法實現都是在 ViewController 中的,因此想要拿到好友請求的方法,要先拿到當前界面的控制器。而這時候能夠經過 UI 分析得到。

先打開新的朋友界面。

使用 usbmuxd 進行端口的轉發(若手機不卡,能夠跳過這步直接使用 ssh 進行 wifi 遠程鏈接)

python tcprelay.py -t 22:2222
複製代碼

再使用 ssh 鏈接至手機

ssh root@localhost -p 2222
// ssh root@192.168.31.94 若是是wifi鏈接,請查看當前手機的wifi地址。
複製代碼

查看微信的進程信息

ps -e |grep WeChat
複製代碼

Cycript 注入

cycript -p WeChat // 或者是當前微信的進程號,以下所示
複製代碼

cycript

啓動Cycript後,使用如下命令查看當前 UI 佈局

UIApp.keyWindow.recursiveDescription().toString()
複製代碼

屏幕快照 2017-04-01 上午10.57.28.png

由於知道當前的視圖有 tableView,因此找到 tableView 的對象。從上圖能夠看到該對象的地址爲0x18c4be00。 在使用 nextResponder()根據響應者往上找當前的 ViewController。

屏幕快照 2017-04-01 上午11.01.03.png
找到當前的控制器,爲 SayHelloViewController

Log 分析

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

class-dump -S -s -H demo.app -o ~/Document/headers/
// dump 微信app的頭文件保存在 ~/Document/headers/ 目錄中
複製代碼

再使用 theos 的 logify 工具,該工具用來注入NSLog來打印方法的入參和出參。(就是在 hook 某個類的全部的方法,並在裏面加 log,並導出xm文件)

logify.pl  ~/Document/headers/SayHelloViewController.h > ~/Desktop/Tweak.xm
複製代碼

**注意:**通常該Tweak.xm仍然沒法執行,須要進行修改:

  • 去掉 .cxx_destruct 方法
  • 將 HBLogDebug 改成 NSLog
  • 去掉全部的 delegate
  • 將全部的參數對象類型改爲 id
  • 去掉全部的 weak

再使用 theos 配置相關文件(具體查看iOS逆向-微信helloWorld), 而後進行make package install 安裝至手機。

從新啓動微信進入新的朋友界面。

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

這時用另外一個微信號添加本身好友。觸發好友請求的方法。能夠看到如下的 log

屏幕快照 2017-04-01 上午11.22.48.png

說明有好友添加請求的時候,會調用 -[SayHelloViewController OnSayHelloDataChange]

動態分析

既然已經知道了有好友請求的時候會調用OnSayHelloDataChange,那麼咱們能夠在當前方法中進行處理,然而有個弊端,就是當有好友請求時,微信不在新的朋友界面時,是不會調用該方法的。因此咱們應該在更底層的類中(假設爲消息管理類)中進行處理,而怎麼找到消息管理類呢?按照通常的邏輯,消息管理類中必定有方法觸發了OnSayHelloDataChange,這時候就要用到 lldb + hopper 神器來找到相應的消息管理類與其處理方法了。

lldb 進行手機端調試,hopper 進行靜態分析,分析OnSayHelloDataChange方法的信息,找出突破口。

先用 hopper 打開微信的二進制文件。搜索SayHelloViewController OnSayHelloDataChange方法。 能夠看到當前方法在微信中的偏移地址0x14a4824。

QQ20170401-113150@2x.png

啓動debugserver 配合lldb調試

先打開微信,並使用 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(這個值不是定值)

QQ20170401-113326@2x.png

經過以上能夠找到 [SayHelloViewController OnSayHelloDataChange]方法在手機上的內存地址。即

內存地址 = 進程內存基地址 + 方法偏移地址
複製代碼

使用br打斷點查看

br s -a "0x000b2000 + 0x14a4824"
複製代碼

屏幕快照 2017-04-01 上午11.39.22.png

接着輸入c繼續運行,從新使用另外一微信帳號添加好友,會觸發該斷點。

屏幕快照 2017-04-01 上午11.41.35.png

使用bt查看調用棧信息,即哪些方法調用了當前的方法,找到方法的上游。(異步調用的話沒辦法查看)

QQ20170401-114245@2x.png

第一個表示當前的方法,能夠看到在調用此方法前,該進程總共調用了3個方法。 分別計算出這三個方法在微信中的偏移量。

屏幕快照 2017-04-01 上午11.48.13.png

將這三個地址在 hopper 中查看(按快捷鍵g,輸入地址),找到了對應的方法爲

// 調用的順序爲從下到上
[SayHelloViewController OnSayHelloDataChange]
[SayHelloDataLogic onFriendAssistAddMsg:]
[FriendAsistSessionMgr OnAddMsgForSpecialSession:MsgList:]
[CMessageMgr MainThreadNotifyToExt:]
複製代碼

從以上方法名能夠猜想

[FriendAsistSessionMgr OnAddMsgForSpecialSession:MsgList:]
複製代碼

是用來接收添加好友消息的函數處理,其中MsgList:後面的參數可能爲消息的數組,爲了證實咱們能夠在該方法中打個斷點查看下。 使用命令register read讀取寄存器地址,並使用po打印該對象。

屏幕快照 2017-04-01 下午2.05.24.png

看出r3寄存器確實是個數組,同時也獲得了消息的對象爲CMessageWrap 證實咱們是對的。

ps: 解釋下爲何要看r3,由於在 armv7 中,一個方法的調用,通常寄存器都是這麼存儲的:前四個參數放在r0~r3,剩下的存放在堆棧中。查看堆棧的話使用x/10 $sp 查看前10個堆棧裏的對象地址。

然而FriendAsistSessionMgr這個類可能在新的好友界面進行一些初始化,且放在SayHelloViewController中,而咱們想要的是無論在哪一個控制器裏均可以 hook 住上面的消息數組對象。所以咱們往上找,[CMessageMgr MainThreadNotifyToExt:],然而裏面並無咱們須要的信息。而根據類名咱們推測CMessageMgr是用來管理消息的。有多是在異步執行了消息數組的獲取。

所以,重複以上步驟,使用 logify 對CMessageMgr進行 Log 分析。最終鎖定了 CMessageMgr MessageReturn:MessageInfo:Event:

屏幕快照 2017-04-05 下午3.02.50.png

定位經過好友請求的方法

動態分析

既然找到了接收好友請求的方法,那麼是時候找經過好友請求的方法了。 咱們知道,經過好友請求的方法,是在新的朋友界面,點擊接受的時候觸發的。(能夠經過 Log 分析,然而這裏還有另外一個比較快速的方法)

Cycript 定位

先經過 Cycript 打印出全部的 UI 層級。 找到接受按鈕的對象,(有個技巧,咱們知道當前按鈕是在某個 cell 下面的,因此定位這個)。

WX20170405-152315@2x.png
再經過cycript將該對象的 hidden 動態修改成 1,看是否隱藏。

#0x186922f0.hidden = 1
複製代碼

發現按鈕不見了,證實咱們是對的。這時候須要找到點擊按鈕的事件。

而咱們知道 UIButton 是繼承 UIControl 的,在 Cycript 中, 能夠經過allTargetsallControlEvents查看當前UIControl全部的targets與events,再使用actionsForTarget:forControlEvent:從而找到觸發的方法。

屏幕快照 2017-04-05 下午3.30.08.png

看出所觸發的方法爲[ContactsItemView onRightBtnAction]

靜態分析

既然拿到了方法名,那咱們怎麼看他具體的實現呢? 接下來就是大名鼎鼎的 hopper 登場了。 用 hopper 打開微信的二進制文件,並進行彙編與僞代碼的轉換。 ~~因爲彙編讀起來比較晦澀,因此仍是進行僞代碼的轉換,這樣效率比較快。~~點擊該按鈕進行轉換

QQ20170401-094655@2x.png

能夠獲得僞代碼

屏幕快照 2017-04-05 下午3.56.21.png
上圖咱們看到了

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:]. 而 ContactsItemViewdelegateSayHelloViewController

再用 hopper 定位onContactsItemViewRightButtonClick

屏幕快照 2017-04-05 下午4.05.49.png
屏幕快照 2017-04-05 下午4.14.09.png

看到這裏估計會很懵逼不知道從何下手。這時候只要加以推測就能夠了。 上圖中進行了兩個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對象,否則下面的代碼都不執行了。咱們能夠根據動態分析,經過 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:方法。主要的部分以下所示。

Paste_Image.png

經過分析,咱們能夠獲得,確認好友申請,顯示構造了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];
            }
    }];
}
複製代碼

總結

本文爲本人根據iOS應用逆向工程 第2版的內容進行分析,因爲整個逆向流程有點繁瑣,有時候也不是隻要分析一次就能夠成功的,須要反反覆覆的進行UI分析、Log分析、lldb 分析。

參考

iOS應用逆向工程 第2版
移動App入侵與逆向破解技術-iOS篇

相關文章
相關標籤/搜索