Files
apk-reverse/docs/verification-jiongnew/VERIFY-OUTPUT.md
T
inliver233 90f7a58615 Record the selective-install measurement and the external checks
The brief asked for the shared-tooling question to be settled by the install product
rather than by preference. Measured with --skill apk-reverse -y --copy in an empty
directory:

  the skill is copied whole and self-contained into each detected agent directory
  (six here), every file under <agent>/skills/apk-reverse/ -- SKILL.md, all 28
  references, all 34 scripts including dexpatch/. Nothing outside the skill directory
  is installed, and there is no cross-skill sharing mechanism.

So a root-level shared/ can never work: it is not part of the product. With one skill
there is nothing to share, and had the skill been split the halves would have needed
duplicated scripts or a path that does not survive installation.

VERIFY-OUTPUT.md gains a section recording which premises were re-checked against
primary sources and which did not hold: the blutter build budget and compiler floor,
aotopsy as the no-toolchain route, the idalib-mcp shift away from the GUI plugin, and
that "GNU readelf is on PATH" is false here (it is pyelftools' readelf.py, and MinGW
objdump/nm reject aarch64 outright). Also records one intermittent check_repo.py exit 1
that printed "0 problem(s)" and did not reproduce in five later runs, with the line
numbers of the encoding path that produces it.
2026-09-20 05:01:48 +08:00

14 KiB
Raw Blame History

Verification output

The four repository gates, run against the tree after the changes described in REPO-DECISIONS.md. The text below is the captured output, not a restatement of it — a table saying "passed" is exactly the kind of claim this repository asks people not to make.

Environment: Python 3.14.0 on Windows 11, npx 11.6.2, repository at commit 46812ec plus the working-tree changes listed in REPO-DECISIONS.md.

# Command Exit Result
1 python -B check_repo.py 0 0 problems
2 python -B check_refs.py 0 0 dangling
3 python -B build_scripts.py . <audit-out> 0 0 findings
4 npx skills add newliver666/apk-reverse --list 0 Found 1 skill

1. python -B check_repo.py

Every skill discovered, frontmatter valid, each script runnable, documented paths resolve, README paths explicit and existing.

Exit code: 0. Output (47 lines):

== skills discovered ==
  skills/apk-reverse

== frontmatter ==
  1 checked

== apk-reverse: python syntax + --help ==
  apk_diff.py                      ok
  blob_decode.py                   ok
  coldstart.py                     ok
  dart_disasm.py                   ok
  dart_pool_strings.py             ok
  dart_pprefs.py                   ok
  datastore_inject.py              ok
  devsh.py                         usage-ok rc=2
  dex_check_verifier.py            ok
  dex_classdiff.py                 ok
  dex_find_insn.py                 ok
  dex_patch_bytes.py               ok
  dex_strings.py                   ok
  dex_strpatch.py                  ok
  dexutil.py                       usage-ok rc=2
  doctor.py                        ok
  elf_plt.py                       ok
  find_refs.py                     ok
  grab_crash.py                    ok
  install_test.py                  ok
  lib_map.py                       ok
  native_crash.py                  ok
  patch_smali.py                   usage-ok rc=2
  preflight.py                     ok
  probe_api.py                     ok
  repack.py                        ok
  run_probe.py                     ok
  sig_probe.py                     ok
  smtool.py                        usage-ok rc=2
  snap.py                          ok
  so_constpatch.py                 ok
  tls_check.py                     ok
  usb_net_proxy.py                 ok

== apk-reverse: reference and script paths resolve ==
  all referenced reference/script paths exist

== README paths are explicit and exist ==

== result: 0 problem(s) ==

2. python -B check_refs.py

Every cross-reference naming a section of another document must reach a real heading there.

Exit code: 0. Output (36 lines):

