前言
本篇文章接着30-项目实战(2),继续讲解自动
抢红包的功能。今天的重点是 👉🏻 还原
红包方法WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:
。
一、还原红包方法(上)
在继续分析WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes:
后面的流程之前,我们先来了解下IDA
的伪代码
功能。
IDA伪代码功能
我们可以按F5
,进入伪代码的界面👇🏻
为了便于我们分析,可以鼠标右键,将这些伪代码生成Html文件(首先cmd+A全选)👇🏻
然后再将Html文件中的代码copy
到sublime编辑器
中,方便查看!
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);
上面我们通过动态调试打印得出 👉🏻 v27
是CContactMgr
,那么还原后👇🏻
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);
先看v29
是CContact
,它有个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);
这是self
的weak弱引用
!👇🏻
__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
👇🏻
上图可见 👉🏻 v63
是v51
,就是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);
那么v78
是WCPayLogicMgr *payLogicMgr
。接着看v82
,就是才得到的id subscript
,而v93
和 v91
是2个NSConcreteStackBlock
栈block。我们可以还原如下👇🏻
[payLogicMgr checkHongbaoOpenLicense:subscript acceptCallback:^(){
} denyCallback:^(){
}];
v93
是acceptCallback
的实现,v91
是denyCallback
的实现,很明显,我们肯定要研究的是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
- 首先新建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 {
// 打开红包
}
- 然后编译生成包,使用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
,其中的1
是PageDefault
,不需要参与运算!
与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:
方法的内部实现👇🏻
- hook
OpenRedEnvelopesRequest:
方法
之前我们知道该方法传递的入参是mutable_dic
,其类型是NSMutableDictionary
,所以这么写👇🏻
- (void)OpenRedEnvelopesRequest:(NSMutableDictionary *)dic{
}
- 接着看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];
}
}
◦ 部分二
接着看下面的反汇编👇🏻
这块的类MMContext
和 getService
方法,是不是很眼熟,就是我们之前还原红包方法
时也还原过的👇🏻
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
?
- attach附加WeChat进程下断点,
image list
获取程序ASLR地址👇🏻
ASLR地址是0x043e0000
(⚠️去掉1
PageDefault)。
- 找方法
OpenOpenIMRedEnvelopesRequest
和OpenRedEnvelopesRequest
的地址,在IDA里面搜索👇🏻
◦ OpenOpenIMRedEnvelopesRequest
OpenOpenIMRedEnvelopesRequest
是0x103E2DE2C
。
◦ OpenRedEnvelopesRequest
OpenRedEnvelopesRequest
是0x103E2DE54
。
- 下断点👇🏻
◦OpenOpenIMRedEnvelopesRequest
是0x103E2DE2C+0x043e0000 = 0x10820DE2C
◦OpenRedEnvelopesRequest
是0x103E2DE54+0x043e0000 = 0x10820DE54
- 接收一个红包消息,点击
开红包
,触发断点👇🏻
上图看到调用栈中的第一个方法的地址是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
)。
- 首先,我们要去
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
。希望大家能够动手自己还原汇编代码。