一加 15 锁 Bootloader root:GhostLock 与 CVE-2026-43499
本页分析 CVE-2026-43499——Linux 内核 futex 优先级继承(Priority Inheritance, PI) 的 use-after-free,以及基于它的 Android 提权利用链,聚焦一加 15(OnePlus 15)。 围绕这个漏洞目前有多个开源实现:
- [p2p3p/GhostLock-for-OnePlus][1]:上游通用版本,支持一加 Ace 6T / 一加 15, 完整利用链 + KernelSU 持久化;
- [cuteaplane/GhostLock-for-OnePlus15T][2]:在[1]基础上适配一加 15T(PLZ110), 作为同机型族的对照实现;
- [bartixxx32/CVE-2026-43499-OnePlus15][3]:另一支 fork(源自 x-spy 的
CVE-2026-43499-popsicle),面向一加 15 的 LD_PRELOAD 版 PoC,附带偏移自动检测 与target.h生成工具; - [d1667018881/myroot][22]:把"拿 shell"也交给浏览器的纯网页版——先用 Firefox JIT 漏洞(CVE-2026-10702)拿到 shell,再用同一套 GhostLock 内核提权,做到免安装、 重启即恢复。
这些实现的核心漏洞与利用原语相同,差异在适配机型、投放方式与工具链。
本页仅作安全研究记录,所有源码引用 pin 到具体 commit。
❗ 受影响范围速查(先看这段):CVE-2026-43499 影响 Linux 2.6.39 ~ 7.0.x 之间的内核;在 Android 上换算成 Kernel 5.x / 6.x 的 GKI 分支——即搭载 Android 12 ~ Android 16 的主流设备基本都在窗口内。判断某台机器是否可利用, 不看 Android 版本号,只看它的内核版本 + 补丁级(见下表 0.0)。
表 0.0|内核分支的修复状态[29]
| 内核分支 | 已修复起(unaffected) | Android 侧对应 |
|---|---|---|
| < 2.6.39 | —(漏洞尚未引入) | 极老设备 |
| 2.6.39 ~ 5.10.260 | 5.10.261 起 | Android 6~10(少见) |
| 5.15.x | 5.15.212 起 | Android 12~13(GKI 5.15) |
| 6.1.x | 6.1.175 起 | Android 14(GKI 6.1) |
| 6.6.x | 6.6.140 起 | Android 15(GKI 6.6) |
| 6.12.x | 6.12.86 起 | Android 16(GKI 6.12,一加 15 所在分支) |
| 6.18.x | 6.18.27 起 | 新一代 GKI |
| 7.0.x | 7.0.4 起 | —(主流修复已入 7.1) |
关键含义:只要设备的内核版本低于表中"已修复起"那一列,且在运行期满足偏移表/
do_futex 调用链条件,就理论上可被利用;反之内核一旦升到修复版(Android 侧随
OEM 月度安全公告下发),此路即封死。注意 GKI 6.12.x 到 2026 年年中仍普遍未修[25],
所以运行 Android 16(GKI 6.12)的一加 15 反而正是受影响的重灾区。
0. 核心问题:一加 15 现在能做什么?
本章直接回答两个问题——(1)一加 15 现在能不能做临时 root?(2)临时 root 之后能否 正确解锁 Bootloader 再永久 root?这两个阶段各自是否完整? 结论先给:
表 0|一加 15 两个阶段的当前状态
| 阶段 | 目标 | 现状 | 是否完整 |
|---|---|---|---|
| 阶段 1:临时 root | 不解锁 BL,拿到本次开机有效的 uid=0 | ✅ 可行(内核/机型/补丁级满足条件时) | 链条完整,但概率性、不持久 |
| 阶段 2:永久 root | 解锁 BL → 刷入受信 init_boot/内核 | ⚠️ 需要独立的 BL 解锁,GhostLock 帮不上忙 | 两阶段不衔接,存在结构性断层 |
0.1 阶段 1:临时 root —— 现在能做,但不是"万能"
一加 15 确实在 GhostLock 的支持列表里,多个来源给出已验证的内核:
表 0.1|一加 15 已被收录的内核版本
| 来源 | 机型标识 | 内核版本 | 备注 |
|---|---|---|---|
| [p2p3p/GhostLock-for-OnePlus][1] | OnePlus 15(PLK110) | 6.12.23-android16-5-…-ab14541642-4k |
✅ 已验证 |
| [JoinChang/ghostlock-oneplus][25] | OnePlus 15(CPH2745/2747/2749) | 6.12.23(android16-5) |
上游可用报告 |
| [bartixxx32/CVE-2026-43499-OnePlus15][3] | OnePlus 15 | 有已验证 profile_oneplus15.json |
LD_PRELOAD 路线 |
| [d1667018881/myroot][22] | OnePlus 15(a8f88 / b2a87) | 6.12.23-android16-5-ga8f88ad96df3-… / …gb2a876903b49-ab14541642-4k |
网页版设备表 |
但"能"有严格前提,任何一条不满足就失败:
- 内核版本必须在偏移表内,且该内核的
do_futex调用链未被 PGO 内联破坏。 [JoinChang/ghostlock-oneplus][25] 明确列出:一加 15 的部分内核(如 PLK110 的6.12.69、6.12.58)因 PGO 把do_futex内联,"waiter word"偏移变化, 被标记为对该路线不可行(infeasible)。也就是说同一机型不同固件版本,结论可能相反。 - 补丁级必须早于修复。CVE-2026-43499 在主线 7.1(commit
3bfdc63936dd)修复, 稳定分支在 2026 年 5 月回合;Android 侧要等 OEM 的月度安全公告。 GKI 6.12.x 到 2026 年年中仍普遍未修[25]。 - 投放方式决定是否需要额外条件:ADB shell 路线要能开
adb tcpip;App 自举路线要 能装/运行 exploit App;网页路线要求 Firefox for Android ≤ 151.0[22]。 - 它是"每次开机重跑"的临时 root:没有任何持久化,重启即失效; 重跑成功与否还受 LMK、后台 cgroup、绑核时序影响(见 §3.3.3 的踩坑表)。
用 OxygenOS 的 AtlasService.setEvent 命令注入 + olc2 HAL doShell 组成的
untrusted_app → uid 0 链[30],普通 App 无特殊权限即可触发。
但它已在一加 15 的 16.0.10.500(EX01) 固件中修复(OnePlus 已于 2026-08-18 推送),
且它拿到的是 vendor_qti_init_shell 域(全 capability),仍属临时 root。
列在此处是为了说明:一加 15 的"临时 root"不止 GhostLock 一条路,但都受补丁级约束。
0.2 阶段 2:临时 root → 解锁 BL → 永久 root —— 断层,不衔接
这是最关键的一点,也是两张"阶段"不完整的地方:
① 临时 root 不能用于解锁 Bootloader。
GhostLock 类的漏洞只改内核内存,不碰 Bootloader、不碰 verified boot 链。
第三方权威整理明确写道,GhostLock "is not a bootloader unlock"、
"cannot flash anything",自定义 ROM / recovery / 内核都需要解锁 BL[26]。
换句话说,阶段 1 拿到的 root 无法绕过 fastboot flashing unlock;
OnePlus 的 BL 解锁仍走官方流程:
设置 → 关于本机 → 连点版本号 → 开发者选项 → 打开 OEM 解锁
adb reboot bootloader
fastboot flashing unlock # 会清空数据
② "永久 root"的两种含义要分清。 平台上流传的"一加 15 不解锁 BL 也能(root 并)锁着 BL"的说法(如 DroidWin 的标题), 实质是 GhostLock 临时 root + 一个软重启(soft reboot)保活的技巧, 不是真正的永久 root:
- 它靠 ReSukiSU 的 Soft Reboot 在"不冷启动"的前提下让模块生效; 一旦冷重启(normal reboot),root 立即消失,必须重跑 exploit[26]。
- 它不能挂载持久化的自定义内核/recovery,也不能改系统分区 (锁 BL + AVB 会阻止),否则可能变砖[26]。
真正意义上的永久 root,仍是"解锁 BL → 打补丁的 init_boot / 自定义内核 → 刷入" 的传统路线(KernelSU / Magisk / APatch)[26][28]。这条路线与 GhostLock 无关—— GhostLock 只是让你在还没解锁时先体验一次 root。
③ 两阶段之间没有官方桥接。
当前没有任何公开工具把"临时 root"当作"解锁 BL"的跳板:临时 root 的 uid=0 权限
不足以写入 efisp / ABL 分区去伪造 Fastboot(这也是锁 BL 的意义)。
一加 15 上确实存在 GBL / superfastboot(efisp 分区刷入 *.efi) 这类
"锁 BL 下获得 Fastboot 能力"的玩法[28],但那需要先解锁 BL 或用其它漏洞取得的
刷写能力,不是从 GhostLock 临时 root 直接过渡过去的。
0.3 小结:完整性判断
- 阶段 1(临时 root):链条完整、现在可用,但受"内核版本 + 补丁级 + 投放条件" 三重约束,且天生不持久(每次开机重跑)。它适用于"只想偶尔用 root、 又不想丢 BL 锁 / 不想被应用检测到已解锁"的场景[26]。
- 阶段 2(永久 root):解锁 BL 是独立步骤,GhostLock 无法替代。 因此"临时 root → 永久 root"不是一条连续可利用的链条,两个阶段之间存在 结构性断层。想永久 root,请直接走官方解锁 + 刷机路线;想保留 BL 锁, 就只有临时 root,二者不可兼得。
- 随时间推移,阶段 1 的窗口在收窄(OEM 持续打补丁),而阶段 2 一直依赖 BL 解锁政策(国行受 Deep Testing 约束)。不要把 GhostLock 当成永久方案[26]。
1. 漏洞成因:futex PI 的 waiter UAF
1.1 futex 与优先级继承
futex(fast userspace mutex)是 Linux 上实现用户态锁的内核原语:无竞争时锁在用户态
自旋解决,只有需要睡眠/唤醒时才陷入内核。它有一个 PI 变体(FUTEX_LOCK_PI /
FUTEX_WAIT_REQUEUE_PI 等),用于优先级继承:当高优先级线程需要在低优先级线程
持有的锁上等待时,内核临时把持有者的优先级抬到等待者水平,避免优先级翻转。
PI 机制的实现依赖两个内核结构:
rt_mutex:PI 化的互斥锁本体;rt_mutex_waiter:挂在锁的等待队列(内部是红黑树,rb-tree)上的"等待者"节点, 每个在 PI 锁上睡眠的任务对应一个 waiter。
当锁的 owner 变化、任务被 requeue 时,内核会遍历这条 PI 链,并对 waiter 所在的
rb-tree 做插入 / 删除 / 重平衡(rebalance)——重平衡过程会写入节点指针字段
(如 __rb_parent_color)。CVE-2026-43499 的问题就在于:这个被遍历/改写的 waiter
对象,其内存可能已经被释放并复用。
1.2 pselect6 把 fd_set 放到内核栈上
pselect6 / select 系统调用需要把用户传入的 fd_set(fd_set 是定长位图,
FD_SETSIZE = 1024,即 128 字节)拷贝到内核栈上处理。关键点在于:
- 这份栈上数据的大小与布局是用户可控的(
fd_set的每一位对应一个 fd,用户可任意设置); - 内核栈是每线程一份、复用的内存;一段栈帧被使用后,同一线程的下一次系统调用 可能在同一片地址上重新分配栈空间。
把这两点合起来:如果先让内核在某段栈地址上放好一份可控的 fd_set,随后该栈区域被
重新分配为 rt_mutex_waiter 结构体,那么攻击者就实现了对 waiter 对象的内容控制——
即用 fd_set 的位图"拼"出一个伪造的 waiter。这正是上游 README 的"漏洞原理"描述[1][2]:
pselect6系统调用会将fd_set拷贝到内核栈上。当与 futex PI waiter 机制结合时, 释放后的栈帧可以被重新分配为rt_mutex_waiter结构体。在 PI 链遍历过程中, 红黑树(rb-tree)重平衡操作会向任意内核地址写入可控数据。
1.3 从 UAF 到"任意内核地址写"
一旦伪造的 waiter 被内核当作真实节点挂进 PI 链,rb-tree 的重平衡写入就变成了一次
受控的任意地址写:攻击者通过伪造节点里的父/子指针字段,让内核把某个值写到
攻击者指定的地址上。这个原语在 exploit 中集中体现在 fops.c——fdset_put_word() /
fdset_get_word() 用 unsigned long 数组按"字(word,ARM64 上 8 字节)"为单位精确
设置 fd_set 的每一位[4],把栈布局当作"内存写"来编排。
write(addr, val),而是只能写特定值、且时机与目标节点强相关的一次性原语。所以后面每一步(关 SELinux、改 cred)都要 单独构造一次 PI 写,而不是搭一个读写链后随意操作。
1.4 漏洞时间线
| 项目 | 内容 |
|---|---|
| CVE | CVE-2026-43499 |
| 类型 | Futex PI(优先级继承)Use-After-Free |
| 影响范围 | Linux 内核 2.6.39 ~ 7.1 |
| 修复版本 | 主线内核 7.1(commit 3bfdc63936dd) |
| Android 状态 | GKI 6.12.x 仍未修复 |
2. 利用原语:从任意写到物理内存读写
2.1 KASLR 绕过
在写内核地址之前,必须先知道内核基址(kaslr_base)。kptr_restrict 打开时无法直接
读内核指针,exploit 提供了两条互补路径,实现在 fops.c 与 slide.c:
- SLIDE 模式(
slide.c)— 用boot_id泄露:内核把sysctl_bootid对应数据 (/proc/sys/kernel/random/boot_id的内容)与nfulnl_logger等符号地址混在 同一片区域。攻击者先用一次 PI 写把某个已知偏移的内核符号地址写进boot_id的 存储区,再从用户态读回该 UUID,即可反推 KASLR slide,得到kaslr_base。slide.c里SLIDE_MAX_ATTEMPTS = 20、SLIDE_WAIT_SECONDS = 30等宏控制重试节奏[5]。 - FOPS/CFI 模式(
fops.c)— 用 ashmem fops 表泄露:当设备存在可访问的 ashmem 节点时,先拿到ashmem的file_operations表,读出open/ioctl/mmap/release/show_fdinfo等函数指针,与 image 内的已知偏移对齐即可算出基址。fops.c里kaslr_base/kaslr_slide/kaslr_expected_*一整套变量就是为这条 路径准备的[4]。
2.2 内核堆喷射:SKB + KernelSnitch
要在正确地址上"摆好"伪造对象,需要堆风水(heap spray)。exploit 的策略是:
- 在 order-3(
MM_ORDER = 3,即 32KB)大页上布置伪造对象(common.h定义MM_STRUCT_SZ 0x500、MM_ORDER 3、FAKE_*一套字段偏移)[6];伪造对象包括 fakefile_operations(劫持 ashmem 的 miscdevice fops)、fakert_mutex_waiter、 faketask_struct、fakert_mutex。 - 用 SKB(socket buffer)喷射填充内核堆:
sendmsg发送SKB_SEND_SIZE(order-3 × 2) 大小的数据、SKB_RECLAIM_SENDS控制回收[6]。 - 用 KernelSnitch 泄露
mm_struct地址做辅助定位:kernelsnitch.h通过 futex 哈希碰撞(FUTEX_SZ = 64 GB的映射区间、KSNITCH_COLLISIONS组碰撞) 判断某个 futex 桶是否落到目标对象所在页,从而推断地址[7];util.c里的setup_kernelsnitch()/run_kernelsnitch_bruteforce()/current_kernelsnitch_mm_struct()是对它的封装。
2.3 物理内存读写(Pipe)
拿到 kaslr_base 之后,exploit 用管道缓冲区(pipe_buffer)做物理地址级读写
(pipe.c)[8]:
- 在 physmap 中定位 pipe buffer;
- 伪造
pipe_buffer的ops指向已知的anon_pipe_buf_ops; - 劫持
pipe_buffer.page指向目标物理地址; - 通过正常的 pipe
read/write实现任意物理地址读写。
pipe.c 对外暴露 pipe_read64 / pipe_write64 / pipe_phys_read_data /
pipe_phys_write_data,并用 PHYS_READ_TAG / PHYS_WRITE_TAG 之类的哨兵字符串做
读回校验(physrw_readback / physrw_after_write)[8]。
3. 完整利用链:两阶段写
有了"任意内核写"+"物理读写"+"KASLR 基址",剩下的就是沿特权边界逐步推进。exploit 把
提权拆成两次 PI 写(main.c 的头部注释即写明这一结构)[9]。
表 1|GhostLock 的两次 PI 写
| 阶段 | 目标 | 手段 | 效果 |
|---|---|---|---|
| Write 1 | selinux_state.enforcing |
child-node PI 写,伪造 waiter 的 __rb_parent_color 指向 selinux_enforcing - 8 |
rb-tree 重平衡把 0x00 写进去 → SELinux 关闭 |
| Write 2 | 子进程 cred 指针 |
fork 子进程 → perf_find_task() 定位 task_struct → 算出 cred 偏移 → PI 写 cred = init_cred |
uid=0 且拥有完整 capabilities |
3.1 Write 1:关闭 SELinux
selinux_state.enforcing 位于 selinux_state 结构体偏移 0x00(target.h 里
SELINUX_ENFORCING_OFF 指向 selinux_state 符号本身[10])。因为要写入的值是 0x00,
exploit 巧妙地选择"child-node"模式:伪造 waiter 时把它当作已有父节点的右/左子节点,
令 rb-tree 在重平衡时把子节点指针(此时为 0)写到 selinux_enforcing - 8 的位置。
util.c 里 pselect_child_node 这个开关(1 = 写 page+0x100,0 = 写零)正是区分
"写零"与"写非零"两种写模式的[6]。
关闭 SELinux 后,原本被 SELinux 拦截的操作(例如 setprop)在 policy 层面不再被阻止——
这就是 bootstrap 模式能继续往下走的先决条件。
3.2 Write 2:提权到 root
Write 2 需要先把"要改谁"定位出来(main.c 注释:Phase 2: Write 2 — cred = init_cred
(child-node PI write via perf task leak))[9]:
- fork 子进程:目标是改子进程的 cred,父进程改完后再由子进程继续执行 root 逻辑, 避免把自身 cred 改坏导致控制流异常。
perf_find_task()定位task_struct:利用perf_event_open的可观测能力 在任务链中定位该子进程的task_struct地址(这也是为什么完整利用需要 perf 可用)。- 算出
cred字段偏移:target.h给出TASK_CRED_OFF = 0x900、TASK_REAL_CRED_OFF = 0x8f8(BTF 校验过的字段偏移)[10]。 - PI 写
cred = init_cred:init_cred是内核最初的凭据对象,uid=0 且带完整 capability 集合;把子进程 cred 指针直接指向它即可"借"到 root 身份。 - 清除 seccomp:连同
TIF_SECCOMP标志与seccomp结构体一起清零 (TASK_SECCOMP_OFF = 0x9c8)[10],解除系统调用过滤。
改完后还会回读校验:root.c 里 root_uid_before/after、
cred_sid_before/after、capable_head_before/after 等变量用来确认 uid、SELinux SID、
capability 链头确实变了,而不是"以为写成功了"[11]。
3.3 四种投放方式
同一套内核利用链,各仓库用了不同的"落地点":
表 2|各实现的投放方式对比
| 仓库 | 目标机型 | 投放方式 | 持久化 |
|---|---|---|---|
| [p2p3p/GhostLock-for-OnePlus][1] | 一加 Ace 6T / 一加 15 | 独立 ELF ghostlock,ADB shell 或 --bootstrap 自举 |
KernelSU(需 ksud) |
| [cuteaplane/…15T][2] | 一加 15T | 同上,适配 15T 偏移与时序 | KernelSU(需 ksud) |
| [bartixxx32/…OnePlus15][3] | 一加 15 | LD_PRELOAD 共享库 preload.so |
独立 su 守护进程 |
| [d1667018881/myroot][22] | 55+10 台(多厂商) | 纯网页:Firefox JIT 拿 shell + LD_PRELOAD 加载 .so |
KernelSU 或 su daemon |
3.3.1 独立 ELF + --bootstrap 自举(p2p3p / cuteaplane)
App 上下文通常带 seccomp 过滤,会挡住 perf_event_open,且没有 ADB shell 的完整权限。
exploit 为此设计了 --bootstrap 模式,其"自举"思路是[1][2]:
- 先只做 Write 1(关 SELinux);
- 借 SELinux 关闭后的权限执行
setprop,打开 ADB TCP 端口5555; - 用内置的 Mini ADB 客户端连回
127.0.0.1:5555,用预推送的 RSA 私钥认证; - 在 ADB shell 上下文(无 seccomp 限制)里再跑一次完整利用。
miniadb.c 就是这套逻辑:它实现了精简的 ADB 协议(A_CNXN / A_AUTH / A_OPEN
等命令字),通过 dlopen("libcrypto.so") + PEM_read_bio_RSAPrivateKey + RSA_sign
对 ADB 的 token 挑战签名,支持 SHA-1(NID_SHA1)与 SHA-256(NID_SHA256)[12]。
3.3.2 LD_PRELOAD 注入(bartixxx32)
bartixxx32 的版本把 exploit 编成共享库 preload.so,靠 LD_PRELOAD 注入到任意
系统进程里执行[3]:
adb push source/build/bin/preload.so /data/local/tmp/preload.so
adb shell chmod 0644 /data/local/tmp/preload.so
adb shell LD_PRELOAD=/data/local/tmp/preload.so /system/bin/true
adb shell /data/local/tmp/su -c id
这条路径不依赖 ADB 自举,而是直接把提权逻辑塞进一个普通进程的地址空间;提权成功后落到
一个独立的 su 守护进程(源码里的 su_daemon.c 与 su_blob.S),无需安装 KernelSU。
它源自 [x-spy/CVE-2026-43499-popsicle][13],主要改动是修了一处 kallsyms 对齐 bug
并补了一份已验证的一加 15 profile[3]。
3.3.3 纯网页(Firefox JIT + LD_PRELOAD):myroot
[d1667018881/myroot][22] 把"拿 shell"和"提权"两层漏洞都塞进一个网页里,做到
免安装、纯网页、重启即恢复的临时 root。它不自己实现 CVE-2026-43499 的利用,
而是复用上游 GhostLock 的 src/core/*.c(YuKongA/ghostlock-app),
只是把投送方式换成浏览器:
① Firefox JIT 漏洞 (CVE-2026-10702)
Firefox for Android ≤ 151.0 的 JIT 类型混淆
→ 泄漏 typed array 元数据 → 任意读写 → mprotect shellcode
→ 在 untrusted_app 沙箱内执行 shell(uid 10478)
② 上传 ghostlock.so 到 Firefox 私有目录
/data/data/org.mozilla.firefox/files/res
③ LD_PRELOAD 触发(SELinux 禁止 untrusted_app execve app data 文件,
但允许 dlopen / LD_PRELOAD)→ constructor 加载即自动执行
④ GhostLock 提权链:W1 破 SELinux → W2 改 cred → W3 绕 seccomp
→ root shell → ksud late-load → KernelSU 激活
这套设计里最值得注意的是为什么必须用 LD_PRELOAD 而不是直接 execve:Android 的
SELinux 策略禁止 untrusted_app 域 execve 应用私有目录下的文件,但允许
dlopen / LD_PRELOAD。于是 exploit 被编译成带 constructor 的共享库,
靠 __attribute__((constructor)) ghostlock_preload_init() 在加载时自执行[22]。
这也解释了为何它不能直接拿 YuKongA 原版(PIE 可执行文件)的产物——原版的 main()
不会被 LD_PRELOAD 触发。myroot 内核提权代码的源头是
[YuKongA/ghostlock-app][24] 的 src/core/*.c,本地只保留三处覆盖上游后必须重打的定制
(common.h 的 MM_PARTIALS、util.c 的 mm 喷溅数量、main.c 的 constructor +
unsetenv)[23]。
myroot 的 README 还固化了一张"关键修复记录"表,记录六个只在真机复现的坑[22], 其中两条对理解这套利用链特别有价值:
表 3|myroot 真机踩坑摘录[22]
| # | 现象 | 根因 | 修复 |
|---|---|---|---|
| 3 | W1 "route success" 但 SELinux 不变 | 写入源 *(base+0x100) 的内容环境相关(0 或 0x41) |
util.c 把 skb 偏移 0xf80 显式写 0 |
| 4 | ghostlock 进程被 LMK 杀 | fork ~400 子进程触发后台 cgroup 回收 | MM_PARTIALS 5→2、prepare 8×→5×、每 8 次 fork usleep |
| 5 | KernelSU 永不激活(.ghostlock_ksu.log 不存在) |
root shell exec 时继承 LD_PRELOAD,constructor 在 root 上下文无限重跑 |
main.c exec root script 前 unsetenv("LD_PRELOAD") |
第 5 条最隐蔽:现象是 W1/W2 都过、日志显示 child is root!,但 KernelSU 一直显示
"未安装",且 .ghostlock_ksu.log 不存在——根因是 root shell 链继承了 LD_PRELOAD,
导致 constructor 递归重跑,真正的 root script 永不执行。
除了 GhostLock 方案,myroot 还内置了一组 IonStack 方案:用的是另一支
NebuSec IonStack(经 hexo141/Rootme 搬运),提权后落到裸 su daemon
而非 KernelSU,覆盖约 10 台设备。两组共用同一条 Firefox JIT 入口,区别只在投放的 .so[22]。
两层组合的完整投送工程:manifest.json 按 uname -r 为 55 台 GhostLock 设备
路由到不同的 .so(其中小米 17 钉死在一份真机实测过的 v19 字节上),
并把构建 pin 到上游冻结 ref(CORE_REF / KERNELS_REF)以保证可复现[22][23]。
4. 机型适配:偏移表与内核匹配
4.1 运行时内核匹配(p2p3p / cuteaplane)
这套实现的入口是运行时内核匹配:启动时读 uname -r,在查找表里逐条比对,匹配不上
就直接拒绝运行。src/devices/offsets.h 定义 struct kernel_offsets 与
known_offsets[] 查找表(以 uname_r 为键、NULL 为哨兵)[14],
具体机型的偏移放在 src/devices/<机型>/offsets.h[15][16]。
main.c 里 select_offsets() 完成 uname 匹配,并用一整串 #define 把
active_offsets->off_* 动态绑到编译期宏上——这样同一份代码能适配多个内核版本,
而无需为每个版本重新编译[9]。
表 4|p2p3p/GhostLock-for-OnePlus 支持设备[1]
| 设备 | 代号 | SoC | 内核版本 | 固件 | 状态 |
|---|---|---|---|---|---|
| 一加 Ace 6T | PLR110 | SM8845(骁龙 8s Elite) | 6.12.38-android16-5-…-ab14275539-4k |
ColorOS 16.0.2.403 | ✅ 已验证 |
| 一加 Ace 6T | PLR110 | SM8845(骁龙 8s Elite) | 6.12.38-android16-5-…-ab14552068-4k |
ColorOS 16.0.8.301 | ✅ 已验证 |
| 一加 15 | PLK110 | SM8845(骁龙 8s Elite) | 6.12.23-android16-5-…-ab14541642-4k |
— | ✅ 已验证 |
表 5|cuteaplane/GhostLock-for-OnePlus15T 适配信息[2]
| 项目 | 值 |
|---|---|
| 设备 / 代号 | 一加 15T / PLZ110 |
| SoC | SM8850(骁龙 8 Elite) |
| 内核版本 | 6.12.38-android16-5-g844001fb8721-ab14552068-4k |
| 固件 | ColorOS 16.0.9.400 |
| 状态 | ✅ 已验证 |
offset 的来源分四类:kallsyms 全局符号 28 项由 tools/extract_target.py 提取、
BTF 结构体字段 57 项由 tools/extract_btf.py 提取、推导值 9 项自动计算、
固定常量 12 项硬编码[1]。
extract_target.py从boot.img内嵌的 kallsyms 表里解析符号地址, 例如SELINUX_ENFORCING_OFF = "selinux_state"、INIT_CRED_OFF = "init_cred", 并对loggers[0][1](loggers + 0x10)、Rust-mangled 的ashmem_fops等做特殊处理[17]。extract_btf.py直接在 vmlinux 的 BTF 段里暴力搜索STRUCT类型, 按成员名取bit_offset // 8得到字段偏移——task_struct(17 字段)、rt_mutex_waiter(6 字段)、cred(4 字段)等就是这么来的[18]。
4.2 适配一加 15T 的改动(cuteaplane)
一加 15T 的物理内存布局与 CPU 时序和上游机型不同,所以 target.h 里有一组机型相关的
常量:KIMAGE_TEXT_BASE = 0xffffffc080000000、P0_PAGE_OFFSET
(VABITS=39 的内核地址空间)、KERNELSNITCH_IDENTITY_START/END
(覆盖该 SoC 约 48GB 的 physmap)等[10];common.h 里的 CORE 7(绑核)与
`PSELECT*时序参数则对应 15T 的 CPU 特性<sup class="cite"><a href="#ref-6">[6]</a></sup>。仓库最近一次提交3ea45303`
("load_policy 修复策略")还处理了 bootstrap 模式下策略文件完整性修复[2]。
4.3 自动检测与 target.h 生成(bartixxx32)
bartixxx32 的版本把适配工具链做成两个脚本,并用一份 JSON profile 描述机型[3]:
detect_offset.py:从已 root 的同固件设备上读/proc/iomem,自动推导target.h需要的两个物理地址字段。其派生逻辑(源码注释明确写出了不变量校验[19]):p0_phys_offset=/proc/iomem里System RAM的最低地址向下取整到 1 GiB (ARM64_MEMSTART_ALIGN = 1 << 30,对应 arm64memstart_addr的取值方式);p0_kernel_phys_load=Kernel code段起始地址减去ARM64_STEXT_OFFSET(0x10000,即 arm64 Image 里_stext相对_text的偏移);- 之后还会反向闭合校验(phys_offset 是否 1 GiB 对齐、kernel 载入是否落在 System RAM 内、
kernel_phys_load + 0x10000 == code.start等),避免"只是打印两个猜出来的值"。
generate_target.py:解析boot.img(校验ANDROID!magic、boot header v3/v4 与header_size、arm64 Image 的ARM\x64magic 与image_size),结合 JSON profile 生成source/src/target.h[20]。profile_oneplus15.json:一加 15 的 profile,只含两个字段[21]:{ "p0_phys_offset": "0x80000000", "p0_kernel_phys_load": "0xc7800000" }
target.h 变成了可复现的三步操作,是三个实现里适配工具链最完整的一个。
4.4 跨设备调整项(上游总结)
上游 README 给出了一张"换机型要动哪里"的清单[1]:
表 6|跨设备调整项
| 调整项 | 说明 |
|---|---|
VA_BITS |
48 vs 39 → 更新 target.h 内存布局 |
| 时序参数 | 调整 common.h 中的 PSELECT_* 参数 |
| ashmem 实现 | C vs Rust → 更新 extract_target.py 符号匹配规则 |
| secureguard | 无 OnePlus secureguard 可简化部署流程 |
5. 构建与使用
5.1 构建(p2p3p / cuteaplane)
依赖 Android NDK(r25+),并设置 ANDROID_NDK_HOME 或 ANDROID_NDK_ROOT[1][2]:
make # 默认 API 35
make API=34 # 指定 API 级别
NDK=/path/to/android-ndk make # 指定 NDK 路径
产物 ghostlock 是静态链接的 ARM64 ELF。
5.2 一次性设置与运行
# 1. 启用 ADB TCP 模式
adb tcpip 5555
# 2. 推送 ADB 密钥(bootstrap 模式需要)
adb push ~/.android/adbkey /data/local/tmp/a/adbkey
# 3. 推送利用程序
adb push ghostlock /data/local/tmp/a/e
adb shell chmod 755 /data/local/tmp/a/e
| 模式 | 命令 | 说明 |
|---|---|---|
| 完整利用(ADB shell) | /data/local/tmp/a/e |
perf 可用,精确泄露 task_struct;W1→W2→加载 KernelSU |
| Bootstrap(App 上下文) | /data/local/tmp/a/e --bootstrap |
先关 SELinux,再自举 ADB 进入无 seccomp 环境跑完整利用 |
| 仅 Write 1 | /data/local/tmp/a/e --write1 |
最多尝试 20 次;用于调试或临时关 SELinux |
首次成功后,
resetprop会自动设置persist.adb.tcp.port=5555,后续重启可全自动运行[1][2]。
5.3 KernelSU 的依赖:ksud
GhostLock 只负责提权,KernelSU 的安装依赖 ksud(内含各 KMI 版本的
kernelsu.ko)[1][2]:
| 获取方式 | 说明 |
|---|---|
| ReSukiSU APK(推荐) | APK 捆绑了 libksud.so |
| CI 发行版 | 从 ReSukiSU CI 下载 ksud-aarch64-linux-android.zip |
没有
ksud时,利用仍可获得 uid=0 的 root shell,但 KernelSU 不会被安装,su不会持久化。bartixxx32 的 LD_PRELOAD 版本则改用自带su守护进程[3], 不依赖ksud。
6. 文件结构与模块职责
以 cuteaplane 版为例(与上游结构一致)[2]:
ghostlock-oneplus/
├── Makefile # NDK 交叉编译
├── src/core/
│ ├── main.c # 入口:两阶段写入 + root shell
│ ├── fops.c # FOPS/CFI 模式:pselect 路由、PI 写入、KASLR 泄露
│ ├── util.c # 堆喷射、KASLR 计算、内核 R/W 原语
│ ├── slide.c # SLIDE 模式:boot_id KASLR 泄露 + pselect 路由
│ ├── pipe.c # 管道缓冲区物理内存读写 (physrw)
│ ├── root.c # cred 覆盖、seccomp 清除、root 子进程管理
│ ├── miniadb.c # 内置 ADB 客户端 (TCP + RSA 认证)
│ ├── common.h # 全局宏、结构体、声明、常量
│ ├── target.h # 目标内存布局 / 结构体偏移 / KASLR 参数
│ ├── offset.h # 编译期 target config 桥接
│ └── kernelsnitch/ # KernelSnitch — mm_struct 地址泄露
└── tools/
├── extract_target.py # kallsyms 全局符号偏移 (28)
└── extract_btf.py # BTF 结构体字段偏移 (57)
表 7|核心模块职责[1]
| 模块 | 文件 | 职责 |
|---|---|---|
| 入口 | main.c |
命令行解析、W1/W2 调度、bootstrap 流程 |
| PI 路由 | fops.c / slide.c |
pselect/select 栈布局、futex PI 链操控、竞争触发 |
| KASLR | util.c / fops.c / slide.c |
双模式绕过:boot_id 泄露 + fops 表泄露 |
| 堆喷射 | util.c |
order-3 大页分配、SKB 喷射、伪造对象布局 |
| 物理 R/W | pipe.c |
pipe_buffer 劫持、任意物理内存读写 |
| 提权 | root.c |
cred 覆盖、capability 验证、seccomp 清除 |
| ADB | miniadb.c |
精简 ADB 协议客户端、RSA 认证 |
| 泄露引擎 | kernelsnitch/ |
futex 哈希碰撞定位 mm_struct |
7. 常见问题(上游 FAQ)
- 运行后无输出:确认在 ADB shell 环境运行;App 上下文要用
--bootstrap[1][2]。 no offsets for this kernel:内核版本未收录,需按 §4 提取偏移后重新编译。- Write 1 老是失败:确认绑核正确(
CORE宏)、调整PSELECT_ENTER_DELAY_USEC时序、排查是否有其他进程干扰堆状态。 - Write 2 的 perf 返回 0:检查 seccomp 是否挡住
perf_event_open(App 上下文用--bootstrap);确认 KASLR 绕过已拿到kaslr_base。 - KernelSU 未加载:确认
ksud存在且可执行;检查网络策略文件完整性 (load_policy修复)。
8. 各版本关系小结
- 漏洞与利用原语相同:都是 futex PI waiter UAF → pselect 栈布局 → rb-tree 任意写 →
关 SELinux + cred 提权。后两者都直接或间接继承自 x-spy 的
CVE-2026-43499-popsicle谱系[13]。 - p2p3p/GhostLock-for-OnePlus 是最完整的"整机方案":多机型偏移表、ADB 自举、
KernelSU 持久化、
extract_target.py+extract_btf.py工具链[1]。 - cuteaplane/GhostLock-for-OnePlus15T 在[1]基础上做机型适配,是新机型移植的
最小改动范例:新增
src/devices/plz110t/offsets.h+ 调整target.h/common.h常量[2]。 - bartixxx32/CVE-2026-43499-OnePlus15 走了另一条路:LD_PRELOAD 注入 +
独立
su守护进程,并提供了detect_offset.py+generate_target.py+ JSON profile 的自动化适配工具链[3]。 - d1667018881/myroot 把投送层也做进了浏览器:前置用 Firefox JIT 漏洞 (CVE-2026-10702)拿 shell,内核提权仍复用 GhostLock,做成纯网页、临时 root、 重启即恢复的形态;同时内置 NebuSec IonStack 作为第二方案[22]。
参考文献
[1]: https://github.com/p2p3p/GhostLock-for-OnePlus [2]: https://github.com/cuteaplane/GhostLock-for-OnePlus15T [3]: https://github.com/bartixxx32/CVE-2026-43499-OnePlus15 [13]: https://github.com/x-spy/CVE-2026-43499-popsicle [22]: https://github.com/d1667018881/myroot [25]: https://github.com/JoinChang/ghostlock-oneplus [26]: https://github.com/awesome-android-root/awesome-android-root [27]: https://xdaforums.com/t/how-to-unlock-oneplus-15-fastboot-eu-version-at-least.4768792/ [28]: https://xdaforums.com/t/root-with-locked-bootloader-oneplus-15.4783161/ [29]: https://www.cve.org/CVERecord?id=CVE-2026-43499 [30]: https://blog.nns.ee/2026/09/24/oneplus-root/
1. p2p3p. GhostLock-for-OnePlus: Kernel exploit targeting locked-bootloader OnePlus devices (CVE-2026-43499)[EB/OL]. GitHub. https://github.com/p2p3p/GhostLock-for-OnePlus (分支main,README_CN.md). ↩
2. cuteaplane. GhostLock-for-OnePlus15T: Kernel exploit targeting locked-bootloader OnePlus 15T devices (CVE-2026-43499)[EB/OL]. GitHub, commit 3ea45303bfdbc017a67c1c8583795e1a5bb7f33d. https://github.com/cuteaplane/GhostLock-for-OnePlus15T ↩
3. bartixxx32. CVE-2026-43499-OnePlus15: Android 16 local privilege escalation for OnePlus 15 (Educational PoC)[EB/OL]. GitHub, commit dfdbca7e71a4318e7b4d2c78181506d76e79db16. https://github.com/bartixxx32/CVE-2026-43499-OnePlus15 ↩
4. cuteaplane. src/core/fops.c[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/fops.c ↩
5. cuteaplane. src/core/slide.c[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/slide.c ↩
6. cuteaplane. src/core/common.h, src/core/util.c[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/common.h ↩
7. cuteaplane. src/core/kernelsnitch/kernelsnitch.h[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/kernelsnitch/kernelsnitch.h ↩
8. cuteaplane. src/core/pipe.c[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/pipe.c ↩
9. cuteaplane. src/core/main.c[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/main.c ↩
10. cuteaplane. src/core/target.h[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/target.h ↩
11. cuteaplane. src/core/root.c[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/root.c ↩
12. cuteaplane. src/core/miniadb.c[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/core/miniadb.c ↩
13. x-spy. CVE-2026-43499-popsicle: Original CVE-2026-43499 exploit[EB/OL]. GitHub. https://github.com/x-spy/CVE-2026-43499-popsicle ↩
14. cuteaplane. src/devices/offsets.h[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/devices/offsets.h ↩
15. cuteaplane. src/devices/plz110t/offsets.h[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/src/devices/plz110t/offsets.h ↩
16. p2p3p. src/devices/ace6t/offsets.h, src/devices/op15/offsets.h[EB/OL]. GhostLock-for-OnePlus, 分支 main. https://github.com/p2p3p/GhostLock-for-OnePlus/tree/main/src/devices ↩
17. cuteaplane. tools/extract_target.py[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/tools/extract_target.py ↩
18. cuteaplane. tools/extract_btf.py[EB/OL]. GhostLock-for-OnePlus15T, commit 3ea45303. https://github.com/cuteaplane/GhostLock-for-OnePlus15T/blob/3ea45303bfdbc017a67c1c8583795e1a5bb7f33d/tools/extract_btf.py ↩
19. bartixxx32. detect_offset.py[EB/OL]. CVE-2026-43499-OnePlus15, commit dfdbca7. https://github.com/bartixxx32/CVE-2026-43499-OnePlus15/blob/dfdbca7e71a4318e7b4d2c78181506d76e79db16/detect_offset.py ↩
20. bartixxx32. generate_target.py[EB/OL]. CVE-2026-43499-OnePlus15, commit dfdbca7. https://github.com/bartixxx32/CVE-2026-43499-OnePlus15/blob/dfdbca7e71a4318e7b4d2c78181506d76e79db16/generate_target.py ↩
21. bartixxx32. profile_oneplus15.json[EB/OL]. CVE-2026-43499-OnePlus15, commit dfdbca7. https://github.com/bartixxx32/CVE-2026-43499-OnePlus15/blob/dfdbca7e71a4318e7b4d2c78181506d76e79db16/profile_oneplus15.json ↩
22. d1667018881. myroot: 自用 Android 临时 Root 网页(CVE-2026-10702 + CVE-2026-43499)[EB/OL]. GitHub, commit 16b6015558a2fb9a5d3be9e961b2f7f9a9d6b942. https://github.com/d1667018881/myroot ↩
23. d1667018881. AI_HANDOFF.md, manifest.json, scripts/build-ghostlock.sh[EB/OL]. myroot, commit 16b60155. https://github.com/d1667018881/myroot/blob/16b6015558a2fb9a5d3be9e961b2f7f9a9d6b942/AI_HANDOFF.md ↩
24. YuKongA. ghostlock-app: CVE-2026-43499 exploit app[EB/OL]. GitHub. https://github.com/YuKongA/ghostlock-app ↩
25. JoinChang. ghostlock-oneplus: GhostLock (CVE-2026-43499) kernel exploit for Android devices with locked bootloader[EB/OL]. GitHub. https://github.com/JoinChang/ghostlock-oneplus ↩
26. awesome-android-root. root-without-unlocking-bootloader.md: GhostLock (CVE-2026-43499) 的临时 root 边界与 BL 解锁关系[EB/OL]. GitHub. https://github.com/awesome-android-root/awesome-android-root/blob/main/docs/rooting-guides/root-without-unlocking-bootloader.md ↩
27. XDA Forums. How to unlock OnePlus 15 fastboot (EU version at least)[EB/OL]. 2026. https://xdaforums.com/t/how-to-unlock-oneplus-15-fastboot-eu-version-at-least.4768792/ ↩
28. XDA Forums. Root with locked bootloader — OnePlus 15[EB/OL]. 2026. https://xdaforums.com/t/root-with-locked-bootloader-oneplus-15.4783161/ ↩
29. CVE Program. CVE-2026-43499: rtmutex: Use waiter::task instead of current in remove_waiter()[EB/OL]. 2026. https://www.cve.org/CVERecord?id=CVE-2026-43499 ↩
30. Rasmus Moorats (nns). Getting root on OnePlus 15 from an untrusted app, via an audio debug service and a vendor HAL[EB/OL]. 2026-09-24. https://blog.nns.ee/2026/09/24/oneplus-root/ ↩
© 2026 Yang Huan · yanghuan9812@qq.com