{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/native-hook-techniques-overview/",
  "atlas": "https://www.pystone.net/?node=native-hook-techniques-overview#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/native-hook-techniques-overview.md",
  "context": "https://www.pystone.net/notes/native-hook-techniques-overview.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:native-hook-techniques-overview",
  "slug": "native-hook-techniques-overview",
  "title": "Native hook技术原理及应用",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "Hook 的核心原理是： 1. 劫持控制流 ：通过修改代码或数据结构，使程序的执行跳转到自定义代码。 2. 原始功能保留 ：部分情况下，需要保留原始函数功能，便于继续调用。",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "0e77623d72cf9f073c73feb20afe72101f0100555f584556da15869905700174",
  "assets": [
    {
      "reference": "assets/image-20250106200653584.png",
      "url": "/media/ed90bdc582a0d0809e26.png",
      "mediaType": "image/png",
      "contentHash": "ed90bdc582a0d0809e26f2fcce15dc3371808657c85c82991dfc5eff63be14ca",
      "byteLength": 43010,
      "width": 814,
      "height": 391
    }
  ],
  "headings": [
    {
      "depth": 1,
      "text": "Native hook技术原理及应用",
      "anchor": "native-hook技术原理及应用",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#native-hook%E6%8A%80%E6%9C%AF%E5%8E%9F%E7%90%86%E5%8F%8A%E5%BA%94%E7%94%A8"
    },
    {
      "depth": 2,
      "text": "1. 基于 PLT/GOT 的 Hook",
      "anchor": "1-基于-pltgot-的-hook",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#1-%E5%9F%BA%E4%BA%8E-pltgot-%E7%9A%84-hook"
    },
    {
      "depth": 5,
      "text": "PLT/GOT 表概念",
      "anchor": "pltgot-表概念",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#pltgot-%E8%A1%A8%E6%A6%82%E5%BF%B5"
    },
    {
      "depth": 3,
      "text": "原理与代码",
      "anchor": "原理与代码",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#%E5%8E%9F%E7%90%86%E4%B8%8E%E4%BB%A3%E7%A0%81"
    },
    {
      "depth": 3,
      "text": "相关接口说明",
      "anchor": "相关接口说明",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#%E7%9B%B8%E5%85%B3%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E"
    },
    {
      "depth": 2,
      "text": "2. Inline Hook",
      "anchor": "2-inline-hook",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#2-inline-hook"
    },
    {
      "depth": 3,
      "text": "实现步骤",
      "anchor": "实现步骤",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#%E5%AE%9E%E7%8E%B0%E6%AD%A5%E9%AA%A4"
    },
    {
      "depth": 3,
      "text": "示例说明",
      "anchor": "示例说明",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#%E7%A4%BA%E4%BE%8B%E8%AF%B4%E6%98%8E"
    },
    {
      "depth": 2,
      "text": "适用范围",
      "anchor": "适用范围",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#%E9%80%82%E7%94%A8%E8%8C%83%E5%9B%B4"
    },
    {
      "depth": 2,
      "text": "1. Hook 技术适用的函数类型",
      "anchor": "1-hook-技术适用的函数类型",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#1-hook-%E6%8A%80%E6%9C%AF%E9%80%82%E7%94%A8%E7%9A%84%E5%87%BD%E6%95%B0%E7%B1%BB%E5%9E%8B"
    },
    {
      "depth": 3,
      "text": "1.1 可以被 Hook 的函数",
      "anchor": "11-可以被-hook-的函数",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#11-%E5%8F%AF%E4%BB%A5%E8%A2%AB-hook-%E7%9A%84%E5%87%BD%E6%95%B0"
    },
    {
      "depth": 3,
      "text": "1.2 无法直接被 Hook 的函数",
      "anchor": "12-无法直接被-hook-的函数",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#12-%E6%97%A0%E6%B3%95%E7%9B%B4%E6%8E%A5%E8%A2%AB-hook-%E7%9A%84%E5%87%BD%E6%95%B0"
    },
    {
      "depth": 2,
      "text": "2. Hook 技术的适用场景与限制",
      "anchor": "2-hook-技术的适用场景与限制",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#2-hook-%E6%8A%80%E6%9C%AF%E7%9A%84%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF%E4%B8%8E%E9%99%90%E5%88%B6"
    },
    {
      "depth": 3,
      "text": "2.1 Hook 适用场景",
      "anchor": "21-hook-适用场景",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#21-hook-%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF"
    },
    {
      "depth": 3,
      "text": "2.2 Hook 技术的限制与挑战",
      "anchor": "22-hook-技术的限制与挑战",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#22-hook-%E6%8A%80%E6%9C%AF%E7%9A%84%E9%99%90%E5%88%B6%E4%B8%8E%E6%8C%91%E6%88%98"
    },
    {
      "depth": 4,
      "text": "(1) 不可见符号",
      "anchor": "1-不可见符号",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#1-%E4%B8%8D%E5%8F%AF%E8%A7%81%E7%AC%A6%E5%8F%B7"
    },
    {
      "depth": 4,
      "text": "(2) ASLR（地址空间随机化）",
      "anchor": "2-aslr地址空间随机化",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#2-aslr%E5%9C%B0%E5%9D%80%E7%A9%BA%E9%97%B4%E9%9A%8F%E6%9C%BA%E5%8C%96"
    },
    {
      "depth": 4,
      "text": "(3) 函数体覆盖问题",
      "anchor": "3-函数体覆盖问题",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#3-%E5%87%BD%E6%95%B0%E4%BD%93%E8%A6%86%E7%9B%96%E9%97%AE%E9%A2%98"
    },
    {
      "depth": 4,
      "text": "(4) 多线程环境",
      "anchor": "4-多线程环境",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#4-%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%8E%AF%E5%A2%83"
    },
    {
      "depth": 4,
      "text": "(5) SELinux 和权限限制",
      "anchor": "5-selinux-和权限限制",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#5-selinux-%E5%92%8C%E6%9D%83%E9%99%90%E9%99%90%E5%88%B6"
    },
    {
      "depth": 2,
      "text": "现有工具",
      "anchor": "现有工具",
      "citation": "https://www.pystone.net/notes/native-hook-techniques-overview/#%E7%8E%B0%E6%9C%89%E5%B7%A5%E5%85%B7"
    }
  ],
  "claims": [],
  "outgoing": [
    {
      "id": "note:elf-file-format",
      "title": "ELF文件",
      "url": "https://www.pystone.net/notes/elf-file-format/",
      "atlas": "https://www.pystone.net/?node=elf-file-format#knowledge-atlas",
      "label": "ELF文件",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "当前段落中的“ELF文件”导航项"
    }
  ],
  "incoming": [
    {
      "id": "note:programming-languages-and-runtimes",
      "title": "编程语言与运行时",
      "url": "https://www.pystone.net/notes/programming-languages-and-runtimes/",
      "atlas": "https://www.pystone.net/?node=programming-languages-and-runtimes#knowledge-atlas",
      "label": "编程语言与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "本层中的“Native hook技术原理及应用”导航项",
      "citation": "https://www.pystone.net/notes/programming-languages-and-runtimes/#%E6%9C%AC%E5%B1%82"
    },
    {
      "id": "note:elf-file-format",
      "title": "ELF文件",
      "url": "https://www.pystone.net/notes/elf-file-format/",
      "atlas": "https://www.pystone.net/?node=elf-file-format#knowledge-atlas",
      "label": "ELF文件",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "以下sections与Native hook技术关系比较大：",
      "citation": "https://www.pystone.net/notes/elf-file-format/#sections"
    }
  ],
  "contentMarkdown": "Hook 的核心原理是：\n1. **劫持控制流**：通过修改代码或数据结构，使程序的执行跳转到自定义代码。\n2. **原始功能保留**：部分情况下，需要保留原始函数功能，便于继续调用。\n\nHook 的实现方式可以分为：\n- **函数跳转修改**：修改函数入口地址或 PLT/GOT 表。\n- **动态库劫持**：加载时优先链接自定义库或重定向符号。\n\n相关知识：\n[ELF文件](5%20信息技术领域/编程语言及特性/ELF文件.md)\n\n# Native hook技术原理及应用\n## **1. 基于 PLT/GOT 的 Hook**\n\n##### **PLT/GOT 表概念**\n\n在 Linux 下的 ELF 文件中：\n- **PLT (Procedure Linkage Table)**:\n    - 是程序中用于动态链接函数的跳转表。\n    - 函数调用时，会先跳到 PLT，然后通过 GOT 获取实际函数地址。\n- **GOT (Global Offset Table)**:\n    - 存储动态链接函数的实际地址。\n    - 在程序运行时由动态链接器填充。\n通过修改 **GOT** 中函数指针的值，可以将函数调用重定向到自定义的代码。\n\n### **原理与代码**\n- 查找到目标函数的 **GOT 表地址**。\n- 将 GOT 中存储的目标函数地址替换为自定义函数地址。\n```c\n#define _GNU_SOURCE\n#include <stdio.h>\n#include <stdlib.h>\n#include <dlfcn.h>\n#include <string.h>\n\n// 定义原始函数指针\nstatic int (*original_printf)(const char *format, ...) = NULL;\n\n// 自定义的 hook 函数\nint hooked_printf(const char *format, ...) {\n    printf(\"[HOOKED] \");\n    // 调用原始 printf\n    va_list args;\n    va_start(args, format);\n    int result = original_printf(format, args);\n    va_end(args);\n    return result;\n}\n\nvoid hook_printf() {\n    // 获取目标程序的动态符号表\n    void *handle = dlopen(NULL, RTLD_LAZY);\n    if (!handle) {\n        printf(\"dlopen failed\\n\");\n        return;\n    }\n\n    // 查找 printf 的 GOT 地址\n    void **got_entry = dlsym(handle, \"printf\");\n    if (!got_entry) {\n        printf(\"dlsym failed\\n\");\n        return;\n    }\n\n    // 保存原始函数地址\n    original_printf = (int (*)(const char *, ...))(*got_entry);\n\n    // 修改 GOT 表，重定向到 hooked_printf\n    *got_entry = (void *)hooked_printf;\n\n    printf(\"Hook installed\\n\");\n}\n\n```\n\n- 调用 `printf` 时，程序会通过 GOT 获取目标地址。\n- Hook 后，GOT 中地址被修改，跳转到 `hooked_printf`。\n\n\n### **相关接口说明**\n```c\nvoid *handle = dlopen(NULL, RTLD_LAZY);\nvoid *symbol = dlsym(handle, \"my_function\"); // 获取当前程序中 `my_function` 的地址\n```\n使用 **`dlopen` 函数** 动态加载程序或库，具体含义如下：\n`dlopen` 是 POSIX 标准的动态加载函数，属于动态链接库管理 API，通常用于：\n- 在运行时加载共享库（`.so` 文件）。\n- 获取库中的符号（函数、变量）地址。\n- 实现插件式架构。\n\n参数:\nNULL: 传入 `NULL` 时表示不加载新共享库，而是返回 **当前进程已经加载的程序或共享库的句柄**。\nRTLD_LAZY: 表示符号的解析是延迟的（Lazy Binding）。只有在符号第一次被调用时，才会解析它的地址。\n> - `RTLD_NOW`：在调用 `dlopen` 时立即解析所有符号。\n> - `RTLD_GLOBAL`：符号对全局可见，其他库可以访问这些符号。\n> - `RTLD_LOCAL`：符号只在当前库可见（默认行为）。\n\n## **2. Inline Hook**\n通过修改目标函数的代码（通常是前几条指令），将执行流跳转到自定义代码。\n\n### **实现步骤**\n\n1. 获取目标函数的起始地址。\n2. 将目标函数开头的几条指令替换为跳转指令（通常是 `jmp` 或 `call`）。\n3. 在自定义函数中完成拦截逻辑后，跳回目标函数。\n\n需要的额外操作:\n- **保存原始头部指令（跳过修改后的部分）。**\n    - Inline Hook 的实现通常会拷贝目标函数被覆盖的指令（通常是 5~15 字节）到一个缓冲区中。\n- **在 Hook 函数中调用时，执行保存的指令。**\n    - 自定义的 Hook 函数会调用这些保存的指令，然后跳回目标函数的剩余部分。\n\n\n### 示例说明\n\n原始指令：\n```asm\n0x1000: 55                   ; push %rbp\n0x1001: 48 89 E5             ; mov %rsp, %rbp\n0x1004: E8 XX XX XX XX       ; call next_func\n```\n\nHook后的置零:\n```asm\n0x1000: E9 YY YY YY YY       ; jmp hook_func\n```\n\n保存原始头部：\n被覆盖的指令（`push %rbp` 和 `mov %rsp, %rbp`）被保存到缓冲区：\n```asm\nbuffer:\n0x2000: 55                   ; push %rbp\n0x2001: 48 89 E5             ; mov %rsp, %rbp\n0x2004: E8 XX XX XX XX       ; call next_func\n0x2009: E9 ZZ ZZ ZZ ZZ       ; jmp to 0x1004 (跳过 Hook 头部，执行剩余部分)\n```\n\n执行逻辑:\n```bash\n[调用 printf] --> [jmp hook_func] --> [hooked_printf] --> [缓冲区中的指令] --> [目标函数剩余部分]\n```\n\n示例:\n```c\n#include <stdio.h>\n#include <string.h>\n#include <stdint.h>\n#include <sys/mman.h>\n\nvoid *original_printf; // 保存原始函数地址\nunsigned char saved_instructions[8]; // 保存被覆盖的指令\n\n// 自定义 Hook 函数\nvoid hooked_printf(const char *format, ...) {\n    printf(\"[HOOKED]: \");\n\n    // 调用原始 printf\n    void (*original)() = (void (*)())saved_instructions;\n    original();\n\n    printf(\"[END HOOK]\\n\");\n}\n\n// 修改内存保护\nvoid unprotect_memory(void *addr, size_t size) {\n    uintptr_t page_start = (uintptr_t)addr & ~(getpagesize() - 1);\n    mprotect((void *)page_start, size, PROT_READ | PROT_WRITE | PROT_EXEC);\n}\n\n// 安装 Hook\nvoid hook_printf() {\n    original_printf = dlsym(RTLD_NEXT, \"printf\");\n\n    // 保存原始指令\n    memcpy(saved_instructions, original_printf, sizeof(saved_instructions));\n    saved_instructions[sizeof(saved_instructions) - 1] = 0xE9; // 跳转指令\n    *(int32_t *)(saved_instructions + sizeof(saved_instructions) - 4) =\n        (int32_t)((uintptr_t)original_printf + sizeof(saved_instructions) - (uintptr_t)saved_instructions - 5);\n\n    // 修改原始函数\n    unprotect_memory(original_printf, 8);\n    unsigned char jmp_instruction[5] = {0xE9}; // JMP 指令\n    *(int32_t *)(jmp_instruction + 1) = (int32_t)((uintptr_t)hooked_printf - (uintptr_t)original_printf - 5);\n    memcpy(original_printf, jmp_instruction, sizeof(jmp_instruction));\n}\n```\n\n\n## 适用范围\n\n![](assets/image-20250106200653584.png)\n\n\n## **1. Hook 技术适用的函数类型**\n### **1.1 可以被 Hook 的函数**\n\n- **动态链接的函数：**\n    - 动态链接函数通过 PLT/GOT 表管理其调用地址（如大多数 C 库函数，例如 `printf`、`malloc`）。\n    - Hook 方法：`PLT/GOT Hook` 或者 `xhook`。\n    - 适用场景：重定向共享库中函数的实现，如拦截 `open`、`read` 等系统调用。\n- **主程序或共享库中的全局函数：**\n    - 主程序或通过 `RTLD_GLOBAL` 加载的共享库中全局可见的符号。\n    - Hook 方法：基于 `dlsym` 或 Inline Hook。\n    - 适用场景：拦截主程序调用的函数（如自定义实现某个库函数行为）。\n- **Native 层的系统调用（syscall）：**\n    - 系统调用通过内核提供，例如 `write`、`mmap`。\n    - Hook 方法：Inline Hook 或通过 libc 中封装的调用（如 `write`）实现。\n    - 适用场景：修改底层文件操作或网络行为。\n- **动态加载的库中的函数：**\n    - 动态加载的共享库（通过 `dlopen` 加载）中的函数可以 Hook，但仅限于全局可见符号。\n    - Hook 方法：Inline Hook 或在加载时使用 `xhook`。\n\n### **1.2 无法直接被 Hook 的函数**\n\n- **静态链接的函数：**\n    - 静态链接的函数直接嵌入主程序中，调用时无需通过 PLT/GOT 跳转，地址是固定的。\n    - Hook 方法：必须使用 Inline Hook 修改函数体。\n    - **限制**：由于无符号表支持，难以精确定位函数起始地址。\n- **内联函数（Inline Functions）：**\n    - 编译器可能将内联函数直接展开到调用处，导致没有明确的函数地址。\n    - Hook 方法：几乎无法针对内联函数 Hook，除非修改编译选项或直接修改机器码。\n- **优化后的匿名符号：**\n    - 在高优化模式下，编译器可能合并函数、消除未使用的符号，导致符号无法通过常规方法定位。\n    - Hook 方法：需要使用反汇编或调试工具精确识别指令位置。\n\n## **2. Hook 技术的适用场景与限制**\n\n### **2.1 Hook 适用场景**\n\n- **功能拦截与增强：**\n\n    - 修改已有函数的逻辑，例如拦截 `malloc`，记录内存分配日志。\n    - 在网络请求中拦截函数如 `send`、`recv`，用于数据分析或防护。\n- **调试与监控：**\n\n    - 捕获函数调用栈，用于性能分析或错误诊断。\n    - 拦截 `open`、`close` 等系统调用，分析文件操作行为。\n- **兼容性补丁：**\n\n    - 修改第三方库行为，例如修复 API 的兼容性问题。\n    - 替换废弃的函数调用为新的实现。\n\n---\n\n### **2.2 Hook 技术的限制与挑战**\n\n#### **(1) 不可见符号**\n\n- 动态库中未导出的符号（`static` 修饰的函数）无法通过 PLT/GOT Hook。\n- **解决方案：**\n    - 使用 Inline Hook，直接修改函数体指令。\n    - 或者在库加载时通过符号表或调试工具定位私有函数。\n\n#### **(2) ASLR（地址空间随机化）**\n\n- ASLR 导致每次运行时共享库的加载地址随机变化，无法通过固定地址定位函数。\n- **解决方案：**\n    - 使用 `dlsym` 或类似方法获取符号地址。\n    - 对于内核级函数，通过 `proc` 文件系统或调试接口解析加载地址。\n\n#### **(3) 函数体覆盖问题**\n\n- 如果被 Hook 的函数体过短，修改后可能破坏函数逻辑。\n    - 例如，一个函数只有 4 字节机器码，无法完整插入 `jmp` 指令。\n- **解决方案：**\n    - 使用 trampoline 技术，将指令复制到安全位置执行后跳回。\n    - 或者使用 PLT/GOT Hook 替代 Inline Hook。\n\n#### **(4) 多线程环境**\n\n- 多线程程序中，修改共享数据（如 GOT 表或代码段）可能导致竞争条件或崩溃。\n- **解决方案：**\n    - 使用线程同步机制，确保 Hook 安装过程的原子性。\n    - 或者提前在单线程初始化阶段完成 Hook。\n\n#### **(5) SELinux 和权限限制**\n\n- 在 Android 等系统中，SELinux 会限制对某些进程的内存或文件访问权限。\n- **解决方案：**\n    - 在 Root 权限下关闭 SELinux。\n    - 或者使用非侵入性 Hook 技术（如基于 Frida 的动态注入）。\n\n## 现有工具\n\nxHook:\nhttps://github.com/iqiyi/xHook\nhttps://github.com/iqiyi/xHook/blob/master/docs/overview/android_plt_hook_overview.zh-CN.md\n\nByteHook:\n博客: https://github.com/caikelun/caikelun.github.io/blob/master/site/blog/2021-08-19-bytedance-open-source-bytehook.md\n\n开源库: https://github.com/bytedance/bhook\n"
}
