使用MokeyDev
對WeChat
進行重簽名并安裝篷角,在設(shè)置頁焊刹,增加自動搶紅包的UI
,包含是否啟用自動搶紅包功能的開關(guān)恳蹲,以及搶紅包時的手速設(shè)置
界面分析
使用
class-dump
導(dǎo)出全部頭文件./class-dump -H WeChat -o ./header/
使用
MokeyDev
重簽名wx8.0.2.ipa
真機運行項目虐块,使用
Debug Viwe
找到設(shè)置頁的控制器名稱
使用
Debug Viwe
時,如果經(jīng)臣卫伲卡死贺奠,可以先將其暫停/繼續(xù)
一次
使用
Cycript
附加進程使用
pvcs()
找到設(shè)置頁的控制器
打印控制器
View
下的所有視圖,從中找到UITableView
错忱,并找到對應(yīng)的數(shù)據(jù)源
打開
WCTableViewManager.h
文件儡率,找到數(shù)據(jù)源和關(guān)鍵方法
后續(xù)對關(guān)鍵方法進行
HOOK
精準(zhǔn)定位注入點
找到影響
UITableView
展示行數(shù)的數(shù)據(jù)源對
WCTableViewManager
中的numberOfSectionsInTableView
方法進行HOOK
挂据,打印數(shù)組總數(shù)和Section
數(shù)#import <UIKit/UIKit.h> @interface WCTableViewManager : NSObject @property(retain, nonatomic) NSMutableArray *sections; @end %hook WCTableViewManager - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { NSLog(@"數(shù)據(jù)源:%ld,Sections:%ld", (long)self.sections.count, (long)[tableView numberOfSections]); return %orig; } %end
真機運行項目儿普,查看
HOOK
之后的打印結(jié)果
從打印結(jié)果來看崎逃,
UITableView
的顯示行數(shù)和數(shù)組總數(shù)是一致的。但也打印出其他頁面的內(nèi)容眉孩,證明WCTableViewManager
在項目中是通用的个绍。如果想對其HOOK
,需要精準(zhǔn)定位在設(shè)置頁浪汪,不能影響其他功能
想要精準(zhǔn)定位巴柿,需要在
WCTableViewManager
中,對所屬控制器進行判斷我們要找到
WCTableViewManager
和控制器的關(guān)聯(lián)找到
WCTableViewManager
#0x280bfe5e0 ------------------------- #"<WCTableViewManager: 0x280bfe5e0>"
找到
UITableView
吟宦,通過響應(yīng)鏈條,向下找一層#0x280bfe5e0.tableView.nextResponder ------------------------- #"<UIView: 0x1157e38f0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x281edca00>>"
通過響應(yīng)鏈條涩维,再向下找一層
#0x280bfe5e0.tableView.nextResponder.nextResponder ------------------------- #"<NewSettingViewController: 0x116cb8400>"
通過響應(yīng)鏈條殃姓,成功找到
NewSettingViewController
修改代碼,增加判斷條件瓦阐,保證
HOOK
代碼僅在設(shè)置頁有效- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]){ NSLog(@"數(shù)據(jù)源:%ld蜗侈,Sections:%ld", (long)self.sections.count, (long)[tableView numberOfSections]); } return %orig; }
真機運行項目,查看
HOOK
之后的打印結(jié)果
僅在
NewSettingViewController
中打印結(jié)果
修改界面
確保代碼僅在
NewSettingViewController
中生效睡蟋,接下來對幾個關(guān)鍵方法進行HOOK
踏幻,將界面修改成我們預(yù)期的樣子增加
Section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]){ return %orig+1; } return %orig; }
最后
Section
下面的Rows
,固定為2
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)] && (section==[self numberOfSectionsInTableView:tableView]-1)){ return 2; } return %orig; }
為了編譯通過戳杀,需要在
WCTableViewManager
中该面,聲明numberOfSectionsInTableView:
方法@interface WCTableViewManager : NSObject <UITextFieldDelegate> - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; @end
自定義
Cell
的高度,固定為60
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)] && (indexPath.section==[self numberOfSectionsInTableView:tableView]-1)){ return 60; } return %orig; }
自定義
Cell
信卡,只設(shè)置背景色隔缀,看一下運行后的結(jié)果,確認(rèn)HOOK
代碼的有效性- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)] && (indexPath.section==[self numberOfSectionsInTableView:tableView]-1)){ NSString *strIdentifier=[NSString stringWithFormat:@"HookCell_%i",(int)indexPath.row]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:strIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strIdentifier]; } if(indexPath.row==0){ cell.backgroundColor=[UIColor redColor]; } else{ cell.backgroundColor=[UIColor blueColor]; } return cell; } return %orig; }
真機運行項目傍菇,查看
UI
效果
修改
NewSettingViewController
成功猾瘸,在原有界面的下方,增加了自定義Cell
完善界面
確認(rèn)
HOOK
代碼是有效的丢习,下面完善自定義Cell
的界面將自定義圖標(biāo)牵触,導(dǎo)入
完善
tableView:cellForRowAtIndexPath:
方法- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)] && (indexPath.section==[self numberOfSectionsInTableView:tableView]-1)){ NSString *strIdentifier=[NSString stringWithFormat:@"HookCell_%i",(int)indexPath.row]; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:strIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strIdentifier]; } cell.backgroundColor = [UIColor whiteColor]; cell.selectionStyle = UITableViewCellSelectionStyleNone; if(indexPath.row==0){ BOOL isAutoEnable = NO; cell.imageView.image = [UIImage imageNamed:(isAutoEnable ? @"hook_auto_en" : @"hook_auto_dis")]; cell.textLabel.text = @"自動搶紅包"; UISwitch *switchAuto = [[UISwitch alloc] init]; [switchAuto addTarget:self action:@selector(hookAutoAction:) forControlEvents:UIControlEventValueChanged]; switchAuto.on=isAutoEnable; cell.accessoryView = switchAuto; } else{ cell.imageView.image = [UIImage imageNamed:@"hook_wait"]; cell.textLabel.text = @"等待時間(秒)"; UITextField *txtWait=[[UITextField alloc] initWithFrame:CGRectMake(0, 0, 150, 40)]; txtWait.borderStyle = UITextBorderStyleRoundedRect; txtWait.backgroundColor = [UIColor whiteColor]; txtWait.keyboardType = UIKeyboardTypeNumberPad; txtWait.returnKeyType = UIReturnKeyDone; cell.accessoryView = txtWait; } return cell; } return %orig; }
增加
UISwitch
切換時,觸發(fā)的hookAutoAction:
方法%new -(void)hookAutoAction:(UISwitch *)sender{ NSLog(@"自動搶紅包:%@", (sender.isOn ? @"啟用" : @"禁用")); }
真機運行項目咐低,查看
UI
效果
實現(xiàn)功能
UI
搭建完成后揽思,還差最后一步,實現(xiàn)功能自動搶紅包功能的啟用/禁用標(biāo)識见擦,以及搶紅包時的手速設(shè)置绰更,都要進行本地化保存
增加宏定義
#define HOOKAUTOVALUE @"HookAutoValue" #define HOOKWAITVALUE @"HookWaitValue"
實現(xiàn)
UISwitch
切換的邏輯%new -(void)hookAutoAction:(UISwitch *)sender{ [[NSUserDefaults standardUserDefaults] setBool:sender.isOn forKey:HOOKAUTOVALUE]; [[NSUserDefaults standardUserDefaults] synchronize]; [MSHookIvar<UITableView *>(self,"_tableView") reloadData]; }
修改
tableView:cellForRowAtIndexPath:
方法瞧挤,將UI
和功能進行關(guān)聯(lián)BOOL isAutoEnable = [[NSUserDefaults standardUserDefaults] boolForKey:HOOKAUTOVALUE]; cell.imageView.image = [UIImage imageNamed:(isAutoEnable ? @"hook_auto_en" : @"hook_auto_dis")]; cell.textLabel.text = @"自動搶紅包"; UISwitch *switchAuto = [[UISwitch alloc] init]; [switchAuto addTarget:self action:@selector(hookAutoAction:) >forControlEvents:UIControlEventValueChanged]; switchAuto.on=isAutoEnable; cell.accessoryView = switchAuto;
完成搶紅包時的手速設(shè)置邏輯
添加
UITextFieldDelegate
@interface WCTableViewManager : NSObject <UITextFieldDelegate> @property(retain, nonatomic) NSMutableArray *sections; @end
增加
textField:shouldChangeCharactersInRange:replacementString:
方法,文本框內(nèi)輸入\n
儡湾,視為輸入完成特恬,自動收起鍵盤%new - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { if ([string isEqualToString:@"\n"]) { [textField resignFirstResponder]; return NO; } return YES; }
增加
textFieldDidEndEditing:
方法,輸入完成徐钠,將文本框內(nèi)存本地化保存%new -(void)textFieldDidEndEditing:(UITextField *)textField { [[NSUserDefaults standardUserDefaults] setObject:textField.text forKey:HOOKWAITVALUE]; [[NSUserDefaults standardUserDefaults] synchronize]; }
修改
tableView:cellForRowAtIndexPath:
方法癌刽,將UI
和功能進行關(guān)聯(lián)cell.imageView.image = [UIImage imageNamed:@"hook_wait"]; cell.textLabel.text = @"等待時間(秒)"; UITextField *txtWait=[[UITextField alloc] initWithFrame:CGRectMake(0, 0, 150, 40)]; txtWait.borderStyle = UITextBorderStyleRoundedRect; txtWait.backgroundColor = [UIColor whiteColor]; txtWait.keyboardType = UIKeyboardTypeNumberPad; txtWait.returnKeyType = UIReturnKeyDone; txtWait.delegate = self; txtWait.text = [[NSUserDefaults standardUserDefaults] objectForKey:HOOKWAITVALUE]; cell.accessoryView = txtWait;
真機運行項目,查看
UI
效果
優(yōu)化
整體的界面和功能都已經(jīng)完成尝丐,還有兩個小問題需要優(yōu)化
- 觸發(fā)文本框显拜,鍵盤彈出,會遮擋底部的功能區(qū)域
- 設(shè)置頁的列表滑動時爹袁,鍵盤無法自動收起远荠,影響體驗
解決遮擋問題
對
NewSettingViewController
進行HOOK
,對鍵盤的通知進行監(jiān)聽和銷毀%hook NewSettingViewController - (void)viewDidLoad{ %orig; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } - (void)dealloc{ [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } %end
實現(xiàn)鍵盤彈出方法
%new - (void)keyboardWillShow:(NSNotification *)notification { CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size; CGSize viewSize = self.view.frame.size; self.view.frame = CGRectMake(0, -keyboardSize.height, viewSize.width, viewSize.height); }
實現(xiàn)鍵盤收起方法
%new - (void)keyboardWillHide:(NSNotification *)notification { CGSize viewSize = self.view.frame.size; self.view.frame = CGRectMake(0, 0, viewSize.width, viewSize.height); }
解決列表滑動失息,自動收起鍵盤問題
對
NewSettingViewController
進行HOOK
譬淳,修改viewDidLoad
方法增加
UITableView.keyboardDismissMode
屬性的設(shè)置- (void)viewDidLoad{ %orig; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; WCTableViewManager *m_tableViewMgr = MSHookIvar<WCTableViewManager *>(self, "m_tableViewMgr"); [MSHookIvar<UITableView *>(m_tableViewMgr, "_tableView") setKeyboardDismissMode:UIScrollViewKeyboardDismissModeOnDrag]; }
真機運行項目,優(yōu)化后的
UI
效果
總結(jié)
自動搶紅包
UI
搭建
- 使用
class-dump
盹兢,導(dǎo)出目標(biāo)App
的頭文件- 使用
MokeyDev
重簽名并運行App
- 使用
Debug Viwe
邻梆,快速定位目標(biāo)控制器- 使用
Cycript
,分析控制器中的視圖绎秒、對象浦妄、數(shù)據(jù)源- 在對應(yīng)的頭文件中,找到關(guān)鍵的方法和屬性
- 需要精準(zhǔn)定位到注入點见芹,不能影響其他功能
- 可以使用響應(yīng)鏈條剂娄,找到控件與所屬控制器的關(guān)聯(lián)
- 需要自定義圖標(biāo),直接將圖片導(dǎo)入
App
包即可- 添加的方法玄呛,方法名稱加上自定義前綴宜咒,保證命名唯一
HOOK
關(guān)鍵方法,完成界面與功能MSHookIvar
:獲取對象下的成員變量