溺的文档
KernelPatch · 第 1 篇 / 共 22 篇

KernelPatch 项目概述

2026-06-29 · 阅读 3

项目简介

KernelPatch 是一个强大的 Linux 内核修补和 hook 框架,其最大特点是:

仅需 stripped kernel image(无源码、无符号表)即可对内核进行修补和 hook

核心特性

  • 无需源码和符号表:通过逆向工程 kallsyms 获取所有符号信息
  • 静态和动态 Patch:支持静态修补内核镜像和运行时动态加载模块
  • 完整的 Hook 能力:内核函数 inline hook 和系统调用表 hook
  • Android SU 支持:提供 root 权限管理功能
  • 模块化扩展:通过 KPM (KernelPatch Module) 动态加载功能

支持平台

  • 架构:ARM64 (aarch64)
  • 内核版本:Linux 3.18 - 6.12(理论上)
  • 系统:通用 Linux、Android
  • 前置条件CONFIG_KALLSYMS=y

项目信息

  • 当前版本:0.13.2(参见 version 文件)
  • License:GPL v2.0
  • 作者:bmax121
  • 推荐项目:如果是 Android 用户,建议使用 APatch

三大核心组件架构

KernelPatch 由三个紧密配合的组件构成:

┌─────────────────────────────────────────────────────────────┐
│                     KernelPatch 架构                         │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────────┐  patch   ┌──────────────┐  boot          │
│  │   kptools    │ ──────> │  Kernel Image │ ──────┐        │
│  │  (用户空间)   │          │  + kpimg     │        │        │
│  └──────────────┘          └──────────────┘        │        │
│        │                                            ▼        │
│        │ 解析符号                        ┌──────────────┐   │
│        │ 修补镜像                        │    kpimg     │   │
│        │ 附加 kpimg                      │  (内核空间)   │   │
│        │                                 └──────────────┘   │
│        ▼                                         │          │
│  ┌──────────────┐                                │          │
│  │   kallsyms   │                         提供 SuperCall    │
│  │  逆向引擎     │                                │          │
│  └──────────────┘                                ▼          │
│                                        ┌──────────────┐     │
│                                        │   kpuser     │     │
│                                        │  (用户空间)   │     │
│                                        └──────────────┘     │
│                                                              │
└─────────────────────────────────────────────────────────────┘

1. kptools - 内核镜像修补工具

位置tools/ 目录
编译产物kptools 可执行文件
运行环境:任何支持交叉编译的系统(Windows/Linux/macOS)

主要功能

  1. kallsyms 逆向解析

    • 文件:tools/kallsym.c
    • 从二进制内核镜像中提取符号表
    • 定位任意内核符号的地址
    • 支持本地 kallsyms lookup 和 4.14 内核兼容
  2. 内核镜像修补

    • 文件:tools/patch.c
    • 将 kpimg 附加到内核镜像末尾
    • 修改内核入口点
    • 填充预设数据(preset)
    • 计算 symbol_lookup_anchor 用于运行时符号解析
  3. Boot 镜像处理

    • 文件:tools/bootimg.c(0.13.0+ 新增)
    • 直接修补 boot.img(无需手动解包/重打包)
    • 支持 LZ4/XZ/BZ2 压缩格式的内核解压与重压缩
    • AVB (Android Verified Boot) footer 处理
  4. 镜像管理

    • Patch:修补新的内核镜像或 boot.img
    • Unpatch:还原原始镜像
    • Dump:导出符号表
    • List:查看 patch 信息

命令示例

# 修补内核镜像
kptools -p -i Image -k kpimg -s mykey123 -o Image.patched

# 嵌入 KPM 模块
kptools -p -i Image -k kpimg -s mykey -M hello.kpm -T kpm -o Image.patched

# 导出符号表
kptools -d -i Image > kallsyms.txt

# 还原镜像
kptools -u -i Image.patched -o Image.original

2. kpimg - 内核空间 Patch 镜像

位置kernel/ 目录
编译产物kpimg 二进制文件(特殊设计的 ELF)
运行环境:内核空间(EL1)

