1. 正题
之前看到一个公众号有讲微信的自动抢红包,自己看了好几个月其实也并没有做任何的逆向分析(主要是时间不够,具体原因很复杂)。文章中作者并没有提供完整的代码和思路,只是简单的说了几个点,说好的放github的代码也压根没有,再看看文档格式,好吧,可能就是搬运来的。那就自己分析呗。
以下内容中不明白的可以翻看以前我的文章
那今天我们的主题最基本的也是最简单就是要能够找到拆红包的函数。学习下别人怎么写拆红包和Tweak。
我在git上找了一个19年7月更新的红包插件,就拿这个来学习吧,还有另外一个是很早的了就不看了。
https://github.com/buginux/WeChatRedEnvelop
实际上在将这份代码编译安装跑起来的时候并不能真正的自动抢红包,甚至连抢红包功能都没有。只有作者推广的关注公众号和给作者转账打赏(感觉怎么像新时代的要饭的。。。讲真,从测试来看这种打赏几乎是没有或者超级少的人给你打赏的,尤其是技术圈,具体原因不详述,感兴趣我可以写写)
2.编译打包安装
编译可以自己将代码先clone一份放到theos目录同级目录自己搞个src目录下面去,如下:
total 16
0 drwxr-xr-x 5 bichonfrise staff 160 2 4 01:52 .
0 drwxr-xr-x+ 98 bichonfrise staff 3136 4 10 02:58 ..
16 -rw-r--r--@ 1 bichonfrise staff 6148 2 4 01:52 .DS_Store
0 drwxr-xr-x 4 bichonfrise staff 128 4 10 02:56 src
0 drwxr-xr-x 22 bichonfrise staff 704 12 26 00:04 theos
然后切到cd src/WeChatRedEnvelop/
目录去。
再就是直接make自己测试下看能不能编译的过了。
打包安装可以自己直接就make package; make install
就完事了,这里注意如果出现如下情况:
make[2]: Nothing to be done for `internal-library-compile'.
> Making stage for tweak xxx…
fakeroot, while creating message channels: Invalid argument
This may be due to a lack of SYSV IPC support.
fakeroot: error while starting the `faked' daemon.
/usr/local/bin/fakeroot: line 29: 16651 Abort trap: 6 exit 1
make: *** [internal-package] Error 1
直接在theos/makefiles/package/deb.mk
删除掉内容$(FAKEROOT) -r
,因为我是在自己用户的目录下创建的项目,所以压根用不到fakeroot。
这样就可以自己安装上插件了,以后有机会再整合到自己的私有源上面去,这样你就可以添加源然后自己下载了。
3.抢红包页面结构
自己在Lookin里面看看页面结构
从上面图中我们可以看到抢红包对应的处理事件就是在WCRedEnvelopesReceiveHomeView
中的openRedEnvelopesButton
对应的处理事件了,查看头文件如下:
WCRedEnvelopesReceiveHomeView.h
@interface WCRedEnvelopesReceiveHomeView : MMUIView
{
id <WCRedEnvelopesReceiveHomeViewDelegate> m_delegate;
UIImageView *m_backgroundView;
UIButton *openRedEnvelopesButton; // 抢红包的按钮
UIImageView *openAnimationImageView;
struct CGRect m_frame;
NSDictionary *m_dicBaseInfo;
WCRedEnvelopesControlData *m_data;
_Bool m_bSuccessAnmation;
UIView *oRedView;
UIView *oTopHeaderView;
UIView *imageView;
UIButton *cancel2Button;
MMWebImageView *maskImageView;
ContactUpdateHelper *_m_nickNameHelper;
ContactUpdateHelper *_m_successOpenNickNameHelper;
MMEmoticonView *_emoticonView;
UIImageView *_emoticonBkgImgView;
UIImageView *_loadingImgView;
UIImageView *_nicknameBkgView;
UIImageView *_descTitleBkgView;
}
- (void)OnOpenRedEnvelopes; // 抢红包的响应事件
cycript验证一下:
cy# choose(WCRedEnvelopesReceiveHomeView)
[#"<WCRedEnvelopesReceiveHomeView: 0x11f9d70a0; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x282b39f00>>"]
cy# [#0x11f9d70a0 OnOpenRedEnvelopes]
// 到这里红包被抢了,那么在后面继续分析OnOpenRedEnvelopes的逻辑就可以了。
一般的正向开发中的MVC设计模式都是Model(数据模型)、View(页面)、Controller(控制器),C来控制页面和数据模型的交互,组织和显示数据。但是微信里面并没有完全按照MVC来,所以在分析的时候并不一定要按照MVC模式来分析。
4. 微信APP页面结构
在app中我们会使用下面的currentVC方法来获取当前的ViewController,实际上这个方法也就是判断了下UIApplication实例下的keyWindow下的rootViewController下的visiableViewController,这个方法是给NavigationController内部套tabBarController用的,尤其是在分析微信的时候并不有效。所以在分析时候我暂时用的是UIApp.keyWindow.rootViewController
方式。
function currentVC() {
var app = [UIApplication sharedApplication]
var keyWindow = app.keyWindow
var rootController = keyWindow.rootViewController
var visibleController = rootController.visibleViewController
if (!visibleController) {
return rootController
}
return visibleController.childViewControllers[0]
}
// 先看下rootViewController
cy# UIApp.keyWindow.rootViewController
#"<MainTabBarViewController: 0x11e14e200>"
// 再看下rootViewController下有什么
#"<MainTabBarViewController: 0x11e14e200>"
cy# [#0x11e14e200 _ivarDescription].toString()
`<MainTabBarViewController: 0x11e14e200>:
in MainTabBarViewController:
\tm_whatHasInit (unsigned int): 30
\tm_mainFrameViewController (NewMainFrameViewController*): <NewMainFrameViewController: 0x11e185000>
\tm_mainFrameTabItem (MMTabbarItem*): <MMTabbarItem: 0x11dea5910>
\tm_contactsViewController (ContactsViewController*): <ContactsViewController: 0x11e2e8e00>
\tm_contacsTabItem (MMTabbarItem*): <MMTabbarItem: 0x11dea9590>
\tm_findFriendEntryViewController (FindFriendEntryViewController*): <FindFriendEntryViewController: 0x11e313200>
\tm_findFrientTabItem (MMTabbarItem*): <MMTabbarItem: 0x11dea9bd0>
\tm_moreViewController (MoreViewController*): <MoreViewController: 0x11e919600>
\tm_moreTabItem (MMTabbarItem*): <MMTabbarItem: 0x11deab7c0>
\tm_arrViewController (NSMutableArray*): <__NSArrayM: 0x2825b4270>
\thaveLazyInit (BOOL): YES
\t_mainDelegate (<MainTabBarControllerDelegate>*): <CAppViewControllerManager: 0x2801a7480>
...
...
in UIResponder:
\t_responderFlags (struct ?): {
\t\thasOverrideClient (b1): NO
\t\thasOverrideHost (b1): NO
\t\thasInputAssistantItem (b1): NO
\t\tsuppressSoftwareKeyboard (b1): NO
\t}
in NSObject:
\tisa (Class): MainTabBarViewController (isa, 0x5a10bad755d)`
//只看不为nil的值就行了
//从页面上分析应该有4个navbar \t_childViewControllers (NSMutableArray*): <__NSArrayM: 0x2825b4300>
//接下来看这个,一般都是tabbarController里面套NavigationController
cy# #0x2825b4300
@[#"<MMUINavigationController: 0x11e147800>",#"<MMUINavigationController: 0x11e30c000>",#"<MMUINavigationController: 0x11e315a00>",#"<MMUINavigationController: 0x11e9a5600>"]
cy# #0x2825b4300[0]
#"<MMUINavigationController: 0x11e147800>"
//先看第一个NavigationController,
cy# #0x11e147800.visibleViewController
#"<WCRedEnvelopesRedEnvelopesDetailViewController: 0x11e9a9600>"
// 第二个
cy# #0x11e30c000.visibleViewController
#"<ContactsViewController: 0x11e2e8e00>"
// 第三个
cy# #0x11e315a00.visibleViewController
#"<FindFriendEntryViewController: 0x11e313200>"
// 第四个
cy# #0x11e9a5600.visibleViewController
#"<MoreViewController: 0x11e919600>"
// 至此,就分析了整体的页面结构。
5.如何自动抢红包?
这里暂时没有去分析,因为时间不够了,先讲讲代码。在Lookin里面看到openRedEnvelopesButton
是在WCRedEnvelopesReceiveHomeView
中,看看开源的代码:
%hook CMessageMgr
- (void)AsyncOnAddMsg:(NSString *)msg MsgWrap:(CMessageWrap *)wrap {
%orig;
switch(wrap.m_uiMessageType) {
case 49: { // AppNode
/** 是否为红包消息 */
BOOL (^isRedEnvelopMessage)() = ^BOOL() {
return [wrap.m_nsContent rangeOfString:@"wxpay://"].location != NSNotFound;
};
if (isRedEnvelopMessage()) { // 红包
CContactMgr *contactManager = [[%c(MMServiceCenter) defaultCenter] getService:[%c(CContactMgr) class]];
CContact *selfContact = [contactManager getSelfContact];
BOOL (^isSender)() = ^BOOL() {
return [wrap.m_nsFromUsr isEqualToString:selfContact.m_nsUsrName];
};
/** 是否别人在群聊中发消息 */
BOOL (^isGroupReceiver)() = ^BOOL() {
return [wrap.m_nsFromUsr rangeOfString:@"@chatroom"].location != NSNotFound;
};
/** 是否自己在群聊中发消息 */
BOOL (^isGroupSender)() = ^BOOL() {
return isSender() && [wrap.m_nsToUsr rangeOfString:@"chatroom"].location != NSNotFound;
};
/** 是否抢自己发的红包 */
BOOL (^isReceiveSelfRedEnvelop)() = ^BOOL() {
return [WBRedEnvelopConfig sharedConfig].receiveSelfRedEnvelop;
};
/** 是否在黑名单中 */
BOOL (^isGroupInBlackList)() = ^BOOL() {
return [[WBRedEnvelopConfig sharedConfig].blackList containsObject:wrap.m_nsFromUsr];
};
/** 是否自动抢红包 */
BOOL (^shouldReceiveRedEnvelop)() = ^BOOL() {
if (![WBRedEnvelopConfig sharedConfig].autoReceiveEnable) { return NO; }
if (isGroupInBlackList()) { return NO; }
return isGroupReceiver() || (isGroupSender() && isReceiveSelfRedEnvelop());
};
NSDictionary *(^parseNativeUrl)(NSString *nativeUrl) = ^(NSString *nativeUrl) {
nativeUrl = [nativeUrl substringFromIndex:[@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length]];
return [%c(WCBizUtil) dictionaryWithDecodedComponets:nativeUrl separator:@"&"];
};
/** 获取服务端验证参数 */
void (^queryRedEnvelopesReqeust)(NSDictionary *nativeUrlDict) = ^(NSDictionary *nativeUrlDict) {
NSMutableDictionary *params = [@{} mutableCopy];
params[@"agreeDuty"] = @"0";
params[@"channelId"] = [nativeUrlDict stringForKey:@"channelid"];
params[@"inWay"] = @"0";
params[@"msgType"] = [nativeUrlDict stringForKey:@"msgtype"];
params[@"nativeUrl"] = [[wrap m_oWCPayInfoItem] m_c2cNativeUrl];
params[@"sendId"] = [nativeUrlDict stringForKey:@"sendid"];
WCRedEnvelopesLogicMgr *logicMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:[objc_getClass("WCRedEnvelopesLogicMgr") class]];
[logicMgr ReceiverQueryRedEnvelopesRequest:params];
};
/** 储存参数 */
void (^enqueueParam)(NSDictionary *nativeUrlDict) = ^(NSDictionary *nativeUrlDict) {
WeChatRedEnvelopParam *mgrParams = [[WeChatRedEnvelopParam alloc] init];
mgrParams.msgType = [nativeUrlDict stringForKey:@"msgtype"];
mgrParams.sendId = [nativeUrlDict stringForKey:@"sendid"];
mgrParams.channelId = [nativeUrlDict stringForKey:@"channelid"];
mgrParams.nickName = [selfContact getContactDisplayName];
mgrParams.headImg = [selfContact m_nsHeadImgUrl];
mgrParams.nativeUrl = [[wrap m_oWCPayInfoItem] m_c2cNativeUrl];
mgrParams.sessionUserName = isGroupSender() ? wrap.m_nsToUsr : wrap.m_nsFromUsr;
mgrParams.sign = [nativeUrlDict stringForKey:@"sign"];
mgrParams.isGroupSender = isGroupSender();
[[WBRedEnvelopParamQueue sharedQueue] enqueue:mgrParams];
};
if (shouldReceiveRedEnvelop()) {
NSString *nativeUrl = [[wrap m_oWCPayInfoItem] m_c2cNativeUrl];
NSDictionary *nativeUrlDict = parseNativeUrl(nativeUrl);
queryRedEnvelopesReqeust(nativeUrlDict);
enqueueParam(nativeUrlDict);
}
}
break;
}
default:
break;
}
}
上面的代码测试后发现并不能用,暂时还没修复。写完这个文章再看看修复下。整体思路就是tweak消息接收方法判断是否为红包消息,如果是红包消息就直接自动接收就行了。后面修复后再发文章。
6.如何锁屏下抢红包
在ios系统中,如果应用退到后台,在一定的时间会被杀掉,后面只能走apns的推送下发消息了。锁屏下抢红包不用考虑这个事情的原因,是因为微信自己应该就有保活机制,所以在tweak里面可以不用处理保活,直接处理tweak消息方法就行了。