Skip to content
This repository was archived by the owner on Oct 3, 2026. It is now read-only.

KSuRoot 4.1.0 —— 蓝厂 vr.ko 反 root 实装 + 「解析完整包链接」

Choose a tag to compare

@hmascs hmascs released this 25 Sep 03:27
· 21 commits to main since this release

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

重编时发现并修掉两个独立缺陷:

  1. 不带 vr.ko 抹标记 —— 这是任务书里点名的那一个;
  2. 旧产物根本 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 相对上游的改动(逐条)

  1. package 名;对象名不变;
  2. ZipFileUtils → ZipCentralDirectory;
  3. 去掉 REPLACE_BZ 支持 —— 它要 commons-compress,而现代 OTA 的 boot 分区用的是
    REPLACE / REPLACE_XZ。为几乎不会出现的旧操作类型引一个第三方依赖不值得;遇到时明确报错;
  4. 新增直取条目路径 —— recovery 风格完整包(国内厂商常见)里根本没有 payload.bin;
  5. xbl_config 改为可选(默认不取)—— 本工程走 kallsyms 扫符号,不需要物理加载地址,
    取它只是白白多下几十 MiB;
  6. 日志中文化,产物文件名前缀 ghostlock_ota_ → ksuroot_ota_。

五、没做的事(如实列出)

  1. 一次真机都没跑过。 这一条对所有 4.1.0 的内容都成立 ——
    包括 vr.ko 绕过本身。vr detag=2 只证明代码被编进去了,不证明它在真机上抹掉了标记。
  2. 没有验证过真实 OTA 的 REPLACE_XZ 操作是单块还是多块 —— 见下面第 3 条。
  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 把它翻成
    一句人能看懂的话。谁将来修它,那条测试会先红。
  4. REPLACE_BZ(bzip2)不支持 —— 理由见 §四.3。
  5. 没有下载整包的能力:服务器不支持 Range 时直接拒绝。这是有意的 ——
    不该在用户没同意的情况下下几个 GB。
  6. BundledPayloadImported.kt 是生成器产物:为加 vivoVrBypass 字段,
    生成器(载荷搜寻/gen_catalog.py)与产物两边都改了,但生成器没有重跑
    (重跑会把全部 83 条 imported 条目一起刷新,超出本次范围)。
  7. 任务书 §6.2 点名的 tokay-CP2A.260605.012/target.h 与它自己给的数字矛盾:
    该文件是 TASK_CRED_OFF 0x820(不是 0x838),且没有 TASK_PI_LOCK_OFF。
    配方用的是与旧构建现场 cmp 逐字节相同的 targets_6_1/tokay.h。
    这条需要人来确认哪份才权威。
  8. neutralize_vr() 全局关开关没做(Option B):它需要
    SYS_EXIT_TP_OFF / RVH_COMMIT_CREDS_TP_OFF,这两个偏移在 6.1 / 6.12 上
    没有可核实的来源,按"不许猜"的原则留空,只做 per-task 抹标记。
  9. 载荷列表里那两份无源码载荷是降级标注,不是修好了(见 §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)