iOS之Hook的原理和方法

Hook简介

  • Hook就是一种改变程序执行流程的一种技术的统称;
  • 一段程序的执行流程是 A --> B --> C,现在我们在 A 和 B 之间插入一段代码或者直接改变 B ,这样程序原有的执行流程就发生了改变。如下图所示:
    在这里插入图片描述
  • Hook的方式:Method Swizzle,fishhook,Cydia Substrate;

Hook原理

一、Method Swizzle 原理
  • 利用OC的Runtime特性,动态去改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的主要用于OC方法。
  • Hook中主要用到的方法(参数: Class、SEL、IMP、Method):
// 1、方法交换
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 2、替换方法
OBJC_EXPORT IMP _Nullable
class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
                    const char * _Nullable types)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 3、setIMP & getIMP
OBJC_EXPORT IMP _Nonnull
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

OBJC_EXPORT IMP _Nullable
class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  • Class 一个 objc_class 类型的结构体指针,用于说明对象是哪个类;
  • Method 一个 objc_method 类型的结构体指针,用于定义一个方法,在objc源码中定义如下:
struct objc_method {
    SEL _Nonnull method_name       OBJC2_UNAVAILABLE;
   	char * _Nullable method_types  OBJC2_UNAVAILABLE;
   	IMP _Nonnull method_imp        OBJC2_UNAVAILABLE;
}                                  OBJC2_UNAVAILABLE;
  • SEL 可以发现在 Method 的定义中,也能看见SEL。在苹果官方文档中定义这种类型:
typedef struct objc_selector *SEL;
  • IMP 同样在 Method 的定义中也能看到,在苹果官方文档的定义如下:
id (IMP *)(id, SEL, ...)
  • SEL是一个C String,用于表示一个方法的名称,IMP是一个方法实现首地址,默认有两个参数 self 和 _cmd。其实SEL和IMP的关系可以类比一本书的目录,SEL就是目录中的内容标题,IMP是后面的页码。一个方法调用时,通过SEL找到对应的IMP,进而找到方法的实现。如下图:
    在这里插入图片描述
  • 在Hook一个OC方法时,只需要改变其SEL所指向的IMP时,就可以实现方法的交换的目的,或者使用class_replaceMethod 和 method_setImplementation 改变一个类原有方法的实现,原理如下图:
    在这里插入图片描述
  • 逆向中,Hook一个OC方法其实就是改变其SEL所指向的IMP,从而找到另一个实现地址,执行另一个方法实现。但这种方法的局限在于,其只能针对OC方法进行Hook,对于C函数则无法Hook。
二、fishHook 原理
  • fishHook是由faceBook开发的,是一个动态修改MachO文件的工具,主要是通过修改懒加载和非懒加载表里的指针的指向来达到hook的目的,因此一般用它来hook系统的C函数。
  • 示例
static void (* old_log)(NSString* str);
void newLog(NSString * str){
    str = [str stringByAppendingString:@"\n 勾住了"];
    old_log(str);
}
// hook
rebind_symbols((struct rebinding [1]){{"NSLog",newLog,(void *)&old_log}}, 1);
  • rebind_symbols的简单逻辑
    假设:
    A -> 原方法 ,B -> 新方法 ,Temp -> 中间变量
    Temp = A;
    A = B;
    hook后需要调用原方法就调用Temp
  • 具体如下:
    1 > 首先,MachO文件是被dyld(dynamic load)动态加载的,也就是说,MachO文件被加载到内存的时候是不固定的,它有一个ASLR随机值;
    2 > 同样,依赖的系统库的加载也是随机的,因此,当MachO文件加载到内存之前根本不知道系统库在哪;
    3 > 因此 ,苹果采用了PIC技术(位置代码独立,位置和代码无关);
  • 假设要调用NSLog函数:首先会在映射表中增加一个间接指针,指向外部的NSLog函数;然后dyld会动态的去绑定,将指针指向外部的函数地址。 在这里插入图片描述
三、Cydia Substrate原理
  • 跟method Swizzle类似,也是用的OC的动态性,知识Method Swizzle是用的方法交换,Cydia Substrate 是用的method_getImplementation和 method_setImplementation这两个方法,获取方法实现和设置方法实现。

未完待续

猜你喜欢

转载自blog.csdn.net/Forever_wj/article/details/107777503