Skip to content
objc_msgSend之精简且完整的hook核心功能,并且还有详细注释
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
ObjCHook.xcodeproj 增加打印调用栈 Apr 2, 2019
ObjCHook clean thread share data after release method called Apr 24, 2019
.gitignore First commit Mar 28, 2019
README.md add logo Apr 29, 2019

README.md

精简详解,完整核心功能

1.汇编入门:

10分钟入门
    - http://blackteachinese.com/2017/07/12/arm64/
iOS开发汇编入门
    - https://blog.cnbluebox.com/blog/2017/07/24/arm64-start/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
小例子入门
    - https://juejin.im/post/5aabcae1f265da238d507a68
理解 iOS ARM
    - https://www.jianshu.com/p/544464a5e630?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

2.Mach-O 理解:

Mach-O文件结构
    - https://hawk0620.github.io/blog/2018/03/22/study-mach-o-file/
探秘Mach-O
    - https://www.jianshu.com/p/1f22d1e667e3

3. fishhook

- https://www.jianshu.com/p/4d86de908721

4. objc_msgSend hook 完整精简代码(详细的注释)

hook_core_arm64.c实现了对 arm64架构的objc_msgSend hook


//
//  hook_core_arm64.c
//  ObjCHook
//
//  Created by legendry on 2019/4/1.
//  Copyright © 2019 legendry. All rights reserved.
//

#include "hook_core_arm64.h"
#import "fishhook.h"
#import <objc/runtime.h>
#import <objc/message.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dispatch/dispatch.h>
#include "hook_utils.h"
#include "hook_core.h"