主要功能

  1. 接管内核启动

    • 文件:kernel/base/setup1.S, kernel/base/map.c, kernel/base/start.c
    • 劫持内核 boot 流程
    • paging_init 阶段介入
    • 完成所有动态修补
  2. 内核 Hook 能力

    • 文件:kernel/base/hook.c, kernel/base/hotpatch.c
    • Inline hook 任意内核函数
    • 系统调用表 hook
    • 支持 before/after 回调链
    • 通过 stop_machine + alias page 实现安全的代码热修补
  3. SuperCall 机制

    • 文件:kernel/patch/common/supercall.c
    • 通过特殊系统调用暴露内核功能
    • 支持 SuperKey 和 trusted manager 签名双轨认证
    • 提供 SU、KPM 加载、权限管理等接口
  4. KPM 模块系统

    • 文件:kernel/patch/module/module.c
    • 动态加载内核模块(无需源码树)
    • ELF 格式,支持符号解析和重定位
  5. 安全绕过

    • SELinux bypass(Android)
    • KCFI bypass
    • 内存保护绕过
  6. 符号解析增强

    • 文件:kernel/base/start.c, kernel/base/symbol.c
    • 支持 symbol_lookup_anchor 扫描定位 kallsyms_lookup_name
    • 兼容内核 6.x 新增的 kallsyms_on_each_match_symbol API

目录结构

kernel/
├── base/           # 核心基础设施
│   ├── setup.c/h   # 数据结构定义
│   ├── setup1.S    # 启动汇编代码(setup_entry)
│   ├── map.c       # 内存映射(_paging_init)
│   ├── start.c     # 主初始化(start)
│   ├── hook.c      # Hook 引擎
│   ├── hotpatch.c  # 安全代码热修补(stop_machine)
│   ├── symbol.c    # 符号系统
│   ├── predata.c   # 预置数据管理
│   └── ...
├── patch/          # Patch 逻辑
│   ├── patch.c     # 主 patch 函数
│   ├── common/     # 通用功能(syscall, supercall, 任务管理等)
│   ├── android/    # Android 特定功能(userd, sepolicy_flags)
│   ├── module/     # KPM 加载器
│   └── ksyms/      # 内核符号辅助
├── include/        # 头文件
└── kpimg.lds       # 链接脚本

3. kpuser - 用户空间接口库

位置user/ 目录(新版本)、user_deprecated/ 目录(旧版本)
编译产物kpatch 可执行文件 + 头文件
运行环境:目标系统的用户空间

主要功能

  1. SuperCall 封装

    • 文件:user/supercall.h, user_deprecated/kpatch.c
    • 提供友好的 C 函数接口
    • 封装系统调用细节
  2. 命令行工具

    • SU:权限提升
    • KPM:模块管理(加载/卸载/列表/控制)
    • Key:SuperKey 管理
    • 查询:版本、日志等
  3. Android 扩展

    • SU 权限管理器(sumgr)
    • JNI 接口(apjni)
    • 用户守护进程(android_user)

使用示例

# 获取 root 权限
kpatch mykey123 su

# 加载 KPM 模块
kpatch mykey123 kpm load /path/to/module.kpm

# 查看已加载模块
kpatch mykey123 kpm list

# 获取 KernelPatch 版本
kpatch mykey123 kpver

工作原理概览

整体流程图

┌─────────────────────────────────────────────────────────────┐
│ 第一阶段:静态修补(用户空间 - kptools)                     │
└─────────────────────────────────────────────────────────────┘
  kptools -p -i Image -k kpimg -s key -o Image.patched
    │
    ├─ 1. 读取并解析 Image (kallsym.c)
    │     └─ 逆向工程 kallsyms,获取所有符号
    │
    ├─ 2. 计算布局 (patch.c)
    │     ├─ setup_offset = align_ceil(kimg_len, 4K)
    │     ├─ start_offset = max(kernel_size, setup_offset + kpimg_len)
    │     └─ map_offset = tcp_init_sock 地址
    │
    ├─ 3. 组装修补镜像
    │     ├─ 复制原始内核
    │     ├─ 附加 kpimg 到 setup_offset
    │     ├─ 填充 setup_preset(配置数据)
    │     ├─ 备份内核头部到 header_backup
    │     └─ 修改入口点:B setup_entry
    │
    └─ 4. 输出 Image.patched

