iOS逆向?qū)崙?zhàn)(微信自動搶紅包上)

屏幕快照 2018-01-05 下午6.36.07.png

今天開始介紹如何一步步逆向微信app,寫一個自動搶紅包插件。我們最終成品是寫一個能自動搶紅包并且在非越獄手機(jī)上安裝的多開微信。

我暫時(shí)打算分3篇文章介紹整個逆向開發(fā)過程。逆向的是目前為止微信最新版本6.6.1。我們能做到的是微信APP在前臺任何頁面有紅包消息到來時(shí),實(shí)現(xiàn)自動搶紅包。

前提

1. 一臺越獄iPhone手機(jī),最好是iPhone5s及以上型號手機(jī),因?yàn)閺膇Phone5s后全是arm64處理器。
2. 逆向工具集
  • 檢測工具
    如:Reveal、tcpdump等

  • 反編譯工具(反匯編工具 - 分析二進(jìn)制文件并得到一些信息)
    如:IDA、Hopper Disassembler、classdump等

  • 調(diào)試工具
    如:lldb、Cycript等

  • 開發(fā)工具
    如:Xcode、theos等

  • 打包工具

    如:iTools、iOS App Signer等

  • 輔助工具

    如:OPENSSH、vim、ps、network-cmds等

3. 基本的逆向知識和工具使用技能
4. 一顆戒驕戒躁、勇于嘗試的心

關(guān)于逆向開發(fā)和工具集的介紹、使用我在原來的文章中已經(jīng)介紹過,我們在這里不再介紹直接使用。有疑惑或者興趣的童鞋可以關(guān)注我微信公眾號:樂Coding查看。

砸殼導(dǎo)出頭文件

砸殼把微信的Mach-O文件拷貝到電腦,然后用classdump導(dǎo)出頭文件留作備用。這兩步我在逆向第一課中已經(jīng)介紹過不在贅述。如果覺得自己砸殼還得拷貝到電腦麻煩,可以從PP助手上下載相同版本的越獄包。

查找收紅包方法

我們要實(shí)現(xiàn)在任何頁面都能自動搶紅包,首先我們要找到收紅包的接口,然后再實(shí)現(xiàn)拆紅包和搶紅包。

如何找到收紅包的接口呢,我們要從聊天頁面開始。看看當(dāng)紅包消息到來時(shí),哪些方法會被調(diào)用。

一、定位聊天控制器

在越獄手機(jī)上打開微信,點(diǎn)擊進(jìn)入單聊或者群聊頁面。

1. 打開Mac終端,ssh連接到手機(jī)

這里的192.168.220.195是我越獄手機(jī)的IP地址,你操作時(shí)要換成自己手機(jī)的IP地址

ssh root@192.168.220.195
2. 查看微信進(jìn)程ID
ps -e | grep WeChat
1001.png
3. 使用cycript附加進(jìn)程
cycript -p WeChat
4. 找到當(dāng)前的控制器

找到當(dāng)前的聊天控制器名字可以使用Reveal一眼看到,也可以使用下面我介紹的第二種方法。

4.1 打印UIView對象

UIApp.keyWindow.recursiveDescription().toString()

打印結(jié)果如下:

1002.png

4.2 選擇任意一個控件,用控件地址調(diào)用nextResponder方法,找到控制器。

如果一步不是,就遞歸調(diào)用,直到nextResponder是ViewController為止。

我們以上圖中的藍(lán)色標(biāo)注ImageView為例查找。

1003.png

所以聊天的控制器就是BaseMsgContentViewController。查看我們導(dǎo)出的頭文件也能找到同名的BaseMsgContentViewController.h文件。

二、 創(chuàng)建Tweak項(xiàng)目

我們已經(jīng)找到了聊天頁面的控制器名,并且發(fā)現(xiàn)有同名的頭文件。下面我們先來創(chuàng)建一個Tweak項(xiàng)目用于以后的調(diào)試。

在Mac上創(chuàng)建微信的Tweak項(xiàng)目,不清楚的請移步逆向第二課。

創(chuàng)建命令和最終項(xiàng)目目錄如下圖:

1004.png
1005.png

創(chuàng)建成功后,Makefile初始內(nèi)容:

include $(THEOS)/makefiles/common.mk
TWEAK_NAME = WXRedTweak
WXRedTweak_FILES = Tweak.xm
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
    install.exec "killall -9 WeChat"

