美文网首页
iOS逆向记录(五)微信抢红包第一篇

iOS逆向记录(五)微信抢红包第一篇

作者: Flonger | 来源:发表于2017-09-14 19:52 被阅读198次

    14. 项目实战(微信抢红包插件)

    14.1 定位红包消息响应方法

    14.1.1 砸壳和导出头文件以供分析用

    详细过程见笔记第7章

     DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/mobile/Containers/Bundle/Application/749DC69A-3A8D-4B5C-9926-1220E69FC85F/WeChat.app/WeChat
      
    class-dump -s -S -H WeChat.decrypted -o ./MyHeaders
    

    14.1.2 借助cycript来动态分析界面定位视图控制器(ViewController)

    • 打开应用并使用cycript附加进程
      cycript -p WeChat
    
    • 使用recursiveDescription打印UIView对象
     [[UIApp keyWindow] recursiveDescription].toString()
     或者
     UIApp.keyWindow.recursiveDescription().toString()
    
    • 选择一个UIView对象的id,借助nextResponder方法获取视图控制器
     <UIView: 0x14eaed070; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x14eafa130>>
      
     //使用nextResponder找到视图控制器(ViewController)
     cy# [#0x14eaed070 nextResponder]
     #"<BaseMsgContentViewController: 0x14f1dc200>"
    
    • 在class-dump到处的头文件中,查找BaseMsgContentViewController相关的文件
     ➜ ls -al *BaseMsgContentViewController*
     -rw-r--r--  1 liuzhongzheng  staff  26105 Aug 25 15:59 BaseMsgContentViewController.h
    
    • cycript的其它用法介绍

      • _printHierarchy - 直接打印所有UIViewController

        [[[UIWindow keyWindow] rootViewController] _printHierarchy].toString()
        
      • _autolayoutTrace - recursiveDescription的简化版,去掉了UIView的一些描述

      [[UIApp keyWindow] _autolayoutTrace].toString()
      
      • 获取bundle info
      [[NSBundle mainBundle] infoDictionary].toString()
      

    14.1.3 借助Reveal来动态分析界面定位视图控制器(ViewController)

    • OSX 上安装Reveal
      官网:https://revealapp.com/

    • iOS 上安装Reveal Loader
      通过Cydia搜索安装Reveal Loader

    • 使用Reveal观察界面结构

    14.1.4 借助thoes模块Logify来精确定位消息响应方法

    • 创建Tweak工程

    • 批量生成hook BaseMsgContentViewController.h文件中方法的Tweak.xm文件

    /opt/theos/bin/logify.pl ../WeChat/Headers/BaseMsgContentViewController.h > Tweak.xm
    
    • 用logify.pl生成的Tweak.xm文件替换Tweak工程中的Tweak.xm文件

    • 编译Tweak工程并安装

    make package
    make install
    
    • 通过查看和分析日志定位消息响应的方法
    tail -f /var/log/syslog | grep WeChat
     
    [<BaseMsgContentViewController: 0x1368e9c00> addMessageNode:{m_uiMesLocalID=38, m_ui64MesSvrID=6021494297990342718, m_nsFromUsr=wxi*431~19, m_nsToUsr=wxi*r12~19, m_uiStatus=4, type=1, msgSource="<msgsource><sequence_id>649531066</sequence_id></msgsource>"}  layout:1 addMoreMsg:0]
     
    //定位到与消息响应相关的方法
    - (void)addMessageNode:(id)arg1 layout:(_Bool)arg2 addMoreMsg:(_Bool)arg3 { %log; %orig; }
    

    14.1.5 借助lldb进行动态调试

    • 在iOS上配置debugserver
    • 把iPhone连接xcode,在iOS上/Developer/usr/bin/目录下面生成调试工具debugserver
     Flongers-iPhone:/Developer/usr/bin root# ls
     DTDeviceArbitration*  ScreenShotr*  XcodeDeviceMonitor*  debugserver*  iprofiler*  xctest*
    
    • 给debugserver瘦身和重新签名,并放入/usr/bin/目录下方便随时调用
      lipo(静态库拆分与合并)命令的介绍: http://www.jianshu.com/p/e590f041c5f6

      //拷贝到OS X上去
      scp debugserver luz@192.168.1.138:/tmp 
       
      //查看CPU架构信息
      ➜lipo -info debugserver 
      Architectures in the fat file: debugserver are: armv7 armv7s arm64
      
      //瘦身
      lipo -thin arm64 debugserver -output debugserver.thin
       
      //使用ldid添加task_for_pid权限
      ➜  ldid -Sent.xml debugserver.thin 
      ➜  ldid -e debugserver.thin 
       <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
       <plist version="1.0">
       <dict>
               <key>com.apple.springboard.debugapplications</key>
               <true/>
               <key>get-task-allow</key>
               <true/>
               <key>task_for_pid-allow</key>
               <true/>
               <key>run-unsigned-code</key>
               <true/>
       </dict>
       </plist>
        
      //使用codesign添加task_for_pid权限
      codesign -s - --entitlements ent.plist -f debugserver.thin
      ➜ ldid -e  debugserver.thin
       <?xml version="1.0" encoding="UTF-8"?>
       <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/ PropertyList-1.0.dtd">
       <plist version="1.0">
       <dict>
           <key>com.apple.springboard.debugapplications</key>
           <true/>
           <key>run-unsigned-code</key>
           <true/>
           <key>get-task-allow</key>
           <true/>
           <key>task_for_pid-allow</key>
           <true/>
       </dict> 
       </plist>
      
    • 在iOS使用debugserver附加程序

      • 打开调试
      debugserver -x backboard IP:port /path/to/executable
      
      • 附加调试
      debugserver *:9527 -a "WeChat"
      
    • 在OSX使用lldb连接debugserver进行调试

     lldb 
     process connect connect://192.168.1.113:9527  //ip是iPhone的地址,端口要一致
    
    • 使用usbmuxd工具通过USB口转发ssh和调式信息
    • 通过USB口转发ssh
     //把本地2222端口转发到iOS的22(ssh)端口
     ./tcprelay.py -t 22:2222
     Forwarding local port 2222 to remote port 22
    
     //ssh进行连接 - localhost(127.0.0.1)是本地主机(本机的标准域名)
     ssh root@localhost -p 2222   
    
    • 通过USB口转发lldb调试

      //把本地9527端口转发到iOS的9527端口
      ./tcprelay.py -t 9527:9527
       
      //在iOS上指定debugserver监听的端口号(要与9527:9527冒号前面的一致9527)
      debugserver *:9527 -a "WeChat" 
       
      //在OSX上指定连接的端口号(要与9527:9527冒号后面的一致)
      lldb 
      process connect connect://localhost:9527 
      
    • 查看ASLR偏移

    image list -o -f 
     
    (lldb) image list -o -f
    [  0] 0x0000000000054000 /private/var/mobile/Containers/Bundle/Application/749DC69A-3A8D-4B5C-9926-1220E69FC85F/WeChat.app/WeChat(0x0000000100054000)
    
    偏移后模块基地址 = 偏移前模块基地址 + 模块的ASLR偏移
    偏移后符号基地址 = 偏移前符号基地址 + 符号所在模块的ASLR偏移(一般用在这儿)
    偏移后指令基地址 = 偏移前指令基地址 + 指令所在模块的ASLR偏移
    
    • 使用Hopper查看函数基地址
     -[BaseMsgContentViewController addMessageNode:layout:addMoreMsg:]:
    0000000101dcbb0c         db  0xe9 ; '.'                                         
    0000000101dcbb0d         db  0x23 ; '#'
    
    • 使用lldb的br命令设备断点br

       br s -a '0x0000000000054000+0x0000000101dcbb0c'
       
       设置断点
       b function
       br s –a address
       br s –a 'ASLROffset+address'
        
       //查看所有断点
       br list
        
       //删除断点
       br delete 1
        
       //程序继续运行
        
      
    • 发送消息,等待触发断点,然后查看函数调用堆栈

    (lldb) bt
       * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
         * frame #0: 0x0000000101defb0c WeChat`_mcwxh_dydx33_8to8(_VDecStruct*, unsigned char*, unsigned char*, unsigned int, unsigned int, unsigned int, unsigned int) + 23979876
           frame #1: 0x0000000102035434 WeChat`_mcwxh_dydx33_8to8(_VDecStruct*, unsigned char*, unsigned char*, unsigned int, unsigned int, unsigned int, unsigned int) + 26361996
           frame #2: 0x000000010202059c WeChat`_mcwxh_dydx33_8to8(_VDecStruct*, unsigned char*, unsigned char*, unsigned int, unsigned int, unsigned int, unsigned int) + 26276340
           frame #3: 
           WeChat`_mcwxh_dydx33_8to8(_VDecStruct*, unsigned char*, unsigned char*, unsigned int, unsigned int, unsigned int, unsigned int) + 34703800
           frame #4: 0x0000000187955f9c Foundation`__NSThreadPerformPerform + 372
           frame #5: 0x0000000186a0c240 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
           frame #6: 0x0000000186a0b4e4 CoreFoundation`__CFRunLoopDoSources0 + 264
           frame #7: 0x0000000186a09594 CoreFoundation`__CFRunLoopRun + 712
           frame #8: 0x00000001869352d4 CoreFoundation`CFRunLoopRunSpecific + 396
           frame #9: 0x00000001901536fc GraphicsServices`GSEventRunModal + 168
           frame #10: 0x000000018b4fafac UIKit`UIApplicationMain + 1488
           frame #11: 0x00000001000bab5c WeChat`_mh_execute_header + 617308
           frame #12: 0x00000001988a6a08 libdyld.dylib`start + 4
      
     //计算函数地址
    偏移后符号基地址 = 偏移前符号基地址 + 符号所在模块的ASLR偏移
    偏移后符号基地址 - 符号所在模块的ASLR偏移 = 偏移前符号基地址
    0x0000000101e3db24 -  00000000000f4000 =  101D49B24
    
    • 在Hopper或者IDA中分析函数调用关系

      0x0000000101dcbb0c
      -[BaseMsgContentViewController addMessageNode:layout:addMoreMsg:]
      
      //在此处设置断点,发现只有在当前聊天界面才会触发,不是我们找的目标方法
      0x102011434 = 0x0000000102035434 - 0x0000000000024000 
      -[BaseMsgContentLogicController DidAddMsg:]
      
      a //在此处设置断点,发现只有在当前聊天界面才会触发,不是我们找的目标方法
      0x101FFC59C = 0x000000010202059c - 0x0000000000024000 
      -[BaseMsgContentLogicController OnAddMsg:MsgWrap:]
      
      //在此处设置断点,只要收到消息就会触发,符合我们寻址的目标方法
      0x102805D60 = 0x0000000102829d60 - 0x0000000000024000 
      -[CMessageMgr MainThreadNotifyToExt:]
       
      注:定位到另一个与消息接收有关的类CMessageMgr,在微信到处头文件中发现有同名文件CMessageMgr.h
      
    • 继续使用Tweak来hook CMessageMgr中的方法,观察日志输出

      //使用logify来继续追踪CMessageMgr这个类,发现消息到来时,这几个方法有响应
       
      //单独分析这几个方法
      %hook CMessageMgr
      - (void)AsyncOnPreAddMsg:(id)arg1 MsgWrap:(id)arg2 { %log; %orig; }
      - (void)AsyncOnAddMsg:(id)arg1 MsgWrap:(id)arg2 { %log; %orig; }
      - (void)AsyncOnPushMsg:(id)arg1 { %log; %orig; }
      - (void)CheckMessageStatus:(id)arg1 Msg:(id)arg2 { %log; %orig; }
      %end
       
      1.打开聊天界面时CheckMessageStatus有反应,所以排除它
      2.AsyncOnPreAddMsg、AsyncOnAddMsg、AsyncOnPushMsg这三个方法都是在消息到来时触发,从
        方法命名AsyncOnPreAddMsg应该是消息的前置处理,AsyncOnPushMsg可能是往队列里面添加消息。
        这三个方法都可以用来备选,我们先选AsyncOnAddMsg。
       
      //分析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
      //参数输出日志如下
      Sep  9 16:39:53 Flongers-iPhone WeChat[16048]: arg1 = wxid_lx85gyfaib9431 , arg2 = {m_uiMesLocalID=61, m_ui64MesSvrID=6890149828648546534, m_nsFromUsr=wxi*431~19, m_nsToUsr=wxi*r12~19, m_uiStatus=3, type=1, msgSource="<msgsource><sequence_id>649531092</sequence_id></msgsource>"} 
      Sep  9 16:39:53 Flongers-iPhone WeChat[16048]: arg1 = __NSCFString , arg2 = CMessageWrap
      //所以消息响应函数如下,CMessageWrap亦有同名的导出头文件CMessageWrap.h
      - (void)AsyncOnAddMsg:(NSString *)wxid MsgWrap:(CMessageWrap *)wrap; 
      
    • 分析消息内容相关的类CMessageWrap

    @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; // @synthesize m_nsAtUserList;
      @property(retain, nonatomic) NSString *m_nsBizChatId; // @synthesize m_nsBizChatId;
      @property(retain, nonatomic) NSString *m_nsBizClientMsgID; // @synthesize m_nsBizClientMsgID;
      @property(retain, nonatomic) NSString *m_nsDisplayName; // @synthesize m_nsDisplayName;
      @property(retain, nonatomic) NSString *m_nsKFWorkerOpenID; // @synthesize m_nsKFWorkerOpenID;
      @property(retain, nonatomic) NSString *m_nsMsgSource; // @synthesize m_nsMsgSource;
      @property(retain, nonatomic) NSString *m_nsPattern; // @synthesize m_nsPattern;
      @property(retain, nonatomic) NSString *m_nsPushContent; // @synthesize m_nushContent;
      @property(retain, nonatomic) NSString *m_nsRealChatUsr; // @synthesize 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
    

    相关文章

      网友评论

          本文标题:iOS逆向记录(五)微信抢红包第一篇

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