美文网首页
31-项目实战(3)

31-项目实战(3)

作者: 深圳_你要的昵称 | 来源:发表于2021-08-29 18:28 被阅读0次

    前言

    本篇文章接着30-项目实战(2),继续讲解自动抢红包的功能。今天的重点是 👉🏻 还原红包方法WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:

    一、还原红包方法(上)

    在继续分析WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:后面的流程之前,我们先来了解下IDA伪代码功能。

    IDA伪代码功能

    我们可以按F5,进入伪代码的界面👇🏻

    为了便于我们分析,可以鼠标右键,将这些伪代码生成Html文件(首先cmd+A全选)👇🏻

    然后再将Html文件中的代码copysublime编辑器中,方便查看!

    
     void __cdecl -[WCRedEnvelopesReceiveControlLogic WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:](WCRedEnvelopesReceiveControlLogic *self, SEL a2, id a3)
     {
       id v4; // x21
       __int64 v5; // x21
       id v6; // x0
       id v7; // x22
       id v8; // x0
       id v9; // x25
       id v10; // x0
       id v11; // x26
       id v12; // x0
       id v13; // x0
       id v14; // x20
       id v15; // x0
       id v16; // x20
       id v17; // x0
       id v18; // x22
       id v19; // x0
       id v20; // x27
       id v21; // x0
       id v22; // x26
       id v23; // x0
       id v24; // x24
       id v25; // x0
       id v26; // x0
       id v27; // x20
       id v28; // x0
       id v29; // x26
       id v30; // x0
       id v31; // x20
       id v32; // x0
       id v33; // x20
       __int64 v34; // x21
       id v35; // x0
       id v36; // x20
       __int64 v37; // x21
       id v38; // x0
       id v39; // x19
       id v40; // x0
       id v41; // x20
       id v42; // x0
       id v43; // x23
       id v44; // x0
       id v45; // x20
       id v46; // x0
       id v47; // x0
       id v48; // x23
       id v49; // x0
       id v50; // x19
       __int64 v51; // x21
       id v52; // x0
       id v53; // x0
       void *v54; // x24
       id v55; // x0
       id v56; // x26
       id v57; // x0
       id v58; // x27
       id v59; // x0
       id v60; // x20
       id v61; // x0
       id v62; // x23
       __int64 v63; // x19
       id v64; // x0
       id v65; // x24
       id v66; // x0
       id v67; // x20
       id v68; // x0
       id v69; // x24
       id v70; // x0
       id v71; // x0
       id v72; // x25
       __int64 v73; // x21
       id v74; // x0
       id v75; // x24
       id v76; // x0
       id v77; // x0
       id v78; // x25
       id v79; // x0
       id v80; // x0
       id v81; // x0
       id v82; // x26
       id v83; // x22
       id v84; // x21
       const char *v85; // [xsp+0h] [xbp-110h]
       id v86; // [xsp+8h] [xbp-108h]
       id v87; // [xsp+8h] [xbp-108h]
       id v88; // [xsp+18h] [xbp-F8h]
       id v89; // [xsp+20h] [xbp-F0h]
       id v90; // [xsp+28h] [xbp-E8h]
       __int64 v91[4]; // [xsp+48h] [xbp-C8h] BYREF
       id v92; // [xsp+68h] [xbp-A8h] BYREF
       __int64 v93[4]; // [xsp+70h] [xbp-A0h] BYREF
       id v94; // [xsp+90h] [xbp-80h]
       id v95; // [xsp+98h] [xbp-78h] BYREF
       id location[2]; // [xsp+A0h] [xbp-70h] BYREF
    
       v86 = objc_retain(a3);
       objc_msgSend(v4, "reportReceiveHomeViewEmoticon");
       v6 = objc_msgSend(*(id *)(v5 + 59), "m_oSelectedMessageWrap");
       v7 = objc_retainAutoreleasedReturnValue(v6);
       v8 = objc_msgSend(v7, "m_oWCPayInfoItem");
       v9 = objc_retainAutoreleasedReturnValue(v8);
       v10 = objc_msgSend(v9, "m_c2cNativeUrl");
       v11 = objc_retainAutoreleasedReturnValue(v10);
       v12 = objc_msgSend(CFSTR("wxpay://c2cbizmessagehandler/hongbao/receivehongbao?"), "length");
       v13 = objc_msgSend(v11, "substringFromIndex:", v12);
       v14 = objc_retainAutoreleasedReturnValue(v13);
       v90 = v14;
       objc_release(v11);
       objc_release(v9);
       objc_release(v7);
       v90 = v14;
       v15 = +[WCBizUtil dictionaryWithDecodedComponets:separator:](
               &OBJC_CLASS___WCBizUtil,
               "dictionaryWithDecodedComponets:separator:",
               v14,
               CFSTR("&"));
       v16 = objc_retainAutoreleasedReturnValue(v15);
       v89 = v16;
       v17 = objc_msgSend(&OBJC_CLASS___NSMutableDictionary, "dictionary");
       v18 = objc_retainAutoreleasedReturnValue(v17);
       objc_msgSend(v18, "safeSetObject:forKey:", CFSTR("1"), CFSTR("msgType"));
       v19 = objc_msgSend(v16, "objectForKey:", CFSTR("sendid"));
       v20 = objc_retainAutoreleasedReturnValue(v19);
       objc_msgSend(v18, "safeSetObject:forKey:", v20, CFSTR("sendId"));
       objc_release(v20);
       v21 = objc_msgSend(v16, "objectForKey:", CFSTR("channelid"));
       v22 = objc_retainAutoreleasedReturnValue(v21);
       objc_msgSend(v18, "safeSetObject:forKey:", v22, CFSTR("channelId"));
       objc_release(v22);
       v23 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
       v24 = objc_retainAutoreleasedReturnValue(v23);
       v25 = objc_msgSend(&OBJC_CLASS___CContactMgr, "class");
       v26 = objc_msgSend(v24, "getService:", v25);
       v27 = objc_retainAutoreleasedReturnValue(v26);
       v28 = objc_msgSend(v27, "getSelfContact");
       v29 = objc_retainAutoreleasedReturnValue(v28);
       objc_release(v27);
       objc_release(v24);
       v30 = objc_msgSend(v29, "getContactDisplayName");
       v31 = objc_retainAutoreleasedReturnValue(v30);
       objc_msgSend(v18, "safeSetObject:forKey:", v31, CFSTR("nickName"));
       objc_release(v31);
       v88 = v29;
       v32 = objc_msgSend(v29, "m_nsHeadImgUrl");
       v33 = objc_retainAutoreleasedReturnValue(v32);
       objc_msgSend(v18, "safeSetObject:forKey:", v33, CFSTR("headImg"));
       objc_release(v33);
       objc_msgSend(v18, "safeSetObject:forKey:", a3, CFSTR("left_button_continue"));
       objc_release(v86);
       v35 = objc_msgSend(*(id *)(v34 + 59), "m_oSelectedMessageWrap");
       v36 = objc_retainAutoreleasedReturnValue(v35);
       objc_release(v36);
       if ( v36 )
       {
         v38 = objc_msgSend(*(id *)(v37 + 59), "m_oSelectedMessageWrap");
         v39 = objc_retainAutoreleasedReturnValue(v38);
         v40 = objc_msgSend(v39, "m_oWCPayInfoItem");
         v41 = objc_retainAutoreleasedReturnValue(v40);
         v42 = objc_msgSend(v41, "m_c2cNativeUrl");
         v43 = objc_retainAutoreleasedReturnValue(v42);
         objc_msgSend(v18, "safeSetObject:forKey:", v43, CFSTR("nativeUrl"));
         objc_release(v43);
         objc_release(v41);
         objc_release(v39);
       }
    
       v44 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
       v45 = objc_retainAutoreleasedReturnValue(v44);
       v46 = objc_msgSend(&OBJC_CLASS___MMMsgLogicManager, "class");
       v47 = objc_msgSend(v45, "getService:", v46);
       v48 = objc_retainAutoreleasedReturnValue(v47);
       v49 = objc_msgSend(v48, "GetCurrentLogicController");
       v50 = objc_retainAutoreleasedReturnValue(v49);
       objc_release(v48);
       objc_release(v45);
       if ( v50 )
       {
         v52 = objc_msgSend(v50, "m_contact");
         v53 = objc_retainAutoreleasedReturnValue(v52);
         if ( v53 )
         {
           v54 = v53;
           v55 = objc_msgSend(v50, "m_contact");
           v56 = objc_retainAutoreleasedReturnValue(v55);
           v57 = objc_msgSend(v56, "m_nsUsrName");
           v58 = objc_retainAutoreleasedReturnValue(v57);
           objc_release(v58);
           objc_release(v56);
           objc_release(v54);
           if ( v57 )
           {
             v59 = objc_msgSend(v50, "m_contact");
             v60 = objc_retainAutoreleasedReturnValue(v59);
             v61 = objc_msgSend(v60, "m_nsUsrName");
             v62 = objc_retainAutoreleasedReturnValue(v61);
             objc_msgSend(v18, "safeSetObject:forKey:", v62, CFSTR("sessionUserName"));
             objc_release(v62);
             objc_release(v60);
            }
          }
       }
       v87 = v50;
       v63 = v51;
       v64 = objc_msgSend(*(id *)(v51 + 59), "m_structDicRedEnvelopesBaseInfo", "m_structDicRedEnvelopesBaseInfo");
       v65 = objc_retainAutoreleasedReturnValue(v64);
       v66 = objc_msgSend(v65, "stringForKey:", CFSTR("timingIdentifier"));
       v67 = objc_retainAutoreleasedReturnValue(v66);
       objc_release(v65);
       v68 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
       v69 = objc_retainAutoreleasedReturnValue(v68);
       v70 = objc_msgSend(&OBJC_CLASS___WCPayLogicMgr, "class");
       v71 = objc_msgSend(v69, "getService:", v70);
       v72 = objc_retainAutoreleasedReturnValue(v71);
       objc_msgSend(v72, "setRealnameReportScene:", 1003LL);
       objc_release(v72);
       objc_release(v69);
       objc_initWeak(location, (id)v63);
       v74 = objc_msgSend(*(id *)(v73 + 480), "currentContext");
       v75 = objc_retainAutoreleasedReturnValue(v74);
       v76 = objc_msgSend(&OBJC_CLASS___WCPayLogicMgr, "class");
       v77 = objc_msgSend(v75, "getService:", v76);
       v78 = objc_retainAutoreleasedReturnValue(v77);
       v79 = objc_msgSend(*(id *)(v63 + 59), v85);
       v80 = objc_retainAutoreleasedReturnValue(v79);
       v81 = objc_msgSend(v80, "objectForKeyedSubscript:", CFSTR("agree_duty"));
       v82 = objc_retainAutoreleasedReturnValue(v81);
       v93[0] = (__int64)_NSConcreteStackBlock;
       v93[1] = 3254779904LL;
       v93[2] = (__int64)sub_1045DBCD0;
       v93[3] = (__int64)&unk_10B2AA6D0;
       objc_copyWeak(&v95, location);
       v94 = v18;
       v91[0] = (__int64)_NSConcreteStackBlock;
       v91[1] = 3254779904LL;
       v91[2] = (__int64)sub_1045DBD2C;
       v91[3] = (__int64)&unk_10B2AA580;
       v83 = objc_retain(v18);
       objc_copyWeak(&v92, location);
       objc_msgSend(v78, "checkHongbaoOpenLicense:acceptCallback:denyCallback:", v82, v93, v91);
    
       objc_release(v82);
       objc_release(v84);
       objc_release(v78);
       objc_release(v75);
       objc_destroyWeak(&v92);
       objc_release(v94);
       objc_release(v83);
       objc_destroyWeak(&v95);
       objc_destroyWeak(location);
       objc_release(v67);
       objc_release(v87);
       objc_release(v88);
       objc_release(v89);
       objc_release(v90);
    }
    

    30-项目实战(2)中,其实我们已经还原了前面一小部分。大致是这样👇🏻

    • 我们接着还原👇🏻
    v15 = +[WCBizUtil dictionaryWithDecodedComponets:separator:](
               &OBJC_CLASS___WCBizUtil,
               "dictionaryWithDecodedComponets:separator:",
               v14,
               CFSTR("&"));
    

    这是个+(类方法),类是WCBizUtil,SEL是dictionaryWithDecodedComponets:separator:,第一个参数是v14(即NativeUrl2),第二个参数是"&",那么还原后即👇🏻

    id result = [WCBizUtil dictionaryWithDecodedComponets:NativeUrl2 separator:@"&"];
    

    求证下,WCBizUtil中是有这个类方法的👇🏻

    继续,我们logos hook,查看下result具体是什么?👇🏻
    当然,首先需要声明

    @interface WCPayInfoItem
    @property(retain, nonatomic) NSString *m_c2cNativeUrl;
    @end
    
    @interface CMessageWrap
    @property(retain, nonatomic) WCPayInfoItem *m_oWCPayInfoItem;
    @end
    
    @interface WCRedEnvelopesControlData
    @property(retain, nonatomic) CMessageWrap *m_oSelectedMessageWrap;
    @end
    
    @interface WCRedEnvelopesReceiveControlLogic
    {
        WCRedEnvelopesControlData *m_data;
    }
    - (void)reportReceiveHomeViewEmoticon;
    @end
    
    @interface WCBizUtil
    + (id)dictionaryWithDecodedComponets:(id)arg1 separator:(id)arg2;
    @end
    

    然后,还是在上一篇的WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:方法中hook👇🏻

    %hook WCRedEnvelopesReceiveControlLogic
    - (void)WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:(id)arg1 {
        // 这个流程是UIView相关的
        [self reportReceiveHomeViewEmoticon];
        WCRedEnvelopesControlData *m_data = MSHookIvar<WCRedEnvelopesControlData *>(self, "m_data");
        NSString *NativeUrl2 = m_data.m_oSelectedMessageWrap.m_oWCPayInfoItem.m_c2cNativeUrl;
    //    NSLog(@"m_c2cNativeUrl:%@", NativeUrl2);
        
        id result = [%c(WCBizUtil) dictionaryWithDecodedComponets:NativeUrl2 separator:@"&"];
        NSLog(@"result:%@\nresultClass:%@", result, [result class]);
    }
    %end
    

    ⚠️注意:在logos语法中,调用类方法是这么使用 👉🏻 %c(类名)

    接着编译安装,查看控制台打印👇🏻

    可见,result是个字典,而且是个可变字典NSMutableDictionary,所以我们可以改成这样👇🏻

    • 接着继续反汇编👇🏻
    v17 = objc_msgSend(&OBJC_CLASS___NSMutableDictionary, "dictionary");
    v18 = objc_retainAutoreleasedReturnValue(v17);
    

    这两句不难,很直观,就是👇🏻

    v18 = [NSMutableDictionary dictionary];
    

    给v18个变量接收下,并使用%c👇🏻

    NSMutableDictionary *mutable_dic = [%c(NSMutableDictionary) dictionary];
    
    • 接着继续反汇编👇🏻
    objc_msgSend(v18, "safeSetObject:forKey:", CFSTR("1"), CFSTR("msgType"));
    v19 = objc_msgSend(v16, "objectForKey:", CFSTR("sendid"));
    v20 = objc_retainAutoreleasedReturnValue(v19);
    objc_msgSend(v18, "safeSetObject:forKey:", v20, CFSTR("sendId"));
    objc_release(v20);
    v21 = objc_msgSend(v16, "objectForKey:", CFSTR("channelid"));
    v22 = objc_retainAutoreleasedReturnValue(v21);
    objc_msgSend(v18, "safeSetObject:forKey:", v22, CFSTR("channelId"));
    objc_release(v22);
    

    注意有个safeSetObject:forKey:,而且是v18(NSMutableDictionary)调用的,那我们可以猜测,WeChat中对NSMutableDictionary加了个分类,我们去头文件中搜索一下👇🏻

    果然有,这个我们还原的时候就偷下懒,直接写成这样👇🏻

    [mutable_dic setObject:@"1" forKey:@"msgType"];
    

    再看v20 = objc_retainAutoreleasedReturnValue(v19);,而·v19 = objc_msgSend(v16, "objectForKey:", CFSTR("sendid")),v16是url_dic,于是还原👇🏻

    [mutable_dic setObject:[url_dic objectForKey:@"sendid"] forKey:@"sendId"];
    [mutable_dic setObject:[url_dic objectForKey:@"channelid"] forKey:@"channelId"];
    
    • 继续
    v23 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
    v24 = objc_retainAutoreleasedReturnValue(v23);
    v25 = objc_msgSend(&OBJC_CLASS___CContactMgr, "class");
    

    和之前一样,是类方法,于是还原👇🏻

    MMContext * context = [%c(MMContext) currentContext];
    Class ccMgr = [%c(CContactMgr) class];
    
    • 继续
    v26 = objc_msgSend(v24, "getService:", v25);
    

    v24是context,v25是ccMgr,那么还原👇🏻

    [context getService:ccMgr];
    

    返回类型是什么呢?先去看看头文件MMContext.h中定义的getService:方法👇🏻

    是个id类型,老办法 👉🏻 我们logos hook打印查看👇🏻

    @interface MMContext
    + (id)currentContext;
    - (id)getService:(Class)arg1;
    @end
    
    %hook WCRedEnvelopesReceiveControlLogic
    - (void)WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:(id)arg1 {
        // 这个流程是UIView相关的
        [self reportReceiveHomeViewEmoticon];
        WCRedEnvelopesControlData *data = MSHookIvar<WCRedEnvelopesControlData *>(self, "m_data");
        NSString *NativeUrl2 = data.m_oSelectedMessageWrap.m_oWCPayInfoItem.m_c2cNativeUrl;
        
        NSMutableDictionary *url_dic = [%c(WCBizUtil) dictionaryWithDecodedComponets:NativeUrl2 separator:@"&"];
        NSMutableDictionary *mutable_dic = [%c(NSMutableDictionary) dictionary];
        [mutable_dic setObject:@"1" forKey:@"msgType"];
        [mutable_dic setObject:url_dic[@"sendid"] forKey:@"sendId"];
        [mutable_dic setObject:url_dic[@"channelid"] forKey:@"channelId"];
       
        MMContext * context = [%c(MMContext) currentContext];
        Class ccMgr = [%c(CContactMgr) class];
        id result = [context getService:ccMgr];
        NSLog(@"result:%@\nresultClass:%@", result, [result class]);
    }
    %end
    

    编译安装,运行查看控制台输出👇🏻

    result:<CContactMgr: 0x282641130>
    resultClass:CContactMgr
    

    接着我们改一下👇🏻

    @interface CContactMgr
    
    @end
    
    @interface MMContext
    + (id)currentContext;
    - (CContactMgr *)getService:(Class)arg1;
    @end
    
    // 调用的地方
    CContactMgr * contactMgr = [context getService:ccMgr];
    

    二、还原红包方法(中)

    • 继续,我们接着还原
    v28 = objc_msgSend(v27, "getSelfContact");
    v29 = objc_retainAutoreleasedReturnValue(v28);
    objc_release(v27);
    objc_release(v24);
    

    上面我们通过动态调试打印得出 👉🏻 v27CContactMgr,那么还原后👇🏻

    CContact *selfContact = [contactMgr getSelfContact];
    

    其中CContact也是通过动态调试打印获取!(读者可自行操作)后面的release 操作可忽略。

    • 继续👇🏻
    v30 = objc_msgSend(v29, "getContactDisplayName");
    v31 = objc_retainAutoreleasedReturnValue(v30);
    

    v29是CContact,我们搜索头文件,发现没有方法getContactDisplayName👇🏻

    怎么办 👉🏻 去父类CBaseContact中找,确实有👇🏻

    所以,还原后👇🏻

    id displayName = [selfContact getContactDisplayName];
    
    • 继续👇🏻
       objc_msgSend(v18, "safeSetObject:forKey:", v31, CFSTR("nickName"));
       objc_release(v31);
    

    翻到上面可知,v18就是哪个声明的[NSMutableDictionary dictionary],即mutable_dic,将v31的值set进去👇🏻

    [mutable_dic setObject:displayName forKey:@"nickName"];
    

    *继续

       v88 = v29;
       v32 = objc_msgSend(v29, "m_nsHeadImgUrl");
       v33 = objc_retainAutoreleasedReturnValue(v32);
       objc_msgSend(v18, "safeSetObject:forKey:", v33, CFSTR("headImg"));
       objc_release(v33);
    

    先看v29CContact,它有个m_nsHeadImgUrl方法,求证下,发现还是在父类CBaseContact中👇🏻

    m_nsHeadImgUrl是一个属性,取出它的值即v33,在设置到v18之中,那么还原后👇🏻

    [mutable_dic setObject:[selfContact m_nsHeadImgUrl] forKey:@"headImg"];
    
    • 继续👇🏻
    objc_msgSend(v18, "safeSetObject:forKey:", a3, CFSTR("left_button_continue"));
    objc_release(v86);
    

    关键a3是什么?我们可以查看汇编,可以得到就是方法参数arg1,于是还原👇🏻

    [mutable_dic setObject:arg1 forKey:@"left_button_continue"];
    
    • 继续👇🏻
      // v35 = objc_msgSend(*(id *)(v34 + 59), "m_oSelectedMessageWrap");
    

    难点来了 👉🏻 v34 + 59是什么鬼?看代码可知是个指向指针的指针。我们来具体看看👇🏻

    将鼠标移上去,查看注释是x21,我们回到汇编代码查看👇🏻

    x19就是m_oSelectedMessageWrap👇🏻

    然后,v34 + 59在汇编中是[x21,x27],即x21偏移x27,接下来看看x21是什么?👇🏻

    所以x21即self,那偏移x27是什么意思呢?往上翻看看x27的赋值👇🏻

    x24的值赋给x27,接着看x24👇🏻

    x24就是m_data,答案就出来了👇🏻

    v35 = objc_msgSend(*(id *)(v34 + 59), "m_oSelectedMessageWrap");
    👇🏻
    m_data.m_oSelectedMessageWrap
    
    • 继续👇🏻
      v36 = objc_retainAutoreleasedReturnValue(v35);
      objc_release(v36);
      if ( v36 )
      {
    

    if (m_data.m_oSelectedMessageWrap)
    {
    
    • 继续
    v38 = objc_msgSend(*(id *)(v37 + 59), "m_oSelectedMessageWrap");
    v39 = objc_retainAutoreleasedReturnValue(v38);
    v40 = objc_msgSend(v39, "m_oWCPayInfoItem");
    v41 = objc_retainAutoreleasedReturnValue(v40);
    v42 = objc_msgSend(v41, "m_c2cNativeUrl");
    v43 = objc_retainAutoreleasedReturnValue(v42);
    objc_msgSend(v18, "safeSetObject:forKey:", v43, CFSTR("nativeUrl"));
    objc_release(v43);
    objc_release(v41);
    objc_release(v39);
    }
    

    同理分析v37 + 59,其实也是m_data,那么还原👇🏻

    WCPayInfoItem * payInfoItem = [m_data.m_oSelectedMessageWrap m_oWCPayInfoItem];
    [mutable_dic setObject:[payInfoItem m_c2cNativeUrl] forKey:@"nativeUrl"];
    }
    
    • 继续👇🏻
       v44 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
       v45 = objc_retainAutoreleasedReturnValue(v44);
       v46 = objc_msgSend(&OBJC_CLASS___MMMsgLogicManager, "class");
       v47 = objc_msgSend(v45, "getService:", v46);
       v48 = objc_retainAutoreleasedReturnValue(v47);
       v49 = objc_msgSend(v48, "GetCurrentLogicController");
       v50 = objc_retainAutoreleasedReturnValue(v49);
       objc_release(v48);
       objc_release(v45);
    

    这块也不难,找类找方法,先声明,再还原👇🏻

    MMMsgLogicManager * redEnvelopesLogicMgr = [[%c(MMContext) currentContext] getService:[%c(MMMsgLogicManager) class]];
    WeixinContentLogicController * currentLogicController = [redEnvelopesLogicMgr GetCurrentLogicController];
    
    • 继续
    if ( v50 )
       {
         v52 = objc_msgSend(v50, "m_contact");
         v53 = objc_retainAutoreleasedReturnValue(v52);
         if ( v53 )
         {
           v54 = v53;
           v55 = objc_msgSend(v50, "m_contact");
           v56 = objc_retainAutoreleasedReturnValue(v55);
           v57 = objc_msgSend(v56, "m_nsUsrName");
           v58 = objc_retainAutoreleasedReturnValue(v57);
           objc_release(v58);
           objc_release(v56);
           objc_release(v54);
           if ( v57 )
           {
             v59 = objc_msgSend(v50, "m_contact");
             v60 = objc_retainAutoreleasedReturnValue(v59);
             v61 = objc_msgSend(v60, "m_nsUsrName");
             v62 = objc_retainAutoreleasedReturnValue(v61);
             objc_msgSend(v18, "safeSetObject:forKey:", v62, CFSTR("sessionUserName"));
             objc_release(v62);
             objc_release(v60);
            }
          }
       }
    

    一样不难,还原👇🏻

    CBaseContact *m_contact;
    if (currentLogicController)
    {
      //追踪头文件,在父类(BaseMsgContentLogicController)中定义了CBaseContact *m_contact;
      CBaseContact * m_contact = [currentLogicController m_contact];
      if (m_contact)
      {
        //通过头文件继续追踪,得到成员是NSString类型!
        NSString * nsUsrName = [m_contact m_nsUsrName];
        if ( nsUsrName )
        {
          [mutable_dic setObject:nsUsrName forKey:@"sessionUserName"];
        }
      }
    }
    
    • 继续👇🏻
       v87 = v50;
       v63 = v51;
       v64 = objc_msgSend(*(id *)(v51 + 59), "m_structDicRedEnvelopesBaseInfo", "m_structDicRedEnvelopesBaseInfo");
       v65 = objc_retainAutoreleasedReturnValue(v64);
    

    v64 和 v65就是重点了。
    先看v64 = objc_msgSend(*(id *)(v51 + 59), "m_structDicRedEnvelopesBaseInfo", "m_structDicRedEnvelopesBaseInfo");,一看m_structDicRedEnvelopesBaseInfo就知道v51 + 59就是m_data,但是为什么有2个参数"m_structDicRedEnvelopesBaseInfo", "m_structDicRedEnvelopesBaseInfo"?还是要看汇编代码👇🏻

    所以这2句还原后是👇🏻

    NSDictionary * baseInfo = [m_data m_structDicRedEnvelopesBaseInfo];
    
    • 继续
       v66 = objc_msgSend(v65, "stringForKey:", CFSTR("timingIdentifier"));
       v67 = objc_retainAutoreleasedReturnValue(v66);
       objc_release(v65);
       v68 = +[MMContext currentContext](&OBJC_CLASS___MMContext, "currentContext");
       v69 = objc_retainAutoreleasedReturnValue(v68);
       v70 = objc_msgSend(&OBJC_CLASS___WCPayLogicMgr, "class");
       v71 = objc_msgSend(v69, "getService:", v70);
       v72 = objc_retainAutoreleasedReturnValue(v71);
       objc_msgSend(v72, "setRealnameReportScene:", 1003LL);
       objc_release(v72);
       objc_release(v69);
    

    这块也很简单,参考之前的还原,很快能得到👇🏻

    //通过方法可以判断是获取一个字符串!
    NSString * timingIdentifier = [baseInfo stringForKey:@"timingIdentifier"];
    //通过动态调试获取真实类型
    WCPayLogicMgr * payLogic = [[%c(MMContext) currentContext] getService:[WCPayLogicMgr class]];
    [payLogic setRealnameReportScene:(unsigned int)1003];
    
    • 继续
     objc_initWeak(location, (id)v63);
    

    这是selfweak弱引用!👇🏻

    __weak WCRedEnvelopesReceiveControlLogic * selfWeak = self;
    

    这个弱引用什么时候调用呢?是在后面回调中使用的。

    • 继续
       v74 = objc_msgSend(*(id *)(v73 + 480), "currentContext");
       v75 = objc_retainAutoreleasedReturnValue(v74);
       v76 = objc_msgSend(&OBJC_CLASS___WCPayLogicMgr, "class");
       v77 = objc_msgSend(v75, "getService:", v76);
       v78 = objc_retainAutoreleasedReturnValue(v77);
    
    

    v73 + 480 按照m_data的经验,可根据"currentContext"猜测就是MMContext,于是👇🏻

    WCPayLogicMgr *payLogicMgr = [[%c(MMContext) currentContext] getService:[WCPayLogicMgr class]];
    
    • 继续
       v79 = objc_msgSend(*(id *)(v63 + 59), v85);
       v80 = objc_retainAutoreleasedReturnValue(v79);
       v81 = objc_msgSend(v80, "objectForKeyedSubscript:", CFSTR("agree_duty"));
       v82 = objc_retainAutoreleasedReturnValue(v81);
    

    v79 = objc_msgSend(*(id *)(v63 + 59), v85);这句是难点,主要看v63 + 59), v85
    首先v63👇🏻

    上图可见 👉🏻 v63v51,就是self,所以v63 + 59就是m_data

    然后v85,查看汇编代码👇🏻

    我们继续往上翻汇编,搜索[sp, #0x110+var_110]👇🏻

    所以,v85就是m_structDicRedEnvelopesBaseInfo,那么还原结果👇🏻

    NSDictionary * baseInfo2 = [m_data m_structDicRedEnvelopesBaseInfo];
    

    接着agree_duty的还原👇🏻

    id subscript = [baseInfo2 objectForKeyedSubscript:@"agree_duty"];
    
    • 最后,关于block的还原👇🏻
       v93[0] = (__int64)_NSConcreteStackBlock;
       v93[1] = 3254779904LL;
       v93[2] = (__int64)sub_1045DBCD0;
       v93[3] = (__int64)&unk_10B2AA6D0;
       objc_copyWeak(&v95, location);
       v94 = v18;
       v91[0] = (__int64)_NSConcreteStackBlock;
       v91[1] = 3254779904LL;
       v91[2] = (__int64)sub_1045DBD2C;
       v91[3] = (__int64)&unk_10B2AA580;
       v83 = objc_retain(v18);
       objc_copyWeak(&v92, location);
       objc_msgSend(v78, "checkHongbaoOpenLicense:acceptCallback:denyCallback:", v82, v93, v91);
    

    先看最后面的一句👇🏻

    objc_msgSend(v78, "checkHongbaoOpenLicense:acceptCallback:denyCallback:", v82, v93, v91);
    

    找v78,v78 是v77的结果👇🏻

       v77 = objc_msgSend(v75, "getService:", v76);
       v78 = objc_retainAutoreleasedReturnValue(v77);
    

    那么v78WCPayLogicMgr *payLogicMgr。接着看v82,就是才得到的id subscript,而v93v91是2个NSConcreteStackBlock栈block。我们可以还原如下👇🏻

      [payLogicMgr checkHongbaoOpenLicense:subscript  acceptCallback:^(){
    
      } denyCallback:^(){
            
      }];
    

    v93acceptCallback的实现,v91denyCallback的实现,很明显,我们肯定要研究的是acceptCallback的实现即v93

    小结

    最终的hook代码如下👇🏻

    %hook WCRedEnvelopesReceiveControlLogic
    - (void)WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:(id)arg1 {
        // 这个流程是UIView相关的
        [self reportReceiveHomeViewEmoticon];
        WCRedEnvelopesControlData *data = MSHookIvar<WCRedEnvelopesControlData *>(self, "m_data");
        NSString *NativeUrl2 = data.m_oSelectedMessageWrap.m_oWCPayInfoItem.m_c2cNativeUrl;
        
        NSMutableDictionary *url_dic = [%c(WCBizUtil) dictionaryWithDecodedComponets:NativeUrl2 separator:@"&"];
        NSMutableDictionary *mutable_dic = [%c(NSMutableDictionary) dictionary];
        [mutable_dic setObject:@"1" forKey:@"msgType"];
        [mutable_dic setObject:url_dic[@"sendid"] forKey:@"sendId"];
        [mutable_dic setObject:url_dic[@"channelid"] forKey:@"channelId"];
       
        MMContext * context = [%c(MMContext) currentContext];
        Class ccMgr = [%c(CContactMgr) class];
        CContactMgr *contactMgr = [context getService:ccMgr];
        CContact *selfContact = [contactMgr getSelfContact];
        id displayName = [selfContact getContactDisplayName];
        [mutable_dic setObject:displayName forKey:@"nickName"];
        [mutable_dic setObject:[selfContact m_nsHeadImgUrl] forKey:@"headImg"];
        [mutable_dic setObject:arg1 forKey:@"left_button_continue"];
    
        if(data.m_oSelectedMessageWrap){
            WCPayInfoItem * payInfoItem = [data.m_oSelectedMessageWrap m_oWCPayInfoItem];
            [mutable_dic setObject:[payInfoItem m_c2cNativeUrl] forKey:@"nativeUrl"];
        }
    
        MMMsgLogicManager * redEnvelopesLogicMgr = [[%c(MMContext) currentContext] getService:[%c(MMMsgLogicManager) class]];
        WeixinContentLogicController * currentLogicController = [redEnvelopesLogicMgr GetCurrentLogicController];
        if (currentLogicController) {
          CBaseContact * m_contact = [currentLogicController m_contact];
          if (m_contact) {
              NSString * nsUsrName = [m_contact m_nsUsrName];
              if ( nsUsrName ) {
                  [mutable_dic setObject:nsUsrName forKey:@"sessionUserName"];
              }
          }
        }
        NSDictionary * baseInfo = [data m_structDicRedEnvelopesBaseInfo];
        NSString * timingIdentifier = [baseInfo stringForKey:@"timingIdentifier"];
        if ([timingIdentifier length]){
          [mutable_dic setObject:timingIdentifier forKey:@"timingIdentifier"];
        }
        WCPayLogicMgr * payLogic = [[%c(MMContext) currentContext] getService:[%c(WCPayLogicMgr) class]];
        [payLogic setRealnameReportScene:(unsigned int)1003];
        //这里有一个self的weak弱引用!
        __weak WCRedEnvelopesReceiveControlLogic * selfWeak = self;
        WCPayLogicMgr *payLogicMgr = [[%c(MMContext) currentContext] getService:[%c(WCPayLogicMgr) class]];
        NSDictionary * baseInfo2 = [data m_structDicRedEnvelopesBaseInfo];
        id subscript = [baseInfo2 objectForKeyedSubscript:@"agree_duty"];
        [payLogicMgr checkHongbaoOpenLicense:subscript  acceptCallback:^(){
    
        } denyCallback:^(){
                
        }];
    }
    %end
    

    其中涉及的类声明👇🏻

    @interface NSDictionary (WWKApiSafeCast)
    - (id)stringForKey:(id)arg1;
    @end
    
    @interface WCPayInfoItem
    @property(retain, nonatomic) NSString *m_c2cNativeUrl;
    @end
    
    @interface CMessageWrap
    @property(retain, nonatomic) WCPayInfoItem *m_oWCPayInfoItem;
    @end
    
    @interface WCRedEnvelopesControlData
    @property(retain, nonatomic) CMessageWrap *m_oSelectedMessageWrap;
    @property(retain, nonatomic) NSDictionary *m_structDicRedEnvelopesBaseInfo;
    @end
    
    @interface WCRedEnvelopesReceiveControlLogic
    {
        WCRedEnvelopesControlData *m_data;
    }
    - (void)reportReceiveHomeViewEmoticon;
    @end
    
    @interface WCBizUtil
    + (NSMutableDictionary *)dictionaryWithDecodedComponets:(id)arg1 separator:(id)arg2;
    @end
    
    
    @interface MMContext
    + (id)currentContext;
    - (id)getService:(Class)arg1;
    
    @end
    
    @interface CContactMgr
    - (id)getSelfContact;
    @end
    
    @interface CBaseContact
    @property(retain, nonatomic) NSString *m_nsUsrName;
    @end
    
    @interface CContact
    @property(retain, nonatomic) NSString *m_nsHeadImgUrl;
    - (id)getContactDisplayName;
    @end
    
    @interface WeixinContentLogicController
    @property(retain, nonatomic) CBaseContact *m_contact;
    @end
    
    @interface MMMsgLogicManager
    - (WeixinContentLogicController *)GetCurrentLogicController;
    @end
    
    @interface WCPayLogicMgr
    - (void)setRealnameReportScene:(unsigned int)arg1;
    - (void)checkHongbaoOpenLicense:(id)arg1 acceptCallback:(void(^)())arg2 denyCallback:(void(^)())arg3;
    @end
    

    三、还原红包方法(下)

    接下来,我们就是要研究acceptCallback回调中的实现。先看汇编代码👇🏻

    上图可知,红框处的关键代码,我们得弄清楚参数是什么

    • v2是什么?👇🏻

    v2是loadWeak,一看就猜到是之前的__weak WCRedEnvelopesReceiveControlLogic * selfWeak = self;selfWeak,它调用方法OpenRedEnvelopeRequest

    接下来,我们来验证下,这个是不是selfWeak?如何验证呢,其实不难,我们直接写个demo工程,写个weak,再在block中用一下,然后使用IDA逆向查看下,一对比不就明了了,是吧?!

    验证weak

    1. 首先新建demo工程,在ViewController.m中,写入以下代码👇🏻
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        __weak ViewController *selfWeak = self;
        [self hahaBlock:^{
            [selfWeak openRed];
        } withblock2:^{
            
        }];
        
    }
    
    - (void)hahaBlock:(void(^)(void))block withblock2:(void(^)(void))block2 {
        block();
        block2();
    }
    
    - (void)openRed {
        // 打开红包
    }
    
    1. 然后编译生成包,使用IDA打开Mach-O文件,查看伪代码👇🏻

    接着双击v6查看block实现的伪代码👇🏻

    看到没,block实现里面,果然就是loadWeak,证明我们之前的猜测是对的!其中v1就是外层的v12(即self)。

    继续还原

    再回过头看WeChat中的👇🏻

    至此,我们可以还原出回调里的执行👇🏻

        [payLogicMgr checkHongbaoOpenLicense:subscript  acceptCallback:^(){
                [selfWeak OpenRedEnvelopesRequest:参数];
        } denyCallback:^(){
                
        }];
    

    接着就是看OpenRedEnvelopesRequest的入参,IDA中传递的是a1+32,这个表示什么意思? 看汇编代码当然可以,但是要分析,有些麻烦。还有另一个方法 👉🏻 直接下断点查看。

    • 断点查看a1+32
      首先搜索一下OpenRedEnvelopesRequest,找到汇编代码,拿到地址👇🏻

    当然,这个方法的首地址,是偏移前的地址,需要加上库的首地址,才是方法实现的真正地址,接着我们拿到库的首地址 👉🏻 image list 👇🏻

    然后相加得出真正的地址,dis -s指令查看地址所对应的汇编代码👇🏻

    ⚠️注意:方法首地址0x1045DA0E0,其中的1PageDefault,不需要参与运算!

    与IDA中的汇编比对下👇🏻

    一比对,是一样的!🍺🍺🍺🍺🍺🍺 看来之前的猜测是对的,v2是selfWeak。继续看看request的入参,我们发个红包,点击拆红包触发断点,查看x2寄存器(即入参)👇🏻

    是个字典,看key,是不是很眼熟?通过我们之前还原的代码,就是mutable_dic,因为它里面也有这些key👇🏻

    看看left_button_continue👇🏻

    safeSetObject:forKey:设置进去的,可能此时value是nil,所以x2寄存器中没有left_button_continue

    综上,回调中的汇编就可以还原了👇🏻

      [payLogicMgr checkHongbaoOpenLicense:subscript  acceptCallback:^(){
          [selfWeak OpenRedEnvelopesRequest:mutable_dic];
        } denyCallback:^(){
                
        }];
    

    然后我们收一个红包,一下,可以直接领取红包的,只是此时没有金币转动的动画的。

    四、还原OpenRedER方法

    上面我们还原了拆红包的方法逻辑,但是,我们从始至终都是hookWCRedEnvelopesReceiveControlLogic这个vc的方法- (void)WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:(id)arg1,而这个是在红包视图上的方法,并不能达到提前抢红包的目的 👉🏻 我们是想要在类CMessageMgr的方法- (void)CheckMessageStatus:(id)arg1 Msg:(id)arg2中自动抢红包。

    因此,根据上面还原的方法分析,我们得再做几件事👇🏻

    • mutable_dic这个字典里的参数信息,得想办法获取。
    • 开红包最终调用的是[selfWeak OpenRedEnvelopesRequest:mutable_dic];,而selfWeak的类是WCRedEnvelopesReceiveControlLogic,但是我们之前hook的CheckMessageStatus:Msg:方法不是这个类。

    那怎么办呢?👉🏻 只能继续分析还原OpenRedEnvelopesRequest:方法的内部实现👇🏻

    1. hookOpenRedEnvelopesRequest:方法
      之前我们知道该方法传递的入参是mutable_dic,其类型是NSMutableDictionary,所以这么写👇🏻
    - (void)OpenRedEnvelopesRequest:(NSMutableDictionary *)dic{
    
    }
    
    1. 接着看IDA中的反汇编代码👇🏻

    ◦ 部分一

    先看v5,是x21,我们接着去汇编代码,看看x21到底是什么?👇🏻

    根据上图,所以第一句代码是👇🏻

    - (void)OpenRedEnvelopesRequest:(NSMutableDictionary *)dic{
        if([self isOpenIMHongBao]){
          
        }
    }
    

    WCRedEnvelopesReceiveControlLogic中是有方法isOpenIMHongBao👇🏻

    继续👇🏻

    其中a3即方法参数dic。那么继续还原👇🏻

    - (void)OpenRedEnvelopesRequest:(NSMutableDictionary *)dic{
        if([self isOpenIMHongBao]){
            [dic setObject:@"0" forKey:@"union_source"];
            [xxx OpenOpenIMRedEnvelopesRequest:dic];
        }else{
            [xxx OpenRedEnvelopesRequest:dic];
        }
    }
    

    ◦ 部分二
    接着看下面的反汇编👇🏻

    这块的类MMContextgetService方法,是不是很眼熟,就是我们之前还原红包方法时也还原过的👇🏻

        MMContext * context = [%c(MMContext) currentContext];
        Class ccMgr = [%c(CContactMgr) class];
        CContactMgr * contactMgr = [context getService:ccMgr];
    

    只不过这里getService传递的参数是类WCRedEnvelopesLogicMgr。是否这样呢?我们来验证下,看汇编👇🏻

    继续看最后一句objc_msgSend的调用👇🏻

    当然,最后还有一句self->super.super.super.m_uiLogicStatus = 4。而m_uiLogicStatus所在的类是👇🏻

    再看继承链关系👇🏻

    综上,还原如下👇🏻

    - (void)OpenRedEnvelopesRequest:(NSMutableDictionary *)dic{
        WCRedEnvelopesLogicMgr * redEnvelopesLogicMgr = [[%c(MMContext) currentContext] getService:[%c(WCRedEnvelopesLogicMgr) class]];
        //重新实现!!
        if([self isOpenIMHongBao]){
            NSLog(@"走的isOpenIMHongBao");
            [dic setObject:@"0" forKey:@"union_source"];
            [redEnvelopesLogicMgr OpenOpenIMRedEnvelopesRequest:dic];
        }else{
            NSLog(@"直接开红包!参数:%@",dic);
            [redEnvelopesLogicMgr OpenRedEnvelopesRequest:dic];
    //    self.m_uiLogicStatus = 4; // 这句代码先注释掉,不影响拆红包的流程
        }
    }
    

    当然,需要声明方法,完善我们的头文件声明👇🏻

    @interface WCRedEnvelopesReceiveControlLogic
    {
        WCRedEnvelopesControlData *m_data;
    }
    - (void)reportReceiveHomeViewEmoticon;
    - (void)OpenRedEnvelopesRequest:(id)arg1;
    - (bool)isOpenIMHongBao;
    @end
    
    @interface WCRedEnvelopesLogicMgr
    - (void)OpenOpenIMRedEnvelopesRequest:(id)arg1;
    - (void)OpenRedEnvelopesRequest:(id)arg1;
    @end
    

    断点调试

    最后,我们来调试看看,是走OpenOpenIMRedEnvelopesRequest还是走OpenRedEnvelopesRequest

    1. attach附加WeChat进程下断点,image list获取程序ASLR地址👇🏻

    ASLR地址是0x043e0000(⚠️去掉1PageDefault)。

    1. 找方法OpenOpenIMRedEnvelopesRequestOpenRedEnvelopesRequest的地址,在IDA里面搜索👇🏻

    OpenOpenIMRedEnvelopesRequest

    OpenOpenIMRedEnvelopesRequest0x103E2DE2C

    OpenRedEnvelopesRequest

    OpenRedEnvelopesRequest0x103E2DE54

    1. 下断点👇🏻
      OpenOpenIMRedEnvelopesRequest0x103E2DE2C+0x043e0000 = 0x10820DE2C
      OpenRedEnvelopesRequest0x103E2DE54+0x043e0000 = 0x10820DE54
    1. 接收一个红包消息,点击开红包,触发断点👇🏻

    上图看到调用栈中的第一个方法的地址是10820de54,那么就是触发的OpenRedEnvelopesRequest方法。

    五、找到消息方法中的关键参数

    我们再回过头来看还原的代码👇🏻

    上图可知,现在离自动拆红包,只差一个字典mutable_dic了。于是,我们可以将红框处的代码放到CheckMessageStatus:Msg:👇🏻

    接下来就是参数dic了。我们回过头观察下,dic的参数是从哪里来的?👇🏻

    上图可知,来源于单例类[MMContext currentContext]的dic中的值,从这个类调用getService方法,同时传递的参数也是[xx类 class],所以这些值在哪个方法都能够调用,不受参数约束。所以,重点是来源于data.m_oSelectedMessageWrap的值。

    接下来,我们看看data.m_oSelectedMessageWrap中具体是哪些值,我们打印看看👇🏻

    %hook WCRedEnvelopesReceiveControlLogic
    - (void)WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:(id)arg1 {
        // 这个流程是UIView相关的
        [self reportReceiveHomeViewEmoticon];
        WCRedEnvelopesControlData *data = MSHookIvar<WCRedEnvelopesControlData *>(self, "m_data");
        
        CMessageWrap *msgWrap =  data.m_oSelectedMessageWrap;
        NSLog(@"msgWrap:%@", msgWrap);
    }
    

    编译安装,接收打开红包消息,查看控制台输出👇🏻

    再看之前我们拿CheckMessageStatus:Msg:它的参数的信息是(可参考29-项目实战(1)
    中的小节四、定位消息管理者)👇🏻

    仔细观察arg2,其中的内容是不是和msgWrap一模一样?!🍺🍺🍺🍺🍺🍺

    六、移植代码发现缺乏参数

    接下来,我们就可以将之前在WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:方法中还原的代码,移植到CheckMessageStatus:Msg:之中。在移植之前,我们还有一个前提 👉🏻 必须是红包消息(也就是type == 49)。

    1. 首先,我们要去CMessageWrap类中找一找,有木有跟type相关的👇🏻

    果然有 👉🏻 m_uiMessageType,于是我们在CheckMessageStatus:Msg:中这么判断👇🏻

    %hook CMessageMgr
    - (void)CheckMessageStatus:(NSString *)arg1 Msg:(CMessageWrap *)msgWrap {
        // 判断是否是红包
        if (msgWrap.m_uiMessageType == 49) {
            
        } else {
            %orig;
        }
        %orig;
    }
    %end
    

    只判断红包类型是49满足吗?这里告诉你,不行!转账消息的类型一样是49。所以还要补充条件,我们先看看转账消息中的msgWrap与红包消息的msgWrap的区别。

    首先hook代码👇🏻

    
    %hook CMessageMgr
    - (void)CheckMessageStatus:(NSString *)arg1 Msg:(CMessageWrap *)msgWrap {
        NSString *NativeUrl2 = msgWrap.m_oWCPayInfoItem.m_c2cNativeUrl;
        NSLog(@"NativeUrl2:%@", NativeUrl2);
        NSLog(@"拿到的type:%d", msgWrap.m_uiMessageType);
        /*
        // 判断是否是红包
        if (msgWrap.m_uiMessageType == 49) {
            
        } else {
            %orig;
        }
        %orig;
        */
    }
    %end
    

    转账消息👇🏻(发送一条转账,查看控制台输出)

    红包消息👇🏻

    红包消息是有url的,而转账消息没有!所以,完善红包消息的判断👇🏻

    %hook CMessageMgr
    - (void)CheckMessageStatus:(NSString *)arg1 Msg:(CMessageWrap *)msgWrap {
        NSString *NativeUrl2 = msgWrap.m_oWCPayInfoItem.m_c2cNativeUrl;
        NSLog(@"NativeUrl2:%@", NativeUrl2);
        NSLog(@"拿到的type:%d", msgWrap.m_uiMessageType);
        
        // 判断是否是红包
        if ((msgWrap.m_uiMessageType == 49) && [NativeUrl2 containsString:@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?"]) {
            
        } else {
            %orig;
        }
        %orig;
        
    }
    %end
    

    其中wxpay://c2cbizmessagehandler/hongbao/receivehongbao?的值的由来,可参考30-项目实战(2)中的小节五、动态调试delegate

    再来补充移植的拼接参数的代码👇🏻

    %hook CMessageMgr
    - (void)CheckMessageStatus:(NSString *)arg1 Msg:(CMessageWrap *)msgWrap {
        NSString *NativeUrl2 = msgWrap.m_oWCPayInfoItem.m_c2cNativeUrl;
        if ((msgWrap.m_uiMessageType == 49)
            && [NativeUrl2 containsString:@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?"]) {  // 判断是红包
             NSString *m_c2cNativeUrl = msgWrap.m_oWCPayInfoItem.m_c2cNativeUrl;
            //这里要做一个截取
            NSUInteger len = [@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length];
            NSString *NativeUrl2 = [m_c2cNativeUrl substringFromIndex:length];
            NSMutableDictionary *url_dic = [%c(WCBizUtil) dictionaryWithDecodedComponets:NativeUrl2 separator:@"&"];
            NSMutableDictionary *mutable_dic = [%c(NSMutableDictionary) dictionary];
            [mutable_dic setObject:@"1" forKey:@"msgType"];
            [mutable_dic setObject:url_dic[@"sendid"] forKey:@"sendId"];
            [mutable_dic setObject:url_dic[@"channelid"] forKey:@"channelId"];
            
            MMContext * context = [%c(MMContext) currentContext];
            Class ccMgr = [%c(CContactMgr) class];
            CContactMgr *contactMgr = [context getService:ccMgr];
            CContact *selfContact = [contactMgr getSelfContact];
            id displayName = [selfContact getContactDisplayName];
            [mutable_dic setObject:displayName forKey:@"nickName"];
            [mutable_dic setObject:[selfContact m_nsHeadImgUrl] forKey:@"headImg"];
            // 之前分析 👉🏻 left_button_continue根本不需要
            //    [mutable_dic setObject:arg1 forKey:@"left_button_continue"];
            
            if(msgWrap){
                WCPayInfoItem * payInfoItem = [msgWrap m_oWCPayInfoItem];
                [mutable_dic setObject:[payInfoItem m_c2cNativeUrl] forKey:@"nativeUrl"];
            }
            MMMsgLogicManager * redEnvelopesLogicMgr = [[%c(MMContext) currentContext] getService:[%c(MMMsgLogicManager) class]];
            WeixinContentLogicController * currentLogicController = [redEnvelopesLogicMgr GetCurrentLogicController];
            if (currentLogicController) {
                CBaseContact * m_contact = [currentLogicController m_contact];
                if (m_contact) {
                    NSString * nsUsrName = [m_contact m_nsUsrName];
                    if ( nsUsrName ) {
                        [mutable_dic setObject:nsUsrName forKey:@"sessionUserName"];
                    }
                }
            }
            NSDictionary * baseInfo = [data m_structDicRedEnvelopesBaseInfo];
            NSString * timingIdentifier = [baseInfo stringForKey:@"timingIdentifier"];
            if ([timingIdentifier length]){
                [mutable_dic setObject:timingIdentifier forKey:@"timingIdentifier"];
            }
        } else {
            %orig;
        }
        %orig;
    }
    %end
    

    上图显而易见,timingIdentifier的值拿不到,它的来源是[data m_structDicRedEnvelopesBaseInfo]

    而剩下来的流程就是

        WCPayLogicMgr * payLogic = [[%c(MMContext) currentContext] getService:[%c(WCPayLogicMgr) class]];
        [payLogic setRealnameReportScene:(unsigned int)1003];
        //这里有一个self的weak弱引用!
        __weak WCRedEnvelopesReceiveControlLogic * selfWeek = self;
        WCPayLogicMgr *payLogicMgr = [[%c(MMContext) currentContext] getService:[%c(WCPayLogicMgr) class]];
        NSDictionary * baseInfo2 = [data m_structDicRedEnvelopesBaseInfo];
        id subscript = [baseInfo2 objectForKeyedSubscript:@"agree_duty"];
        [payLogicMgr checkHongbaoOpenLicense:subscript  acceptCallback:^(){
            [selfWeek OpenRedEnvelopesRequest:mutable_dic];
        } denyCallback:^(){
                
        }];
    

    其中

        WCPayLogicMgr * payLogic = [[%c(MMContext) currentContext] getService:[%c(WCPayLogicMgr) class]];
        [payLogic setRealnameReportScene:(unsigned int)1003];
    

    这是个单例方法,可移植!再接着就是checkHongbaoOpenLicense:acceptCallback:denyCallback回调,这个也可以忽略,最终来到OpenRedEnvelopesRequest:方法,这个方法我们之前还原过👇🏻

    - (void)OpenRedEnvelopesRequest:(NSMutableDictionary *)dic{
        WCRedEnvelopesLogicMgr * redEnvelopesLogicMgr = [[%c(MMContext) currentContext] getService:[%c(WCRedEnvelopesLogicMgr) class]];
        //重新实现!!
        if([self isOpenIMHongBao]){
            NSLog(@"走的isOpenIMHongBao");
            [dic setObject:@"0" forKey:@"union_source"];
            [redEnvelopesLogicMgr OpenOpenIMRedEnvelopesRequest:dic];
        }else{
            NSLog(@"直接开红包!参数:%@",dic);
            [redEnvelopesLogicMgr OpenRedEnvelopesRequest:dic];
    //    self.m_uiLogicStatus = 4; // 这句代码先注释掉,不影响拆红包的流程
        }
    }
    

    这里精简完后其实就是2句代码👇🏻

    WCRedEnvelopesLogicMgr * redEnvelopesLogicMgr = [[%c(MMContext) currentContext] getService:[%c(WCRedEnvelopesLogicMgr) class]];
    [redEnvelopesLogicMgr OpenRedEnvelopesRequest:dic];
    

    综上,最后就只差timingIdentifier,接下来在最后一篇文章中在分析!

    总结

    本篇文章接着30-项目实战(2),继续还原拆红包的剩余流程,其中包括WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:OpenRedEnvelopesRequest:这2个核心方法,接着通过移植代码到CheckMessageStatus:Msg:,我们需要配置字典参数,而巧合的是大部分参数值都是在CheckMessageStatus:Msg:Msg参数之中,最后就差timingIdentifier。希望大家能够动手自己还原汇编代码。

    相关文章

      网友评论

          本文标题:31-项目实战(3)

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