Skip to main content

apk-reverse

Reverse engineer, debloat, de-ad, patch, or re-sign Android APKs, and analyze their runtime and server-side behaviour. Use for an .apk/.aab/.dex/.so sample, smali or dex patching, Frida hooking, repacking, ad or SDK removal, API probing, or deciding whether a client-side patch can work. Covers recon, anti-tamper, membership limits, surgical dex patching, repack pitfalls, device setup, and a failure catalogue.

ソース情報

リポジトリ
newliver666/apk-reverse
ソースの最終更新活動
2026年9月22日 12:57
検出された SKILL.md の言語
英語
スター
3,452
フォーク
710

apk-reverse: analyze and validate Android APK changes

Investigate Android application behavior, locate the relevant code layer and work through APK patching, repacking and verification. apk-reverse provides task routing, tool checks and failure records for authorized Android analysis.

Uses

Use it with APK, AAB, DEX or native library samples. The workflow distinguishes an installable result from a workaround that depends on root, instrumentation or a host proxy.

Prerequisites

Scripts use Python 3.9+. Device work needs ADB; dynamic analysis needs a matching Frida server; re-signing needs zipalign and apksigner. Dependencies vary by route, and doctor.py reports which capabilities are available.

How to use

Provide the sample and state the required result in a testable sentence. Start with the documented capability check:

python skills/apk-reverse/scripts/doctor.py --json

Classify the runtime and owning layer, locate the relevant code, and record a baseline and control build before changing one variable. Verify the exact changed feature on the resulting installed build rather than treating successful assembly as completion.

Limitations

