syscall 与 supercall 详解
概述
本文详解 KernelPatch 的两大通信机制:
- Syscall Hook:拦截系统调用
- SuperCall:用户空间与 KernelPatch 通信的专用接口
核心文件:
kernel/patch/common/syscall.c(384 行)- 系统调用 hookkernel/patch/common/supercall.c(~430 行)- SuperCall 实现(0.13.2 三层认证重构)kernel/patch/include/uapi/scdefs.h- 命令定义与数据结构kernel/base/predata.c- SuperKey 存储与认证kernel/patch/common/sucompat.c- SU 兼容层与排除机制user/supercall.h- 用户空间接口
系统调用表 Hook
syscall_init() - 初始化
// kernel/patch/common/syscall.c
uintptr_t *sys_call_table = 0;
uintptr_t *compat_sys_call_table = 0;
int has_syscall_wrapper = 0;
int has_config_compat = 0;
void syscall_init()
{
// 1. 定位系统调用表
sys_call_table = (uintptr_t *)kallsyms_lookup_name("sys_call_table");
if (!sys_call_table) {
sys_call_table = (uintptr_t *)kallsyms_lookup_name("ksys_call_table");
}
// 2. 定位 compat 系统调用表(32 位兼容)
compat_sys_call_table = (uintptr_t *)kallsyms_lookup_name("compat_sys_call_table");
// 3. 检测是否使用 syscall wrapper
unsigned long test_addr = sys_call_table[__NR_read];
char *name_buf = (char *)kallsyms_lookup_name("__arm64_sys_read");
if (test_addr == (unsigned long)name_buf) {
has_syscall_wrapper = 1; // 内核 >= 4.17
}
if (compat_sys_call_table) {
has_config_compat = 1;
}
log_boot("sys_call_table: %llx\n", sys_call_table);
log_boot("has_syscall_wrapper: %d\n", has_syscall_wrapper);
log_boot("has_config_compat: %d\n", has_config_compat);
}
syscall 表结构
内核 < 4.17(直接函数指针):
asmlinkage long sys_read(unsigned int fd, char __user *buf, size_t count);
const sys_call_ptr_t sys_call_table[__NR_syscalls] = {
[__NR_read] = sys_read,
[__NR_write] = sys_write,
[__NR_open] = sys_open,
// ...
};
内核 >= 4.17(syscall wrapper):
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
// ...
}
// 展开为:
long __arm64_sys_read(const struct pt_regs *regs)
{
return __se_sys_read(regs->regs[0], regs->regs[1], regs->regs[2]);
}
const sys_call_ptr_t sys_call_table[__NR_syscalls] = {
[__NR_read] = __arm64_sys_read,
// ...
};
Hook 系统调用
// 示例:Hook read 系统调用
void before_sys_read(hook_fargs3_t *args, void *udata)
{
unsigned int fd = (unsigned int)args->arg0;
char __user *buf = (char __user *)args->arg1;
size_t count = (size_t)args->arg2;
pr_info("sys_read: fd=%d, count=%lu\n", fd, count);
// 可以修改参数或阻止调用
}
// 安装
unsigned long sys_read_addr = syscalln_addr(__NR_read, 0);
hook_wrap3((void *)sys_read_addr, before_sys_read, 0, 0);
SuperCall 机制
概念
SuperCall 是 KernelPatch 专用的系统调用接口,用于用户空间与内核空间通信。
设计:
- 使用一个未使用的系统调用号
- 通过三层认证体系验证身份(0.13.2 重构)
- 提供丰富的命令接口,按权限层次分块分发
系统调用签名:
long supercall(long cmd, long arg1, long arg2, long arg3, const char *key);
// 实际调用
ret = syscall(__NR_supercall, SUPERCALL_SU, uid, 0, 0, "mykey123");
supercall_install() - 安装
// kernel/patch/common/supercall.c:300-417
int supercall_install()
{
// 1. 查找未使用的系统调用号
int sc_number = get_unused_syscall_number();
if (sc_number < 0) {
log_boot("no unused syscall number\n");
return -1;
}
log_boot("supercall number: %d\n", sc_number);
// 2. 劫持该系统调用
unsigned long supercall_handler_addr = (unsigned long)supercall_handler;
if (has_syscall_wrapper) {
// 内核 >= 4.17
sys_call_table[sc_number] = supercall_handler_addr;
} else {
// 内核 < 4.17
sys_call_table[sc_number] = supercall_handler_addr;
}
// 3. 修改页表权限(使 sys_call_table 可写)
uint64_t *pte = pgtable_entry_kernel((uint64_t)sys_call_table);
uint64_t old_pte = *pte;
*pte = (*pte | PTE_DBM) & ~PTE_RDONLY;
flush_tlb_kernel_page((uint64_t)sys_call_table);
// 4. 写入地址
sys_call_table[sc_number] = supercall_handler_addr;
// 5. 恢复权限
*pte = old_pte;
flush_tlb_kernel_page((uint64_t)sys_call_table);
return 0;
}
before() - 三层认证入口(0.13.2 重构)
在 0.12.0 中,before() 函数仅使用 SuperKey 做单一认证,且用 "su" 作为 key 代表 su_allow_uid,变量名为 is_key_auth。0.13.2 对此进行了完全重写,引入了三层认证体系。
// kernel/patch/common/supercall.c:390-430(概念性重建)
static void before(hook_fargs6_t *args, void *udata)
{
long cmd = args->arg0;
uid_t uid = current_uid();
// ============ 第 0 层:排除检查 ============
// 如果 uid 在排除列表中,直接跳过 supercall 处理
if (get_ap_mod_exclude(uid)) {
return; // 不处理,让原始 syscall 继续
}
// ============ 认证状态初始化 ============
int is_authed = 0; // 完全认证(SuperKey 或 trusted_manager)
int is_trusted_caller = 0; // 可信调用者(含 su_allow_uid)
// ============ 第 1 层:SuperKey 认证 ============
if (has_preset_superkey()) {
char key[SUPERCALL_KEY_MAX_LEN];
long klen = compat_strncpy_from_user(key, (const char __user *)args->arg4, sizeof(key));
if (klen > 0) {
if (auth_superkey(key) == 0) {
is_authed = 1;
is_trusted_caller = 1;
}
}
}
// ============ 第 2 层:Trusted Manager UID ============
if (!is_authed && is_trusted_manager_uid(uid)) {
is_authed = 1;
is_trusted_caller = 1;
}
// ============ 第 3 层:SU Allow UID ============
if (!is_trusted_caller && is_su_allow_uid(uid)) {
is_trusted_caller = 1;
// 注意:is_authed 仍然为 0
}
// ============ 命令分发(按权限层次) ============
// 参见下方 supercall_handler() 详解
}
关键变化对比(0.12.0 vs 0.13.2):
| 特性 | 0.12.0 | 0.13.2 |
|---|---|---|
| 认证变量名 | is_key_auth |
is_authed |
| SuperKey 认证 | 唯一认证方式 | 三层之一 |
| trusted_manager | 不存在 | is_trusted_manager_uid() |
| su_allow_uid 判断 | 用 "su" 作 key |
独立的 is_su_allow_uid() |
| 排除检查 | 不存在 | get_ap_mod_exclude(uid) |
| trusted_manager 来源 | N/A | 代理到 is_trusted_manager_uid_android() |
认证流程图
调用者 UID
|
v
get_ap_mod_exclude(uid)? ──YES──> 直接返回,不处理
|
NO
v
has_preset_superkey()?
|
YES ──> auth_superkey(key)
| |
| 成功? ──YES──> is_authed=1, is_trusted_caller=1
| |
| NO
v v
is_trusted_manager_uid(uid)?
|
YES ──> is_authed=1, is_trusted_caller=1
| (Android: is_trusted_manager_uid_android())
NO
|
v
is_su_allow_uid(uid)?
|
YES ──> is_trusted_caller=1(但 is_authed=0)
|
NO ──> 拒绝(仅能执行无需认证的命令)
|
v
命令分发(按权限层次)
|
|──> 无需认证: HELLO, KLOG, VER, BUILD_TIME, AP_LOAD_PACKAGE_CONFIG
|
|──> 需要 is_trusted_caller:
| SU 系列, KStorage 系列, safemode
|
|──> 无需认证(调试): BOOTLOG, PANIC, TEST
|
|──> 需要 is_authed:
SKEY 管理(GET/SET/ROOT_ENABLE)
KPM 管理(LOAD/UNLOAD/CONTROL/NUMS/LIST/INFO)
is_trusted_manager_uid() - Trusted Manager 认证
这是 0.13.2 引入的全新认证路径,允许经过 APK 签名验证的管理器应用无需 SuperKey 即可操作。
// kernel/patch/common/supercall.c:382-388
int is_trusted_manager_uid(uid_t uid)
{
#ifdef ANDROID
return is_trusted_manager_uid_android(uid);
#else
return 0; // 非 Android 平台始终不信任
#endif
}
该函数代理到 Android 层的 is_trusted_manager_uid_android(),后者通过硬编码的 SHA256 证书摘要验证 APK 签名,确定管理器应用的 UID。详见 13-Android特定功能.md。
get_ap_mod_exclude() - 排除检查
// kernel/patch/common/sucompat.c:346-353
int get_ap_mod_exclude(uid_t uid)
{
int exclude = 0;
int rc = read_kstorage(exclude_kstorage_gid, uid, &exclude, 0, sizeof(exclude), false);
if (rc < 0) return 0;
return exclude;
}
功能:从内核存储(KStorage,KSTORAGE_EXCLUDE_LIST_GROUP gid=1)读取指定 UID 的排除标志。如果 UID 被标记为排除,supercall 的 before() 函数会直接跳过处理,使得该 UID 的进程完全无法使用 supercall 接口。
配置来源:通过 load_ap_package_config() 从 /data/adb/ap/package_config CSV 文件加载,其中 exclude 字段为 1 的 UID 会被写入此排除列表。
supercall_handler() - 命令分发(0.13.2 四块结构)
0.13.2 将命令分发重组为四个权限块,不再是 0.12.0 的单一 switch:
// kernel/patch/common/supercall.c:273-380(概念性重建)
// ============ 块 1:无需认证(所有调用者均可) ============
switch (cmd) {
case SUPERCALL_HELLO:
return SUPERCALL_HELLO_MAGIC;
case SUPERCALL_KLOG:
return call_klog((const char __user *)arg1);
case SUPERCALL_KERNELPATCH_VER:
return kpver;
case SUPERCALL_KERNEL_VER:
return kver;
case SUPERCALL_BUILD_TIME:
return build_time;
#ifdef ANDROID
case SUPERCALL_AP_LOAD_PACKAGE_CONFIG: // 新增 0x100d
return call_ap_load_package_config();
#endif
}
// 以下命令需要认证
if (!is_trusted_caller) return -EACCES;
// ============ 块 2:需要 trusted_caller ============
switch (cmd) {
// SU 系列
case SUPERCALL_SU:
return call_su((struct su_profile __user *)arg1);
case SUPERCALL_SU_TASK:
return call_su_task((pid_t)arg1, (struct su_profile __user *)arg2);
case SUPERCALL_SU_GRANT_UID:
return call_grant_uid((struct su_profile __user *)arg1);
case SUPERCALL_SU_REVOKE_UID:
return call_revoke_uid((uid_t)arg1);
case SUPERCALL_SU_NUMS:
return call_su_nums();
case SUPERCALL_SU_LIST:
return call_su_list((uid_t __user *)arg1, (int)arg2);
case SUPERCALL_SU_PROFILE:
return call_su_profile((uid_t)arg1, (struct su_profile __user *)arg2);
case SUPERCALL_SU_RESET_PATH:
return call_su_reset_path((const char __user *)arg1);
case SUPERCALL_SU_GET_PATH:
return call_su_get_path((char __user *)arg1, (int)arg2);
case SUPERCALL_SU_GET_ALLOW_SCTX:
return call_su_get_allow_sctx((char __user *)arg1, (int)arg2);
case SUPERCALL_SU_SET_ALLOW_SCTX:
return call_su_set_allow_sctx((const char __user *)arg1);
// KStorage 系列
case SUPERCALL_KSTORAGE_WRITE:
return call_kstorage_write(...);
case SUPERCALL_KSTORAGE_READ:
return call_kstorage_read(...);
// Android 安全模式
#ifdef ANDROID
case SUPERCALL_SU_GET_SAFEMODE:
return call_su_get_safemode();
#endif
}
// ============ 块 3:无需认证(调试命令) ============
switch (cmd) {
case SUPERCALL_BOOTLOG:
return call_bootlog();
case SUPERCALL_PANIC:
return call_panic();
case SUPERCALL_TEST:
return call_test(...);
}
// 以下命令需要完全认证
if (!is_authed) return -EACCES;
// ============ 块 4:需要 is_authed(SuperKey 或 trusted_manager) ============
switch (cmd) {
// SuperKey 管理
case SUPERCALL_SKEY_GET:
return call_skey_get((char __user *)arg1, (int)arg2);
case SUPERCALL_SKEY_SET:
return call_skey_set((char __user *)arg1);
case SUPERCALL_SKEY_ROOT_ENABLE:
return call_skey_root_enable((int)arg1);
// KPM 模块管理
case SUPERCALL_KPM_LOAD:
return call_kpm_load((const char __user *)arg1,
(const char __user *)arg2,
(void __user *)arg3);
case SUPERCALL_KPM_UNLOAD:
return call_kpm_unload((const char __user *)arg1, (void __user *)arg2);
case SUPERCALL_KPM_NUMS:
return call_kpm_nums();
case SUPERCALL_KPM_LIST:
return call_kpm_list((char __user *)arg1, (int)arg2);
case SUPERCALL_KPM_INFO:
return call_kpm_info((const char __user *)arg1,
(char __user *)arg2, (int)arg3);
case SUPERCALL_KPM_CONTROL:
return call_kpm_control((const char __user *)arg1,
(const char __user *)arg2,
(void __user *)arg3);
}
return -EINVAL;
权限层次总结:
| 权限级别 | 获取方式 | 可用命令 |
|---|---|---|
| 无需认证 | 任何调用者 | HELLO, KLOG, VER, BUILD_TIME, AP_LOAD_PACKAGE_CONFIG, BOOTLOG, PANIC, TEST |
| trusted_caller | SuperKey / trusted_manager_uid / su_allow_uid | SU 系列, KStorage 系列, safemode |
| is_authed | SuperKey / trusted_manager_uid(不含 su_allow_uid) | SKEY 管理, KPM 管理 |
SuperKey 验证机制
auth_superkey() - 认证实现
// kernel/base/predata.c:31-58
static int auth_superkey(const char *key)
{
// 1. 直接 XOR 比较(防时序攻击)
const char *superkey = get_superkey();
if (xor_compare(key, superkey) == 0) {
return 0; // 验证成功
}
// 2. Root SuperKey 哈希验证(如果启用)
if (is_root_key_enabled()) {
uint8_t hash[SHA256_BLOCK_SIZE];
sha256_hash((const uint8_t *)key, strlen(key), hash);
const uint8_t *root_hash = get_root_superkey_hash();
if (!memcmp(hash, root_hash, ROOT_SUPER_KEY_HASH_LEN)) {
// 成功后自动重置 superkey 并禁用 root_key
reset_superkey(key);
disable_root_key();
return 0;
}
}
return -EACCES;
}
has_preset_superkey() - 检查是否可认证
// kernel/base/predata.c:109-112
int has_preset_superkey()
{
// 使用持久化标志(0.13.2 修复)
return superkey_is_user_set || root_superkey_is_set;
}
此函数决定是否需要从用户空间读取 key 参数。如果既没有设置 superkey 也没有设置 root superkey,则跳过 SuperKey 认证路径。
Root SuperKey 的优势:
- SuperKey 本身不存储在内核中(只存储哈希)
- 更安全(即使内核被 dump 也无法获取 key)
- 可以动态修改 SuperKey
- 成功认证后自动恢复为直接模式(reset_superkey + disable_root_key)
SuperCall 命令详解
SUPERCALL_SU - 权限提升
// kernel/patch/common/supercall.c:131-139
static long call_su(struct su_profile *__user uprofile)
{
struct su_profile *profile = memdup_user(uprofile, sizeof(struct su_profile));
if (!profile || IS_ERR(profile)) return PTR_ERR(profile);
profile->scontext[sizeof(profile->scontext) - 1] = '\0';
int rc = commit_su(profile->to_uid, profile->scontext);
kvfree(profile);
return rc;
}
su_profile 结构:
// kernel/patch/include/uapi/scdefs.h
#define SUPERCALL_SCONTEXT_LEN 0x60 // 96 字节
struct su_profile {
uid_t uid; // 原始 UID
uid_t to_uid; // 目标 UID(0 = root)
char scontext[SUPERCALL_SCONTEXT_LEN]; // SELinux 上下文(Android)
};
commit_su() 实现:
// kernel/patch/common/accctl.c:78-125
int commit_su(uid_t to_uid, const char *sctx)
{
struct task_struct *task = current;
struct task_ext *ext = get_task_ext(task);
// 1. 禁用 seccomp
struct thread_info *thi = current_thread_info();
thi->flags &= ~(_TIF_SECCOMP);
if (task_struct_offset.seccomp_offset > 0) {
struct seccomp *seccomp =
(struct seccomp *)((uintptr_t)task + task_struct_offset.seccomp_offset);
seccomp->mode = SECCOMP_MODE_DISABLED;
}
// 2. 标记 SELinux 豁免
ext->sel_allow = 1;
// 3. 准备新凭证
struct cred *new = prepare_creds();
if (!new) return -ENOMEM;
// 4. 修改 UID/GID 和 capabilities
su_cred(new, to_uid);
// 5. 设置 SELinux 上下文
if (sctx && sctx[0]) {
uint32_t sid;
int rc = security_secctx_to_secid(sctx, strlen(sctx), &sid);
if (!rc) set_security_override(new, sid);
}
// 6. 提交凭证
return commit_creds(new);
}
su_cred() - 设置全能力:
// kernel/patch/common/accctl.c:32-49
static void su_cred(struct cred *cred, uid_t uid)
{
kernel_cap_t full_cap = {
.cap = { 0xFFFFFFFF, 0xFFFFFFFF } // 所有 capabilities
};
// 设置所有 capabilities
*(kernel_cap_t *)((uintptr_t)cred + cred_offset.cap_inheritable_offset) = full_cap;
*(kernel_cap_t *)((uintptr_t)cred + cred_offset.cap_permitted_offset) = full_cap;
*(kernel_cap_t *)((uintptr_t)cred + cred_offset.cap_effective_offset) = full_cap;
*(kernel_cap_t *)((uintptr_t)cred + cred_offset.cap_bset_offset) = full_cap;
*(kernel_cap_t *)((uintptr_t)cred + cred_offset.cap_ambient_offset) = full_cap;
// 设置 UID/GID
*(uid_t *)((uintptr_t)cred + cred_offset.uid_offset) = uid;
*(uid_t *)((uintptr_t)cred + cred_offset.euid_offset) = uid;
*(uid_t *)((uintptr_t)cred + cred_offset.fsuid_offset) = uid;
*(uid_t *)((uintptr_t)cred + cred_offset.suid_offset) = uid;
*(gid_t *)((uintptr_t)cred + cred_offset.gid_offset) = uid;
*(gid_t *)((uintptr_t)cred + cred_offset.egid_offset) = uid;
*(gid_t *)((uintptr_t)cred + cred_offset.fsgid_offset) = uid;
*(gid_t *)((uintptr_t)cred + cred_offset.sgid_offset) = uid;
}
SUPERCALL_AP_LOAD_PACKAGE_CONFIG (0x100d) - 新增
这是 0.13.2 新增的命令,用于从用户空间触发加载 AP 包配置。
// kernel/patch/common/supercall.c:287-290
#ifdef ANDROID
case SUPERCALL_AP_LOAD_PACKAGE_CONFIG:
return call_ap_load_package_config();
#endif
特殊之处:此命令无需任何认证(不需要 SuperKey,不需要 trusted_caller),任何进程都可以调用。这允许 APD 在启动早期、trusted_manager 尚未确认之前就加载包配置。
实际实现在 kernel/patch/android/userd.c 的 load_ap_package_config() 中,解析 /data/adb/ap/package_config CSV 文件。详见 13-Android特定功能.md。
SUPERCALL_KPM_LOAD - 加载模块
// kernel/patch/common/supercall.c:76-83
static long call_kpm_load(const char __user *arg1, const char __user *arg2,
void __user *reserved)
{
char path[1024], args[KPM_ARGS_LEN];
long pathlen = compat_strncpy_from_user(path, arg1, sizeof(path));
if (pathlen <= 0) return -EINVAL;
long arglen = compat_strncpy_from_user(args, arg2, sizeof(args));
return load_module_path(path, arglen <= 0 ? 0 : args, reserved);
}
用户空间调用:
// user_deprecated/kpm.c
int kpm_load(const char *key, const char *path, const char *args)
{
return syscall(__NR_supercall, SUPERCALL_KPM_LOAD, path, args, 0, key);
}
SUPERCALL_SKEY_* - SuperKey 管理
// kernel/patch/common/supercall.c:151-173
// 获取当前 SuperKey
static long call_skey_get(char __user *out_key, int out_len)
{
const char *key = get_superkey();
int klen = strlen(key);
if (klen >= out_len) return -ENOMEM;
int rc = compat_copy_to_user(out_key, key, klen + 1);
return rc;
}
// 设置新的 SuperKey
static long call_skey_set(char __user *new_key)
{
char buf[SUPER_KEY_LEN];
int len = compat_strncpy_from_user(buf, new_key, sizeof(buf));
if (len >= SUPER_KEY_LEN && buf[SUPER_KEY_LEN - 1]) return -E2BIG;
reset_superkey(new_key);
return 0;
}
// 启用/禁用 Root SuperKey 模式
static long call_skey_root_enable(int enable)
{
enable_auth_root_key(enable);
return 0;
}
命令定义
完整的命令列表(0.13.2)
// kernel/patch/include/uapi/scdefs.h
// ---- 基础命令(无需认证) ----
#define SUPERCALL_HELLO 0x1001
#define SUPERCALL_KERNELPATCH_VER 0x1002
#define SUPERCALL_KERNEL_VER 0x1003
#define SUPERCALL_BUILDTIME 0x1004
#define SUPERCALL_KLOG 0x1005
// ---- AP 命令(无需 superkey,0.13.2 新增) ----
#define SUPERCALL_AP_LOAD_PACKAGE_CONFIG 0x100d
// ---- SU 相关(需要 trusted_caller) ----
#define SUPERCALL_SU 0x2001
#define SUPERCALL_SU_TASK 0x2002
#define SUPERCALL_SU_GRANT_UID 0x2003
#define SUPERCALL_SU_REVOKE_UID 0x2004
#define SUPERCALL_SU_NUMS 0x2005
#define SUPERCALL_SU_LIST 0x2006
#define SUPERCALL_SU_PROFILE 0x2007
#define SUPERCALL_SU_GET_SAFEMODE 0x2008
#define SUPERCALL_SU_RESET_PATH 0x2009
#define SUPERCALL_SU_GET_PATH 0x200a
#define SUPERCALL_SU_GET_ALLOW_SCTX 0x200b
#define SUPERCALL_SU_SET_ALLOW_SCTX 0x200c
// ---- KStorage(需要 trusted_caller) ----
#define SUPERCALL_KSTORAGE_WRITE 0x2101
#define SUPERCALL_KSTORAGE_READ 0x2102
// ---- KPM 相关(需要 is_authed) ----
#define SUPERCALL_KPM_LOAD 0x3001
#define SUPERCALL_KPM_UNLOAD 0x3002
#define SUPERCALL_KPM_NUMS 0x3003
#define SUPERCALL_KPM_LIST 0x3004
#define SUPERCALL_KPM_INFO 0x3005
#define SUPERCALL_KPM_CONTROL 0x3006
// ---- SuperKey 管理(需要 is_authed) ----
#define SUPERCALL_SKEY_GET 0x4001
#define SUPERCALL_SKEY_SET 0x4002
#define SUPERCALL_SKEY_ROOT_ENABLE 0x4003
// ---- 调试(无需认证) ----
#define SUPERCALL_BOOTLOG 0x5001
#define SUPERCALL_PANIC 0x5002
#define SUPERCALL_TEST 0x9999
用户空间接口
封装函数
// user/supercall.h
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 int sc_su(const char *key, uid_t to_uid, const char *scontext)
{
struct su_profile profile = {
.uid = getuid(),
.to_uid = to_uid,
};
if (scontext) strncpy(profile.scontext, scontext, sizeof(profile.scontext) - 1);
return syscall(__NR_supercall, SUPERCALL_SU, &profile, 0, 0, key);
}
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);
}
static inline int sc_kpm_unload(const char *key, const char *name)
{
return syscall(__NR_supercall, SUPERCALL_KPM_UNLOAD, name, 0, 0, key);
}
实际使用示例
示例 1:获取 root
// C 代码
#include "supercall.h"
int main() {
const char *key = "mykey123";
// 验证 KernelPatch 是否安装
if (sc_hello(key) == SUPERCALL_HELLO_MAGIC) {
printf("KernelPatch installed!\n");
// 获取 root 权限
int ret = sc_su(key, 0, "u:r:su:s0");
if (ret == 0) {
printf("Got root!\n");
system("id"); // uid=0(root) gid=0(root)
}
}
return 0;
}
示例 2:加载 KPM
int load_my_module(const char *key)
{
const char *path = "/data/local/tmp/my_module.kpm";
const char *args = "debug=1 verbose=1";
int ret = sc_kpm_load(key, path, args);
if (ret < 0) {
fprintf(stderr, "Load KPM failed: %d\n", ret);
return ret;
}
printf("KPM loaded successfully\n");
return 0;
}
示例 3:管理 SuperKey
// 获取当前 SuperKey
char current_key[64];
sc_skey_get(key, current_key, sizeof(current_key));
printf("Current key: %s\n", current_key);
// 修改 SuperKey
sc_skey_set(key, "new_key_456");
// 启用 Root SuperKey 模式
sc_skey_root_enable(key, 1);
syscall wrapper 兼容性
参数获取
// kernel/patch/common/syscall.c:55-75
const char __user *get_user_arg_ptr(void *a0, void *a1, int nr)
{
char __user *const __user *native = (char __user *const __user *)a0;
int size = 8;
if (has_config_compat) {
native = (char __user *const __user *)a1;
if (a0) size = 4; // compat (32 位)
}
native = (char __user *const __user *)((unsigned long)native + nr * size);
// 从用户空间复制指针
char __user **upptr = memdup_user(native, size);
if (IS_ERR(upptr)) return ERR_PTR((long)upptr);
char __user *uptr;
if (size == 8) {
uptr = *upptr;
} else {
uptr = (char __user *)(unsigned long)*(int32_t *)upptr;
}
kfree(upptr);
return uptr;
}
安全性考虑
1. SuperKey 保护
// SuperKey 存储位置
static char superkey[SUPER_KEY_LEN] = { 0 };
const char *get_superkey()
{
return superkey;
}
void reset_superkey(const char *new_key)
{
strncpy(superkey, new_key, SUPER_KEY_LEN - 1);
superkey[SUPER_KEY_LEN - 1] = '\0';
// 内存屏障(确保所有 CPU 可见)
dsb(ish);
}
风险:
- SuperKey 明文存储在内核内存
- 可以通过内存 dump 获取
缓解:
- 使用 Root SuperKey 模式(只存哈希)
- 定期更换 SuperKey
- 使用强密码
- 0.13.2 引入 LSM hook 监听避免侧信道攻击(低版本内核通过用户态处理)
2. 三层权限检查(0.13.2)
+------------------------------------------------------+
| 调用者身份 | 权限标志 |
|--------------------------|-------------------------------|
| SuperKey 持有者 | is_authed=1, trusted_caller=1 |
| Trusted Manager UID | is_authed=1, trusted_caller=1 |
| su_allow_uid 白名单 | is_authed=0, trusted_caller=1 |
| 其他 | 均为 0,仅可执行公开命令 |
+------------------------------------------------------+
设计要点:
su_allow_uid虽然是可信调用者,但不能管理 KPM 和 SuperKeytrusted_manager_uid通过 APK 证书签名验证,等同于 SuperKey 的权限等级get_ap_mod_exclude()提供了最前置的 UID 排除机制,被排除的 UID 完全无法触及 supercall- 与 0.12.0 相比,认证体系从"有 key 就全能"变为"分层授权"
调试接口
SUPERCALL_BOOTLOG - 获取启动日志
// kernel/patch/common/supercall.c:45-49
static long call_bootlog()
{
print_bootlog();
return 0;
}
void print_bootlog()
{
const char *log = get_boot_log();
char buf[1024];
int off = 0;
char c;
for (int i = 0; (c = log[i]); i++) {
if (c == '\n') {
buf[off++] = c;
buf[off] = '\0';
printk("KP %s", buf);
off = 0;
} else {
buf[off++] = log[i];
}
}
}
用户空间调用:
$ kpatch mykey123 bootlog
KP _ __ _ ____ _ _
KP | |/ /___ _ __ _ __ ___| | _ \ __ _| |_ ___| |__
KP ...
KP Kernel pa: 80000000
KP Kernel va: ffffffc008000000
...
SUPERCALL_KLOG - 用户日志
// kernel/patch/common/supercall.c:58-65
static long call_klog(const char __user *arg1)
{
char buf[1024];
long len = compat_strncpy_from_user(buf, arg1, sizeof(buf));
if (len <= 0) return -EINVAL;
if (len > 0) logki("user log: %s", buf);
return 0;
}
用途:
- 用户空间程序可以向内核日志写入
- 方便调试
总结
syscall 机制:
- 定位 sys_call_table
- 支持 wrapper 和非 wrapper 模式
- 支持 32 位兼容层
SuperCall 机制(0.13.2 三层认证):
- 劫持未使用的系统调用号
- 三层认证:SuperKey / trusted_manager_uid / su_allow_uid
- 四级命令权限:无需认证 / trusted_caller / is_authed / 调试
get_ap_mod_exclude()前置 UID 排除机制- 新增
SUPERCALL_AP_LOAD_PACKAGE_CONFIG(0x100d) 无认证包配置加载
核心功能:
- SU 权限提升
- KPM 模块管理(需要完全认证)
- SuperKey 动态管理(需要完全认证)
- KStorage 内核存储(需要 trusted_caller)
- AP 包配置加载(无需认证)
- 调试接口
下一篇:11-KPM模块系统.md - 动态模块加载的完整实现
文档版本:2.0
最后更新:2026-06-26
对应代码版本:0.13.2
评论
- 还没有评论,来说点什么吧。