fishhook鱼钩介绍(一)

以前了解过相关介绍fishhook的文章,没有细看,去年安全考试又考到了,而且第一次做题还做错了,因为印象中是可以hook方法的,那怎么可能只可以hook系统方法呢。。。

只有fishhook.h/fishhook.c两个文件,.h文件里就提供了2个方法,.c文件只有200多行,瞅瞅呗!

官方文档

fishhook是一个可以在模拟器、真机上的Mach-O文件进行动态重绑定符号的简单的库。效果类似于MacOS上的DYLD_INTERPOSE。在FaceBook,我们发现它对于调试、跟踪libSystem的调用非常有效(比如,审计文件描述符的双重关闭问题)

用法

项目中加入fishhook.h/fishhook.c,

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "fishhook.h"

static int (*orig_close)(int);

int my_close(int fd) {
    printf("Calling real close(%d)\n", fd);
   
	return orig_close( fd );
}

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        rebind_symbols((struct rebinding[1])close, 1);
        int fd = open(argv[0], O_RDONLY);
        uint32_t magic_number = 0;
        read(fd, &magic_number, 4);
        printf("Mach-O Magic Number: %x \n", magic_number);
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
它是怎么工作的呢

dyld通过更新Mach-O二进制文件中__DATA段的特定节中的指针来绑定lazy 和 non-lazy符号。fishhook通过传给rebind_symbols函数的每个符号名称来确定需要更新的位置,然后写入相应的替换来重新绑定这些符号。

对于给定的镜像,__DATA段可能包含与动态符号绑定相关的两个节:__nl_symbol_ptr 、 __la_symbol_ptr。__nl_symbol_ptr是一个非延迟绑定数据的指针数组(这些在库加载时绑定),而__la_symbol_ptr是一个关于导入函数的指针数组,通常在第一次调用这个符号时由dyld_stub_binder例程来填入(也可能调用dyld在启动时绑定).

为了在某一个节中找到与特定位置对应的符号名称,我们需要跳转过几个间接层。对于两个相关的节,节头部(<mach-o/loader.h>定义了结构体sections)提供一个偏移量(reserved1字段)到间接符号表中。二进制文件中的__LINKEDIT段中的间接符号表只是符号表(也在__LINKEDIT中)的索引数组,与non-lazy、lazy符号节中的指针顺序相同。因此,根据结构体节nl_symbol_ptr,该节中第一个地址的符号表中的相应索引是indirect_symbol_table[nl_symbol_ptr->reserved1]。

符号表是一个元素为结构体nlists的数组(参见<mach-o/nlist.h>),每个nlist都包含一个指向__LINKEDIT中字符串表的索引,字符串表中存储量实际的符号名称。因此,对于每个__nl_symbol_pt、 __la_symbol_ptr指针,可以找到相应的符号,然后找到相应的字符串与请求的符号名称进行对比,如果匹配,则替换掉相应节中的指针。

从lazy或non-lazy指针表中查找指定条目名称的过程 如下图所示: _config.yml

一些知识点

dylib

dylib(dynamic library)动态库,dylib在程序静态编译时并不被链接进目标代码中,而是在程序运行时完成链接。(Windows下的DLL文件,Linux下的so文件)

dyld

dyld(the dynamic link editor)动态链接器,是用来加载动态库的库,开源。Mac、iOS上,程序的启动时,在系统内核为程序启动做好准备之后,执行由内核态切换到用户态,有dyld完成后面的加载工作:dyld会将App依赖的动态库和App文件加载到内存后执行。

链接器的作用主要是将符号绑定到地址上,dyld加载动态库有两种方式,1. 程序启动加载时绑定 2.符号第一次被用到时绑定

dyld先加载Mach-O文件,根据Mach-O文件里undefined符号加载对应的动态库,加载后,将undefined符号绑定到动态库里对应的地址上。

进程内存布局

