我是怎樣一步一步實現微信自動搶紅包的?

在逆向工程中,我們可以使用靜態分析和動態調試的方法去尋找我們的目標函數!本文我將分享一下逆向微信實現微信自動搶紅包的實戰經驗越獄,非越獄機器 都可適用,項目 GitHub 地址: RHWeChat

本項目在以下環境下測試運行:

  • 非越獄 iOS10.3.2,iPhone 6設備
  • 開發環境:Xcode 8.3.2 , MonkeyDev
簡單說下環境搭建:
  • 安裝最新的 theos:命令行式的開發 Tweak 的編譯環境
sudo git clone --recursive https://github.com/theos/theos.git /opt/theos
  • 安裝 ldid:簽名工具
    • 自己編譯
    • 使用 brew 安裝, 有點慢!
// 自己編譯
sudo mv ldidpath /opt/theos/bin
sudo chmod 777 /opt/theos/bin/ldid
// brew 安裝
brew install ldid
  • 安裝 MonkeyDev:基于 theos 實現的Xcode的開發環境。
git clone https://github.com/AloneMonkey/MonkeyDev.git
cd MonkeyDev/bin
sudo ./md-install

關于MonkeyDev詳情,請參見:MonkeyDev的文檔
大家踴躍給猴神 star 啊!
猴神在他的 MonkeyDev 中,默認集成了Reveal.framework,Cycript.framework,class-dump;并且在MonkeyDev中,你不需要手動提取ipa中的二進制文件, 修改二進制文件的Load Commands列表,加入要hook的dylib ,hook.dylib在函數constructor函數中完成對特定函數的hook,對修改后的ipa進行重簽名,打包和安裝,等一系列復雜的過程!Command+R 一鍵搞定,若要生成 ipa 文件只需在 Command+R 運行之后在源代碼的 LatestBuild 目錄雙擊createIPA.command 生成。

逆向思路:

要實現自動搶紅包的功能,我們首先應該知道一個手動搶紅包的流程!
所以我們先分析手動搶紅包實現:

  • 首先我們從 UI 入手
    使用 reveal 找到打開紅包的按鈕,獲取內存地址,通過 UIControl 的以下方法獲取target和action
- (NSSet *)allTargets;                                                                     // set may include NSNull to indicate at least one nil target
- (UIControlEvents)allControlEvents;                                                       
- (nullable NSArray<NSString *> *)actionsForTarget:(nullable id)target forControlEvent:(UIControlEvents)controlEvent;    // single event. returns NSArray of NSString selector names. returns nil if none
image.png
  • 打開 Hopper Disassembler,載入經過解密的微信可執行文件,搜索得到的 target 和 action,分別對應:WCRedEnvelopesReceiveHomeView 的 OnOpenRedEnvelopes 方法!
image.png
  • 由匯編代碼得到他的 delegate 調用了
    WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes 方法
  • 我們繼續在 class-dump 得到的頭文件中搜索關鍵字WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes 方法
image.png
  • 我們找到了 WCRedEnvelopesReceiveControlLogic 類,實現了該代理方法,所以我們繼續打開 Hopper Disassembler 搜索WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes 方法
image.png
image.png
image.png
  • 可以大致分析出拆開一個紅包大致需要 msgtype,sendid,channelid,nickName,headImg,nativeUrl,sessionUserName,timingIdentifier,這些參數,最后調用 OpenRedEnvelopesRequest: 打開紅包
image.png
  • 以上是看匯編代碼大致得出的結論,具體還要結合測試驗證,我們 hook OpenRedEnvelopesRequest:方法