== markdown documents ==
  README.md
  skills/apk-reverse/SKILL.md
  skills/apk-reverse/references/account-gates.md
  skills/apk-reverse/references/ad-removal.md
  skills/apk-reverse/references/byte-level-patching.md
  skills/apk-reverse/references/code-virtualization-and-custom-linkers.md
  skills/apk-reverse/references/dart-aot.md
  skills/apk-reverse/references/detection-and-anti-analysis.md
  skills/apk-reverse/references/dex-patching.md
  skills/apk-reverse/references/dynamic-frida.md
  skills/apk-reverse/references/environment.md
  skills/apk-reverse/references/framework-runtimes.md
  skills/apk-reverse/references/long-task-discipline.md
  skills/apk-reverse/references/membership-and-limits.md
  skills/apk-reverse/references/native-and-so.md
  skills/apk-reverse/references/native-tamper-and-suicide.md
  skills/apk-reverse/references/packers.md
  skills/apk-reverse/references/patch-audit.md
  skills/apk-reverse/references/pitfalls.md
  skills/apk-reverse/references/recon.md
  skills/apk-reverse/references/repack-and-sign.md
  skills/apk-reverse/references/runtime-data.md
  skills/apk-reverse/references/server-api.md
  skills/apk-reverse/references/server-config-and-updates.md
  skills/apk-reverse/references/signature-derived-keys.md
  skills/apk-reverse/references/third-party-builds.md
  skills/apk-reverse/references/tls-and-cert.md
  skills/apk-reverse/references/toolchain.md
  skills/apk-reverse/references/updates-and-forced-upgrade.md
  skills/apk-reverse/references/verification.md
  skills/apk-reverse/scripts/dexpatch/README.md

== section references: 166 reachable ==

== result: 0 dangling, 0 warning(s) ==

3. python -B build_scripts.py .

Audit for machine-specific leftovers: absolute paths and credential literals.

Exit code: 0. Output (2 lines):

== audit: 77 file(s) checked, 0 finding(s). Nothing was written.
   re-run with --apply to copy, and with --scrub to neutralise absolute-path literals.

4. npx skills add newliver666/apk-reverse --list

The skills CLI must still discover the skill after the changes.

Exit code: 0. Output (28 lines):


            
           
           
           
  
  

   skills 

  Tip: use the --yes (-y) and --global (-g) flags to install without prompts.
