Adds docs/tool-verification/fixtures/verify_absorbed_claims.py: seven device-free checks covering the mask identity, the PKCS#7 length law, zero-fill versus skipped offsets, the hidden-salt search, compression drift, Python percent-encoding, and AES CTR/CBC/ECB separation. The AES run falsified an absorbed claim: zero prefix-XOR does not imply a stream cipher (CBC and ECB show it too). The discriminator is a one-byte flip -- 1 changed byte for CTR, all following blocks for CBC, one block for ECB -- and protocol-reverse section 7 now states that, with the capability row recording the correction. Labels are now per claim rather than per section: observed where a host command produced output, inferred for rules derived from the mechanisms, unverified for anything needing a device, a JDK, a lz4 binding or a VM sample. native-vm-recovery's claim-strength paragraph is aligned with the repository definition, and the cipher row moves to inferred. Gates and pytest green.
Tool verification record
This directory is the evidence record for the apk-reverse skill measured against real
targets, kept separate from the skill itself so the Coverage statement in SKILL.md can be
checked against the runs behind it, and so a later reader can tell which claims were observed,
which were inferred, and which are still unverified.
It is not a set of target-specific notes kept for reuse, and it deliberately carries no target identity. The measurements are about the tools and the skill; they stay valid whichever APK produced them. Where a concrete figure was needed to make a point (a class count, a file size, a build time) the figure is kept and the target is named generically.
Targets measured: Flutter AOT applications, multi-dex, mid-size native library sets, no packer.
The APKs themselves are not in this repository (.gitignore excludes *.apk, and tools/).
Workbench paths are an operation record, not a pointer. A record may say a run happened at
tools/_work/... so the command can be judged; .gitignore excludes tools/, so that path is not
something a reader can open. Anything a reader is meant to follow is named under
docs/tool-verification/ or inside the skill — that rule is why a reference document never cites a
tools/ path as its evidence, even when the work happened there.
Strength labels
Every claim carries one of three labels, used the same way references/verification.md uses them:
- observed — reproduced here, with the exact command and its output.
- inferred — follows from observed facts, but the step itself was not executed.
- unverified — assumed or reported by a tool and not independently confirmed.
Index
| File | Covers |
|---|---|
TOOL-VERDICTS.md |
One verdict per tested script and external toolchain, with the independent cross-check behind it |
FINDINGS.md |
Findings about the skill itself: defects, contradictions, boundary evidence |
REPO-DECISIONS.md |
What changed in the repo as a result, and what deliberately did not |
Extension-pass record
The extension pass (the six coverage gaps named in the external review: module ecosystem, extraction shells and VMP, emulation/RPC, native DBI, kernel-level environment, protocol layers) keeps its evidence here, one file per topic, in the same three labels:
| File | Covers |
|---|---|
EXTENSION-device-run.md |
The on-device run against a hardened Flutter sample: the two control runs, the frida refusals, the twice-corrected pid-drift attribution, the failed-dump artefacts checked with dex_dump_validate.py, and a shared-device hazard measured with Stalker |
EXTENSION-unpacking.md |
Extraction-shell diagnosis by trivial-body ratio, dex_dump_validate.py against a fixture derived from the sample's own shell dex, and the tool's own defect found during the run |
EXTENSION-rootdump.md |
The root-side /proc/<pid>/maps + mem route that does not use frida: 17 real dex images recovered, the shell skeleton they are not, four device-shell traps, and the negative result on anonymous/ART-heap scanning |
EXTENSION-lsposed.md |
Module route: the gradle-free build chain with timings, three build traps, LSPosed config-DB anatomy, the module that could never have worked, and the root-hiding configuration the device actually has |
EXTENSION-emulation-rpc.md |
ASC/ddc measured on a hardened APK, Frida-RPC exercised end to end on a live device, and the unidbg build repair |
EXTENSION-native-dbi.md |
Stalker: the zero-event boundary, the crash from following a hot libc export, and an offline protobuf round-trip that caught a defect in its own decoder |
EXTENSION-kernel-ondevice.md |
The MT Manager APK MCP probe, the module-environment facts it could verify, and the kernel routes that this device's 4.14 kernel puts out of reach |
Benchmark-pass record
The benchmark pass put public targets under the documented routes — tests/benchmark.md is the
matrix, and these files are the evidence behind its rows. Same three labels. Several of these rows
came back negative, and four of them overturned a conclusion this repository had already recorded;
those are called out in the matrix rather than buried here.
| File | Covers |
|---|---|
EXTENSION-benchmark-l1-l3.md |
The first two matrix rows: the L1 equal-length patch -> repack -> re-sign chain with its on-screen behaviour change and its zero-change control, and the L3 time-to-death baseline, the two spawn/patch/detach orderings that cannot work, and the terminate path that patching the obvious death site does not stop |
EXTENSION-extraction-shell-bench.md |
dex_dump_validate.py against an 11-variant skeleton set: the trivial-body ratio as a bimodal result rather than a threshold, the three script defects the variants exposed, and the ranking that pointed at a modified image until independent review caught it |
EXTENSION-stalker-exclude.md |
Stalker exclusion measured across three arms: the no-follow baseline, the follow that kills the process, and the follow with 20 modules excluded that survives but still delivers zero events |
EXTENSION-java2c.md |
Java2C against JNI sinking: native-declaration density roughly 2000x apart, why a Java_* symbol search fails silently, and the Dex-to-C build chain no toolchain on this host could complete |
EXTENSION-vmp-diff.md |
The known-plaintext differential: a 218-of-224-opcode coverage fixture, a closed-loop check that recovered 218/218 mappings with zero fabrications, and the one link that cannot be automated |
EXTENSION-kernel-weapons.md |
The generated KernelSU/APatch scaffold and its three kernel-side templates, all shipped unbuilt, plus the structural correction that a userspace module cannot change a syscall return value |
EXTENSION-split-apk.md |
Two real split sets through both branches: unified re-signing, the merge that is refused by design, the merge that succeeds, and the signer chain this host actually needs |
EXTENSION-reconstruction.md |
The reconstruction pass: the doctor capability closure (a JDK-only PATH used to report "can re-sign", now BLOCKED with a next action), the zip rebuild that stops dropping per-entry metadata (zipalign -c -v 4 fails the old output and passes the new), and check_commands.py catching a documented flag that does not exist |
EXTENSION-test-harness.md |
The executable half of the evidence: what tests/ asserts and what it measured, the dex_patch_bytes regression reproduced byte-for-byte, the 153 skips and 26 xfails that form the exit-code work order, the device-bound exclusion forced by an incident where the suite drove a real phone, and CI written but never run |
EXTENSION-rasc.md |
The Rust ASC measured against the Python one: identical class-definition sets on two archives, the speedup per scenario, the build (no prebuilt artifact exists, and a 32-bit MinGW cannot link it), and the enum shape whose method bodies it drops with no warning |
EXTENSION-protobuf-raw.md |
A schema-free decoder cross-checked against the official runtime and a real DataStore container, with the packed-boundary and proto3-zero ambiguities reproduced on real bytes |
Absorption-pass record
A later pass absorbed mechanics from external skill repositories (reverse-skill,
awesome-game-security, android-reverse-engineering-claude-skill, MobileRE-Skill), keeping each
project's own discipline: only the phase, the checklist and the command pattern cross over, with the
source URL and the access date recorded, and nothing vendored as a runtime dependency. These files
live here rather than in the skill because two of them corrected a claim the repository had already
written down.
| File | Covers |
|---|---|
EXTENSION-desensitization.md |
The leak scanner's rules and exemptions, its four RESULT= states and exit codes, the escaping false positives, and the pass that found its own evidence file leaking 26 strong hits — the fixture's literal match values, quoted to prove the rules fire |
EXTENSION-dart-aot-formats.md |
Two string-table claims tested against a real dual-ABI Flutter app: the arm64 packed scheme confirmed against literal tag bytes, and the assumed armv7 len*2/UTF-16 form refuted — the 32-bit record is [header u32][byte-count u32le][UTF-8], and the extractor's zero is a format mismatch rather than an empty table |
EXTENSION-detection-pipeline.md |
Naming the check that fires: three arms on a public MASTG target (control alive, probe arm dead in ≈300 ms via strstr("frida"), with the TracerPid=0 versus 4-frida-mappings asymmetry), svc_scan.py's two decoders agreeing on an identical 214-site set, the layered Java-to-svc descent, and the ptrace-free dump boundary on targets whose dex is deflated inside the APK |
EXTENSION-dexsql.md |
The SQL query layer adopted as a third indexer beside rasc/droidasc/ddc: identical class-definition sets on four archives (zero differences either way), the string xref agreement and the = versus LIKE match-semantics trap, decode against ddc disasm line for line, and the persistent cache measured as not a speed switch |
EXTENSION-ref-absorption-2026-10.md |
Sixteen public write-ups in four groups (Unity/IL2CPP metadata protection, linker-level SO packing, VM protector idioms, Pyarmor bytecode), the four reference files they became, the registration surface that changed, and the one cheap experiment that would move each row off unverified |
Hardware-layer pass record
The hardware pass (hardware watchpoints, ETE differential tracing, kernel-side event injection) follows the same shape: what runs on a host is measured, and everything that needs a device or a kernel toolchain is an explicitly-labelled template whose gate says where it closes.
| File | Covers |
|---|---|
EXTENSION-hw-watchpoints.md |
The ptrace hardware-watchpoint route: encoders accepted and refused exactly as the alignment rules demand, and the emitted on-device helper compiled clean as a static aarch64 ELF — while no watchpoint was armed on any device, and the ledger that says the ptrace route leaves TracerPid visible |
EXTENSION-ete-diff.md |
The differential half measured on synthetic flattened-dispatcher fixtures (first divergence, unique-per-side sets), the gate/capture plumbing measured to the no-device boundary, and what was deliberately not done: no perf-with-CORESIGHT build, no cs_etm stream, no overhead claim |
EXTENSION-kernel-instrument.md |
The kernel-side scaffold's generate/verify round trip, the spec-refusal rules shared with hw_watch.py, the capability map whose no cells do the gatekeeping, and the three-time-fenced experimental register-rewrite action |
EXTENSION-sampling-crash.md |
The second hardware pass: trace_addr2sym.py verified against a real cross-compiled .so (readelf-cross-checked), the interpreter-trace slicer measured on the calibrated xvmp shape (including the 4x-margin bug it found in itself), and the flattening-recipe corrections read from the two public obfuscator implementations the user supplied |
EXTENSION-absorption-kx.md |
The absorption pass over four public write-ups: the native-VM-recovery procedure (every step inferred), pitfalls P43–P46, the slicer's nested-loop diagnostic (with the latent len(tuple) bug this pass's review caught), and what deliberately did not cross over |
Adding a run
Record the tools, versions and exact commands, and label each claim. Keep target-specific facts out: what belongs here is what a future reader can apply to a different APK — a defect, a measured cost, a method that did or did not work, and the cross-check that settled it.