CHDeclareClass(WCRedEnvelopesLogicMgr)
CHOptimizedMethod1(self, void, WCRedEnvelopesLogicMgr, ReceiverQueryRedEnvelopesRequest, id, arg1) {
    NSLog(@"%@", arg1);
    CHSuper1(WCRedEnvelopesLogicMgr, ReceiverQueryRedEnvelopesRequest, arg1);
}
  • 運行程序打上斷點,運行打開紅包流程,看會不會進斷點,進了斷點看下打印內容!果真如我們所預料的一樣,進了參數打印如下:
{
    channelId = 1;
    headImg = "http://wx.qlogo.cn/mmhead/ver_1/tUsGXv3VM2C4f6Nj1vibaWy2jJUFPMnUfobCtP1iajd5QLCn8B7YM6L6E1UTwSzxkiaichIJuVibBx2BRiaAIu1UkYfjMnJiatYxpvlWdHdfBD8xKU/132";
    msgType = 1;
    nativeUrl = "wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201709137014370817311&sendusername=wxid_mryox0wndn8122&ver=6&sign=8f446c219788bb6db911ff7de1eefd3ac9a66298b8d8d9be3f0018f14524bbb16ca78e0b22357967ebeb36860b892b0d5300d8ae5ae70ac29759a3c673cea9f6205adb65ae01d71d29a7b7a2b757333c0f264b07f893dfb88c28ece6e9869236";
    nickName = "xxxx";
    sendId = xxx;
    sessionUserName = "xxxxx";
    timingIdentifier = 88BF75742FFAB59D7F88C494670C3FE3;
}
  • 接下來我們要仔細分析一下每個參數如何獲取:

    • nickName,headImg通過 CContactMgr 的 getSelfContact 獲取到 selfContact,selfContact的m_nsHeadImgUrl和getContactDisplayName屬性分別對應headImg,nickName


      image.png
    • msgType 為常數1


      image.png
    • nativeUrl,應該是截取紅包 url 的一段字符串,通過上面的打印參數可以驗證!


      image.png
    • sessionUserName,沒太看懂,應該是發紅包人的名字,因為上文有個 nickName 我看懂了是 selfContact,自己的名稱!
    • sendid,channelid,timingIdentifier,通過 objectForKey: 獲取,上文字典的獲取只有通過 WCBizUtil 的 dictionaryWithDecodedComponets:separator: 獲取


      image.png
  • 所以我們接著進入 dictionaryWithDecodedComponets:separator: 函數的匯編代碼分析

  • 參數我沒有分析出來是什么類型的,我們Hook dictionaryWithDecodedComponets:separator: 方法

%hook WCBizUtil

+ (id)dictionaryWithDecodedComponets:(id)arg1 separator:(id)arg2 {
    %log;
    return %orig;
}