[?25l
  Source: https://github.com/newliver666/apk-reverse.git
[?25h[?25l

[?25h[?25l
  Found 1 skill
[?25h

  Available Skills

    apk-reverse

      Reverse engineer, debloat, de-ad, patch, or re-sign Android APKs, and analyze their runtime and server-side behavior. Use when a task involves an .apk/.aab/.dex/.so sample, smali or dex patching, Frida/objection runtime hooking, repacking and re-signing, removing ads or SDK trackers, probing a mobile app's HTTP API, or deciding whether a client-side patch is even capable of achieving the goal. Covers recon, anti-tamper, ad removal, membership/paywall limits, dex-level surgical patching, repack pitfalls, device and emulator setup, and a hard-won failure catalogue. Load the body before planning any patch work: it opens with a symptom index and four gates that must be cleared first.


  Use --skill <name> to install specific skills


Two notes about running these gates

Both were found by experiment during this pass and are worth knowing before trusting a green run.

check_repo.py and check_refs.py have no argparse. Passing --help does not print usage — it is silently ignored and the full check runs, producing byte-identical output to a normal invocation. A "low-impact probe" assumption about these two is wrong.

Do not set PYTHONIOENCODING=utf-8 when running check_repo.py. Doing so produced a spurious exit 1 with one script (dex_strpatch.py) reported as crashed. Root cause is at check_repo.py:152-154: subprocess.run(..., text=True) does not pin an encoding, so the parent decodes the child's UTF-8 output as cp936, the reader thread raises UnicodeDecodeError, both streams come back empty, helped (line 158) goes false and the run is recorded as FAIL (lines 164-165). Clearing the variable and re-running restores exit 0. The same lines also carry a diagnostic inconsistency: verdict checks only returncode while ok additionally requires helped, so a run with both streams empty and returncode 0 prints ok yet is recorded as crashed in the results block.

This is also why check_refs.py failed once during this pass and now passes: it resolves cross-references within the skill only and has no fallback outside it, so a sentence in SKILL.md pointing at a path outside the skill directory read as dangling. It was right to fail; the reference was rewritten rather than the checker.

One intermittent exit 1 from check_repo.py was observed and not reproduced. During this pass check_repo.py once returned 1 while printing result: 0 problem(s) and 47 lines of all-ok script status — and five subsequent runs (to a file, to $null, and through a pipe) all returned 0 with the same 47 lines. The mechanism is the encoding path documented above: at check_repo.py:153 the child is run with text=True and no fixed encoding, and at :158-159 helped requires at least one of the two streams to be non-empty while verdict reads only returncode, so a decode that consumes both streams turns a healthy script into a recorded FAIL. Recorded rather than dismissed because a gate that can fail without a cause is worse than one that fails for a stated reason; if you see an isolated red run whose text says 0 problem(s), re-run before investigating the repository.


5. External checks — what was verified against primary sources

Not a command output: a record of the facts that were re-checked before they were relied on, so a later reader can tell which of this pass's premises came from a live source and which did not. Kept here because several of them changed a decision.

Claim that was checked Result Source
npx skills command surface and flags Confirmed in use here: add <package>, --list/-l, --skill/-s, --copy, -y, --full-depth, use, list, find, update, init, remove, experimental_sync local npx --yes skills --help; justjavac/skills README
Skill discovery rules and required frontmatter Confirmed: containers walked one level for skills/<name>/SKILL.md, one extra level for skills/<category>/<name>/SKILL.md; a shallower SKILL.md shadows anything nested; name and description required; metadata.internal hides a skill same README (§Skill Discovery, §Creating Skills), and agentskills.io specification
skills/<name>/ is the layout the CLI resolves Confirmed by the repair commit already in history (Restructure to the skills/<name>/ layout the skills CLI resolves) and by --list finding 1 skill both before and after this pass live npx skills add newliver666/apk-reverse --list
idalib-mcp exists, is the recommended headless route, and its prerequisites Confirmed, with one update worth recording: the IDA GUI plugin is no longer recommended and will be deprecated in favour of idalib-mcp. Prerequisites as stated: Python 3.11+, IDA Pro 8.3+ (9 recommended), IDA Free unsupported, and idalib activated globally. Headless usage: uv run idalib-mcp --host 127.0.0.1 --port 8745 <binary> or --stdio. Workers are persistent, adopt existing sessions, and self-exit on idle TTL mrexodia/ida-pro-mcp README
GhidraMCP is a GUI extension + Python bridge, not headless Confirmed. Install is "Import the plugin into Ghidra"; the bridge is bridge_mcp_ghidra.py talking to http://127.0.0.1:8080/. Unattended use therefore means analyzeHeadless plus a post-script, not this bridge LaurieWired/GhidraMCP README
The IDA MCP author's warning about LLMs on obfuscated code Confirmed verbatim in the README: LLMs will not perform well on obfuscated code, and string encryption / import hashing / control-flow flattening / code encryption / anti-decompilation tricks should be removed first. Not applied in this pass — the target is not an OLLVM sample, so citing it here would have been decoration same README (§Tips for Enhancing LLM Accuracy)
blutter's supported scope and build model Confirmed: Android libapp.so, arm64 only; auto-detects the Dart version from the engine and builds the matching Dart VM; needs a Haskell-free C++ toolchain (g++>=13 / clang>=16 / MSVC); bin/ holds per-version executables named blutter_dartvm<ver>_<os>_<arch>; output includes asm/, objs.txt, pp.txt, blutter_frida.js. Its own TODO still lists obfuscated apps as incompletely supported worawit/blutter README
No prebuilt blutter binary is published Confirmed by measurement: the repo has zero GitHub releases and its bin/ is gitignored. Building is mandatory — and, measured here, costs ≈78 s, not "tens of minutes" local run; blutter README
aotopsy's scope, accuracy claims and hard limits Confirmed from its README: pure Go, static, no Dart VM and no SDK compile; ARM64 + x86_64; Dart 2.10–3.13; claims 90.2% name-recovery agreement and 0% fabrication. Its stated hard AOT floors match what this pass saw: instance field names ~97–99% absent, local/captured names gone, truly polymorphic dispatch not statically resolvable BroNils/aotopsy README
Which Flutter SDK ships Dart 3.6.0 Not confirmed from an authoritative table. The engine banner (3.6.0 (stable) (Thu Dec 5 07:46:24 2024 -0800)) and the snapshot hash are decisive for our purposes, and no 3.6.2 string exists in either .so. The Flutter SDK version number was deliberately not asserted anywhere in the repository output, because the release-notes page that would have settled it was not reachable in a form that could be quoted banner + byte search, both local and observed

Two checks changed what the repository says. First, the blutter build budget and compiler floor were both wrong in the documentation and are now corrected from measurement. Second, aotopsy turned out to be the no-toolchain route, which is why dart-aot.md now names a front end as a dependency instead of implying the object pool decodes itself.

One premise from the original brief did not survive: "GNU readelf is on PATH" is false on this host. readelf resolves to pyelftools' own readelf.py, whose CLI rejects --dyn-syms and -W as unrecognized arguments, and MinGW objdump/nm (binutils 2.28) reject every aarch64 ELF with File format not recognized. All aarch64 cross-checks in this pass therefore went through the pyelftools / lief / capstone library APIs.