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

syscall 与 supercall 详解

2026-06-29 · 阅读 0

概述

本文详解 KernelPatch 的两大通信机制:

  1. Syscall Hook:拦截系统调用
  2. SuperCall:用户空间与 KernelPatch 通信的专用接口

核心文件

  • kernel/patch/common/syscall.c(384 行)- 系统调用 hook
  • kernel/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.cload_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 和 SuperKey
  • trusted_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 机制

  1. 定位 sys_call_table
  2. 支持 wrapper 和非 wrapper 模式
  3. 支持 32 位兼容层

SuperCall 机制(0.13.2 三层认证)

  1. 劫持未使用的系统调用号
  2. 三层认证:SuperKey / trusted_manager_uid / su_allow_uid
  3. 四级命令权限:无需认证 / trusted_caller / is_authed / 调试
  4. get_ap_mod_exclude() 前置 UID 排除机制
  5. 新增 SUPERCALL_AP_LOAD_PACKAGE_CONFIG (0x100d) 无认证包配置加载

核心功能

  • SU 权限提升
  • KPM 模块管理(需要完全认证)
  • SuperKey 动态管理(需要完全认证)
  • KStorage 内核存储(需要 trusted_caller)
  • AP 包配置加载(无需认证)
  • 调试接口

下一篇11-KPM模块系统.md - 动态模块加载的完整实现


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

评论

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

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