%end
?[m +[<WCBizUtil: 0x103b28eb8> dictionaryWithDecodedComponets:msgtype=1&channelid=1&sendid=xxxxxxxxxxxxx&sendusername= xxx-com&ver=6&sign=f4455577adc87b21387127f45e6c3803649800302ac90134018c10c4a87b3a0a5df85d4360028ad28b997bbdb4e52e6b8b62804fc04444185bb4f6617359a8c41565d1592d5d251c1a8b0aaef1fdb3de576d88890323720ae701bb3e56b3e900 separator:&]
  • 他的參數應該是紅包 url 的 get 參數,我們使用 & 解析出來然后我們可以得到以下參數 sendid,channelid,sessionUserName!
  • 至此 我們得到了除 timingIdentifier 之外的所有參數
  • 但是這個 timingIdentifier 怎么解決呢?
image.png
  • 可以分析出來的是從 m_structDicRedEnvelopesBaseInfo ?獲取到的,在 class-dump 出來的頭文件搜索 ,發現這個字段在WCRedEnvelopesControlData中,但是怎么獲取,我本人沒有從匯編代碼中研究出 timingIdentifier 的來源!
  • google 了一下 timingIdentifier 應該是微信加的一個防止機器人搶紅包的字段,怎么解決呢?
  • 猜!既然不在點擊打開紅包按鈕的時候獲取,會不會在點擊紅包進入打開紅包頁面的時候獲取呢?
    繼續從 UI 入手


    image.png
  • 首先從[BaseMsgContentViewController tableView:didSelectRowAtIndexPath:] 的匯編代碼來看,沒有找到類似的請求!
  • 那就從 Cell 本身去看,在class-dump 的頭文件中,搜索 WCPayC2CMessageCellView本身沒有類似方法
  • 繼續找它的父類WCPayBaseMessageCellView,onTouchUpInside方法有可能是我們的目標


    image.png
  • 查看onTouchUpInside匯編代碼


    image.png
  • 調用了 tapAppNodeView方法 搜索tapAppNodeView,發現在BaseMsgContentViewController中,我們繼續回到BaseMsgContentViewController
  • 查看BaseMsgContentViewController的tapAppNodeView方法的匯編代碼:


    image.png

    image.png
  • 調用了[WCRedEnvelopesControlMgr startReceiveRedEnvelopesLogicByC2C:Data:] 查看匯編代碼沒有找到!
  • 繼續查看 tapAppNodeView 匯編代碼發現最后調用了
    [WCRedEnvelopesControlMgr startReceiveRedEnvelopesLogic:Data:]方法


    image.png
  • 查看匯編代碼,我們發現 startReceiveRedEnvelopesLogic:Data 調用了WCRedEnvelopesReceiveControlLogic的startLogic 方法
  • 于是我們又回到了 WCRedEnvelopesReceiveControlLogic中,使用 Hopper 打開WCRedEnvelopesReceiveControlLogic 的 startLogic實現


    image.png

    image.png
  • 我們發現顯示為灰色,完全看不懂,查了一下Hopper 的官方文檔發現灰色代表undefined,不知道為什么!這個問題想了很久沒想通!但是在搜索關鍵字的時候有一個極其相似的方法:
    [WCRedEnvelopesGreetingReceiveControlLogic startLogic]
    然后我就想這兩個會不會實現類似,于是我分析了該函數發現


    image.png

    image.png
  • 以上調用了 WCRedEnvelopesLogicMgr的ReceiverQueryRedEnvelopesRequest
    方法,接下來就是驗證看有沒有調用這個方法,hook 該方法,打上斷點
CHDeclareClass(WCRedEnvelopesLogicMgr);

CHOptimizedMethod1(self, void, WCRedEnvelopesLogicMgr, ReceiverQueryRedEnvelopesRequest, id, arg1) {
    NSLog(@"%@", arg1);
    /*
     agreeDuty = 0;
     channelId = 1;
     inWay = 1;
     msgType = 1;
     nativeUrl = "wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201709137007742337288&sendusername=jirenbang-com&ver=6&sign=f4455577adc87b21387127f45e6c3803649800302ac90134018c10c4a87b3a0a5df85d4360028ad28b997bbdb4e52e6b8b62804fc04444185bb4f6617359a8c41565d1592d5d251c1a8b0aaef1fdb3de576d88890323720ae701bb3e56b3e900";
     sendId = 1000039501201709137007742337288;
     */
    CHSuper1(WCRedEnvelopesLogicMgr, ReceiverQueryRedEnvelopesRequest, arg1);
}
  • 點擊紅包消息,發現觸發了斷點,打印參數同上面打開紅包獲取的一樣,接下來我們測試一下ReceiverQueryRedEnvelopesRequest:方法的參數
  • 查看匯編代碼


    image.png
  • 沒看懂,只能又靠猜,這里肯定是發了一個網絡請求,肯定會有響應!我們查看一下WCRedEnvelopesLogicMgr的頭文件搜索 response發現以下OnWCToHongbaoCommonResponse: Request:方法有點像,我們測試一下該方法:
CHOptimizedMethod2(self, void, WCRedEnvelopesLogicMgr, OnWCToHongbaoCommonResponse, id, arg1, Request, id, arg2) {
    
    NSLog(@"%@", arg1);
    NSLog(@"%@", arg2);
    /*
     <HongBaoRes: 0x1123400f0>
     <HongBaoReq: 0x1123805f0> 
    */
    CHSuper2(WCRedEnvelopesLogicMgr, OnWCToHongbaoCommonResponse, arg1, Request, arg2);
}
  • 我們搜索打開HongBaoRes.h文件,查看發現了一個可疑的retText屬性類型為SKBuiltinBuffer_t
@property(retain, nonatomic) SKBuiltinBuffer_t *retText; // 
  • 我們繼續搜索SKBuiltinBuffer_t.h 文件,我們又發現
@property(retain, nonatomic) NSData *buffer; // @dynamic buffer;
  • 一般我們的網絡數據返回都是 json 格式,我們不妨寫代碼測試一下
        if ([NSStringFromClass([arg1 class]) isEqualToString:@"HongBaoRes"]) {
            NSData *data = [[arg1 retText] buffer];
            
            if (nil != data && 0 < [data length]) {
                NSError* error = nil;
                id jsonObj = [NSJSONSerialization JSONObjectWithData:data
                                                             options:NSJSONReadingAllowFragments
                                                               error:&error];
              }
        }
  • 經調試,如我們所猜測的一樣,我們的 jsonObj 中含有timingIdentifier字段!
  • 到此,我們完整的分析出了手動領取一個紅包的目標函數,所要的所有參數,和所有的參數是如何獲取的!
接下來我們就應該著手實現自動搶紅包的邏輯!
  • 不用多想,自動搶紅包的時機,肯定是在收到消息的時候咯!所以我們去 hook 收到消息的方法!
  • 接下來我們分析消息,對于這種沒有 UI 的方法,我的逆向思路就是猜!關鍵字搜索是我用來最常用的猜測方法,消息英文 message,縮寫msg!
  • 我們在 class-dump 頭文件中搜索 message


    image.png
  • 上圖紅框部分這幾個最像了,點擊一個一個查看!最終定位到CMessageMgr的方法最像了,CMessage,CMessageWrap更像是模型,CMessageMgr是管理消息的地方!
  • 我們打開CMessageMgr.h文件,搜索關鍵字 receive沒有,搜索 message,也沒有,搜索 msg


    image.png
  • 這些畫紅圈是我認為比較可以可疑的方法,不過我覺的最可疑的還是
- (void)AsyncOnAddMsg:(id)arg1 MsgWrap:(id)arg2;
  • 這個方法,原因很簡單,首先即有 msg,又有 msgwrap,然后一般這種多參數的方法,按照我寫代碼的思路來說,參數越多說明這個方法越受約束,越不不通用,這樣想的話我首先 hook AsyncOnAddMsg:MsgWrap:方法!
  • 接下來就是寫代碼驗證了:
CHDeclareClass(CMessageMgr);
CHMethod(2, void, CMessageMgr, AsyncOnAddMsg, id, arg1, MsgWrap, id, arg2) {
    NSLog(@"%@", arg1);
    NSLog(@"%@", arg2);
    CHSuper(2, CMessageMgr, AsyncOnAddMsg, arg1, MsgWrap, arg2);
}
  • 叫人發個紅包給我們,觸發斷點,查看參數:


    image.png
  • 上圖我們需要關注的是 m_uiMessageType 和 m_nsContent
  • m_uiMessageType == 49 為 紅包消息
  • m_nsContent 如下圖
<msg>
    <appmsg appid="" sdkver="">
        <des><![CDATA[我給你發了一個紅包,趕緊去拆!]]></des>
        <url><![CDATA[https://wxapp.tenpay.com/mmpayhb/wxhb_personalreceive?showwxpaytitle=1&msgtype=1&channelid=1&sendid=1000039501201709137014370817311&ver=6&sign=8f446c219788bb6db911ff7de1eefd3ac9a66298b8d8d9be3f0018f14524bbb16ca78e0b22357967ebeb36860b892b0d5300d8ae5ae70ac29759a3c673cea9f6205adb65ae01d71d29a7b7a2b757333c0f264b07f893dfb88c28ece6e9869236]]></url>
        <type><![CDATA[2001]]></type>
        <title><![CDATA[微信紅包]]></title>
        <thumburl><![CDATA[http://wx.gtimg.com/hongbao/1701/hb.png]]></thumburl>
        <wcpayinfo>
            <templateid><![CDATA[7a2a165d31da7fce6dd77e05c300028a]]></templateid>
            <url><![CDATA[https://wxapp.tenpay.com/mmpayhb/wxhb_personalreceive?showwxpaytitle=1&msgtype=1&channelid=1&sendid=1000039501201709137014370817311&ver=6&sign=8f446c219788bb6db911ff7de1eefd3ac9a66298b8d8d9be3f0018f14524bbb16ca78e0b22357967ebeb36860b892b0d5300d8ae5ae70ac29759a3c673cea9f6205adb65ae01d71d29a7b7a2b757333c0f264b07f893dfb88c28ece6e9869236]]></url>
            <iconurl><![CDATA[http://wx.gtimg.com/hongbao/1701/hb.png]]></iconurl>
            <receivertitle><![CDATA[Best wishes]]></receivertitle>
            <sendertitle><![CDATA[Best wishes]]></sendertitle>
            <scenetext><![CDATA[微信紅包]]></scenetext>
            <senderdes><![CDATA[查看紅包]]></senderdes>
            <receiverdes><![CDATA[領取紅包]]></receiverdes>
            <nativeurl><![CDATA[wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201709137014370817311&sendusername=wxid_mryox0wndn8122&ver=6&sign=8f446c219788bb6db911ff7de1eefd3ac9a66298b8d8d9be3f0018f14524bbb16ca78e0b22357967ebeb36860b892b0d5300d8ae5ae70ac29759a3c673cea9f6205adb65ae01d71d29a7b7a2b757333c0f264b07f893dfb88c28ece6e9869236]]></nativeurl>
            <sceneid><![CDATA[1002]]></sceneid>
            <innertype><![CDATA[0]]></innertype>
            <paymsgid><![CDATA[1000039501201709137014370817311]]></paymsgid>
            <scenetext>微信紅包</scenetext>
            <locallogoicon><![CDATA[c2c_hongbao_icon_cn]]></locallogoicon>
            <invalidtime><![CDATA[1505373603]]></invalidtime>
        </wcpayinfo>
    </appmsg>
    <fromusername><![CDATA[wxid_mryox0wndn8122]]></fromusername>
</msg>
  • 對比剛開始的 dictionaryWithDecodedComponets:separator:方法傳入的參數就是我們的 url 的一部分!

  • 我們可以通過 url 獲取到msgType,sendId,channelId

  • 至于nickName ,headImg我們可以從前面的匯編代碼還原出實現來

  • nativeUrl截取 url 的一部分

  • sessionUserName可以從 arg2 中獲得

  • 至此我們已經獲取到了打開紅包除了 timingIdentifier 外的所有參數

  • timingIdentifier參數,我們調用前面分析出來的 [WCRedEnvelopesLogicMgr ReceiverQueryRedEnvelopesRequest:] 方法來獲取

  • 該方法需要以下參數都可以通過 m_nsContent 獲取到

     agreeDuty = 0;
     channelId = 1;
     inWay = 1;
     msgType = 1;
     nativeUrl = "wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201709137007742337288&sendusername=jirenbang-com&ver=6&sign=f4455577adc87b21387127f45e6c3803649800302ac90134018c10c4a87b3a0a5df85d4360028ad28b997bbdb4e52e6b8b62804fc04444185bb4f6617359a8c41565d1592d5d251c1a8b0aaef1fdb3de576d88890323720ae701bb3e56b3e900";
     sendId = 1000039501201709137007742337288;
  • 之后我們 hook WCRedEnvelopesLogicMgr 的
    OnWCToHongbaoCommonResponse方法,通過arg1取到 timingIdentifier字段!
  • 最終我們打開紅包的所有參數獲取完畢,接下來只需要調用OpenRedEnvelopesRequest:方法,便可以實現自動搶紅包功能!
  • 至此,我們成功實現了微信自動搶紅包的功能!
總結:

在逆向搶紅包的過程中,我主要使用了靜態分析和動態調試的手段:

  • 靜態分析,主要是從 class-dump 出來的頭文件,輔助 Reveal,cycript,來尋找目標類或者目標函數,涉及到具體邏輯的時候,我會使用 Hopper Disassembler 來查看具體方法的具體實現,來進一步的猜測實現邏輯
  • 動態調試,我們靜態分析出來的結果或猜測,需要動態的去調試驗證!
    • 由于我是未越獄設備,我主要使用的是 Xcode + MonkeyDev 來 Hook 原方法,然后打印參數等簡單的調試
    • 如果你是越獄設備你可以使用 lldb+debugserver 神器來調試你的程序,那樣的話理論上整個程序對你來說都是透明,因為你可以像在 Xcode正向開發應用一樣具體一步一步的調試,對那些感興趣的功能點,你可以下斷點,然后去觸發斷點,跟蹤調試!
最后分享一下我的逆向技巧:
  • 猜 + 驗證,用關鍵字去猜,想想如果是自己寫這個 App 會怎樣去寫,會怎樣命名!
    • 然后你可以使用 Finder 的搜索功能,說句題外話,Finder 的搜索功能確實好用不僅可以搜索到文件名,內容也可以搜索到!
    • 當然你也可以使用 Xcode 新建一個工程將 class-dump 出來的頭文件添加到工程,利用 Xcode 的搜索shift+cmd+o 搜索文件,shift+cmd+f,全局搜索關鍵字!
  • 一般我都是使用常用的命名去搜,比如在尋找微信消息收發目標函數的時候,我們沒有 UI 輔助,這時候只能靠猜,我使用了 message msg receive 等關鍵字查找定位
  • 在獲取 timingIdentifier 參數的時候我不知道 我通過分析找到了
    ReceiverQueryRedEnvelopesRequest方法來獲取該參數,但是它的實現我看不太懂,這時候我又使用了猜,搜索了 response,找到了
    ReceiverQueryRedEnvelopesResponse 方法!最終獲取到了timingIdentifier 參數!
  • 當然猜的過程中你可能會出錯,這是一個非常枯燥的過程,逆向工程相對于正向開發來說是一件比較枯燥的事情,可能你花了幾天最終得到的只是一個目標函數!而在正向開發過程中,你寫的代碼都會轉換為成果!或許是一個完美的動畫,或許是流暢的界面!
  • 所以看完如果你能夠完整看完本文,然后一步一步去實踐的話,那說明你還是比較適合干這個的,如果不能那還是老老實實搞點 app 開發吧!
本人是個逆向新手,若有錯誤之處,請多多指正
參考文章:

iOS 應用逆向工程(入門書籍)
iOS冰與火之歌 – UAF and Kernel Pwn - 蒸米
MonkeyDev 文檔

跪求Star
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容