破壳
参考文章iOS逆向之博破壳
外观篇
参考文章初涉iOS逆向工程:免越狱修改微信(外观篇)
功能篇
本文是在以上两篇文章的基础上来分析如何逆向修改微信原生功能,包含以下3个小功能
- 微信消息防撤回
- 修改微信运动步数
- 掷骰子、石头剪刀布自定义
关于微信消息防撤回和修改微信步数网上也有很多的资料了,我也借鉴了一部分
原始代码参考于https://github.com/Abeautifulliar/WeChatHook,之后我自定义实现了掷骰子和剪刀石头布作弊功能
1.界面分析与准备
-
头文件分析
首先对破壳后拿到的头文件进行分析,根据文件类名、方法名、变量名等猜测其作用
文件名是如此之多,足足10000+,想要一个一个看文件名难免力不从心,这时就需要考验我们的技巧了,比如你想搜登录相关的逻辑可以搜索一些关键字进行过滤以找到你想要的文件,如搜索login
ViewController
manager
。 头文件内又有很多实例变量和方法申明,我们也可以很容易猜测出用途 -
UI界面分析
通过debug查看UI层级图可以很容易的知道当前UI界面所属的ViewController了
如图可以得知微信聊天界面所属的VC是BaseMsgContentViewController
同样的方法可以知道设置界面是NewSettingViewController
,通讯录界面是ContactsViewController
消息防撤回
网上很多资料也说了,微信的消息管理类是CMessageMgr
,一些关键方法如下
- (void)AsyncOnAddMsg:(id)arg1 MsgWrap:(id)arg2;
- (void)AsyncOnDelMsg:(id)arg1;
- (void)onRevokeMsg:(id)arg1;
根据猜测得知微信接收到撤回消息时会先调用 onRevokeMsg
方法,该方法会创建一条通知消息,即xxx撤回了一条消息
,然后会调用AsyncOnDelMsg
方法,该方法会将本地的消息删除,而我们要做的就是调用onRevokeMsg
方法,但是不调用AsyncOnDelMsg
方法,这样就达到了防撤回的目的
注:用户自己删除本地消息时也会调用
AsyncOnDelMsg
方法,所以为了不影响其它操作,我们应该只屏蔽掉因接受到撤回消息时调用AsyncOnDelMsg
关键代码如下
// 阻止撤回消息
CHOptimizedMethod1(self, void, CMessageMgr, onRevokeMsg, id, msg){
[WeChatManager manager].revokeMsg = YES;
CHSuper1(CMessageMgr, onRevokeMsg, msg);
}
CHDeclareMethod3(void, CMessageMgr, DelMsg, id, arg1, MsgList, id, arg2, DelAll, BOOL, arg3){
if ([WeChatManager manager].revokeMsg) {
[WeChatManager manager].revokeMsg = NO;
} else {
CHSuper3(CMessageMgr, DelMsg, arg1, MsgList, arg2, DelAll, arg3);
}
}
WeChatManager是自定义的类,用于记录是否收到了通知撤回消息和保存要修改的步数
修改步数
修改步数的关键类是WCDeviceStepObject
#import "MMObject.h"
@class NSMutableArray;
@interface WCDeviceStepObject : MMObject
{
unsigned int beginTime;
unsigned int endTime;
unsigned int m7StepCount;
unsigned int hkStepCount;
NSMutableArray *allHKSampleSource;
}
- (void).cxx_destruct;
@property(retain, nonatomic) NSMutableArray *allHKSampleSource; // @synthesize allHKSampleSource;
@property(nonatomic) unsigned int beginTime; // @synthesize beginTime;
@property(nonatomic) unsigned int endTime; // @synthesize endTime;
@property(nonatomic) unsigned int hkStepCount; // @synthesize hkStepCount;
@property(nonatomic) unsigned int m7StepCount; // @synthesize m7StepCount;
@end
只需Hook住m7StepCount方法即可
如下代码
CHOptimizedMethod0(self, unsigned int, WCDeviceStepObject, m7StepCount){
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
NSDate *today = [cal dateFromComponents:components];
components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[WeChatManager manager].lastChangeStepCountDate];
NSDate *otherDate = [cal dateFromComponents:components];
BOOL modifyToday = NO;
if([today isEqualToDate:otherDate]) modifyToday = YES;
if ([WeChatManager manager].stepCount < CHSuper0(WCDeviceStepObject, m7StepCount) || !modifyToday) {
[WeChatManager manager].stepCount = CHSuper0(WCDeviceStepObject, m7StepCount);
}
return [WeChatManager manager].stepCount;
}
掷骰子、剪刀石头布
有过掷骰子、剪刀石头布的都知道,掷骰子的结果其实是在发送方就决定了,而不是到服务端或者接收方才确定的结果,所以我们把发送消息类型拦截就行啦~
原理和防撤回消息类似,当发送Emoticon消息时会调用- (void)AddEmoticonMsg:(id)arg1 MsgWrap:(id)arg2;
,修改消息内容即可
代码如下
CHMethod(2, void, CMessageMgr, AddEmoticonMsg, id, arg1, MsgWrap, id, arg2) {
id msgType = [arg2 valueForKey:@"m_extendInfoWithMsgType"];
if (((NSNumber *)([msgType valueForKey:@"m_uiGameType"])).intValue == 2) {
[msgType setValue:[NSNumber numberWithInt:[WeChatManager manager].gameContent2] forKey:@"m_uiGameContent"];
[arg2 setValue:msgType forKey:@"m_extendInfoWithMsgType"];
}
if (((NSNumber *)([msgType valueForKey:@"m_uiGameType"])).intValue == 1) {
[msgType setValue:[NSNumber numberWithInt:[WeChatManager manager].gameContent1] forKey:@"m_uiGameContent"];
[arg2 setValue:msgType forKey:@"m_extendInfoWithMsgType"];
}
CHSuper(2, CMessageMgr, AddEmoticonMsg, arg1, MsgWrap, arg2);
}
注:
m_uiGameType
为1时表示剪刀石头布,为2时表示掷骰子,而当石头剪刀布时m_extendInfoWithMsgType
的值为1~3,分别代表剪刀(1),石头(2)
,布(3),当为掷骰子时m_extendInfoWithMsgType
的值分别为49,也就是掷骰子的结果(16)
其它
关于如何修改原生UI,拿设置界面举例,如果我们要添加一个自定义的设置选项,首先需要分析类NewSettingViewController
@class MMTableViewInfo, MMTipsViewController, NSString, WCAccountLogoutLogic, WCAccountSwitchLogic;
@interface NewSettingViewController : MMUIViewController <WCAccountSwitchLogicDelegate, WCAccountLogoutLogicDelegate, MMTipsViewControllerDelegate>
{
MMTableViewInfo *m_tableViewInfo;
_Bool m_bFromSetting;
WCAccountSwitchLogic *m_switchLogic;
WCAccountLogoutLogic *m_logoutLogic;
MMTipsViewController *m_introView;
}
@end
根据头文件可知,该VC的tableView被封装到了m_tableViewInfo
,类型为MMTableViewInfo
,该类提供了一个- (void)insertSection:(id)arg1 At:(unsigned int)arg2;
方法,所以我们想办法加一个section就行啦
代码如下
CHDeclareMethod0(void, NewSettingViewController, reloadTableData){
CHSuper0(NewSettingViewController, reloadTableData);
MMTableViewInfo *tableInfo = [self valueForKeyPath:@"m_tableViewInfo"];
MMTableViewSectionInfo *sectionInfo = [objc_getClass("MMTableViewSectionInfo") sectionInfoDefaut];
MMTableViewCellInfo *stepcountCellInfo1 = [objc_getClass("MMTableViewCellInfo") editorCellForSel:@selector(recordStepCount:) target:[WeChatManager manager] title:@"微信运动步数" margin:300.0 tip:@"请输入步数" focus:NO text:[NSString stringWithFormat:@"%ld", (long)[WeChatManager manager].stepCount]];
MMTableViewCellInfo *cellInfo2 = [objc_getClass("MMTableViewCellInfo") editorCellForSel:@selector(recordGameContent1:) target:[WeChatManager manager] title:@"石头剪刀(1~3)" margin:300.0 tip:@"" focus:NO text:[NSString stringWithFormat:@"%ld", (long)[WeChatManager manager].gameContent1]];
MMTableViewCellInfo *cellInfo3 = [objc_getClass("MMTableViewCellInfo") editorCellForSel:@selector(recordGameContent2:) target:[WeChatManager manager] title:@"掷骰子(1-6)" margin:300.0 tip:@"" focus:NO text:[NSString stringWithFormat:@"%ld", (long)[WeChatManager manager].gameContent2-3]];
[sectionInfo addCell:stepcountCellInfo1];
[sectionInfo addCell:cellInfo2];
[sectionInfo addCell:cellInfo3];
[tableInfo insertSection:sectionInfo At:0];
MMTableView *tableView = [tableInfo getTableView];
[tableView reloadData];
}
最后
随着微信防越狱技术的提高和版本的迭代,当前越狱版本可能在后续无法使用。而且第三方的越狱版app也不要太相信,可能会偷偷记录个人隐私。能自己弄的还是自己弄吧,且用且珍惜!