Repository navigation
Releases: hmascs/KSuRoot
Release list
KSuRoot 4.1.2 —— 从镜像读 vr.ko 标记偏移 + 导入完整刷机包
KSuRoot 4.1.2 —— 从镜像读 vr.ko 标记偏移 + 导入完整刷机包
versionCode = 412·versionName = 4.1.2· AOSP testkey 签名(a40da80a…5dc,可直接覆盖 4.1.1 原地升级)
上一个正式版本:4.1.1
单测 41 个类 / 417 个用例 · 0 失败(1 条环境门控跳过)
一、vr.ko 的标记偏移:从「赌一个」变成「读出来」
vr.ko 会给来自 app 的 task 打两个标记字节,该 task 一旦持有 euid 0,
sys_exit 探针就把它杀掉 —— 提权看着成功,子进程随即被杀,日志里一切正常。
要抹掉那两个字节,就得知道它们在哪个偏移。而那是编译期烤进载荷、patch 不了的。
麻烦在于它有 0x04 / 0x06 两种取值 —— 而且同一台设备的两份 vr.ko 就各一种:
| 路径 | vermagic | tag A |
|---|---|---|
lib/modules/vr.ko |
…aarch64 vivo |
0x04 |
lib/modules/6.1-gki/vr.ko |
…aarch64 |
0x06 |
两份的 srcversion 完全相同(同一份源码编的),差别只在编译配置。
这台机器实际加载的那份 vr.ko,就在 vendor_boot.img 里。 所以:
- 蓝厂方案现在要求你提供
vendor_boot.img—— 应用从里面那份平铺的
lib/modules/vr.ko(按 Android 的modprobe查找顺序,uname -r是完整串而非
6.1-gki,命中的就是平铺那份)反汇编出真正的偏移; - 然后改写载荷里那个值,而不是"对不上就停下";
- 通用方案不需要它:通用方案不做 vr.ko 绕过,也就没有这个偏移要定。
界面上会直接把结论摊开:本机 vr.ko tag A = 0x04 · 已改写载荷 3 处。
二、四份自带/自编库强制配自己的档位
打补丁是「拿档位里的旧值去 .so 里找字面量」,所以库与档位必须同源。
而"选哪份库"按结构体族走、"选哪一档"按三级路由(内核串)走 —— 两条路会选岔:
- X100 Pro 6.1.145:库是
libbaseline_6_1.so(旧值来自 tokay target.h),
档位却命中上游档up-6-1-145-…(旧值来自 GhostLock offsets.h)→ 26 项一处没匹配上 - 本机 PD2463 6.6.89:库是
libbs.so(旧值来自 PD2520 target.h),
档位命中up-6-6-89-…-vivo→ 25 项一处没匹配上
两次的产物都只是原样拷贝,装到机器上必然失败 —— 而界面显示的是"跑完了"。
现在四份库全部强制配对,选岔了由零命中闸门当场拦下:
| 库 | 配到 |
|---|---|
libbaseline_6_1.so |
baseline-6-1-tokay |
libbaseline_6_12.so |
baseline-6-12-honor |
libbs.so |
PD2520-BP2A.250605.031.A3 |
libionstack.so |
IONSTACK-P10-CP2A.260605.012 |
三、导入完整刷机包
载荷构建页新增入口:直接选你下载的 OTA zip 或 fastboot tgz,
它自己把 boot 和 vendor_boot 一起取出来 —— 不用你先解压,也不用手动提取。
- OTA zip:认
payload.bin,支持REPLACE/REPLACE_XZ/REPLACE_ZSTD/REPLACE_BZ/ZERO/DISCARD,ZIP64 也认 - fastboot tgz:流式解析 gzip + tar,只把需要的那一个文件读出来
实测:真实的 6.5 GB 完整包上跑通,boot / vendor_boot 与独立参考实现
(Python + zstd CLI)逐字节一致。
⚠️ 代价要说明白:完整包要整份拷进应用缓存才能随机读(ZIP 中央目录在包尾,
而 SAF 的 uri 变不成 File)。6.5 GB 的包就要拷 6.5 GB —— 空间不够会当场拒绝
并说清差多少,抽完立刻删缓存(失败路径也删)。
四、本版修掉的其它问题
- 蓝厂方案被自己的闸门 100% 拦死:构建开始时重建状态时把
vendorBootName清成了"",
而闸门正是拿它判的 → 选不选 vendor_boot 都被拦。现在状态重置用copy保留输入,
且判据改用真正的输入句柄,不再拿展示名当控制变量。 payloadSource两份来源不同步:Compose 状态与持久化偏好各一份,
而真正决定装哪份载荷的是偏好。构建自动落库那条路径只写了偏好 →
界面高亮「内置」,点安装装的却是刚构建的自定义载荷。- 移交 root 那一串弹窗统一到 miuix(确认 / 进行中 / 结果 / 选管理器)。
它们原来还是 Material3AlertDialog;换成 miuixOverlayDialog时
必须放进 Scaffold 里 —— miuix 的 Overlay 组件依赖 Scaffold 提供的
CompositionLocal,放在外面根本不渲染(表现就是"点了没反应")。 KallsymsFinder.kt里有个裸 NUL 字节(注释里想写x\0,写进了真的0x00)→
grep把整个 1161 行文件当二进制跳过。
五、边界(不掩饰)
- 「抹标记」这一步仍然没有在任何真机上跑过。
vr detag只证明代码编进去了,
不证明它抹对了、更不证明抹完就不被杀 —— 三族基线一律仍标beta。 - 整包导入没在真机跑过(只做了编译 + 单测 + 在真实包上的离线验证)。
- fastboot tgz 没有厂商真包样本:成功路径是用 GNU tar 现场造包验证的,
错误路径用了真实 tar.gz。 0x04/0x06由什么决定还没查清:目前只有"vermagic 里含vivo"这条
5 样本的相关性,因果未查明。所以别把它当规律用。- 第三方载荷的许可证正文仍未补齐:随包 127 份
.so来自多个上游仓库,
登记表里逐条署了名(source字段写明仓库与文件名),但许可证正文只带了
GhostLock 的 Apache-2.0。其中一份来自
qsvggff-spec/oppo-A5-PRO-5G-CVE-2026-43499(MIT,
与包内libksu_oppo_oppoa5pro5gcve202643499_any_d77f.sosha256 完全相同)——
那个仓库是失败研究的归档,作者自己的结论是「利用链无法收敛」。
该载荷仍在软件中(可供对照与研究),但不建议作为提权载荷使用,
列表里会标红说明原因。
KSuRoot 4.1.1 —— 零命中闸门 + 动态库库房 + 滑动删除
KSuRoot 4.1.1 —— 零命中闸门 + 动态库库房 + 滑动删除
versionCode = 411·versionName = 4.1.1· AOSP testkey 签名(a40da80a…5dc,可直接覆盖 4.1.0 原地升级)
上一个正式版本:4.1.0(18 个提交之前)
单测 39 个类 / 398 个用例 · 0 失败(1 条环境门控跳过)· 全矩阵 14/14blocked = false
〇、先说最要紧的一条:6.1 上「看着跑完了,其实一行都没改」
用 X100 Pro(6.1.145)构建时,界面给的是:
偏移解析: 28 / 28 项
需要改写: 26 项 · 校验通过: 0 项
该动态库未引用 26 项偏移
校验通过: 0 不是「没什么可改」,是「一个都没找到」。 根因是两条独立的路选岔了:
| 选什么 | 按什么选 | 6.1.145 上选到 | 旧值来自 |
|---|---|---|---|
| 基础库 | 结构体族 F6_1 | libbaseline_6_1.so |
baseline-6.1-tokay/target.h |
| 档位 | 三级路由(内核串) | up-6-1-145-… |
GhostLock 的 offsets.h |
打补丁的方式是「拿档位里的旧值去 .so 里找字面量」。两套旧值毫无交集,于是 26 项一处都对不上,产物等于把基础库原样拷贝了一份 —— 装到机器上必然失败,而界面看起来是"跑完了"。
为什么原来的硬闸门没拦住:hasFailures 的判据是 changed && !ok && !absent —— absent 被明确排除在外。那个理由("老版本 .so 可能没有对应代码路径")对个别键成立,对"全部键都 absent"不成立。
修法两条:
- 库与档位强制同源 —— 自编族基线必须配它自己的档(
baseline-6-1-tokay/baseline-6-12-honor),上游up-*档只适用于上游那份.so。 - 零命中硬闸门 —— 只要偏移规格一条都没落到站点上就停止打包。基址那条不计入:它即使全不匹配也常常会变,会把"一条偏移都没改"掩盖过去。
回归用例 BaselineLibraryPairingTest(4 条),其中一条直接断言两套旧值没有交集 —— 把"为什么必须配对"变成可执行的事实。
一、动态库:导入之后「点了没用」
| # | 缺陷 | 后果 |
|---|---|---|
| 1 | 「载荷构建 → 用作载荷」用的是内存里的产物;进程被回收后 outputLibrary 为 null,旧代码是 ?: return |
点了什么都不发生 —— 没 Toast、没报错、按钮不变 |
| 2 | PayloadStaging 缓存键是「同名 + 同长度」,而自定义载荷在私有目录里固定叫 payload.so |
换一份字节数恰好相同的载荷 → 静默复用上一份 |
| 3 | shizukuStage 缓存键只有长度 |
同一个坑在 Shizuku 路径上又踩一次 |
第 2、3 条最阴:它们不报错,只是加载了错的载荷。 现在缓存键改成内容哈希 / 逐字节比对。
二、动态库库房:Download/动态库/<机型>/<文件名>.so
- 库房(用户可见、可自己整理):
Download/动态库/<机型>/<文件名>.so - 活动副本(真正被加载的那份):
files/active_payload/payload.so - 界面新增按机型分组的库房列表,每份可直接「使用」设为当前,左右滑动删除
- 同名但内容不同的两份不会互相覆盖(自动加哈希后缀)
- 删掉当前生效那条时会真正激活下一条(旧写法只改 id 不拷文件,会留下"界面说当前、实际没有活动副本"的自相矛盾状态)
为什么库房那份不能直接 dlopen:分区存储下应用只能读自己经 MediaStore 创建的那批文件;
而且/storage是 FUSE(u:object_r:fuse:s0),非untrusted_app的域根本读不到 ——
这一条是在pm install时被 SELinux 当场拦出来的(avc: denied { read } ... tclass=file)。
所以库房负责"让用户看得见、管得住",私有副本负责"一定能被加载"。
⚠️ 搜寻边界(如实说):库房不能自动扫描Download/动态库/里你自己丢进去的文件 ——
分区存储不允许应用读取别的应用(包括文件管理器)创建的下载文件。
条目只有两个来源:本应用写进去的、以及你通过系统选择器显式授权的那一个。
滑动删除踩到的两个坑
- 最后一条永远删不掉:
items.forEach没有key(),Compose 只能按位置复用;而手势的pointerInput(enabled, triggerPx)两个 key 换数据时都不变,于是手势块从不重启,一直握着第一次组合时捕获的onDelete(指向已经删掉的条目)。修法两处缺一不可:key(entry.id)+rememberUpdatedState(onDelete)。 - 删除底色不统一:之前是硬编码红 + 直角 + 文字随方向左右跳。现在用主题
error色、与行同一个圆角常量、同高同内边距、图标 + 文字垂直居中,红底不透明度跟着滑动进度长出来。
手势没有用 detectHorizontalDragGestures,而是沿用本工程在底栏滑块上验证过的写法 —— 本机系统 touchSlop 实测约 75px(≈19dp),且它的回调只给越过阈值之后的增量,那段距离会被永久丢掉(表现为"开头要空拖一小段才动")。这里是自定义 4dp 阈值 + 位移从按下点起算,并且在阈值之内一个事件都不消费,所以行内的「使用」按钮照常能点。
三、vr.ko 标记偏移分族
| 内核族 | tag A | tag B | 样本 |
|---|---|---|---|
| 6.1 | 0x04 |
0x2c |
X100 Pro (PD2324) · 6.1.145 |
| 6.6 | 0x06 |
0x2c |
本机 PD2463 · 6.6.89 |
| 6.12 | 0x06 |
0x2c |
iQOO 15 (PD2505) · 6.12.58 |
6.1 与 6.6 只差半代,tag A 就挪了 2 个字节 —— 而这个值编译期烤死、patch 改不了。填错的表现是"真机上标记一个字节都没抹掉,而自检报成功"。
VR_TAG_A_OFF由单一常量改为分族,新增tagAOffForSeries()/tagAOffForRelease(),认不出返回null,不猜- 早先那段「跨内核版本未经证实、假定相同、从未验证」的结论留档保留(它当时是对的,而且明确把"假定"标了出来),但结论已随实测更新
顺带说一个被实测否掉的想法:曾加
add#0x4计数想"分族把 tag A 也数上"。
add #0x4是极常见指令 —— 89 份没有绕过的载荷里 86 份命中,放出来只会误导。
换成正确的指令形态(ldrb/strb的立即数)再测,仍然 19/25 命中负例。
所以 tag A 只能当声明,不能当判据:判定继续只用「tag B + 清0x400」(37/37 与 0/89)。
四、这一版包含的其余修复(4.1.0 之后累积)
- 6.4+ 内核整档失败的总根源:
kallsyms偏移表定位用了负数取模(Python 语义直译) - 偏移表判据从「前三项单调」换成查表的形状 —— 单调几乎没有区分度
BASELINE_6_12.imageBase填成目标基址 → 绝对地址形式的构造点全部漏改(39 处 → 9 处)- 符号对不齐时只置空产物、
gate仍是Proceed→blocked=false且产物 0 B,UI 不弹阻断框 FopsSlot的 fallback 从来没被用上- 6.12 真正跑通:ashmem 在 6.12 变成 Rust 实现、符号名全 mangled → 改成运行时从 miscdevice 的 fops 槽读出来
- 6.1 / 6.12 在通用 / 蓝厂两个方案下都可用(四个组合各 16 / 16 / 13 / 13 档)
五、边界(不掩饰)
- 偏移是实测的,「抹标记」这一步没在任何真机上跑过。
vr detag只证明代码编进去了,不证明它抹对了、更不证明抹完就不被杀 —— 所以三族基线一律仍标beta。 - 库房不能自动扫描
Download/动态库/里外部添加的文件(分区存储限制,见上)。 - 安装包与源码归档:本 Release 提供已签名 APK;源码归档由 GitHub 依据 tag 自动生成。
- 有一份随包载荷标了「不建议使用」。
libksu_oppo_oppoa5pro5gcve202643499_any_d77f.so(OPPO A5 Pro 5G)与上游仓库qsvggff-spec/oppo-A5-PRO-5G-CVE-2026-43499的preload-tokay-PKP110.sosha256 完全相同 —— 而那个仓库是失败研究的归档,作者自己的结论是「利用链无法收敛:三条 KASLR 泄露路线全堵死」(并注明 iQOO Neo9s PRO / Z10x / vivo X Fold5 的独立研究结论相同)。
它仍然留在软件里(仍可选取,供对照与研究),但不建议作为提权载荷使用 —— 列表里会直接标红写明原因,点选时会先弹一次确认。
KSuRoot 4.1.0 —— 蓝厂 vr.ko 反 root 实装 + 「解析完整包链接」
KSuRoot 4.1.0 —— 蓝厂 vr.ko 反 root 实装 + 「解析完整包链接」
versionCode = 410·versionName = 4.1.0· AOSP testkey 签名(a40da80a…5dc,可原地升级)
上一个正式版本:4.0.0
编译:debug / release 双变体通过 · 单测 362 条 0 失败(1 条环境门控跳过)· 0 警告
〇、先看这一条:4.0.0 的包不含蓝厂绕过
KSuRoot-4.0.0.apk 打包于 03:55,而蓝厂 vr.ko 那批改动落在 04:32 之后。
所以:
4.1.0 才是第一个真正带上
vr.ko反 root 绕过的正式版本。
一、蓝厂 vr.ko 反 root 实装
1.1 两份自编族基线:从「没有」到「有」
| 文件 | 旧 | 新 | strings -a | grep -c "vr detag" |
|---|---|---|---|
libbaseline_6_1.so |
135184 B | 175792 B | 0 → 2 |
libbaseline_6_12.so |
135184 B | 175776 B | 0 → 2 |
重编时发现并修掉两个独立缺陷:
- 不带
vr.ko抹标记 —— 这是任务书里点名的那一个; - 旧产物根本 load 不起来(任务书里没有)—— 旧构建只编了
fops.c main.c pipe.c posture.c preload.c slide.c util.c,没编root.c/io_daemon.c,
也没提供wallpaper_blob.S需要的素材。后果是install_android_root/
io_daemon_main/embedded_wallpaper_start/_end成了指向 UND 的动态重定位 ——
这份.so连dlopen都过不去。判定依据是readelf -sW … | awk '$4=="FILE"'的
文件符号表对比。
修完之后:NEEDED 只剩 libdl.so / libc.so,STT_FILE 里出现了 root.c / io_daemon.c。
⚠️ 上表是源码树里的尺寸。 APK 里装的那两份更小(154872 B / 154856 B)——
release 构建会走 AGP 的 native 符号剥离。剥离不影响vr detag,
已从成品包里取出来复核过:vr detag仍是 2。
别拿这两个数字对不上当成构建出错。
1.2 能力与闸门成对出现
按任务书 §4 的结论,Kotlin 侧写不了内核内存 —— 所以实装落点在载荷的 C 代码里
(pipe_phys_write_data / pipe_write64 那段),Kotlin 只做校验与呈现:
| 层 | 文件 | 作用 |
|---|---|---|
| 能力 | 载荷 .so 里的 patch_task_vr_tag(已内联进 install_android_root) |
先清 thread_info.flags 的 0x400,再逐字节清 task+0x06 / task+0x2c 两个标记,最后回读自证 |
| 校验 | com.kernelpack.vivo.VrKoPayloadCheck |
只读字节,判定"这份载荷带没带绕过" |
| 闸门 | com.kernelpack.vivo.VrKoPayloadGate |
只对蓝厂方案生效;通用方案短路,一次 ELF 解析都不做 |
判据是两条组合:日志串 vr detag + 机器码特征(add #0x2c 且 AND ~0x400)。
全量 126 份载荷实测:
| 份数 | 命中判据 | |
|---|---|---|
| 带绕过 | 36 | 36 / 36 |
| 不带 | 89 | 0 / 89(零假阳性) |
任务书 §6.3 的建议是错的:它建议扫
movz/movk找0x2c与0x400。
实测 clang 折成的是add立即数与AND逻辑立即数 —— 照movz扫会漏判 36/36。
为此在AArch64.kt补了两个解码器(decodeAddImmediate/decodeLogicalImmediate)。
1.3 两份无源码载荷:降级标注(按你选的方案 A)
libksu_vivo_iqoo12_a15.so 与 libksu_vivo_iqooz95gvivot35grootghos_any_3cc6.so
没有源码、无法重编,所以不假装它们带绕过:vivoVrBypass = false →
载荷列表标红 + 选中时一次确认对话框(可以"仍要选用",但得先看过这句话)。
二、新增功能:「解析完整包链接」
载荷构建页的第三条路:粘一个完整包(OTA ZIP)直链,自动解出 boot.img 交给已有流程。
2.1 它不是"下载整个包"
完整包通常 4–8 GiB。实际网络代价:
| 取什么 | 大约 |
|---|---|
| 文件尾部(找 ZIP 中央目录) | 64 KiB |
| ZIP 中央目录本体 | 几百 KiB |
payload.bin 头 + 分区清单 |
24 B + 几百 KiB |
boot 分区真正用到的那几块 |
~64–96 MiB |
实现方式:HTTP Range 请求 + 手工跟随重定向(厂商 CDN / GitHub Release 大量用 302)。
2.2 两种完整包形态都认
| 形态 | 识别 | 处理 |
|---|---|---|
| A/B 完整包 | 包里有 payload.bin(必须是 Stored) |
读 update_engine 清单,按 dst_extents 还原分区;支持 REPLACE / REPLACE_XZ / ZERO / DISCARD |
| recovery 风格 | 包里直接放 boot.img / init_boot.img |
Stored 原样落盘;Deflate 流式解压 |
2.3 出处分级(Apache-2.0)
com.kernelpack.ota 下的 5 个文件移植自
YuKongA/ghostlock-app 的 data/ota/:
| 文件 | 移植方式 |
|---|---|
XzDecoder.kt |
逐字节原样(只改 package) |
PayloadBinUtils.kt |
逐字节原样(只改 package) |
ZipCentralDirectory.kt |
原实现 + 三处只加不改(改名 / 候选条目表 / 边界检查) |
HttpRangeReader.kt |
原实现 + probe()(原版拿不到"服务器支不支持 Range")+ readToFile()(流式落盘) |
OtaPayloadExtractor.kt |
见下 §四 的改动清单 |
许可证沿用 app/src/main/assets/GL_LICENSE_Apache2.txt。
三、其它打磨
- 清掉构建里全部 5 条警告(1 条生产代码 + 4 条测试代码),现在编译 0 警告:
InstallViewModel.kt的Condition is always 'true'(similar由snapshot?.let{}推出,
顺序倒过来即消,语义不变);- 测试用例名里的半角引号 /
**(会触发 Windows 路径警告); - 3 处多余的
!!(编译器对 JUnit 的assertNotNull有数据流支持)。
- 载荷构建页的输入现在有两条互斥来源(
content://URI 与本地文件),
ViewModel 里互斥赋值 —— 避免"界面写着 A、实际构建用的是 B"。 - 解出的镜像放应用缓存目录,只保留最近一份(boot 镜像 96 MiB,堆着会被系统无预告清掉)。
- 给 payload 操作长度加了上限闸(256 MiB),防
toInt()溢出把"包有问题"伪装成"应用崩了"。
四、OtaPayloadExtractor 相对上游的改动(逐条)
- package 名;对象名不变;
ZipFileUtils→ZipCentralDirectory;- 去掉
REPLACE_BZ支持 —— 它要commons-compress,而现代 OTA 的 boot 分区用的是
REPLACE/REPLACE_XZ。为几乎不会出现的旧操作类型引一个第三方依赖不值得;遇到时明确报错; - 新增直取条目路径 —— recovery 风格完整包(国内厂商常见)里根本没有
payload.bin; xbl_config改为可选(默认不取)—— 本工程走 kallsyms 扫符号,不需要物理加载地址,
取它只是白白多下几十 MiB;- 日志中文化,产物文件名前缀
ghostlock_ota_→ksuroot_ota_。
五、没做的事(如实列出)
- 一次真机都没跑过。 这一条对所有 4.1.0 的内容都成立 ——
包括vr.ko绕过本身。vr detag=2只证明代码被编进去了,不证明它在真机上抹掉了标记。 - 没有验证过真实 OTA 的
REPLACE_XZ操作是单块还是多块 —— 见下面第 3 条。 - 内置 XZ 解码器只支持单块流,这是上游的缺陷,没有修。
实测:xz --block-size=16384 -9产出的 5 块流,第 1 块 16384 字节完全正确,
第 2 块从第 2 个字节起发散(期望0356: KS,实得00000000),随后抛
XzException: match distance beyond decoded data。
各块的 LZMA2 头解析(reset=3/props=93/packSize/ 偏移)与xz侧完全一致,
输入字节也一致 —— 问题在解码状态而非定位。
没有去修:这是一段算术编码器,"差一位"和"完全正确"之间没有中间状态,
在没有吃透它之前动手改,风险比不修大得多。
处理方式:单块流已用 3 条真实xz向量逐字节验证(含 crc32 / crc64 两种 check);
多块流由XzDecoderTest钉住"会抛异常"这一现状;OtaPayloadExtractor把它翻成
一句人能看懂的话。谁将来修它,那条测试会先红。 REPLACE_BZ(bzip2)不支持 —— 理由见 §四.3。- 没有下载整包的能力:服务器不支持
Range时直接拒绝。这是有意的 ——
不该在用户没同意的情况下下几个 GB。 BundledPayloadImported.kt是生成器产物:为加vivoVrBypass字段,
生成器(载荷搜寻/gen_catalog.py)与产物两边都改了,但生成器没有重跑
(重跑会把全部 83 条 imported 条目一起刷新,超出本次范围)。- 任务书 §6.2 点名的
tokay-CP2A.260605.012/target.h与它自己给的数字矛盾:
该文件是TASK_CRED_OFF 0x820(不是0x838),且没有TASK_PI_LOCK_OFF。
配方用的是与旧构建现场cmp逐字节相同的targets_6_1/tokay.h。
这条需要人来确认哪份才权威。 neutralize_vr()全局关开关没做(Option B):它需要
SYS_EXIT_TP_OFF/RVH_COMMIT_CREDS_TP_OFF,这两个偏移在 6.1 / 6.12 上
没有可核实的来源,按"不许猜"的原则留空,只做 per-task 抹标记。- 载荷列表里那两份无源码载荷是降级标注,不是修好了(见 §1.3)。
六、怎么自己复核
# 1. 编译 + 全部单测(会同步源码树,防止"编的是旧代码")
bash ksu-toolchain/compile_check.sh /绝对路径/merge-repo
# 期望:SCRIPT_RC=0 · 362 条 0 失败 · 0 警告
# 2. 两份族基线真的带上了绕过
strings -a merge-repo/app/src/main/jniLibs/arm64-v8a/libbaseline_6_1.so | grep -c "vr detag" # 2
strings -a merge-repo/app/src/main/jniLibs/arm64-v8a/libbaseline_6_12.so | grep -c "vr detag" # 2
# 3. 旧产物为什么 load 不起来(对比用)
readelf -sW <旧.so> | awk '$4=="FILE"' # 缺 root.c / io_daemon.c
readelf -dW <旧.so> | grep -E "UND|NEEDED" # install_android_root 等是 UND
# 4. 出包
bash ksu-toolchain/package_beta.sh /绝对路径/merge-repo
# 脚本自带三道断言:versionCode == 410 · 签名 == testkey · 成品指纹比对七、本轮改动的文件
新增(com.kernelpack.ota,5 个)
HttpRangeReader.kt · ZipCentralDirectory.kt · PayloadBinUtils.kt · XzDecoder.kt · OtaPayloadExtractor.kt
新增(单测 3 个)
ota/XzDecoderTest.kt · ota/XzVectors.kt(4 条真实 xz 向量)· ota/ZipCentralDirectoryTest.kt · ota/PayloadBinUtilsTest.kt
改动(生产代码)
PayloadBuilderViewModel.kt(新增 OtaLinkState + parseOtaLink() + 输入双来源)
MainActivity.kt(构建页第三个按钮 + 对话框 + 状态行)
res/values/strings.xml · res/values-zh-rCN/strings.xml(10 条新字符串)
InstallViewModel.kt(消警告)· app/build.gradle.kts(版本号)
改动(文档)
README.md · MODULE.md
改动(构建脚本)
ksu-toolchain/package_beta.sh(VERSION=4.1.0 / VCODE=410)
KSuRoot 4.0.0 —— 三族内核(6.1/6.6/6.12)+ GhostLock 50 档内核适配
KSuRoot 4.0.0 —— 三族内核(6.1 / 6.6 / 6.12)+ GhostLock 50 档内核适配
versionCode = 400·versionName = 4.0.0· AOSP testkey 签名(a40da80a…5dc,可原地升级)
上一个正式版本:3.2.0(2026-09-24)
编译:debug / release 双变体通过 · 单测 304 条 0 失败(1 条环境门控跳过)
一、这个版本做了什么
1. 内核覆盖:从「一条 6.6」到「三族 6.1 / 6.6 / 6.12」
3.2.0 实际只有 6.6 一条线。4.0.0 扩到三个结构体族,
而且不是靠"版本号外推" —— 每一族都有自己的基线库:
| 结构体族 | pi_lock |
cred |
tasks |
基线库 | 来源 |
|---|---|---|---|---|---|
6_1 |
0x924 |
0x838 |
0x550 |
libbaseline_6_1.so |
自编(tokay target.h) |
6_6 |
0x90C |
0x820 |
0x550 |
libionstack.so / libbs.so |
沿用 |
6_12 |
0x9EC |
0x900 |
0x638 |
libbaseline_6_12.so |
自编(GhostLock + 荣耀 BTF) |
为什么必须按族选库:结构体偏移是编译期烤死的,patch 改不了。
拿 6.6 的库去打 6.1 的内核,符号就算全 patch 对,写进去的也是错位置的字段 ——
这比"符号对不上"隐蔽得多(符号对不上会报错,字段错位不会)。
6.1 的口径变更(留档):6.1 曾在 2026-09-12 被移出主线,理由是"它是 flat 形态
(88 字节 / task@0x30),与 6.6/6.12 的 nested(112 字节 / task@0x50)不是一套"。
这个理由本身是对的;变的是我们后来为 6.1 编出了专属基线,
于是"拿 6.6 的偏移硬打 6.1"这个危险不再存在。
⚠️ 两条要一起读:6.1 能进主线的唯一前提是它有自己的族基线 ——
那份基线若被拿掉,6.1 必须同时退回拒绝。
2. 登记档位 51(通用)+ 52(蓝厂)= 103 档
引入上游 ghostlock-app 源码登记的
50 个内核适配(每档 9 个符号地址 + 结构体族),"通用方案"从 1 档扩到 51 档;
蓝厂方案把这 51 档全部挂上 vr.ko 绕过 → 52 档。
⚠️ 这 50 档一律标UPSTREAM_TARGET_H+beta:数据是真的(来自上游源码),
但我方未在真机上验证过。界面上与实测档分开计数显示,不谎报验证状态。
3. 双向符号对齐闸门(新的安全闸)
打补丁只能改写基线 .so 里本来就有的字面量,所以"基线有哪些键"与
"boot.img 解出哪些键"必须双向对齐。原先的写法是
baseline.symbolOffsets[key] ?: continue(直接跳过)—— 于是"看起来支持、实际写错内存"。
现在 SymbolAlignment 对不齐就阻断打包。
4. 与 GhostLock 的 offsets.json 互通
采用它的 offsets.json 作为互通格式(字段命名与结构已逐键核对),
实现读 / 写 / 合并(三种冲突策略),往返不丢未知键 ——
别人文件里我们不认识的键原样保留,不会被静默丢掉。
5. 内置厂商载荷:39 → 122 条(126 个 .so)
新增并入批次 83 份:Pixel 6~11 全系、三星 Galaxy 全系、
OPPO / realme / 一加 / 华硕 / 魅族等。读不出内核版本的 77 份一律
kernelUnknown —— 只允许手动选择、绝不参与自动匹配(载荷靠编译期常量寻址内核符号,
拿错一份就是提权失败)。
6. 设置页新增「基线覆盖面」
把注册表真实覆盖面摊开给用户看,而不是让人猜:三个主线系列 × 两个方案各登记多少档
(实测 / beta 分开计数)· 上游清单对账(GhostLock 登记的 50 档 vs 我方登记,必须零差异)
· 偏移可信度直方图。
7. 其它
- 删除孤儿库
libgl_universal.so(全工程 0 引用)与重复载荷libksu_vivo_sixmodels_a.so - 单测 154 → 304 条
二、修掉的 bug:两处「基线 ABI 冲突」误报
实测 6.1.145 的 boot.img 时被拒,提示
「基线 ABI 档位是 GKI 6.6,而 boot.img 是 6.1」。根因有两层,
都是"两套东西对不上",而且都表现为同一句话,极易误判:
| 症状 | 根因 | 修法 | |
|---|---|---|---|
| 第 1 层 | 两张表 | 路由查 BaselineRegistry.allEntries(103 档),打包却查 BaselineProfiles.byId(只有 2 档,都是 6.6)→ 查不到就兜底回落 PD2520 |
统一到 BaselineRegistry.byId / findByBytes(两个调用点都改) |
| 第 2 层 | 两套词汇 | 上游档的 abi.kernelSeries 填的是三段小版本(6.6.118),而闸门比的是两段大系列(6.6)→ 50 档一档都构建不出来 |
拆开两个语义:路由键保留三段小版本,ABI 比较键压成两段 |
同源的第 3 处:lookup() / coverageReport() / availableLabels() 也只查那 2 档 ——
导致报缺文案会在明明有档位时说"没有基线"。已一并修正。
并且不再兜底猜系列:认不出基础 .so 就如实报缺并停止打包,
而不是"取第一档顶上" —— 那等于拿 6.6 的旧值去打 6.1 的库。
回归测试
新增 BaselineRoutingLookupTest(11 条),把两条不变量写成断言:
- 路由给出的每一个
profileId,byId都必须能取到(防"两张表"再次分叉); - 每一档都必须能通过 ABI 硬闸门(用它的真实内核串端到端跑一遍)——
这条直接复现用户看到的那句「基线 ABI 冲突」,只不过是在单测里。
三、参考了 GhostLock 的哪些源码
上游仓库:YuKongA/ghostlock-app(Apache-2.0)。
本项目只取方法与数据,不复制其 C 代码;其许可证随包携带于
app/src/main/assets/GL_LICENSE_Apache2.txt。
| 上游路径 | 取用了什么 | 落在本项目哪里 |
|---|---|---|
src/kernels/<内核串>/offsets.h |
50 个内核各自的 9 个符号地址 | GhostLockKernelOffsets.KERNELS |
src/kernels/offsets.h |
3 个结构体族的字段偏移 | GhostLockKernelOffsets.STRUCT_FAMILIES |
src/core/main.c |
_RSO(field, fallback) 覆盖模型:运行时值 > 编译期基线 > 载荷兜底常量 |
UnifiedOffsetResolver |
src/core/offsets_json.c / .h |
offsets.json 读写与字段命名 |
GhostLockOffsets · GhostLockOffsetsIo · MiniJsonCodec |
src/core/runtime_struct_offsets.h |
运行时结构体偏移键名 | SymbolKeyMapping(上游 9 键 ↔ 我方 11 键) |
src/core/target.h |
目标描述形态 | BaselineProfile / AbiProfile 字段对照 |
buildSrc/.../GenerateSupportedKernelsTask.kt |
内核清单由扫描源码树生成 | GhostLockKernelCatalog(50 条 + 出处标记) |
反向核对:它的数据经我们独立验证过(4 处零差异)
不是"抄了就信":
| 上游数据 | 独立佐证 | 结果 |
|---|---|---|
STRUCT_OFFSETS_6_6 |
frankel-CP2A.260605.012/target.h |
一致 |
STRUCT_OFFSETS_6_12 |
pyyyc 荣耀 YLP-W00 6.12.38 BTF 实测 offsets.json |
15 字段 0 差异 |
| 编译期兜底常量(11 项) | 荣耀 struct_fields |
一致 |
STRUCT_OFFSETS_6_1 |
tokay-CP2A.260605.012/target.h |
一致(pi_lock=0x924、compact_waiter=1) |
手写档 PD2520 |
boxiaolanya2008 PD2520-BP2A.250605.031.A3/target.h |
28/28 键 0 差异 |
明确没有引用(高风险,拒绝照搬)
libextract.so(3.88 MB Rust 黑盒):kallsyms 提取口径我方没审过,
且具备 LZ4 重压缩回写 boot.img 的能力。在没验证其提取结果与我方口径一致之前,
引入它等于把校验权外包 —— 不得用于生成可刷写产物。libghostlock.so的运行时架构:它是"1 份通用载荷 + 运行时读 json",
与我方"编译期常量写进.so"根本不同。我们只采用它的格式与覆盖模型,
主线仍是直接写入.so。
四、已知限制(如实说明)
- 50 档上游内核 + 两份自编族基线均未在真机验证(一律标 beta)。
- 6.12 基线的符号是
CROSS_REFERENCE(取自荣耀 YLP-W00 6.12.38,是别的机型)。
构建时会被 boot.img 解析出的新值逐项改写,且SymbolAlignment保证改写不齐就阻断。
结构体偏移才是编译期烤死、决定成败的那一半。 - 蓝厂 vr.ko 绕过尚未接入提权路径:数据层(
VrKoBypass,12 条单测)已完成,
但"清零0x28-0x2f"这一步需要在真机上先确认安全才敢接线。 offsets.json导入目前是阶段 1(读取与呈现):UnifiedOffsetResolver
的合并流水线已实现并有 10 条单测,但尚未接入构建路径。
五、发布资产
| 文件 | 内容 |
|---|---|
KSuRoot-4.0.0.apk |
签名 APK(versionCode 400,AOSP testkey,可原地升级) |
KSuRoot-4.0.0-source-full.zip |
完整构建源码(含 UI 外壳、厂商载荷目录、全部 jniLibs) |
KSuRoot-4.0.0-module-src.zip |
仅构建与模块相关源码(com.kernelpack 载荷引擎 + 对接点 + 构建脚本 + 两份自编族基线) |
单测:304 条 0 失败(./gradlew :app:testDebugUnitTest)。
KSuRoot 3.2.0 —— 39 份厂商载荷 + 硬闸门 + 移交 root + 安全补丁提示
⚠️ 温馨提示:蓝厂机型调度策略较为严格,提权时子进程易被系统回收。请先在「开发者选项」中开启「停止限制子进程」后再执行提权,成功率更高。
真实内核提权工具,请只在自有且愿意承担风险的设备上使用,动手前先备份数据。
覆盖安装即可(同签名),versionCode 320。
本版是两条开发线的合并版
3.2.0 = 「厂商载荷目录线」+「闸门/移交 root 线」双向合并后的第一个版本。
两条线此前都叫 3.1.1 但内容不同,因此合并版换了版本号以便区分。
一、新增:39 份厂商载荷内置 + 分级匹配
随包内置 39 份逐机型编译的载荷(libksu_*.so,小米 6 / vivo·iQOO 33):
- 覆盖 6.6.30 / 6.6.57 / 6.6.89 / 6.6.127
- 三级匹配:
Exact(机型+内核+commit 全中)/SameKernel(厂商小版本 OTA)/SimilarDevice(同厂商同内核,仅建议、绝不自动选中) - 6.6.89 的三款(iQOO Neo10 Pro+ / iQOO 13 / Neo11)显式指向随包
libbs.so
二、新增:Android 安全补丁提示
- 设备卡新增「Android 安全补丁」一行(原「系统 ABI」行位置)
- 2026-06:黄色感叹号 —— 可能已合并幽灵锁修复,只提示、不限制、不冷却
- 2026-07 及以后:红色感叹号 + 提权按钮上方常驻红字 + 弹窗确认键 10 秒冷却
- 判据只有月份、没有官方补丁映射表,所以两级都只提示、不阻断
三、机型识别优化
厂商把「这台机器叫什么」放在哪个字段完全不统一,本版做了两件事:
- 多源取市场名:17 个属性按可信度排序(
ro.product.marketname系列 → 厂商自定义)+Settings.Global.device_name兜底 - 匹配改用全身份串:haystack 从「只有
Build.MODEL」改为manufacturer+brand+model+device+product+board+市场名;
vivo/iQOO 的Build.MODEL是型号代码(V2463A)而目录登记的是市场名(iqoo 13),原实现必然匹配不上 - 取不到市场名时界面如实标注「(型号代码)」,不拿代号冒充名字
四、新增:移交 root
- 每次打开、检测到 root 即弹框询问是否移交给 Root 管理器
- 管理器二选一:KernelSU(
me.weishu.kernelsu)/ SukiSU Ultra(com.sukisu.ultra) - root 探测含
/apex/com.android.virt/bin/su(载荷提权后的落点,置首位) - 载荷跑完自动重探;失败可展开逐条诊断并重试
五、新增:5.x 内核支持(beta)开关
设置 → 内核支持,默认关。关着时识别到 5.x 不采用五系方案并拦住引导开启。
内核支持口径:主线 6.6 / 6.12(两个方案都覆盖);5.x 测试;6.1 拒绝
(6.1 的 rt_mutex_waiter 是 flat 形态 88 字节,与 6.6/6.12 的 nested 112 字节不是一套)。
六、本版删除
- 主页「工作原理」卡片
- 日志翻译引擎整套(
ExploitLogDigest及其 13 个用例)—— 提权页现在只显示原始日志,
不再有「原文/译文」切换;进度条由 16 级里程碑退回阶段制 - 设置里的「日志详细显示」开关
七、本版修掉的 bug
matchesModel大小写 bug:MODEL_SEPARATOR字符类只认小写,而displayName是"iQOO 13",
导致新增的 displayName 匹配静默失效(一条都命中不了且不报错)ro.vivo.product.model实测是代号(PD2463)却被列为市场名候选 → 界面会把代号当设备名显示。已移出 + 加looksLikeCode()兜底su候选路径缺/product/bin/su与/apex/com.android.virt/bin/su→ 真机上「有 root 却显示未检测到 root」- 裸名
"su"被当成"一定存在",报错理由掩盖真因 Operation not permitted(late-load 最常见失败)被判成成功- 提权页日志滚动只盯译文 → 切到原文后不再跟随
- 合并时
MainActivity多出一个}导致整份文件塌掉;A 线删除的DialogDimAmount等被 git 干净应用而消失
八、签名
AOSP testkey(CN=Android,SHA-256 a40da80a…),与设备当前安装一致,可覆盖安装。
⚠️ testkey 是公开密钥,只保证兼容性、不提供身份保证。适合自用 / 内测。
验证情况
- 单测 168 个(1 个环境门控跳过),debug + release 两变体均编译通过
- 真机:
pm install -r通过,启动无崩溃 - 源码:本 Release 对应分支
merge-3.2.0
已知局限(如实说明)
- 6.12 声明支持但会明确报缺:代码路径已铺好(两个方案 × 两个主线系列),但没有 6.12 的偏移数据。补数据需要为该内核编译的载荷 .so,或用真实 6.12 boot.img 实测 —— 本版不会拿 6.6 的数值去凑
- 移交 root 的完整流程未在真机点过,只验证到命令行层;且 late-load 成功信号很弱(成功 = 空输出 + rc 0),界面只报「已执行移交」并原样展示输出
- 红色补丁档未在真机验证(本机补丁为 2026-06,走黄色分支)
- 本机 vivo 属性里没有市场名(
marketname/marketing_name全空),因此设备卡仍显示型号代码并标注。要真正解决需一张「型号代码 → 市场名」映射表
KSuRoot 3.1.1 —— 移交 root + 5.x beta 开关 + 内核支持口径(主线 6.6/6.12)
⚠️ 温馨提示:蓝厂机型调度策略较为严格,提权时子进程易被系统回收。请先在「开发者选项」中开启「停止限制子进程」后再执行提权,成功率更高。
真实内核提权工具,请只在自有且愿意承担风险的设备上使用,动手前先备份数据。
覆盖安装即可(同签名),versionCode 311。
本次更新(相对 v3.0.2)
一、新增:移交 root
载荷拿到的 root 只活在当前进程里,进程一结束就没了。本版新增把它移交给常驻 Root 管理器的完整链路:
- 每次打开软件、只要检测到 root 就弹框询问是否移交(主页最底部卡片可随时再进)
- 管理器二选一:KernelSU(
me.weishu.kernelsu)或 SukiSU Ultra(com.sukisu.ultra) - 执行
libksud.so late-load --allow-shell --package-name <包名> - 把「管理器没装」与「移交失败」分成两种结果 —— 这两种该给的建议完全不同(一个去装、一个查版本)
- root 探测路径含
/apex/com.android.virt/bin/su(载荷提权后的落点,置于首位) - 载荷跑完自动重探 root,不需要手动点「重新检测」
- 探测失败时可展开逐条诊断(哪条路径不存在 / 被拒绝 / 超时),并支持手动重试
⚠️ 移交没有在真机上点过完整流程,只验证了命令行层:
SukiSU 那条late-load实测rc=0且静默成功;不存在的包名rc=1并输出
kernelnosu: exec /data/adb/ksud failed。
另外 late-load 的成功信号很弱(成功 = 空输出 + rc 0),
所以界面只报「已执行移交」并原样展示输出,请自己去管理器里确认。
二、新增:5.x 内核支持(beta)开关
- 位置:设置 → 内核支持,默认关
- 关着时识别到 5.x → 不采用五系方案,构建被拦下并引导去开启
- 开启前弹确认框说明风险;开启后构建仍带
[测试内核]提示
三、内核支持口径
| 系列 | |
|---|---|
| 主线 | 6.6 / 6.12 —— 「通用方案」与「vivo / iQOO 方案」都覆盖 |
| 测试 | 5.x(需开上面的 beta 开关) |
| 拒绝 | 6.1 及其它 6.x 次版本 —— 明确拦下,不走 beta 开关 |
构建前先定版本与方案:读完 boot.img 即按实测内核版本选方案;版本认不出、5.x 开关没开、或强制指定与实测冲突,都在打补丁之前零成本拒绝。
⚠️ 6.12 声明支持,但当前会明确报缺:还没有为 6.12 登记偏移产物。
代码路径(两个方案 × 两个主线系列)已实装,补数据即可生效 —— 但那需要
为该内核编译的载荷 .so,或用真实 6.12 boot.img 实测。
本版不会拿 6.6 的数值去凑 6.12(那正是本项目两次布局勘误的同一种错)。
四、boot 镜像格式识别
- AOSP boot v0~v4、vendor_boot(点名「内核不在这个文件里」并指路)、裸 Image / 压缩镜像
- MTK / 高通 split 的启发式提示(输出一律带「疑似」,不假装是解析)
五、设置页重构
- 去掉原先的大段正文说明与三个大按钮
- 「内核系列」改为一行可点选;「忽略冲突」与「5.x 内核支持」合并进同一个「内核支持」分组
- 详细说明移到做决定的地方(确认框 / 选择器),设置页只留短摘要
- 全部改用与全页一致的 miuix 偏好行,不再各写一套
六、修掉的 bug
su候选路径缺料:真机上检测不到 root —— 可用 su 在/product/bin/su,而候选只赌/system/bin、/sbin- 裸名
"su"被无条件当作「存在」,等于假装它在,报出来的理由是「找不到可用的 su」,把真因盖住 looksSuccessful漏not permitted:Operation not permitted(late-load 最常见失败)会被报成成功- 强制指定未参与前置判定:boot.img 白解一遍才被内部闸门拒绝
- 阻断只标红不弹窗:违反 P1 定的规矩,已加分类阻断弹窗
- 进度框错用确认框正文(「正在执行」里写着「是否移交?」)
e2e_check.sh用旧快照覆盖 security 模块 → 两个变体都编不过,而检查脚本一直报成功KSU_TEST_BOOT_IMG从未透传进测试 JVM → 那条端到端用例永远 SKIP
七、签名
设备上及本项目历史版本用的都是 AOSP testkey(CN=Android,SHA-256 a40da80a…)。
打包脚本原先用的自造密钥与之不一致,签出来无法原地升级(必须卸载重装、丢数据)——
已改用 testkey,pm install -r 实测通过。
⚠️ testkey 是公开密钥,只保证兼容性、不提供身份保证。适合自用 / 内测。
验证情况
- 单测 136 个(1 个环境门控跳过),debug + release 两变体均编译通过
- 真机原地升级:versionCode 310 → 311
KSuRoot 3.0.2 —— 看门狗 180 秒 + 内置动态库 v1.3.0
⚠️ 温馨提示:蓝厂机型调度策略较为严格,提权时子进程易被系统回收。请先在「开发者选项」中开启「停止限制子进程」后再执行提权,成功率更高。
本次更新
- 看门狗放宽至 180 秒:
EXPLOIT_STALL_MILLIS = 180_000L,九个语言提示文案同步更新为 180 秒,慢速提权不再容易被误判「停滞」而中断。 - 内置动态库换回 v1.3.0:与官方 release v1.3.0 资产逐字节一致(sha256
8c3410cb…95d9,176544 字节);已确认 v1.3.0 与 v1.0.0 共用同一套偏移(ASHMEM_MISC_FOPS 0x226b4e8/INIT_TASK 0x210e280,上游 PD2520 target.h 无改动),无需重新适配,直接替换即可。
覆盖安装即可(同签名),versionCode 302。
KSuRoot 3.0.1 —— 修掉「内置动态库」的真正卡点
KSuRoot 3.0.1
修掉「内置动态库不可用」的真正卡点 —— 一行多余的
chmod。
覆盖安装即可(同签名),versionCode 301。
⚠️ 真实内核提权工具,请只在自有且愿意承担风险的设备上使用,动手前先备份数据。
问题是什么
2.2.0 起,只要选择「内置动态库(libbs.so)」并点安装,安装链就会在刚打印完下面这行之后立刻断掉:
[+] 支持配置: fixed-bn-so
使用内置动态库:libbs.so
[-] chmod failed: EACCES (Permission denied)
[*] 安装失败
chmod failed: EACCES (Permission denied) 是 Android Os.chmod() 抛出的 ErrnoException 原文,来自 PayloadRepository.bundledPayloads() 里的一行:
Os.chmod(exploit.absolutePath, 0b100100100) // 0444为什么它会失败
内置载荷住在 applicationInfo.nativeLibraryDir。这个目录里的文件是安装器以 system 身份提取的:
/data/app/~~…/com.ting.root-…/lib/arm64/
-rwxr-xr-x system system 162328 libbs.so
-rwxr-xr-x system system 26024 libcve43499root.so
属主是 system,不是应用自己 —— 非属主执行 chmod,内核直接回 EACCES。而它本来就是 r-xr-xr-x:那次 chmod 纯属多余(载荷只被 LD_PRELOAD 映射,不需要额外权限),却因为没有兜异常把整条安装链打断了。
「自定义动态库」一直没这个问题,是因为那份文件由应用自己写进私有目录、属主是应用自己。
3.0.1 怎么修
新增 PayloadStaging,四个载荷入口(内置 / 自定义 / 下载 / KernelSU)与执行助手统一走它:
1. 已经够用(可读 / 可执行)→ 原样返回,一次 chmod 都不做 ← 本机走的就是这条
2. 不够用 → 先试就地 chmod(有些 ROM/分区允许)
3. 还不满足 → 复制到应用私有目录(一定能写)再补权限,并按「同名同长度」缓存复用
4. 全失败 → 返回原文件,交给调用方已有的 require 兜底
因为原生库本来就 -rwxr-xr-x、对应用世界可读,第一步的 canRead() 即为真 → chmod 那行再也不会被执行 → EACCES 在结构上不可能再出现。
顺带把另外两层也一起说清楚
「内置动态库不可用」其实是三层叠在一起,前两层是隐患,第三层才是卡住安装的那一步。三层现在都已处理:
| 层 | 问题 | 状态 |
|---|---|---|
| 打包 | AGP 默认 strip jniLibs 里的 .so,libbs.so 162328 → 142848 字节,跑起来的其实是被二次加工过的二进制 |
已用 keepDebugSymbols 保住原件 |
| 版本 | 内置的是上游 v1.3.0 | 已固定为 v1.0.0(162328 字节,sha256 87bf839f…b861),慢但稳 |
| chmod | 原生库属主是 system → Os.chmod EACCES → 安装链中断(真凶) |
3.0.1 修复 |
上一版(3.0)的发布说明把「内置动态库已修复」写在了前两层上,那是不完整的 —— 特此更正。
你可以自己核对
装完之后从 /data/app 把 base.apk 抠出来:
unzip -p base.apk lib/arm64-v8a/libbs.so | sha256sum
# 87bf839fc8524ed25831a22166ebc6cef898e7ddc77ce77736add5ca49b78b61其它(与 3.0 相同)
- 载荷构建页:boot.img → kallsyms → 偏移 → 打补丁的动态库;点构建先选方案(通用 / vivo·iQOO vr.ko 绕过);产物自动进
Download/与「自定义动态库」 - 免 ADB:内核 6.6 及以上(6.6 / 6.12 已验证,6.7~6.11 同理);低于 6.6 才需要 Shizuku(ADB),设备信息里新增内核版本行与判定
- 运行记录只记过程不判成败;日志支持一键复制与保存到指定目录
- 液态玻璃悬浮底栏、MIUIX 卡片分组、版本号 3.0.1
Apache-2.0 · 仅供安全研究与学习,请仅在自有且受控的设备上使用。
KSuRoot 3.0 —— 载荷构建
⚠️ 本版已被 v3.0.1 取代。
下面「内置动态库」一节写的是版本与打包被 strip 两层原因,但那两层只是隐患 ——
真正卡住安装的那一步是第三层:原生库属主是system,应用执行Os.chmod必回EACCES,
安装链会在打印完「使用内置动态库:libbs.so」后立刻断掉([-] chmod failed: EACCES)。
本版仍带该问题,请直接使用 v3.0.1。
KSuRoot 3.0
基于 CVE-2026-43499(GhostLock) 的一键 KernelSU 提权工具 · 本版是「载荷构建」大版本
⚠️ 这是真实的内核提权工具。竞态未命中、偏移错误都可能触发 oops/panic,轻则进程被杀,重则内核卡死或设备重启。请只在你自己拥有、且愿意承担风险的设备上使用,使用前先备份数据。
一、这个版本最重要的三件事
1. 内置动态库终于「能用」了 —— 而且现在能自证
之前「内置动态库不可用」其实是两层原因叠在一起:
| 层 | 问题 | 本版处理 |
|---|---|---|
| 版本 | 内置的是上游 release v1.3.0 的 preload.so |
换成 v1.0.0(162328 字节,SHA-256 87bf839f…b861)—— 提权速度稍慢但稳 |
| 打包 | AGP 默认会 strip jniLibs 里的 .so:实测 libbs.so 被削成 142848 字节,执行载荷的 helper 也被削 |
用 keepDebugSymbols 逐个保住,逐字节保留原件 |
「打包被 strip」这层特别隐蔽:版本号看着是对的,但跑起来的其实是一份被二次加工过的二进制。现在装完可以自己核对 —— 从 /data/app 里把 base.apk 抠出来:
unzip -p base.apk lib/arm64-v8a/libbs.so | sha256sum
# 87bf839fc8524ed25831a22166ebc6cef898e7ddc77ce77736add5ca49b78b612. 新增「载荷构建」页 —— 换机型不用再重新编译
把这条链路整个搬到手机上:
本机 boot.img ──► 内核符号表(kallsyms) ──► 这条链需要的偏移 ──► 打好补丁的动态库
- 点「开始构建」会先问用哪种方案(从下往上弹出):
- 通用方案(推荐) —— IonStack 上游分支,不含厂商适配,适用于大部分 GKI 6.6+ 设备
- vivo / iQOO(vr.ko 反 su 绕过) —— 额外做厂商反 root 绕过,蓝厂机型必须选这项
- 产物自动做两件事,不用你再点:写进
/storage/emulated/0/Download/(走 MediaStore,不需要任何存储权限),并写入「自定义动态库」且把载荷源切过去 - 界面上直接摊开「打的是哪一份库」:文件名 / 大小 / SHA-256 / 来源版本 —— 这条信息不该藏在代码里
- 全程离线,构建日志实时上屏;算法是独立的纯 Kotlin 模块(
com.kernelpack),原地改写常量、不增删一个字节,改完还会自己重扫一遍自证(旧值残留必须为 0)
通用方案的载荷是本项目真编译出来的:用原生 clang 交叉编译 IonStack 的
CVE-2026-43499/exploit(PROJECT=blazer-CP2A.260605.012),并用真机boot.img验证过补丁链 —— 一次构建会把基线里的 13 项偏移改写成你机器的实际值。
3. 免 ADB 的边界说清楚了
GhostLock 打的是 rt_mutex + pipe_buffer 这条链:
| 内核 | 免 ADB 提权 |
|---|---|
| 6.6 及以上(6.6 / 6.12 两系已验证,6.7~6.11 同理) | ✅ 直接可用,不需要电脑、不需要 ADB |
| 低于 6.6 | ❌ 只能走 Shizuku(ADB)授权那条路 |
设备信息卡新增内核版本行,并直接给出判定(6.6.89 · 支持 GhostLock 免 ADB)。内核低于 6.6 时按「安装」会先弹提示:免 ADB 走不通、请在设置中打开 Shizuku 授权;确认按钮带 3 秒倒计时(期间只有取消可点),避免手快直接确认。
二、界面与体验
- 运行记录不再判成败:蓝厂机型上「跑完了但没拿到 root」和「真的跑挂了」根本分不开,判定既不准也没意义。现在只保留「进行中 / 已记录」,图标与配色一律中性。是否真的装上,看主页 KernelSU 状态 —— 那是实测出来的,不是推断的。
- 日志一键复制 + 保存到指定目录:安装页的实时日志、运行记录详情,两处都有这两个动作。出问题时直接复制贴群里/issue。
- 关于页改为「关于 KSUroot」,补上本仓库地址与简介,版本号统一为 3.0。
- 上一轮做的液态玻璃 UI(悬浮玻璃底栏 + 滑块折射、MIUIX 卡片分组)与内置库路由修复(此前用内置库点安装会被「定位到官方在线源」,因为判断里漏了载荷来源这一维)一并包含在本版。
三、升级与使用建议
- 覆盖安装即可(同签名),版本号 300 / 3.0。
- iQOO / vivo → 主页选内置动态库。
- 三星 → 主页选官方在线源(按机型与内核版本匹配)。
- 其它机型(内核 6.6+)→ 到载荷构建用本机
boot.img生成载荷,产物会自动进入「自定义动态库」。 - minSdk 33(Android 13+),仅 arm64-v8a。
四、致谢与许可
- Root-My-Galaxy(应用架构与在线载荷体系)
- CVE-2026-43499-Neo11Plus(内置动态库 v1.0.0 与基线)
- IonStack / CyberMeowfia(通用方案载荷上游)
- KernelSU · miuix · Backdrop
Apache-2.0。仅供安全研究与学习,请仅在自有且受控的设备上使用。
KSuRoot 2.2.0 — CVE-2026-43499
KSuRoot 2.2.0
基于 CVE-2026-43499(GhostLock) 内核漏洞的一键 KernelSU 提权工具。同步 Root-My-Galaxy v0.2.6 主线更新,新增自定义动态库导入功能。
本次更新
- 同步 Root-My-Galaxy v0.2.6 主线:Compose 界面、在线设备清单(schema v3)自动匹配、安装历史、主题多语言
- 三种载荷来源主页切换:官方在线源 / 内置 libbs.so / 自定义导入,切换即时生效无需重启
- 自定义导入动态库:ELF 魔数校验、SHA-256 指纹记录、256MB 大小限制、随时移除
- 支持机型详情页:三星 11 组型号(含内核版本)/ iQOO 骁龙 8 至尊版 3 款
- 安装确认文案随来源动态变化,本地载荷全程不联网
支持设备
- 官方在线源(三星):Galaxy S25 全系、Z Fold 7、S24 系列、S23 Ultra、A56 5G、A36 5G(内核版本需完全一致)
- 内置动态库(iQOO · 骁龙 8 至尊版):iQOO Neo 10 Pro+、iQOO Neo 11、iQOO 13
安装
下载下方 KSURoot-2.2.0-CVE43499.apk 直接安装(Android 13+ / minSdk 33)。
One-click KernelSU rooting based on CVE-2026-43499. Synced from Root-My-Galaxy v0.2.6 with custom payload (.so) import.
Mod by hmascs · Apache-2.0