Giter VIP home page Giter VIP 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

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

objc_msgsend_hook's Issues

为什么要保存x8、x9?

#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"
);

启动奔溃

image
我觉得是你在before的时候不为0的时候没有释放first对象

call的宏汇编有误

#define call(b, value)
__asm volatile ("stp x8, x9, [sp, #-16]!\n");
__asm volatile ("mov x12, %0\n" :: "r"(value));
__asm volatile ("ldp x8, x9, [sp], #16\n");
__asm volatile (#b " x12\n");

通过 blr 指令 跳转执行 before_objc_msgSend 函数。这里会先保存 x8、x9 寄存器的值,原因是__asm volatile ("mov x12, %0\n" :: "r"(value)) 执行命令过程中会通过 x8 来保存函数地址,再进行跳转,所以这里会先要保存 x8,和步骤1相同,栈指针移动必须满足 SP Mod 16 = 0 的条件,所以 x9 也被保存。执行完之后 x8、x9 恢复。

作者:人类买水精华
链接:https://juejin.im/post/5d29e230f265da1b67213c06
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

setFrame:莫名崩溃

大神你好 我发现无论是戴明的DEMO和你的DEMO都有一个问题,只要调用setFrame:方法 就会莫名其妙的崩溃。我正在调试,但是没什么思路。

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.