iOS 逆向 - 实际逆向中 hook 方式 -- Logos

3,898

前言

Hook / fishHook 原理与符号表 这篇文章中我们提到过 基本的 hook 的使用和原理分析 , 但是在逆向中 , 由于我们需要大量的去 hook 来调试业务逻辑等等 , 我们这么一个个的去 hook 别人的方法也太麻烦 ( ben ) 了些 .

在实际逆向开发过程中 , 我们最常用到的 代码注入 / 调试 使用的 hook 方式是 Cydia Substrate , 它能帮助我们非常简单的去 hook 某个类的某个方法 , 获取某个类的属性 , 再 hook 之后继续调用原本的函数 等等 , 它使用的就是 Logos 语法.

因此 , 对于 iOS 逆向开发人员这个技能是必不可少的 .

Cydia Substrate

Cydia Substrate 是绝大部分 tweak ( 本质是 dylib ) 正常工作的基础,主要分为三部分:Mobile Hooker , Mobile LoaderSafe mode

Mobile Hooker

也就是本篇文章重点讲述的部分 .

顾名思义用于 HOOK。它定义一系列的宏和函数,底层调用 objcruntimefishhook 来替换系统或者目标应用的函数. 其中有两个函数:

  • MSHookMessageEx  主要作用于 Objective-C 方法

    void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result) 
    
  • MSHookFunction  主要作用于 CC++ 函数

    void MSHookFunction(voidfunction,void* replacement,void** p_original) 
    

    Logos 语法的 %hook 就是对此函数做了一层封装 .

Mobile Loader

Mobile Loader 用于加载第三方 dylib 在运行的应用程序中。启动时 Mobile Loader 会根据规则把指定目录的第三方的动态库加载进去,第三方的动态库也就是我们写的破解程序 .

safe mode

破解程序本质是 dylib,寄生在别人进程里。 系统进程一旦出错,可能导致整个进程崩溃,崩溃后就会造成 iOS 瘫痪。所以 Cydia Substrate 引入了安全模式,在安全模 式下所有基于 Cydia Substrate 的三方 dylib 都会被禁用,便于查错与修复。

在安全模式里,所有基于 Cydia Substrate 的第三方 dylib 会被禁用,便于查找于修复;如果设备因为 dylib 的原因无法进入系统,比如,开机一直卡在白苹果上,或者进度圈不停地转—> home + lock +然后音量上键禁用 Cydia Substrate,系统重启后再查错与排修,修复后重启 iOSCydia Substrate 会自动重启)

说了很多前导知识 , 主要是为了大家清楚 Cydia SubstrateLogos 的关系 .

Logos

概述

Logos : iphonedevwiki.net/index.php/L…

Logos 语法其实是 Cydia Substruct 框架提供的一组宏定义。便于开发者使用宏进行 HOOK 操作。语法简单,功能强大且稳定。

Logos 语法分为三大类:

  • Block level

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

    • 这个 Top Level 指令不放在 Block Level 中。 %config%hookf%ctor%dtor
  • Function level

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

简单语法解释

HOOK 方法

%hook ClassName
//对象方法
- (void)instanceMethod{

    //输出方法调用的详细信息
    %log;

    //继续执行原本方法
    %orig;
}
//类方法
+ (void)classMethod{

    //输出方法调用的详细信息
    %log;

    //继续执行原本方法
    %orig;
}
%end

这样就完成了一个方法的 hook , 不管从简便性 , 可读性方面都得到了大幅度的提高 .

新增方法

//新增方法
%hook ClassName
//新增对象方法
%new
- (void)newInstanceMethod{

}
%new
//新增类方法
+ (void)newClassMethod{

}
%end

这样就可以为类添加实例方法 / 类方法 .

注意 : 一个 %new 添加一个方法 , 无须 %end .

group 分组

请看一下以下代码

//分组
%group group1

%hook ClassName
- (void)instanceMethod{
    NSLog(@"第一组 hook到");
}
%end

%end

%group group2

%hook ClassName
- (void)instanceMethod{
    NSLog(@"第二组 hook到");
}
%end

%end

%ctor{
    %init(group1)%init(group2);
}

打印结果证明是第二组 hook 到了 .

先解释下 %ctorconstructor 的简写 , 即构造函数 ,

同理 %dtor 就是析构函数 .

那么为什么是最后 hook 的是第二组呢 ?

构造函数中 先后去执行了第一组和第二组 , 那么第二组的 hook 就会覆盖掉第一组 hook 的效果 , 因此显然打印会是第二组加载到了 .

类方法调用

Objective-C 中调用类方法我们一般是 [ClassName ClassMethod] 的方式 , 在 Logos 中这么写会报错 , 因此需要用到 %c , 其写法为 :

[%c(ClassName) ClassMethod] ;

可简单理解为 getClassFromString .

案例

( 模拟逆向过程中 使用 Logos 进行 hook )

  • 先准备一个工程 , 新建工程 , 添加一个方法
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)testFunc{
    [[[UIAlertView alloc] initWithTitle:@"提示" message:@"源方法" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:@"取消", nil] show];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self testFunc];
}
@end
  • 选择真机 , 开发者 ,运行工程 , 找到工程 Mach-O
  • class-dump -H LBLogosOriginalDemo -o ./headers/ 不熟悉 class-dump 的同学可以阅读一下 重签应用调试与代码修改 (Hook) 这篇文章 .
  • 得到头文件查看类与方法 .
  • 新建 monkey 工程 , 选择开发者 , 选择真机 . 先运行一下空 monkey 工程 ( 安装描述文件 ) .
  • 把我们工程的 app 包放到 target app
  • 编写 logos
#import <UIKit/UIKit.h>

%hook ViewController
- (void)testFunc{
    %log;
    [[[UIAlertView alloc] initWithTitle:@"提示" message:@"已hook方法" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:@"取消", nil] show];
}
%end
  • 运行 monkey 工程 .

  • 点击屏幕

  • 同时 , 控制台 ( %log ) 打印如下 :

至此 , 一个逆向过程中实际的 hook 写法我们就完成了 . 实际逆向开发中 , 我们往往还会结合 动态调试 ( lldb , cycript) , 静态分析 ( 汇编代码 ) , View-Debug 等方式来分析业务逻辑等等 . 但往往实际代码注入 , 都是采用如上方式进行的 hook .

后续继续分享 .