
基于kernelsu next 3.1.0做了一个支持lkm模式下临时卸载模块的功能。现在无需和少部分牛逼的root检测app斗智斗勇了(比如bochk用的promon shield方案),要用检测root的app的时候临时卸载就行。临时卸载会短暂黑屏,就检测不到了,欢迎参加测试。网上的一些root检测app可能还是能检测到异常,很正常,能过app即可。
使用claude opus4.6开发,修复了pstore以记录内核和app崩溃日志,如果pstore无效可以使用高通自带方案dump日志,
开源地址:https://github.com/fjh1997/KernelSU-Next
pstore修复方案如下:
先找出自己设备地址的分配方案:
查看adb shell cat /proc/iomem | head -n 20 (查看物理内存分布)
用payload-dumper-go和Android_boot_image_editor解包vendor_boot分区得到很多dts文件,找到芯片型号接近的几个 .dts 文件,找到 ramoops_region,把里面的参数根据物理内存分布直接改成下面这样,我是一加Ace3 Pro:
1 | ramoops_region { |
重新编译这 8 个文件,打包 vendor_boot.img 和对应的vbmeta一起刷入。
开机后,直接用 dmesg | grep ramoops。
以下是claude opus4.6记录:
KernelSU-Next 代码修改总结
一、fjh1997 commit a88b3885 审查结果
结论: 代码质量良好,无需删除任何内容。
| 文件 | 改动 | 评估 |
|---|---|---|
allowlist.c |
try_module_get/module_put 保护 task_work |
✅ 正确,防止 rmmod 后 use-after-free |
app_profile.c |
root_groups 从 static 改为 heap alloc |
✅ 正确,防止 rmmod 后悬空指针 |
kernel_umount.c |
module ref 保护 umount task_work | ✅ 正确 |
setuid_hook.c |
module ref 保护 install-fd task_work | ✅ 正确 |
su_mount_ns.c |
module ref 保护 mount-ns task_work | ✅ 正确 |
supercalls.c |
do_prepare_unload() + mount_list cleanup |
✅ SIGKILL fd 持有者是合理的 |
ksud.c |
mutex 保护 kprobe 注册状态,恢复 init.rc fops | ✅ 正确,防止 double-unregister |
pkg_observer.c |
故意泄漏 fsnotify group (~1KB) | ⚠️ 可接受的 tradeoff,避免竞态 crash |
throne_tracker.c |
APK path hash 清理 | ✅ 正确 |
syscall_hook_manager.c |
unmark_all 在 unregister 之前 | ✅ 正确 |
ksu.c |
ksu_cred 故意泄漏 + rcu_barrier |
✅ 注释充分,避免 rcu 回调后 put_cred crash |
二、rmmod 清理代码修改(内核层)
执行顺序
1 | kernelsu_exit() |
[!IMPORTANT]
ksu_umount_all()必须在ksu_cred = NULL之前调用,因为它需要override_creds(ksu_cred)获取权限来执行 umount。
三、用户态深度清理(防御 Zygisk 痕迹)
背景:
高级模块通过手动 shell 脚本执行 mount --bind,不经过 KernelSU 核心框架,因此内核级的 ksu_umount_all() 无法清理它们。模块开发者往往使用匿名 tmpfs 伪装,所以我们需要精准定位目标。
修改文件:
render_diffs(file:///G:/下载/KernelSU-Next-dev/manager/app/src/main/java/com/rifsxd/ksunext/ui/screen/Settings.kt)
清理方案,注重极致安全性 (绝对无系统损坏风险):
在 Manager App 的“临时卸载”逻辑触发 rmmod 之前,通过 root shell 执行:
- 强制清理 Zygisk 文件:手动
umount -l /system/bin/app_process32和app_process64,剥离 Zygisk 劫持。(注:OS 级别app_process仅仅是一个文件,在原版镜像中绝不是独立挂载点。如果发生卸载报错会静默跳过,百分百安全)。 - 杀死残留进程:执行
killall -9 zygiskd,确保由于 fd 继承没被杀干净的 Zygisk 守护进程死亡。
[!CAUTION]
基于用户要求的“系统维稳”策略,我们已经移除了对/data/resource-cache、/system/etc/security/cacerts与/apex/.../cacerts的一切卸载操作。因为这些路径在原生安卓中就属于系统基础组件甚至是底层tmpfs的构成部分。不盲目执行 umount 能极大地降低由于 APEX 冲突及第三方框架耦合导致的断网或系统崩溃风险,我们将稳定性放在了最优先级。
KernelSU-Next rmmod 清理代码修改
问题
rmmod 只卸载了内核模块代码,但留下 3 类残留物被 BOCHK 检测到:
- SELinux policydb 中的
su域和规则 - 模块创建的 overlay 挂载
ksu_cred泄漏
Proposed Changes
1. SELinux Policy 恢复
[MODIFY] rules.c
添加 revert_kernelsu_rules() 函数:
- 将
su域从 permissive 改回 enforcing - 清除
su域的 所有 avtab allow 规则(遍历te_avtab,删除 source_type == su 的节点) - 重置 AVC 缓存使改动生效
[!WARNING]
完全从 policydb 中删除sutype 是不安全的(会导致 type_val_to_struct 下标错乱)。采用的策略是:将 su 域设为 enforcing + 清除所有 allow 规则,让su域变成一个无权限的死域。这对检测工具来说,和不存在效果相同。
[MODIFY] selinux.h
声明 revert_kernelsu_rules() 函数。
2. 全局 Overlay 挂载清理
[MODIFY] kernel_umount.c
添加 ksu_umount_all() 函数:
- 遍历
mount_list中的所有挂载点 - 用
init的 cred + mount namespace 执行path_umount() - 设置
ksu_module_mounted = false
[MODIFY] kernel_umount.h
声明 ksu_umount_all() 函数。
3. 主退出函数集成
[MODIFY] ksu.c
在 kernelsu_exit() 的 最前面 添加:
ksu_umount_all()— 先卸载所有挂载revert_kernelsu_rules()— 再恢复 SELinux
Verification Plan
[!IMPORTANT]
这是内核模块代码,无法用常规 unit test。需要在 Android 设备上实际验证。
Manual Verification
- 编译并加载修改后的 KSU 模块
- 安装 BOCHK 并确认检测到 root (
reason=22) - 执行
rmmod kernelsu - 验证 SELinux:
adb shell cat /sys/fs/selinux/enforce→ 应为1adb shell sesearch --allow -s su→ 应无结果或极少
- 验证挂载:
adb shell cat /proc/self/mountinfo | grep -i overlay→ 应无 KSU overlay
- 重新打开 BOCHK → 验证
reason码是否降低或消失
KernelSU-Next 临时卸载 vs BOCHK Root检测 — 为什么仍被检测
KernelSU-Next 的隐藏机制
KernelSU-Next 有三个核心隐藏功能:
1. KERNEL_UMOUNT — 模块挂载卸载
文件: kernel_umount.c
1 | 触发链: |
只卸载: KSU 模块添加的 overlay 挂载点(如 Zygisk 模块注入的 /system 覆盖)
2. AVC_SPOOF — SELinux 审计欺骗
文件: extras.c
1 | kprobe hook slow_avc_audit(): |
只隐藏: SELinux avc: denied 日志中的 su context,不隐藏实际的 SELinux policy 修改
3. SU_COMPAT — su 二进制兼容
文件: sucompat.c
KernelSU 没有落地 su 二进制文件,通过内核 hook 直接处理 su 请求
BOCHK 检测 vs KernelSU-Next 隐藏 — 逐项对照
| BOCHK 检测方法 | KSU 是否隐藏 | 原因 |
|---|---|---|
RootBeer checkSuExists() |
❌ 未隐藏 | Runtime.exec(“which su”) — KSU 不落地su文件,但某些模块可能会 |
RootBeer checkForRWPaths() |
❌ 未隐藏 | 读 /proc/mounts 检查系统分区 rw → KSU 的 overlay mount 可能暴露 rw 挂载 |
RootBeer checkForRootNative() |
❌ 未隐藏 | Native JNI 检测 → 扫描 /system/xbin/su 等路径 |
RootBeer detectRootManagementApps() |
❌ 未隐藏 | 检查已安装App包名 (Magisk/KSU Manager) → KSU Manager 依然存在 |
RootBeer detectRootCloakingApps() |
❌ 未隐藏 | 检查 Shamiko/RootCloak 等包名 |
Promon /mountinfo 扫描 |
⚠️ 部分 | KSU umount 只移除模块挂载,但内核本身的修改痕迹仍在 mountinfo |
Promon SVC openat(/proc/self/maps) |
❌ 完全无效 | 直接 SVC 系统调用,不经过任何可 hook 的层 |
| Promon GOT 验证 | ❌ 完全无效 | 检查 libc GOT 表完整性 — KSU 的 syscall hook 不修改 GOT |
| Promon Frida线程检测 | ❌ 不相关 | 这是检测 Frida,不是 root |
| Promon 设备完整性 | ❌ 未隐藏 | Play Integrity API → bootloader unlocked / custom kernel |
reason=22 (你的设备) 具体泄露点
reason=22 = 二进制 10110:
- bit4 = 1:
checkSuExists()— 最可能的泄露 - bit2 = 1:
checkForRWPaths()— 模块 overlay mount 泄露 - bit1 = 1:
checkForRootNative()— native 层泄露
bit4: checkSuExists() 触发原因
1 | // RootBeer 实现 |
KernelSU-Next 不落地 su 文件,但:
- 如果安装了 KSU 模块(如 Zygisk-Next),可能会在
/system/binoverlay 中注入 su wrapper KERNEL_UMOUNT只在 deny list 中的 App (如 BOCHK)启动时卸载 overlay- 但
checkSuExists()用Runtime.exec("which su")在全局 PATH 中搜索 - KSU 的
sucompat.chook 了faccessatsyscall 来处理 su — 这个 hook 仍在内核中活跃
bit2: checkForRWPaths() 触发原因
1 | // RootBeer 实现 |
KernelSU 模块通过 magic mount (overlay) 修改 /system,这些 overlay 在 kernel_umount.c 的 mount_list 中:
- 如果 BOCHK 不在 deny list → overlay mount 完全可见
- 即使在 deny list 中,umount 是异步的 (
task_work),可能存在竞态条件
bit1: checkForRootNative() 触发原因
RootBeer 的 native 检测通过 JNI 调用 libtool-checker.so:
1 | // Native root detection |
KernelSU-Next 修改了 SELinux policy(通过 sepolicy.c),这些修改在内核 policydb 中持久存在,native 代码可以检测到新增的 policy rules。
根本原因分析
graph TD
A["KernelSU-Next 临时卸载"] --> B["kernel_umount.c
只卸载 module overlay mounts"]
A --> C["extras.c
只 spoof SELinux audit log"]
D["BOCHK Java层检测"] --> E["RootBeer 10项检测
走 Java/JNI 标准 API"]
D --> F["PackageManager
检查 KSU Manager 包名"]
G["BOCHK Native层检测"] --> H["Promon SVC #0
直接系统调用 → 内核
绕过所有 userspace"]
B -.->|"部分有效"| E
B -.->|"无效"| H
C -.->|"无效"| E
style H fill:#f44,color:#fff
style B fill:#ffa,color:#000
style C fill:#ffa,color:#000
核心问题: KernelSU-Next 的隐藏机制工作在 mount namespace 层面,只处理文件系统覆盖。但 BOCHK 的检测工作在多个层面:
- Java 层:
Runtime.exec("which su"),PackageManager.getInstalledApplications()— KSU umount 无法隐藏非文件系统的痕迹 - Native JNI 层:
libtool-checker.so直接检测 — 不经过 KSU 的任何 hook - Promon SVC 层: 直接
SVC #0读取/proc/self/maps和/proc/self/mountinfo— 完全绕过所有 userspace hook,包括 KSU 的 syscall table hook
具体泄露清单
| # | 泄露类型 | 检测方 | KSU 组件 | 状态 |
|---|---|---|---|---|
| 1 | KSU Manager App 包名 | RootBeer detectRootManagementApps |
无隐藏 | ⚠️ 需要在 deny list 中隐藏 |
| 2 | /system overlay mount |
RootBeer checkForRWPaths |
kernel_umount.c |
⚠️ 仅对 deny list UID 有效 |
| 3 | su 相关文件路径 | RootBeer checkSuExists |
sucompat.c hook |
⚠️ hook 在内核仍活跃 |
| 4 | SELinux policy 修改 | RootBeer native | sepolicy.c |
❌ 无法隐藏内核 policydb |
| 5 | Bootloader 解锁状态 | Promon 设备完整性 | 无 | ❌ 硬件层面无法隐藏 |
| 6 | /proc/self/mountinfo |
Promon SVC 直读 | 无 | ❌ SVC 绕过所有 userspace |
| 7 | 内核模块痕迹 | Promon /proc/modules |
无 | ❌ KSU 以模块加载时可见 |
| 8 | ro.debuggable/build props |
Promon 模拟器检测 | 无 | ⚠️ 取决于 ROM |
建议
要绕过 BOCHK 的检测,需要在多个层面同时隐藏:
- 确保 BOCHK 在 deny list 中 — 否则
kernel_umount完全不工作 - 隐藏 KSU Manager — 改包名或从已安装列表中隐藏
- 处理 Promon SVC 检测 — 需要在内核层面 hook
sys_openat返回假的 maps/mountinfo 给特定进程 - 隐藏 SELinux policy —
extras.c的avc_spoof只隐藏 audit 日志,不隐藏 policy 本身 - 处理 Play Integrity — bootloader 解锁 → 需要 Play Integrity Fix 模块
说些什么吧!