上文已经知道收到红包消息与开红包方法,那么只需在收到红包消息时,调用开红包方法,就可以完成自动抢红包
完整的工程地址
一 红包消息时调用开红包方法
上篇hook到了收到红包消息的方法就是CMessageMgr类中的onNewSyncAddMessage:方法,于是把开红包的代码放到此方法中。
onNewSyncAddMessage:方法中,当m_uiMessageType=49的时候是红包消息,此时判断是否开启了自动抢红包功能
- (void)onNewSyncAddMessage:(CMessageWrap *)m_Wrap{
if(MSHookIvar<unsigned int>(m_Wrap,"m_uiMessageType") != 49){//不是红包消息
%orig;
return;
}
if([WCDefaults boolForKey:WCSWITCHKEY] == NO){//是否开启自动抢红包
%orig;
return;
}
WCPayInfoItem *item =[m_Wrap m_oWCPayInfoItem];
NSString *url =[item m_c2cNativeUrl];
NSInteger length = [@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length];
id componets = [url substringFromIndex:length];
NSDictionary *dictionary=[%c(WCBizUtil) dictionaryWithDecodedComponets:componets separator:@"&"];
NSMutableDictionary *mul_dict =[%c(NSMutableDictionary) dictionary];
[mul_dict setObject:@"1" forKey:@"msgType"];
[mul_dict setObject:dictionary[@"sendid"] forKey:@"sendId"];
[mul_dict setObject:dictionary[@"channelid"] forKey:@"channelId"];
CContactMgr *server =[[%c(MMServiceCenter) defaultCenter] getService:[%c(CContactMgr) class]];
CContact *contact = [server getSelfContact];
id displayName = [contact getContactDisplayName];
[mul_dict setObject:displayName forKey:@"nickName"];
id m_nsHeadImgUrl = [contact m_nsHeadImgUrl];
[mul_dict setObject:m_nsHeadImgUrl forKey:@"headImg"];
if ( url )
{
[mul_dict setObject:url forKey:@"nativeUrl"];
}
NSString *m_nsUsrName = MSHookIvar<NSString *>(m_Wrap,"m_nsFromUsr");
[mul_dict setObject:m_nsUsrName forKey:@"sessionUserName"];
NSLog(@"---%@",m_nsUsrName);
//因为m_data获取不到,所以编译不过
NSDictionary *m_struct = [m_data m_structDicRedEnvelopesBaseInfo];
NSString *timingIdentifier= [m_struct objectForKey:@"timingIdentifier"];
if ( [timingIdentifier length])
{ [mul_dict setObject:timingIdentifier forKey:@"timingIdentifier"];}
WCPayLogicMgr *payLogicMgr =[[%c(MMServiceCenter) defaultCenter] getService:[%c(WCPayLogicMgr) class]];
[payLogicMgr setRealnameReportScene:1003];
id subScript = [m_struct objectForKeyedSubscript:@"agree_duty"];
[payLogicMgr checkHongbaoOpenLicense:subScript acceptCallback:^(){
WCRedEnvelopesLogicMgr *envelopesLogicMgr = [[%c(MMServiceCenter) defaultCenter] getService:[%c(WCRedEnvelopesLogicMgr) class]];
[envelopesLogicMgr OpenRedEnvelopesRequest:mul_dict];
}denyCallback:^(){
}];
}
%end
这个方法主要就是拼接mul_dict然后作为参数,调用WCRedEnvelopesLogicMgr类的OpenRedEnvelopesRequest:函数。mul_dict需要的参数,能从CMessageWrap中获取的直接获取,但是timingIdentifier获取不到,所以要另作分析,要想办法获取到timingIdentifier。
二 其他方法获取timingIdentifier
当红包消息收到后,点击红包消息时有菊花在转,说明在请求网络,请求过后才是红包弹出的界面,所以现在可以进行hook点击红包消息时的方法
-
通过分析知道WCRedEnvelopesLogicMgr类就是红包管理类,所以直接hook这个类中的所有方法:
然后进行分析,发现以下三个方法是点击红包消息时调用的方法:
[<WCRedEnvelopesLogicMgr: 0x10ef9aff0> ReceiverQueryRedEnvelopesRequest:{
agreeDuty = 0;
channelId = 1;
inWay = 1;
msgType = 1;
nativeUrl = "wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201805287004922762149&sendusername=wxid_hy6hye79l4q241&ver=6&sign=9995652ef3b076bbc7b626c0272f7cbbb2168732bc1a9af34777acbf4206d97062af9a8e3c84ad8812d45b48039d9c74e2f6fadb01223e74a9361a61947d4a19789abfde732b339ffb17c05c7bd7a2a0";
sendId = 1000039501201805287004922762149;
} ]
2018-05-28 10:21:42.258137+0800 WeChat[28565:2640576] -[<WCRedEnvelopesLogicMgr: 0x10ef9aff0> GetHongbaoBusinessRequest:{
agreeDuty = 0;
channelId = 1;
inWay = 1;
msgType = 1;
nativeUrl = "wxpay://c2cbizmessagehandler/hongbao/receivehongbao?msgtype=1&channelid=1&sendid=1000039501201805287004922762149&sendusername=wxid_hy6hye79l4q241&ver=6&sign=9995652ef3b076bbc7b626c0272f7cbbb2168732bc1a9af34777acbf4206d97062af9a8e3c84ad8812d45b48039d9c74e2f6fadb01223e74a9361a61947d4a19789abfde732b339ffb17c05c7bd7a2a0";
sendId = 1000039501201805287004922762149;
} CMDID:3 OutputType:1 ]
2018-05-28 10:21:42.490993+0800 WeChat[28565:2640576] [<WCRedEnvelopesLogicMgr: 0x10ef9aff0> OnWCToHongbaoCommonResponse:<HongBaoRes: 0x10efd1550> Request:<HongBaoReq: 0x10ed82840> ]
ReceiverQueryRedEnvelopesRequest:方法和GetHongbaoBusinessRequest:方法都是红包请求方法,参数也差不多,通过ida分析ReceiverQueryRedEnvelopesRequest:发现这个方法就是对GetHongbaoBusinessRequest:方法的封装。
- 那么可以得到ReceiverQueryRedEnvelopesRequest:和OnWCToHongbaoCommonResponse:Request:这两个就是关键方法
- 在onNewSyncAddMessage:收到红包消息的时候,主动调用GetHongbaoBusinessRequest:方法
WCRedEnvelopesLogicMgr *envelopesLogicMgr = [[%c(MMServiceCenter) defaultCenter] getService:[%c(WCRedEnvelopesLogicMgr) class]];
NSMutableDictionary *param =[%c(NSMutableDictionary) dictionary];
[param setObject:@"1" forKey:@"msgType"];
[param setObject:dictionary[@"channelid"] forKey:@"channelId"];
[param setObject:@"0" forKey:@"agreeDuty"];
[param setObject:@"1" forKey:@"inWay"];
if([m_nsUsrName cons containsString:@"@chatroom"]){//群红包
[param setObject:@"0" forKey:@"inWay"];
}
[param setObject:dictionary[@"sendid"] forKey:@"sendId"];
[param setObject:url forKey:@"nativeUrl"];
[envelopesLogicMgr ReceiverQueryRedEnvelopesRequest:param];
- 拼接参数,m_nsFromUsr字段包含@chatroom就是群红包
可以得到,主动调用ReceiverQueryRedEnvelopesRequest:方法和手动点击红包消息是一样的效果。那么我们再来分析OnWCToHongbaoCommonResponse:Request:方法,通过打印参数类型得到此方法的发第一个参数是HongBaoRes,然后通过头文件得到HongBaoRes如下:
@interface SKBuiltinBuffer_t : NSObject
@property(retain, nonatomic) NSData *buffer;
@property(nonatomic) unsigned int iLen;
@end
@interface SKBuiltinString_t : NSObject
@property(retain, nonatomic) NSString *string;
@end
@interface BaseResponse : NSObject
@property(retain, nonatomic) SKBuiltinString_t *errMsg;
@property(nonatomic) int ret;
@end
@interface HongBaoRes : NSObject
@property(retain, nonatomic) BaseResponse *baseResponse;
@property(nonatomic) int cgiCmdid; // @dynamic cgiCmdid;
@property(retain, nonatomic) NSString *errorMsg; // @dynamic errorMsg;
@property(nonatomic) int errorType; // @dynamic errorType;
@property(retain, nonatomic) NSString *platMsg; // @dynamic platMsg;
@property(nonatomic) int platRet; // @dynamic platRet;
@property(retain, nonatomic) SKBuiltinBuffer_t *retText; // @dynamic retText;
@end
然后我们在OnWCToHongbaoCommonResponse:Request:中打印第一个参数的相关信息:
- (void)OnWCToHongbaoCommonResponse:(HongBaoRes *)arg1 Request:(id)arg2
{
NSLog(@"platMsg:%@",arg1.platMsg);
NSLog(@"cgiCmdid:%d",arg1.cgiCmdid);
NSLog(@"platRet:%d",arg1.platRet);
NSLog(@"errorType:%d",arg1.errorType);
NSLog(@"errorMsg:%@",arg1.errorMsg);
NSData *buffer = arg1.retText.buffer;
NSString *aString = [[NSString alloc] initWithData:buffer encoding:NSUTF8StringEncoding];
NSLog(@"retText:%@",aString);
%orig;
}
打印输出:
2018-05-28 11:31:44.197315+0800 WeChat[28877:2671064] platMsg:
2018-05-28 11:31:44.197483+0800 WeChat[28877:2671064] cgiCmdid:3
2018-05-28 11:31:44.197575+0800 WeChat[28877:2671064] platRet:0
2018-05-28 11:31:44.197654+0800 WeChat[28877:2671064] errorType:0
2018-05-28 11:31:44.197852+0800 WeChat[28877:2671064] errorMsg:ok
2018-05-28 11:31:44.198045+0800 WeChat[28877:2671064] retText:{"retcode":0,"retmsg":"ok","sendId":"1000039501201805287004922762149","wishing":"恭喜发财,大吉大利","isSender":0,"receiveStatus":0,"hbStatus":2,"statusMess":"给你发了一个红包","hbType":0,"watermark":"","sendUserName":"wxid_hy6hye79l4q241","timingIdentifier":"35971B49DE4AE3E604E17E3EAB463272"}
可以得到retText:中有timingIdentifier
- 所以获取timingIdentifier
在收到红包消息时主动调用ReceiverQueryRedEnvelopesRequest:方法,然后在- (void)OnWCToHongbaoCommonResponse:(HongBaoRes *)arg1 Request:(id)arg2方法中的第一个参数HongBaoRes的retText属性里面包含timingIdentifier - 分析HongBaoRes参数
cgiCmdid==3,点击红包消息,没有被抢过的红包
HongBaoRes的retText中:
receiveStatus == 0没有抢过的红包 receiveStatus == 2抢过的红包;isSender==0自己不是发红包消息的人;isSender==1自己发的红包
- 获取到timingIdentifier
要在红包还没有抢过的情况下才抢红包
NSError *error;
NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:arg1.retText.buffer options:NSJSONReadingMutableContainers error:&error];
NSString *receiveStatus =[NSString stringWithFormat:@"%@",responseDict[@"receiveStatus"]];
if(arg1 !=nil && arg2 !=nil && arg1.cgiCmdid==3 && [receiveStatus isEqualToString:@"0"])//没有被抢过的红包
{
NSString *timingIdentifier = responseDict[@"timingIdentifier"];
}
那么获取到timingIdentifier后怎么通知收到红包消息的方法->CMessageMgr类中的onNewSyncAddMessage:方法呢?而且红包消息方法一下来很多条呢?
我们可以用单例队列来保存红包消息,在获取到timingIdentifier后取出队列中的参数,然后调用开红包方法
三 开始抢红包
- 单例队列来保存红包消息
WeChatMessageWarp.h
@interface WeChatMessageWarp : NSObject
+(instancetype)shareInstanceQueue;
- (void)inputQueue:(NSMutableDictionary *)param;
- (NSMutableDictionary *)getParamQueue;
@end
WeChatMessageWarp.m
@interface WeChatMessageWarp()
@property (nonatomic,strong)NSMutableArray *queue;
@end
@implementation WeChatMessageWarp
+(instancetype)shareInstanceQueue
{
static WeChatMessageWarp *queue = nil;
static dispatch_once_t once;
dispatch_once(&once,^{
queue = [[WeChatMessageWarp alloc]init];
});
return queue;
}
- (instancetype)init
{
self = [super init];
if (self) {
_queue = [NSMutableArray array];
}
return self;
}
- (void)inputQueue:(NSMutableDictionary *)param{
[self.queue addObject:param];
}
- (NSMutableDictionary *)getParamQueue{
if (self.queue.count == 0 && !self.queue.firstObject) {
return nil;
}
NSMutableDictionary *first = self.queue.firstObject;
[self.queue removeObjectAtIndex:0];
return first;
}
@end
- 在收到红包消息的时候保存参数mul_dict,参数重可以标记是不是自己写的红包插件
WeChatMessageWarp *share = [WeChatMessageWarp shareInstanceQueue];
//标记是插件抢红包
[mul_dict setObject:@"YES" forKey:@"isMySelfMeryin"];
[share inputQueue:mul_dict];
- 然后在获取到timingIdentifier后把参数取出来,并判断是不是自己写的插件
WeChatMessageWarp *share = [WeChatMessageWarp shareInstanceQueue];
NSMutableDictionary *warp = [share getParamQueue];
NSString *flag = [warp objectForKey:@"isMySelfMeryin"];
if(![flag isEqualToString:@"YES"]){//自己写的插件抢红包
return;
}
[warp removeObjectForKey:@"isMySelfMeryin"];
if(timingIdentifier.length >0 && warp.count >0){
[warp setObject:timingIdentifier forKey:@"timingIdentifier"];
}
- 开始抢红包
抢红包的时候,要判断插件是否设置了延迟时间,然后延迟几秒开始抢红包
//开始开红包
WCRedEnvelopesLogicMgr *envelopesLogicMgr = [[%c(MMServiceCenter) defaultCenter] getService:[%c(WCRedEnvelopesLogicMgr) class]];
if(envelopesLogicMgr){
//延迟抢红包时间
NSString *time = [WCDefaults valueForKey:WCTIMEKEY];
float second = 1;
if(time){//默认1秒
second =[time floatValue];
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(second * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[envelopesLogicMgr OpenRedEnvelopesRequest:warp];
});
}
四 优化工程结构
完成了自动抢红包代码后,一个类中的代码太多,我们可以按功能拆分代码
- 把设置界面的代码出来
新建->iOS -Other -Empty->WeChatSettingViewControllerCell.xm 注意targets勾选dylib
把设置界面的代码拷贝到WeChatSettingViewControllerCell.xm 然后build生成WeChatSettingViewControllerCell.mm,然后把.mm拖到工程中,注意targets勾选dylib