Skip to content

Linux kernel version 5.15.197-g08015331567-dirty adapted for IQOO Neo9 - #300

Closed
linux-tools wants to merge 35 commits into
YuKongA:mainfrom
linux-tools:vr-ko-bypass-dev
Closed

linux-tools wants to merge 35 commits into
YuKongA:mainfrom
linux-tools:vr-ko-bypass-dev

Conversation

@linux-tools

Copy link
Copy Markdown

补丁修复的问题

  1. 无 KMI 内核 KSU 无法加载:PD2338 uname -r 无 KMI token,ksud late-load 选不到模块 → 新增 DIRECT 分支 ksud insmod 直连
  2. "threads joined" 卡死:join 看门狗 + verify 3s 超时
  3. 直连分支 unlabeled packet 拒绝:upstream 的整份策略重载(fixup)在 PD2338 上不足以放行 unlabeled 包 → 新增 sepolicy patch send/recv 两条增量规则(仅 DIRECT=1 时执行)
  4. tagB 5.15 僵尸:__state@0x28 写入产生僵尸 task(PD2338 实测复现)→ 改为 GHOSTLOCK_VR_TAG_B 显式 opt-in,默认跳过
  5. KSU_ENFORCE 格式 bug:模板 %d 接 char* → 改 %s

配套内容

  • PD2338 设备 profile(5.15.197-….conf + index.conf 追加)
  • PAGE_SIZE 宏守卫、CPU 对 env 覆盖(均 opt-in,不影响其他设备)
  • 注释:PD2338 设备 scoping、PER-KERNEL 规则、tagA 对 5.15 为 no-op 的取证结论
  • 版本号 1.2.568

重要约束

KSU 在 Neo9 必须自行编译:upstream 无任何现成 kernelsu-vivo.ko;模块绑定具体内核构建(vermagic 逐字匹配 + struct module 布局由该内核 .config 决定)。交付包里的 .ko 仅对 5.15.197-g708015331567-dirty 有效;Neo9 任何内核版本变化都须用新内核树重编后重新部署。本人测试通过的ksu是ReSukiSU版本

NickJi2019 and others added 30 commits September 28, 2026 19:54
Introduce a controller for behaviors outside the vulnerability path
(vendor guard neutralization, environment fixups), decoupled from the
frontend x backend x middleware axes:

- AncillaryPolicy concept + neutral defaults and the AncillaryStage
  entry points (PreSpawn/PostSpawn/PreHandoff).
- Header-only AncillaryController<M> that dispatches the enabled
  behaviors; the write/read primitives are injected by the backend.
- VrGuardPolicy registered as the first behavior, gated off (skeleton).
- Host test for the registry/gate/dispatch signature; Makefile wiring.
Introduce a controller for behaviors outside the vulnerability path
(vendor guard neutralization, environment fixups), decoupled from the
frontend x backend x middleware axes:

- AncillaryPolicy concept + neutral defaults and the AncillaryStage
  entry points (PreSpawn/PostSpawn/PreHandoff).
- Header-only AncillaryController<M> that dispatches the enabled
  behaviors; the write/read primitives are injected by the backend.
- VrGuardPolicy registered as the first behavior, gated off (skeleton).
- Host test for the registry/gate/dispatch signature; Makefile wiring.
The route did one setsockopt and one walk per stage. The copy landing on the
caller's stack races skb page recycling, so a single call is a single lottery
ticket, and on a non-resident (one-shot) multicast write a miss is not
recoverable: w1_attempts is forced to 1 for this middleware and the miss
usually panics the kernel.

Repeat the poison/walk instead, arming from the point where the earlier copies
have overwritten each other, and hold the waiter thread on yield between the
copy and the walk (no syscall may run there or the poisoned frame is
clobbered). The counts come from route.multicast_waiter {attempts,
arm_sequence, arm_hold}; 0 keeps the compiled-in default, so existing profiles
are unchanged.

Measured on vivo 6.1.145 (iQOO 12, compact waiter): every stage of a completed
run now reports attempts=16 calls=1 success=1, i.e. the first armed poison
lands, where the single-shot form failed the same stage repeatedly.
Implements VrGuardPolicy per docs/analysis/ancillary-controller-guide.md.

- plan_vr_guard(): pure, host-testable arithmetic over the resolved profile
  (__tracepoint_sys_exit image offset + offsetof(struct tracepoint, funcs));
  nullopt when either fact is absent, so a profile that describes no tracepoint
  can never produce a write.
- enabled(): the profile gate only (meta.vr_guard). Runtime applicability is
  decided at apply() time from /proc/modules, as the guide requires: the same
  GKI release ships from many vendors, so the release is not a device identity.
- apply<Middleware>(): PreSpawn only. SELinux is permissive and no victim exists
  yet, so one write covers every process the run brings up, ksud included.
