美文网首页iOS逆向工程女程序猿程序员
iOS逆向篇之微信抢红包(上)

iOS逆向篇之微信抢红包(上)

作者: meryin | 来源:发表于2018-05-25 13:45 被阅读205次

    本文仅供学习和参考,建议不用于商业用途,若用于商业用途后果自负
    完整的工程地址

    一 设置界面添加抢红包功能

    在设置界面添加抢红包开关和延迟时间的两个cell

    1. 建立MonkeyApp工程,把微信ipa拖到TargetApp文件夹中,真机运行
    2. 用Xcode的Debug view找到设置界面的代码注入的地方


      11.png
    • 设置界面的控制器是:NewSettingViewController
      控制器包含MMTableView 数据源是0x117964b60
    • 利用Xcode的lldb调试:
    (lldb) po 0x117964b60
    delegate[0x11793f130], class[MMTableViewInfo]
    
    • 在微信包内容中找到可执行文件,用class-dump导出头文件,用Sublime分析得到:
    @interface MMTableViewInfo : MMTableViewUserInfo <UITableViewDelegate, UITableViewDataSource, tableViewDelegate>
    {
        MMTableView *_tableView;
        NSMutableArray *_arrSections;
        id <MMTableViewInfoDelegate> _delegate;
        _Bool _disableAutoDeselectRows;
    }
    - (id)tableView:(id)arg1 cellForRowAtIndexPath:(id)arg2;
    - (long long)tableView:(id)arg1 numberOfRowsInSection:(long long)arg2;
    - (long long)numberOfSectionsInTableView:(id)arg1;
    

    可以看出NewSettingViewController主要由MMTableViewInfo的MMTableView *_tableView构成
    由此分析得到想在设置界面添加两个cell,要在MMTableViewInfo类中的tableView代理中设置

    1. 代码注入
    %hook MMTableViewInfo
    
    - (id)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)])//定位到设置界面
        {
            if([indexPath section] == [self numberOfSectionsInTableView:tableView]-1){
                UITableViewCell * cell = nil;
                if([indexPath row] == 0){
                    static NSString * switchCell = @"switchCell";
                    cell = [tableView dequeueReusableCellWithIdentifier:switchCell];
                    if(!cell){
                        cell  = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:switchCell];
                    }
                   
                    cell.textLabel.text = @"自动抢红包";
                    UISwitch * switchView = [[UISwitch alloc] init];
                    cell.accessoryView = switchView;
                    
                }else if([indexPath row] == 1){
                    static NSString * waitCell = @"waitCell";
                    cell = [tableView dequeueReusableCellWithIdentifier:waitCell];
                    if(!cell){
                        cell  = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:waitCell];
                    }
                    cell.textLabel.text = @"等待时间(秒)";
                    UITextField * textField = [[UITextField alloc] initWithFrame:CGRectMake(0,0,150,40)];
                    textField.placeholder = @"等待时间";
                    textField.borderStyle = UITextBorderStyleRoundedRect;
                    cell.accessoryView = textField;
                     //监听键盘输入
                    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidChangeValue:) name:UITextFieldTextDidChangeNotification object:textField];
                    textField.text = [WCDefaults valueForKey:WCTIMEKEY];
                }
                
                cell.backgroundColor = [UIColor whiteColor];
                return cell;
            }
            return %orig;
            
        }
        return %orig;
    }
    - (long long)tableView:(UITableView *)arg1 numberOfRowsInSection:(NSInteger)section
    {
        if([arg1.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)])//定位到设置界面
        {
            NSMutableArray *arr = MSHookIvar<NSMutableArray *>(self,"_arrSections");
            NSLog(@"--%@",arr);
            if(section == [self numberOfSectionsInTableView:arg1]-1)
            {NSLog(@"xxx--%@",arg1);
                return 2;
            }
            return %orig;
        }
        return %orig;
    }
    - (long long)numberOfSectionsInTableView:(UITableView *)arg1
    {
        if([arg1.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)])//定位到设置界面
        {
           
            return %orig+1;
        }
        return %orig;
    }
    - (double)tableView:(UITableView *)tableView heightForRowAtIndexPath:(id)indexPath{
        //定位设置界面&&最后一组
        if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]
           && [indexPath section] == [self numberOfSectionsInTableView:tableView]-1){
            return 44;
            
        }
        
        return %orig;
    }
    
    %end
    
    1. 本地数据缓存
    • 图片直接拖到工程的Dylib文件夹中


      22.png
    • 数据直接NSUserDefaults 沙盒读写
    #define WCDefaults [NSUserDefaults standardUserDefaults]
    #define WCSWITCHKEY @"WCSWITCHKEY"
    - (id)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    ....
           switchView.on = [WCDefaults boolForKey:WCSWITCHKEY];
           [switchView addTarget:self action:@selector(switchChang:) forControlEvents:(UIControlEventValueChanged)];
    ....
    }
    %new
    -(void)switchChang:(UISwitch *)switchView{
        [WCDefaults setBool:switchView.isOn forKey:WCSWITCHKEY];
        [WCDefaults synchronize];
    }
    %new
    - (void)textFieldDidChangeValue:(NSNotification *)notification{
        
        UITextField *sender = (UITextField *)[notification object];
        [WCDefaults setValue:sender.text forKey:WCTIMEKEY];
        [WCDefaults synchronize];
    }
    

    二 hook到收到红包消息的方法

    1. 分析聊天界面构成
    • 手机停留在和某人聊天的界面,然后cycript连接手机调试
      获取全部信息:cy# UIApp.keyWindow.recursiveDescription.toString()
    • 然后找到某个label,比如显示时间的label然后一层一层用cycript [0x11ad4af30 nextResponder]查看,一层一层的找到BaseMsgContentViewController就是聊天界面控制器
    • 或者直接用Xcode的debug view 找到BaseMsgContentViewController
    1. 找到BaseMsgContentViewController后就需要找到哪个方法是红包消息方法,可以hook所有方法,然后打印输出。手动方法(不建议)
    • 利用theos的logify.pl把BaseMsgContentViewController变成BaseMsgContentViewControllerHook.xm
    logify.pl /Users/mac/Desktop/wechatHeaders/BaseMsgContentViewController.h > ./BaseMsgContentViewControllerHook.xm
    
    • 把BaseMsgContentViewControllerHook.xm拖到工程文件夹的logos文件夹下,拖的时候target要勾选工程文件的target。然后build一下
    • build过后生成BaseMsgContentViewControllerHook.mm,把它拖到logos文件夹下,target要勾选动态库
    • 要想编译通过,那么要把微信相关的头文件都拖到工程中
    1. 上面hook所有方法的方式太慢,MonkeyApp可以直接完成(速度很快)
    • 找到工程的Config文件下的MethodTraceConfig.plist,把ENABLE_METHODTRACE改为YES,然后把BaseMsgContentViewController添加上去:


      11.png
    • 然后运行,发消息测试一下,Xcode输出。在输出中可以看到与Message相关的只有addMessageNode:和getMessageChatContactByMessageWrap:两个方法
    • 在BaseMsgContentViewControllerHook.h中找到addMessageNode:和getMessageChatContactByMessageWrap:然后只hook这两个方法:


    1. hook消息方法
    //红包逻辑分析
    %group group4
    %hook BaseMsgContentViewController
    
    - (void)addMessageNode:(id)arg1 layout:(_Bool)arg2 addMoreMsg:(_Bool)arg3
    {
        %orig;
    }
    %end
    
    %end
    

    断点到addMessageNode,然后po arg1可以看出addMessageNode方法就是消息收发相关的方法,那么当新消息来的时候,查看函数调用栈,就知道是哪个对象在管理消息的收发。可以通过恢复符号表找到这个对象

    1. 利用restore-symbol 恢复符号表
    • 进入到restore-symbol,然后make restore-symbol
    • 把TargetApp里的WeChat.app里的macho找到,然后拆分lipo WeChat -thin arm64 -output WeChat_arm64
    • 恢复符号表:./restore-symbol WeChat_arm64 -o weichat_symbol
    • 把weichat_symbol的名字改为WeChat,替换WeChat.app包内容里的WeChat
    1. 恢复符号表后运行,收到新消息的函数调用栈:bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
        frame #0: 0x00000001078189dc lib003--WeiChatTestDylib.dylib`_logos_method$group4$BaseMsgContentViewController$addMessageNode$layout$addMoreMsg$(self=0x00000001089b1e00, _cmd="addMessageNode:layout:addMoreMsg:", arg1=0x00000001166881a0, arg2=true, arg3=false) at _03__WeiChatTestDylib.xm:217
      * frame #1: 0x0000000102d7f3ec WeChat`-[BaseMsgContentLogicController DidAddMsg:] + 520
        frame #2: 0x0000000102d67250 WeChat`-[BaseMsgContentLogicController OnAddMsg:MsgWrap:] + 360
        frame #3: 0x0000000106603980 MMCommon`_callExtension + 480
        frame #4: 0x0000000103647dd4 WeChat`-[CMessageMgr MainThreadNotifyToExt:] + 560
        frame #5: 0x00000001850ca0ec Foundation`__NSThreadPerformPerform + 340
    

    可以看出frame #4: 0x0000000103647dd4 WeChat`-[CMessageMgr MainThreadNotifyToExt:]就是消息中转站
    hook消息相关方法:

    %hook BaseMsgContentLogicController
    - (void)DidAddMsg:(id)arg1{
        %orig;
    }
    - (void)OnAddMsg:(id)arg1 MsgWrap:(id)arg2{
           %orig;
    }
    %end
    %hook CMessageMgr
    - (void)MainThreadNotifyToExt:(id)arg1{
        %orig;
    }
    %end
    

    可以分析出CMessageMgr这个类就是消息中转站

    • 现在又hook住CMessageMgr的所有方法,分析出onNewSyncAddMessage:方法就是接收新消息的方法
    - (void)onNewSyncAddMessage:(id)arg1{
        NSLog(@"%@\n%@",arg1,[arg1 class]);
        %orig;
    }
    

    输出:

     {m_uiMesLocalID=9, m_ui64MesSvrID=6148490851876887842, m_nsFromUsr=wxi*241~19, m_nsToUsr=wxi*l12~19, m_uiStatus=3, type=1, msgSource="<msgsource><sequence_id>634140695</sequence_id></msgsource>"} 
    CMessageWrap
    

    分析CMessageWrap的type=49的时候是红包消息

    三 hook开红包的方法

    1. 找到开红包的按钮
    • 手机停留在开红包界面,cycript调试,输出界面所有信息UIApp.keyWindow.recursiveDescription.toString()
    • 随便改一个label的text看界面是否改变,然后查看上一层:
    cy# #0x10eb580b0.text = "meryin"
    "meryin"
    cy# [#0x10eb580b0 nextResponder]
    #"<UIImageView: 0x10c4c18c0; frame = (25 116.5; 325 434); autoresize = LM+RM+TM+BM; layer = <CALayer: 0x10d061800>>"
    
    • UIImageView查看其所有子view
    cy# # 0x10c4c18c0.subviews
    

    所有子控件中有两个UIButton,根据其frame,找到并验证UIButton: 0x116d71840; frame = (107.5 233; 110 110)就是拆红包按钮

    1. 找到拆红包按钮的target
    cy# #0x116d71840.allTargets
    [NSSet setWithArray:@[#"<WCRedEnvelopesReceiveHomeView: 0x11393a310; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x10d3a4e40>>"]]]
    

    WCRedEnvelopesReceiveHomeView就是UIButton 的target

    1. 找到拆红包的方法
      利用cyprit 一个方法一个方法的试:
    cy# [#0x11393a310 OnOpenRedEnvelopes]
    

    WCRedEnvelopesReceiveHomeView 类中的OnOpenRedEnvelopes就是开红包的方法
    以上只能在拆红包界面才能自动拆红包,我们的目的是,无论在哪个界面或者后台运行,只要红包消息来了就能自动拆红包

    1. 静态分析OnOpenRedEnvelopes
    • 用ida静态分析OnOpenRedEnvelopes
    #_OBJC_IVAR_$_WCRedEnvelopesReceiveHomeView.m_dicBaseInfo@PAGE
    __text:0000000101157290                 LDRSW           X24, [X8,#_OBJC_IVAR_$_WCRedEnvelopesReceiveHomeView.m_dicBaseInfo@PAGEOFF] ; NSDictionary *m_dicBaseInfo;
    .....
    #_OBJC_IVAR_$_WCRedEnvelopesReceiveHomeView.m_delegate@PAGE
    __text:0000000101157378                 LDRSW           X8, [X8,#_OBJC_IVAR_$_WCRedEnvelopesReceiveHomeView.m_delegate@PAGEOFF] ; WCRedEnvelopesReceiveHomeViewDelegate *m_delegate;
    #selRef_WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes@PAGE
    [X8,#selRef_WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes@PAGEOFF]
    __text:0000000101157390                 BL              _objc_msgSend
    

    分析可以看出这个方法大概执行了,拿到 NSDictionary *m_dicBaseInfo做一系列判定,然后拿到id <WCRedEnvelopesReceiveHomeViewDelegate> m_delegate;然后m_delegate发送消息WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes

    • hook OnOpenRedEnvelopes
    %hook WCRedEnvelopesReceiveHomeView
    - (void)OnOpenRedEnvelopes{
        NSDictionary *dict = MSHookIvar<NSDictionary*>(self,"m_dicBaseInfo");
        NSArray *arr = dict.allKeys;
        for(int i=0;i<arr.count;i++){
            NSLog(@"%@:%@",arr[i],[dict objectForKey:arr[i]]);
        }
        id delegate = MSHookIvar<id >(self,"m_delegate");
        NSLog(@"---%@",[delegate class]);
    }
    %end
    

    打印输出:

    2018-05-23 17:51:00.011658+0800 WeChat[9997:949894] wishing:恭喜发财,大吉大利
    2018-05-23 17:51:00.011940+0800 WeChat[9997:949894] watermark:
    2018-05-23 17:51:00.012051+0800 WeChat[9997:949894] sendUserName:wxid_hy6hye79l4q241
    2018-05-23 17:51:00.012153+0800 WeChat[9997:949894] timingIdentifier:7149DB99A65AA9FDEAC9095342D16F8D
    2018-05-23 17:51:00.012487+0800 WeChat[9997:949894] hbStatus:2
    2018-05-23 17:51:00.012603+0800 WeChat[9997:949894] receiveStatus:0
    2018-05-23 17:51:00.012757+0800 WeChat[9997:949894] sendId:1000039401201805237018373647074
    2018-05-23 17:51:00.012921+0800 WeChat[9997:949894] isSender:0
    2018-05-23 17:51:00.013326+0800 WeChat[9997:949894] statusMess:给你发了一个红包
    2018-05-23 17:51:00.013543+0800 WeChat[9997:949894] hbType:0
    2018-05-23 17:51:00.013752+0800 WeChat[9997:949894] ---WCRedEnvelopesReceiveControlLogic
    

    可以看出拆红包的核心代码就是WCRedEnvelopesReceiveControlLogic类中的 WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes方法

    1. 静态分析 WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes
      ida找到ctrl+f键找到WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes方法,然后按f5就可以还原伪代码
      调用类方法 [%c(类名称) 方法名]
    %hook WCRedEnvelopesReceiveControlLogic
    - (void)WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes{
        WCRedEnvelopesControlData *m_data =  MSHookIvar<WCRedEnvelopesControlData* >(self,"m_data");
        CMessageWrap *m_Wrap =[m_data m_oSelectedMessageWrap];
        WCPayInfoItem *item =[m_Wrap m_oWCPayInfoItem];
        NSString *url =[item  m_c2cNativeUrl];
        NSInteger length = [@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length];
        id componets = [url substringFromIndex:length];
        //调用类方法 [%c(类名称)  方法名]
        NSDictionary *dictionary=[%c(WCBizUtil) dictionaryWithDecodedComponets:componets separator:@"&"];
        NSLog(@"dic===%@",dictionary);
            NSMutableDictionary *mul_dict =[%c(NSMutableDictionary) dictionary];
            [mul_dict setObject:@"1" forKey:@"msgType"];
            [mul_dict setObject:dictionary[@"sendid"] forKey:@"sendId"];
            [mul_dict setObject:dictionary[@"channelid"] forKey:@"channelId"];
            CContactMgr *server =[[%c(MMServiceCenter) defaultCenter] getService:[%c(CContactMgr) class]];
            CContact *contact = [server getSelfContact];
            id  displayName = [contact getContactDisplayName];
            [mul_dict setObject:displayName forKey:@"nickName"];
            id m_nsHeadImgUrl = [contact m_nsHeadImgUrl];
            [mul_dict setObject:m_nsHeadImgUrl forKey:@"headImg"];
            CMessageWrap *warp =[m_data m_oSelectedMessageWrap];
            if ( warp )
            {
                [mul_dict setObject:url forKey:@"nativeUrl"];
            }
            MMMsgLogicManager *logicserver =[[%c(MMServiceCenter) defaultCenter] getService:[%c(MMMsgLogicManager) class]];
            WeixinContentLogicController *currentLogicController = [logicserver GetCurrentLogicController];
           NSLog(@"--%@",[currentLogicController class]);
            if ( currentLogicController )
            {
                id m_contact =[currentLogicController m_contact];
                if ( m_contact )
                {
                    NSString *m_nsUsrName =[m_contact m_nsUsrName];
                    if ( m_nsUsrName )
                    {
                        [mul_dict setObject:m_nsUsrName forKey:@"sessionUserName"];
                    }
                }
            }
            NSDictionary  *m_struct = [m_data m_structDicRedEnvelopesBaseInfo];
            NSString *timingIdentifier= [m_struct objectForKey:@"timingIdentifier"]
            if ( [timingIdentifier length])
            { [mul_dict setObject:timingIdentifier forKey:@"timingIdentifier"];}
           WCPayLogicMgr *payLogicMgr =[[%c(MMServiceCenter) defaultCenter] getService:[%c(WCPayLogicMgr) class]];
            [payLogicMgr setRealnameReportScene:1003];
          id subScript = [m_struct objectForKeyedSubscript:@"agree_duty"];
        /*
            v97 = _NSConcreteStackBlock;
            v98 = 3254779904LL;
            v99 = sub_1015C17E0;
            v100 = &unk_10390BBD0;
            v101 = v17;
            v86 = objc_retain(v17);
            v87 = objc_retain(v72);
            v102 = v87;
            v91 = _NSConcreteStackBlock;
            v92 = 3254779904LL;
            v93 = sub_1015C18F8;
            v94 = &unk_10390BC00;
            objc_copyWeak(&v96, &v103);
            v95 = objc_retain(v87);
            objc_msgSend(v80, "checkHongbaoOpenLicense:acceptCallback:denyCallback:", v85, &v97, &v91);
         */
    
    }
    %end
    

    以上除了block其他都还原了

    1. 还原block符号表
    • ida->file->script file 选中ida_search_block.py 生成block_symbol.json文件
    • block_symbol.json 和restore-symbol放在一起 然后 ./restore-symbol WeChat -o wwchat_block -j block_symbol.json
    • 再把wwchat_block改名为WeChat替换微信包内容里的WeChat
    • 再把WeChat用ida打开,然后还原代码
     [payLogicMgr checkHongbaoOpenLicense:subScript acceptCallback:^(){
           WCRedEnvelopesLogicMgr *envelopesLogicMgr = [[%c(MMServiceCenter) defaultCenter] getService:[%c(WCRedEnvelopesLogicMgr) class]];
            [envelopesLogicMgr OpenRedEnvelopesRequest:mul_dict];
        }denyCallback:^(){
            
        }];
    

    可以分析出WCRedEnvelopesLogicMgr就是红包管理类,OpenRedEnvelopesRequest:就是开红包的关键方法

    接下篇

    相关文章

      网友评论

      • Fintecher:楼主牛人哦。
      • Detailscool:现在是重点在如何重新打包,之前用的ipa resign的方法走不通
        meryin:@Detailscool 用MonkeyDev https://github.com/AloneMonkey/MonkeyDev
      • IT人故事会:,楼主的心血啊!谢谢博主

      本文标题:iOS逆向篇之微信抢红包(上)

      本文链接:https://www.haomeiwen.com/subject/mqdqjftx.html