Native Hook详细讲解
本文链接:https://blog.csdn.net/feather_wch/article/details/131737387
文章目录
1、Native Hook
是一种用于拦截某个native函数的调用,使得执行流程转向到自定义的代码中。
2、应用场景有哪些?
内存监控
线程监控
GC监控
3、实现方法分为三大类
- GOT/PLT hook
- Trap hook
- inline hook
4、GOT/PLT是什么?
Global Offset Table(全局偏移表)和 Procedure Linkage Table(过程链接表)
5、ELF是什么?
Linux文件格式,有四大类
- 可重定向文件 a.o
- 可执行文件 a.out
- so
- core dump文件
6、GOT/PLT hook大体思路
- 修改GOT表中目标函数的地址
- 让其跳转到我们的函数
- 我们函数执行完之后,执行原函数
7、inline hook
- 将目标函数的开头几条指令替换为跳转指令,跳转到我们的 hook 函数中。
- 在 hook 函数中执行被替换的指令,再跳回目标函数的剩余部分。
8、trap hook
- 在目标跳转指令前,增加trap,导致会触发SIGTRAP信号
- 信号处理函数里面修改PC值
- 执行我们的函数,执行完之后,修改PC值到原函数
Native hook是什么
Native hook 是一种用于截获进程对某个原生函数(native function)的调用,使得函数的执行流程转向我们实现的代码片段,从而实现我们所需要的功能¹。Native hook 技术在 Android 应用性能优化中有很多应用场景,比如监控内存、线程、GC 等²。
Native hook 的实现方式有多种,比较常见的有 GOT/PLT hook、Trap hook 和 Inline hook¹。这些方式的原理和优缺点可以参考这篇文章¹。
GOT/PLT hook
下面我给出一个使用 GOT/PLT hook 的例子,它是通过修改 GOT 表或 PLT 表中的函数地址来实现的³。
假设我们想要 hook libart.so 中的 pthread_create 函数,我们可以使用如下的代码:
#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>
#include <sys/mman.h>
#include <elf.h>
// 定义一个结构体,用于保存原始函数地址和 hook 函数地址
typedef struct {
void *original;
void *hook;
} HookStruct;
// 定义一个全局变量,用于保存 pthread_create 的地址信息
HookStruct pthread_create_hook;
// 定义一个新的 pthread_create 函数,用于替换原始函数
int pthread_create_hooked(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) {
printf("Hooked pthread_create\n");
// 调用原始函数
return ((int (*)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *))pthread_create_hook.original)(thread, attr, start_routine, arg);
}
// 定义一个函数,用于获取目标函数在 GOT 表中的地址
void* get_got_address(const char* library_name, const char* function_name) {
// 打开目标库文件
void* handle = dlopen(library_name, RTLD_NOW);
if (handle == NULL) {
printf("dlopen failed: %s\n", dlerror());
return NULL;
}
// 获取目标库文件的基址
Elf32_Ehdr* elf_header = (Elf32_Ehdr*)handle;
// 获取目标库文件的程序头表
Elf32_Phdr* program_header = (Elf32_Phdr*)((uint8_t*)handle + elf_header->e_phoff);
// 遍历程序头表,找到动态段
Elf32_Dyn* dynamic = NULL;
for (int i = 0; i < elf_header->e_phnum; i++) {
if (program_header[i].p_type == PT_DYNAMIC) {
dynamic = (Elf32_Dyn*)(program_header[i].p_vaddr + (uint32_t)handle);
break;
}
}
if (dynamic == NULL) {
printf("dynamic segment not found\n");
return NULL;
}
// 遍历动态段,找到符号表和重定位表
Elf32_Sym* symtab = NULL;
char* strtab = NULL;
Elf32_Rel* rel = NULL;
int rel_count = 0;
for (int i = 0; dynamic[i].d_tag != DT_NULL; i++) {
switch(dynamic[i].d_tag) {
case DT_SYMTAB:
symtab = (Elf32_Sym*)(dynamic[i].d_un.d_ptr + (uint32_t)handle);
break;
case DT_STRTAB:
strtab = (char*)(dynamic[i].d_un.d_ptr + (uint32_t)handle);
break;
case DT_JMPREL:
rel = (Elf32_Rel*)(dynamic[i].d_un.d_ptr + (uint32_t)handle);