- The write arrives through AncillaryContext as a plain function pointer
  (AncillaryZeroFn); the backend supplies it through a new
  Cve2026_43499Policy::zero_word<M>() adapter. The behavior never names the
  middleware, the attack path gains no indirect dispatch, and the header stays
  host-compilable (the host test passes a stub).
- Profile contract: meta.vr_guard (gate), offset.vr_sys_exit_tp (symbol),
  vr_guard.tracepoint_funcs (struct layout). Nothing is derived from the kernel
  version: struct tracepoint gained probestub in 6.6 and moved funcs from 0x40 to
  0x48, so the offset is a per-image fact.

Verified on vivo 6.1.145 (iQOO 12 / PD2307, ~4 min uptime, screen untouched):

  [*] vr guard: neutralizing __tracepoint_sys_exit.funcs
      image=ffffffc0823cb1c0 target=ffffff802a3cb1c0 width=8
  [+] vr guard: sys_exit probe disabled (attempt 1)
  [+] child is root!
  [+] KernelSU ready

image = KIMAGE_TEXT_BASE + 0x23cb180 (symbol) + 0x40 (layout); target is its
direct-map alias. Measured layout for this image (BTF): struct tracepoint is
0x48 bytes with funcs at 0x40, regfunc 0x30, unregfunc 0x38, no probestub. su
works afterwards and the device stays up. The existing per-task tag strip only
covered the exploit child, which is why ksud and the shells it spawned were
still killed (upstream YuKongA#154, YuKongA#201).

Still to come in this PR (see the description): the extractor emitting
tracepoint_funcs from BTF, and the profile-core mirror so the app can author the
three fields.

Host tests: ancillary_test extended with the gate, the fail-closed plan and the
target arithmetic. The plan is deliberately gate-free: the guide keeps gate,
applicability and plan as three separate facts.
Completes the profile contract the guide asks for (section 6).

Extractor (tools/extract_rs):
- The funcs offset is now read from the image's BTF (`struct tracepoint`,
  member `funcs`) instead of being a constant: on this 6.1.145 image BTF reports
  sizeof(struct tracepoint) = 0x48 with funcs at 0x40, regfunc 0x30, unregfunc
  0x38 and no probestub; a 6.6 image reports funcs at 0x48. Nothing consults the
  release, which is what the guide requires.
- `__tracepoint_sys_exit` joins the symbol table as an optional symbol, and
  --format conf emits `recommend_vr_guard = 1`, `offset.vr_sys_exit_tp` and a
  `vr_guard { tracepoint_funcs }` section when the image yields them. Verified
  against this device's own boot.img: 37532032 / 64, matching what the device run
  used.

profile-core / app:
- NativeProfile.kt carries the gate (`recommend_vr_guard` -> meta.vr_guard), the
  symbol (offset.vr_sys_exit_tp) and the layout section (vr_guard.tracepoint_funcs)
  on both the writer and the reader path. The gate and the symbol are written
  only when non-zero, so every existing profile keeps byte-identical output and
  the frozen golden is unchanged.
- ProfileResolver accepts the two new top-level keys.
- ProfileRoundTripTest gains a vr.ko fixture: gate, layout and symbol survive a
  toBinary/fromBinary round trip, and a profile without the layout decodes as
  absent (fail-closed).
- Built-in profile for 6.1.145-android14-11-maybe-dirty (vivo iQOO 12 / PD2307),
  with `recommend_vr_guard = 1` and the BTF-derived layout, registered in
  index.conf.

Test status: `cargo test` 34/34; `:app:testDebugUnitTest` + `:profile-core:test`
all green (79 app tests), including the previously failing frozen golden — the
zero-omission above is what keeps it byte-identical.

Exported profile decode check (app-side exporter, this device):
  [meta]   vr_guard = 1
  [offset] vr_sys_exit_tp = 37532032
  [vr_guard] tracepoint_funcs = 64
The vr.ko gate/symbol/layout facts and the multicast tuning values were added as
new struct members, which grew sizeof(kernel_offsets) and moved every member
after them: the attack functions load those members by offset, so the
disassembly gate (engineering-standards §8.2) flagged the shifted offsets as
hard operand failures.

Put them in padding that already exists instead: KernelMisc's tail carries the
gate, the symbol offset and offsetof(struct tracepoint, funcs); the four bytes
of kernel_offsets' tail padding carry the multicast tuning (attempts,
arm_sequence, arm_hold). sizeof and every offset are unchanged - host probe on
both trees: kernel_offsets 448, TargetProfile 712, misc@248 geometry@288
execution@328, KernelMisc 40, tail pad 4.

Also bind the ancillary write adapter to the session global. Passing its session
parameter through the AncillaryZeroFn function pointer stopped LTO from proving
the argument constant at the new call site, which changed attack_write's
register allocation (126 -> 130 instructions, one extra callee-saved register).
With the adapter bound to g_exploit_session every call site passes the same
constant again and do_one_write is IDENTICAL (strict, 126).

