Hook原理--逆向开发

今天咱们将继续讲解逆向开发工程另外一个重要内容--Hook原理讲解。Hook,能够中文译为“挂钩”或者“钩子”,逆向开发中改变程序运行的一种技术。按照以下过程进行讲解git

  1. Hook概述
  2. Hook技术方式
  3. fishhook原理及实例
  4. 符号表查看函数名称
  5. 总结

1、Hook概述

在逆向开发中是指改变程序运行流程的技术,经过Hook可让本身的代码运行在别人的程序中。须要了解其Hook原理,这样就可以对恶意代码攻击进行有效的防御。github

 

2、Hook技术方式

2.1 Method Swizzle方式

Method Swizzle 上次已经讲到,是利用OC的Runtime的特性,去动态改变SEL(方法编号)与IMP(方法实现)的对应关系,达到OC方法调用流程更改的目的。也是主要用于OC方法。算法

2.2 Cydia Substrate方式

Cydia Substrate 原名叫作Mobile SubStrate,主要做用为针对C函数,OC函数以及函数的地址进行Hook操做。而且有个很大的优点,Cydia Substrate 并非仅仅是针对iOS设计,Andriod同样也可使用。数组

2.2.1

Cydia Substrate定义了一系列的函数和宏,底层调用了objc的runtime和fishHook来替代目标函数或者系统方法。安全

其中有两个函数app

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

2.2.2 MobileLoader

MobileLoader主要用于加载第三方dylib运行的应用程序中。启动时MobileLoader会根据指定的第三方动态库加载进去,第三方动态库也是咱们写的破解程序。函数

2.2.3 safe mode

破解程序的本质在于dylib,寄生于别人程序进程中。可是系统进程一旦出现错误,可能会致使整个进程崩溃,也可能会致使iOS程序崩溃。在Cydia Substrate 中引入了安全模式,若是一旦错误,三方的dylib会被禁用,便于查错和修复。工具

2.3 fishHook

fishHook是Facebook提供一种动态修改连接Mach-O文件的工具。此利用Mach-O文件加载原理,经过修改非懒加载和懒加载两个表的指针达到C函数的Hook的目的。测试

今天咱们主要讲解第三种方式fishHook达到更改程序的目的。spa

 

3、fishhook原理及实例

3.1 概述

fishhook的源码地址为https://github.com/facebook/fishhook

fishhook的主要方法有两个还有一个结构体

查看代码结构为,将红色圈起来部分移入到代码中,便可使用fishhook来hook代码。

 

 3.2 实例

3.2.1 Demo1实例1

// rebinding 结构体的定义 // struct rebinding { // const char *name; // 须要 HOOK 的函数名称,字符串 // void *replacement; // 替换的新函数(函数指针,也就是函数名称) // void **replaced; // 保存原始函数指针变量/地址的指针(它是一个二级指针!) // }; // C 语言传参是值/址传递的,把它的值/址穿过去,就能够在函数内部修改函数指针变量的值

- (void)viewDidLoad {
    [super viewDidLoad];
     NSLog(@"123"); //rebinding结构体
    struct rebinding nslog;
    nslog.name = "NSLog";// 函数名称
    nslog.replacement = myNslog; // 新的函数指针
    nslog.replaced = (void *)&sys_nslog;// 保存原始函数地址的变量的指针
    //rebinding结构体数组
    struct rebinding rebs[1] = {nslog};
    /**
     * 存放rebinding结构体的数组
     * 数组的长度
     */
    rebind_symbols(rebs, 1);
}
//---------------------------------更改NSLog-----------
//函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
static void(*sys_nslog)(NSString * format,...);
//定义一个新的函数
void myNslog(NSString * format,...){
    format = [format stringByAppendingString:@"勾上了!\n"];
    //调用原始的
    sys_nslog(format);
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"点击了屏幕!!");
}

上面的代码运行结果以下:

3.2.2 Demo2实例2

void func(const char * str){
    NSLog(@"%s",str);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    //rebinding结构体
    struct rebinding nslog;
    nslog.name = "func";
    nslog.replacement = new_func;
    nslog.replaced = (void *)&old_func;
    //rebinding结构体数组
    struct rebinding rebs[1] = {nslog};
    /**
     * 存放rebinding结构体的数组
     * 数组的长度
     */
    rebind_symbols(rebs, 1);
}
//---------------------------------更改NSLog-----------
//函数指针
static void(*old_func)(const char * str);
//定义一个新的函数
void new_func(const char * str){
      NSLog(@"%s + 1",str);
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    func("哈哈");
}

