31-项目实战(3)

前言

本篇文章接着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。希望大家能够动手自己还原汇编代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容