美文网首页iOS开发
iOS 逆向开发21:Logos

iOS 逆向开发21:Logos

作者: differ_iOSER | 来源:发表于2021-08-31 09:27 被阅读0次

    iOS 逆向开发 文章汇总

    目录

    一、Logos 简介

    Logos语法其实是CydiaSubsuct框架提供的一组宏定义。便于开发者使用宏进行HOOK操作。语法简单,功能强大且稳定。
    http://iphonedevwiki.net/index.php/Logos

    Logos 语法

    Logos语法分为三大类:

    • Block level
      这一类型的指令会开辟一个代码块,以%end结束。
      %group、%hook、% subclass 、 %end

    • Top level
      这个TopLevel指令不放在BlockLevel中。
      %config、%hookf、%ctor、%dtor

    • Function level
      这一块的指令就放在方法中。
      %init、%class、 %c、 %orig、%log

    常用语法

    HOOK 某个类里面的某个方法

    %hook ClassName
    // 对象方法
    - (void)instanceMethod {
    }
    
    // 类方法
    + (void)classMethod {
    }
    %end
    

    为某个类添加新方法

    %hook ClassName
    // 添加对象方法
    %new
    - (void)newInstanceMethod {
    }
    
    // 添加类方法
    %new
    + (void)newClassMethod {
    }
    %end
    
    • %group
      用来将代码分组。开发中hook代码会很多,这样方便管理Logos代码。
    %group group1
    %hook ClassName
    
    %end
    %end
    
    %ctor {
        NSString *version = [UIDevice currentDevice].systemVersion;
        if (version.doubleValue >= 14.0) {
            %init(group1);
        }
        // 如果有group未在某种条件下初始化就会报错
    }
    
    • %ctor(constructor)
      构造函数,用于确定加载那个组。和%init结合用

    • %init
      用来初始化某个组。

    • %log;
      能够输出日志!! 输出方法调用的详细信息

    • %orig(original)
      这个就是保持原有的方法实现,如果原来的方法有返回值,那么%orig 就有返回值的。

    • %new
      给某个类添加方法,在%hook 和 %end 中使用。

    %new
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [self.view endEditing:YES];
    }
    
    • %c(ClassName)
      类似getClass函数,获得一个类对象。一般用于调用类方法。

    二、Logos 使用

    • 创建目标项目Demo
    • 创建Hook项目HookDemo(MonkeyApp),并安装到手机上

    • 将目标项目Demo的IPA放到Hook项目HookDemo中的TargetApp文件夹中

    • 重新运行HookDemo即可重签注入

    • 修改HookDemoDylib.xm type

    关于xm文件:xm表示支持OC、C/C++语法

    2.1 HOOK loginBtnClick:方法

    修改HookDemoDylib.xm文件中的代码

    #import <UIKit/UIKit.h>
    
    // 下面两行代码self 调用showViewController: sender:方法需要
    @interface ViewController : UIViewController
    
    @end
    
    %hook ViewController
    
    - (void)loginBtnClick:(id)sender {
        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"HOOK成功!" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
        UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleCancel) handler:nil];
        
        [alertVC addAction:cancel];
        [self showViewController:alertVC sender:nil];
    }
    
    %new
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [self.view endEditing:YES];
    }
    
    %end
    

    2.2 将要HOOK的类的头信息导入HookDemoDylib.xm文件

    • dump出目标项目Demo的头文件
      class-dump -H Demo -o DemoHeaders/

    导入头后就能直接使用原APP中的属性和方法

    三、使用 Logos 为 WeChat 设置界面添加Cell

    • 完成后的界面如下:

    3.1 找到需要修改的控制器

    • 使用Debug View Hierarchy看到设置界面的控制器为:NewSettingViewController
    • 使用Cycript也能查看当前的控制器

    3.2 分析界面中的数据源由当前控制器管理还是在其他类中管理

    • dump出目标项目WeChat的头文件
      class-dump -H WeChat -o WeChatHeaders/
    • WeChatHeaders拖到Sublime Text中打开,Command + Shift + F搜索NewSettingViewController
      可以看到NewSettingViewController中没有tableView相关的数据源和代理方法:

    因此我们要修改设置界面的cell数量就只能HOOK NewSettingViewController 中的WCTableViewManager。因为设置界面的TableView的DataSource就是WCTableViewManager

    cy# #0x1156ad000.dataSource
    #"<WCTableViewManager: 0x280c44270>"
    cy#
    

    由于WCTableViewManager在多个控制器中使用,因此HOOKWCTableViewManager时还需要判断当前的控制器(通过响应链条找到当前控制器:
    [tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)])

    • Sublime Text中Command + Shift + F搜索WCTableViewManager :。可以看到TableView的数据源和代理方法均在这里实现:

    3.3 实现修改界面的代码

    • CycriptDemoDylib.xm中的代码如下:
    // See http://iphonedevwiki.net/index.php/Logos
    
    #import <UIKit/UIKit.h>
    
    #define CJDefaults [NSUserDefaults standardUserDefaults]
    #define CJSWITCHKEY @"CJSWITCHKEY"
    #define CJTIMEKEY   @"CJTIMEKEY"
    
    // 关于界面
    @interface WCTableViewManager
    - (long long)numberOfSectionsInTableView:(id)arg1;
    @end
    
    @interface NewSettingViewController:UIViewController
    @end
    
    
    %hook WCTableViewManager
    %new
    - (void)cjtextFieldDidChangeValue:(NSNotification *)notification {
        UITextField *sender = (UITextField *)[notification object];
        [CJDefaults setValue:sender.text forKey:CJTIMEKEY];
        [CJDefaults synchronize];
    }
    
    %new
    - (void)cjswitchChang:(UISwitch *)switchView {
        [CJDefaults setBool:switchView.isOn forKey:CJSWITCHKEY];
        [CJDefaults synchronize];
        [MSHookIvar <UITableView *>(self,"_tableView") reloadData];
    }
    
    - (void)scrollViewWillBeginDragging:(id)arg1 {
        %orig;
        [MSHookIvar <UITableView *>(self,"_tableView") endEditing:YES];
    }
    
    //返回高度
    - (double)tableView:(UITableView *)tableView heightForRowAtIndexPath:(id)indexPath {
        //定位设置界面,并且是最后一组
        if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]
           && [indexPath section] == [self numberOfSectionsInTableView:tableView]-1){
            return 44;
        }
        return %orig;
    }
    
    //每一个Cell
    - (id)tableView:(UITableView *)tableView cellForRowAtIndexPath:(id)indexPath {
        //定位设置界面,并且是最后一组
        if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]
           && [indexPath section] == [self numberOfSectionsInTableView:tableView]-1) {
    
            UITableViewCell * cell = nil;
            if ([indexPath row] == 0) {
                static NSString *swCell = @"SWCELL";
                cell = [tableView dequeueReusableCellWithIdentifier:swCell];
                if (!cell) {
                    cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:nil];
                }
                cell.textLabel.text = @"自动抢红包";
                //抢红包开关!!
                UISwitch *switchView = [[UISwitch alloc] init];
                switchView.on = [CJDefaults boolForKey:CJSWITCHKEY];
                [switchView addTarget:self action:@selector(cjswitchChang:) forControlEvents:(UIControlEventValueChanged)];
                cell.accessoryView   = switchView;
                cell.imageView.image = [UIImage imageNamed:([CJDefaults boolForKey:CJSWITCHKEY] == 1) ? @"unlocked" : @"locked"];
            } else if([indexPath row] == 1) {
                static NSString * waitCell = @"waitCell";
                cell = [tableView dequeueReusableCellWithIdentifier:waitCell];
                if(!cell){
                    cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:nil];
                }
                cell.textLabel.text = @"等待时间(秒)";
                UITextField * textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 150, 40)];
                //监听键盘输入
                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cjtextFieldDidChangeValue:) name:UITextFieldTextDidChangeNotification object:textField];
                textField.text = [CJDefaults valueForKey:CJTIMEKEY];
                textField.borderStyle = UITextBorderStyleRoundedRect;
                cell.accessoryView    = textField;
                cell.imageView.image  = [UIImage imageNamed:@"clock"];
            }
            cell.backgroundColor = [UIColor whiteColor];
            return cell;
        }
        return %orig;
    }
    
    //每组多少行
    - (long long)tableView:(UITableView *)tableView numberOfRowsInSection:(long long)section {
        //定位设置界面,并且是最后一个
        if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]
           && section == [self numberOfSectionsInTableView:tableView]-1) {
            return 2;
        }
        return %orig;
    }
    
    //多少组
    - (long long)numberOfSectionsInTableView:(UITableView *)tableView {
        if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]) {// 定位设置界面
            // 在原来基础上多搞一组
            return %orig+1;
        }
        return %orig;
    }
    %end
    
    
    %hook NewSettingViewController
    
    %new
    -(void)cjkeyboardWillShow:(NSNotification *)note {
        UIView * view = self.view;
        CGRect keyBoardRect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
        view.frame = CGRectMake(0, -keyBoardRect.size.height, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height );
    }
    
    %new
    -(void)cjkeyboardWillHide:(NSNotification *)note {
        UIView *view = self.view;
        view.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
    }
    
    - (void)viewDidLoad {
        %orig;
        //监听textField弹出和消失
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(cjkeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(cjkeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
    }
    
    %end
    

    注意一:logos中获取对象的成员变量一般有如下三种方法:

    1. 可以导入头文件中属性和成员变量的定义
    2. KVC
    3. MSHookIvar :UITableView *tableView = MSHookIvar<UITableView *>(self,"_tableView");

    注意二:所有%new新添加的方法一定要加上自己的前缀,避免意外覆盖了原APP已有的方法

    注意三:可以在CycriptDemoDylib.xm的方法中添加断点进行调试

    注意四:引用的图片的方法:将ipa包解压后添加需要的图片并打包即可
    zip –ry 输出文件 输入文件 将输入文件压缩为输出文件
    参考:iOS 逆向开发12:iOS 应用重签名

    相关文章

      网友评论

        本文标题:iOS 逆向开发21:Logos

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