除夕打开微信摇一摇抢红包,红包雨还没开始时,会显示如下界面:
底部圆形的logo在不停地跳动,点击一下,居然领到了一分钱:
如果不断摇一摇再点击logo,就可以领到很多一分钱。不过手动操作太麻烦了,所以写了个tweak
插件来自动领取。
插件要做的操作大概如下:
1. 在摇一摇界面,插件自动调用摇一摇的方法。
2. 进入红包详情界面后,插件自动点击图标领钱。
3. 领完钱后,插件自动退出红包详情界面,回到摇一摇界面。
4. 重复步骤1。
当前的微信版本是6.3.10,iOS设备是越狱后的iPad mini。
这个插件的功能算是很简单的,下面就说一下编写插件的过程:
一、获取微信的头文件
使用class-dump可以获取微信的头文件。
解压微信.ipa
,将Payload/WeChat.app/WeChat
这个可执行文件拷贝出来,然后在终端执行命令:
class-dump -sSH WeChat -o WeChatHeaders
在生成的WeChatHeaders
文件夹里可以看到微信的所有类的头文件:
.
├── ABNewPersonViewControllerDelegate-Protocol.h
├── ABPeoplePickerNavigationControllerDelegate-Protocol.h
├── ABTestItem.h
├── ABtestCase.h
├── ABtestMgr.h
├── ABtestPoint.h
├── ABtestPointPeriod.h
......
二、自动摇一摇
首先进入摇一摇界面:
使用Reveal查看视图控制器的类名:
然后在微信头文件里找到NewYearShakeViewController.h
,搜索关键字shake
,可以搜到一个方法:- (void)OnShake;
。
用电脑打开终端远程连接到iPad,再用Cycript附加微信,调用OnShake
方法:
Jobs: ~$ ssh root@remoteip
Jobss-iPad:~ root# cycript -p WeChat
cy# [#0x1457f95b0 OnShake]
执行命令后界面果然自动摇一摇了,并且跳到了红包详情界面。
三、自动领RMB
在红包详情界面,用Reveal可以看到logo是一个按钮:
在Cycript中使用以下方法可以获取点击按钮时调用的方法:
cy# button = #0x1468358f0
#"<ImagesAnimationButton: 0x1468358f0; baseClass = UIButton; frame = (364 0; 40 40); opaque = NO; layer = <CALayer: 0x174439640>>"
cy# button.allTargets()
[NSSet setWithArray:@[#"<NewYearShakeInteractiveLogoView: 0x1468c6d50; frame = (0 930; 768 94); layer = <CALayer: 0x174622e40>>"]]]
cy# target = #0x1468c6d50
#"<NewYearShakeInteractiveLogoView: 0x1468c6d50; frame = (0 930; 768 94); layer = <CALayer: 0x174622e40>>"
cy# [button actionsForTarget:target forControlEvent:UIControlEventTouchUpInside]
@["onClickLogoButton:"]
可以看到,点击按钮时会调用[NewYearShakeInteractiveLogoView onClickLogoButton:]
方法。
可以在NewYearShakeInteractiveLogoView
初始化后调用点击按钮的方法,就可以实现自动领取了。
这个方法需要传入按钮对象作为参数,在NewYearShakeInteractiveLogoView.h
里可以看到按钮是一个成员变量ImagesAnimationButton *m_logoView;
,所以可以通过[self valueForKey:@"m_logoView"]
方法获取按钮对象。
四、自动退出红包详情界面
领到钱后需要退出当前界面,才能继续重新摇一摇。
而点击按钮时会出现金币旋转的动画,然后请求网络领取RMB,领到RMB后再结束动画,也就是说动画执行的时间可能不是固定的。
如果能找到结束动画的方法的话,通过hook这个方法,就可以在动画结束后退出当前界面了。
用Hopper Disassembler
反编译微信的可执行程序,搜索NewYearShakeInteractiveLogoView onClickLogoButton
,按 alt + enter 生成伪代码:
void -[NewYearShakeInteractiveLogoView onClickLogoButton:](void * self, void * _cmd, void * arg2) {
r2 = arg2;
r0 = self;
r1 = @selector(onClickLogoEvent);
r0 = loc_1c0d0d4(r0, r1, r2);
return;
}
可以看到调用了onClickLogoEvent
方法,查看该方法的伪代码:
void -[NewYearShakeInteractiveLogoView onClickLogoEvent](void * self, void * _cmd) {
r7 = &arg_4;
r4 = self;
r1 = r4->m_hasClickLogo;
asm{ it ne };
if (r1 != 0x0) {
return;
}
r0 = *objc_ivar_offset_NewYearShakeInteractiveLogoView_m_hasClickLogo;
*(int8_t *)(r4 + r0) = 0x1;
r0 = *objc_ivar_offset_NewYearShakeInteractiveLogoView_m_logoView;
r0 = *(r4 + r0);
loc_e0a05c(r0, @selector(setEnabled:), 0x0, 0x1);
loc_e0a05c(r4, @selector(setEmptyTipText));
loc_e0a05c(r4, @selector(stopShowAnimation));
loc_e0a05c(r4, @selector(startClickAnimation));
r1 = @selector(callClickLogoHongBao);
r0 = r4;
Pop();
Pop();
Pop();
r0 = loc_1c0d0d4(r0, r1, 0x0);
return;
}
伪代码里调用了startClickAnimation
方法开始执行动画,那么很可能存在一个停止执行动画的方法。
在NewYearShakeInteractiveLogoView.h
里搜索stop
,可以发现- (void)stopClickAnimation;
方法,基本上就可以确定这是停止动画时调用的方法了。
如果要在view的stopClickAnimation
方法里关闭红包界面的话,就需要通过view获取view所在的视图控制器,可以通过nextResponder
方法来获取。
最后让视图控制器对象调用dismissViewControllerAnimated:completion:
方法来退出红包界面。
五、编写插件
使用Theos新建一个tweak插件,代码非常简单:
@interface NewYearShakeViewController : UIViewController
- (void)viewDidAppear:(BOOL)animated;
- (void)OnShake;
@end
@interface NewYearShakeInteractiveLogoView : UIView
- (void)initView;
- (void)onClickLogoButton;
- (void)stopClickAnimation;
@end
%hook NewYearShakeViewController
- (void)viewDidAppear:(BOOL)animated
{
%orig;
//显示界面一秒后自动摇一摇
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self OnShake];
});
}
%end
%hook NewYearShakeInteractiveLogoView
- (void)initView
{
%orig;
//view初始化一秒后自动点击按钮
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIButton *button = [self valueForKey:@"m_logoView"];
[self onClickLogoButton:button];
});
}
- (void)stopClickAnimation
{
%orig;
//动画停止一秒后自动退出界面
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIViewController *noticeViewController = (UIViewController *)self.nextResponder.nextResponder.nextResponder;
[noticeViewController dismissViewControllerAnimated:NO completion:nil];
});
}
%end
iPad安装插件后,重新打开微信,微信就会自动领一分钱了:
最后领到了很多一分钱,不知不觉中已经发财了:
2016/05/24 20:05:10
哇哇,做Android的我对这个技术很感兴趣啊
2016/05/28 21:59:03
恭喜你成功入坑
2016/03/04 20:35:32
您长得一定很帅!
2016/03/04 21:27:22
没想到这都被你发现了
2016/03/04 18:24:56
大神一看你就知道你是自学成才的料。你平时是怎么自学的呢?有没有什么逆向编程(说白就是破解别人软件)的书籍介绍?

