ghostlock-app

Introduction: GhostLock One-Tap Execution App (CVE-2026-43499)
More: Author   ReportBugs   
Tags:

中文: README_ZH.md

Documentation

  • Kernel Profile Porting Guide - add support for a new kernel. GhostLock matches kernels by exact uname -r and rejects unsupported builds, showing the status at the top. Built-in profiles live in app/src/main/assets/kernel_profiles/: one HOCON file per release, index.conf as the runtime index, and <major.minor>-template.conf version-family templates.
  • Supported devices - the built-in kernel list.
  • Shared execution defaults - every execution-tuning field, its default, and why.
  • Profile schema - full profile structure and data flow.
  • Adding a component - developer guide for a new native middleware / backend / frontend (Chinese).

For the complete device-porting workflow, kernel-family template links, and tuning rationale, see the Kernel Profile Porting Guide.

Rows explicitly marked Shizuku required run through a shell UserService. Start Shizuku with ADB and tap the status card to grant access; all other rows use the app's normal execution path.

Quick Start

Open GhostLock and tap Run. KernelSU (me.weishu.kernelsu), ReSukiSU (com.resukisu.resukisu), or KowSU (com.kowx712.supermanager) provides ksud for module loading; without it, W1/W2 still grant uid 0 but no module is loaded.

The execution chain is a pipeline of three components: a frontend (root_child startup/handoff), a backend (the CVE-2026-43499 futex primitive), and a middleware route. The catalogued combinations are instantiated at build time; the resolved profile selects which one runs. The route races two cores: on the 6.6/6.12 tree-waiter kernels the main thread hammers select while a consumer thread perturbs the waiter's priority; on the 6.1 compact-waiter kernels it drives getsockopt(TCP_ZEROCOPY_RECEIVE) through a punched-hole page; the 5.15 kernels use the multicast waiter. The CPU pair also comes from the resolved profile.

Command-Line Debugging

adb/shell has no seccomp filter, so W3 is skipped - handy for quick verification:

make -C src ghostlock
./gradlew exportKernelProfiles
adb push build/native/ghostlock /data/local/tmp/ghostlock
adb push build/kernel-profiles/<release>.bin /data/local/tmp/profile.bin
adb shell chmod 755 /data/local/tmp/ghostlock
adb shell /data/local/tmp/ghostlock --load-prebuilt-profile /data/local/tmp/profile.bin

Offset Extraction

tools/extract_rs derives offsets from a boot.img (plus optional xbl_config.img), a full OTA ZIP, or an http(s) URL pointing at one. kallsyms come from --kallsyms or are recovered from the image's embedded table. pselect_waiter_shift and off_slide_loggers_0_1 are derived by the built-in arm64 disassembler. MediaTek images have no xbl_config.img and usually no BTF: the physical load address is derived from kallsyms _text (override with --phys).

cargo build --release --manifest-path tools/extract_rs/Cargo.toml
build/extract/release/ghostlock-extract.exe boot.img --xbl-config xbl_config.img --format conf --out profile.conf
build/extract/release/ghostlock-extract.exe OTA.zip --format conf --out profile.conf

--format conf is the extractor output: a flattened, self-contained profile (no include lines, the shared 6.x credential/KernelSnitch constants inlined, the route selected from --analysis evidence unless --route overrides it). The extractor emits every field the image actually yields and omits the rest; it never fills gaps from a neighbouring kernel family's guesses (unverified-family 6.6, the default -2, the 5.15 multicast constants, or a phys default). Every output is an unverified candidate: importable and parseable, with missing or invalid fields blocked by the app's pre-execution validation, so a successful run never implies device support. On 5.x it also derives the credential reference repair from init_cred and the multicast geometry from BTF (see docs/analysis/extractor-5x-derivation-plan.md). --format json stays for the v1 import path. To add a built-in profile, complete and validate the matching version-family template, save it as a standalone .conf profile, and add it to kernel_profiles/index.conf. The old C offsets.h registry is deprecated and removed.

Preflight

The extractor disassembles remove_waiter() before extracting offsets. Kernels with the fix are rejected with exit code 6; only vulnerable kernels continue.

On-device analysis

A full OTA can be analyzed entirely on the phone: boot plus xbl_config are extracted automatically. Pass --work-dir an app-writable dir when running inside the app sandbox. Cross-compile and push:

rustup target add aarch64-linux-android
$ndk = "$env:ANDROID_HOME\ndk\<version>\toolchains\llvm\prebuilt\windows-x86_64\bin"
$env:CC_aarch64_linux_android = "$ndk\aarch64-linux-android35-clang.cmd"
$env:AR_aarch64_linux_android = "$ndk\llvm-ar.exe"
$env:CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER = $env:CC_aarch64_linux_android
cargo build --release --target aarch64-linux-android --manifest-path tools/extract_rs/Cargo.toml
adb push build/extract/aarch64-linux-android/release/ghostlock-extract /data/local/tmp/
adb shell /data/local/tmp/ghostlock-extract /sdcard/OTA.zip

Importing offsets without rebuilding the app

New kernels no longer need an app rebuild: tap Import offsets.conf (HOCON) and pick the extractor's flattened .conf, or use Import offsets.json (v1) for an older JSON report. v1 JSON is converted in-app, so nothing has to be pushed to the device: native always starts from the GLK1 document the app sends on stdin, and matches the current uname -r against the resolved profile before rejecting the kernel. Imports merge across files; a release already stored prompts before overwrite.

The app can also generate the profile itself — Parse OTA link (full OTA ZIP URL) and Parse image (boot.img + optional xbl_config.img) run the extractor in-process and write a flattened .conf into the app data dir on success:

# GhostLock kernel profile: 6.12.38-android16-5-g844001fb8721-ab14552068-4k (HOCON, self-contained).
release = "6.12.38-android16-5-g844001fb8721-ab14552068-4k"
schema_version = 1
kernel_major = 6
recommend_shizuku = 0
kernel_phys_load = 0xC7800000
route {
  select_stack {
    waiter_shift = 0
  }
}
fallback {
  to = "none"
}
kernelsnitch {
  collisions = 4
}
task_struct {
  prio = 148
  cred = 2304
}
cred {
  caps_offset = 48
  copy_size = 136
  usage_value = 1
  caps_count = 5
  caps_value = -1
}
offset {
  init_task = 37801728
  init_cred = 37891184
}

Credits & License

Based on the following projects, licensed under Apache License 2.0 (see LICENSE):

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools
AI Daily Digest