┌─────────────────────────────────────────────────────────────┐
│ 第二阶段:启动劫持(内核空间 - kpimg setup)                 │
└─────────────────────────────────────────────────────────────┘
  Bootloader 加载 Image.patched
    │
    ├─ setup_entry (setup1.S:358)
    │     ├─ 设置临时栈
    │     ├─ start_prepare():准备启动数据
    │     │   ├─ 复制 kpimg 到 start_offset
    │     │   └─ 备份 tcp_init_sock 到 map_backup
    │     ├─ map_prepare():劫持 paging_init
    │     │   ├─ 备份 paging_init[0] 到 paging_init_backup
    │     │   ├─ 写入 B _paging_init 指令
    │     │   └─ 覆盖 tcp_init_sock(复制 _map_start)
    │     ├─ 恢复内核头部(从 header_backup)
    │     └─ 跳转回 _head
    │
    └─ 内核继续启动...

┌─────────────────────────────────────────────────────────────┐
│ 第三阶段:内存映射(内核空间 - kpimg map)                   │
└─────────────────────────────────────────────────────────────┘
  内核执行到 paging_init()
    │
    ├─ _paging_init (map.c:176)
    │     ├─ 分配新的物理内存(memblock_phys_alloc)
    │     ├─ 映射 kpimg 到高虚拟地址
    │     ├─ 复制 kpimg 到新位置
    │     ├─ 恢复 paging_init 原始指令
    │     ├─ 调用原始 paging_init()
    │     └─ 跳转到 start()
    │
    └─ 原始 paging_init 正常执行

┌─────────────────────────────────────────────────────────────┐
│ 第四阶段:动态 Patch(内核空间 - kpimg start & patch)       │
└─────────────────────────────────────────────────────────────┘
  start() 函数执行 (start.c:604)
    │
    ├─ start_init():初始化全局变量(可失败,返回 int)
    │     ├─ 通过 symbol_lookup_anchor 或 preset 定位 kallsyms_lookup_name
    │     ├─ 定位 printk
    │     ├─ 解析 kallsyms_on_each_match_symbol(内核 6.1+)
    │     └─ 计算页表参数
    │
    ├─ prot_myself():设置 kpimg 内存保护
    │     ├─ text/rodata → R-X
    │     ├─ data/bss → RW-
    │     ├─ hook 区域 → RWX(1MB)
    │     ├─ rw_mem(2MB TLSF)、rox_mem(4MB TLSF)
    │     └─ 注册 vmalloc area
    │
    ├─ restore_map():恢复 tcp_init_sock
    │     └─ 从 map_backup 恢复原始代码
    │
    ├─ predata_init():初始化预置数据(superkey / root_superkey)
    ├─ symbol_init():初始化符号系统
    │
    └─ patch():安装 hooks (patch.c:140)
          ├─ hotpatch_symbol_init():解析 aarch64_insn_patch_text_nosync
          ├─ module_init() + syscall_init()
          ├─ hook panic(用于调试)
          ├─ 触发 EXTRA_EVENT_PAGING_INIT 事件
          ├─ hook rest_init(或 cgroup_init)
          │     └─ before_rest_init()
          │           ├─ hotpatch_init()(alias page 分配)
          │           ├─ bypass_kcfi()
          │           ├─ bypass_selinux()(hook avc_denied)
          │           ├─ task_observer()
          │           ├─ supercall_install()(hook __NR_supercall=45)
          │           ├─ su_compat_init()
          │           ├─ android_sepolicy_flags_fix()
          │           └─ android_user_init()(LSM hook + execve hook)
          └─ hook kernel_init
                └─ before/after_kernel_init()
                      └─ 加载 Extra KPM 模块(PRE/POST 事件)

