——把结论钉死在一个可证伪的预测上
这篇讲一件有点扫兴的事:DirtyFrag(CVE-2026-43284)这套 2026 年最香的免解锁 root 方案,
在 Galaxy S25(6.6 内核)和 S23(5.15 内核)上都能跑通,在 Galaxy S24 全系上却怎么都跑不了,
而且不是”没调好”——是结构性不通。我不想写”因为我们安全补丁比它新”,那句话既不解释也不可信。下面是我怎么把这件事钉死的:
从内核源码推出一个能算出具体整数的预测,然后在真机上数出那个整数。
0x00 开场:别人能跑,为什么我这里不行
事情的起点是一个很朴素的对比:
| 设备 | 内核 | DirtyFrag |
|---|---|---|
| Galaxy S23 系列 | 5.15 | ✅ 机主实测能跑 |
| Galaxy S25 Ultra(SM-S938B) | 6.6.98 | ✅ 有公开移植,还能开机自动恢复 root |
| Galaxy S24 全系(SM-S9280…) | 6.1.x | ❌ 我们实测跑不了 |
DirtyFrag 的吸引力在于它的性质:确定性(不靠竞态)、失败不 panic、只改内存里的 page cache、
重启即还原——对 bootloader 已锁定、又不想每次手动跑一遍工具的人来说,这几乎是理想解。
所以当自研的移植在我们手上失败时,第一反应一定是”我们哪里写错了”:
IV 算错了?HMAC 算错了?ICV 长度猜错了?SA 没建起来?包没发出去?
这些怀疑最后全部被排除。而排除它们靠的不是反复试参数,而是一条能算出整数的判据。
0x01 先把链条拆开
0x01-1 完整链条长什么样
有公开移植把它串成这样(我对着源码逐条核过,机主的描述和实现是对得上的):
① 借 crash_dump64 突破沙盒 → 获得 vendor 读权限
② 用 DirtyFrag 篡改只读文件的页缓存 ← 全链唯一的写原语
③ 把内核模块注入厂商库(libstagefrighthw.so)的页缓存
④ 挂钩 libc / libc++,等触发
⑤ modprobe 加载模块时触发钩子
⑥ 加载 dirtyfrag.ko 进内核
⑦ 关闭 SELinux
⑧ bind mount logcat,启动 KernelSU
⑨ 完成无痕迹临时 root
0x01-2 谁是支点
这里有件事必须先说清楚:这条链只有一把钥匙。
③ 需要 ②(把模块写进只读的厂商库)+ ① (读 vendor 文件算 IV);
④ 需要 ②;⑤ 依赖 ④;⑥ 依赖 ⑤。
也就是说,② 一旦不成立,后面全是空谈——换 ksud、补参数槽、改 LKM、调 SELinux,
这些”下游工作”做得再漂亮都没用,因为根本走不到那一步。
我们后来确实在真机上看到它死在 ②:目标文件 crash_dump64 的 md5 前后一字未变。
(顺便,这一条也说明:有人反馈”我在 S23U 上用的是 ROOT_s23 那套 ksud,打了个跳转槽补丁就好了”——
那是链条 ⑥⑦⑧ 段的活,属于”拿到写原语之后”,在 S24 上不解决任何问题。)
0x02 原理:拆到能自己写出来
这一节是全文的地基。要看懂后面为什么”构造是对的但还是写不进去”,得先知道内核到底认什么。
0x02-1 报文长什么样
[ ESP 头 8 ][ IV 16 ][ 密文 = 文件页 16 ][ ICV A ]
↑ SPI+Seq ↑ 网络序 ↑ 这段来自页缓存 ↑ 长度 A = authsize
套路是:把目标文件的页缓存页用 splice() 送进管道,再 splice() 到 UDP socket。
如果内核在这条发送路径上是零拷贝的,那么这一页就会被原样挂进 skb 的 frag;
之后 ESP 输入路径在这个 frag 上做就地解密,写出来的明文就直接落在文件的页缓存里——
不需要写权限、绕过 COW、绕过只读挂载。
0x02-2 内核是”先验 ICV,再解密”
crypto/authenc.c:
/* authenc = auth + crypt 的组合。解密路径先算 ICV 并比对,
验过了才真正调用 skcipher 解密 —— 顺序很关键:
认证不过,解密根本不会发生,也就没有任何写入。 */
ahash_request_set_crypt(ahreq, req->src, hash,
req->assoclen + req->cryptlen - authsize);
err = crypto_ahash_digest(ahreq);
...
return crypto_authenc_decrypt_tail(req, ...); /* 验过了才到这里 */
net/ipv4/esp4.c 的输入侧:
int ivlen = crypto_aead_ivsize(aead); /* 16 */
int elen = skb->len - sizeof(struct ip_esp_hdr) - ivlen;
int assoclen = sizeof(struct ip_esp_hdr); /* 8 */
/* src == dst:这就是"就地"的意思 —— 解密结果覆盖在原文所在的内存上 */
aead_request_set_crypt(req, sg, sg, elen + ivlen, iv);
aead_request_set_ad(req, assoclen);
0x02-3 覆盖范围其实是个恒等式
把上面两段拼起来,ICV 的覆盖长度是:
assoclen + cryptlen - authsize
= 8 + (skb->len - 8) - authsize
= skb->len - authsize
对本文的布局,skb->len = 40 + A(A = 随包附加的 ICV 字节数),所以:
ICV 的覆盖范围恒为
ESP头 + IV + 文件页 = 40字节,跟 authsize 无关。
这句话的价值在于:它把”HMAC 该覆盖哪 40 个字节”这件事变成确定的,
于是整条链上真正未知的量只剩一个:
随包附加的 ICV 字节数 A,必须恰好等于内核 SA 的 authsize。
0x02-4 IV 怎么算
CBC 解密是 P = DEC(C) ⊕ IV。我们希望解出来的明文就是想要的新内容,于是:
IV = AES_ECB_DEC(key, C_old) ⊕ C_new
代入:P = DEC(C_old) ⊕ IV
= DEC(C_old) ⊕ DEC(C_old) ⊕ C_new
= C_new ✓
而 C_old 就是目标页的当前内容——pread() 读出来,splice() 送出去,读和写用的是同一页。
/* 只写 16 字节整数倍:密文块必须是完整的 CBC 块 */
static void compute_iv(const uint8_t old_content[16],
const uint8_t desired[16], uint8_t iv[16]) {
uint8_t dec[16];
aes256_ecb_decrypt(g_aes_key, old_content, dec); /* DEC(C_old) */
for (int i = 0; i < 16; i++)
iv[i] = dec[i] ^ desired[i]; /* 再异或目标内容 */
}
0x02-5 trailer 判据:把密码学断言变成能数的整数 ★
ESP 解密之后内核要校验 trailer(padding 长度 + next header):
static inline int esp_remove_trailer(struct sk_buff *skb) {
alen = crypto_aead_authsize(aead);
hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); /* 24 */
elen = skb->len - hlen;
/* 从"密文末尾往前数 alen,再往前 2 字节"读 trailer ——
这个位置恰好等于【解密后明文的最后两字节】 */
skb_copy_bits(skb, skb->len - alen - 2, nexthdr, 2);
padlen = nexthdr[0];
if (padlen + 2 + alen >= elen)
return -EINVAL; /* 调用方会把它计成 XfrmInStateProtoError */
...
}
划重点:skb->len - alen - 2 正好落在解密后明文的最后两字节。
于是我们得到一个可证伪的推论:
如果报文被正确解密,那么报错与否完全由我们自己构造的第 15 字节决定。
换句话说:/proc/net/xfrm_stat里那个计数器应该增加多少,是可以算出来的。
这就是后文一切的支点。
0x03 怎么验的:五条判据
0x03-1 判据一:一个能算出来的整数(31 / 62)
我们的写入目标是 /apex/com.android.runtime/bin/crash_dump64,往它里面写一个 1392 字节的
静态 helper(用来读 vendor 文件),也就是 87 个 16 字节块。加 6 个探针包,一共 93 个包。
按 0x02-5 的判据:alen = 16、elen = 32,报错条件是 padlen + 18 >= 32,即desired[14] >= 14。
数一遍 splicehelper 的 87 个块,其中 25 个满足这个条件(另有 1 个补零块必然通过):
| 预测 | 实测 | |
|---|---|---|
XfrmInStateProtoError 增量 | 6 + 25 = 31 | 31 |
| 无声通过(认证+解密+trailer 都过) | 61 + 1 = 62 | 62 |
逐数字吻合。 这说明:
- 内核 SA 的 authsize 就是 16(Android 确实把
truncLen=128传下去了); - 我们的 HMAC 输入、密钥、IV 数学、报文布局全部正确;
- 包确实被内核解密了,而且解密出的明文就是我们的
desired——因为 trailer 判据
恰好命中了我们构造的图案。
可是页缓存一个字节都没变:目标文件 md5 = 5f6d588628f5a69423d435419eae2191 前后一致,
ELF 头仍是 e_type=3 / 14 个 program header / 带 PT_INTERP
(如果被写进去,这里已经变成我们那个静态 helper 了)。
这一条一次性排除了整整一类怀疑:ICV 长度猜错(6 种候选全试过 0/12/16/20/24/32)、
密钥不匹配、算法不对、IV 算错、布局写错、SA 没建起来、包没到内核。
剩下的唯一可能:解密作用在载荷的副本上,页缓存页压根没进 skb。
0x03-2 判据二:七形态探针(以及我差点被骗的那个坑)
最快的验证办法是把 IPsec 整个剥掉,只用普通 UDP socket 测同一件事:
把源页发出去之后立刻原地改写这一页,然后才收包。
收到旧值 = 发送路径做了拷贝;收到新值 = 页被共享进了 skb frag。
但这里有个坑,我第一版就被它骗了:探针文件放在 /data(Android 的 FBE 加密盘),
万一内核在 splice() 读路径上就拷贝了,那”拷贝”的结论就不能算在发送路径头上。
所以先加一条探针有效性校验:
内核: 6.1.145-android14-11-3254743-abS9280ZCS6DZH3
测试目录: /data/local/tmp 文件系统: /data(f2fs)
* 管道页身份校验: 管道内是新值 -> splice 挂的就是页缓存页
splice(file→pipe) 走 generic_file_splice_read,不经 GUP,管道缓冲直接引用页缓存页,
所以改文件就等于改管道里那一页。校验通过,后面才能算数。结果:
| 形态 | 结果 |
|---|---|
A splice(file 16B) → pipe → UDP | 拷贝 |
B splice(file 整页 4096B) → pipe → UDP | 拷贝 |
C sendfile(UDP ← file) | 拷贝 |
D mmap(RO) → vmsplice → pipe → UDP | 拷贝(受 GUP 的 FOLL_WRITE→COW 干扰,仅参考) |
E1 mmap(RO) + MSG_ZEROCOPY | 拷贝(同上) |
E2 三段 ESP 形态 + MSG_ZEROCOPY | 拷贝(同上) |
F mmap(RW) + MSG_ZEROCOPY | 拷贝 |
F 组是决定性的那一格:可写 MAP_SHARED 映射下 GUP 不可能触发 COW,
它照样拷贝 —— 所以”拷贝”是发送路径自己的行为,和 COW 无关,也不是文件系统的锅。
0x03-3 判据三:反汇编设备自己的内核
对着机型固件抽出来的 vmlinux_dzh3.elf 看:
| 目标 | 看到什么 | 含义 |
|---|---|---|
udp_sendpage | 调用 iov_iter_bvec + udp_sendmsg | 是新写法,不是老式 ip_append_page |
__ip_append_data | 无任何 MSG_SPLICE_PAGES(BIT 27)判定;0 次调用 skb_append_pagefrags;分片走 sk_alloc_send_pskb + sk_page_frag_refill | 它只会自己分配 frag 页再拷进去,从不把”来源页”挂上去 |
esp_input | 全文只有 skb_cloned() 的两个判定,没有任何 skb_shinfo->flags 位测试 | 修复版应有的 tbnz w?, #0x1 不存在 ⇒ 未修补 |
ip_append_page | 全文 0 处 SKBFL_SHARED_FRAG 写入 | 6.1 分支的修复点也是空的,而且这个函数在 6.1 的 UDP 路径上已经是死代码 |
“未修补”这个结论很重要:它把”漏洞已经被厂商修掉了”这个解释也排除了,
把结论唯一地钉在”页缓存页从来没进过 skb“上。
0x03-4 判据四:内核配置(设备自报)
# Linux/arm64 6.1.145 Kernel Configuration ← 7694 行完整 config
# CONFIG_AF_RXRPC is not set ← CVE-2026-43500 这条备选路没有
# CONFIG_CRYPTO_USER is not set
# CONFIG_CRYPTO_USER_API_AEAD is not set ← Copy Fail(AF_ALG) 那条路也没有
# CONFIG_USER_NS is not set ← 原 PoC 靠 userns 自建 SA 也没门
CONFIG_NET_NS=y
旁证:nm vmlinux_dzh3.elf | grep -icE 'af_alg|algif|rxrpc' = 0;/vendor/lib/modules(357 个模块)里没有 af_alg.ko / algif_aead.ko;
真机 socket(AF_ALG, SOCK_SEQPACKET, 0) → EACCES(SELinux 在家族查找之前就拦),unshare(CLONE_NEWUSER) → EINVAL。
0x03-5 判据五:stable 6.1.y 的分界线
把 net/ipv4/udp.c 的 udp_sendpage() 逐版本翻出来:
5.15.204 udp_sendpage → ip_append_page ✅ 共享(S23 可用)
6.1.25 udp_sendpage → ip_append_page ✅ 共享
6.1.50 udp_sendpage → ip_append_page ✅ 共享
6.1.75 iov_iter_bvec + MSG_SPLICE_PAGES ❌ 拷贝 ← 断点在这里
6.1.100 同上 ❌
6.1.128(CZB2) ❌
6.1.145(DZH3) ❌
6.6.98 没有 udp_sendpage,走 splice_to_socket ✅ 共享
再把本机所有机型内核镜像普查一遍(取 udp_sendpage 的 callee):
| 镜像 | callee | 结论 |
|---|---|---|
vmlinux_pixel6 | ip_append_page | ✅ 老内核 |
CZB2/dzf2/dzh3/cza1/cza1_chc/dze2/dzg1/byh7 | iov_iter_bvec + udp_sendmsg | ❌ 全是拷贝 |
vmlinux_czg1(S25 / 6.6) | 没有这个函数 | ✅ |
0x04 根因:一次”半截回移”
结论一句话:
stable 6.1.y 把
udp_sendpage()回移成了新写法(构造 bvec 迭代器并置MSG_SPLICE_PAGES),
但同一分支的__ip_append_data()没有同步获得对MSG_SPLICE_PAGES的处理。
于是这个标志被静默忽略,数据被copy_from_iter拷进内核自己分配的 frag 页——
页缓存页从未进入 skb,就地解密自然写不到目标文件。
“把来源页原样挂进 skb”这个能力是 Linux 6.5 才引入的语义(7da0dde68486)。
5.10/5.15 用的是老式 ip_append_page()(它会直接挂页,所以 S23 能跑);
6.5/6.6 起新路径完整(所以 S25 能跑);
6.1.75~6.1.14x 这一小段,恰好卡在”新写法已经进来、底层能力还没来”的裂缝里。
这件事在 Linux 内核邮件列表上有独立佐证。在 CVE-2026-43284 的修复讨论里,内核开发者写道:
“I don’t see the support for MSG_SPLICE_PAGES present in kernels 6.1 and older
(i.e. 7da0dde68486 (“ip, udp: Support MSG_SPLICE_PAGES”) is not there in 6.1.y and older
stable branches). Are you sure the PoC didn’t fall back to the RxRPC Page-Cache Write
vulnerability in your case?“
更妙的是:6.1 分支的官方修复(f4c50a4034e6)被打在 ip_append_page() 上——
而那正是 6.1 上已经不可达的函数。也就是说,6.1 的”修复”打在了一条死路径上,
真正决定可达性的那条路径(新式 udp_sendpage)根本没有对应的共享能力。
0x05 那能不能自己改一下利用?
这是我被问得最多的问题,值得逐层回答。
| 层 | 现状 | 能改吗 |
|---|---|---|
| 报文层 | authsize=16、HMAC 覆盖 40 字节、IV 构造全部验证正确(0x03-1) | 改不出更好的 |
| 发送层 | splice / sendfile / vmsplice / MSG_ZEROCOPY(只读与可写映射)全拷贝;raw / AF_PACKET 之类最终都汇进同一个 ip_append_data | 否 |
| 内核层 | 需要 __ip_append_data 认识 MSG_SPLICE_PAGES(6.5 语义) | 用户态无法补 |
| 目标层 | 换目标文件没用(不是权限或文件系统问题,0x03-2 已证) | 否 |
| 旁路层 | RxRPC 未编、AF_ALG 未编、user namespace 未开 | 否 |
一句话:缺的那一环在内核里,用户态无论怎么改报文、换发送 API、换目标文件都绕不过去。
0x05-1 被排除的替代解释(汇总表)
排查一个”应该能跑却跑不了”的问题,最怕的是自己骗自己。这是我逐条打掉的清单:
| 替代解释 | 怎么验的 | 结果 |
|---|---|---|
| ICV / authsize 猜错 | trailer 判据的可证伪预测 | ✗ 预测 31 = 实测 31 |
| 密钥 / 算法不匹配 | 同上(HMAC 过了才可能解密、才会触发 trailer 判据) | ✗ |
| IV 数学写错 | 同上(解出来的必须是我们的 desired) | ✗ |
| SA 没建起来 / 包没到内核 | XfrmIn* 计数器 | ✗ 只有 ProtoError 在涨,SA 命中 |
| 内核已打修复 | 反汇编 esp_input / ip_append_page | ✗ 未修补 |
文件系统不让 splice 挂页(EROFS / FBE) | 管道页身份校验 | ✗ 管道里就是页缓存页 |
zerocopy 被 FOLL_WRITE 的 COW 打断 | F 组:可写映射 | ✗ 照样拷贝 |
| 原 PoC 的 userns 路径 | unshare(CLONE_NEWUSER) | ✗ EINVAL |
| RxRPC 备选路 | CONFIG_AF_RXRPC + 符号 | ✗ 未编译 |
| AF_ALG(Copy Fail)备选路 | CONFIG_CRYPTO_USER_API_AEAD + 真机 socket() | ✗ 未编译且 SELinux 拦 |
| 6.1 的 UDP 拼接路径不做页共享 | 七形态矩阵 | ✅ 成立 |
0x06 副产品与几个教训
这次排查本身也产出了一些能复用的东西,顺便记几个教训。
0x06-1 一个自研的”能不能用”探针
以后遇到任何机型、任何内核,都可以几百毫秒内回答”DirtyFrag 这类页缓存写原语可不可达”。
它是自研的(不依赖 app、不依赖 IPsec),NDK 一条命令编译成静态二进制,adb push 就能跑:
/* 核心判据:把源页发出去之后**立刻原地改写这一页**,再收包看新值还是旧值。
旧值 = 发送路径拷贝(不可达);新值 = 页被共享进 skb frag(可达)。 */
static int try_send(int kind, int fd, const char *path, int sfd, int rfd) {
...
if (kind == 0) { /* A: splice(file->pipe) -> UDP */
pipe(pfd);
ffd = open(path, O_RDONLY);
off_t off = 0;
/* 第一步:把文件页挂进管道(这一步不经 GUP,管道里就是页缓存页) */
if (splice(ffd, &off, pfd[1], NULL, 16, SPLICE_F_MOVE) == 16)
/* 第二步:管道 -> UDP socket。能不能"原样挂进 skb"全看这一步 */
sent = splice(pfd[0], NULL, sfd, NULL, 16, 0);
}
if (sent > 0) {
pwrite(fd, pageB, 16, 0); /* 关键时序:先改源页,再收包 */
...
}
}
# 编译 + 推送 + 运行(建议同时跑 /dev 与 /data,两边对比排除加密盘干扰)
$CC -O2 -static -Wall -o psp psp.c
adb push psp /data/local/tmp/ && adb shell chmod 755 /data/local/tmp/psp
adb shell /data/local/tmp/psp /data/local/tmp
输出直接给结论,附带”管道页身份校验”作为有效性背书:
* 管道页身份校验: 管道内是新值 -> splice 挂的就是页缓存页
A splice(file 16B)->pipe->UDP 【拷贝】收到旧值
...
F mmap(RW)+MSG_ZEROCOPY 【拷贝】收到旧值
0x06-2 “发包成功”不等于”写进去了”
这可能是本次最有价值的一条工程教训。
原实现(以及我第一版)在写入后打印 “已写入 N 字节到 xxx”,
但那句话的真实含义只是 “UDP 包发出去了” —— 内核完全可能因为认证不过、
或者因为页压根没进 skb,把包丢掉,而用户看到的是一行”成功”。
所以我给它加了回读校验:每写一个 16 字节块,就 pread() 回来比对。
回读不符 @0x10: 期望 0200b700... 实际 0300b700...
回读校验: 通过 5 块 / 失败 82 块
(那”通过 5 块”其实还是巧合——splicehelper 里有几块本来就是全零,跟原内容正好一致。)
没有这个回读,这次排查会绕远得多。
0x06-3 关于”看看安全补丁级别”那条判据
参考实现的中文排障指引里写:
上游修复提交是
f4c50a4034e6(2026-05-08)。如果手机的安全补丁级别已经包含该修复,
本工具会失败——这不是 bug,是漏洞没了。
…… 走到一半失败,提示补丁 #1 / #2 失败? 大概率是内核里的 DirtyFrag 漏洞已经被修复了。
本机安全补丁级别是 2026-08-05(晚于 2026-05),而实测失败点正是补丁 #1 ——
从现象看完全对得上。但必须说清楚:这条判据在本机只是”碰巧给了对的结论”。
反汇编证据显示本机内核并未包含该修复(esp_input 里没有 shared-frag 判定、ip_append_page 里没有标志写入)。真正的原因是 0x04 说的那件事:
页缓存页压根没进 skb,共享 frag 根本不存在——谈不上”被修复的检查拦下”。
所以更准确的表述是:
S24 上失败不是”漏洞被修了”,而是这条漏洞的输入端在该内核上不可达。
安全补丁级别是经验判据,不是因果解释。
0x06-4 一个有趣的推论:S24 出厂固件曾经能用
由 0x03-5 的分界线可以推:S24 系列出厂固件(2024 年初,约 6.1.25~6.1.50)的内核,udp_sendpage() 还是老式实现,理论上当时是可达的。2024 年中把 sendpage-removal 系列
回移进 6.1.y 之后,这条共享路径就断了——属于回移的副作用,不是有意修洞。
这也解释了参考实现为什么同时携带 android12-5.10 / android13-5.15 与android15-6.6 / android16-6.12 / android17-6.18 两头的模块:
它支持的是一头一尾两类内核,而 6.1 恰好夹在裂缝里。
0x07 机型对照 & 结语
| 内核 | udp_sendpage | DirtyFrag ESP 原语 | 机型举例 |
|---|---|---|---|
| 5.10 / 5.15 | ip_append_page | ✅ 可达 | S23 系列(机主实测) |
| 6.1.25 ~ 6.1.50 | ip_append_page | ✅ 可达(代码层已核实;出厂固件是否落在该区间未逐一验证) | S24 出厂固件(推断) |
| 6.1.75 及以后 | bvec + MSG_SPLICE_PAGES | ❌ 不可达 | S24 全系现行固件 |
| 6.5 / 6.6+ | 无 udp_sendpage,走 splice_to_socket | ✅ 可达 | S25 / S938B |
一句话总结:
DirtyFrag 的密码学我们全对、内核也确实没打补丁,但 6.1 的 UDP 发送路径压根不会把
页缓存页共享进 skb —— 缺的是 6.5 才有的MSG_SPLICE_PAGES语义,而 6.1.y 的”半截回移”
让这条能力只到一半。备选的 RxRPC 与 AF_ALG 在本内核配置下都没有编译,
原 PoC 依赖的 user namespace 也没有开启。S24 上这条路是结构性不通,不是调参问题。
对 S24 来说,现实结论是:想要免解锁 root,还是得回到竞态型的那套方案;
DirtyFrag 的”确定性 + 开机自动恢复”这套好处,得等到 6.6 及以上内核的机型才有。
0x08 复现附录
| 材料 | 说明 |
|---|---|
独立探针 tools/psp.c | 自研;静态二进制;adb shell /data/local/tmp/psp <目录> |
app 内探针 tools/page_share_probe.c | 便于编进任何 app 的 native 库 |
完整证据归档 FEASIBILITY_VERDICT.md | 含全部原始输出与反汇编结论 |
内核镜像 01_固件内核/vmlinux_dzh3.elf、vmlinux_CZB2.elf | 反汇编与符号普查的对象 |
| 设备自报配置 | zcat /proc/config.gz(7694 行) |
验证环境:
机型 SM-S9280(e3q,国行 DZH3)
系统 Android 16(API 36)
固件 BP4A.251205.006.S9280ZCS6DZH3
内核 6.1.145-android14-11-3254743-abS9280ZCS6DZH3
KMI android14-6.1
补丁级 2026-08-05
对照组 CZB2 = 6.1.128-android14-11-2755301-abS9280ZCS5CZB2(逐项相同)
本文只对自己拥有的设备做验证;所有写入目标都是内存里的 page cache,重启即还原。
引用到的第三方实现/文档均无 LICENSE,本文只引用其结论与事实,未复制其代码;
文中探针、转换工具等均为自行实现。
本文采用 CC BY-NC-SA 4.0 国际许可协议进行许可。
转载请注明作者及原文链接,并遵循相同方式共享原则。
链接:








