iOS&mac OS中hook框架的梳理

2018-09-17 10:21:57 浏览数 (1)

本文梳理了ios或osx中可以用于hook的框架及其使用,对于C/C 方法,进行了私有和系统方法的区分阐述,本文仅针对hook框架做讨论,对于实验中用到的注入、签名等不作阐述。

0x01 背景:要hook的代码,以下是测试demo

本文我们会对一个编写的测试mac app进行hook,其中mac app的主要代码如下:

代码语言:txt复制
#import "ViewController.h"

int cfunc(int x,int y,int z) {
    return x   y   z;
}

@implementation ViewController
- (IBAction)button:(id)sender {
    [self ocFunc];
    NSLog(@"%@", [NSString stringWithFormat:@"%zu",_length]);
}

-(void)ocFunc{
    NSLog(@"haha");
    _length = cfunc(1, 2, 3);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    _length = cfunc(1, 2, 3);
    // Do any additional setup after loading the view.
}

接下来要对cfunc和ocFunc进行hook。

0x02 CydiaSubstrate Hook

首先要来的自然是大名鼎鼎的Jay Freeman(saurik)写的CydiaSubstrate,iOS7越狱之前名为 MobileSubstrate(简称为MS或MS框架)。

MobileHooker组件主要提供了MSHookMessageEx和MSHookFunction两个函数针对不同语言的inline hook功能,其中MSHookMessageEx负责用来hook Objective-C函数,MSHookFunction负责用来hook C/C 函数。

Objective-C函数的hook

原理:MSHookMessageEx对于ObjC函数采用的也是method swizzle的方法,主要是Objetive-C的runtime机制,可以在ObjC方法时动态采用class_replaceMethod等runtime函数替换其实现。

代码语言:txt复制
void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old);

其中第一个参数_class为要Hook的Objective-C函数的类名;第二个参数message为要Hook的Objective-C函数的message;第三个参数hook为hook后新的对应该message的执行逻辑,即替换后的函数地址;第四个参数old为对应该message的原函数的地址,若无需调用原函数则该参数可以设为NULL。

对ocFunc进行hook的动态库代码如下:

代码语言:txt复制
@class ViewController;
static void (*origin_ViewController_ocFunc)(id self,SEL _cmd);
static void new_ViewController_ocFunc(id self,SEL _cmd){
    NSLog(@"oc func hook");
    origin_ViewController_ocFunc(self,_cmd);
}

static void __attribute__((constructor)) initialize(void) {
    // 初始化方法里进行替换
    MSHookMessageEx(objc_getClass("ViewController"),  @selector(ocFunc), (IMP)&new_ViewController_ocFunc, (IMP *)&origin_ViewController_ocFunc);
}

可以成功hook

C/C 方法(私有或系统)的hook

原理:MSHookFunction对于C函数是在函数的开头修改了汇编指令,使其跳转到新的实现,执行完成后再返回执行原指令。

代码语言:txt复制
void MSHookFunction(void *symbol, void *hook, void **old);

其中第一个参数为所要Hook的函数地址,值得注意的是该地址不一定限于函数头,也可以是函数内部的任一代码地址;第二个参数为Hook后要替换的函数地址;第三个参数为指向Hook地址的指针,用来保存被Hook函数替换掉的汇编指令方便执行完自己的代码逻辑后能够继续执行原函数的逻辑,若不需要调用原函数,则此处可以设为“NULL”。

私有方法

话不多说,上代码,来hook我们自己写的C方法

代码语言:txt复制
static int (*orig_cfunc)(int x,int y,int z);
int new_cfunc(int x,int y,int z){
    NSLog(@"c func hook");
    return orig_cfunc(x,y,z);
}

static void __attribute__((constructor)) initialize(void) {

    MSHookFunction(MSFindSymbol(NULL,"_cfunc"), (void *)&new_cfunc, (void **)&orig_cfunc);
}

注意这里在调用**MSFindSymbol**时传入的是**'_cfunc'**

另外注意:

  1. 新方法的函数地址这里也可以使用new_cfunc,因为new_cfunc和&new_cfunc地址是一样的,new_cfunc是函数的首地址,它的类型是void (),&new_cfunc表示一个指向函数new_cfunc这个对象的地址,它的类型是void (*)(),因此new_cfunc和&new_cfunc所代表的地址值是一样的,但类型不一样
  2. 第三个参数必须使用&orig_cfunc,因为这里要用的是函数地址,用来保存被Hook函数替换掉的汇编指令方便执行完自己的代码逻辑后能够继续执行原函数的逻辑,使用orig_cfunc是无效的。
系统方法

以上是hook私有的c方法,当然也可以hook系统的方法,比如官网的示例

代码语言:txt复制
void *(*oldConnect)(int, const sockaddr *, socklen_t);

void *newConnect(
    int socket, const sockaddr *address, socklen_t length) {
    if (address->sa_family == AF_INET) {
        sockaddr_in *address_in = address;
        if (address_in->sin_port == htons(6667)) {
            sockaddr_in copy = *address_in;
            address_in->sin_port = htons(7001);
            return oldConnect(socket, &copy, length);
        }
    }

    return oldConnect(socket, address, length);
}

MSHookFunction(&connect, &newConnect, &oldConnect);

0x03 fishhook

fishhook是Facebook提供的一个动态修改链接mach-O文件的工具,可以用来hook C/C 方法。

不能hook私有方法

这里也尝试对cfunc进行了hook,发现并未生效,代码如下:

代码语言:txt复制
static int (*orig_cfunc)(int x,int y,int z);
int new_cfunc(int x,int y,int z){
    NSLog(@"c func hook");
    return (*orig_cfunc)(x,y,z);
}
static void __attribute__((constructor)) initialize(void) {

    rebind_symbols((struct rebinding[1]){{"cfunc", new_cfunc, (void *)&orig_cfunc}}, 1);

}

结合fishhook的原理,可以知道fishhook是不能hook私有C方法的。

hook系统方法

但是hook系统的方法还是很好使的,代码如下:

代码语言:txt复制
static void (*orig_NSLog)(NSString *format, ...);
void(new_NSLog)(NSString *format, ...) {
    va_list args;
    if(format) {
        va_start(args, format);
        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
        orig_NSLog(@"hook:%@", message);
        va_end(args);
    }
}

static void __attribute__((constructor)) initialize(void) {
    rebind_symbols((struct rebinding[1]){{"NSLog", new_NSLog, (void *)&orig_NSLog}}, 1);
}

0x04 method swizzle

method swizzle的原理主要是Objetive-C的runtime机制,可以在ObjC方法时动态采用class_replaceMethod等runtime函数替换其实现。

由于是基于runtime的,所以C/C 方法是不生效的,仅针对Objective-C方法有效,swift中不是基于OC的对象也不会生效。

话不多说,直接上一个hook viewWillAppear方法的例子

代码语言:txt复制
#import <objc/runtime.h>

@implementation UIViewController (Swizzle)

 (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
       // Class class = object_getClass((id)self);
        NSLog(@"%@",class);
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(my_viewWillAppear:);
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), 	method_getTypeEncoding(swizzledMethod));
        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), 	method_getTypeEncoding(originalMethod));
        }
        else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        } });
}
-(void)my_viewWillAppear:(BOOL)animated
{
    [self my_viewWillAppear:animated];
    NSLog(@"%@",[self class]);
}


@end

目前就针对这三种进行了实验和梳理,后续如有遇到其它再进行补充,也欢迎了解其他框架的大佬进行补充和指正。

参考

https://www.jianshu.com/p/3479f9632a6f

https://www.jianshu.com/p/98c97a32da29

0 人点赞