The repository is for learning, research and authorized testing. Coverage is Android-specific; it excludes iOS, Unity/IL2CPP, React Native/Hermes/Cordova and defeating server-side authority. Some documented routes remain inferred or unverified, and Flutter/Dart AOT analysis requires an external snapshot-decoding tool.

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
100 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
apk-reverse
description
Reverse engineer, debloat, de-ad, patch, or re-sign Android APKs, and analyze their runtime and server-side behaviour. Use for an .apk/.aab/.dex/.so sample, smali or dex patching, Frida hooking, repacking, ad or SDK removal, API probing, or deciding whether a client-side patch can work. Covers recon, anti-tamper, membership limits, surgical dex patching, repack pitfalls, device setup, and a failure catalogue.
license
MIT — see LICENSE at the repository root
compatibility
Python 3.9+. Device work needs adb; dynamic analysis a matching frida-server; re-signing zipalign plus apksigner, not just a JVM. Static dex/ELF work and the leak scanner are offline. Run doctor.py --json for status.
metadata
{"version":"1.0","capability_registry":"skills/apk-reverse/scripts/capabilities.py","evidence_summary":"skills/apk-reverse/references/evidence-summary.md","last_reconstruction_pass":"2026-09-22","strength_labels":"observed | inferred | unverified"}
# APK Reverse Engineering & Patching Goal: reach a **verified, installable, still-working artifact** fast — and avoid the whole class of mistakes that destroy an APK while looking completely healthy. ## Immediate — do these four things before anything else This file is a **procedure with gates**, not background reading, and it is long enough that its middle gets skimmed. So the four moves come first; everything below is the explanation for them. **1. Classify before choosing a route.** Answer the thirteen questions in §Start here. **R4** decides whether you are editing the right layer at all, and a wrong branch does not fail loudly — it produces an artifact that builds, runs, and does the wrong thing. **2. Clear the four gates, in order, with their pass criteria** (§Gates). G1 names the deliverable form in one testable sentence *before* any work; G2 establishes what this machine can really do; G3 locates the code; G4 records the baseline and the control. "I understand the idea" is not clearing a gate. **3. A symptom you cannot explain is a stop signal, not a puzzle.** Search §Symptom index for the shape **before your next attempt** and load the file that row names. Those rows exist because each one cost hours — and in several cases the answer was already written down while it was being re-derived. **4. Two strikes on one shape of attempt sends you back to classification**, not to a third variant (§Stop conditions). And **never report done without the six items in §What "done" means.** **Two rules about this file itself.** When a failure does not fit your plan, the symptom index is the next action, not more reasoning. And when this file and your own reasoning disagree, **this file wins** until you have evidence that overrides it: every rule here is the residue of a failure that cost hours, and your current intuition is the intuition of someone who has not hit it yet. ## Four rules that override everything else **R1 — Write the deliverable as a testable sentence before you touch the target.** "It works" is not the goal; "it works under the stated constraint" is. Root-assisted, live- instrumentation, host-proxy and patched-device results frequently do **not** satisfy a request for an installable artifact that works on a normal phone — and it is easy to present such a result as finished. Write the sentence, re-read it at every checkpoint, and if you cannot meet it, say so plainly and label the privileged workaround a **fallback**, never the deliverable. → `references/long-task-discipline.md` §the most expensive drift **R2 — Change one variable at a time, and keep a control build.** An experiment that flips two things teaches nothing when it fails, and a failure you cannot attribute will be attributed to the wrong cause. Every "the app rejects X" claim needs its own run, and every patch needs a same-pipeline control that still fails the old way. → `references/long-task-discipline.md` §single-variable discipline **R3 — Never ship or claim an unverified artifact.** "It assembles" is not "it works"; "the process started" is not "the feature works"; "no error in the log" is not "the check is gone". Install it, launch it, exercise the exact feature you changed, and look at the screen. Prove the device is running the build you made — hash it, do not trust the filename. → `references/verification.md` **R4 — Identify the owning layer before patching, and re-classify when reality disagrees.** Ads, paywalls, feature gates, integrity checks and update gates live in different layers (Java, dex, native, Dart/Unity, server). Patching the wrong layer either does nothing or breaks the app. If a patch "had no effect", the diagnosis was wrong — go back to classification instead of patching harder. → `references/recon.md`, then the layer-specific file the symptom index points at ## Tooling — reach for the right instrument, and check before declaring it absent Most wasted rounds in this domain are not bad reasoning about the target. They are **the right question asked of a tool too weak to answer it**: an hour of `grep` over a hand-exported smali tree where one indexed query would do, a manual ELF walk where a decompiler was one `pip install` away. The failure is invisible from the inside, because the weak route still produces output. **Five obligations. These are instructions; a violation is a defect, not a preference.** | Obligation | Do this | Cost of ignoring it | |---|---|---| | **Orient with an indexer, not an export** | Build the ability to *ask the artifact questions* before reading code: `droidasc findrefs` / `ddc findrefs` (string/type/method → every reference site, sub-second). A decompile reads a class you have **already located** — it is not how you locate it. `jadx` is a readable viewer of last resort, never the source of truth and never the entry point of a recon | hours of `grep` per question asked, over an export you paid for first | | **Install the missing tool; it is part of the task** | A missing arm64 decompiler is the next step, not a constraint to route around. Ask a human only when installation is genuinely impossible | hours re-derived by hand for output a decompiler gives in minutes | | **Name the gap before spending against it** | State the capability the blocker requires and whether this machine has it, **out loud**. This is a G2 item | designing around a tool that installs in ten minutes | | **Reach for a shipped script before writing one** | Parsing, hashing, alignment and hot-plug probes are already written; a bespoke script in place of `scripts/dex_find_insn.py` is how offsets get **guessed instead of computed** | a wrong offset that decodes cleanly and behaves wrongly | | **Pick the instrument for the layer the question lives on** | A dex in memory wants `dex_mem_scan.py` (find and cut) **plus** `dex_dump_validate.py` (judge), not a decompiler pointed at a fragment. An algorithm you only have to *call* wants `emulation-and-rpc.md`. "Does this class ever load at runtime" wants a hook that fires or does not | the heavier tool is not the safer one — it produces a *plausible* answer, which is worse than none | **Run `python skills/apk-reverse/scripts/doctor.py --json` before concluding anything is unavailable.** It reports per-capability closure — what is missing, what to install, and roughly how long that takes — and a capability you have not checked for is not a capability you lack. **It is deliberately allowed to report `BLOCKED`:** a machine with a JVM but no `zipalign`/`apksigner` cannot re-sign, and the report says so rather than inferring capability from a tool that is merely present. Detail: `references/toolchain.md`; the registry it reads is `scripts/capabilities.py`. ## Coverage — what this skill claims, and what it does not The failure this section prevents is not ignorance. It is **a confident wrong answer produced by applying the nearest available procedure to a target it was never written for.** A documented method that almost fits is more dangerous than no method at all, because it arrives with a plan, a vocabulary and a set of reassuring numbers. **How strong these claims are — this is load-bearing, not cosmetic.** Every claim in this skill carries one of three labels, and you must label your own results the same way: | Label | Means | May it justify a decision? | |---|---|---| | `observed` | a command was run and its output exists | yes | | `inferred` | follows from an observation, but the step is reasoned | provisionally | | `unverified` | assumed, or reported elsewhere and never reproduced here | only as something to test | **The inventory that replaces a guess:** the per-route list of what is covered and what is not — by verified mechanism versus by documented-but-mostly-inferred route — is in `references/coverage-and-limits.md`. Load it when the question is "can this skill actually do X". **Partial coverage that is easy to over-read** — these are documented routes whose *dependency* this skill does not ship, and each has a fence around it in the same file: Dart AOT analysis needs a snapshot dump you must obtain elsewhere and naming the front end matters · extraction-shell recovery stops at the VMP boundary · a Stalker trace is not guaranteed on every device · a kernel-side answer requires a module this skill only scaffolds. **Not covered — say so rather than improvise:** Unity / IL2CPP · React Native / Hermes / Cordova · iOS · defeating a server-side authority · an off-the-shelf unpacker or an anti-detection arms race · building and shipping a kernel module. Reasons and evidence boundaries are in `references/coverage-and-limits.md`; **silence in that record is not support.** **The fallback, as an instruction — this is a rule, not advice.** If the target does not match the covered list, or no symptom-index row matches, then **stop and classify before choosing a branch**: answer the thirteen questions (§Start here). If the shape still does not fit — an unknown runtime, a mechanism you cannot name — **say exactly that and propose the cheapest experiment that would identify it.** Do not take the closest documented route and apply it anyway. A wrong branch here does not fail loudly: it produces an artifact that builds, runs, and does the wrong thing. ## Hand-off points — where this skill ends and another view begins Four boundaries that are easy to walk into without noticing. Each names what the other side owns rather than restating it, because two copies of the same advice drift apart. The JNI form table and the per-form verification table live in `references/handoff-boundaries.md`. 1. **JNI** — a Java `native` declaration and its implementation are two views of one function, and this skill reads each with a different tool. A symbol search fails **silently** on dynamic registration. Authoritative treatment: `java2c-and-jni-sinking.md` §The JNI boundary — why a symbol search fails silently. 2. **Hardening** — a dex-side packer observation is a native-side implementation question. `packers.md` owns the dex-side identification; the deep dive belongs on the other side. 3. **Native anomalies** — read them *from the APK side*, because the judgement is whether your patch caused the death. Restoring a symbol or reversing an OLLVM function is a different activity with a different toolchain: point across instead of extending `native-and-so.md` into it. 4. **Deliverable form** — when the artifact stops being an APK, the verification question changes with it. G1's other three forms each move the evidence somewhere else, and the failure is quiet: a privileged result reported in the language of a finished build. ## Symptom index — a matching row is a stop signal You arrive at a symptom, not at a file name. Each row below is a failure that has already been paid for. **If any row matches what you are observing, load the file before your next command** — not after your next three attempts. Reasoning from first principles at this point is how the same hours get spent twice; more than one entry here is a lesson that was re-derived by hand while the answer sat unread in this repository. | What you observe | Load first | |---|---| | A repackaged/re-signed build **dies before your code runs**; `SIGSEGV`, all registers zero, `pc=0`, `fault addr` near `0x0` | `native-tamper-and-suicide.md` (deliberate crash), then `code-virtualization-and-custom-linkers.md` | | **No packer** (Application is the app's own, dex readable) **and it still dies** | `code-virtualization-and-custom-linkers.md` §a loader is still a possibility; but if the same build also dies on a *second, unrelated* device you are looking at an ordinary startup fault, not a hardened one | | The app dies at startup on **every** device, packed or not, with **no tombstone** while `crash_dump` says `already traced` and logcat says `exited cleanly (0)` | a bundled crash reporter has taken the signal handlers, so the platform's own trail is gone. Frida spawn-gating is the recovery route | | A `FORTIFY: pthread_mutex_lock called on a destroyed mutex` abort in a Flutter app, on the **main** thread, before the first frame completes | `dart-aot.md` — check `libapp.so` is actually being loaded; Flutter's engine bootstrap is the usual place a native lifecycle fault surfaces | | Log says a **Java-layer** signature/integrity check **passed**, yet the process dies | `code-virtualization-and-custom-linkers.md` §a Java-layer "signature killer" is a decoy | | Deleting a library fixes validation but yields `UnsatisfiedLinkError: dlopen failed: library "X" not found` | `code-virtualization-and-custom-linkers.md` §the deadlock that eats hours | | Whole classes appear as bare `native` declarations with no body | `java2c-and-jni-sinking.md` — read it **before** dumping memory: if this is Java2C there is no DEX to find, at any point in the process lifetime. A handful of `native` methods in an otherwise ordinary dex is JNI sinking, not this | | A `Java_*` search over a hardened library returns nothing at all | `java2c-and-jni-sinking.md` §The JNI boundary — why a symbol search fails silently — dynamic registration, or `-fvisibility=hidden`. The check that works is "exports `JNI_OnLoad` and zero `Java_*`" | | You are about to publish an evidence file, a transcript or a README that quotes real work | `references/desensitization-and-leak-scans.md` — run `scripts/scan_leaks.py` **before** it is committed; the hit list is a set of lines to look at, and `--show-exempt` is where the wrong suppressions show | | A hooking module appears to have run but its log tag is silent, and you are about to record "it never loaded" | `references/precedents/logd-broken-module-never-ran-case-3.md` — a broken `logd` delivers nothing on `logcat` while the module's whole run sits in LSPosed's file log; read both channels | | A library's **SONAME does not match its filename** | `code-virtualization-and-custom-linkers.md`, `native-and-so.md` | | Your edit had **no effect at all**, with no error | `server-config-and-updates.md` §3 (the value may be server-sent), then `packers.md` §map the validation boundary | | Process **hangs** with no crash record, or dies to a `uid 0` killer | `native-tamper-and-suicide.md` §the rule (you probably made a terminate path *not return*) | | Death looks like an ordinary null dereference in a hardened library | `native-tamper-and-suicide.md` §deliberate-crash stubs | | The app dies **only while you are attached/rooted** | `detection-and-anti-analysis.md`; run the unmodified original under identical conditions first | | **Install fails with `[-124]` and mentions `resources.arsc` / alignment** | `repack-and-sign.md` §2a — STORED **and** 4-byte aligned, both required | | **Install fails with a bare numeric code (e.g. `[-99]`) and no `INSTALL_FAILED_*`** | `repack-and-sign.md` §vendor install interception — a device-side interceptor, not your build. Use the root `pm install` path | | **After an install, `am start` does nothing / screenshots show another app / `am start -W` hangs** | `repack-and-sign.md` §the installer may still own the screen | | Log shows `Failure to verify dex file ...: Bad checksum` and a startup `ClassNotFoundException` for an ordinary class | `byte-level-patching.md` §the dex header has two integrity fields — order matters | | An install "succeeded" but nothing changed, or the version did not move | `long-task-discipline.md` §keep the observation window clean | | Evidence contradicts itself, or a capture looks like two states mixed | `long-task-discipline.md` §keep the observation window clean | | You took screenshots but drew the conclusion from logs or from the patch itself | `long-task-discipline.md` §captures you never looked at are not evidence | | You are about to re-run an experiment whose result you already recorded | `long-task-discipline.md` §long-context decay | | A script will not start, or a tool "is missing" | `scripts/doctor.py`, then `toolchain.md` §"not on PATH" is not "not installed" | | A hook or probe reports **no events at all**, and you are about to call it detection | `scripts/anti_detect_probe.js` for the environment self-report first, then `detection-and-anti-analysis.md` §Step 3: locating the check — the order of search from Stage 0 | | `attach` hangs and then fails **while the process is still in `ps`** | `detection-and-anti-analysis.md` §Step 3 Stage 0 — check for `D` in `/proc/<pid>/stat`, and attach a *different* pid as a one-line control before blaming the target | | The app exits with no tombstone, no crash and no ANR record | `detection-and-anti-analysis.md` §Step 3 — a clean self-exit means the check ran before your hooks existed; the branch conditions there say which Stage | | A dump region validates as the wrong thing, or an `r--s` view of `base.apk` looks like a dex | `advanced-unpacking.md` §What this route cannot do, and how to tell before you spend the window | | Feature-scoped network failure (login/register/pay) while the rest works | `tls-and-cert.md` — do not assume your patch caused it | | Everything works but **every signed request fails** after repack | `signature-derived-keys.md` | | A re-signed build **runs fine, renders its whole UI and logs no error — but one feature silently never loads**, and `dumpsys`/DNS/logcat show **no request for it at all** (not a rejected request: *no request*) | `code-virtualization-and-custom-linkers.md` §what the native check actually reads — refusing **before** the request is built. Not the row above: "sent and rejected" and "never sent" have different owners | | You cannot tell whether a missing feature is **your patch's fault or the target's own behaviour** | `long-task-discipline.md` §single-variable discipline. Run the **zero-change control through the same pipeline**, and the decisive variant: the unmodified original with the patch applied **in memory only**, same device, same network | | Under Frida `spawn`, the UI never appears — `mCurrentFocus` stays `null`, screenshots come back blank, the Activity stack never builds | `dynamic-frida.md` §spawn keeps the Activity stack down: write the patch into memory, **detach**, then start the Activity normally | | `frida-server` keeps disappearing mid-experiment, or the device reboots itself while you are working | `dynamic-frida.md` §when the ROM hunts your instrumentation | | Ads still appear after a patch that should have killed them | `server-config-and-updates.md` §6 (cached config / remote re-enable), then `ad-removal.md` §step 4 (count the SDK's own log lines; n -> 0, not "I did not see it") | | A forced-update or "must update" gate blocks the build | `updates-and-forced-upgrade.md` §step 6 | | The dialog is gone but the feature is still locked | `membership-and-limits.md` / `account-gates.md` — decide server vs client authority before patching again | | You are about to discard a route as "blocked" | `packers.md` — re-read it before writing any route off; mis-attributed failures have removed viable routes for hours | | The task has run long and you are unsure what is already proven | `long-task-discipline.md` §keep a live record | | A dumped dex parses in full, the classes are all there, and most method bodies are `return-void` stubs or nop fills | `references/advanced-unpacking.md` — an extraction shell: measure the `stub%` with `scripts/dex_dump_validate.py` before trusting any of it, and know that recovering the bodies is a different route | | Your `frida` dump dies mid-write (`script has been destroyed`), or the process you are dumping keeps changing pid | `advanced-unpacking.md` §dumping when frida is refused — rule out memory pressure first; a reclaim-and-relaunch needs no instrumentation | | A repack is refused by several independent checks, or the build has to keep working through store updates | `references/lsposed-and-modules.md` — deliver a module instead of an APK; G1's form table says when | | A hook module is installed, enabled and scoped, yet its log tag never appears — and you are about to conclude it never ran | `lsposed-and-modules.md` §Deploy, enable, and verify — **a `logcat`-only verdict has already been wrong here**: on one ROM `logd` is broken and output reaches only `/data/adb/lspd/log/modules_<ts>.log` | | A module's entry class is missing from its own dex (so it can never load), yet the package installs, enables and looks healthy | `references/lsposed-and-modules.md` — check `assets/xposed_init` against the dex's actual classes; installation is not evidence of anything | | You only need to **call** the target's own routine (sign, token, encrypt) rather than change the app | `references/emulation-and-rpc.md` — emulate it, or service-ify the live function over Frida RPC | | A native function is a many-thousand-line `switch` state machine, or the decompiler's output is meaningless | `references/native-dbi-and-deobfuscation.md` — OLLVM shapes, a Stalker trace, and how far a trace actually gets you | | `Stalker.follow` installs but no events arrive, or following a hot libc export crashes the process | `references/native-dbi-and-deobfuscation.md` §6 failure modes — this repository measured both | | The traffic is protobuf/gRPC/QUIC, or a proxy sees TLS but requests still fail on a Flutter app | `references/protocol-reverse.md` — schema-less protobuf, frame capture, and native-side pinning | | Userspace hooks land and the app still dies: the check reads `/proc/self/status` through a raw `svc`, or runs before `JNI_OnLoad` | `references/kernel-and-environment-hardening.md` — what the next layer up and down can actually do, and when to stop | | You must edit, repack, sign or inspect the APK **from the phone itself** | `references/on-device-tooling.md`, `scripts/mt_mcp_probe.py` | | A captured body decodes to nothing readable, or you cannot tell whether a length-delimited field is a string, a nested message or a packed array | `protocol-reverse.md`. Protobuf on the wire (measured) — run `scripts/protobuf_decode_raw.py`; the candidate list and its `tie:` lines are the answer | | Method bodies are present but decode as **private opcodes**, and you need the mapping rather than an explanation of why VMP is hard | `vmp-differential-analysis.md`, then `advanced-unpacking.md` for the shape diagnosis | | A store build arrives as `base.apk` + `split_config.*.apk`, or a rebuilt build is refused **as a set** although every file verifies on its own | `split-apk.md` — one keystore across every member for `pm install-multiple`, and check that a merge is legal before trusting a merged single APK | ## Gates — clear these before you patch, in order Each gate is an **action with a pass criterion**. Do not proceed past a gate you have not cleared, and do not treat "I understand the idea" as clearing it. Skipping a gate is not a shortcut; it is how the work gets redone. **G1 · Deliverable form — and the cost ceiling on it.** State, in one sentence you could hand to someone else, what artifact must exist at the end and under what constraints (rooted or not, installable on a stock device or not, must survive updates or not, online or offline). *Pass:* the sentence names a testable constraint, not an activity. *Fail:* you are solving a problem in an environment the deliverable will never see. Then name the **form** that sentence implies. "A rebuilt, self-contained APK" is only one of four, and
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る