2016/03/04 19:35:03
你可以买一本《iOS应用逆向工程》第2版,我就是看这本书入门的。
这本书的官方论坛是http://bbs.iosre.com,里面有很多逆向的教程。
2016/03/04 17:10:46
果真大神级人物


有个人问题想请教大神。为什么我的class-tump 只能提取 .app 的头文件。 然后我在itunes 下一个 .ipa 的文件。提取出来报错。class-dump: Input file (/Users/aaa/Desktop/cepin.ipa) is neither a Mach-O file nor a fat archive.
那我怎么弄到微信的app后缀名文件?
还有你这篇文章里的 class-dump -sSH WeChat -o WeChatHeaders 。这个WeChat 值的是.app 还是.ipa 呢?
2016/03/04 17:39:11
不敢当,文章没有说详细,`WeChat`是`WeChat.ipa/Payload/WeChat.app/WeChat`这个可执行文件,你可以把这个文件复制出来
2016/03/04 18:07:36
骚年生不逢时啊。

网上有些资料说不能直接用class-tump提取头文件的。好像还要用一些软件”去壳”。你那边不用吗?不可能那么容易就可以拿到.h 文件的吧。
2016/03/04 19:28:52
确实是这样,在Appstore下载的app是加壳的,需要先脱壳才能class-dump。
手动脱壳的话可以参考 http://bbs.iosre.com/t/dumpdecrypted-app/22。
也可以在第三方应用市场里下载越狱版的app,都已经脱过壳了。