┌─────────────────────────────────────────────────────────────┐
│ 第五阶段:运行时(内核空间 + 用户空间)                       │
└─────────────────────────────────────────────────────────────┘
  内核继续正常启动
    │
    ├─ SuperCall 已安装
    │     └─ 用户空间可通过特殊系统调用与 KernelPatch 交互
    │
    ├─ Hooks 已激活
    │     └─ 监控进程创建、系统调用等
    │
    ├─ Trusted Manager 认证(Android)
    │     └─ APK 签名验证 + packages.list 监听
    │
    └─ 用户空间工具可用
          └─ supercmd(truncate)或 kpatch 命令行

与传统 LKM 的对比

特性 KernelPatch 传统 LKM (Loadable Kernel Module)
需要内核源码 ❌ 否 ✅ 是
需要符号表 ❌ 否 ✅ 是(通常)
需要 CONFIG_MODULES ❌ 否 ✅ 是
修改方式 静态修补镜像 运行时 insmod
生效时机 内核启动时 启动后加载
持久性 镜像级别(永久) 需要启动脚本
绕过验证 ✅ 可以 ❌ 受限于模块签名
访问内核内部 ✅ 完全访问 ⚠️ 仅导出符号
Hook 能力 ✅ inline hook + syscall hook ⚠️ 有限
内核版本兼容性 ⚠️ 需要针对版本调整 ✅ 源码编译自动适配
安全性 ⚠️ 完全绕过保护 ✅ 受内核安全机制约束

关键技术点

1. kallsyms 逆向工程

问题:如何在没有符号表的情况下获取内核符号?