cmp_disasm, baseline deff0b1 (md5 27879fa7) vs this branch (md5 02f2be01),
branch tool plus the origin/main two-level tool:
- owner_thread, do_one_write (all three middleware instantiations): IDENTICAL
  (strict).
- waiter_thread / consumer_thread / run_main_route_threads: differences are
  adrp page encodings and rodata string offsets only; all 102 differing
  immediates resolve to the same member of the same object or the same string
  literal (0 exceptions).
- do_kernel5_fake_lock_route: 174 -> 222, the intended multicast re-poison
  loop; the poison/walk body and the prepare/recycle order are unchanged.
- multicast_owner_worker / multicast_waiter_worker: not instantiated in this
  branch's route (the tool reports them missing on both sides, as recorded in
  kernel-phys-offset-plan.md).
Full record, including the device gate and its open items:
docs/analysis/vr-guard-plan.md.
resolveMerged seeded the merge base with the tuning-execution map instance
itself, and deepMergeValues writes into the maps it is handed. The first
builtin that overrides an execution value (6.1.145-android14-11-maybe-dirty,
heap.prepare_max_attempts = 12) therefore wrote that value into the shared
preset, and the exporter — which merges every builtin in one process —
handed it to every following profile until one set its own value.
ExporterAgreementTest compares the exported set against the app's own
documents and caught the drift.

Copy the preset when seeding the base; each merge stays isolated.
route.multicast_waiter.{attempts,arm_sequence,arm_hold} are in the native
field table (the route reads them for the poison/walk repetition) but the
Kotlin mirror never wrote them, so the knob was unreachable from the app and
the exporter. Mirror them like the tcp route's tuning, with the native field
widths: written only when the profile provides them, absent or 0 keeps the
compiled route default, and every existing profile stays byte-identical.

Test vectors extend both the round-trip fixture and the route-section test.
ProfileMigrationEquivalenceTest requires the fixture to carry every current
6.x builtin, and the fixture-size tripwire moves from 52 to 53. The new entry
carries the release's real values and declares its multicast route: the
remote/main era could not express that route, and the nested-route form is
passed through the converter untouched, so the legacy import resolves to the
same document as the builtin.
The branch-level PR note (head/base, scale, changes, the two fixes the new
builtin surfaced, verification with fresh results, reviewer items, open
risks) plus the plan doc's control-flow diagrams, the refreshed verification
matrix and the commit references after the reword.
Cold-boot, lock-screen, single-route (multicast_waiter) run of the final
candidate binary (build/native/ghostlock, sha256 7d85c26b...) with the built-in
6.1.145-android14-11-maybe-dirty profile on the V2307A/PD2307 device:

  vr guard: neutralizing __tracepoint_sys_exit.funcs -> probe disabled
  child is root! -> handoff -> KernelSU ready; su -c id = uid=0(root)

The plan doc's pending items are closed and its gate section now records the
run, the per-attempt statistics (everything that failed is archived too, all
route-stage panics — the KERNEL-PANIC-01 class) and the observation that the
low-noise window right after boot was much better than the 240s+ window.
origin/main moved past this branch's base (kernel_phys_offset, MTK physical
addresses, per-route field universe with explicit nulls). The note records how
this PR's presence semantics map onto that rule when the branch lands on main,
so the reviewer can decide whether to align it now.
…tion

The three poison/walk knobs belong to the multicast route branch, so the
editor treats them like its other fields: a route switch seeds them as
placeholders, the field labels map them, and values outside the native widths
(u8/u8/u16) are reported instead of being wrapped by the typed cast.

Tests: switching to multicast seeds the tuning fields; 256 attempts is
surfaced as invalid while the documented recipe values (128/16/20000) pass.
…ates

