kpatch 用户工具详解
2026-06-29 · 阅读 0
概述
kpatch 是 KernelPatch 的用户空间命令行工具,提供与内核 KernelPatch 交互的所有功能。
位置:user_deprecated/ 目录
主要文件:
main.c- 主程序和命令分发kpatch.c/h- SuperCall 封装su.c/h- SU 功能实现kpm.c/h- KPM 管理功能
编译产物:kpatch 可执行文件
主程序架构
main.c - 命令分发
// user_deprecated/main.c:63-174
int main(int argc, char **argv)
{
strcat(program_name, argv[0]);
if (argc == 1) usage(EXIT_FAILURE);
// ============ 第一个参数:SuperKey ============
key = argv[1];
strcat(program_name, " <SUPERKEY>");
if (argc == 2) {
if (!strcmp(argv[1], "-v") || !(strcmp(argv[1], "--version"))) {
fprintf(stdout, "%x\n", version());
} else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
usage(EXIT_SUCCESS);
} else {
usage(EXIT_FAILURE);
}
return 0;
}
// ============ 验证 SuperKey ============
if (!key[0]) error(-EINVAL, 0, "invalid superkey");
if (strnlen(key, SUPERCALL_KEY_MAX_LEN) >= SUPERCALL_KEY_MAX_LEN)
error(-EINVAL, 0, "superkey too long");
// ============ 第二个参数:命令 ============
const char *scmd = argv[2];
int cmd = -1;
struct {
const char *scmd;
int cmd;
} cmd_arr[] = {
{ "hello", SUPERCALL_HELLO },
{ "kpver", SUPERCALL_KERNELPATCH_VER },
{ "kver", SUPERCALL_KERNEL_VER },
{ "key", 'K' },
{ "su", 's' },
{ "kpm", 'k' },
{ "bootlog", 'l' },
{ "panic", '.' },
{ "reload-cfg", 'r' }, // [v2.0 新增] 重新加载配置
{ "event", 'e' }, // [v2.0 新增] 触发事件
#ifdef ANDROID
{ "sumgr", 'm' },
{ "android_user", 'a' },
#endif
};
for (int i = 0; i < sizeof(cmd_arr) / sizeof(cmd_arr[0]); i++) {
if (strcmp(scmd, cmd_arr[i].scmd)) continue;
cmd = cmd_arr[i].cmd;
break;
}
if (cmd < 0) error(-EINVAL, 0, "Invalid command: %s!\n", scmd);
// ============ 命令分发 ============
switch (cmd) {
case SUPERCALL_HELLO:
hello(key);
return 0;
case SUPERCALL_KERNELPATCH_VER:
kpv(key);
return 0;
case SUPERCALL_KERNEL_VER:
kv(key);
return 0;
case 's': // su
strcat(program_name, " su");
return su_main(argc - 2, argv + 2);
case 'K': // key
strcat(program_name, " key");
return skey_main(argc - 2, argv + 2);
case 'k': // kpm
strcat(program_name, " kpm");
return kpm_main(argc - 2, argv + 2);
case 'l': // bootlog
bootlog(key);
break;
case '.': // panic
panic(key);
break;
#ifdef ANDROID
case 'm': // sumgr
strcat(program_name, " sumgr");
return sumgr_main(argc - 2, argv + 2);
#endif
default:
fprintf(stderr, "Invalid command: %s!\n", scmd);
return -EINVAL;
}
return 0;
}
SuperCall 通信机制
v2.0 更新:supercmd 现在通过 truncate 路径工作。
supercmd 通过 truncate 路径
KernelPatch 使用一种巧妙的通信机制:用户空间工具通过 truncate 系统调用向特定路径写入命令,内核端的 KernelPatch hook 拦截该系统调用并解析命令。
// 通信流程:
// 1. 用户空间构造命令字符串
// 2. 调用 truncate(path, length) 或 ftruncate
// 3. KernelPatch 的 hook 拦截 truncate 系统调用
// 4. 检查路径是否匹配特殊前缀
// 5. 解析命令并执行
// 6. 通过返回值传递结果
// 路径格式:
// /dev/kp/<superkey>/<command>[/args...]
//
// 示例:
// truncate("/dev/kp/mykey123/hello", 0)
// truncate("/dev/kp/mykey123/kpver", 0)
为什么使用 truncate?
truncate是标准 POSIX 系统调用,不需要特殊权限- 路径参数可以携带任意长度的命令数据
- 不需要额外的设备节点或 proc/sys 接口
- 即使在 SELinux enforcing 模式下也能工作
- 对抗侧信道攻击的效果优于传统的 ioctl 方式
SuperCall 封装函数
基础命令
// user_deprecated/kpatch.c:22-56
// 验证 KernelPatch 是否安装
void hello(const char *key)
{
long ret = sc_hello(key);
if (ret == SUPERCALL_HELLO_MAGIC) {
fprintf(stdout, "%s\n", SUPERCALL_HELLO_ECHO);
}
}
// 获取 KernelPatch 版本
void kpv(const char *key)
{
uint32_t kpv = sc_kp_ver(key);
fprintf(stdout, "%x\n", kpv);
}
// 获取内核版本
void kv(const char *key)
{
uint32_t kv = sc_k_ver(key);
fprintf(stdout, "%x\n", kv);
}
// 获取启动日志
void bootlog(const char *key)
{
sc_bootlog(key);
}
// 触发内核 panic(调试用)
void panic(const char *key)
{
sc_panic(key);
}
SuperCall 底层实现
// user/supercall.h
#define __NR_supercall 500 // 实际号码由内核决定
static inline long sc_hello(const char *key)
{
return syscall(__NR_supercall, SUPERCALL_HELLO, 0, 0, 0, key);
}
static inline uint32_t sc_kp_ver(const char *key)
{
return (uint32_t)syscall(__NR_supercall, SUPERCALL_KERNELPATCH_VER, 0, 0, 0, key);
}
static inline void sc_bootlog(const char *key)
{
syscall(__NR_supercall, SUPERCALL_BOOTLOG, 0, 0, 0, key);
}
SU 功能 - su_main()
命令行解析
// user_deprecated/su.c:140-240
int su_main(int argc, char **argv)
{
int opt;
struct option longopts[] = {
{ "command", required_argument, NULL, 'c' },
{ "help", no_argument, NULL, 'h' },
{ "login", no_argument, NULL, 'l' },
{ "preserve-environment", no_argument, NULL, 'p' },
{ "shell", required_argument, NULL, 's' },
{ "version", no_argument, NULL, 'v' },
{ "context", required_argument, NULL, 'Z' },
{ "mount-master", no_argument, NULL, 'M' },
{ NULL, 0, NULL, 0 }
};
bool preserve_env = false;
bool login = false;
char const *shell = NULL;
char const *command = NULL;
char *scontext = NULL;
// 解析参数
while ((opt = getopt_long(argc, argv, "c:hlmps:VvZ:M", longopts, NULL)) != -1) {
switch (opt) {
case 'c':
command = optarg;
break;
case 'h':
usage_su(EXIT_SUCCESS);
break;
case 'l':
login = true;
break;
case 'p':
preserve_env = true;
break;
case 's':
shell = optarg;
break;
case 'Z':
scontext = optarg;
break;
case 'M':
// mount master
break;
// ...
}
}
// ============ 调用 SuperCall 提升权限 ============
struct su_profile profile = {
.uid = getuid(),
.to_uid = 0, // root
};
if (scontext) {
strncpy(profile.scontext, scontext, sizeof(profile.scontext) - 1);
} else {
// 默认上下文(Android)
strcpy(profile.scontext, "u:r:su:s0");
}
int ret = sc_su(key, &profile);
if (ret) {
error(-ret, 0, "su failed");
return EXIT_FAILURE;
}
// ============ 执行 Shell 或命令 ============
if (!shell) shell = DEFAULT_SHELL;
char *exec_args[argc + 1];
if (command) {
exec_args[0] = (char *)shell;
exec_args[1] = "-c";
exec_args[2] = (char *)command;
exec_args[3] = NULL;
} else {
exec_args[0] = (char *)shell;
exec_args[1] = NULL;
}
// 设置环境变量
if (!preserve_env) {
xsetenv("PATH", DEFAULT_ROOT_PATH);
xsetenv("HOME", "/root");
xsetenv("USER", "root");
}
// 执行
execvp(shell, exec_args);
// 如果到这里,说明 exec 失败
error(0, errno, "failed to exec %s", shell);
return EXIT_FAILURE;
}
KPM 管理 - kpm_main()
命令结构
// user_deprecated/kpm.c:80-200
int kpm_main(int argc, char **argv)
{
if (argc < 2) usage(EXIT_FAILURE);
const char *scmd = argv[1];
int cmd = -1;
struct {
const char *scmd;
int cmd;
} cmd_arr[] = {
{ "load", KPM_CMD_LOAD },
{ "unload", KPM_CMD_UNLOAD },
{ "list", KPM_CMD_LIST },
{ "info", KPM_CMD_INFO },
{ "control", KPM_CMD_CONTROL },
{ "help", 0 },
};
// 查找命令
for (int i = 0; i < sizeof(cmd_arr) / sizeof(cmd_arr[0]); i++) {
if (strcmp(scmd, cmd_arr[i].scmd)) continue;
cmd = cmd_arr[i].cmd;
break;
}
if (cmd < 0) usage(EXIT_FAILURE);
// ============ 命令处理 ============
switch (cmd) {
// 加载模块
case KPM_CMD_LOAD: {
if (argc < 3) error(-EINVAL, 0, "no kpm path");
const char *path = argv[2];
const char *args = argc > 3 ? argv[3] : "";
int ret = sc_kpm_load(key, path, args);
if (ret < 0) {
error(-ret, 0, "load kpm failed");
return EXIT_FAILURE;
}
fprintf(stdout, "load kpm %s success\n", path);
break;
}
// 卸载模块
case KPM_CMD_UNLOAD: {
if (argc < 3) error(-EINVAL, 0, "no kpm name");
const char *name = argv[2];
int ret = sc_kpm_unload(key, name);
if (ret < 0) {
error(-ret, 0, "unload kpm failed");
return EXIT_FAILURE;
}
fprintf(stdout, "unload kpm %s success\n", name);
break;
}
// 列出所有模块
case KPM_CMD_LIST: {
int nums = sc_kpm_nums(key);
if (nums <= 0) {
fprintf(stdout, "no kpm loaded\n");
return 0;
}
char buf[4096];
int ret = sc_kpm_list(key, buf, sizeof(buf));
if (ret < 0) {
error(-ret, 0, "list kpm failed");
return EXIT_FAILURE;
}
fprintf(stdout, "%s", buf);
break;
}
// 查看模块信息
case KPM_CMD_INFO: {
if (argc < 3) error(-EINVAL, 0, "no kpm name");
const char *name = argv[2];
char buf[2048];
int ret = sc_kpm_info(key, name, buf, sizeof(buf));
if (ret < 0) {
error(-ret, 0, "get kpm info failed");
return EXIT_FAILURE;
}
fprintf(stdout, "%s", buf);
break;
}
// 控制模块
case KPM_CMD_CONTROL: {
if (argc < 3) error(-EINVAL, 0, "no kpm name");
const char *name = argv[2];
const char *args = argc > 3 ? argv[3] : "";
char out_msg[1024];
int ret = sc_kpm_control(key, name, args, out_msg, sizeof(out_msg));
if (ret < 0) {
error(-ret, 0, "control kpm failed");
return EXIT_FAILURE;
}
fprintf(stdout, "%s\n", out_msg);
break;
}
case 0: // help
usage(EXIT_SUCCESS);
break;
default:
usage(EXIT_FAILURE);
}
return 0;
}
SuperCall 封装函数详解
sc_su() - SU 权限提升
// user/supercall.h
static inline int sc_su(const char *key, struct su_profile *profile)
{
return syscall(__NR_supercall, SUPERCALL_SU, profile, 0, 0, key);
}
// 使用示例
struct su_profile profile = {
.uid = getuid(),
.to_uid = 0, // root
};
strcpy(profile.scontext, "u:r:su:s0");
int ret = sc_su("mykey123", &profile);
if (ret == 0) {
// 现在是 root 了
system("id");
}
sc_kpm_load() - 加载模块
// user/supercall.h
static inline int sc_kpm_load(const char *key, const char *path, const char *args)
{
return syscall(__NR_supercall, SUPERCALL_KPM_LOAD, path, args, 0, key);
}
// 使用
sc_kpm_load("mykey", "/data/local/tmp/module.kpm", "debug=1");
sc_kpm_control() - 控制模块
// user/supercall.h
static inline int sc_kpm_control(const char *key, const char *name,
const char *args, char *out_msg, int outlen)
{
return syscall(__NR_supercall, SUPERCALL_KPM_CONTROL,
name, args, out_msg, outlen, key);
}
// 使用
char response[256];
sc_kpm_control("mykey", "my-module", "get_status", response, sizeof(response));
printf("Module says: %s\n", response);
SuperKey 管理
skey_main() - Key 管理命令
// user_deprecated/kpatch.c:85-138
int skey_main(int argc, char **argv)
{
if (argc < 2) usage(EXIT_FAILURE);
const char *scmd = argv[1];
int cmd = -1;
struct {
const char *scmd;
int cmd;
} cmd_arr[] = {
{ "get", SUPERCALL_SKEY_GET },
{ "set", SUPERCALL_SKEY_SET },
{ "rootkey", SUPERCALL_SKEY_ROOT_ENABLE },
{ "help", 0 },
};
// 查找命令
for (int i = 0; i < sizeof(cmd_arr) / sizeof(cmd_arr[0]); i++) {
if (strcmp(scmd, cmd_arr[i].scmd)) continue;
cmd = cmd_arr[i].cmd;
break;
}
if (cmd < 0) usage(EXIT_FAILURE);
char out_buf[SUPERCALL_KEY_MAX_LEN] = { '\0' };
switch (cmd) {
// 获取当前 SuperKey
case SUPERCALL_SKEY_GET:
sc_skey_get(key, out_buf, sizeof(out_buf));
fprintf(stdout, "%s\n", out_buf);
break;
// 设置新的 SuperKey
case SUPERCALL_SKEY_SET:
if (argc < 3) error(-EINVAL, 0, "no new superkey");
const char *new_key = argv[2];
return sc_skey_set(key, new_key);
// 启用/禁用 Root SuperKey 模式
case SUPERCALL_SKEY_ROOT_ENABLE:
if (argc < 3) error(-EINVAL, 0, "no enable or disable specified");
if (!strcmp("enable", argv[2])) {
sc_skey_root_enable(key, true);
} else if (!strcmp("disable", argv[2])) {
sc_skey_root_enable(key, false);
} else {
error(-EINVAL, 0, "no enable or disable specified");
}
break;
case 0: // help
usage(EXIT_SUCCESS);
default:
usage(EXIT_FAILURE);
}
return 0;
}
实际使用示例
示例 1:基础使用
# 验证 KernelPatch 是否安装
$ kpatch mykey123 hello
KernelPatch!
# 查看版本
$ kpatch mykey123 kpver
c00
$ kpatch mykey123 kver
50a95
# 查看启动日志
$ kpatch mykey123 bootlog
KP _ __ _ ____ _ _
KP | |/ /___ _ __ _ __ ___| | _ \ __ _| |_ ___| |__
...
示例 2:获取 root
# 方法 1:启动 root shell
$ kpatch mykey123 su
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root)
# 方法 2:执行单个命令
$ kpatch mykey123 su -c "cat /data/data/com.example.app/databases/app.db"
# 方法 3:指定 SELinux 上下文(Android)
$ kpatch mykey123 su -Z u:r:magisk:s0
示例 3:KPM 管理
# 加载模块
$ kpatch mykey123 kpm load /data/local/tmp/module.kpm
load kpm /data/local/tmp/module.kpm success
# 列出模块
$ kpatch mykey123 kpm list
kpm-hello-demo
demo-inlinehook
module
# 查看模块信息
$ kpatch mykey123 kpm info module
name=module
version=1.0.0
license=GPL v2
author=myself
description=My custom module
base=ffffffc00b000000
size=4000
state=2
# 控制模块
$ kpatch mykey123 kpm control module "get_status"
echo: get_status
# 卸载模块
$ kpatch mykey123 kpm unload module
unload kpm module success
示例 4:SuperKey 管理
# 获取当前 SuperKey
$ kpatch mykey123 key get
mykey123
# 修改 SuperKey
$ kpatch mykey123 key set new_key_456
# 现在旧 key 失效,必须使用新 key
$ kpatch new_key_456 hello
KernelPatch!
# 启用 Root SuperKey 模式
$ kpatch new_key_456 key rootkey enable
# 现在 SuperKey 以哈希形式存储,更安全
Android 特定功能
sumgr - SU 权限管理器
// user_deprecated/android/sumgr.c
int sumgr_main(int argc, char **argv)
{
// 管理允许 root 的 UID 列表
// sumgr grant <uid> [to_uid] [scontext]
// sumgr revoke <uid>
// sumgr list
// sumgr profile <uid>
}
使用示例:
# 授予 UID 1000 root 权限
$ kpatch mykey sumgr grant 1000 0 u:r:su:s0
# 列出所有被授权的 UID
$ kpatch mykey sumgr list
1000
2000
# 撤销权限
$ kpatch mykey sumgr revoke 1000
编译 kpatch
Makefile 编译
cd user_deprecated
make
# 输出:kpatch
CMake 编译
cd user_deprecated
mkdir build && cd build
cmake ..
make
# 输出:build/kpatch
Android 交叉编译
export ANDROID_NDK=/path/to/ndk
export ANDROID=1
cd user_deprecated
mkdir -p build/android && cd 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 ../..
cmake --build .
# 输出:libkpatch.so 或 kpatch
部署到设备
Android 设备
# 1. 推送到设备
adb push kpatch /data/local/tmp/
adb shell chmod +x /data/local/tmp/kpatch
# 2. 测试
adb shell /data/local/tmp/kpatch mykey123 hello
KernelPatch!
# 3. 安装到系统(可选)
adb root
adb remount
adb push kpatch /system/bin/
adb shell chmod 755 /system/bin/kpatch
# 4. 创建符号链接(方便使用)
adb shell ln -s /system/bin/kpatch /system/bin/kp
adb shell kp mykey123 kpver
错误处理
常见错误码
// user_deprecated/kpatch.c
switch (ret) {
case 0:
// 成功
break;
case -EACCES:
fprintf(stderr, "SuperKey verification failed\n");
break;
case -ENOENT:
fprintf(stderr, "KPM not found\n");
break;
case -EEXIST:
fprintf(stderr, "KPM already loaded\n");
break;
case -ENOMEM:
fprintf(stderr, "Out of memory\n");
break;
case -ENOSYS:
fprintf(stderr, "Function not supported\n");
break;
default:
fprintf(stderr, "Unknown error: %d\n", ret);
}
调试技巧
技巧 1:检查 KernelPatch 状态
# 验证安装
$ kpatch mykey hello
# 如果输出 "KernelPatch!",说明已安装
# 如果无输出或错误:
# - SuperKey 错误
# - KernelPatch 未正确安装
# - 系统调用号不匹配
技巧 2:查看内核日志
# 获取启动日志
$ kpatch mykey bootlog | grep "KP"
# 查看 dmesg
$ adb shell dmesg | grep "KP"
# 或
$ adb shell dmesg | grep "kpm"
技巧 3:验证权限
$ kpatch mykey su
# id
uid=0(root) gid=0(root) groups=0(root)
# 检查 SELinux 上下文
# id -Z
u:r:su:s0
# 检查 capabilities
# cat /proc/self/status | grep Cap
CapInh: 0000003fffffffff
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
脚本集成
自动化脚本
#!/system/bin/sh
KEY="mykey123"
# 检查 KernelPatch
if ! /system/bin/kpatch $KEY hello >/dev/null 2>&1; then
echo "KernelPatch not installed"
exit 1
fi
# 加载模块
/system/bin/kpatch $KEY kpm load /data/adb/modules/my_module.kpm
# 配置权限
/system/bin/kpatch $KEY sumgr grant 1000 0 u:r:su:s0
echo "Setup complete"
init.rc 集成(Android)
# /system/etc/init/kpatch.rc
on post-fs-data
# 创建目录
mkdir /data/adb/kp 0755 root root
# 加载模块
exec /system/bin/kpatch mykey123 kpm load /data/adb/kp/module.kpm
on boot
# 设置权限
exec /system/bin/kpatch mykey123 sumgr grant 1000 0 u:r:su:s0
新增子命令
v2.0 新增:kpatch 新增了
reload-cfg和event两个子命令。
reload-cfg - 重新加载配置
reload-cfg 子命令用于在运行时重新加载 KernelPatch 的配置文件,无需重启设备。
# 重新加载配置
$ kpatch mykey123 reload-cfg
# 典型用途:
# 1. 修改 SU 管理器配置后生效
# 2. 更新 KPM 自动加载列表
# 3. 刷新安全策略
工作流程:
- 用户空间发送
reload-cfg命令 - 内核端 KernelPatch 重新读取并解析
patch_config - 应用新的配置项(如 SU 白名单、安全策略等)
- 返回操作结果
event - 触发事件
event 子命令用于手动触发 extra item 事件,可以在运行时加载嵌入在镜像中但尚未被加载的 KPM 模块。
# 触发指定事件
$ kpatch mykey123 event post-fs-data
# 列出可用事件
$ kpatch mykey123 event list
典型用途:
- 在特定的 Android 启动阶段手动触发模块加载
- 调试 extra item 的事件绑定
- 延迟加载某些模块到合适的时机
总结
kpatch 工具的核心价值:
- ✅ 友好的 CLI 接口:封装底层 syscall
- ✅ 完整的功能覆盖:SU、KPM、Key 管理等
- ✅ 跨平台支持:Linux、Android
- ✅ 易于集成:可嵌入脚本和自动化流程
命令总结:
| 命令 | 功能 | 示例 |
|---|---|---|
hello |
验证安装 | kpatch key hello |
kpver |
KP 版本 | kpatch key kpver |
su |
获取 root | kpatch key su |
kpm load |
加载模块 | kpatch key kpm load mod.kpm |
kpm unload |
卸载模块 | kpatch key kpm unload name |
kpm list |
列出模块 | kpatch key kpm list |
key get |
获取 key | kpatch key key get |
key set |
修改 key | kpatch key key set newkey |
reload-cfg |
重新加载配置 | kpatch key reload-cfg |
event |
触发事件 | kpatch key event post-fs-data |
下一篇:13-Android特定功能.md - Android 平台的专用功能
文档版本:2.0
最后更新:2026-06-26
评论
- 还没有评论,来说点什么吧。