美文网首页
21-自动抢红包UI

21-自动抢红包UI

作者: 深圳_你要的昵称 | 来源:发表于2021-06-07 07:24 被阅读0次

    前言

    本篇文章将使用前面所讲的工具和语法,做一个实战演练,修改微信的【设置】页面 👉 增加2个cell,包含是否启用自动抢红包功能的开关,以及抢红包时的手速设置,最终效果图如下👇

    当然,在做这个之前,需要使用MokeyDevWeChat进行重签名并安装。请参考15-Hook原理(二)反Hook防护 & MokeyDev

    一、UI分析

    1. 使用Cycript附加进程。具体请参考19-Cycript

    2. 使用pvcs()找到设置页的控制器👇🏻

    1. 打印控制器View下的所有视图,从中找到UITableView,并找到对应的数据源👇🏻
    1. 接着我们dump出微信App所有的头文件👇🏻
    ./class-dump -H WeChat -o ./header/
    

    下载地址:http://stevenygard.com/projects/class-dump

    打开WCTableViewManager.h文件,找到数据源和关键方法👇🏻

    接下来就是对这些方法进行Hook了。

    重签名后微信无法登录的问题

    以上的方式,必须利用MokeyDev对微信的ipa进行重签名,在运行项目,登录进入微信的页面,但是,我自己尝试登录的时候,发现👇🏻

    于是没法进入微信的设置页面,此时怎么办呢?

    1. 将手机越狱,可借助爱思助手,也可使用越狱工具👇🏻

    2. 手机越狱后安装Cycript 👉🏻 Cycript 基本使用

    3. 使用OpenSSH连接手机,使用cy指令查看微信的页面(即ViewController)信息

      • 有封装的比较好的cy文件 👉🏻 mjcript.cy
    1. 接下来就跟上面一样,搜索出WCTableView

    二、定位

    接下来,我们要对【设置】页面进行分析,找到具体的注入代码位置

    1. WCTableViewManager中的numberOfSectionsInTableView方法进行HOOK,打印数据源countSection个数👇🏻
    #import <UIKit/UIKit.h>
    
    @interface WCTableViewManager : NSObject
    @property(retain, nonatomic) NSMutableArray *sections;
    @end
    
    %hook WCTableViewManager
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
       NSLog(@"数据源:%ld,Sections:%ld", (long)self.sections.count, (long)[tableView numberOfSections]);
       return %orig;
    }
    
    %end
    
    1. 真机运行项目,查看HOOK之后的打印结果👇🏻

    从上图中可以看出,数据源的数目和Section的数目也打印了很多条,说明UITableView是一个通用的控件,不止一个VC在使用,证明WCTableViewManager在项目中是通用的,所以,要精准定位Hook,必须在【设置页】,【设置页】上面我们打印过,是NewSettingViewController👇🏻

    1. 修改代码,增加判断NewSettingViewController的条件👇🏻
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
       
       if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]){
           NSLog(@"数据源:%ld,Sections:%ld", (long)self.sections.count, (long)[tableView numberOfSections]);
       }
    
       return %orig;
    }
    

    真机运行项目,查看HOOK之后的打印结果👇🏻

    三、注入修改

    接下来,注入代码修改,实现抢红包的UI。

    1. 增加Section,我们在单独的分组中添加2个cell👇🏻
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
       
       if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]){
           return %orig+1;
       }
    
       return %orig;
    }
    
    - (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
    
    1. 自定义Cell的实现
    • 行高 👉🏻 固定60pt
    - (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初始化(先设置背景色,看看效果)
    - (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;
    }
    

    真机运行👇🏻

    增加了自定义Cell成功!🍺🍺🍺🍺🍺🍺

    1. 完善cell
    2. 需要导入图片资源,将图片copy到app包中👇🏻
    1. 完善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;
    }
    
    1. 增加UISwitch切换时,触发的hookAutoAction:方法👇🏻
    %new
    -(void)hookAutoAction:(UISwitch *)sender{
       NSLog(@"自动抢红包:%@", (sender.isOn ? @"启用" : @"禁用"));
    }
    
    1. 运行👇🏻

    四、优化

    优化一:❤️图标切换

    switch开关的状态,应该关联❤️图标,比如开关开启时👇🏻

    1. 自动抢红包功能的启用/禁用标识,以及抢红包时的手速设置,都要进行NSUserDefaults本地化保存
    • 增加宏定义 👉🏻 缓存的key 👇🏻
    #define HOOKAUTOVALUE @"HookAutoValue"
    
    • 实现UISwitch切换的逻辑👇🏻
    %new
    -(void)hookAutoAction:(UISwitch *)sender{
    
       [[NSUserDefaults standardUserDefaults] setBool:sender.isOn forKey:HOOKAUTOVALUE];
       [[NSUserDefaults standardUserDefaults] synchronize];
       [MSHookIvar<UITableView *>(self,"_tableView") reloadData];
    }
    
    • 修改cell的初始化,关联开关状态
    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;
    

    优化二:手速设置逻辑

    • 同样需要本地缓存,增加宏定义👇🏻
    #define HOOKWAITVALUE @"HookWaitValue"
    
    • 添加UITextFieldDelegate
    @interface WCTableViewManager : NSObject <UITextFieldDelegate>
    @property(retain, nonatomic) NSMutableArray *sections;
    @end
    
    • 在输入时间的同时,delegate出输入完成的时机
    %new
    -(void)textFieldDidEndEditing:(UITextField *)textField {
       [[NSUserDefaults standardUserDefaults] setObject:textField.text forKey:HOOKWAITVALUE];
       [[NSUserDefaults standardUserDefaults] synchronize];
    }
    

    缓存手速时间

    • 同时识别`输入回车后,放弃焦点,收起键盘
    %new
    - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
       
       if ([string isEqualToString:@"\n"]) {
           [textField resignFirstResponder];
           return NO;
       }
       
       return YES;
    }
    
    • 完善cell初始化,将UI上述交互功能关联
    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;
    

    优化三:键盘遮挡

    现在整体的界面和功能都已经完成,但体验时仍有2个小问题👇🏻

    • 键盘弹出时,会遮挡底部的功能区域
    • 设置页的列表滑动时,键盘无法自动收起

    遮挡问题优化

    1. NewSettingViewController进行HOOK,对键盘的通知进行监听和销毁
    
    %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
    
    1. 键盘弹出
    %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);
    }
    
    1. 键盘收起
    %new
    - (void)keyboardWillHide:(NSNotification *)notification {
       CGSize viewSize = self.view.frame.size;
       self.view.frame = CGRectMake(0, 0, viewSize.width, viewSize.height);
    }
    

    列表滚动收起键盘

    viewDidLoad增加UITableView.keyboardDismissMode属性的设置👇🏻

    - (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];
    }
    

    最终的效果👇🏻

    相关文章

      网友评论

          本文标题:21-自动抢红包UI

          本文链接:https://www.haomeiwen.com/subject/mdpysltx.html