添加一些便于測試的配置,修改后如下:

ARCHS = armv7 arm64                  #支持cpu類型
TARGET = iphone:latest:8.0           #最低支持版本
THEOS_DEVICE_IP = 192.168.220.195    #手機(jī)的ip
include $(THEOS)/makefiles/common.mk

TWEAK_NAME = WXRedTweak
WXRedTweak_FILES = Tweak.xm
WXRedTweak_FRAMEWORKS = UIKit       #需要的框架

include $(THEOS_MAKE_PATH)/tweak.mk

clean::
    rm -rf ./packages/* 

after-install::
    install.exec "killall -9 WeChat"

三、查找BaseMsgContentViewController中接收消息的方法

1. 使用logify.pl給BaseMsgContentViewController所有函數(shù)添加log
/opt/theos/bin/logify.pl /Users/lixingle/Desktop/test/result/Headers/BaseMsgContentViewController.h > /Users/lixingle/Desktop/test/result/Tweak/WXRedTweak/wxredtweak/Tweak.xm

執(zhí)行完以上命令,查看Tweak.xm文件就會發(fā)現(xiàn)已經(jīng)給BaseMsgContentViewController的所有方法添加上了log

1006.png

**1.1 **刪除影響編譯的函數(shù)

Tweak項(xiàng)目執(zhí)行make命令,發(fā)現(xiàn)那個函數(shù)影響編譯先刪除。

最終刪除的函數(shù)有

10061.png

**1.2 ** 編譯安裝到越獄手機(jī):


1007.png

1.3 在越獄手機(jī)上查看log

這里需要另一個手機(jī)給這臺越獄手機(jī)發(fā)紅包,有女票的可以用女票的手機(jī),沒有的用別人女票的,像我這樣都沒有的只能注冊兩個微信號了。

  • 在越獄手機(jī)終端執(zhí)行查看log命令tail -f /var/log/syslog | grep WeChat
  • 用另一個手機(jī)給這臺越獄手機(jī)發(fā)送一個紅包或者文本消息,
  • 多次嘗試上一步,查看log有沒有規(guī)律

經(jīng)過多次分析 — 刪除無用函數(shù) — 編譯安裝 — 再分析的過程,最后只剩下幾個和message相關(guān)的函數(shù)

1008.png

根據(jù)常識定位到與消息響應(yīng)相關(guān)的方法:

-(void)addMessageNode:(id)arg1 layout:(_Bool)arg2 addMoreMsg:(_Bool)arg3 { %log; %orig; }

四、 查找全局接收消息的方法

我們知道了聊天頁面響應(yīng)消息的方法,下面就需要分析這個方法的調(diào)用堆棧,找到那個最頂層接收消息的方法。猜測可能是通知、代理、監(jiān)聽或者管理中心之類機(jī)制會在消息到來時(shí)告知聊天控制器。那我們應(yīng)該怎么分析呢?答案就是通過lldb動態(tài)調(diào)試。

1. lldb 調(diào)試準(zhǔn)備

1.1 在越獄手機(jī)終端中輸入

debugserver *:9527 -a "WeChat"

1009.png

1.2 重新打開一個Mac終端,進(jìn)入lldb后執(zhí)行

process connect connect://192.168.220.195:9527

1010.png
2. 分析addMessageNode函數(shù)調(diào)用堆棧

-(void)addMessageNode:(id)arg1 layout:(_Bool)arg2 addMoreMsg:(_Bool)arg3方法添加斷點(diǎn),首先要找到該方法的內(nèi)存地址也就是該函數(shù)偏移后的基地址。

偏移后的基地址=偏移前基地址+指令所在模塊的ASLR偏移

**2.1 ** 查找ASLR偏移地址

在lldb中輸入image list -o -f命令,執(zhí)行結(jié)果如下,第一個就是ASLR地址:0x94000

1011.png

2.2 查看偏移前函數(shù)基地址

打開IDA或者Hopper,搜索addMessageNode,就可以找到。我用的是IDA反匯編微信的Mach-O文件。下圖中標(biāo)紅的位置就是該函數(shù)偏移前的基地址:0x0000000101FA957C

1012.png

所以addMessageNode的內(nèi)存偏移后地址 = 0x101FA957C + 0x94000 = 0x10203D57C

2.3 添加斷點(diǎn)

lldb中在0x10203D57C處添加斷點(diǎn)br s -a 0x10203D57C,添加成功后,給微信發(fā)送一條信息看能不能進(jìn)斷點(diǎn)。

發(fā)送一條消息果然進(jìn)斷點(diǎn)了,這時(shí)執(zhí)行bt命令查看調(diào)用堆棧如下:

1013.png

2.4 依次把上圖中的fame#0 - frame4對應(yīng)地址翻譯成函數(shù)名

偏移前基地址 = 偏移后地址 - ASLR偏移地址

frame #0 = 0x000000010203d57c - 0x94000 = 0x101FA957C

frame #1 = 0x00000001022cc920 - 0x94000 = 0x102238920

frame #2 = 0x00000001022b4c38 - 0x94000 = 0x102220C38

frame #3 = 0x0000000104a479a0 - 0x94000 = 0x1049B39A0

frame #4 = 0x0000000102b661dc - 0x94000 = 0x102AD21DC

在lldb中依次查看偏移前基地址對應(yīng)函數(shù)名

0x101FA957C:[BaseMsgContentViewController addMessageNode:layout:addMoreMsg:]

0x102238920:[BaseMsgContentLogicController DidAddMsg:]

0x102220C38:[BaseMsgContentLogicController OnAddMsg:MsgWrap:]

0x1049B39A0:MMCommon`_callExtension + 480 //擴(kuò)展函數(shù),排除

0x102AD21DC:[CMessageMgr MainThreadNotifyToExt:]

2.5 繼續(xù)查找堆棧中函數(shù)

依次在frame0 、1、2、3 、5處添加斷點(diǎn),查看當(dāng)退出聊天室在其他頁面時(shí)能否進(jìn)斷點(diǎn)。

經(jīng)過測試,只有frmae #5: 0x0000000102b661dc處設(shè)置的斷點(diǎn),無論在微信任何頁面收到紅包都能觸發(fā)斷點(diǎn)。

1014.png

0x0000000102b661dc對應(yīng)的方法名我們已經(jīng)在上一步分析到是:[CMessageMgr MainThreadNotifyToExt:],到此我們初步分析到一個收到消息會調(diào)用的全局函數(shù)[CMessageMgr MainThreadNotifyToExt:]。

五、 分析CMessageMgr類

接下來我們需要分析CMessageMgr看這個名字像是一個消息管理中心,可能離我們要找的全局接收消息的函數(shù)不遠(yuǎn)了。

1. logify分析CMessageMgr

按照第三部logify.pl添加log的方式分析CMessageMgr類。這里我們不在贅述具體步驟。

經(jīng)過分析log,消息到來時(shí)按照先后順序會依次調(diào)用一下10個函數(shù),當(dāng)然并不是每一個都是接收消息方法。

1015.png

根據(jù)函數(shù)名和程序員第七感我們著重分析以下四個函數(shù):

- (void)CheckMessageStatus:(id)arg1 Msg:(id)arg2 { %log; %orig; }
- (void)AsyncOnPreAddMsg:(id)arg1 MsgWrap:(id)arg2 { %log; %orig; }
- (void)AsyncOnAddMsg:(id)arg1 MsgWrap:(id)arg2 { %log; %orig; }
- (void)AsyncOnPushMsg:(id)arg1 { %log; %orig; }

1.1 當(dāng)每次進(jìn)聊天室時(shí)都會調(diào)用CheckMessageStatus,所以排除它。

1.2 AsyncOnPreAddMsg、AsyncOnAddMsg、AsyncOnPushMsg這三個方法都是在消息到來時(shí)觸發(fā),從
方法命名AsyncOnPreAddMsg應(yīng)該是消息的前置處理,AsyncOnPushMsg可能是往隊(duì)列里面添加消息。
這三個方法都可以用來備選,我們先選AsyncOnAddMsg。

2. 分析AsyncOnAddMsg
%hook CMessageMgr
- (void)AsyncOnAddMsg:(id)arg1 MsgWrap:(id)arg2 {
    NSLog(@"arg1 = %@ , arg2 = %@", arg1, arg2);
    NSLog(@"arg1 class = %@ , arg2 class = %@", [arg1 class], [arg2 class]);
    %orig;
}
%end

log輸出如下:

1016.png

arg1:NSString

arg2: CMessageWrap

我們也能在頭文件中找到CMessageWrap同名的頭文件,所以函數(shù)聲明如下:

- (void)AsyncOnAddMsg:(NSString *)wxid MsgWrap:(CMessageWrap *)wrap;
3. 分析CMessageWrap消息內(nèi)容相關(guān)的類

經(jīng)過分析CMessageWrap頭文件和查看不同消息類型的log輸出,Tweak最終修改如下:

@interface CMessageWrap
@property (nonatomic, strong) NSString* m_nsContent;
@property (nonatomic, assign) NSInteger m_uiMessageType;
@property(retain, nonatomic) NSString *m_nsFromUsr;
@property(retain, nonatomic) NSString *m_nsToUsr;
@property(retain, nonatomic) NSString *m_nsAtUserList;
@property(retain, nonatomic) NSString *m_nsBizChatId;
@property(retain, nonatomic) NSString *m_nsBizClientMsgID;
@property(retain, nonatomic) NSString *m_nsDisplayName;
@property(retain, nonatomic) NSString *m_nsKFWorkerOpenID;
@property(retain, nonatomic) NSString *m_nsMsgSource;
@property(retain, nonatomic) NSString *m_nsPattern;
@property(retain, nonatomic) NSString *m_nsPushContent;
@property(retain, nonatomic) NSString *m_nsRealChatUsr;
@end

%hook CMessageMgr
- (void)AsyncOnAddMsg:(NSString *)wxid MsgWrap:(CMessageWrap *)wrap {
    %orig;
    NSInteger uiMessageType = [wrap m_uiMessageType];
    NSString* content = [wrap m_nsContent];
    NSString* nsFromUsr = [wrap m_nsFromUsr];
    NSString* nsToUsr = [wrap m_nsToUsr];
    NSString* nsAtUserList = [wrap m_nsAtUserList];
    NSString* nsBizChatId = [wrap m_nsBizChatId];
    NSString* nsBizClientMsgID = [wrap m_nsBizClientMsgID];
    NSString* nsKFWorkerOpenID = [wrap m_nsKFWorkerOpenID];
    NSString* nsMsgSource = [wrap m_nsMsgSource];
    NSString* nsDisplayName = [wrap m_nsDisplayName];
    NSString* nsPattern = [wrap m_nsPattern];
    NSString* nsRealChatUsr = [wrap m_nsRealChatUsr];
    NSString* nsPushContent = [wrap m_nsPushContent];

    NSLog(@"m_uiMessageType=%zd m_nsContent=%@ m_nsFromUsr=%@ m_nsToUsr=%@ m_nsAtUserList=%@ m_nsBizChatId=%@ m_nsBizClientMsgID=%@ m_nsDisplayName=%@ m_nsKFWorkerOpenID=%@ m_nsMsgSource=%@ m_nsPattern=%@ m_nsPushContent=%@ m_nsRealChatUsr=%@",
        uiMessageType,
        content,
        nsFromUsr,
        nsToUsr,
        nsAtUserList,
        nsBizChatId,
        nsBizClientMsgID,
        nsDisplayName,
        nsKFWorkerOpenID,
        nsMsgSource,
        nsPattern,
        nsPushContent,
        nsRealChatUsr);
    //記錄消息
    if( 1 == uiMessageType ){ //普通消息
        if( 0 == nsPushContent.length){
            if([nsToUsr rangeOfString:@"filehelper"].location != NSNotFound){
                NSLog(@"[文件助手: %@]",content);
            }else {
                NSLog(@"[我: %@]",content);
            }
        }else{
            NSLog(@"[%@]",nsPushContent);
        }
    }else if ( 3 == uiMessageType ){ //圖片消息
        NSLog(@"收到圖片消息");
    }else if ( 49 == uiMessageType ){ //紅包消息
        NSLog(@"收到紅包消息");
    }
}
%end

至此我們已經(jīng)找到了微信中全局接收消息的方法和紅包的類型類型uiMessageType = 49。接下來我們只需要找到搶紅包的方法,然后當(dāng)紅包消息到來時(shí)自動調(diào)用搶紅包方法就可以了。

何去何從

萬里長征我們邁出了第一步,更難的還在后邊。接下來我會用2篇文章講解怎么反匯編找到搶紅包的函數(shù),怎么在設(shè)置頁面添加一個自動搶紅包的開關(guān)以及微信的多開。敬請期待...


想及時(shí)獲得最新微信自動搶紅包文章,請關(guān)注微信公眾賬號:樂Coding,或者微信掃描下方二維碼。

lecoding

icon.jpg

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容