运行结果以下:

从上面能够看出自定义的交换方法为何交换不了呢?首先能够确定的是代码是OK的,下面咱们讲解原理,为何自定义的方法不行呢?

 

 3.3 原理探究

Mach-O文件是如何加载的?

Dyld工具动态加载,加载MachO文件完成后,开始加载依赖的动态库,也就是经过上篇博客的image List 可看到相关的类库。

PIC(Promrammable Interrupt Controller)位置代码独立,由外设发出中断请求须要中断控制器来处理。

Mach-O文件内部调用系统函数时:

  • Mach-O _data段创建了一个指针(也就是符号,实现指向内部的函数调用,指向了外部的函数地址),指向了外部函数(dyld),可读可写,当Mach-O被加载进去,就会指向所指的函数。
  • Dyld会动态的绑定,将Mach-O中的data 段中指针指向了外部的函数,也是Dyld为何叫作动态绑定的缘由。

这也回答了上面的问题,为何内部/自定义的函数不能修改,只能修改Mach-O文件的外部函数,若是是另一个动态库或者须要动态符号绑定的就能够(符号表中能找到才能够实现)

 

下面咱们是真实查看内容,经过实例

利用第一个Demo来测试,运行起来,而后查看可执行文件,经过MachoView工具

 

从图2看出offset偏移地址为3028,也就是NSLog函数文件的偏移地址,懒加载此表时在Mach-O文件偏移地址+函数偏移的地址。

下面以Demo1查看,在Demo1打断点,查看Mach-O函数偏移地址,经过指令image list 第一个就是Mach-O内容和地址(本人上篇博客地址便可)

Mach-O在内存的偏移地址也就是Mach-O的真实地址,发现为 0x000000010a9c5000

经过上面红色加剧算法,计算Mach-O文件Data段的函数指针

发现执行完只有就会被绑定。NSLog函数文件就会被绑定。

下面再看一下,对于屏幕点击的,hook以下

前提是咱们去除ViewDidLoad方法里面的NSLog(@“123”)这句代码,运行代码,最后将断点断在touchesBegan里面,此时开始看地址和内容

截图的前两次打印是程序运行时,可是不曾点击touchesBegan,后两次是点击屏幕时断点进入到了里面,再看内容,打印的对象是NSLog仍是myNslog,经过上面发现是myNslog,说明Hook成功。

经过上面可看出,fishhook可以Hook c函数,是由于Mach-O文件特色,PIC位置代码独立造就了静态语言C也有动态的部分,以后经过Dyld进行动态绑定的时机,在这其中咱们就能够作手脚,替换自定义的方法。

fishhook是根据方法字符串的名字“NSLog”,它是怎么找到的呢?下面将讲解利用符号表查看函数名称字符串。

 

4、符号表查看函数名称

 再次查看Mach-O文件,查看懒加载表中的NSLog函数

懒加载表是和动态符号表是一一对应关系,经过上面发现NSLog函数时第一个,而对应的Dynamic Symbol table也是第一个,打开Dynamic Symbol table

查看Dynamic Symbol Table 第一个也是NSLog,查看Data值为7A,对应的十进制为122,而后到Symbols Table里面查看122,以下:

 

查看Symbols Table的data值为0000009B,而后在String Table Index去看函数偏移值为0000009B的内容,以下:

 

 为何选择00004F94查看NSLog呢,咱们从上面得知Symbols Table的data值为0000009B,而后加上String Table的函数第一个地址为00004F04,而后将0000009B + 00004F04 = 0X4F9F,最后看00004F94里面包含了0X4F9F,蓝色内容看出是NSLog内容,也就是找到啦。完美!!!

以上过程能够在fishhook中github上有说明图:

 

上面的说明图也就是经过符号表查看函数名称以及反过来也能够逆查的过程。配上说明图,方便你们熟悉流程。

 

5、总结

上面讲述了Hook的几种技术方式以及fishhook的原理探究,以及如何让别人的app实现本身的代码。下面咱们对此总结一下,写了一个本篇博客的整个过程便于你们整理,但愿对你们有所帮助加深理解。

相关文章
相关标签/搜索