美文网首页iOS--逆向
HOOK原理及介绍

HOOK原理及介绍

作者: Superman168 | 来源:发表于2018-07-25 00:51 被阅读0次

    注入小结

    通过之前的学习,我们知道了利用动态库注入的两种方式:

    - Framework
    - Dylib
    
    • 注入 App 后,使得 项目和动态库产生关联关系。
    • 修改 Mach-o 文件的 Load Commands 段。

    然后开始在注入的动态库中,开发你想实现的逻辑功能!

    一、HOOK概述

    HOOK(钩子) 其实就是改变程序执行流程的一种技术的统称!

    15245516017168.jpg

    iOS中HOOK技术的几种方式

    1、Method Swizzle

    利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的。主要用于OC方法

    2、fishhook

    它是Facebook提供的一个动态修改链接mach-O文件的工具。利用MachO文件加载原理,通过修改懒加载和非懒加载两个表的指针达到C函数HOOK的目的。

    OC 是动态语言,C 则是静态语言,一经编译,所有的都可以获取到,那 Fishhook 是怎么做到的呢????

    3、Cydia Substrate

    Cydia Substrate 原名为 Mobile Substrate ,它的主要作用是针对OC方法、C函数以及函数地址进行HOOK操作。当然它并不是仅仅针对iOS而设计的,安卓一样可以用。官方地址:http://www.cydiasubstrate.com/

    Cydia Substrate主要由3部分组成:

    • MobileHooker

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

      • MSHookMessageEx 主要作用于Objective-C方法
       void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
      
      
      • MSHookFunction 主要作用于C和C++函数
       void MSHookFunction(voidfunction,void* replacement,void** p_original)
      
      

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

    • MobileLoader

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

    • safe mode

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

    fishhook 的简单使用

    Github 有相关的介绍,使用方法等;

    https://github.com/facebook/fishhook

    • fishhook Demo 测试 1:系统方法交换
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // rebinding 结构体的定义
        //    struct rebinding {
        //        const char *name; // 需要 HOOK 的函数名称,字符串
        //        void *replacement; // 替换的新函数(函数指针,也就是函数名称)
        //        void **replaced; //  保存原始函数指针变量/地址的指针(它是一个二级指针!)
        //    };
        // C 语言传参是值/址传递的,把它的值/址穿过去,就可以在函数内部修改函数指针变量的值
    
        // 定义 rebinding 结构体
        struct rebinding nslogRebind;
        // 函数名称
        nslogRebind.name = "NSLog";
        // 新的函数指针
        nslogRebind.replacement = myLog;
        // 保存原始函数地址的变量的指针
        nslogRebind.replaced = (void *)&old_Nslog;// fishhook 将 old_Nslog 这个指针指向了 NSLog 这个函数,怎么做的呢???
    
        // 定义数组
        struct rebinding rebs[] = {nslogRebind};
        
        /*
         1. rebindings : 存放 rebinding 结构体的数组,可以交换 N 种方法
         2. size_t rebindings_nel : 数组的长度???
         */
        rebind_symbols(rebs, 1);
    }
    
    // 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
    static void (*old_Nslog)(NSString *format, ...);
    
    // 自定义的 Log
    void myLog (NSString *format, ...){
        format = [format stringByAppendingString:@"——> 勾上了!!!💖💖💖💖💖💖💖💖"];
        old_Nslog(format);
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"点击了屏幕");
        
    }
    

    点击屏幕,可以看到控制台日志:

    image.png
    • fishhook Demo 测试2:自定义方法交换 :
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 交换简写!
        // (struct rebinding [1]) 数组类型
        // {{"func",newFunc,(void *)&funcP}} 值为结构体,三个参数
        rebind_symbols((struct rebinding [1]){{"func",newFunc,(void *)&funcP}}, 1);
    }
    
    // 原始函数指针
    static void (*funcP)(const char *);
    // 新函数
    void newFunc(const char *str){
        NSLog(@"———> 勾住了!!!💖💖💖💖💖💖");
        funcP(str);
    }
    
    // 原始函数
    void func(const char * str){
        NSLog(@"%s",str);
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        
        func("Hello world!!!");
    }
    
    image.png

    首先代码是没有问题的,为什么呢?自定义方法交换不了呢???

    fishhook 原理探究

    1. Mach-O 文件是怎么加载的?
      DYLD 工具动态加载,加载完 Mach-O 文件后,加载依赖的动态库,通过 image list 可以看到加载的相关类库。

    2. ASLR 地址空间随机布局,Mach-O 文件加载的时候是随机地址的。
      系统类库,每次启动,所有地址也是随机布局。

    3. PIC 位置代码独立

    ???

    控制器(Programmable Interrupt Controller)”的简称。

    ???

    可编程中断控制器(Programmable Interrupt Controller),也简称为PIC,是微处器与外设之间的中断处理的桥梁,由外设发出的中断请求需要中断控制器来进行处理。

    如果 Mach-o 文件内部需要调用系统的函数时:

    • 现在 Mach-o _data 段 建立一个指针(就是符号,实现指向内部的函数调用,指向外部的函数地址),指向外部函数,(dyld 加载)
      可读可写,当 Mach-o 被加载进去的时候,它就会指向所指的函数。

    • DYLD会进行动态的绑定,将 Mach-o 文件Data 段中的指针指向外部的函数!!!所以 DYLD 叫动态加载。
      故 fishhook 中 的绑定方法叫做:rebind_symbols 重新绑定符号,就是绑定程序内部的函数。

    这也就是为什么内部/自定义的函数修改不了,只能修改 Mach-O 文件外部的函数,如果是另一个动态库的或者是需要动态符号绑定的就可以,在符号表中能找到的才可以。

    接下来,验证一下:

    还是用 fishhook Demo 测试 1 来测试,运行,查看 Mach-o 符号表:

    image.png NSLog 函数

    offset :8018, NSLog文件偏移地址,也就是说懒加载这个表也就在 Mach-O 文件偏移的地址 + 函数偏移地址

    动态调试:

    断点位置 Mach-O 在内存中的偏移地址

    Mach-O 在内存中的偏移地址也就是 Mach-O 的真实地址。

    Mach-o 文件Data 段中的函数指针

    计算符号表的地址

    通过符号表找方法的地址,通过 dis -s 反汇编命令查看函数详情。

    接下来,过段点继续查看:

    前后比较

    通过上面可以看出,fishhook 之所以能够 Hook C 函数,是因为根据 Mach-O 文件 特点,PIC 位置代码独立 也就造就了所谓的静态语言 C 也有动态的部分,通过 DYLD 进行动态绑定的时候,我们做了手脚,替换为我们自定义的方法。

    备注: 值得注意的是,调用之前先调用一下 NSLog 函数,(符号表中应该没有 NSLog 函数的地址,但其实是有,但是可能是不正确的/固定的地址??)否则则调用 rebind_symbols 方法前不能正确的获取 函数的符号表地址(fishHook 内部会调用吧??)

    • 懒加载表中 NSLog 函数什么时候给他赋值呢??

    现在是存放在 Mach-o 文件偏移 8018 的位置 ,而它存放的一个 8 个字节的指针,保存的就是 动态缓存区 中保存 NSLog 函数的真实的地址,那这个地址是什么时候保存进去的呢???
    并不是 Mach-O 文件加载的时候保存的,而是第一次调用 该函数时保存进去的,绑定一下,由 DYLD 绑定 NSLog 这个符号指向真实的 NSLog 的地址。

    未调用方法的符号表 image.png

    那 fishhook 根据方法字符串名字 "NSLog" 也就是 struct rebinding const char *name 是怎么找到的呢 ???

    通过符号表查看函数名称字符串

    再次查看 Mach-o 文件,上面查看的懒加载表中的 NSLog 函数:

    懒加载表和动态符号表

    懒加载表和动态符号表是一一对应 的关系,如下:

    动态符号表

    动态符号表中的 Data 其实就是 方法的 Symbol Table 中的下标,转换为 10 进制 (0x88 = 136),到 Symbol Table 中查看:

    Symbol Table

    _ 和 .
    _ 是函数的开始,. 是分隔符,\0 的转义字符。

    这里还不是最终的 NSLog 字符串,可以看到 _NSLog 在 String Table 中的 Index 偏移为 0x000000AC。接下来继续查看 String Table

    NSLog 字符串存放的位置

    String Table 首地址0xD0A4 + 偏移地址 0xAc = 0xD150

    以上过程也就是 GitHub fishhook 的说明图,如下:

    查找过程

    小结:

    image.png

    相关文章

      网友评论

        本文标题:HOOK原理及介绍

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