Absorbs four public reverse-engineering write-ups into the skill, and completes
the xvmp IR-virtualizer work started in the hardware pass. 20 files, +1151/-151.
What it adds:
* references/native-vm-recovery.md -- the recovery procedure for a native VM:
VM-existence signals, separating stacked CFF/VM dispatchers, register
anchoring with triple verification, nested-VM segment stacks, STORE-backward
operand slicing with byte provenance, and the clean-re-VM validation loop.
Absorbed, so every step is labelled inferred in the file itself.
* pitfalls P43-P46, including the Go stack-ABI one (a watchpoint hit whose
argument is not in x0-x7 because the frame holds it) and the observer whose
crash silently disarms every observer installed after it.
* vm_handler_slice.py: a nested-dispatch diagnostic (a block repeating 4+ times
inside one segment is a second dispatcher running under the first), plus the
fix for a latent bug this pass's own review found -- diagnostics() had been
measuring len() of the (run, closed) tuples since the open-tail change, which
is always 2, quietly disabling the spin-loop note on the real code path.
* evidence for a completed workbench toolchain and a measured end-to-end run,
recorded in EXTENSION-sampling-crash.md SS5-SS6 and EXTENSION-absorption-kx.md.
Verified on the merged tree, not on the PR branch alone:
* check_repo.py, check_refs.py, check_commands.py, check_routing.py and
check_budget.py pass with zero problems; flake8 and mypy are clean;
* pytest: 772 passed, 172 skipped, 25 xfailed;
* the code change was reproduced independently of the PR's own tests:
nested_loop_segments() flags the repeat-inside-one-segment shape in both the
(run, closed) and the legacy bare-run form, and diagnostics() now reports
avg_segment_len over the run -- 5.0 on a fixture whose tuples are length 2,
where the pre-fix code reported 2.0.
One defect fixed during the merge: the PR's own record says the local copies of
the absorbed write-ups under `.ref/` are "git-ignored in effect", but .gitignore
had no such rule -- it rested on nobody having run `git add -A` yet. The rule is
now explicit, with the reason stated beside it, the same way this file handles
tools/ and the user's own input material.
PR #3 proposed adding mypy and flake8 to CI, with configurations for both. The idea is right and the
configurations are kept. Three things about the PR itself needed correcting, and all three were found
by running the gate rather than reading the diff:
1. It was written against an older tree. Its own edits reference code that no longer exists there
(`unknown, positions, warnings = ...`, a `struct` import at line 857 that is used 600 lines
later), so merging it as-is would have removed a live import. The fixes were re-derived against
the current tree instead, and the PR is superseded.
2. It added a `paths:` filter to the workflow, watching only `*.py` plus the two lint configs. That
would stop `check_refs.py` and `check_routing.py` -- documentation gates -- from running on a
documentation-only commit, which is exactly the change they exist to catch. The workflow keeps
the PR's `actions/*@v7` bump and gains no filter; the reason is written next to the trigger.
3. `pip install --upgrade pip pytest` was dropped to just the packages, because upgrading pip in CI
is not a goal and can fail for reasons unrelated to this repository.
What the gate found on the current tree, and what was done about it -- 70 flake8 findings and 3 mypy
errors, none of which were cosmetic-only:
F401 (23) unused imports, removed. These were safe to delete because the import statement itself
cannot have a side effect that matters here.
F841 (12) unused bindings, handled three ways rather than one. Dead code was deleted
(`mt_mcp_probe.print_waiting_help`'s `host`, `repack.py`'s `signer_jar`); a computed
value that was then forgotten was **wired up** (`rasc_build.py` now names the linker
target in its build-failure hint instead of discarding it); and a binding that is
intentional kept its place with `# noqa: F841` and the reason -- `java2c_probe` reads
dex header fields one by one against the format spec, and deleting three of them to
please a linter would have made the parser stop lining up with the format it implements.
E741 (14) the ambiguous loop name `l`, renamed with an AST pass that resolves each binding to its
own scope. A regex rename would have been wrong: several scopes bind their own `l`.
That pass had a defect of its own -- it treated a comprehension target as living in the
comprehension scope while leaving references to it in the enclosing one -- which
produced seven `undefined name 'l'`. Found by running flake8, fixed, and the counting
bug is documented in `check_routing.py`, whose own `tables()` had the same class of
error in the other direction.
style continuation indents, blank lines between top-level definitions, and lines over length.
The line limit is 160, not 120, and the number is a **measurement**: this repository's
secret-scanner rule table holds raw regex source of 148 and 156 characters, which cannot
be split without changing the pattern.
mypy (3) two missing annotations on module-level containers, and one optional-dependency rebind
(`dexutil = None` after `except ImportError`) that is the repository's own idiom and
now says so with a `type: ignore[assignment]` and the reason. No code was reordered to
satisfy the checker.
Verified on a venv containing only `mypy` and `flake8` -- the poorest environment the configuration
must work in, and the one CI uses: **flake8 reports 0, mypy reports "Success: no issues found in 55
source files"**. Behaviour is unchanged: 625 passed / 163 skipped / 25 xfailed, the same five gates
green (check_repo 0 problem, check_refs 0 dangling, check_routing coherent, check_budget 0 failure,
check_commands 0 drift), and every key script still prints help.
.flake8 new, with the line-length and complexity numbers and their reasons
mypy.ini new; optional imports declared, nothing excluded from the analysis
ci.yml `actions/*@v7`; new `lint` job on one interpreter; no path filter
30 files the findings above
Closes the six coverage gaps named in an external review of this skill, and moves the
deliverable-form decision out of the single assumption it rested on.
New references (7):
- lsposed-and-modules.md deliver a hook module when a repack is refused
- advanced-unpacking.md extraction shells, FART-style recovery, the root-side dump
- emulation-and-rpc.md call the routine instead of reading it
- native-dbi-and-deobfuscation.md OLLVM, Stalker traces, two measured boundaries
- protocol-reverse.md protobuf/gRPC/QUIC and native-side pinning
- kernel-and-environment-hardening.md when userspace hooking cannot reach the check
- on-device-tooling.md MT Manager, its APK MCP, working from the phone
New scripts (8): dex_dump_validate.py (+ --trim for page-aligned captures),
dex_mem_scan.py, lsposed_scaffold.py, frida_rpc_serve.py + rpc_template.js,
stalker_trace.js + stalker_report.py, mt_mcp_probe.py
SKILL.md:
- G1 carries a four-form deliverable table (rebuilt APK / module / RPC service / report)
with the evidence that says when to leave the repack column, plus a matching hand-off
section on how "verified" moves with the form
- Coverage gains a documented-routes tier carrying its own strength note; Not-covered
loses the now-inaccurate "general unpacker" line and gains the limits that do apply
- 13 new symptom rows, 7 reference rows, 8 script rows, a fifth tooling obligation
- the pid-drift symptom is stated with its competing causes, not a single attribution
toolchain.md gains measured ASC/ddc cards; README gains the structure, capability and
scope entries for everything above; .gitignore stops tracking the local workspace.
Evidence: docs/tool-verification/EXTENSION-*.md (7 files), same three labels as the rest
of the record. Measured: the gradle-free module build chain (5.27 s end to end), a
successful injection confirmed through the LSPosed file channel (9 rounds) after three
logcat queries came back empty on a ROM with a broken logd, 17 dex images recovered by
root-side /proc/<pid>/mem dumping, the trivial-body metric separating a shell skeleton
from real bodies, ASC/ddc on a hardened APK, and Stalker's zero-event and crash
boundaries. The pid-drift attribution was corrected twice against better measurements.
The negation rule pointed at scripts/dexpatch/, which no longer exists at that path. Also ignore the scratch directory build_scripts.py audits into and the agent skill directories that installing this repo into itself would create.