The schema's multicast section, the 5.x reference template and the bundled
5.15 template now carry attempts / arm_sequence / arm_hold (8/8/16 bits,
absent or 0 keeps the compiled default), and the PR note records the
editor/validation follow-up.
The per-cluster pairing drops the last core of an odd-sized cluster and can
never produce a pair that straddles two frequency groups — which is exactly
the pair measured as stable on the iQOO 12 kernel line (main=4, consumer=5).
Add it explicitly, guarded on both cores reporting a frequency, and make it
the default; every other pair stays one tap away.
The note now answers "which approaches did this use" directly: the branch's own
guide and skeleton, the in-tree per-task tag clear, the kernel-side
funcs==NULL iterator behavior, the reference kit's measured parameters on this
kernel line, and BTF-derived layout. It also compares against abdulla-li's
YuKongA#141 and YuKongA#220/YuKongA#221 (base main, version-hardcoded layout, no device evidence) so
the reviewer can see what this PR adds rather than duplicating it.
The note's head/scale line predated the last two commits; it now states the
built-from C++ revision and the pushed scale, and the plan doc's checklist
records the opened PR (YuKongA#228).
Cite the diffstat only; the commit count changes with every metadata commit
and is visible on the PR page anyway.
Review finding: the post-loop status accepted `setsockopt(...) == 0` as success.
A return code only means the copy landed — the warm-up attempts never arm the
consumer at all — so the route could report ROUTE_OK without any verified
write and let the caller skip its retry on an unverified target. Judge the
result by the consumer's success signal alone, matching select_stack, and keep
the setsockopt return in the log line.
Review findings (two):
- the transport field is a u8, so a BTF offset at or above 0x100 would be
  narrowed onto a different tracepoint member instead of refusing the profile;
  the conf output now emits the layout only for 1..=0xFF and otherwise omits
  the whole section, which keeps the guard fail-closed.
- a kernel whose BTF has no `struct tracepoint` made every non-conf format
  fail, because only `struct_slab_cache` was exempt from the required-field
  check; `vr_tracepoint_funcs` joins it.
Review finding: the multicast tuning keys narrow to UByte/UShort before any
validation runs, and the exporter (which calls validateMerged, not the app
controller) could silently wrap attempts = 256 to 0. The shared validation now
rejects out-of-range route tuning and vr_guard layouts for every caller, with
tests.
Review finding: promoting a hard-coded (4, 5) to index 0 on every device whose
cores 4 and 5 report a frequency silently changed the default for unrelated
devices. The suggestion now comes from the profile, exactly like
recommend_shizuku: BuiltinProfileCatalog carries each entry's
execution.recommended_cpus, UserProfileStore exposes it for imported
documents, and the repository offers it (adding the pair when the per-cluster
pairing cannot express it) while an explicit user choice always wins.
Fixes the five review findings; the note lists each response and the plan doc
records them with the refreshed disassembly count (do_kernel5 174 -> 224).
Asserts the review's scenario directly: an image whose BTF has no
struct tracepoint passes require_fields with the optional set (every format
still extracts) and fails without it.
Cold-boot, lock-screen, single-route run of the post-review binary (sha256
9cfd7c83...) on the V2307A/PD2307 device: first attempt PASS, su = root,
KernelSU ready, SELinux back to enforcing. The new criterion is visible in the
log (sockopt=-1 with success=1, so status=0 rests on the consumer's verified
write alone). Plan doc and PR note updated with the record.
Same-window alternating rounds of the pre-fix and post-fix binaries: 2/10 vs
2/10 passes, 2 discordant pairs (1:1), exact p = 1.00 — the stricter route
criterion does not cost success rate. The same pre-fix binary scored 70% in
the afternoon and 20% at night, so the metric is dominated by device fatigue
after repeated panic cycles and cross-session comparisons are invalid.
…omment

The note now opens with the three things the reviewer has to settle (the
multicast commit's scope, whether to align with main's newer profile
conventions now, and two API/type choices) instead of burying them at the end.
The built-in profile's CPU pair comment described the pre-review behaviour
("selected_cpus would be overwritten"); it now matches what the app does —
offer the pair until the user picks one.
ancillary: implement the VrGuardPolicy behavior (vr.ko sys_exit neutralization)
Please do further test in dev branch, and upload pre-release to gather testers
NickJi2019 and others added 4 commits October 2, 2026 08:28
# Conflicts:
#	AGENTS.md
#	app/src/main/kotlin/com/ghostlock/app/data/AndroidProfileConfigController.kt
#	app/src/main/kotlin/com/ghostlock/app/ui/FieldLabels.kt
#	profile-core/src/main/kotlin/com/ghostlock/app/data/profile/ProfileResolver.kt
#	src/Makefile
… (PD2463/iQOO15 verified)

- Implemented vr.ko probe neutralization in CFI stage
- Redirects sys_exit tracepoint probes to no-op probestub
- Verified tracepoint constants on PD2463 and iQOO15 devices
- Data-only writes, idempotent, KCFI compatible
- Includes tracepoint constant extraction guide for vivo devices

Verified on:
- PD2463 (vivo iQOO Neo10 Pro+, SM8750, kernel 6.6.89-android15-8)
- iQOO15 (vivo iQOO 15, SM8750, kernel 6.6.89-android15-8)

Note: Host tests and disasm validation passed; real-device functional test pending.
feat: 添加 vivo vr.ko 反 Root 检测中和模块 (基于 CFI 重定向)
@linux-tools linux-tools linked an issue Oct 5, 2026 that may be closed by this pull request
@NickJi2019

Copy link
Copy Markdown
Collaborator

vr.ko bypass is going to be move to plugins system in future, now is deleting from main

@NickJi2019 NickJi2019 closed this Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

incorrect vr.ko bypass on vivo

4 participants