_config.yml 堆和文件映射段的内存是动态分配的,比如使用c标准库的malloc和mmap可以分别在堆和文件映射段动态分配内存。

用户态/内核态切换

_config.yml Mac、iOS采用的是混合型内核,Mach微内核和BSD层。每个进程都有独立的虚拟内存,每个虚拟内存中的内核地址都关联相同的物理内存。

ALSR

ASLR(Address space layout randomization)地址空间布局随机化,是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,增加攻击者预测目的地址的难度。

元数据

元数据(MetaData),描述数据的数据,对数据及信息资源的描述性信息。

脑经急转弯:c标准库中free方法是怎么释放内存的?malloc需要传入指定申请多少内存,为什么free不需要传入指定需要释放多少内存?

MacOS上的DYLD_INTERPOSE

dyld-interposing.h

interpose(介入、插入),dyld支持动态库功能,提供一个新的__DATA区__interpose,在这个区中列出替换函数和被替换函数。通过DYLD_INSERT_LIBRARIES环境变量指定动态库的路径,在执行程序加载时会将该动态库插入。

试了一波,没起到作用😮‍💨

Mach-O文件

Mach-O主要由3个部分组成:

  1. Mach-O头部(描述Mach-O的CPU架构、文件类型及加载命令等信息)

  2. 加载指令(描述文件中数据的具体组织结构,不同的数据类型使用不同的加载指令来表示)

  3. 数据区(其中的每个段拥有一个或者多个节,用于存放数据和代码)

几个小工具

MachOView、 Synalyze It! (十六进制编辑器)、Hopper Disassembler

符号绑定过程
  1. 非延迟绑定是在动态库链接期间立即进行绑定,解析出符号的真实地址
  2. 延迟绑定是符号被用到的时候才进行绑定

延迟绑定过程:

  1. 从Section64(__TEXT,__text)中调用到Section64(__TEXT,__stubs),再从__stubs找到__la_symbol_ptr,根据__la_symbol_ptr中指定符号条目的内容找到__stub_helper
  2. 执行__stub_helper会跳转到__nl_symbol_ptr调用dyld_stub_binder函数,最终找到指定符号的真实地址
  3. dyld_stub_binder将地址写入__la_symbol_ptr相应的条目中
  4. dyld_stub_binder跳转到指定符号的真实地址执行(之后再访问指定符号时,stub中jump指令会直接跳转到符号的真实地址)
头文件

#include <mach-o/loader.h>

struct mach_header_64 {
	uint32_t	magic;		/* mach 魔数标识符r */
	cpu_type_t	cputype;	/* cpu 类型标识符 */
	cpu_subtype_t	cpusubtype;	/* cpu子类型标识符 */
	uint32_t	filetype;	/* 文件类型 */
	uint32_t	ncmds;		/* 加载指令的条数 */
	uint32_t	sizeofcmds;	/* 所有加载指令的总大小 */
	uint32_t	flags;		/* 标志 */
	uint32_t	reserved;	/* 保留字段 */
};
// 加载指令结构体
struct load_command {
	uint32_t cmd;		/* 加载指令类型 */
	uint32_t cmdsize;	/* 指令大小 */
};


#define	SEG_PAGEZERO	"__PAGEZERO"	/* 当是MH_EXECUTE文件时,用来捕获空指针 */
#define	SEG_TEXT	"__TEXT"	/* 代码/只读数据段 */
#define	SEG_DATA	"__DATA"	/* 数据段 可读写 */
#define	SEG_OBJC	"__OBJC"	/* objective-C runtime 段 */
#define	SEG_LINKEDIT	"__LINKEDIT"	/* 包含需要被dyld使用的符号表、字符串表、重定位表等结构 */
				
源码
TODO
				
反fishhook

TODO

反反fishhook

TODO

小结

  1. fishhook能够rebind的符号必需在动态库里,本地符号是没办法重绑定的,所以的确不是用来hook自定义方法

引用源

Written on April 19, 2022