前言
每次打开微信,总有一些公众号推送烦人的且无营养消息,而微信又没提供全部已阅的功能,本章详细讲述一键阅读所有消息.
- 安装一个砸壳的微信,或者自己去App Store下载一个再砸壳.
- 恢复微信符号表 iOS符号表恢复
- 给微信添加调试权限.
- 分析.
- Tweak.xm
- 总结.
0x1:
0x2:
Reveal分析:
- 小红点是个封装的图片控件 MMBadgeView
- 红点中的数字是个封装了UILabel控件 MMUILabel
XCode加载微信进程:
从MMBadgeView控件入手:
这里选择直接从MMBadgeView入手,po [MMBadgeView _shortMethodDescription] 命令,打印出MMBadgeView的所有属性和方法,发现三个方法格外显眼.
Symbolic BreakPoint下断:
放开断点后,小号发送条信息给这个微信,看看断点是否被断下.
微信在收到消息之后经过非常多的页面UI处理,然后再设置小红点的数量。这个时候往上回溯发现除了系统的渲染函数,第一个被调用的微信函数是[MMTableView layoutSubviews]
,这个函数是微信首页这个表格加载子视图的函数,我们所看到的首页上的任何东西都是这个函数加载出来的,那么在这之前发生了什么?如何收到的消息?怎么计算的未读消息数量?
继续下一步分析:
现在程序停留在了UITableView这个控件的一系列渲染和布局函数上面,如果继续盯着这块分析,最后会发现在做无用功.
理论上是,当微信收到消息后,经过一系列处理,最后通知微信的首页,刷新这个表格(UITableView),给对应栏目(Cell)上的小红点+1.
至于为什么有那么多UI渲染函数是因为微信收到消息后,如果是一个没有在首页列表的朋友发过来的呢?微信会发出通知声音,然后加入一个新的栏目(Cell)并置顶.
跳出这个UITableView控件的渲染层,转到这个UITableView的父级,也就是首页的控制器
NewMainFrameViewController
,在前面Reveal分析中已经得到了这个控制器.
分析 NewMainFrameViewController:
po [NewMainFrameViewController _shortMethodDescription] 打印输出控制器.
在列举的方法中寻找
Undead
,这个关键字如何来的?在前面UITableView的视图渲染中就有这么个函数[MainFrameitemView updateUnreadCountView]
,简洁明了一看就知道是修改未读数量.-
记录所有和这个关键字有关的方法:
- (void) cleanAllUnreadSession;
-(void)setNewMainFrameTableViewContentOffsetToTheFirstUnreadSessionAndOpen:(BOOL)arg1;
- (unsigned int) getTotalUnreadCount;
- (void) onNextUnreadChat;
- (void) notifyTaskBarOnWillFocusOnFirstUnreadSession;
- (void) updateUnReadCount;
- (void) openMessageContentView:(id)arg1 startSendMessage:(BOOL)arg2 msgWrapToAdd:(id)arg3 animated:(BOOL)arg4 jumpToFirstUnreadNode:(BOOL)arg5 indexPath:(id)arg6 reuse:(BOOL)arg7 extraInfo:(id)arg8;
- (void) openMessageContentView:(id)arg1 startSendMessage:(BOOL)arg2 msgWrapToAdd:(id)arg3 animated:(BOOL)arg4 jumpToFirstUnreadNode:(BOOL)arg5 indexPath:(id)arg6;
从字面意思
- (void) cleanAllUnreadSession;
方法是取消所有未读会话,貌似是我们要的,实际上这是个坑,执行了这个方法,微信首页所有的消息都没了,包括栏目(Cell),再次打开微信,首页就是个白板.-
最后定位到
- (void) updateUnReadCount;
Symbolic BreakPoint下断
微信收到消息后断在- (void) updateUnReadCount;
,注意:如果这个时候已经在当前会话的聊天房间,并不会触发这个函数.
* 这个时候出现了一个新的类在堆栈调用过程中 MMNewSessionMgr
* 当微信收到消息,如果这个消息栏目(Cell)在首页列表中,小红点的数量会递增.
* 点击某个有小红点的栏目进去后,小红点会消失,说明当前栏目是已读的.
* 未读消息为0,表示当前栏目已读,小红点消失.
* 逻辑推测:收到消息,未读数量递增. 进入房间未读数置0,隐藏小红点.说明都会改变消息未读数
-
查看进入房间的堆栈调用:
进入单间,表示消息已读,调用了MMNewSessionMgr
类的三个函数。分别给三个函数下断,查看参数的值.
* MMNewSessionMgr
类 - 函数:
-
$arg3
表示第一个参数$agr4
表示第二个po
printObject
通过三个函数调用,然后调用微信首页控制器的- (void) updateUnReadCount
方法,会清除小红点。 -
[MMNewSessionMgr OnUnReadCountChange:]
的参数是一串字符串
-
[MMNewSessionMgr ChangeSessionUnReadCount:to:]
的两个参数分别是 上一个函数的参数 和 一个 0|假 . - 返回到首页,刚刚进去过的栏目小红点消失,其余的还是存在。
- 再进入一个别的有未读消息的房间,
[MMNewSessionMgr OnUnReadCountChange:]
又会不同,说明这个参数很可能是一个ID,代表首页的每一个栏目(Cell). - 羊毛出在羊身上,打印一波
MMNewSessionMgr
类:
-
尝试调用一下那些不需要参数就有返回值的函数:
- 至此能拿到的东西已经很多 - 整理一下逻辑就能完成一键阅读
Tweak.xm
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
%hook MMServiceCenter
%end
%hook MMNewSessionMgr
%end
%hook MMSessionInfo
%end
%hook NewMainFrameViewController
- (void) viewDidAppear:(BOOL)arg1{
%log;
%orig;
//微信首页左上角添加一个功能按钮
UIBarButtonItem *barBtn = [[UIBarButtonItem alloc]initWithTitle:@"" style:UIBarButtonItemStylePlain target:self action:@selector(cleanAllUnreadSession)];
[((UIViewController *)self).navigationItem setLeftBarButtonItem:barBtn];
}
%new
- (void)cleanAllUnreadSession{
//获取 MMNewSessionMgr 实例
MMNewSessionMgr *sessionObj = [[%c(MMServiceCenter) defaultCenter] getService:[%c(MMNewSessionMgr) class]];
//获取所有的 sessionID
NSArray *SessionListArray = [sessionObj GetUserNamesOnSessionList];
for (NSString *str in SessionListArray) {
// 循环修改每个会话 改成已读
[sessionObj ChangeSessionUnReadCount:str to:0];
}
}
%end
总结
实际上分析过程中会用到 Hopper Disassembler
静态分析.
实际上部分公众号推送的消息还是有价值的.
我没有任何针对微信的意思,如果你非要问我支持不支持微信,我当然支持,可以很明确的告诉你,我不支持我还天天用微信。🤣
最后附上效果图。