Android 特定功能详解
概述
KernelPatch 为 Android 平台提供了丰富的专用功能,包括 SELinux 绕过、SU 权限管理、Trusted Manager 签名验证、APD 集成和 LSM 监听等。
核心文件:
kernel/patch/android/userd.c(~1700 行)- Android 核心实现(Trusted Manager、init.rc 注入、LSM hook、包配置)kernel/patch/android/sepolicy_flags.c- SELinux 策略标志修复kernel/patch/common/sucompat.c- SU 兼容层与排除机制user/supercmd.c- 用户空间 supercmd 命令(含 reload-cfg、event)user_deprecated/android/- 用户空间 Android 功能
主要功能(0.13.2):
- SELinux 绕过和策略修复
- Trusted Manager APK 签名验证(全新)
- SU 权限管理器(sumgr,KStorage 后端)
- APD 守护进程集成(替代已删除的 user_init.sh)
- init.rc 多源注入(替代单一 atrace.rc hook)
- LSM hook 监听 packages.list 变更(全新)
- AP 包配置加载(全新)
- JNI 接口(apjni)
重要变更(0.13.x):
user_init.sh已被完全移除,所有用户空间初始化改由 APD(/data/adb/apd)直接执行。认证机制从纯 SuperKey 扩展为 SuperKey + Trusted Manager UID + su_allow_uid 三层体系。
SELinux 绕过
sepolicy_flags_fix() - 策略标志修复
// kernel/patch/android/sepolicy_flags.c:50-64
int android_sepolicy_flags_fix()
{
unsigned long policydb_write_addr = kallsyms_lookup_name("policydb_write");
if (likely(policydb_write_addr)) {
hook_err_t err = hook_wrap2((void *)policydb_write_addr,
before_policydb_write,
after_policydb_write, 0);
if (unlikely(err != HOOK_NO_ERR)) {
log_boot("hook policydb_write_addr: %llx, error: %d\n",
policydb_write_addr, err);
return -1;
}
}
return 0;
}
Hook policydb_write
// kernel/patch/android/sepolicy_flags.c:25-48
static void before_policydb_write(hook_fargs2_t *args, void *udata)
{
struct _policy_file *fp = (struct _policy_file *)args->arg1;
args->local.data0 = (uint64_t)fp->data; // 保存数据指针
}
static void after_policydb_write(hook_fargs2_t *args, void *udata)
{
struct _policydb *p = (struct _policydb *)args->arg0;
char *data = (char *)args->local.data0;
if (!args->ret) { // 写入成功
__le32 *config = (__le32 *)(data + POLICYDB_CONFIG_OFFSET);
__le32 before_config = *config;
// 检查并添加 Android netlink 路由标志
bool android_netlink_route_exists =
before_config & POLICYDB_CONFIG_ANDROID_NETLINK_ROUTE;
bool android_netlink_getneigh_exists =
before_config & POLICYDB_CONFIG_ANDROID_NETLINK_GETNEIGH;
if (p->android_netlink_route == 1 && !android_netlink_route_exists) {
*config |= POLICYDB_CONFIG_ANDROID_NETLINK_ROUTE;
}
if (p->android_netlink_getneigh == 1 && !android_netlink_getneigh_exists) {
*config |= POLICYDB_CONFIG_ANDROID_NETLINK_GETNEIGH;
}
}
}
问题背景:
- Android 13+ 引入了新的 SELinux 策略标志
- 如果策略文件缺少这些标志,某些网络功能会失败
- 此 hook 自动添加缺失的标志
Trusted Manager 签名验证(0.13.2 全新机制)
概念
0.13.2 引入了 Trusted Manager 机制,通过硬编码的 SHA256 证书摘要验证 APK 签名,自动识别可信的管理器应用(如 APatch Manager)。经过签名验证的管理器应用无需 SuperKey 即可获得 is_authed 权限。
硬编码可信包列表
// kernel/patch/android/userd.c:67-95
static const struct trusted_package {
const char *package_name;
const uint8_t cert_digest[TRUSTED_MANAGER_DIGEST_LEN]; // SHA256_BLOCK_SIZE = 32
} trusted_packages[] = {
{
.package_name = "me.bmax.apatch",
.cert_digest = { 0xd7, 0x1d, 0xad, 0xc0, ... } // 32 字节 SHA256
},
{
.package_name = "com.example.apatch",
.cert_digest = { 0xe5, 0x11, 0x33, 0x12, ... }
},
};
APK 签名验证流程
Trusted Manager 验证流程
|
v
find_trusted_manager_apk_path()
|-- 在 /data/app/ 下查找 APK 文件
| |-- Pre-Android 11: /data/app/<pkg>-<N>/base.apk
| +-- Android 11+: /data/app/~~<hash>/<pkg>-<hash>/base.apk
|
v
apk_matches_trusted_signature()
|-- 解析 APK 的签名块
| |-- 支持 APK Signature Scheme v2
| |-- 支持 APK Signature Scheme v3
| +-- 支持 APK Signature Scheme v3.1
|
v
cert_der_matches_trusted_digest()
|-- 计算证书 DER 的 SHA256
|-- 与硬编码的 cert_digest 比较
|
v
lookup_package_list_uid()
|-- 解析 /data/system/packages.list
|-- 查找包名对应的 UID
|
v
设置 trusted_manager_uid = UID
证书验证核心函数
// kernel/patch/android/userd.c:191-203
static int cert_der_matches_trusted_digest(const uint8_t *cert_der, int cert_len,
const uint8_t *trusted_digest)
{
uint8_t computed_digest[SHA256_BLOCK_SIZE];
sha256_hash(cert_der, cert_len, computed_digest);
return memcmp(computed_digest, trusted_digest, SHA256_BLOCK_SIZE) == 0;
}
// kernel/patch/android/userd.c:294-417
static int apk_matches_trusted_signature(const char *apk_path,
const uint8_t *trusted_digest)
{
// 1. 打开 APK 文件(ZIP 格式)
// 2. 定位 APK Signing Block(在 Central Directory 之前)
// 3. 解析签名块,尝试 v3.1 -> v3 -> v2 方案
// 4. 提取证书 DER(最大 APK_CERT_MAX_LENGTH = 4096 字节)
// 5. 调用 cert_der_matches_trusted_digest() 验证
return matches;
}
APK 路径查找
// kernel/patch/android/userd.c:761-901
static int find_trusted_manager_apk_path(const char *pkg_name, char *out_path, int path_len)
{
// 方法 1:直接目录扫描
// Pre-Android 11 格式
snprintf(path, sizeof(path), "/data/app/%s-%d/base.apk", pkg_name, N);
// Android 11+ 格式(随机化目录名)
// /data/app/~~<random_hash>/<pkg_name>-<random_hash>/base.apk
// 需要遍历 /data/app/ 下的 ~~* 目录
// 方法 2:从 packages.xml 解析(回退)
return find_apk_from_packages_xml(pkg_name, out_path, path_len);
}
UID 查找
// kernel/patch/android/userd.c:419-496
static int lookup_package_list_uid(const char *pkg_name)
{
// 解析 /data/system/packages.list
// 格式:<package_name> <uid> <debuggable> <data_dir> <seinfo> <gids>
// 查找匹配 pkg_name 的行,返回 uid
struct file *f = filp_open("/data/system/packages.list", O_RDONLY, 0);
// ... 逐行解析
return uid;
}
is_trusted_manager_uid_android()
// kernel/patch/android/userd.c
int is_trusted_manager_uid_android(uid_t uid)
{
// 与缓存的 trusted_manager_uid 比较
return (uid != 0) && (uid == trusted_manager_uid);
}
该函数被 kernel/patch/common/supercall.c 中的 is_trusted_manager_uid() 调用,作为 supercall 三层认证的第二层。详见 10-syscall与supercall.md。
SU 权限管理器 - sumgr
核心概念
sumgr (SU Manager) 允许管理哪些 UID 可以获得 root 权限。
三种认证路径(0.13.2):
- SuperKey 直接 SU:通过 SuperKey 获取 root
- Trusted Manager:经 APK 签名验证的管理器应用
- UID 白名单:通过包配置或 sumgr 授权的 UID
sumgr 命令
# 授予 UID 权限
kpatch key sumgr grant <uid> [to_uid] [scontext]
# 撤销权限
kpatch key sumgr revoke <uid>
# 列出所有授权的 UID
kpatch key sumgr list
# 查看 UID 的配置
kpatch key sumgr profile <uid>
内核实现(KStorage 后端)
0.13.2 中 SU 白名单使用 KStorage(内核存储)作为后端,替代了 0.12.0 的简单链表:
// kernel/patch/common/sucompat.c
// UID 白名单操作(KStorage 后端,KSTORAGE_SU_LIST_GROUP gid=0)
int su_add_allow_uid(uid_t uid, uid_t to_uid, const char *scontext)
{
struct su_profile profile = {
.uid = uid,
.to_uid = to_uid,
};
strncpy(profile.scontext, scontext, SUPERCALL_SCONTEXT_LEN - 1);
// 写入 KStorage
return write_kstorage(su_kstorage_gid, uid, &profile, 0, sizeof(profile), true);
}
int su_remove_allow_uid(uid_t uid)
{
return remove_kstorage(su_kstorage_gid, uid);
}
// 检查 UID 是否被允许(RCU 读锁保护)
bool is_su_allow_uid(uid_t uid)
{
struct su_profile profile;
int rc = read_kstorage(su_kstorage_gid, uid, &profile, 0, sizeof(profile), false);
if (rc < 0) return false;
return profile.uid == uid;
}
// 模块排除检查(KSTORAGE_EXCLUDE_LIST_GROUP gid=1)
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;
}
int set_ap_mod_exclude(uid_t uid, int exclude)
{
return write_kstorage(exclude_kstorage_gid, uid, &exclude, 0, sizeof(exclude), true);
}
用户空间实现
// user_deprecated/android/sumgr.c:50-150
int sumgr_main(int argc, char **argv)
{
if (argc < 2) usage(EXIT_FAILURE);
const char *scmd = argv[1];
// ============ grant 命令 ============
if (!strcmp(scmd, "grant")) {
if (argc < 3) error(-EINVAL, 0, "no uid");
uid_t uid = (uid_t)atoi(argv[2]);
uid_t to_uid = 0; // 默认 root
char *scontext = "u:r:su:s0"; // 默认上下文
if (argc > 3) to_uid = (uid_t)atoi(argv[3]);
if (argc > 4) scontext = argv[4];
struct su_profile profile = {
.uid = uid,
.to_uid = to_uid,
};
strncpy(profile.scontext, scontext, sizeof(profile.scontext) - 1);
int ret = sc_su_grant_uid(key, &profile);
if (ret) error(-ret, 0, "grant failed");
fprintf(stdout, "grant uid %d success\n", uid);
return 0;
}
// ============ revoke 命令 ============
if (!strcmp(scmd, "revoke")) {
if (argc < 3) error(-EINVAL, 0, "no uid");
uid_t uid = (uid_t)atoi(argv[2]);
int ret = sc_su_revoke_uid(key, uid);
if (ret) error(-ret, 0, "revoke failed");
fprintf(stdout, "revoke uid %d success\n", uid);
return 0;
}
// ============ list 命令 ============
if (!strcmp(scmd, "list")) {
int nums = sc_su_allow_uid_nums(key);
if (nums <= 0) {
fprintf(stdout, "no allowed uid\n");
return 0;
}
uid_t uids[nums];
int ret = sc_su_list_allow_uids(key, uids, nums);
if (ret < 0) error(-ret, 0, "list failed");
for (int i = 0; i < nums; i++) {
fprintf(stdout, "%d\n", uids[i]);
}
return 0;
}
// ============ profile 命令 ============
if (!strcmp(scmd, "profile")) {
if (argc < 3) error(-EINVAL, 0, "no uid");
uid_t uid = (uid_t)atoi(argv[2]);
struct su_profile profile;
int ret = sc_su_allow_uid_profile(key, uid, &profile);
if (ret) error(-ret, 0, "get profile failed");
fprintf(stdout, "uid=%d\n", profile.uid);
fprintf(stdout, "to_uid=%d\n", profile.to_uid);
fprintf(stdout, "scontext=%s\n", profile.scontext);
return 0;
}
usage(EXIT_FAILURE);
return 0;
}
APD 集成(替代 user_init.sh)
变化说明
0.13.2 中 user_init.sh 已被删除。不再使用 shell 脚本进行初始化,改为通过 init.rc 注入直接调用 APD(APatch Daemon,/data/adb/apd)。
关键路径:
- APD 二进制:
/data/adb/apd - SELinux 策略工具:
/data/adb/ap/bin/magiskpolicy - AP 根目录:
/data/adb/ap/ - 日志目录:
/data/adb/ap/log/ - SU 命令:
/system/bin/kp(默认)或/system/bin/truncate
init.rc 注入内容(0.13.2)
user_rc_data 改为直接调用 APD 和 magiskpolicy,不再调用 user_init.sh:
# kernel/patch/android/userd.c:108-130(注入到 /dev/user_init.rc)
on early-init
exec -- truncate %s event early-init before
on init
exec -- truncate %s event init before
on late-init
exec -- truncate %s event late-init before
on post-fs-data
exec -- truncate su -Z u:r:magisk:s0 exec /data/adb/ap/bin/magiskpolicy --magisk --live
exec -- truncate su -Z u:r:magisk:s0 exec /data/adb/apd -s %s post-fs-data
on nonencrypted
exec -- truncate su -Z u:r:magisk:s0 exec /data/adb/apd -s %s services
on property:vold.decrypt=trigger_restart_framework
exec -- truncate su -Z u:r:magisk:s0 exec /data/adb/apd -s %s services
on property:sys.boot_completed=1
exec -- truncate su -Z u:r:magisk:s0 exec /data/adb/apd -s %s boot-completed
exec -- truncate su event boot-completed
exec -- truncate su -Z u:r:magisk:s0 exec /data/adb/apd uid-listener &
rm /dev/user_init.rc
exec -- truncate su -Z u:r:magisk:s0 -c "mv -f /dev/user_init_log/ /data/adb/ap/log/"
APD 生命周期事件:
| 事件 | 触发时机 | 说明 |
|---|---|---|
post-fs-data |
数据分区挂载后 | 早期初始化,加载包配置 |
services |
nonencrypted 或解密后 | 服务启动阶段 |
boot-completed |
sys.boot_completed=1 | 启动完成 |
uid-listener |
boot-completed 后台 | 后台监听 UID 变更 |
init.rc 多源注入(0.13.2 变化)
多个候选 RC 文件
0.12.0 仅 hook atrace.rc,0.13.2 改为多个候选 init.rc 路径:
// kernel/patch/android/userd.c:101-130
static const char *ORIGIN_RC_FILES[] = {
"/system/etc/init/hw/init.rc",
"/init.rc",
"/vendor/etc/init/hw/init.target.rc",
};
Hook 机制
通过 hook __NR_openat 系统调用实现 RC 文件替换:
// kernel/patch/android/userd.c:1512-1610
// before_openat: 检查打开的文件路径是否匹配候选 RC 文件
static void before_openat(hook_fargs4_t *args, void *udata)
{
const char __user *filename = (const char __user *)args->arg1;
// 如果匹配候选 RC 文件之一,记录标志
}
// after_openat: 将已打开的 fd 重定向到 /dev/user_init.rc
static void after_openat(hook_fargs4_t *args, void *udata)
{
// 关闭原始 fd,打开 /dev/user_init.rc
// 该文件包含原始 RC 内容 + 注入的 APD 启动命令
}
注入流程:
- Hook
__NR_openat,拦截 init 进程打开候选 RC 文件 - 将原始 RC 内容与
user_rc_data合并写入/dev/user_init.rc - 将 fd 重定向到
/dev/user_init.rc - 启动完成后删除
/dev/user_init.rc
LSM Hook 监听(0.13.2 全新)
概念
0.13.2 引入了 LSM(Linux Security Module)hook,通过监听文件重命名操作来检测 packages.list 的更新。这使得 KernelPatch 能在包管理器安装/卸载应用时自动刷新 trusted_manager_uid。
实现
// kernel/patch/android/userd.c:1612-1679
// hook security_path_rename 或 security_inode_rename(回退)
static void refresh_packages_list_tmp_rename(hook_fargs5_t *args, void *udata)
{
// 提取 dentry 的完整路径
struct dentry *old_dentry = (struct dentry *)args->arg1;
char path_buf[256];
char *path = dentry_path_raw(old_dentry, path_buf, sizeof(path_buf));
// 检查是否为 packages.list.tmp -> packages.list 的重命名
if (strstr(path, "packages.list.tmp")) {
// 触发 trusted_manager_uid 刷新
refresh_trusted_manager_state();
}
}
// 安装 LSM hook
void install_packages_list_monitor()
{
unsigned long addr;
// 优先尝试 security_path_rename
addr = kallsyms_lookup_name("security_path_rename");
if (addr) {
hook_wrap5((void *)addr, refresh_packages_list_tmp_rename, 0, 0);
return;
}
// 回退到 security_inode_rename
addr = kallsyms_lookup_name("security_inode_rename");
if (addr) {
hook_wrap5((void *)addr, refresh_packages_list_tmp_rename, 0, 0);
}
}
刷新流程
packages.list.tmp 重命名
|
v(LSM hook 触发)
refresh_trusted_manager_state()
|
v
重新查找 trusted_manager APK
|
v
重新验证 APK 签名
|
v
重新查找 UID
|
v
更新 trusted_manager_uid
触发时机:
- 包管理器在安装/更新/卸载应用时,会先写入
packages.list.tmp,然后原子性重命名为packages.list - LSM hook 拦截此重命名操作,自动刷新 trusted_manager_uid
- 这确保了管理器应用更新后,新的 UID 能被即时识别
为何使用 LSM 而非其他方式:
- LSM hook 是内核级别的监听,无需用户态轮询
- 使用 LSM 可以避免侧信道攻击(0.13.2 变更说明中提到)
- 对于低版本不支持 LSM 的内核,通过用户态方式处理
AP 包配置加载(0.13.2 全新)
load_ap_package_config()
// kernel/patch/android/userd.c:1133-1322
int load_ap_package_config()
{
// 配置文件路径
const char *config_path = "/data/adb/ap/package_config";
// 打开并逐行解析 CSV
struct file *f = filp_open(config_path, O_RDONLY, 0);
if (IS_ERR(f)) return PTR_ERR(f);
int count = 0;
char line[512];
while (read_line(f, line, sizeof(line)) > 0) {
// 解析 CSV 字段
char *pkg, *exclude_str, *allow_str, *uid_str, *to_uid_str, *sctx;
parse_csv_line(line, &pkg, &exclude_str, &allow_str,
&uid_str, &to_uid_str, &sctx);
uid_t uid = simple_strtoul(uid_str, NULL, 10);
uid_t to_uid = simple_strtoul(to_uid_str, NULL, 10);
int exclude = simple_strtol(exclude_str, NULL, 10);
int allow = simple_strtol(allow_str, NULL, 10);
// 验证 UID 范围
if (uid > UINT_MAX) continue;
// 处理排除标志
if (exclude) {
set_ap_mod_exclude(uid, exclude);
}
// 处理允许标志
if (allow) {
// 截断 sctx 到 SUPERCALL_SCONTEXT_LEN
su_add_allow_uid(uid, to_uid, sctx);
}
count++;
}
filp_close(f, NULL);
return count;
}
CSV 配置格式
文件路径:/data/adb/ap/package_config
格式:pkg,exclude,allow,uid,to_uid,sctx
| 字段 | 类型 | 说明 |
|---|---|---|
| pkg | string | 包名(解析但未直接使用) |
| exclude | int | 排除标志(1=排除该 UID 使用 supercall) |
| allow | int | 允许标志(1=允许该 UID 获取 SU) |
| uid | int | 应用 UID |
| to_uid | int | 目标 UID(通常为 0=root) |
| sctx | string | SELinux 上下文(最长 95 字节) |
示例:
com.example.app,0,1,10123,0,u:r:su:s0
com.malware.app,1,0,10456,0,
com.trusted.tool,0,1,10789,0,u:r:magisk:s0
supercall 集成
包配置可通过 supercall 命令 SUPERCALL_AP_LOAD_PACKAGE_CONFIG (0x100d) 加载,无需任何认证:
// kernel/patch/common/supercall.c:287-290
#ifdef ANDROID
case SUPERCALL_AP_LOAD_PACKAGE_CONFIG:
return call_ap_load_package_config();
#endif
Execve Hook 与自动 SU
execve/execveat Hook
0.13.2 hook 了 __NR_execve 和 __NR_execveat,用于:
// kernel/patch/android/userd.c:1360-1508
// 1. 检测 init 进程启动阶段
// - 首次 /system/bin/init 或 /init 执行 -> pre_init_first_stage()
// - --second-stage 参数或 INIT_SECOND_STAGE=1 -> pre_init_second_stage()
// 2. 检测首个 app_process 启动
// - app_process/app_process64 首次执行 -> on_first_app_process()
// - 触发 refresh_trusted_manager_state()
// 3. 自动 SU(Trusted Manager 进程)
// - 由 trusted_manager_uid 执行的进程自动获得 SU 权限
// - 使用 all_allow_sctx 作为 SELinux 上下文
supercmd 命令(0.13.2 变化)
新增子命令
0.13.2 在 supercmd 中新增了 reload-cfg 和 event 子命令:
# 重新加载配置(刷新 trusted_manager + 重新加载包配置)
supercmd su reload-cfg
# 触发事件
supercmd su event <EVENT> [ARGS]
reload-cfg 实现
// user/supercmd.c:464-469
case "reload-cfg":
refresh_trusted_manager_state(); // 重新验证管理器签名
load_ap_package_config(); // 重新加载包配置
break;
event 子命令
// kernel/patch/android/user_event.c:11-31
int report_user_event(const char *event, const char *args)
{
// 支持的事件
if (!strcmp(event, "early-init")) {
// 早期初始化阶段
} else if (!strcmp(event, "init")) {
// 初始化阶段
} else if (!strcmp(event, "late-init")) {
// 晚期初始化阶段
} else if (!strcmp(event, "post-fs-data")) {
// 触发 load_ap_package_config()
load_ap_package_config();
} else if (!strcmp(event, "boot-completed")) {
// 启动完成
} else if (!strcmp(event, "uid_listener") &&
!strcmp(args, "package-list-updated")) {
// 包列表更新,触发 trusted_manager 刷新
refresh_trusted_manager_state();
}
return 0;
}
事件与 init.rc 的关系:
- init.rc 注入的
exec -- truncate %s event <EVENT>命令会触发对应事件 post-fs-data事件中自动加载包配置boot-completed事件后启动 APD uid-listener
JNI 接口 - apjni.cpp
功能
apjni 提供 Java 应用调用 KernelPatch 的接口。
JNI 函数定义
// user_deprecated/android/apjni.cpp
#include <jni.h>
#include "supercall.h"
extern "C" {
// 验证 KernelPatch
JNIEXPORT jboolean JNICALL
Java_com_kernelpatch_KernelPatch_isInstalled(JNIEnv *env, jclass clazz, jstring key)
{
const char *c_key = env->GetStringUTFChars(key, NULL);
long ret = sc_hello(c_key);
env->ReleaseStringUTFChars(key, c_key);
return (ret == SUPERCALL_HELLO_MAGIC) ? JNI_TRUE : JNI_FALSE;
}
// 获取版本
JNIEXPORT jint JNICALL
Java_com_kernelpatch_KernelPatch_getVersion(JNIEnv *env, jclass clazz, jstring key)
{
const char *c_key = env->GetStringUTFChars(key, NULL);
uint32_t ver = sc_kp_ver(c_key);
env->ReleaseStringUTFChars(key, c_key);
return (jint)ver;
}
// SU 权限提升
JNIEXPORT jboolean JNICALL
Java_com_kernelpatch_KernelPatch_su(JNIEnv *env, jclass clazz,
jstring key, jint uid, jstring scontext)
{
const char *c_key = env->GetStringUTFChars(key, NULL);
const char *c_scontext = scontext ? env->GetStringUTFChars(scontext, NULL) : "u:r:su:s0";
struct su_profile profile = {
.uid = getuid(),
.to_uid = (uid_t)uid,
};
strncpy(profile.scontext, c_scontext, sizeof(profile.scontext) - 1);
int ret = sc_su(c_key, &profile);
env->ReleaseStringUTFChars(key, c_key);
if (scontext) env->ReleaseStringUTFChars(scontext, c_scontext);
return (ret == 0) ? JNI_TRUE : JNI_FALSE;
}
// 加载 KPM
JNIEXPORT jboolean JNICALL
Java_com_kernelpatch_KernelPatch_loadKpm(JNIEnv *env, jclass clazz,
jstring key, jstring path, jstring args)
{
const char *c_key = env->GetStringUTFChars(key, NULL);
const char *c_path = env->GetStringUTFChars(path, NULL);
const char *c_args = args ? env->GetStringUTFChars(args, NULL) : "";
int ret = sc_kpm_load(c_key, c_path, c_args);
env->ReleaseStringUTFChars(key, c_key);
env->ReleaseStringUTFChars(path, c_path);
if (args) env->ReleaseStringUTFChars(args, c_args);
return (ret >= 0) ? JNI_TRUE : JNI_FALSE;
}
} // extern "C"
Java 端使用
// Android 应用
public class KernelPatch {
static {
System.loadLibrary("kpatch");
}
public static native boolean isInstalled(String key);
public static native int getVersion(String key);
public static native boolean su(String key, int uid, String scontext);
public static native boolean loadKpm(String key, String path, String args);
}
// 使用
String key = "mykey123";
if (KernelPatch.isInstalled(key)) {
int version = KernelPatch.getVersion(key);
Log.i("KP", "Version: " + Integer.toHexString(version));
// 获取 root
if (KernelPatch.su(key, 0, "u:r:su:s0")) {
Log.i("KP", "Got root!");
}
}
SELinux 上下文管理
常见 SELinux 上下文
# SU 上下文(完全权限)
u:r:su:s0
# Magisk 上下文(APD 使用)
u:r:magisk:s0
# System 上下文
u:r:system_app:s0
# Untrusted App 上下文
u:r:untrusted_app:s0
# Platform App 上下文
u:r:platform_app:s0
set_all_allow_sctx() - 全局豁免上下文
// kernel/patch/common/accctl.c:51-69
int set_all_allow_sctx(const char *sctx)
{
if (!sctx || !sctx[0]) {
// 清除豁免
all_allow_sctx[0] = 0;
all_allow_sid = SECSID_NULL;
dsb(ish);
logkfd("clear all allow sconetxt\n");
return 0;
}
// 转换上下文为 SID
int rc = security_secctx_to_secid(sctx, strlen(sctx), &all_allow_sid);
if (!rc && all_allow_sid != SECSID_NULL) {
strncpy(all_allow_sctx, sctx, sizeof(all_allow_sctx) - 1);
all_allow_sctx[sizeof(all_allow_sctx) - 1] = '\0';
dsb(ish);
logkfd("set all allow sconetxt: %s, sid: %d\n", all_allow_sctx, all_allow_sid);
}
return rc;
}
使用:
# 设置豁免上下文
kpatch key su -c "echo 'u:r:su:s0' > /sys/kernel/kp/all_allow_sctx"
# 所有切换到 u:r:su:s0 上下文的进程都绕过 SELinux
task_ext - 任务扩展数据
概念
task_ext 是附加到每个 task_struct 的扩展数据,用于存储 KernelPatch 相关信息。
结构定义
// kernel/patch/include/taskext.h
struct task_ext {
pid_t pid; // 进程 PID
int sel_allow; // SELinux 豁免标志
int allow_all; // 全权限标志
// ... 其他字段
};
获取和使用
// kernel/patch/common/taskob.c
struct task_ext *get_task_ext(struct task_struct *task)
{
// 从 task_struct 末尾获取扩展数据
return (struct task_ext *)((uintptr_t)task + task_ext_offset);
}
void init_task_ext(struct task_struct *task)
{
struct task_ext *ext = get_task_ext(task);
memset(ext, 0, sizeof(struct task_ext));
ext->pid = task->pid;
ext->sel_allow = 0;
ext->allow_all = 0;
}
// Hook copy_process,在新进程创建时初始化
void after_copy_process(hook_fargs12_t *args, void *udata)
{
struct task_struct *task = (struct task_struct *)args->ret;
if (task && !IS_ERR(task)) {
init_task_ext(task);
}
}
Android 初始化流程(0.13.2 完整启动序列)
内核启动
|
v
KernelPatch 初始化
|-- syscall_init() 安装 syscall hook
|-- supercall_install() 安装 supercall
|-- hook execve/execveat 拦截进程创建
|-- hook openat 拦截 RC 文件打开
+-- install LSM hooks 监听 packages.list 变更
|
v
init 第一阶段 (execve hook 检测)
|-- pre_init_first_stage()
|
v
init 第二阶段 (--second-stage 检测)
|-- pre_init_second_stage()
+-- 准备 /dev/user_init.rc(原始 RC + 注入命令)
|
v
init 解析 RC 文件 (openat hook 重定向)
|-- 读取 /dev/user_init.rc 而非原始 RC
|
v
on early-init
|-- supercmd event early-init
|
v
on post-fs-data
|-- magiskpolicy --magisk --live 加载 SELinux 策略
|-- apd -s <key> post-fs-data APD 数据分区初始化
+-- load_ap_package_config() 加载包配置(通过 event)
|
v
on nonencrypted / vold.decrypt
|-- apd -s <key> services APD 服务启动
|
v
首个 app_process 启动 (execve hook 检测)
|-- on_first_app_process()
+-- refresh_trusted_manager_state() 首次验证管理器签名
|
v
on sys.boot_completed=1
|-- apd -s <key> boot-completed APD 启动完成
|-- supercmd event boot-completed
|-- apd uid-listener & 后台 UID 监听
|-- rm /dev/user_init.rc 清理临时文件
+-- mv logs to /data/adb/ap/log/ 转移日志
|
v
运行时
|-- LSM hook 监听 packages.list.tmp 重命名
+-- 自动刷新 trusted_manager_uid
安全模式(Safe Mode)
概念
安全模式允许在 KernelPatch 出现问题时禁用某些功能。
检测方式(0.13.2)
// kernel/patch/android/userd.c:1681-1701
// Hook input_handle_event() 检测音量键
static int volume_down_count = 0;
void on_input_event(...)
{
// 检测连续 3 次音量下键按下
if (is_volume_down_press(event)) {
volume_down_count++;
if (volume_down_count >= 3) {
android_is_safe_mode = 1;
}
} else {
volume_down_count = 0;
}
}
用户空间查询:
# 通过 supercall 查询(需要 trusted_caller)
# SUPERCALL_SU_GET_SAFEMODE (0x2008)
# 或通过 supercmd
supercmd su get-safemode
关键文件与路径参考
配置文件
| 路径 | 说明 |
|---|---|
/data/adb/ap/package_config |
AP 包配置(CSV 格式) |
/data/system/packages.list |
系统包列表(UID 查找) |
/data/system/packages.list.tmp |
临时包列表(LSM 监听目标) |
/data/system/packages.xml |
XML 包数据库(APK 路径回退查找) |
二进制文件
| 路径 | 说明 |
|---|---|
/data/adb/apd |
APatch 守护进程 |
/data/adb/ap/bin/magiskpolicy |
SELinux 策略工具 |
/system/bin/truncate |
supercmd 路径 |
/system/bin/kp |
SU 命令(默认) |
临时文件
| 路径 | 说明 |
|---|---|
/dev/user_init.rc |
注入的 init.rc(启动后删除) |
/dev/user_init_log/ |
启动日志(启动后转移) |
数据目录
| 路径 | 说明 |
|---|---|
/data/adb/ap/ |
APatch 根目录 |
/data/adb/ap/log/ |
日志目录 |
/data/app/ |
应用安装目录(APK 签名验证扫描) |
实际使用场景
场景 1:Magisk 模块集成
# Magisk 模块结构
module_id/
+-- module.prop
+-- service.sh
+-- system/
+-- bin/
+-- kpatch
# service.sh
#!/system/bin/sh
MODDIR=${0%/*}
KEY="module_key_123"
# 等待启动完成
while [ "$(getprop sys.boot_completed)" != "1" ]; do
sleep 1
done
# 加载 KPM
$MODDIR/system/bin/kpatch $KEY kpm load /data/adb/modules/my_module.kpm
# 授权 shell
$MODDIR/system/bin/kpatch $KEY sumgr grant 2000 0 u:r:su:s0
场景 2:应用集成
// Android 应用
public class RootManager {
private static final String KEY = "app_key_123";
public static boolean requestRoot() {
try {
if (!KernelPatch.isInstalled(KEY)) {
return false;
}
return KernelPatch.su(KEY, 0, "u:r:su:s0");
} catch (Exception e) {
Log.e("Root", "Failed to get root", e);
return false;
}
}
public static void loadCustomModule() {
String modulePath = "/data/data/com.example.app/files/module.kpm";
KernelPatch.loadKpm(KEY, modulePath, "app_mode=1");
}
}
场景 3:使用 supercmd 管理
# 重新加载配置(trusted_manager + 包配置)
supercmd su reload-cfg
# 触发事件
supercmd su event post-fs-data
# 查看安全模式状态
supercmd su get-safemode
与其他 Root 方案对比
| 特性 | KernelPatch | Magisk | KernelSU |
|---|---|---|---|
| 内核修改 | 修补镜像 | 启动时注入 | 修改内核源码 |
| 无需源码 | 是 | 是 | 否 |
| Bootloader 锁定 | 需解锁 | 需解锁 | 需解锁 |
| SELinux 处理 | 完全绕过 | Permissive | 部分绕过 |
| 模块系统 | KPM | Magisk 模块 | KernelSU 模块 |
| 应用隐藏 | 有限 | MagiskHide | 有限 |
| OTA 更新 | 需重新 patch | 需重新刷入 | 需重新编译 |
| 管理器认证 | APK 签名验证 | 包名检测 | 包名检测 |
| 包配置 | CSV 文件 | Magisk Manager DB | KernelSU Manager DB |
故障排查
问题 1:SELinux 仍然拒绝访问
# 检查 SELinux 状态
$ getenforce
Enforcing
# 检查当前上下文
$ id -Z
u:r:untrusted_app:s0
# 检查是否正确设置
$ kpatch key su
# id -Z
u:r:su:s0 # 应该变为 su
# 如果仍然是 untrusted_app,说明 SELinux 绕过失败
调试:
# 查看 avc_denied hook 是否成功
$ adb shell dmesg | grep "hook avc_denied"
KP hook avc_denied: 0 # 0 表示成功
问题 2:UID 授权不生效
# 列出授权的 UID
$ kpatch key sumgr list
2000
# 尝试 SU
$ su # 假设当前 UID 是 1000
Permission denied
# 原因:UID 1000 未授权
$ kpatch key sumgr grant 1000 0 u:r:su:s0
$ su
# whoami
root # 成功
问题 3:Trusted Manager 未被识别
# 检查 trusted_manager_uid 是否已设置
# 通常在首个 app_process 启动时自动设置
# 手动刷新
supercmd su reload-cfg
# 检查 APK 是否存在于 /data/app/
ls /data/app/ | grep apatch
# 检查 packages.list 中的 UID
grep "me.bmax.apatch" /data/system/packages.list
问题 4:包配置加载失败
# 检查配置文件是否存在
ls -la /data/adb/ap/package_config
# 检查格式(每行 6 个逗号分隔字段)
cat /data/adb/ap/package_config
# 手动触发重新加载
supercmd su reload-cfg
# 查看内核日志
dmesg | grep "package_config"
总结
Android 特定功能的核心价值(0.13.2):
- SELinux 完全绕过:对授权进程透明
- Trusted Manager 签名验证:APK 证书级别的管理器认证(全新)
- 细粒度权限控制:基于 UID 的三层授权 + 排除机制
- APD 原生集成:直接通过 init.rc 注入调用 APD(替代 user_init.sh)
- LSM 实时监听:包列表变更时自动刷新 trusted_manager_uid(全新)
- CSV 包配置:灵活的 per-package 权限配置(全新)
- 应用集成友好:JNI 接口和 supercmd 命令行
- 多 RC 文件源:不再局限于单一 RC 文件 hook
适用场景:
- Root 管理应用
- 安全研究工具
- 系统定制和优化
- 应用权限提升
注意事项:
- 需要解锁 Bootloader
- 会破坏 Verified Boot
- 仅用于合法用途
下一篇:14-备份与恢复机制.md - 详解三大备份的完整数据流
文档版本:2.0
最后更新:2026-06-26
对应代码版本:0.13.2
评论
- 还没有评论,来说点什么吧。