关闭朋友圈有一年多了,突然有一天微信的策略变了,在关闭朋友圈的同时也不让别人查看自己的朋友圈了。有妹子表示看不到我朋友圈很不爽,于是我决定对微信进行一番改造!初步实现效果:
手机无需越狱,项目 GitHub 地址: ,Make WeChat Great Again!
因为没有越狱手机,所以不是直接写 tweak 放手机里,而是需要将
工程编译出的 dylib 注入到已砸壳 app 的二进制文件中。同样因为没有越狱机,所以砸壳的文件只能从 某 P 助手下载了。
- CaptainHook
我写了一个 Shell 脚本 帮我完成一些重复性的任务:
段中加入一条加载 dylib 的指令
- Load Commands
需要传入的三个参数分别为:已砸壳的 ipa 文件,没过期的 mobileprovision 文件,要注入的 dylib 文件。
犹豫了很久,还是贴上脚本代码,壮气势充篇幅吧。
- # ! /bin/bash SOURCEIPA = "$1"MOBILEPROV = "$2"DYLIB = "$3"cd $ {
- SOURCEIPA %
- /*}
- security find-identity -v -p codesigning > cers.txt
- while IFS='' read -r line || [[ -n "$line" ]]; do
- if [[ "$line" =~ "iPhone Developer" ]]; then
- DEVELOPER=${line:47:${#line}-48}
- fi
- done < cers.txt
- unzip -qo "$SOURCEIPA" -d extracted
- APPLICATION=$(ls extracted/Payload/)
- echo "Copying dylib and mobileprovision"
- cp "$DYLIB" "extracted/Payload/$APPLICATION/${DYLIB##*/
- }
- "
- cp "$MOBILEPROV " "extracted / Payload / $APPLICATION / embedded.mobileprovision "
- echo "Insert dylib into Mach - O file "
- yololib "extracted / Payload / $APPLICATION / $ {
- APPLICATION % . *
- }
- " "$ {
- DYLIB## * /}"
- echo "Resigning with certificate: $DEVELOPER"
- find -d extracted \( -name "*.app" -o -name "*.appex" -o -name "*.framework" -o -name "*.dylib" \) > directories.txt
- security cms -D -i "extracted/Payload / $APPLICATION / embedded.mobileprovision " > t_entitlements_full.plist
- /usr/libexec/PlistBuddy -x -c 'Print:Entitlements' t_entitlements_full.plist > t_entitlements.plist
- while IFS='' read -r line || [[ -n "$line " ]]; do
- /usr/bin/codesign --continue -f -s "$DEVELOPER " --entitlements "t_entitlements.plist " "$line "
- done < directories.txt
- echo "Creating the Signed IPA "
- cd extracted
- zip -qry ../extracted.ipa *
- cd ..
- rm -rf "extracted "
- rm directories.txt
- rm cers.txt
- rm t_entitlements.plist
- rm t_entitlements_full.plist
- echo "Installing APP to your iOS Device "
- mobiledevice install_app extracted.ipa
- "
想要让 一气呵成执行下去,需要依赖以下几项:
工具用于注入 dylib 文件到二进制文件中
- yololib
可以将 ipa 安装到 USB 连接到 Mac 上的手机中
- mobiledevice
在这里多再说几句:
很多次,一直失败,就当前辈们劝我还是用
- iOSOpenDev
稳妥的时候,我觉得还是再试一次吧,果然还是失败了。不过新建 Xcode 项目选择 template 时却出现了
- theos
哈哈哈哈!
- iOSOpenDev
注入 dylib 后 crash,后来用
- insert_dylib
就好了!不过
- yololib
有个 bug 是对 dylib 的版本号有要求。这里可以直接改源码,把
- yololib
和
- DYLIB_CURRENT_VER
的宏定义都改成
- DYLIB_COMPATIBILITY_VERSION
。懒人直接用我上传的 。
- 0x0000
打印所有的
- otool -l
,建议搭配
- Load Commands
进行正则过滤。
- grep
可以查看使用的库文件。
- otool -L
来完成一些调试工作,这样就不用一次次打 Log 了。同样也可以打印出视图层级,不过建议有条件的同学用 Reveal 2,已经支持 USB 调试了。
- Cycript
只支持在同网段下连接到手机 IP 的
- Cycript
端口,cy 脚本还是跟
- 8888
命令有一些差别的。如果
- lldb
官网的 sdk 不好用,那就用用我上传的吧:
- Cycript
得到的头文件寻找蛛丝马迹了。Dump 出的文件:
- class-dump
我曾经在『 』这篇文章中说过:
之前看的一些逆向的教程里,感觉前期工作都是装软件配环境,噼里啪啦命令一顿敲,整的挺玄乎,其实都是用人家现成儿的工具做些事情,美其名曰『站在巨人的肩膀上』,这里不再赘述。在我看来第一个真正意义上有难度的事情就是一个字儿:『猜』!
是啊,头文件有了,UI 层级有了,该猜了!那么检验是否猜对需要做啥?Hook 呗!
的用法很简单,新建工程的模板注释里面已经写得很详细了,就不赘述了。
- CaptainHook
在关掉各种乱码七糟的功能之后,发现页面仍留下几个无法关闭的入口。本次逆向微信的动机也由此引发:我只想关闭朋友圈入口,并没想关闭自己朋友圈内容,不过微信的这项策略也是很符合一些人的需求的。很多人真的想关闭自己朋友圈不让别人看,不过将这个需求跟旧的『关闭朋友圈入口』功能强绑定在一起,就有些绑架用户的味道了,鱼和熊掌不可兼得啊!不过关闭朋友圈后,别人依然能看到自己在 TimeLine 上新发的内容,但是一旦点击头像进入主页后就提示『该朋友暂未开启朋友圈』,奇怪的是回到自己的 TimeLine 上后,以前那条新发的内容就消失了。我觉得这不是 bug,而是产品策略。微信在努力保持用户粘性,不得不在用户需求和产品数据之间权衡。好吧,扯远了。。。
我只保留了这俩『活儿好不粘人』的工具类入口:
其实扫一扫页面可以通过右上角加号更快进入,也可以去掉。小程序其实平时也基本不用,偶尔用的时候现搜,鸡肋入口。不能再干掉了,否则还不如索性干掉整个发现页面。
删入口有两种思路,一种是删数据源,另一种是 hook
和
- UITableViewDelegate
。发现页面的 VC 是
- UITableViewDataSource
,发现数据源数组包含的结构体需要花功夫猜下含义,索性简单粗暴 Plan B。
- FindFriendEntryViewController
- // 关闭朋友圈入口
- CHOptimizedMethod2(self, CGFloat, FindFriendEntryViewController, tableView, UITableView *, tableView, heightForRowAtIndexPath, NSIndexPath *, indexPath)
- {
- NSIndexPath *timelineIndexPath = [self valueForKeyPath:@"m_WCTimeLineIndexPath"];
- if ([indexPath isEqual: timelineIndexPath] || indexPath.section == 2) {
- NSLog(@"## Hide Time Line Entry ##");
- return 0;
- }
- return CHSuper2(FindFriendEntryViewController, tableView, tableView, heightForRowAtIndexPath, indexPath);
- }
- CHOptimizedMethod2(self, UITableViewCell *, FindFriendEntryViewController, tableView, UITableView *, tableView, cellForRowAtIndexPath, NSIndexPath *, indexPath)
- {
- NSIndexPath *timelineIndexPath = [self valueForKeyPath:@"m_WCTimeLineIndexPath"];
- UITableViewCell *cell = CHSuper2(FindFriendEntryViewController, tableView, tableView, cellForRowAtIndexPath, indexPath);
- if ([indexPath isEqual: timelineIndexPath] || indexPath.section == 2) {
- NSLog(@"## Hide Time Line Entry ##");
- cell.hidden = YES;
- for (UIView *subview in cell.subviews) {
- [subview removeFromSuperview];
- }
- }
- return cell;
- }
简单粗暴地将想要隐藏的入口 Cell 高度设为
后发现
- 0
被挤出来了,我日,只好再干掉这些
- subview
。最后记得在页面出现时刷新下 table 数据:
- subview
- CHOptimizedMethod1(self, void, FindFriendEntryViewController, viewDidAppear, BOOL, animated)
- {
- CHSuper1(FindFriendEntryViewController, viewDidAppear, animated);
- [self performSelector:@selector(reloadData)];
- }
修改微信运动步数的方法网上一搜就有好多文章,就是 hook
的
- WCDeviceStepObject
方法罢了。我在这里为了更方便地装逼,当然不能 hook 时把步数写死了,随机数也不够屌,要装逼就装到位:
- m7StepCount
先到设置页面:
在文本框输入个正数:
完美:
微信的一些列表页面是由数据来驱动 UI 的。table 对应
,section 对应
- MMTableViewInfo
,cell 对应
- MMTableViewSectionInfo
。以前做项目时也见到过类似的框架,理解起来不难。但是这种过度的封装完全改变了原有系统 API,使用者碰到问题需要深入到框架去调试,又因为是内部框架,网上也搜不到方案。所以要求框架作者规范的编码习惯和较强的能力。又扯远了,我是用
- MMTableViewCellInfo
这个单例类来保存状态值的,目前还没在持久层写入磁盘。可以在
- FishConfigurationCenter
头文件看到微信中常用的 cell 是封装好的,这里直接获取个带文本框的就行了。我顺便还加了个夜间模式的开关 cell:
- MMTableViewCellInfo
- CHDeclareMethod0(void, NewSettingViewController, reloadTableData)
- {
- CHSuper0(NewSettingViewController, reloadTableData);
- MMTableViewInfo *tableInfo = [self valueForKeyPath:@"m_tableViewInfo"];
- MMTableViewSectionInfo *sectionInfo = [objc_getClass("MMTableViewSectionInfo") sectionInfoDefaut];
- MMTableViewCellInfo *nightCellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(handleNightMode:) target:[FishConfigurationCenter sharedInstance] title:@"夜间模式" on:[FishConfigurationCenter sharedInstance].isNightMode];
- [sectionInfo addCell:nightCellInfo];
- MMTableViewCellInfo *stepcountCellInfo = [objc_getClass("MMTableViewCellInfo") editorCellForSel:@selector(handleStepCount:) target:[FishConfigurationCenter sharedInstance] tip:@"请输入步数" focus:NO text:[NSString stringWithFormat:@"%ld", (long)[FishConfigurationCenter sharedInstance].stepCount]];
- [sectionInfo addCell:stepcountCellInfo];
- [tableInfo insertSection:sectionInfo At:0];
- MMTableView *tableView = [tableInfo getTableView];
- [tableView reloadData];
- }
然后获取步数的时候从单例里取值就可以啦:
- // 微信运动步数
- CHOptimizedMethod0(self, unsigned int, WCDeviceStepObject, m7StepCount)
- {
- if ([FishConfigurationCenter sharedInstance].stepCount == 0) {
- [FishConfigurationCenter sharedInstance].stepCount = CHSuper0(WCDeviceStepObject, m7StepCount);
- }
- return [FishConfigurationCenter sharedInstance].stepCount;
- }
微信真的是越来越臃肿,大有追赶 QQ 的架势,连小红点也是越来越多。『发现』页面撸的挺干净了,我就不信扫一扫入口还能有小红点(flag 已立)。『我』Tab 页里什么钱包啊卡包啊老有小红点,真烦人,老得点进去。
通过查看视图层级发现小红点来源有两种,一种是 TabBar 上的小红点,另一种是 cell 上的小红点。前者是系统 API 带的,后者是微信的
类实现的。
- MMBadgeView
微信的
继承于
- MMTabBarController
,它提供了几个设置小红点的快捷方法,统统 hook 掉,屏蔽后两个『发现』和『我』上的小红点:
- UITabBarController
- CHOptimizedMethod2(self, void, MMTabBarController, setTabBarBadgeImage, id, arg1, forIndex, unsigned int, arg2)
- {
- if (arg2 != 2 && arg2 != 3) {
- CHSuper2(MMTabBarController, setTabBarBadgeImage, arg1, forIndex, arg2);
- }
- }
- CHOptimizedMethod2(self, void, MMTabBarController, setTabBarBadgeString, id, arg1, forIndex, unsigned int, arg2)
- {
- if (arg2 != 2 && arg2 != 3) {
- CHSuper2(MMTabBarController, setTabBarBadgeString, arg1, forIndex, arg2);
- }
- }
- CHOptimizedMethod2(self, void, MMTabBarController, setTabBarBadgeValue, id, arg1, forIndex, unsigned int, arg2)
- {
- if (arg2 != 2 && arg2 != 3) {
- CHSuper2(MMTabBarController, setTabBarBadgeValue, arg1, forIndex, arg2);
- }
- }
去除
就更简单了,直接隐藏掉就好了。不直接 remove 的好处是可以保留聊天页面的小红点提醒,而其他页面的小红点被隐藏了。我猜原因是聊天页面的小红点在添加上去后会设置下
- MMBadgeView
,因为 cell 是重用的。
- hidden = NO
- CHOptimizedMethod1(self, void, UIView, didAddSubview, UIView *, subview)
- {
- if ([subview isKindOfClass:NSClassFromString(@"MMBadgeView")]) {
- subview.hidden = YES;
- }
- }
她说睡了,其实是躺在被窝里继续玩手机罢了。
夜间模式其实也就是主题适配,这个手机 QQ 玩的是最 6 的了,无人能敌。要想做一个完美的皮肤引擎是很庞大的工作,不仅是多套色值方案的存储和切换问题,还有多套图片资源的适配问题。这里由于时间仓促,只做了个很辣眼睛的夜间模式,而且切换回来需要杀进程重新进:
这么辣眼睛的审美会被狂吐槽,就不贴代码了,有兴趣的去项目里查看哈哈。
有时候被撤回的消息看到了会后悔的,但这依然阻止不了我的好奇心 + 强迫症。
在 『 』 里我介绍过用 Hopper 逆向的方法。直接看汇编代码来的不那么直接,还是 hook OC 代码稳一些。
- // 阻止撤回消息
- CHOptimizedMethod1(self, void, CMessageMgr, onRevokeMsg, id, msg)
- {
- NSLog(@"onRevokeMsg: %@", msg);
- return;
- }
若不是时间匆忙,或许还可以让微信变得更伟大。比如加个『彻底屏蔽群消息』入口,或者加个『彻底清理缓存』按钮。平时使用微信确实有很多不爽的地方,尤其是群功能太弱太弱了。我还想加个功能就是如果对方发了超过 30s 的语音,并且对方不是妹子也不是老板不是亲戚,此时自动回复 #&*DF@$@(M!….. 我没太听清,请你重新再发一遍?
此项目仅用于逆向工程交流学习,黑产死开!
来源: http://www.tuicool.com/articles/nEb6BnE