解决方案

  • 内核编译时会嵌入 kallsyms 数据结构(如果 CONFIG_KALLSYMS=y
  • 通过特征搜索定位:
    • Token table:"0\x00 1\x00 ... a\x00 b\x00 ..."
    • Token index:计算理论索引,在镜像中搜索
    • Addresses/Offsets:递增序列
    • 验证:vectors 符号的 2KB 对齐特征

核心文件tools/kallsym.c(1151 行)


2. 启动流程劫持

问题:如何在内核启动时介入?

解决方案

  • 第一次劫持:修改内核入口点 → setup_entry
  • 第二次劫持:Hook paging_init_paging_init
  • 为什么选择 paging_init
    • 此时页表已初始化,可以操作虚拟内存
    • memblock 分配器可用,可以申请内存
    • 还在早期阶段,可以 hook 后续的初始化函数

核心文件kernel/base/setup1.S(369 行)


3. 指令重定位

问题:ARM64 大量使用 PC 相对寻址,如何处理?

解决方案

  • 识别需要重定位的指令:B/BL/BC/ADR/ADRP/LDR literal/CBZ/TBZ 等
  • 计算绝对地址:从相对偏移还原目标地址
  • 生成跳转序列:使用 LDR + RET 实现无限制跳转

核心文件kernel/base/hook.c, kernel/base/hotpatch.c


4. 内存布局管理

问题:kpimg 应该放在哪里?如何避免与内核冲突?

解决方案

  • 磁盘布局:紧接原始内核(setup_offset = align_ceil(kimg_len, 4K)
  • 运行时布局:动态分配内存(start_offset ≥ kernel_size
  • 临时代码注入:覆盖 tcp_init_sock(启动早期不会调用)
  • 完成后恢复:从 map_backup 恢复原始代码

核心文件tools/patch.c, kernel/base/start.c


5. SuperCall 通信机制

问题:用户空间如何与内核空间的 KernelPatch 通信?

解决方案

  • 劫持系统调用号 45(truncate)作为 __NR_supercall
  • 双轨认证:SuperKey 验证 + trusted manager APK 签名验证
  • 三层权限:is_authed(SuperKey/trusted manager) > is_trusted_caller(含 su_allow_uid) > 未认证
  • 提供命令接口:
    long ret = syscall(__NR_supercall, key, ver_cmd, arg1, arg2, arg3, arg4);
  • 同时支持 CLI 命令接口(通过 truncate 命令 + supercmd 分发)

核心文件kernel/patch/common/supercall.c, kernel/patch/common/supercmd.c


典型使用场景

场景 1:修补内核实现 root(boot.img 直接修补,推荐)

# 1. 编译 kptools 和 kpimg
cd tools && make && cd ../kernel
export TARGET_COMPILE=aarch64-none-elf-
export ANDROID=1
make

# 2. 提取设备 boot 镜像
adb pull /dev/block/by-name/boot boot.img

# 3. 解包 boot.img(自动解压 LZ4/GZIP/XZ 等压缩内核)
./kptools unpack boot.img   # 输出 kernel 文件

# 4. 修补内核
./kptools -p -i kernel -k ../kernel/kpimg -s mykey -o kernel

# 5. 重打包 boot.img(自动重压缩 + AVB footer 更新)
./kptools repack boot.img   # 输出 new-boot.img

# 6. 刷入设备
fastboot flash boot new-boot.img
fastboot reboot

场景 1b:传统方式修补(手动解包/打包)

# 提取内核
unpack_bootimg.py --boot_img boot.img
# 修补
./kptools -p -i kernel -k kpimg -s mykey -o kernel.patched
# 重新打包
mkbootimg --kernel kernel.patched --ramdisk ramdisk ... -o boot.patched.img
# 刷入
fastboot flash boot boot.patched.img

场景 2:开发和加载 KPM 模块

# 1. 编写 KPM
cat > my_hook.c << 'EOF'
#include <kpmodule.h>

KPM_NAME("my-hook");
KPM_VERSION("1.0.0");
KPM_LICENSE("GPL v2");

static long my_init(const char *args, const char *event, void *reserved)
{
    pr_info("My KPM loaded!\n");
    // 可以在这里做 hook
    return 0;
}

KPM_INIT(my_init);
EOF

# 2. 编译 KPM
export TARGET_COMPILE=aarch64-none-elf-
aarch64-none-elf-gcc -c my_hook.c -o my_hook.o
aarch64-none-elf-ld -r -T module.lds my_hook.o -o my_hook.kpm

# 3. 加载到运行中的内核
adb push my_hook.kpm /data/local/tmp/
adb shell kpatch mykey kpm load /data/local/tmp/my_hook.kpm

# 4. 验证
adb shell dmesg | grep "My KPM"

场景 3:Hook 系统调用

通过 KPM hook execve 系统调用:

#include <kpmodule.h>
#include <linux/syscall.h>

KPM_NAME("execve-hook");

static void before_execve(hook_fargs3_t *args, void *udata)
{
    const char *filename = (const char *)args->arg0;
    pr_info("execve: %s\n", filename);
    
    // 可以修改参数或阻止执行
    // args->skip_origin = 1;  // 跳过原始调用
}

static long hook_init(const char *args, const char *event, void *reserved)
{
    // 获取 execve 地址
    unsigned long execve_addr = kallsyms_lookup_name("__arm64_sys_execve");
    
    // Hook
    hook_err_t err = fp_hook_syscalln(__NR_execve, 0, before_execve, 0, 0);
    
    pr_info("hook execve: %d\n", err);
    return 0;
}

KPM_INIT(hook_init);

项目目录结构

KernelPatch/
├── kernel/                 # kpimg 源码
│   ├── base/               # 核心基础设施
│   ├── patch/              # Patch 逻辑
│   ├── include/            # 内核空间头文件
│   ├── linux/              # Linux 内核头文件(部分)
│   ├── kpimg.lds           # kpimg 链接脚本
│   └── Makefile
│
├── tools/                  # kptools 源码
│   ├── kptools.c           # 主程序(含 unpack/repack 子命令)
│   ├── kallsym.c           # kallsyms 逆向引擎
│   ├── patch.c             # 镜像修补
│   ├── bootimg.c           # boot.img 解包/重打包(0.13.0+ 新增)
│   ├── image.c             # 镜像解析
│   ├── symbol.c            # 符号处理(含 symbol_lookup_anchor)
│   ├── kpm.c               # KPM 信息提取
│   ├── insn.c              # 指令处理
│   ├── lib/                # 压缩库(lz4, xz, bz2, sha)
│   ├── Makefile / CMakeLists.txt
│   └── ...
│
├── user/                   # 用户空间接口(新版本)
│   └── supercall.h         # SuperCall 头文件
│
├── user_deprecated/        # 用户空间工具(旧版本)
│   ├── main.c              # kpatch 主程序
│   ├── kpatch.c/h          # SuperCall 封装
│   ├── su.c/h              # SU 功能
│   ├── kpm.c/h             # KPM 管理
│   ├── android/            # Android 特定功能
│   │   ├── android_user.c  # 用户守护进程
│   │   ├── sumgr.c         # SU 权限管理
│   │   └── apjni.cpp       # JNI 接口
│   └── Makefile / CMakeLists.txt
│
├── kpms/                   # KPM 示例模块
│   ├── demo-hello/         # Hello World 示例
│   ├── demo-inlinehook/    # Inline Hook 示例
│   └── demo-syscallhook/   # Syscall Hook 示例
│
├── doc/                    # 官方文档
│   ├── en/                 # 英文文档
│   └── zh-CN/              # 中文文档
│
├── version                 # 版本号定义
├── banner                  # ASCII Logo
├── README.md               # 项目说明
└── LICENSE                 # GPL v2.0 许可证

核心数据结构

preset_t - Patch 配置

// kernel/include/preset.h
typedef struct {
    setup_header_t header;      // 64 字节头部
    setup_preset_t setup;       // 配置数据
} preset_t;

typedef struct _setup_header_t {
    char magic[8];              // "KP1158"
    version_t kp_version;       // KernelPatch 版本
    config_t config_flags;      // 配置标志(Android/Debug)
    char compile_time[24];      // 编译时间
} setup_header_t;

typedef struct _setup_preset_t {
    version_t kernel_version;   // 内核版本
    int64_t kimg_size;          // 原始内核大小
    int64_t kpimg_size;         // kpimg 大小
    int64_t kernel_size;        // 内核运行时大小
    int64_t setup_offset;       // kpimg 在镜像中的位置
    int64_t start_offset;       // kpimg 运行时位置
    int64_t map_offset;         // map 代码注入位置(tcp_init_sock)
    int64_t kallsyms_lookup_name_offset;
    int64_t paging_init_offset;
    map_symbol_t map_symbol;    // memblock 函数地址(MAP_SYMBOL_NUM=7)
    uint8_t header_backup[8];   // 内核头部备份
    uint8_t superkey[64];       // SuperKey
    uint8_t root_superkey[32];  // Root SuperKey 哈希(SHA256)
    int64_t sprintf_offset;     // sprintf 函数偏移(0.13.0+ 新增)
    int64_t symbol_lookup_anchor_offset; // 符号扫描锚点偏移(0.13.0+ 新增)
    patch_config_t patch_config;  // 要 hook 的函数地址
    char additional[512];       // 附加信息
} setup_preset_t;

安全考虑

优势

  • 完全控制内核执行流
  • 可以绕过所有内核安全机制
  • 适合安全研究和漏洞挖掘

风险

  • 需要物理访问或解锁 bootloader
  • 修改内核镜像会破坏验证启动(Verified Boot)
  • 滥用可能导致严重安全问题
  • 仅用于研究和合法用途

参考资源

官方文档

  • doc/en/guide.md:英文使用指南
  • doc/en/build.md:编译说明
  • doc/en/inline-hook.md:Inline Hook 教程
  • doc/en/syscall-hook.md:Syscall Hook 教程
  • doc/en/module.md:KPM 开发指南

相关项目


下一步

本文档提供了 KernelPatch 的整体概览。后续文档将深入解析:

  • 02-kptools详解.md:用户空间修补工具的详细实现
  • 03-kallsyms逆向工程.md:符号表逆向算法的深入分析
  • 06-启动劫持流程.md:kpimg 如何接管内核启动
  • 09-hook机制详解.md:inline hook 的完整实现
  • 12-kpatch用户工具详解.md:用户空间工具的使用和原理

文档版本:2.0
最后更新:2026-06-26
对应 KernelPatch 版本:0.13.2

评论

  • 还没有评论,来说点什么吧。

无需注册或登录,填个昵称即可评论。