#if __aarch64__
static id (*orig_objc_msgSend)(void);
// 将参数value(地址)传递给x12寄存器
// blr开始执行
#define call(b, value) \
__asm volatile ("mov x12, %0\n" :: "r"(value)); \
__asm volatile (#b " x12\n");

/// 依次将寄存器数据入栈
#define save() \
__asm volatile ( \
"stp x8, x9, [sp, #-16]!\n" \
"stp x6, x7, [sp, #-16]!\n" \
"stp x4, x5, [sp, #-16]!\n" \
"stp x2, x3, [sp, #-16]!\n" \
"stp x0, x1, [sp, #-16]!\n" \
);

/// 依次将寄存器数据出栈
#define load() \
__asm volatile ( \
"ldp x0, x1, [sp], #16\n" \
"ldp x2, x3, [sp], #16\n" \
"ldp x4, x5, [sp], #16\n" \
"ldp x6, x7, [sp], #16\n" \
"ldp x8, x9, [sp], #16\n" );

/// 程序执行完成,返回将继续执行lr中的函数
#define ret() __asm volatile ("ret\n");

static void hook_objc_msgSend() {
    /// before之前保存objc_msgSend的参数
    save()
    /// 将objc_msgSend执行的下一个函数地址传递给before_objc_msgSend的第二个参数x0 self, x1 _cmd, x2: lr address
    __asm volatile ("mov x2, lr\n");
    /// 执行before_objc_msgSend
    call(blr, &before_objc_msgSend)
    /// 恢复objc_msgSend参数,并执行
    load()
    call(blr, orig_objc_msgSend)
    /// after之前保存objc_msgSend执行完成的参数
    save()
    /// 调用 after_objc_msgSend
    call(blr, &after_objc_msgSend)
    /// 将after_objc_msgSend返回的参数放入lr,恢复调用before_objc_msgSend前的lr地址
    __asm volatile ("mov lr, x0\n");
    /// 恢复objc_msgSend执行完成的参数
    load()
    /// 方法结束,继续执行lr
    ret()
}

///开始
void arm64_start_objc_msgSend_hook() {
    start_hook(hook_objc_msgSend, (void *)(&orig_objc_msgSend));
}
void arm64_stop_objc_msgSend_hook() {
    
}
#endif

hook_core.c 实现了对调用堆栈的统计与打印

//
//  hook_core.c
//  ObjCHook
//
//  Created by legendry on 2019/4/1.
//  Copyright © 2019 legendry. All rights reserved.
//

#include "hook_core.h"
#include "hook_utils.h"
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <objc/runtime.h>
#include <dispatch/dispatch.h>
#include "fishhook.h"

static pthread_key_t _thread_key;

typedef struct {
    uintptr_t lr;
    SEL _cmd;
    /// 开始时间
    uint64_t start_time;
    /// 结束时间
    uint64_t end_time;
    /// 类名
    char *class_name;
    /// 函数名
    char *method_name;
    /// 调用深度
    uint8_t dept;
    /// 是否是类方法
    bool is_class_method;
}call_stack;

typedef struct {
    /// 调用栈
    call_stack *cs;
    /// 调用栈第一个: 每一个objc_msgSend开始
    call_stack *first;
    uint index;
    /// 当前数据总大小
    uint size;
    /// 总开销
    uint64_t cost;
    /// 是否是主线程
    bool is_main_thread;
    // 调用栈信息
    char *stack_info;
    uint64_t stack_info_size;
}common_data;

///获取线程共享数据
common_data *get_thread_call_stack() {
    /// pthread_getspecific 获取指定key的同一线程中不同方法的共享数据
    common_data *_cd = (common_data *)pthread_getspecific(_thread_key);
    if (_cd == NULL) {
        _cd = (common_data *)malloc(sizeof(common_data));
        _cd->size = 10;
        /// 调用栈
        _cd->cs = (call_stack *)malloc(sizeof(call_stack) * _cd->size);
        /// 当前调用栈索引
        _cd->index = 0;
        _cd->cost = 0;
        /// 存放共享数据
        pthread_setspecific(_thread_key, (void *)_cd);
    }
    _cd->is_main_thread = pthread_main_np();
    return _cd;
}
/// 如果调用栈大小不够,重新分配空间
void realloc_call_stack_memery_ifneed() {
    common_data *_cd = get_thread_call_stack();
    /// 重新分配空间
    if (_cd->index >= _cd->size) {
        _cd->size += 10;
        _cd->cs = (call_stack *)realloc(_cd->cs, sizeof(call_stack) * _cd->size);
    }
}
/// 打印调用信息
char *dump_call_stack(call_stack *_cs) {
    uint64_t cost = _cs->end_time - _cs->start_time;
    if (cost > 100 && !hook_has_prefix(_cs->class_name, "OS_")) {
        int _count = _cs->dept;
        while (_count > 0) {
            printf(" ");
            _count --;
        }
        char *space = (char *)malloc(_cs->dept);
        memset(space, ' ', _cs->dept);
        char *str = (char *)malloc(1024);
        sprintf(str, "%s %c [%s %s] %lld ms\n", space, '-', _cs->class_name, _cs->method_name, cost);
        free(space);
        return str;
    }
    return NULL;
}
/// 打印一个objc_msgSend完整调用信息
void dump_method() {
    common_data *_cd = get_thread_call_stack();
    _cd->cost = _cd->first->end_time - _cd->first->start_time;
    if(_cd->cost > 400 && _cd->first && !hook_has_prefix(_cd->first->class_name, "OS_")) {
        printf("[%s][%s: %s]: %lld ms\n", _cd->is_main_thread ? "主" : "子", _cd->first->class_name, _cd->first->method_name, _cd->cost);
        printf("%s \n", _cd->stack_info);
    }
    
}
/// hook 之前
void before_objc_msgSend(id object, SEL _cmd, uintptr_t lr) {
    realloc_call_stack_memery_ifneed();
    common_data *_cd = get_thread_call_stack();
    /// 保存当前调用栈下一个函数方法执行地址
    call_stack *_cs =  &(_cd->cs[_cd->index]);
    /// 正常执行程序的下一条指令,保存起来在hook完之后需要恢复到hook前的状态,程序lr寄存器能正常执行
    _cs->lr = lr;
    /// 保存执行的SEL
    _cs->_cmd = _cmd;
    /// 保存执行前的时间
    _cs->start_time = hook_getMillisecond();
    /// 保存当前调用栈的深度
    _cs->dept = _cd->index;
    /// 保存当前对象的类名
    _cs->class_name = (char *)hook_getObjectClassName(object);
    /// 保存当前调用的方法名
    _cs->method_name = (char *)_cs->_cmd;
    /// 判断是否是元类的实例方法,即类的类方法
//    Class __cls = object_getClass(object);
//    _cs->is_class_method = class_isMetaClass(__cls);
    /// index为0表示调用栈顶,即objc_msgSend初始调用(objc_msgSend内部还会调用其它objc_msgSend)
    if(_cd->index == 0) {
        _cd->first = _cs;
        _cd->stack_info_size = 1024;
        if(_cd->stack_info) free(_cd->stack_info);
        _cd->stack_info = (char *)malloc(1024);
    }
    /// 入栈
    /// 调用栈前进
    _cd->index ++;
}
/// hook 之后
uintptr_t after_objc_msgSend() {
    common_data *_cd = (common_data *)pthread_getspecific(_thread_key);
    /// 后退
    _cd->index --;
    /// 获取即将完成的调用
    call_stack *stack = &(_cd->cs[_cd->index]);
    stack->end_time = hook_getMillisecond();
//    _cd->cost += (stack->end_time - stack->start_time);
    /// 打印单个方法执行信息
    if(_cd->index > 0) {
        char *_stack_info = dump_call_stack(stack);
        if(_stack_info) {
            uint64_t _stack_info_count = strlen(_stack_info);
            uint64_t cur_stack_info_size = strlen(_cd->stack_info);
            // 判断是否超出当前容量
            if(_stack_info_count + cur_stack_info_size > _cd->stack_info_size) {
                //扩容
                _cd->stack_info_size = _stack_info_count + cur_stack_info_size + 1024;
                _cd->stack_info = (char *)realloc(_cd->stack_info, _cd->stack_info_size);
            }
            memcpy(_cd->stack_info + cur_stack_info_size, _stack_info, _stack_info_count);
            free(_stack_info);
        }
    }
    /// 一个函数调用完成
    /// 打印所有方法执行信息
    if (_cd->index == 0){
        dump_method();
        _cd->stack_info_size = 1024;
        if(_cd->stack_info) free(_cd->stack_info);
        _cd->stack_info = (char *)malloc(_cd->stack_info_size);
    }
    /// 将下一条函数指令返回,并存放到寄存器x0
    return stack->lr;
}
/// 释放与线程共享数据的相关资源
void release_thread_stack() {
    printf("release \n");
    common_data *_cd = (common_data *)pthread_getspecific(_thread_key);
    if(!_cd) return;
    if (_cd->cs) free(_cd->cs);
    if (_cd->stack_info) free(_cd->stack_info);
    free(_cd);
}

void start_hook(void *hook_objc_msgSend, void **origin_objc_msgSend) {
    printf("hook_start\n");
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        pthread_key_create(&_thread_key, &release_thread_stack);
        /// 替换系统的函数: 因为apple使用了系统函数动态绑定技术,所以可以hook系统函数,本地函数编译时地址已固定
        /// rebinding: 第一个参数,需要hook的系统函数名称
        ///            第二个参数,替换系统的函数
        ///            第三个参数,指向被hook函数,调用orig_objc_msgSend相当于调用原始函数
        rebind_symbols((struct rebinding[1]){ "objc_msgSend", hook_objc_msgSend, origin_objc_msgSend}, 1);
    });
}

You can’t perform that action at this time.