Giter Site home page Giter Site logo

objc_msgsend_hook's Introduction

精简详解,完整核心功能

1.汇编入门:

10分钟入门
    - https://blackteachinese.github.io/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);
    });
}

objc_msgsend_hook's People

Contributors

czqasn6 avatar czqasngit avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.