上次完成了 macOS 版微信小助手,如今終於有(xian)時(de)間(huang)來講說 iOS 逆向了。本篇主要實如今微信上自動添加好友(即自動驗證新的朋友申請),從而熟悉 iOS 逆向分析的過程,可能總結的有點粗糙,若是有不懂的地方歡迎探討。python
github地址: iOS 版微信小助手(防撤回、修改微信運動、羣管理、好友請求管理)ios
如下工具的詳細使用方法能夠查看iOS應用逆向工程 第2版 第二部分 工具篇。git
theosgithub
製做 Tweak 的工具數組
用於靜態分析bash
usbmuxd微信
端口轉發,可讓咱們經過 usb 鏈接手機進行 ssh、lldb 調試等。
主要使用apppython-client
目錄下的文件
class-dumpssh
dump 目標對象的 class 信息的工具.
調試神器,用過的都說好。默認自帶,在
/Applications/Xcode.app/Contents/Developer/usr/bin/lldb
中。
如下軟件在 Cydia 中便可下載(debugserver 除外)
實如今越獄手機上遠程進行 ssh 服務,經過 ssh,便可以經過終端鏈接 iPhone 進行控制。
**iOS 工具大部分都須要在 ssh 環境中使用**
複製代碼
腳本語言,用於 hook 正在運行的進程,並實時注入代碼。
用於在 Terminal 中查看手機的 log
用於鏈接手機進行 lldb 調試的工具。用 Xcode 在手機上進行 app 調試便可在iPhone目錄的
/Developer/usr/bin/
中生成。
使用 debugserver 須要先進行處理。由於缺乏task_for_pid
權限,因此調試不了其餘的 app。 先經過 ssh 拷貝 debugserver
scp root@iOSIP:/Developer/usr/bin/debugserver ~/debugserver // iOSIP 爲手機的ip地址
複製代碼
ldid - Sent.xml debugserver
複製代碼
在使用 ssh 拷貝至手機,完成。
思路:想要實現自動驗證好友請求,則要拿到獲取好友請求的方法,以及經過好友請求的方法。hook 獲取好友請求的方法,在接收到好友請求的時候,執行添加好友的方法。 而這些主要邏輯在「新的朋友」界面。
咱們知道,根據 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
後,使用如下命令查看當前 UI 佈局
UIApp.keyWindow.recursiveDescription().toString()
複製代碼
由於知道當前的視圖有 tableView,因此找到 tableView 的對象。從上圖能夠看到該對象的地址爲0x18c4be00。 在使用 nextResponder()
根據響應者往上找當前的 ViewController。
SayHelloViewController
使用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仍然沒法執行,須要進行修改:
再使用 theos 配置相關文件(具體查看iOS逆向-微信helloWorld), 而後進行make package install 安裝至手機。
從新啓動微信進入新的朋友界面。
在ssh中使用ondeviceconsole
打印手機的 log。
這時用另外一個微信號添加本身好友。觸發好友請求的方法。能夠看到如下的 log
說明有好友添加請求的時候,會調用 -[SayHelloViewController OnSayHelloDataChange]
既然已經知道了有好友請求的時候會調用OnSayHelloDataChange
,那麼咱們能夠在當前方法中進行處理,然而有個弊端,就是當有好友請求時,微信不在新的朋友界面時,是不會調用該方法的。因此咱們應該在更底層的類中(假設爲消息管理類)中進行處理,而怎麼找到消息管理類呢?按照通常的邏輯,消息管理類中必定有方法觸發了OnSayHelloDataChange
,這時候就要用到 lldb + hopper 神器來找到相應的消息管理類與其處理方法了。
lldb 進行手機端調試,hopper 進行靜態分析,分析OnSayHelloDataChange
方法的信息,找出突破口。
先用 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 中查看(按快捷鍵g,輸入地址),找到了對應的方法爲
// 調用的順序爲從下到上
[SayHelloViewController OnSayHelloDataChange]
[SayHelloDataLogic onFriendAssistAddMsg:]
[FriendAsistSessionMgr OnAddMsgForSpecialSession:MsgList:]
[CMessageMgr MainThreadNotifyToExt:]
複製代碼
從以上方法名能夠猜想
[FriendAsistSessionMgr OnAddMsgForSpecialSession:MsgList:]
複製代碼
是用來接收添加好友消息的函數處理,其中MsgList:
後面的參數可能爲消息的數組,爲了證實咱們能夠在該方法中打個斷點查看下。 使用命令register read
讀取寄存器地址,並使用po
打印該對象。
看出r3寄存器確實是個數組,同時也獲得了消息的對象爲CMessageWrap
證實咱們是對的。
ps: 解釋下爲何要看r3,由於在 armv7 中,一個方法的調用,通常寄存器都是這麼存儲的:前四個參數放在r0~r3,剩下的存放在堆棧中。查看堆棧的話使用x/10 $sp
查看前10個堆棧裏的對象地址。
然而FriendAsistSessionMgr
這個類可能在新的好友界面進行一些初始化,且放在SayHelloViewController
中,而咱們想要的是無論在哪一個控制器裏均可以 hook 住上面的消息數組對象。所以咱們往上找,[CMessageMgr MainThreadNotifyToExt:]
,然而裏面並無咱們須要的信息。而根據類名咱們推測CMessageMgr
是用來管理消息的。有多是在異步執行了消息數組的獲取。
所以,重複以上步驟,使用 logify 對CMessageMgr
進行 Log 分析。最終鎖定了 CMessageMgr MessageReturn:MessageInfo:Event:
既然找到了接收好友請求的方法,那麼是時候找經過好友請求的方法了。 咱們知道,經過好友請求的方法,是在新的朋友界面,點擊接受的時候觸發的。(能夠經過 Log 分析,然而這裏還有另外一個比較快速的方法)
先經過 Cycript 打印出全部的 UI 層級。 找到接受按鈕的對象,(有個技巧,咱們知道當前按鈕是在某個 cell 下面的,因此定位這個)。
再經過cycript將該對象的 hidden 動態修改成 1,看是否隱藏。#0x186922f0.hidden = 1
複製代碼
發現按鈕不見了,證實咱們是對的。這時候須要找到點擊按鈕的事件。
而咱們知道 UIButton 是繼承 UIControl 的,在 Cycript 中, 能夠經過allTargets
與 allControlEvents
查看當前UIControl全部的targets與events,再使用actionsForTarget:forControlEvent:
從而找到觸發的方法。
看出所觸發的方法爲[ContactsItemView onRightBtnAction]
既然拿到了方法名,那咱們怎麼看他具體的實現呢? 接下來就是大名鼎鼎的 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
對象,否則下面的代碼都不執行了。咱們能夠根據動態分析,經過 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
中,能夠重複上面的分析方法得到。
經過以上的分析,將代碼合併起來
%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 分析。