22.KernelPatch安卓”全版本”内核hook补丁
在内核中写入hook的方式,就是给安卓打补丁,使用 kernelpatch这个工具。kernelpatch 和 Apatch是一家人。Apatch是基于kernelpatch。是同一位作者,也是阿里的大佬。学习kernelpatch , 那么就是使用, Apatch, 跟着magisk的流程差不多。给手机root ,root也是使用 kpatch的相关的工具。root完成后,加入内核补丁。来看内核模块,观察hook的效果,看看有没有,哪些支持的形式。inlinehook都可以。源码编译一次,跑一次。甚至把kernelpatch的源码编译一遍。将要对比他们优劣点 ,对比分析。
pixel6用Apatch刷机流程与内核补丁
适用于大部分安卓设备,不仅限于GKI内核设备。
提供类似 Magisk 模块的 APM 支持。
提供内核补丁模块支持。允许将任何代码注入内核(提供内核函数 inline-hook 和 syscall-table-hook)。
APatch 依赖于 KernelPatch。
APatch 管理器 和 APM 的源代码来自对 KernelSU 管理器 和 KernelSU 的复制和修改。
****************************
****************************
APatch Boot Image Patcher
****************************
- Patching kernel
+ ./kptools -p -i kernel.ori -S ncmddx1997 -k kpimg -o kernel //使用了密钥 -S参数, -k 对kernel的img文件补丁, 输出 内核
[+] kernel image_size: 0x0314aa00
[+] kernel uefi header: true
[+] kernel load_offset: 0x00000000
[+] kernel kernel_size: 0x031f0000
[+] kernel page_shift: 12
[+] new kernel image ...
[+] linux_banner 1: Linux version 5.10.189-android13-4-00012-g1217bb583cc5-ab11174560 (build-user@build-host) (Android (8508608, based on r450784e) clang version 14.0.7 (https://android.googlesource.com/toolchain/llvm-project 4c603efb0cca074e9238af8b4106c30add4418f6), LLD 14.0.7) #1 SMP PREEMPT Mon Dec 4 18:59:42 UTC 2023
[+] linux_banner offset: 0x24da668
[+] kernel version major: 5, minor: 10, patch: 189
[+] kallsyms_token_table offset: 0x0224f6a8
[+] endian: little
[+] kallsyms_token_index offset: 0x0224fa48
[+] arm64 relocation kernel_va: 0xffffffffffffffff
[?] can't find arm64 relocation table
[+] kallsyms_markers range: [0x0224edc8, 0x0224f6ac), count: 0x00000238
[+] approximate kallsyms_offsets range: [0x01fbe554, 0x0204c534) count: 0x000237f8
[+] kallsyms_names offset: 0x0204c540
[?] can't find kallsyms_num_syms, try: 0x000237ee
[+] names table linux_banner index: 0x00017a1e
[+] linux_banner index: 0
[+] kallsyms_offsets offset: 0x01fbe558
[+] pid_vnr: type: T, offset: 0x00174098
[+] pid_vnr verfied sp_el0, insn: 0xd5384108
[+] layout kimg: 0x0,0x314aa00, kpimg: 0x314b000,0x2c110, extra: 0x3177110,0x80, end: 0x3177190, start: 0x31f0000
[+] kpimg version: b02
[+] kpimg compile time: 01:05:21 Feb 12 2025
[+] kpimg config: android, release
[+] tcp_init_sock: type: T, offset: 0x01406e44
[+] map_start: 0x1406e50, max_size: 0x800
[+] kallsyms_lookup_name: type: T, offset: 0x00292468
[+] printk: type: T, offset: 0x001e90d8
[+] memblock_reserve: type: T, offset: 0x004aa13c
[+] memblock_free: type: T, offset: 0x004a887c
[+] memblock_mark_nomap: type: T, offset: 0x004aa450
[+] memblock_phys_alloc_try_nid: type: T, offset: 0x02cc6074
[?] no symbol: memblock_virt_alloc_try_nid
[+] memblock_alloc_try_nid: type: T, offset: 0x02cc6348
[+] panic: type: T, offset: 0x0012a720
[+] rest_init: type: T, offset: 0x018309fc
[+] kernel_init: type: t, offset: 0x01830adc
[?] no symbol: report_cfi_failure
[?] no symbol: __cfi_slowpath_diag
[+] __cfi_slowpath: type: T, offset: 0x003b9770
[+] copy_process: type: t, offset: 0x00123510
[+] avc_denied: type: t, offset: 0x008944c4
[+] slow_avc_audit: type: T, offset: 0x00892d64
[+] input_handle_event: type: t, offset: 0x00f21c58
[+] root superkey hash: c013e7b3db8b8305725bf888bee8c36973860194af24a072e34afc0bc1b96242
[+] paging_init: type: T, offset: 0x02cb19f4
[+] patch done: kernel
+ patch_rc=0
+ set +x
- Repacking boot image
- Successfully Patched!
Output file is written to
/storage/emulated/0/Download/apatch_patched_11039_0.11.2_grjj.img
****************************
使用了 kpimg 这样的工具可能加入了一些节区。或者多少东西。repack,从新打包。就patch 完成。就放到 download的目录下。最好的方法。
– 手动加载内核模块观察系统调用hook效果
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2023 bmax121. All Rights Reserved.
*/
#include <compiler.h>
#include <kpmodule.h>
#include <linux/printk.h>
#include <common.h>
#include <kputils.h>
#include <linux/string.h>
///< The name of the module, each KPM must has a unique name.
KPM_NAME("kpm-hello-demo");
///< The version of the module.
KPM_VERSION("1.0.0");
///< The license type.
KPM_LICENSE("GPL v2");
///< The author.
KPM_AUTHOR("bmax121");
///< The description.
KPM_DESCRIPTION("KernelPatch Module Example");
/**
* @brief hello world initialization
* @details
*
* @param args
* @param reserved
* @return int
*/
static long hello_init(const char *args, const char *event, void *__user reserved)
{
pr_info("kpm hello init, event: %s, args: %s\n", event, args);
pr_info("kernelpatch version: %x\n", kpver);
return 0;
}
static long hello_control0(const char *args, char *__user out_msg, int outlen)
{
pr_info("kpm hello control0, args: %s\n", args);
char echo[64] = "echo: ";
strncat(echo, args, 48);
compat_copy_to_user(out_msg, echo, sizeof(echo));
return 0;
}
static long hello_control1(void *a1, void *a2, void *a3)
{
pr_info("kpm hello control1, a1: %llx, a2: %llx, a3: %llx\n", a1, a2, a3);
return 0;
}
static long hello_exit(void *__user reserved)
{
pr_info("kpm hello exit\n");
return 0;
}
KPM_INIT(hello_init);
KPM_CTL0(hello_control0);
KPM_CTL1(hello_control1);
KPM_EXIT(hello_exit);
字符串—》
以上是kpm-hello-demo.c 的原始文件
以下是逐行解释代码中的每一行:
/* SPDX-License-Identifier: GPL-2.0-or-later */
这是一个 SPDX许可证标识符,声明该代码使用的许可证是 GPL-2.0或更晚版本。SPDX(软件包数据交换)是一个标准化格式,用于声明源代码的许可证。
/*
* Copyright (C) 2023 bmax121. All Rights Reserved.
*/
这行是版权声明,标明该代码的版权所有者是 bmax121,并且版权归其所有。2023 是版权年份。
#include <compiler.h>
#include <kpmodule.h>
#include <linux/printk.h>
#include <common.h>
#include <kputils.h>
#include <linux/string.h>
这些是 头文件,包含了该模块所需的各种功能。它们提供了各种系统级的操作:
<compiler.h>: 提供与编译器相关的工具或宏。
<kpmodule.h>: 可能是自定义的内核模块接口头文件,定义了与 KPM(Kernel Patch Module)相关的功能。
<linux/printk.h>: 提供 pr_info 等打印函数,用于打印信息到内核日志。
<common.h>: 包含常用的定义和函数。
<kputils.h>: 可能包含与 KPM 相关的工具函数。
<linux/string.h>: 提供字符串操作函数(例如 strncat)。
///< The name of the module, each KPM must has a unique name.
KPM_NAME("kpm-hello-demo");
使用 KPM_NAME 宏定义模块的名称为 kpm-hello-demo。每个 KPM 模块必须有一个唯一的名称。
///< The version of the module.
KPM_VERSION("1.0.0");
使用 KPM_VERSION 宏定义模块的版本为 1.0.0。
///< The license type.
KPM_LICENSE("GPL v2");
使用 KPM_LICENSE 宏定义该模块的许可证类型为 GPL v2,即该模块遵循 GNU 通用公共许可证 v2。
///< The author.
KPM_AUTHOR("bmax121");
使用 KPM_AUTHOR 宏定义该模块的作者是 bmax121。
///< The description.
KPM_DESCRIPTION("KernelPatch Module Example");
使用 KPM_DESCRIPTION 宏定义模块的描述为 "KernelPatch Module Example",即这是一个内核补丁模块的示例。
/**
* @brief hello world initialization
* @details
*
* @param args
* @param reserved
* @return int
*/
这是一个函数注释块,描述了 hello_init 函数的功能:
@brief 提供函数的简短描述。
@details 提供函数的详细描述(这里没有详细描述)。
@param 描述函数参数。
@return 描述函数返回值。
static long hello_init(const char *args, const char *event, void *__user reserved)
{
pr_info("kpm hello init, event: %s, args: %s\n", event, args);
pr_info("kernelpatch version: %x\n", kpver);
return 0;
}
hello_init 是一个初始化函数,接受事件和参数,并通过 pr_info 输出日志。
输出事件 (event) 和参数 (args) 的内容。
输出内核补丁的版本号(通过 kpver 变量)。
返回 0 表示成功。
static long hello_control0(const char *args, char *__user out_msg, int outlen)
{
pr_info("kpm hello control0, args: %s\n", args);
char echo[64] = "echo: ";
strncat(echo, args, 48);
compat_copy_to_user(out_msg, echo, sizeof(echo));
return 0;
}
hello_control0 是一个控制函数,接受参数并将其与 "echo: " 组合,然后将结果复制到用户空间:
使用 pr_info 输出传入的 args。
创建一个 64 字节的缓冲区 echo,将 "echo: " 和参数合并。
使用 strncat 将 args 附加到 echo 中,最多 48 字符。
使用 compat_copy_to_user 将 echo 内容复制到用户空间 out_msg。
static long hello_control1(void *a1, void *a2, void *a3)
{
pr_info("kpm hello control1, a1: %llx, a2: %llx, a3: %llx\n", a1, a2, a3);
return 0;
}
hello_control1 是一个控制函数,接受三个指针类型的参数(a1, a2, a3),并输出它们的值:
使用 pr_info 输出这三个参数的十六进制值。
返回 0 表示成功。
static long hello_exit(void *__user reserved)
{
pr_info("kpm hello exit\n");
return 0;
}
hello_exit 是模块的退出函数:
使用 pr_info 打印模块退出的日志。
返回 0 表示退出成功。
KPM_INIT(hello_init);
使用 KPM_INIT 宏将 hello_init 函数指定为模块的初始化函数。
KPM_CTL0(hello_control0);
使用 KPM_CTL0 宏将 hello_control0 函数绑定为控制函数 0。
KPM_CTL1(hello_control1);
使用 KPM_CTL1 宏将 hello_control1 函数绑定为控制函数 1。
KPM_EXIT(hello_exit);
使用 KPM_EXIT 宏将 hello_exit 函数绑定为模块退出时的清理函数。
这些代码是一个简单的内核补丁模块示例,展示了如何初始化模块、控制它以及在退出时清理它。
KernelPatch/kpms/demo-syscallhook/syscallhook.c
/* SPDX-License-Identifier: GPL-2.0-or-later */ // 指定代码遵循 GPL-2.0 或更高版本许可证,允许修改和分发。
/* Copyright (C) 2023 bmax121. All Rights Reserved. */ // 版权声明,代码的版权所有者是 bmax121,版权归其所有。
#include <compiler.h> // 包含与编译器相关的工具或宏,可能是特定于架构的编译器优化。
#include <kpmodule.h> // 引入内核补丁模块接口的头文件,定义了 KPM 模块相关功能。
#include <linux/printk.h> // 提供用于打印日志的函数,如 pr_info、pr_warn 等。
#include <uapi/asm-generic/unistd.h> // 包含系统调用号定义,主要用于内核与用户空间的接口。
#include <linux/uaccess.h> // 提供用户空间与内核空间之间数据拷贝的函数。
#include <syscall.h> // 包含系统调用的封装和定义,允许进行系统调用操作。
#include <linux/string.h> // 提供字符串操作函数,如 `strncpy`、`strncat` 等。
#include <kputils.h> // 可能包含与 KPM 相关的工具函数,提供辅助功能。
#include <asm/current.h> // 提供当前任务的相关信息,如当前进程、线程等。
KPM_NAME("kpm-syscall-hook-demo"); // 定义模块名称为 "kpm-syscall-hook-demo",每个 KPM 必须有唯一名称。
KPM_VERSION("1.0.0"); // 定义模块版本号为 "1.0.0"。
KPM_LICENSE("GPL v2"); // 定义模块使用的许可证为 "GPL v2"。
KPM_AUTHOR("bmax121"); // 定义模块的作者为 "bmax121"。
KPM_DESCRIPTION("KernelPatch Module System Call Hook Example"); // 定义模块描述。
const char *margs = 0; // 定义一个全局字符串指针 margs 用于存储传入的参数。
enum hook_type hook_type = NONE; // 定义一个枚举类型 hook_type,用于标识钩子类型,初始为 NONE。
enum pid_type // 定义一个枚举类型 pid_type,表示不同类型的进程 ID。
{
PIDTYPE_PID, // 进程 ID
PIDTYPE_TGID, // 线程组 ID
PIDTYPE_PGID, // 进程组 ID
PIDTYPE_SID, // 会话 ID
PIDTYPE_MAX, // 最大值
};
struct pid_namespace; // 前向声明 pid_namespace 结构体,表示 PID 命名空间。
pid_t (*__task_pid_nr_ns)(struct task_struct *task, enum pid_type type, struct pid_namespace *ns) = 0; // 定义一个函数指针,用于获取进程 ID(通过 __task_pid_nr_ns)。
void before_openat_0(hook_fargs4_t *args, void *udata) // 定义一个钩子函数,处理 openat 系统调用的前置操作。
{
int dfd = (int)syscall_argn(args, 0); // 获取 openat 系统调用的第一个参数 dfd。[解析参数1]
const char __user *filename = (typeof(filename))syscall_argn(args, 1); // 获取文件名参数。[解析参数2]
int flag = (int)syscall_argn(args, 2); // 获取标志参数。[解析参数3]
umode_t mode = (int)syscall_argn(args, 3); // 获取模式参数。 [解析参数4]
char buf[1024]; // 创建一个缓冲区用于存储文件名。
compat_strncpy_from_user(buf, filename, sizeof(buf)); // 从用户空间复制文件名到缓冲区。
struct task_struct *task = current; // 获取当前任务(进程)。
pid_t pid = -1, tgid = -1; // 定义进程 ID 和线程组 ID。
if (__task_pid_nr_ns) { // 如果函数指针有效,获取进程 ID 和线程组 ID。
pid = __task_pid_nr_ns(task, PIDTYPE_PID, 0);
tgid = __task_pid_nr_ns(task, PIDTYPE_TGID, 0);
}
args->local.data0 = (uint64_t)task; // 保存当前任务信息到 args 中。
pr_info("hook_chain_0 task: %llx, pid: %d, tgid: %d, openat dfd: %d, filename: %s, flag: %x, mode: %d\n", task, pid, tgid, dfd, buf, flag, mode); // 打印日志信息,显示钩子调用的详细信息。
}
uint64_t open_counts = 0; // 定义一个全局计数器 open_counts,用于记录 openat 调用次数。
void before_openat_1(hook_fargs4_t *args, void *udata) // 定义另一个 openat 钩子函数。
{
uint64_t *pcount = (uint64_t *)udata; // 获取传递的计数器指针
(*pcount)++ ; // 增加计数器
pr_info("hook_chain_1 before openat task: %llx, count: %llx\n", args->local.data0, *pcount); //打印任务信息和当前计数器
void after_openat_1(hook_fargs4_t *args, void *udata) // 定义另一个 openat 钩子函数。
{
pr_info("hook_chain_1 after openat task: %llx\n", args->local.data0); // 打印任务信息.
}
static long syscall_hook_demo_init(const char *args, const char *event, void *__user reserved) // 定义模块的初始化函数.
{
margs = args; // 保存传入的参数.
pr_info("kpm-syscall-hook-demo init ...., args: %s\n", margs); // 打印初始化日志
__task_pid_nr_ns = (typeof(__task_pid_nr_ns))kallsyms_lookup_name("_task_pid_nr_ns"); // 查找系统调用函数 __task_pid_nr_ns 的地址
pr_info("kernel funciton __task_pid_nr_ns addr: %llx\n", __task_pid_nr_ns); // 打印系统调用函数地址
if (!margs) { // 如果没有传入参数, 跳过钩子安装
pr_warn("no args specified, skip hook\n");
return 0;
}
hook_err_t err = HOOK_NO_ERR; // 定义错误类型变量, 初始化无错误
if (!strcmp("function_pointer_hook", margs)) { // 如果参数为"function_pointer_hook",则使用函数指针钩子.
pr_info("function pointer hook ...");
hook_type = FUNCTION_POINTER_CHAIN; // 设置钩子类型为函数指针链
err = fp_hook_syscalln(__NR_openat, 4, before_openat_0, 0, 0); // 勾住 openat 系统调用,参数1--地址,参数2--参数,参数3---回调函数.
if (err) goto out; // 如果钩子安装失败, 跳转到 out.
err = fp_hook_syscalln(__NR_openat, 4, before_openat_1, after_openat_1, &open_counts); // 安装另一个钩子
} else if (!strcmp("inline_hook", margs)) { // 如果参数为 "inline_hook", 则使用内联钩子.
pr_info ("inline hook ...");
hook_type = INLINE_CHAIN; // 设置钩子类型为内联链
err = inline_hook_syscalln(__NR_openat, 4, before_openat_0, 0,0); // 勾住 openat 系统调用
} else {
pr_warn("unknown args: %s\n", margs); // 如果传入参数未知, 打印警告
return 0;
}
out:
if (err) { // 如果钩子安装出错,信息打印错误日志
pr_err("hook openat error: %d\n",err);
} else {
pr_info("hook openat success\n"); // 打印成功日志
}
return 0;
}
static long syscall_hook_control0(const char *args, char *__user out_msg, int outlen) // 控制函数, 用于接受命令并且打印日志
{
pr_info("syscall_hook control, args:%s\n",args); // 打印传入的参数
return 0;
}
static long syscall_hook_demo_exit(void *__user reserved) //模块退出函数, 用于卸载钩子
{
pr_info("kpm-syscall-hook-demo exit ....\n"); // 打印退出日志
if ( hook_type == INLINE_CHAIN) { // 如果钩子类型是内联链, 卸载内联钩子
inline_unhook_syscalln(__NR_openat,before_openat_0,00;
} else if (hook_type == FUNCTION_POINTER_CHAIN) { // 如果钩子类型是函数指针链, 卸载函数指针钩子
fp_unhook_syscalln(__NR_openat, before_openat_0,0);
fp_unhook_syscalln(__NR_openat, before_openat_1, after_openat_1);
} else {
}
return 0;
}
KPM_INIT(syscall_hook_demo_init); // 初始化模块化时候调用 syscall_hook_demo_init 函数
KPM_CTL0(syscall_hook_control0); // 控制时 调用syscall_hook_control0 函数
KMP_EXIT(syscall_hook_demo_exit); // 退出时调用 syscall_hook_demo_exit 函数
该代码是一个示例 内核补丁模块 (KPM),用于挂钩和拦截 openat 系统调用。具体的逻辑和原理如下:
1. 模块初始化与配置
- 模块信息:通过
KPM_NAME、KPM_VERSION、KPM_LICENSE、KPM_AUTHOR和KPM_DESCRIPTION宏,定义了模块的基本信息,如名称、版本、许可证、作者和描述。 syscall_hook_demo_init函数:该函数是模块的初始化函数,接收传入的参数并决定使用哪种类型的钩子(例如,函数指针钩子或内联钩子)。它通过kallsyms_lookup_name查找内核函数的地址(如__task_pid_nr_ns),并安装系统调用钩子。
2. 系统调用钩子
before_openat_0和before_openat_1:这些是钩子函数,会在调用openat系统调用之前被调用。它们提取系统调用的参数(如文件描述符、文件名、标志和模式),然后打印出相关信息。before_openat_1还会计数调用次数。after_openat_1:这是openat调用后的钩子函数,用于打印调用后的任务信息。
3. 钩子类型
- 函数指针钩子 (
FUNCTION_POINTER_CHAIN):将钩子函数注册到系统调用表中,使用函数指针的方式来挂钩openat系统调用。这种方式通过fp_hook_syscalln来实现。 - 内联钩子 (
INLINE_CHAIN):通过内联方式修改系统调用的执行流,将钩子直接插入系统调用的执行路径。
4. 控制和退出
syscall_hook_control0:这是一个控制函数,接受参数并打印日志信息。它允许在模块加载后通过控制接口与模块交互。syscall_hook_demo_exit:这是模块的退出函数,用于卸载挂钩的系统调用。如果使用内联钩子,它将调用inline_unhook_syscalln来卸载钩子;如果使用函数指针钩子,它将通过fp_unhook_syscalln卸载。
5. 钩子原理
- 系统调用钩子:钩子允许模块在系统调用执行前、执行中或执行后插入自定义代码。这里的钩子拦截了
openat系统调用,通过访问和修改参数,甚至统计调用次数。 - 内核空间与用户空间交互:该模块利用
pr_info和pr_warn打印调试信息,并通过kallsyms_lookup_name动态查找内核符号。还使用compat_strncpy_from_user等函数在内核空间和用户空间之间传递数据。
6. 总结
该内核模块展示了如何通过 函数指针钩子 或 内联钩子 技术,拦截和修改系统调用(如 openat)。模块在初始化时安装钩子,在退出时卸载钩子,并提供控制接口来与模块进行交互。

卸载模块的样子. –> syscall_hook_demo_exit
下面的 kmp-syscall-hook-demo exit —– // 这个卸载函数成功了.
内核模块系统调用与inlinehook回调开发
inlinehook 的本质就是地址的hook.
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2023 bmax121. All Rights Reserved.
*/
#include <log.h>
#include <compiler.h>
#include <kpmodule.h>
#include <hook.h>
#include <linux/printk.h>
KPM_NAME("kpm-inline-hook-demo");
KPM_VERSION("1.0.0");
KPM_LICENSE("GPL v2");
KPM_AUTHOR("bmax121");
KPM_DESCRIPTION("KernelPatch Module Inline Hook Example");
init __noinline add(int a, int b)
{
logkd("origin add called\n");
int ret = a + b;
return ret;
}
void before_add(hook_fargs2_t *args,void *udata) // 被调用前的处理函数
{
logkd("before and arg0: %d, arg1: %d\n",(int)args->arg0, (int)args->arg1);
}
void after_add(hook_fargs2_t *args, void *udata) // 被调用之后的处理函数
{
logkd("after add arg0: %d, arg1: %d, ret: %d\n",(int)args->arg0, (int)args->arg1, (int)args->ret);
args->ret = 100; // 这是我hook修改的地方
}
// 初始化函数
static long inline_hook_demo_init(const char *args, const char *event, void *__user reserved)
{
logkd("kpm inline-hook-demo init\n");
int a = 20;
int b = 10;
int ret = add(a,b); // 这是调用的过程
logkd("%d + %d = %d\n", a, b, ret); // 这是打日志
hook_err_t arr = hook_wrap2(void *)add/*这里确实是高, 直接将add转换为地址*/, before_add , after_add, 0 ) ; // 这是开始hook,在内核中, 你得直接去找其他函数的地址
logkd("hook err : %d\n", err); // 观察一下
ret = add(a,b); // 执行嗯,这里输出一下
logkd("%d + %d = %d\n", a, b, ret);
return 0;
}
static long inline_hook_control0(const char *args, char *__user out_msg, int outlen) // 控制函数,当模块被控制时候,通过用户空间命令,他会打印传递的参数 args.
{
pr_info("kpm control, args: %s\n", args);
return 0;
}
static long inline_hook_demo_exit(void *__user reserved)
{
unhook((void *)add);
int a = 20;
int b = 10;
int ret = add(a,b);
logkd("%d + %d = %d\n", a, b, ret);
logkd("kpm inline-hook-demo exit\n");
}
// 模块钩子的注册
KPM_INIT(inline_hook_demo_init);
KPM_CTL0(inline_hook_control0);
KPM_EXIT(inline_hook_demo_exit);

内核模块开发编译运行完整流程与源码解析
如果和ebpf一样,那么, 一次性hook 200多个, 全部都hook上, 那么怎么办不能一个个写对把, 这个就得一个个写,

inline hook , 就是直接找地址hook , 然后hook_wrap2
static long inline_hook_demo_init(const char *args, const char *event, void *__user reserved)
{
logkd("kpm inline-hook-demo init\n");
int a = 20;
int b = 10;
int ret = add(a, b);
logkd("%d + %d = %d\n", a, b, ret);
hook_err_t err = hook_wrap2((void *)add, before_add, after_add, 0); // 使用了hook_wrap2这个函数, hook_wrap2是怎么实现就要看源码.
logkd("hook err: %d\n", err);
ret = add(a, b);
logkd("%d + %d = %d\n", a, b, ret);
return 0;
}
hook上就好了
用户态用的函数都在这里面.
编译 Build kptools
calleng@calleng-GB-BNi7HG6-1060:~/下载$ cd KernelPatch-0.11.1-dev
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev$ ls
banner doc kernel kpms LICENSE README.md tools user version
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev$ export TARGET_COMPILE=aarch64-none-elf-
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev$ cd kernel
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/kernel$ export ANDROID=1
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/kernel$ make
cp -Rf patch/include/uapi ../user
cp -f include/preset.h ../tools
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/setup.o base/setup.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -o base/setup1.o base/setup1.S
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -o base/cache.o base/cache.S
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/tlsf.o base/tlsf.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/start.o base/start.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/map.o base/map.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -o base/map1.o base/map1.S
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/hook.o base/hook.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/fphook.o base/fphook.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/hmem.o base/hmem.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/predata.o base/predata.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/symbol.o base/symbol.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/baselib.o base/baselib.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o base/sha256.o base/sha256.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/patch.o patch/patch.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/accctl.o patch/common/accctl.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/hotpatch.o patch/common/hotpatch.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/pmem.o patch/common/pmem.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/secpass.o patch/common/secpass.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/selinuxhook.o patch/common/selinuxhook.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/supercall.o patch/common/supercall.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/syscall.o patch/common/syscall.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/taskob.o patch/common/taskob.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/common/utils.o patch/common/utils.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/module/insn.o patch/module/insn.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/module/module.o patch/module/module.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/module/relo.o patch/module/relo.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/ksyms/execv.o patch/ksyms/execv.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/ksyms/libs.o patch/ksyms/libs.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/ksyms/misc.o patch/ksyms/misc.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/ksyms/task_cred.o patch/ksyms/task_cred.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/android/sucompat.o patch/android/sucompat.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/android/supercall.o patch/android/supercall.c
aarch64-none-elf-gcc -Wall -fno-builtin -std=gnu11 -nostdinc -g -DANDROID -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include -c -O2 -o patch/android/userd.o patch/android/userd.c
aarch64-none-elf-ld -nostdlib -static -no-pie -Tkpimg.lds -o kpimg.elf base/setup.o base/setup1.o base/cache.o base/tlsf.o base/start.o base/map.o base/map1.o base/hook.o base/fphook.o base/hmem.o base/predata.o base/symbol.o base/baselib.o base/sha256.o patch/patch.o patch/common/accctl.o patch/common/hotpatch.o patch/common/pmem.o patch/common/secpass.o patch/common/selinuxhook.o patch/common/supercall.o patch/common/syscall.o patch/common/taskob.o patch/common/utils.o patch/module/insn.o patch/module/module.o patch/module/relo.o patch/ksyms/execv.o patch/ksyms/libs.o patch/ksyms/misc.o patch/ksyms/task_cred.o patch/android/sucompat.o patch/android/supercall.o patch/android/userd.o
aarch64-none-elf-ld: warning: kpimg.elf has a LOAD segment with RWX permissions
aarch64-none-elf-objcopy -O binary -S kpimg.elf kpimg
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/kernel$ file kpimg.elf
kpimg.elf: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, with debug_info, not stripped
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/kernel$ file kpimg
kpimg: data
修改路径为相对于 user/CMakeLists.txt
编译 Building kpatch
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev$ export ANDROID=1
cd tools
make
cc -o kptools image.o kallsym.o kptools.o order.o insn.o patch.o symbol.o kpm.o common.o sha256.o
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/tools$ ls
CMakeLists.txt elf image.o kallsym.c kpm.h kptools.o order.o preset.h sha256.o
common.c fls_ffs.h insn.c kallsym.h kpm.o Makefile patch.c ptrace.h symbol.c
common.h image.c insn.h kallsym.o kptools order.c patch.h sha256.c symbol.h
common.o image.h insn.o kpm.c kptools.c order.h patch.o sha256.h symbol.o
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/tools$ file kptools
kptools: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8a320c3d936a66d961bb0324a95cac4f2cc0eaf8, for GNU/Linux 3.2.0, not stripped
编译 Build kpimg
在 CMakeLists.txt 末尾,加上下面的这段
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/user$ vim CMakeLists.txt
因为 CMakeLists.txt 在 user/ 下,而 include 路径在 ../../kernel/patch/include,你加的这一行:
include_directories(${CMAKE_SOURCE_DIR}/kernel/patch/include)
可能在交叉编译环境中不被解释为相对路径。
请尝试替换为 相对 user 目录的路径:
🔁 请修改:
include_directories(${CMAKE_SOURCE_DIR}/../kernel/patch/include)
因为你是在 user 子目录中,CMAKE_SOURCE_DIR 会指向 KernelPatch-0.11.1-dev/user,所以需要回退到上一层。
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/user/build/android$ rm -rf *
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/user/build/android$ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DANDROID_PLATFORM=android-33 \
-DANDROID_ABI=arm64-v8a ../..
-- The C compiler identification is Clang 17.0.2
-- The CXX compiler identification is Clang 17.0.2
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /media/calleng/8f93e36b-93d5-4178-b393-88323a7c217b/home/calleng/Android/Sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64/bin/clang - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /media/calleng/8f93e36b-93d5-4178-b393-88323a7c217b/home/calleng/Android/Sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.4s)
-- Generating done (0.0s)
-- Build files have been written to: /home/calleng/下载/KernelPatch-0.11.1-dev/user/build/android
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/user/build/android$ cmake --build .
[ 6%] Building C object CMakeFiles/kp.dir/kpatch.c.o
[ 13%] Building C object CMakeFiles/kp.dir/kpm.c.o
[ 20%] Building C object CMakeFiles/kp.dir/su.c.o
[ 26%] Building C object CMakeFiles/kp.dir/android/android_user.c.o
[ 33%] Building C object CMakeFiles/kp.dir/android/sumgr.c.o
[ 40%] Linking C static library libkp.a
[ 40%] Built target kp
[ 46%] Building C object CMakeFiles/kpatch.dir/kpatch.c.o
[ 53%] Building C object CMakeFiles/kpatch.dir/kpm.c.o
[ 60%] Building C object CMakeFiles/kpatch.dir/su.c.o
[ 66%] Building C object CMakeFiles/kpatch.dir/android/android_user.c.o
[ 73%] Building C object CMakeFiles/kpatch.dir/android/sumgr.c.o
[ 80%] Building C object CMakeFiles/kpatch.dir/main.c.o
[ 86%] Linking C executable kpatch
[ 86%] Built target kpatch
[ 93%] Building CXX object CMakeFiles/apjni.dir/android/apjni.cpp.o
[100%] Linking CXX shared library libapjni.so
[100%] Built target apjni
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/user/build/android$ file kpatch
kpatch: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /system/bin/linker64, stripped
编译 Build KernelPatch Module
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/kpms/demo-hello$ export TARGET_COMPILE=aarch64-none-elf-
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/kpms/demo-hello$ make
aarch64-none-elf-gcc -I../../kernel/. -I../../kernel/include -I../../kernel/patch/include -I../../kernel/linux/include -I../../kernel/linux/arch/arm64/include -I../../kernel/linux/tools/arch/arm64/include -Thello.lds -c -O2 -o hello.o hello.c
aarch64-none-elf-gcc -r -o hello.kpm hello.o
calleng@calleng-GB-BNi7HG6-1060:~/下载/KernelPatch-0.11.1-dev/kpms/demo-hello$ ls
hello.c hello.kpm hello.lds hello.o Makefile
KernelPatch与eBPF相比优劣分析
对比的小结
既然源码都可以自己编译了, 那么是不是,可以自己修改逻辑自己玩一下.
就是你无法hook所有的系统调用, 你每hook一个系统调用都要从头写 , ebpf的话就可以hook很多的系统调用, ebpf他没有办法hook一个地址, 但是可以hook系统调用
kernelpatch 就是一个安卓强版本, 可以用在所有的版本上, 3.10 -6.8
ebpf 非常简单,清晰, 联合GPT 是非常好玩的
