diff --git a/skills/apk-reverse/references/advanced-unpacking.md b/skills/apk-reverse/references/advanced-unpacking.md index 59d471a..4af8ffe 100644 --- a/skills/apk-reverse/references/advanced-unpacking.md +++ b/skills/apk-reverse/references/advanced-unpacking.md @@ -525,8 +525,10 @@ Three rules that keep the descent honest: happens to be. **The kernel layer is not reachable on this device, and the gate is a kernel version.** -eBPF tracing (`stackplz`, kprobes, `seccomp` filters pushed from userspace) requires -kernel **5.10+**; the measured device is **4.14.186**, so that entire layer is closed +eBPF tracing (`stackplz`, kprobes) requires +kernel **5.10+**; the measured device is **4.14.186**, so that layer is closed (seccomp is the +one syscall-layer mechanism *not* closed there — a filter was observed active on this very device; +see `kernel-and-environment-hardening.md` §4) here regardless of tooling. `kernel-and-environment-hardening.md` maps the route and its version gate; do not spend a session on the layer before checking `adb shell uname -r`. The consequence for this file: the descent above stops at the `svc` boundary, and a check diff --git a/skills/apk-reverse/references/detection-and-anti-analysis.md b/skills/apk-reverse/references/detection-and-anti-analysis.md index bdad00d..fc41fd1 100644 --- a/skills/apk-reverse/references/detection-and-anti-analysis.md +++ b/skills/apk-reverse/references/detection-and-anti-analysis.md @@ -60,7 +60,11 @@ nothing at delivery time. **C. Accept and report.** If the detection blocks the *deliverable* rather than your analysis — for example, the app verifies something the user's device will not have — then the honest output is a statement of what is blocked, with the evidence. Do not ship a build whose only purpose is to defeat a -detector you were not asked about. +detector you were not asked about. One named threat pattern fits here: **Snowblind (2024)** — an added +native library installs a seccomp filter (`SECCOMP_RET_TRAP` on `open`-family) plus a `SIGSYS` +handler that fakes the results, so a *runtime* observation contradicts a *static* integrity check +(recognition strings in `third-party-builds.md`'s marker table; the technique itself is not reproduced +here). **One interactive escape hatch exists before B:** a human-driven gdbserver session (`references/toolchain.md`, the GDB form in the Tier-2 row) — the AI names the commands, the user runs them and pastes the output. @@ -68,6 +72,17 @@ It survives *frida-specific* detection (the Stage-1 measured mappings) while sti detection (`TracerPid`) — so it discriminates the two families before you decide the check is unbeatable. Observer-only: the session must not also modify state (Step 2B). +**The seccomp discriminator** — the fourth cause the environment table does not name: *the +process's own filter denied the syscall my agent just issued.* `Seccomp: 2` in +`/proc//status` is the **normal** state of every zygote-forked app — its presence is not +hostility. The discriminator is a death whose tombstone reads `signal 31 (SIGSYS), code 1 +(SYS_SECCOMP)` **naming the syscall your instrumentation called** (or an `EPERM`/`ENOSYS` on +exactly that syscall), vanishing in the instrumentation-free control run. `Seccomp_filters: >1` +means a *target-installed* extra filter — see `kernel-and-environment-hardening.md` §4 for the +mechanism and the decision set; observe denials via `dmesg` seccomp-audit lines and +`logcat -s seccomp SIGSYS`. *(unverified — absorbed from milksu.org notes, retrieved 2026-10-19; +the probe already captures the `Seccomp:` field, this gives it its reading)* + **Signal to stop escalating:** you have spent more effort on the analysis environment than on the change the user asked for. That is the drift this skill treats as most expensive, in its runtime form (`long-task-discipline.md` §the most expensive drift). diff --git a/skills/apk-reverse/references/kernel-and-environment-hardening.md b/skills/apk-reverse/references/kernel-and-environment-hardening.md index d87fc2b..536094d 100644 --- a/skills/apk-reverse/references/kernel-and-environment-hardening.md +++ b/skills/apk-reverse/references/kernel-and-environment-hardening.md @@ -146,7 +146,7 @@ uname -r # on the device. The gate for most of this section is 5.10+ | Route | What it gives you | Prereqs | Strength | |---|---|---|---| | **eBPF kprobes/uprobes** | Fire a BPF program on kernel or userspace function entry; observe syscall arguments/results system-wide, invisible to the target process | GKI 5.10+ kernel with the BTF/tracing config; root; toolchain (bpftrace or a custom loader) | `inferred` — documented upstream, not run here | -| **seccomp-BPF filter** | Per-process syscall allow/deny — can make a syscall **fail**, and seccomp can be installed by the app on itself (no root needed for self-filtering) | Any modern kernel; but a filter you install into the target requires ptrace/zygisk injection first | `inferred`. **Key limitation**: seccomp can reject or error a syscall (SECCOMP_RET_ERRNO/TRAP); it cannot rewrite the *content* of what a successful read returns. It closes doors; it does not paint them. | +| **seccomp-BPF filter** | Per-process syscall allow/deny — decision set ALLOW / ERRNO / TRAP / **TRACE** (hands the syscall to a tracer) / **KILL_PROCESS** (the action that impersonates a detector: app just dies) | Any modern kernel; but a filter you install into the target requires ptrace/zygisk injection first | `inferred`. **Key limitation**: seccomp can reject or error a syscall (SECCOMP_RET_ERRNO/TRAP); it cannot rewrite the *content* of what a successful read returns. It closes doors; it does not paint them. Platform timing *(unverified — network: cnb.cool mirror carries no system/seccomp or bionic repo; URLs to check when possible: `…/platform/system/seccomp/-/git/raw/main/bpf/app.cc`, `…/platform/bionic/-/git/raw/main/libc/bionic/seccomp.cpp`)*: the app-process filter is installed by bionic at libc init — *before any app code runs* — so an in-process agent always operates under it; but a filter evaluates the **installing process's own** syscalls, so the platform filter cannot deny an *external* tracer's ptrace — "they seccomp-blocked my attach" is structurally wrong; what it blocks is your in-process agent's syscalls. `Seccomp: 2` in `/proc//status` is the normal state of every zygote-forked app; `Seccomp_filters: >1` is the signal of a *target-installed* extra filter. | | **Kernel module hooking** (KPM on APatch; out-of-tree LKM elsewhere) | Inline hooks and syscall-table hooks in kernel space — the layer that *can* rewrite what a `/proc` read returns | APatch (KPM) or a self-built kernel/LKM load path; kernel-dev skills | `inferred`. This is genuine kernel development — out of scope for this skill () | | **Zygisk injection** (not kernel, but below the target's defences) | Run code in the target's process from zygote fork, before `.init_array` of the app's own libs | Magisk/KernelSU+ZygiskNext + a Zygisk module; no ptrace involved | `measured` as a framework (LSPosed runs this way on the reference device); a purpose-built module for a given target is `inferred` here | @@ -269,7 +269,12 @@ honest outcome (`observed` on that device): The generalisable rule: **enumerate the ladder for *your* device once, write the one-line environment facts, and stop re-deriving them mid-task.** A closed rung is a route decision, not a -failure. +failure. Three one-line gate reads belong in that enumeration, because they decide whether +kernel-side observation is open before anyone plans it *(inferred — absorbed from milksu.org notes, +retrieved 2026-10-19)*: `/proc/sys/kernel/kptr_restrict` (0 = kallsyms addresses readable), +`/proc/sys/kernel/dmesg_restrict` (0 = dmesg readable without root), and +`/proc/sys/kernel/perf_event_paranoid` (≤1 = userspace perf counters) — with zeroed kallsyms +addresses as the visible symptom of the first. ## 7. When to stop escalating — the decision table diff --git a/skills/apk-reverse/references/system-ipc-audit.md b/skills/apk-reverse/references/system-ipc-audit.md index 6650114..47dfb91 100644 --- a/skills/apk-reverse/references/system-ipc-audit.md +++ b/skills/apk-reverse/references/system-ipc-audit.md @@ -224,7 +224,18 @@ binary, or generate the exact allow rule with `adb shell dmesg | audit2allow -p pulling the policy from `/sys/fs/selinux/policy`, and audit what it emits before applying it (`audit2allow` is deliberately over-broad). For *reading* what an app domain may do, `sesearch -A -s untrusted_app sepolicy.bin` answers "can this app even reach X" questions that -save a hook that could never fire. +save a hook that could never fire. For the measured frida-server execution denial specifically, +the live one-line fix is `magiskpolicy --live "allow file execute"` — or run the +binary in a different domain altogether. + +The same policy file answers the *explain-the-target* half — why the app reaches something you +thought was closed: query wildcard grants (`sesearch -A -s -t ` — an `allow … *` +row like `system_app vendor_data_file:file *` is the one-line explanation for "why can this +preinstalled app read vendor data"), attribute memberships (`seinfo -t -x`), and missing +`type_transition` rules. A policy read explains the behaviour without touching the running +system — and that is the boundary: reading the target's policy to explain it is in scope; +injecting rules or flipping enforcement to defeat it is root-framework development, not this +skill. *(unverified — absorbed from milksu.org notes, retrieved 2026-10-19)* ## 5. Keystore attestation — the check that survives client-side patching diff --git a/skills/apk-reverse/references/third-party-builds.md b/skills/apk-reverse/references/third-party-builds.md index a4d2ab8..10cd36b 100644 --- a/skills/apk-reverse/references/third-party-builds.md +++ b/skills/apk-reverse/references/third-party-builds.md @@ -50,7 +50,7 @@ re-protected an already-protected app. Look for independent, coexisting families |---|---| | Commercial packer | shell `Application`/`appComponentFactory` in the manifest, a `lib*protect*.so`, encrypted assets, `assets/*.jar` with near-maximal entropy | | DEX-to-native (dex2c) | a "stub" `lib*.so` containing the tool's name, `EntryPoint`/`protect`-style symbols, and **business class names as strings** alongside a near-empty dex layer | -| Anti-debugging / RASP | strings such as `TracerPid`, `ptrace`, `NoNewPrivs`, `CheckPtraceSelf`, `AntiFrida`, plus a self-protection log path | +| Anti-debugging / RASP | strings such as `TracerPid`, `ptrace`, `NoNewPrivs`, `CheckPtraceSelf`, `AntiFrida`, plus a self-protection log path; **Snowblind-shape**: a `PR_SET_SECCOMP`/`SECCOMP_MODE_FILTER` call together with `sigaction(SIGSYS)` — a filter+handler pair that fakes syscall results (see `detection-and-anti-analysis.md` Step 2C) | | Generic hardening marker files | small text files under `assets/` naming a protection vendor and a protection timestamp | The practical consequence of an anti-debugging layer is that Frida-based dynamic work will hit diff --git a/skills/apk-reverse/references/toolchain.md b/skills/apk-reverse/references/toolchain.md index 01f9ca9..0c98740 100644 --- a/skills/apk-reverse/references/toolchain.md +++ b/skills/apk-reverse/references/toolchain.md @@ -306,6 +306,7 @@ deadline reported nothing about the target, only about your deadline. | `capstone` (python) | library | decoding in your own scripts — see the silent-stop trap below | | **`svc_scan.py`** | `python scripts/svc_scan.py libfoo.so [--context 3]` | **names the syscall behind an inline `svc`** and reports which PT_LOAD segment each hit is in. Use it *before* planning a libc-level hook: a module with its own `exit`/`kill` `svc` sites is not observable through libc, and one without them means a missing event is a finding about your hook. Read the neighbours (`--context`) — a byte scan for `svc` also matches inside data. Measured on the test device: device `libc.so` = 4 termination sites (its own exports), a shell library's 21 hits = all data | | `keystone` (python) | library | assembling a short patch when you are editing bytes by hand | +| `seccomp-tools` | `gem install seccomp-tools`; `seccomp-tools disasm ./filter.bpf`, `emu ./filter.bpf` | offline disassembly/emulation of a captured seccomp BPF filter — the reading tool when a target-installed filter is the blocker (`detection-and-anti-analysis.md` §the seccomp discriminator). `dump --pid` catches the filter at install time by attaching to the install syscall — its on-device Android behaviour is unproven here. *(unverified — tool URL not reachable this session; absorbed from milksu.org notes, 2026-10-19)* | | `pyelftools` (python) | library | **section-based — unreliable on hardened targets.** Prefer hand-walking `PT_LOAD`/`PT_DYNAMIC`, as `scripts/elf_plt.py` does | | IDA Pro | GUI, **or headless via `idalib-mcp`** | the strongest decompiler output. Do not assume it needs a human in front of it — a headless MCP server exists (see *MCP tool servers* below). Ask for a human only when that setup is genuinely unavailable | | `x64dbg` | GUI, Windows | human-driven live debugging of a native Windows target | diff --git a/skills/apk-reverse/scripts/svc_scan.py b/skills/apk-reverse/scripts/svc_scan.py index 9676fef..ce800e0 100644 --- a/skills/apk-reverse/scripts/svc_scan.py +++ b/skills/apk-reverse/scripts/svc_scan.py @@ -68,7 +68,7 @@ ARM64_SYSCALLS = { 173: "getppid", 174: "getuid", 175: "geteuid", 178: "gettid", 214: "brk", 215: "munmap", 216: "mremap", 220: "clone", 221: "execve", 222: "mmap", 226: "mprotect", 260: "wait4", 273: "set_robust_list", 276: "renameat", - 278: "getrandom", 280: "utimensat", 291: "statx", + 277: "seccomp", 278: "getrandom", 280: "utimensat", 291: "statx", } # The three groups that decide whether a userspace strategy is even applicable.