From 5ec451e3520c9c3d0b1ad2dc6cf8c0f596744654 Mon Sep 17 00:00:00 2001 From: Simone Avogadro Date: Mon, 2 Feb 2026 16:18:11 +0100 Subject: [PATCH] commit iniziale --- LICENSE | 190 ++++++++ README.md | 141 ++++++ .../.claude-plugin/plugin.json | 10 + .../commands/decompile.md | 86 ++++ .../android-reverse-engineering/SKILL.md | 191 ++++++++ .../references/api-extraction-patterns.md | 119 +++++ .../references/call-flow-analysis.md | 176 +++++++ .../references/fernflower-usage.md | 115 +++++ .../references/jadx-usage.md | 116 +++++ .../references/setup-guide.md | 221 +++++++++ .../scripts/check-deps.sh | 129 +++++ .../scripts/decompile.sh | 375 +++++++++++++++ .../scripts/find-api-calls.sh | 118 +++++ .../scripts/install-dep.sh | 448 ++++++++++++++++++ 14 files changed, 2435 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 plugins/android-reverse-engineering/.claude-plugin/plugin.json create mode 100644 plugins/android-reverse-engineering/commands/decompile.md create mode 100644 plugins/android-reverse-engineering/skills/android-reverse-engineering/SKILL.md create mode 100644 plugins/android-reverse-engineering/skills/android-reverse-engineering/references/api-extraction-patterns.md create mode 100644 plugins/android-reverse-engineering/skills/android-reverse-engineering/references/call-flow-analysis.md create mode 100644 plugins/android-reverse-engineering/skills/android-reverse-engineering/references/fernflower-usage.md create mode 100644 plugins/android-reverse-engineering/skills/android-reverse-engineering/references/jadx-usage.md create mode 100644 plugins/android-reverse-engineering/skills/android-reverse-engineering/references/setup-guide.md create mode 100755 plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/check-deps.sh create mode 100755 plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh create mode 100755 plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh create mode 100755 plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/install-dep.sh diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..460b91f --- /dev/null +++ b/LICENSE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2025 simonea + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..efe646c --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# Android Reverse Engineering — Claude Code Plugin + +A Claude Code plugin for decompiling Android APK/XAPK/JAR/AAR files with jadx, tracing call flows through application code, and documenting extracted APIs. + +## What it does + +- **Decompiles** APK, XAPK, JAR, and AAR files using jadx and Fernflower/Vineflower (single engine or side-by-side comparison) +- **Analyzes** app structure: manifest, packages, architecture patterns +- **Traces call flows** from Activities/Fragments through ViewModels and repositories down to HTTP calls +- **Extracts and documents APIs**: Retrofit endpoints, OkHttp calls, hardcoded URLs, auth patterns +- **Handles obfuscated code**: strategies for navigating ProGuard/R8 output + +## Requirements + +**Required:** +- Java JDK 17+ +- [jadx](https://github.com/skylot/jadx) (CLI) + +**Optional (recommended):** +- [Vineflower](https://github.com/Vineflower/vineflower) or [Fernflower](https://github.com/JetBrains/fernflower) — better output on complex Java code +- [dex2jar](https://github.com/pxb1988/dex2jar) — needed to use Fernflower on APK/DEX files + +See `plugins/android-reverse-engineering/skills/android-reverse-engineering/references/setup-guide.md` for detailed installation instructions. + +## Installation + +### From GitHub (recommended) + +Inside Claude Code, run: + +``` +/plugin marketplace add simonea/android-reverse-engineering-skill +/plugin install android-reverse-engineering@android-reverse-engineering-skill +``` + +The plugin will be permanently available in all future sessions. + +### From a local clone + +```bash +git clone https://github.com/simonea/android-reverse-engineering-skill.git +``` + +Then in Claude Code: + +``` +/plugin marketplace add /path/to/android-reverse-engineering-skill +/plugin install android-reverse-engineering@android-reverse-engineering-skill +``` + +## Usage + +### Slash command + +``` +/decompile path/to/app.apk +``` + +This runs the full workflow: dependency check, decompilation, and initial structure analysis. + +### Natural language + +The skill activates on phrases like: + +- "Decompile this APK" +- "Reverse engineer this Android app" +- "Extract API endpoints from this app" +- "Follow the call flow from LoginActivity" +- "Analyze this AAR library" + +### Manual scripts + +The scripts can also be used standalone: + +```bash +# Check dependencies +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/check-deps.sh + +# Install a missing dependency (auto-detects OS and package manager) +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/install-dep.sh jadx +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/install-dep.sh vineflower + +# Decompile APK with jadx (default) +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh app.apk + +# Decompile XAPK (auto-extracts and decompiles each APK inside) +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh app-bundle.xapk + +# Decompile with Fernflower +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh --engine fernflower library.jar + +# Run both engines and compare +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh --engine both --deobf app.apk + +# Find API calls +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh output/sources/ +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh output/sources/ --retrofit +bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh output/sources/ --urls +``` + +## Repository Structure + +``` +android-reverse-engineering-skill/ +├── .claude-plugin/ +│ └── marketplace.json # Marketplace catalog +├── plugins/ +│ └── android-reverse-engineering/ +│ ├── .claude-plugin/ +│ │ └── plugin.json # Plugin manifest +│ ├── skills/ +│ │ └── android-reverse-engineering/ +│ │ ├── SKILL.md # Core workflow (5 phases) +│ │ ├── references/ +│ │ │ ├── setup-guide.md +│ │ │ ├── jadx-usage.md +│ │ │ ├── fernflower-usage.md +│ │ │ ├── api-extraction-patterns.md +│ │ │ └── call-flow-analysis.md +│ │ └── scripts/ +│ │ ├── check-deps.sh +│ │ ├── install-dep.sh +│ │ ├── decompile.sh +│ │ └── find-api-calls.sh +│ └── commands/ +│ └── decompile.md # /decompile slash command +├── LICENSE +└── README.md +``` + +## References + +- [jadx — Dex to Java decompiler](https://github.com/skylot/jadx) +- [Fernflower — JetBrains analytical decompiler](https://github.com/JetBrains/fernflower) +- [Vineflower — Fernflower community fork](https://github.com/Vineflower/vineflower) +- [dex2jar — DEX to JAR converter](https://github.com/pxb1988/dex2jar) +- [apktool — Android resource decoder](https://apktool.org/) + +## License + +Apache 2.0 — see [LICENSE](LICENSE) diff --git a/plugins/android-reverse-engineering/.claude-plugin/plugin.json b/plugins/android-reverse-engineering/.claude-plugin/plugin.json new file mode 100644 index 0000000..9b21626 --- /dev/null +++ b/plugins/android-reverse-engineering/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "android-reverse-engineering", + "version": "1.0.0", + "description": "Decompile Android APK/JAR/AAR with jadx, trace call flows through libraries, and document extracted APIs.", + "author": { + "name": "simonea" + }, + "repository": "https://github.com/simonea/android-reverse-engineering-skill", + "license": "Apache-2.0" +} diff --git a/plugins/android-reverse-engineering/commands/decompile.md b/plugins/android-reverse-engineering/commands/decompile.md new file mode 100644 index 0000000..61c402a --- /dev/null +++ b/plugins/android-reverse-engineering/commands/decompile.md @@ -0,0 +1,86 @@ +--- +allowed-tools: Bash, Read, Glob, Grep, Write, Edit +description: Decompile an Android APK/XAPK/JAR/AAR and analyze its structure +user-invocable: true +argument: path to APK, XAPK, JAR, or AAR file (optional) +--- + +# /decompile + +Decompile an Android application and perform initial structure analysis. + +## Instructions + +You are starting the Android reverse engineering workflow. Follow these steps: + +### Step 1: Get the target file + +If the user provided a file path as an argument, use that. Otherwise, ask the user for the path to the APK, XAPK, JAR, or AAR file they want to decompile. + +### Step 2: Check and install dependencies + +Run the dependency check: + +```bash +bash skills/android-reverse-engineering/scripts/check-deps.sh +``` + +Parse the output looking for `INSTALL_REQUIRED:` and `INSTALL_OPTIONAL:` lines. + +**If required dependencies are missing**, install them one by one: + +```bash +bash skills/android-reverse-engineering/scripts/install-dep.sh java +bash skills/android-reverse-engineering/scripts/install-dep.sh jadx +``` + +The install script auto-detects the OS and installs without sudo when possible (user-local install to `~/.local/`). If sudo is needed, it will prompt — if the user declines or sudo is unavailable, the script prints exact manual instructions (exit code 2). Show those instructions to the user and stop. + +**For optional dependencies** (`INSTALL_OPTIONAL:vineflower`, `INSTALL_OPTIONAL:dex2jar`, etc.), ask the user if they want to install them. Recommend vineflower and dex2jar for better results. + +After any installations, re-run `check-deps.sh` to verify. Do not proceed until all required dependencies pass. + +### Step 3: Decompile + +Run the decompile script on the target file. Choose the engine based on the input: + +- **APK or XAPK** → use jadx first (handles resources natively; XAPK is auto-extracted): + ```bash + bash skills/android-reverse-engineering/scripts/decompile.sh + ``` + +- **JAR/AAR** and Fernflower is available → prefer fernflower for better Java output: + ```bash + bash skills/android-reverse-engineering/scripts/decompile.sh --engine fernflower + ``` + +- **If jadx output has warnings** or the user wants the best quality → run both and compare: + ```bash + bash skills/android-reverse-engineering/scripts/decompile.sh --engine both + ``` + +For obfuscated apps (if the user mentions it or you detect single-letter package names), add `--deobf`: + +```bash +bash skills/android-reverse-engineering/scripts/decompile.sh --deobf +``` + +### Step 4: Analyze structure + +After decompilation completes: + +1. Read `AndroidManifest.xml` from the resources directory. For XAPK, check the base APK's output first. +2. If XAPK, review `xapk-manifest.json` in the output directory to understand the split structure. +3. List the top-level package structure +4. Identify the app's main Activity, Application class, and architecture pattern +5. Report a summary to the user + +### Step 5: Offer next steps + +Tell the user what they can do next: +- **Trace call flows**: "I can follow the execution flow from any Activity to its API calls" +- **Extract APIs**: "I can search for all HTTP endpoints and document them" +- **Analyze specific classes**: "Point me to a specific class or feature to analyze" +- **Re-decompile with Fernflower**: If jadx output has warnings, offer to re-run with `--engine both` for comparison + +Refer to the full skill documentation in `skills/android-reverse-engineering/SKILL.md` for the complete workflow. diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/SKILL.md b/plugins/android-reverse-engineering/skills/android-reverse-engineering/SKILL.md new file mode 100644 index 0000000..f2aea75 --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/SKILL.md @@ -0,0 +1,191 @@ +--- +trigger: decompile APK|decompile XAPK|reverse engineer Android|extract API|analyze Android|jadx|fernflower|vineflower|follow call flow|decompile JAR|decompile AAR|Android reverse engineering|find API endpoints +--- + +# Android Reverse Engineering + +Decompile Android APK, XAPK, JAR, and AAR files using jadx and Fernflower/Vineflower, trace call flows through application code and libraries, and produce structured documentation of extracted APIs. Two decompiler engines are supported — jadx for broad Android coverage and Fernflower for higher-quality output on complex Java code — and can be used together for comparison. + +## Prerequisites + +This skill requires **Java JDK 17+** and **jadx** to be installed. **Fernflower/Vineflower** and **dex2jar** are optional but recommended for better decompilation quality. Run the dependency checker to verify: + +```bash +bash skills/android-reverse-engineering/scripts/check-deps.sh +``` + +If anything is missing, follow the installation instructions in `skills/android-reverse-engineering/references/setup-guide.md`. + +## Workflow + +### Phase 1: Verify and Install Dependencies + +Before decompiling, confirm that the required tools are available — and install any that are missing. + +**Action**: Run the dependency check script. + +```bash +bash skills/android-reverse-engineering/scripts/check-deps.sh +``` + +The output contains machine-readable lines: +- `INSTALL_REQUIRED:` — must be installed before proceeding +- `INSTALL_OPTIONAL:` — recommended but not blocking + +**If required dependencies are missing** (exit code 1), install them automatically: + +```bash +bash skills/android-reverse-engineering/scripts/install-dep.sh +``` + +The install script detects the OS and package manager, then: +- Installs without sudo when possible (downloads to `~/.local/share/`, symlinks in `~/.local/bin/`) +- Uses sudo and the system package manager when necessary (apt, dnf, pacman) +- If sudo is needed but unavailable or the user declines, it prints the exact manual command and exits with code 2 — show these instructions to the user + +**For optional dependencies**, ask the user if they want to install them. Vineflower and dex2jar are recommended for best results. + +After installation, re-run `check-deps.sh` to confirm everything is in place. Do not proceed to Phase 2 until all required dependencies are OK. + +### Phase 2: Decompile + +Use the decompile wrapper script to process the target file. The script supports three engines: `jadx`, `fernflower`, and `both`. + +**Action**: Choose the engine and run the decompile script. The script handles APK, XAPK, JAR, and AAR files. + +```bash +bash skills/android-reverse-engineering/scripts/decompile.sh [OPTIONS] +``` + +For **XAPK** files (ZIP bundles containing multiple APKs, used by APKPure and similar stores): the script automatically extracts the archive, identifies all APK files inside (base + split APKs), and decompiles each one into a separate subdirectory. The XAPK manifest is copied to the output for reference. + +Options: +- `-o ` — Custom output directory (default: `-decompiled`) +- `--deobf` — Enable deobfuscation (recommended for obfuscated apps) +- `--no-res` — Skip resources, decompile code only (faster) +- `--engine ENGINE` — `jadx` (default), `fernflower`, or `both` + +**Engine selection strategy**: + +| Situation | Engine | +|---|---| +| First pass on any APK | `jadx` (fastest, handles resources) | +| JAR/AAR library analysis | `fernflower` (better Java output) | +| jadx output has warnings/broken code | `both` (compare and pick best per class) | +| Complex lambdas, generics, streams | `fernflower` | +| Quick overview of a large APK | `jadx --no-res` | + +When using `--engine both`, the outputs go into `/jadx/` and `/fernflower/` respectively, with a comparison summary at the end showing file counts and jadx warning counts. Review classes with jadx warnings in the Fernflower output for better code. + +For APK files with Fernflower, the script automatically uses dex2jar as an intermediate step. dex2jar must be installed for this to work. + +See `references/jadx-usage.md` and `references/fernflower-usage.md` for the full CLI references. + +### Phase 3: Analyze Structure + +Navigate the decompiled output to understand the app's architecture. + +**Actions**: + +1. **Read AndroidManifest.xml** from `/resources/AndroidManifest.xml`: + - Identify the main launcher Activity + - List all Activities, Services, BroadcastReceivers, ContentProviders + - Note permissions (especially `INTERNET`, `ACCESS_NETWORK_STATE`) + - Find the application class (`android:name` on ``) + +2. **Survey the package structure** under `/sources/`: + - Identify the main app package and sub-packages + - Distinguish app code from third-party libraries + - Look for packages named `api`, `network`, `data`, `repository`, `service`, `retrofit`, `http` — these are where API calls live + +3. **Identify the architecture pattern**: + - MVP: look for `Presenter` classes + - MVVM: look for `ViewModel` classes and `LiveData`/`StateFlow` + - Clean Architecture: look for `domain`, `data`, `presentation` packages + - This informs where to look for network calls in the next phases + +### Phase 4: Trace Call Flows + +Follow execution paths from user-facing entry points down to network calls. + +**Actions**: + +1. **Start from entry points**: Read the main Activity or Application class identified in Phase 3. + +2. **Follow the initialization chain**: Application.onCreate() often sets up the HTTP client, base URL, and DI framework. Read this first. + +3. **Trace user actions**: From an Activity, follow: + - `onCreate()` → view setup → click listeners + - Click handler → ViewModel/Presenter method + - ViewModel → Repository → API service interface + - API service → actual HTTP call + +4. **Map DI bindings** (if Dagger/Hilt is used): Find `@Module` classes to understand which implementations are provided for which interfaces. + +5. **Handle obfuscated code**: When class names are mangled, use string literals and library API calls as anchors. Retrofit annotations and URL strings are never obfuscated. + +See `references/call-flow-analysis.md` for detailed techniques and grep commands. + +### Phase 5: Extract and Document APIs + +Find all API endpoints and produce structured documentation. + +**Action**: Run the API search script for a broad sweep. + +```bash +bash skills/android-reverse-engineering/scripts/find-api-calls.sh /sources/ +``` + +Targeted searches: +```bash +# Only Retrofit +bash skills/android-reverse-engineering/scripts/find-api-calls.sh /sources/ --retrofit + +# Only hardcoded URLs +bash skills/android-reverse-engineering/scripts/find-api-calls.sh /sources/ --urls + +# Only auth patterns +bash skills/android-reverse-engineering/scripts/find-api-calls.sh /sources/ --auth +``` + +Then, for each discovered endpoint, read the surrounding source code to extract: +- HTTP method and path +- Base URL +- Path parameters, query parameters, request body +- Headers (especially authentication) +- Response type +- Where it's called from (the call chain from Phase 4) + +**Document each endpoint** using this format: + +```markdown +### `METHOD /path` + +- **Source**: `com.example.api.ApiService` (ApiService.java:42) +- **Base URL**: `https://api.example.com/v1` +- **Path params**: `id` (String) +- **Query params**: `page` (int), `limit` (int) +- **Headers**: `Authorization: Bearer ` +- **Request body**: `{ "email": "string", "password": "string" }` +- **Response**: `ApiResponse` +- **Called from**: `LoginActivity → LoginViewModel → UserRepository → ApiService` +``` + +See `references/api-extraction-patterns.md` for library-specific search patterns and the full documentation template. + +## Output + +At the end of the workflow, deliver: + +1. **Decompiled source** in the output directory +2. **Architecture summary** — app structure, main packages, pattern used +3. **API documentation** — all discovered endpoints in the format above +4. **Call flow map** — key paths from UI to network (especially authentication and main features) + +## References + +- `references/setup-guide.md` — Installing Java, jadx, Fernflower/Vineflower, dex2jar, and optional tools +- `references/jadx-usage.md` — jadx CLI options and workflows +- `references/fernflower-usage.md` — Fernflower/Vineflower CLI options, when to use, APK workflow +- `references/api-extraction-patterns.md` — Library-specific search patterns and documentation template +- `references/call-flow-analysis.md` — Techniques for tracing call flows in decompiled code diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/api-extraction-patterns.md b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/api-extraction-patterns.md new file mode 100644 index 0000000..5467eb1 --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/api-extraction-patterns.md @@ -0,0 +1,119 @@ +# API Extraction Patterns + +Patterns and grep commands for finding HTTP API calls in decompiled Android source code. + +## Retrofit + +Retrofit is the most common HTTP client in Android apps. API endpoints are declared as annotated interface methods. + +### Annotations to search for + +```bash +# HTTP method annotations +grep -rn '@GET\|@POST\|@PUT\|@DELETE\|@PATCH\|@HEAD' sources/ + +# Parameter annotations +grep -rn '@Query\|@QueryMap\|@Path\|@Body\|@Field\|@FieldMap\|@Part\|@Header\|@HeaderMap' sources/ + +# Headers annotation (static headers) +grep -rn '@Headers' sources/ + +# Base URL configuration +grep -rn 'baseUrl\|\.baseUrl(' sources/ +``` + +### Typical Retrofit interface + +```java +public interface ApiService { + @GET("users/{id}") + Call getUser(@Path("id") String userId); + + @POST("auth/login") + @Headers({"Content-Type: application/json"}) + Call login(@Body LoginRequest request); +} +``` + +When documenting, capture: HTTP method, path, path parameters, query parameters, request body type, response type, and any static headers. + +## OkHttp + +OkHttp is often used directly or as the transport layer for Retrofit. + +```bash +# Request building +grep -rn 'Request\.Builder\|Request.Builder\|\.url(\|\.post(\|\.put(\|\.delete(\|\.patch(' sources/ + +# URL construction +grep -rn 'HttpUrl\|\.addQueryParameter\|\.addPathSegment' sources/ + +# Interceptors (often add auth headers) +grep -rn 'Interceptor\|addInterceptor\|addNetworkInterceptor\|intercept(' sources/ + +# Response handling +grep -rn '\.execute()\|\.enqueue(' sources/ +``` + +## Volley + +```bash +grep -rn 'StringRequest\|JsonObjectRequest\|JsonArrayRequest\|Volley\.newRequestQueue\|RequestQueue' sources/ +``` + +Volley requests typically pass the URL as a constructor argument and override `getHeaders()` or `getParams()` for custom headers/parameters. + +## HttpURLConnection (legacy) + +```bash +grep -rn 'HttpURLConnection\|HttpsURLConnection\|openConnection\|setRequestMethod\|setRequestProperty' sources/ +``` + +## WebView + +```bash +grep -rn 'loadUrl\|evaluateJavascript\|addJavascriptInterface\|WebViewClient\|shouldOverrideUrlLoading' sources/ +``` + +WebView-based apps may load API endpoints via JavaScript bridges. Look for `@JavascriptInterface` annotated methods. + +## Hardcoded URLs and Secrets + +```bash +# HTTP/HTTPS URLs +grep -rn '"https\?://[^"]*"' sources/ + +# API keys and tokens +grep -rni 'api[_-]\?key\|api[_-]\?secret\|auth[_-]\?token\|bearer\|access[_-]\?token\|client[_-]\?secret' sources/ + +# Base URL constants +grep -rni 'BASE_URL\|API_URL\|SERVER_URL\|ENDPOINT\|API_BASE' sources/ +``` + +## Documentation Template + +For each discovered API endpoint, document it using this template: + +```markdown +### `METHOD /path/to/endpoint` + +- **Source**: `com.example.app.api.ApiService` (file:line) +- **Base URL**: `https://api.example.com/v1` +- **Full URL**: `https://api.example.com/v1/path/to/endpoint` +- **Path parameters**: `id` (String) +- **Query parameters**: `page` (int), `limit` (int) +- **Headers**: + - `Authorization: Bearer ` + - `Content-Type: application/json` +- **Request body**: `LoginRequest { email: String, password: String }` +- **Response type**: `ApiResponse` +- **Notes**: Called from `LoginActivity.onLoginClicked()` +``` + +## Search Strategy + +1. Start with **base URL constants** — find where the API root is configured +2. Search for **Retrofit interfaces** — they give the clearest picture of all endpoints +3. Check **interceptors** — they reveal auth schemes and common headers +4. Search for **hardcoded URLs** — catch any one-off API calls outside the main client +5. Look for **WebView URLs** — some apps use hybrid web/native approaches diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/call-flow-analysis.md b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/call-flow-analysis.md new file mode 100644 index 0000000..7669f62 --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/call-flow-analysis.md @@ -0,0 +1,176 @@ +# Call Flow Analysis + +Techniques for tracing execution flows in decompiled Android applications, from entry points down to network calls. + +## 1. Start from AndroidManifest.xml + +The manifest declares all entry points. After decompilation, find it at: + +``` +/resources/AndroidManifest.xml +``` + +Key elements to look for: + +```bash +# Activities (UI screens) +grep -n 'android:name=.*Activity' resources/AndroidManifest.xml + +# Services (background work) +grep -n 'android:name=.*Service' resources/AndroidManifest.xml + +# BroadcastReceivers +grep -n '50MB), quick overview | jadx | +| Obfuscated Android app | jadx first, Fernflower on problem areas | +| Both decompilers available | Use `--engine both` and compare | + +## Basic Usage + +```bash +java -jar fernflower.jar [options] ... +``` + +- `` — JAR file, class file, or directory containing class files +- `` — output directory + +For a JAR input, Fernflower produces a JAR in the destination containing `.java` source files. Extract it with `unzip` to browse the sources. + +## Key Options + +Options use the format `-=`. Boolean options: `1` = enabled, `0` = disabled. + +| Option | Default | Description | +|---|---|---| +| `-dgs=1` | 0 | Decompile generic signatures (recommended) | +| `-ren=1` | 0 | Rename obfuscated identifiers | +| `-mpm=60` | 0 | Max seconds per method — prevents hangs (recommended) | +| `-hes=0` | 1 | Show empty super() calls | +| `-hdc=0` | 1 | Show empty default constructors | +| `-udv=1` | 1 | Use debug variable names if available | +| `-ump=1` | 1 | Use debug parameter names if available | +| `-lit=1` | 0 | Output numeric literals as-is | +| `-asc=1` | 0 | Encode non-ASCII as unicode escapes | +| `-lac=1` | 0 | Decompile lambdas as anonymous classes | +| `-log=WARN` | INFO | Reduce output verbosity | +| `-e=` | — | Add library for context (not decompiled, improves type resolution) | + +## Recommended Presets + +### General use + +```bash +java -jar fernflower.jar -dgs=1 -mpm=60 input.jar output/ +``` + +### Obfuscated code + +```bash +java -jar fernflower.jar -dgs=1 -ren=1 -mpm=60 input.jar output/ +``` + +### Maximum detail + +```bash +java -jar fernflower.jar -dgs=1 -hes=0 -hdc=0 -mpm=60 input.jar output/ +``` + +### With Android SDK context (better type resolution) + +```bash +java -jar fernflower.jar -dgs=1 -mpm=60 -e=$ANDROID_HOME/platforms/android-34/android.jar input.jar output/ +``` + +## Working with APK Files + +Fernflower cannot read APK/DEX files directly. Use dex2jar first: + +```bash +# Step 1: Convert DEX to JAR +d2j-dex2jar -f -o app-converted.jar app.apk + +# Step 2: Decompile with Fernflower +java -jar fernflower.jar -dgs=1 -mpm=60 app-converted.jar output/ + +# Step 3: Extract the resulting source JAR +unzip -o output/app-converted.jar -d output/sources/ +``` + +The `decompile.sh --engine fernflower` script automates these steps. + +## Supported Input Formats + +| Format | Direct support | Via dex2jar | +|---|---|---| +| `.jar` | Yes | — | +| `.class` | Yes | — | +| `.zip` (with classes) | Yes | — | +| `.apk` | No | Yes | +| `.dex` | No | Yes | +| `.aar` | No | Yes | + +## Output Format + +- **JAR input** → Produces `/.jar` containing `.java` files +- **Class file input** → Produces `.java` files directly in the destination +- **No resource decoding** — Fernflower only produces Java source, never XML/resources + +## Fernflower vs Vineflower + +Vineflower is the recommended fork. Improvements over upstream Fernflower: + +- Published releases on GitHub and Maven Central +- Better handling of modern Java (records, sealed classes, pattern matching) +- More accurate lambda and switch expression decompilation +- Active bug fixes and community maintenance +- Same CLI interface — drop-in replacement diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/jadx-usage.md b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/jadx-usage.md new file mode 100644 index 0000000..953f27a --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/jadx-usage.md @@ -0,0 +1,116 @@ +# jadx CLI Reference + +## Basic Usage + +```bash +jadx [options] +``` + +Input can be an `.apk`, `.jar`, `.aar`, `.dex`, or `.zip` file. + +## Key Options + +| Option | Description | +|---|---| +| `-d ` | Output directory for decompiled sources | +| `--deobf` | Enable deobfuscation — renames obfuscated classes/methods to readable names | +| `--show-bad-code` | Show partially decompiled code instead of error comments | +| `--no-res` | Skip resource decoding — faster when you only need code | +| `--no-src` | Skip source decompilation — only decode resources | +| `--export-gradle` | Generate a Gradle project structure (useful for importing into IDE) | +| `-e` | Same as `--export-gradle` | +| `--threads-count ` | Number of processing threads (default: CPU count) | +| `-Xmx` | Set maximum Java heap (e.g., `-Xmx4g` for large APKs) | + +## Decompiling Different File Types + +### APK (Android Application Package) + +```bash +jadx -d output-dir app.apk +``` + +Produces: +- `output-dir/sources/` — Decompiled Java source files +- `output-dir/resources/` — Decoded resources (AndroidManifest.xml, layouts, drawables, etc.) + +### JAR (Java Archive) + +```bash +jadx -d output-dir library.jar +``` + +Useful for analyzing third-party libraries bundled within an APK. + +### AAR (Android Archive) + +```bash +jadx -d output-dir library.aar +``` + +AAR files contain both compiled code and Android resources. jadx handles them directly. + +## Handling Obfuscated Code + +Apps built with ProGuard or R8 produce obfuscated bytecode with single-letter class and method names. + +### Strategies + +1. **Use `--deobf`** to generate readable replacement names: + ```bash + jadx --deobf -d output-dir app.apk + ``` + jadx creates a mapping file at `output-dir/deobf-mapping.txt` that maps original obfuscated names to generated names. + +2. **Use the ProGuard mapping file** if available (sometimes shipped in the APK under `assets/` or obtainable from build artifacts): + ```bash + jadx --deobf-map mapping.txt -d output-dir app.apk + ``` + +3. **Focus on string constants and API calls** rather than class names when navigating obfuscated code. URL strings, annotation values, and library classes are not obfuscated. + +## jadx-gui + +For interactive exploration, use the GUI version: + +```bash +jadx-gui app.apk +``` + +Features: +- Full-text search across all decompiled sources +- Click-through navigation (jump to definition) +- Deobfuscation with live renaming +- Smali view alongside Java + +jadx-gui is included in the same distribution as the CLI tool. + +## Common Workflows + +### Code-only decompilation (fastest) + +```bash +jadx --no-res --show-bad-code -d output app.apk +``` + +### Full decompilation with deobfuscation + +```bash +jadx --deobf --show-bad-code -d output app.apk +``` + +### Export as Gradle project for IDE import + +```bash +jadx -e -d output app.apk +# Then open output/ in Android Studio or IntelliJ +``` + +### Decompile a specific DEX from a multi-dex APK + +Extract the APK (it's a ZIP), then target individual DEX files: + +```bash +unzip app.apk -d extracted/ +jadx -d output extracted/classes2.dex +``` diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/setup-guide.md b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/setup-guide.md new file mode 100644 index 0000000..75e5303 --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/setup-guide.md @@ -0,0 +1,221 @@ +# Setup Guide: Dependencies for Android Reverse Engineering + +## Java JDK 17+ + +jadx requires Java 17 or later. + +### Ubuntu / Debian + +```bash +sudo apt update +sudo apt install openjdk-17-jdk +``` + +### Fedora + +```bash +sudo dnf install java-17-openjdk-devel +``` + +### Arch Linux + +```bash +sudo pacman -S jdk17-openjdk +``` + +### macOS (Homebrew) + +```bash +brew install openjdk@17 +``` + +After installation on macOS, follow the symlink instructions printed by Homebrew, or add to your shell profile: + +```bash +export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH" +``` + +### Verify + +```bash +java -version +# Should show version 17.x or higher +``` + +--- + +## jadx + +jadx is the Java decompiler used to convert APK/JAR/AAR files to readable Java source. + +### Option 1: GitHub Releases (recommended) + +1. Go to +2. Download the `jadx-.zip` file (not the source archive) +3. Extract and add to PATH: + +```bash +unzip jadx-*.zip -d ~/jadx +export PATH="$HOME/jadx/bin:$PATH" +# Add the export line to your ~/.bashrc or ~/.zshrc for persistence +``` + +### Option 2: Homebrew (macOS / Linux) + +```bash +brew install jadx +``` + +### Option 3: Build from source + +```bash +git clone https://github.com/skylot/jadx.git +cd jadx +./gradlew dist +# Binaries will be in build/jadx/bin/ +export PATH="$(pwd)/build/jadx/bin:$PATH" +``` + +### Verify + +```bash +jadx --version +``` + +--- + +## Fernflower / Vineflower (optional, recommended) + +Fernflower is the JetBrains Java decompiler. It produces better output than jadx on complex Java constructs, lambdas, and generics. [Vineflower](https://github.com/Vineflower/vineflower) is the actively maintained community fork with published releases — prefer it over upstream Fernflower. + +### Option 1: Vineflower from GitHub Releases (recommended) + +1. Go to +2. Download `vineflower-.jar` +3. Place it and set the environment variable: + +```bash +mkdir -p ~/vineflower +mv vineflower-*.jar ~/vineflower/vineflower.jar +export FERNFLOWER_JAR_PATH="$HOME/vineflower/vineflower.jar" +# Add the export to ~/.bashrc or ~/.zshrc for persistence +``` + +### Option 2: Build Fernflower from source + +```bash +git clone https://github.com/JetBrains/fernflower.git +cd fernflower +./gradlew jar +# Produces: build/libs/fernflower.jar +export FERNFLOWER_JAR_PATH="$(pwd)/build/libs/fernflower.jar" +``` + +### Option 3: Homebrew (Vineflower) + +```bash +brew install vineflower +``` + +### Verify + +```bash +java -jar "$FERNFLOWER_JAR_PATH" --version +``` + +> **Note**: Fernflower only works on JVM bytecode (JAR, class files). For APK/DEX files, you also need **dex2jar** (see below) as an intermediate conversion step. + +--- + +## dex2jar (optional, needed for Fernflower on APK files) + +Converts Android DEX bytecode to standard Java JAR files. + +### GitHub Releases + +1. Go to +2. Download and extract: + +```bash +unzip dex-tools-*.zip -d ~/dex2jar +export PATH="$HOME/dex2jar:$PATH" +``` + +### Homebrew + +```bash +brew install dex2jar +``` + +### Verify + +```bash +d2j-dex2jar --help +``` + +### Usage + +```bash +# Convert APK (or DEX) to JAR +d2j-dex2jar -f -o output.jar app.apk + +# Then decompile with Fernflower +java -jar vineflower.jar output.jar decompiled/ +``` + +--- + +## Optional Tools + +### apktool + +Useful for decoding resources (XML layouts, drawables) that jadx sometimes handles poorly. + +```bash +# Ubuntu/Debian +sudo apt install apktool + +# macOS +brew install apktool + +# Manual: https://apktool.org/docs/install +``` + +### adb (Android Debug Bridge) + +Useful for pulling APKs directly from a connected Android device. + +```bash +# Ubuntu/Debian +sudo apt install adb + +# macOS +brew install android-platform-tools +``` + +Pull an APK from a device: + +```bash +# List installed packages +adb shell pm list packages | grep + +# Get APK path +adb shell pm path com.example.app + +# Pull the APK +adb pull /data/app/com.example.app-xxxx/base.apk ./app.apk +``` + +--- + +## Troubleshooting + +| Problem | Solution | +|---|---| +| `jadx: command not found` | Ensure the jadx `bin/` directory is in your `$PATH` | +| `Error: Could not find or load main class` | Java is missing or wrong version — verify with `java -version` | +| jadx runs out of memory on large APKs | Increase heap: `jadx -Xmx4g -d output app.apk` or set `JAVA_OPTS="-Xmx4g"` | +| Decompiled code has many `// Error` comments | Try `--show-bad-code` to see partial output, or use `--deobf` for obfuscated apps | +| Fernflower hangs on a method | Use `-mpm=60` to set a 60-second timeout per method | +| Fernflower JAR not found | Set `FERNFLOWER_JAR_PATH` env variable to the full path of the JAR | +| dex2jar fails with `ZipException` | The APK may have a non-standard ZIP structure — try `jadx` instead | diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/check-deps.sh b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/check-deps.sh new file mode 100755 index 0000000..b7f1bf7 --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/check-deps.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# check-deps.sh — Verify dependencies and report what's missing +# Output includes machine-readable INSTALL: lines for each missing dependency. +# The install-dep.sh script can install each one. +set -euo pipefail + +REQUIRED_JAVA_MAJOR=17 +errors=0 +missing_required=() +missing_optional=() + +echo "=== Android Reverse Engineering: Dependency Check ===" +echo + +# --- Java --- +java_ok=false +if command -v java &>/dev/null; then + java_version_output=$(java -version 2>&1 | head -1) + java_version=$(echo "$java_version_output" | sed -n 's/.*"\([0-9]*\)\..*/\1/p') + if [[ -z "$java_version" ]]; then + java_version=$(echo "$java_version_output" | grep -oP '\d+' | head -1) + fi + if [[ "$java_version" == "1" ]]; then + java_version=$(echo "$java_version_output" | sed -n 's/.*"1\.\([0-9]*\)\..*/\1/p') + fi + + if [[ -n "$java_version" ]] && (( java_version >= REQUIRED_JAVA_MAJOR )); then + echo "[OK] Java $java_version detected" + java_ok=true + else + echo "[WARN] Java detected but version $java_version is below $REQUIRED_JAVA_MAJOR" + errors=$((errors + 1)) + missing_required+=("java") + fi +else + echo "[MISSING] Java is not installed or not in PATH" + errors=$((errors + 1)) + missing_required+=("java") +fi + +# --- jadx --- +if command -v jadx &>/dev/null; then + jadx_version=$(jadx --version 2>/dev/null || echo "unknown") + echo "[OK] jadx $jadx_version detected" +else + echo "[MISSING] jadx is not installed or not in PATH" + errors=$((errors + 1)) + missing_required+=("jadx") +fi + +# --- Fernflower / Vineflower --- +ff_found=false +if command -v vineflower &>/dev/null; then + echo "[OK] vineflower CLI detected" + ff_found=true +elif command -v fernflower &>/dev/null; then + echo "[OK] fernflower CLI detected" + ff_found=true +else + for candidate in \ + "${FERNFLOWER_JAR_PATH:-}" \ + "$HOME/.local/share/vineflower/vineflower.jar" \ + "$HOME/fernflower/build/libs/fernflower.jar" \ + "$HOME/vineflower/build/libs/vineflower.jar" \ + "$HOME/fernflower/fernflower.jar" \ + "$HOME/vineflower/vineflower.jar"; do + if [[ -n "$candidate" ]] && [[ -f "$candidate" ]]; then + echo "[OK] Fernflower/Vineflower JAR found: $candidate" + ff_found=true + break + fi + done +fi +if [[ "$ff_found" == false ]]; then + echo "[MISSING] Fernflower/Vineflower not found (optional — better output on complex Java code)" + missing_optional+=("vineflower") +fi + +# --- dex2jar --- +if command -v d2j-dex2jar &>/dev/null || command -v d2j-dex2jar.sh &>/dev/null; then + echo "[OK] dex2jar detected" +else + echo "[MISSING] dex2jar not found (optional — needed to use Fernflower on APK/DEX files)" + missing_optional+=("dex2jar") +fi + +# --- Optional: apktool --- +if command -v apktool &>/dev/null; then + echo "[OK] apktool detected (optional)" +else + echo "[MISSING] apktool not found (optional — useful for resource decoding)" + missing_optional+=("apktool") +fi + +# --- Optional: adb --- +if command -v adb &>/dev/null; then + echo "[OK] adb detected (optional)" +else + echo "[MISSING] adb not found (optional — useful for pulling APKs from devices)" + missing_optional+=("adb") +fi + +# --- Machine-readable summary --- +echo +if [[ ${#missing_required[@]} -gt 0 ]]; then + for dep in "${missing_required[@]}"; do + echo "INSTALL_REQUIRED:$dep" + done +fi +if [[ ${#missing_optional[@]} -gt 0 ]]; then + for dep in "${missing_optional[@]}"; do + echo "INSTALL_OPTIONAL:$dep" + done +fi + +echo +if (( errors > 0 )); then + echo "*** ${#missing_required[@]} required dependency/ies missing. ***" + echo "Run install-dep.sh to install, or see references/setup-guide.md." + exit 1 +else + if [[ ${#missing_optional[@]} -gt 0 ]]; then + echo "Required dependencies OK. ${#missing_optional[@]} optional dependency/ies missing." + echo "Run install-dep.sh to install optional tools." + else + echo "All dependencies are installed. Ready to decompile." + fi + exit 0 +fi diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh new file mode 100755 index 0000000..e36ca1a --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh @@ -0,0 +1,375 @@ +#!/usr/bin/env bash +# decompile.sh — Decompile APK/JAR/AAR using jadx, fernflower, or both +set -euo pipefail + +usage() { + cat < + +Decompile an Android APK, XAPK, JAR, or AAR file. + +Arguments: + Path to the .apk, .xapk, .jar, or .aar file + +Options: + -o, --output DIR Output directory (default: -decompiled) + --deobf Enable deobfuscation of names + --no-res Skip resource decoding (faster, code-only) + --engine ENGINE Decompiler engine: jadx, fernflower, or both (default: jadx) + -h, --help Show this help message + +Engines: + jadx Use jadx (default). Handles APK/JAR/AAR natively, decodes resources. + fernflower Use Fernflower/Vineflower. Better on complex Java, lambdas, generics. + For APK files, requires dex2jar as intermediate step. + both Run both decompilers side by side for comparison. + jadx output → /jadx/ + fernflower → /fernflower/ + +Environment: + FERNFLOWER_JAR_PATH Path to fernflower.jar or vineflower.jar + +Examples: + decompile.sh app-release.apk + decompile.sh app-bundle.xapk + decompile.sh --engine both --deobf app-release.apk + decompile.sh --engine fernflower library.jar +EOF + exit 0 +} + +# --- Parse arguments --- +OUTPUT_DIR="" +DEOBF=false +NO_RES=false +ENGINE="jadx" +INPUT_FILE="" + +while [[ $# -gt 0 ]]; do + case "$1" in + -o|--output) OUTPUT_DIR="$2"; shift 2 ;; + --deobf) DEOBF=true; shift ;; + --no-res) NO_RES=true; shift ;; + --engine) ENGINE="$2"; shift 2 ;; + -h|--help) usage ;; + -*) echo "Error: Unknown option $1" >&2; usage ;; + *) INPUT_FILE="$1"; shift ;; + esac +done + +# --- Validate input --- +if [[ -z "$INPUT_FILE" ]]; then + echo "Error: No input file specified." >&2 + usage +fi + +if [[ ! -f "$INPUT_FILE" ]]; then + echo "Error: File not found: $INPUT_FILE" >&2 + exit 1 +fi + +ext="${INPUT_FILE##*.}" +ext_lower=$(echo "$ext" | tr '[:upper:]' '[:lower:]') +case "$ext_lower" in + apk|xapk|jar|aar) ;; + *) + echo "Error: Unsupported file type '.$ext'. Expected .apk, .xapk, .jar, or .aar" >&2 + exit 1 + ;; +esac + +case "$ENGINE" in + jadx|fernflower|both) ;; + *) + echo "Error: Unknown engine '$ENGINE'. Use jadx, fernflower, or both." >&2 + exit 1 + ;; +esac + +BASENAME=$(basename "$INPUT_FILE" ".$ext_lower") +INPUT_FILE_ABS=$(realpath "$INPUT_FILE") + +if [[ -z "$OUTPUT_DIR" ]]; then + OUTPUT_DIR="${BASENAME}-decompiled" +fi + +# --- XAPK handling --- +# XAPK is a ZIP containing one or more APKs, optional OBB files, and a manifest.json. +# We extract it, find all APKs inside, and decompile each one. +XAPK_EXTRACTED_DIR="" +XAPK_APK_FILES=() + +if [[ "$ext_lower" == "xapk" ]]; then + XAPK_EXTRACTED_DIR=$(mktemp -d "${TMPDIR:-/tmp}/xapk-extract-XXXXXX") + echo "=== Extracting XAPK archive ===" + unzip -qo "$INPUT_FILE_ABS" -d "$XAPK_EXTRACTED_DIR" + + # Show manifest.json if present + if [[ -f "$XAPK_EXTRACTED_DIR/manifest.json" ]]; then + echo "XAPK manifest found:" + cat "$XAPK_EXTRACTED_DIR/manifest.json" + echo + fi + + # Find all APK files inside + while IFS= read -r -d '' apk_file; do + XAPK_APK_FILES+=("$apk_file") + done < <(find "$XAPK_EXTRACTED_DIR" -name "*.apk" -print0 | sort -z) + + if [[ ${#XAPK_APK_FILES[@]} -eq 0 ]]; then + echo "Error: No APK files found inside XAPK archive." >&2 + rm -rf "$XAPK_EXTRACTED_DIR" + exit 1 + fi + + echo "Found ${#XAPK_APK_FILES[@]} APK(s) inside XAPK:" + for f in "${XAPK_APK_FILES[@]}"; do + echo " - $(basename "$f")" + done + echo +fi + +# --- Locate fernflower JAR --- +find_fernflower_jar() { + if [[ -n "${FERNFLOWER_JAR_PATH:-}" ]] && [[ -f "$FERNFLOWER_JAR_PATH" ]]; then + echo "$FERNFLOWER_JAR_PATH" + return + fi + # Check common locations + for candidate in \ + "$HOME/fernflower/build/libs/fernflower.jar" \ + "$HOME/vineflower/build/libs/vineflower.jar" \ + "$HOME/fernflower/fernflower.jar" \ + "$HOME/vineflower/vineflower.jar"; do + if [[ -f "$candidate" ]]; then + echo "$candidate" + return + fi + done + return 1 +} + +# --- Locate dex2jar --- +find_dex2jar() { + if command -v d2j-dex2jar &>/dev/null; then + echo "d2j-dex2jar" + elif command -v d2j-dex2jar.sh &>/dev/null; then + echo "d2j-dex2jar.sh" + else + return 1 + fi +} + +# --- jadx decompilation --- +run_jadx() { + local out_dir="$1" + + if ! command -v jadx &>/dev/null; then + echo "Error: jadx is not installed or not in PATH." >&2 + return 1 + fi + + local args=() + args+=("-d" "$out_dir") + [[ "$DEOBF" == true ]] && args+=("--deobf") + [[ "$NO_RES" == true ]] && args+=("--no-res") + args+=("--show-bad-code") + args+=("$INPUT_FILE_ABS") + + echo "Running: jadx ${args[*]}" + jadx "${args[@]}" + + echo "jadx output: $out_dir/sources/" + if [[ -d "$out_dir/sources" ]]; then + local count + count=$(find "$out_dir/sources" -name "*.java" | wc -l) + echo "Java files decompiled by jadx: $count" + fi +} + +# --- Fernflower decompilation --- +run_fernflower() { + local out_dir="$1" + local jar_to_decompile="" + + local ff_jar + if ! ff_jar=$(find_fernflower_jar); then + echo "Error: Fernflower/Vineflower JAR not found." >&2 + echo "Set FERNFLOWER_JAR_PATH or see references/setup-guide.md" >&2 + return 1 + fi + + mkdir -p "$out_dir" + + # For APK/AAR, we need dex2jar first to convert DEX→JAR + if [[ "$ext_lower" == "apk" || "$ext_lower" == "aar" ]]; then + local d2j + if ! d2j=$(find_dex2jar); then + echo "Error: dex2jar is required to use Fernflower on .$ext_lower files." >&2 + echo "Install dex2jar — see references/setup-guide.md" >&2 + return 1 + fi + + echo "Converting $ext_lower to JAR with dex2jar..." + local converted_jar="$out_dir/${BASENAME}-dex2jar.jar" + "$d2j" -f -o "$converted_jar" "$INPUT_FILE_ABS" 2>&1 || true + if [[ ! -f "$converted_jar" ]]; then + echo "Error: dex2jar conversion failed." >&2 + return 1 + fi + jar_to_decompile="$converted_jar" + else + jar_to_decompile="$INPUT_FILE_ABS" + fi + + # Build fernflower args + local ff_args=() + ff_args+=("-dgs=1") # decompile generic signatures + ff_args+=("-mpm=60") # 60s max per method to avoid hangs + if [[ "$DEOBF" == true ]]; then + ff_args+=("-ren=1") # rename obfuscated identifiers + fi + ff_args+=("$jar_to_decompile") + ff_args+=("$out_dir") + + echo "Running: java -jar $ff_jar ${ff_args[*]}" + java -jar "$ff_jar" "${ff_args[@]}" + + # Fernflower outputs a JAR containing .java files — extract it + local result_jar="$out_dir/$(basename "$jar_to_decompile")" + if [[ -f "$result_jar" ]]; then + local sources_dir="$out_dir/sources" + mkdir -p "$sources_dir" + unzip -qo "$result_jar" -d "$sources_dir" + rm -f "$result_jar" + echo "Fernflower output: $sources_dir/" + local count + count=$(find "$sources_dir" -name "*.java" | wc -l) + echo "Java files decompiled by Fernflower: $count" + fi + + # Clean up intermediate dex2jar output + if [[ -n "${converted_jar:-}" ]] && [[ -f "${converted_jar:-}" ]]; then + rm -f "$converted_jar" + fi +} + +# --- Summary helper --- +print_structure() { + local src_dir="$1" + local label="$2" + if [[ -d "$src_dir" ]]; then + echo + echo "Top-level packages ($label):" + find "$src_dir" -mindepth 1 -maxdepth 3 -type d | head -20 | sed "s|$src_dir/||" | grep -v '^$' | sort + fi +} + +# --- Decompile a single file with the selected engine --- +decompile_single() { + local file_abs="$1" + local out_dir="$2" + local label="$3" + + # Temporarily override INPUT_FILE_ABS for run_jadx/run_fernflower + local saved_input="$INPUT_FILE_ABS" + local saved_ext="$ext_lower" + INPUT_FILE_ABS="$file_abs" + ext_lower="${file_abs##*.}" + ext_lower=$(echo "$ext_lower" | tr '[:upper:]' '[:lower:]') + + if [[ -n "$label" ]]; then + echo "=== Decompiling $label (engine: $ENGINE) ===" + fi + + case "$ENGINE" in + jadx) + run_jadx "$out_dir" + print_structure "$out_dir/sources" "jadx" + ;; + fernflower) + run_fernflower "$out_dir" + print_structure "$out_dir/sources" "fernflower" + ;; + both) + echo "--- Pass 1: jadx ---" + run_jadx "$out_dir/jadx" + echo + echo "--- Pass 2: Fernflower ---" + run_fernflower "$out_dir/fernflower" + + print_structure "$out_dir/jadx/sources" "jadx" + print_structure "$out_dir/fernflower/sources" "fernflower" + + echo + echo "=== Comparison ===" + local jadx_count=0 ff_count=0 + if [[ -d "$out_dir/jadx/sources" ]]; then + jadx_count=$(find "$out_dir/jadx/sources" -name "*.java" | wc -l) + fi + if [[ -d "$out_dir/fernflower/sources" ]]; then + ff_count=$(find "$out_dir/fernflower/sources" -name "*.java" | wc -l) + fi + echo "jadx: $jadx_count Java files" + echo "Fernflower: $ff_count Java files" + + if [[ -d "$out_dir/jadx/sources" ]]; then + local jadx_errors + jadx_errors=$(grep -rl 'JADX WARNING\|JADX WARN\|JADX ERROR\|Code decompiled incorrectly' "$out_dir/jadx/sources" 2>/dev/null | wc -l || echo 0) + echo "jadx files with warnings/errors: $jadx_errors" + fi + echo + echo "Tip: compare specific classes between jadx/ and fernflower/ to pick the better output." + ;; + esac + + INPUT_FILE_ABS="$saved_input" + ext_lower="$saved_ext" +} + +# --- Run --- +echo "=== Decompiling $INPUT_FILE (engine: $ENGINE) ===" +echo "Output directory: $OUTPUT_DIR" +echo + +if [[ "$ext_lower" == "xapk" ]]; then + # Decompile each APK found inside the XAPK + mkdir -p "$OUTPUT_DIR" + + # Copy XAPK manifest for reference + if [[ -f "$XAPK_EXTRACTED_DIR/manifest.json" ]]; then + cp "$XAPK_EXTRACTED_DIR/manifest.json" "$OUTPUT_DIR/xapk-manifest.json" + fi + + # Copy OBB file list for reference + obb_files=() + while IFS= read -r -d '' obb; do + obb_files+=("$obb") + done < <(find "$XAPK_EXTRACTED_DIR" -name "*.obb" -print0 2>/dev/null) + if [[ ${#obb_files[@]} -gt 0 ]]; then + echo "OBB files found (not decompiled, data-only):" + for obb in "${obb_files[@]}"; do + echo " - $(basename "$obb") ($(du -h "$obb" | cut -f1))" + done + echo + fi + + for apk_file in "${XAPK_APK_FILES[@]}"; do + apk_name=$(basename "$apk_file" .apk) + echo + echo "======================================================" + decompile_single "$apk_file" "$OUTPUT_DIR/$apk_name" "$apk_name.apk" + done + + # Cleanup extracted XAPK + rm -rf "$XAPK_EXTRACTED_DIR" + + echo + echo "=== XAPK decompilation complete ===" + echo "Subdirectories in $OUTPUT_DIR/:" + ls -1 "$OUTPUT_DIR/" +else + decompile_single "$INPUT_FILE_ABS" "$OUTPUT_DIR" "" + echo + echo "=== Decompilation complete ===" +fi diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh new file mode 100755 index 0000000..db52acf --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# find-api-calls.sh — Search decompiled source for API calls and HTTP endpoints +set -euo pipefail + +usage() { + cat < [OPTIONS] + +Search decompiled Java/Kotlin source for HTTP API calls and endpoints. + +Arguments: + Path to the decompiled sources directory + +Options: + --retrofit Search only for Retrofit annotations + --okhttp Search only for OkHttp patterns + --volley Search only for Volley patterns + --urls Search only for hardcoded URLs + --auth Search only for auth-related patterns + --all Search all patterns (default) + -h, --help Show this help message + +Output: + Results are printed as file:line:match for easy navigation. +EOF + exit 0 +} + +SOURCE_DIR="" +SEARCH_RETROFIT=false +SEARCH_OKHTTP=false +SEARCH_VOLLEY=false +SEARCH_URLS=false +SEARCH_AUTH=false +SEARCH_ALL=true + +while [[ $# -gt 0 ]]; do + case "$1" in + --retrofit) SEARCH_RETROFIT=true; SEARCH_ALL=false; shift ;; + --okhttp) SEARCH_OKHTTP=true; SEARCH_ALL=false; shift ;; + --volley) SEARCH_VOLLEY=true; SEARCH_ALL=false; shift ;; + --urls) SEARCH_URLS=true; SEARCH_ALL=false; shift ;; + --auth) SEARCH_AUTH=true; SEARCH_ALL=false; shift ;; + --all) SEARCH_ALL=true; shift ;; + -h|--help) usage ;; + -*) echo "Error: Unknown option $1" >&2; usage ;; + *) SOURCE_DIR="$1"; shift ;; + esac +done + +if [[ -z "$SOURCE_DIR" ]]; then + echo "Error: No source directory specified." >&2 + usage +fi + +if [[ ! -d "$SOURCE_DIR" ]]; then + echo "Error: Directory not found: $SOURCE_DIR" >&2 + exit 1 +fi + +GREP_OPTS="-rn --include=*.java --include=*.kt" + +section() { + echo + echo "==== $1 ====" + echo +} + +run_grep() { + local pattern="$1" + # shellcheck disable=SC2086 + grep $GREP_OPTS -E "$pattern" "$SOURCE_DIR" 2>/dev/null || true +} + +# --- Retrofit --- +if [[ "$SEARCH_ALL" == true || "$SEARCH_RETROFIT" == true ]]; then + section "Retrofit Annotations" + run_grep '@(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|HTTP)\s*\(' + section "Retrofit Headers & Parameters" + run_grep '@(Headers|Header|Query|QueryMap|Path|Body|Field|FieldMap|Part|PartMap|Url)\s*\(' + section "Retrofit Base URL" + run_grep '(baseUrl|base_url)\s*\(' +fi + +# --- OkHttp --- +if [[ "$SEARCH_ALL" == true || "$SEARCH_OKHTTP" == true ]]; then + section "OkHttp Request Building" + run_grep '(Request\.Builder|HttpUrl|\.newCall|\.enqueue|addInterceptor|addNetworkInterceptor)' + section "OkHttp URL Construction" + run_grep '(\.url\s*\(|\.addQueryParameter|\.addPathSegment|\.scheme\s*\(|\.host\s*\()' +fi + +# --- Volley --- +if [[ "$SEARCH_ALL" == true || "$SEARCH_VOLLEY" == true ]]; then + section "Volley Requests" + run_grep '(StringRequest|JsonObjectRequest|JsonArrayRequest|ImageRequest|RequestQueue|Volley\.newRequestQueue)' +fi + +# --- Hardcoded URLs --- +if [[ "$SEARCH_ALL" == true || "$SEARCH_URLS" == true ]]; then + section "Hardcoded URLs (http:// and https://)" + run_grep '"https?://[^"]+' + section "HttpURLConnection" + run_grep '(openConnection|setRequestMethod|HttpURLConnection|HttpsURLConnection)' + section "WebView URLs" + run_grep '(loadUrl|loadData|evaluateJavascript|addJavascriptInterface|WebViewClient|WebChromeClient)' +fi + +# --- Auth patterns --- +if [[ "$SEARCH_ALL" == true || "$SEARCH_AUTH" == true ]]; then + section "Authentication & API Keys" + run_grep -i '(api[_-]?key|auth[_-]?token|bearer|authorization|x-api-key|client[_-]?secret|access[_-]?token)' + section "Base URLs and Constants" + run_grep -i '(BASE_URL|API_URL|SERVER_URL|ENDPOINT|API_BASE|HOST_NAME)' +fi + +echo +echo "=== Search complete ===" diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/install-dep.sh b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/install-dep.sh new file mode 100755 index 0000000..b25e8e2 --- /dev/null +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/install-dep.sh @@ -0,0 +1,448 @@ +#!/usr/bin/env bash +# install-dep.sh — Install a single dependency for Android reverse engineering +# Usage: install-dep.sh +# Dependencies: java, jadx, vineflower, dex2jar, apktool, adb +# +# Exit codes: +# 0 — installed successfully +# 1 — installation failed +# 2 — requires manual action (e.g. sudo needed but not available) +set -euo pipefail + +usage() { + cat < + +Install a dependency required for Android reverse engineering. + +Available dependencies: + java Java JDK 17+ + jadx jadx decompiler + vineflower Vineflower (Fernflower fork) decompiler + dex2jar DEX to JAR converter + apktool Android resource decoder + adb Android Debug Bridge + +The script detects your OS and package manager, then: + - Installs directly if possible (brew, or user-local install) + - Uses sudo if available and needed + - Prints manual instructions if neither option works +EOF + exit 0 +} + +if [[ $# -lt 1 || "$1" == "-h" || "$1" == "--help" ]]; then + usage +fi + +DEP="$1" + +# --- Detect environment --- +OS="unknown" +PKG_MANAGER="none" +HAS_SUDO=false +ARCH=$(uname -m) + +case "$(uname -s)" in + Linux) OS="linux" ;; + Darwin) OS="macos" ;; +esac + +# Detect package manager +if command -v brew &>/dev/null; then + PKG_MANAGER="brew" +elif command -v apt-get &>/dev/null; then + PKG_MANAGER="apt" +elif command -v dnf &>/dev/null; then + PKG_MANAGER="dnf" +elif command -v pacman &>/dev/null; then + PKG_MANAGER="pacman" +fi + +# Check sudo availability +if command -v sudo &>/dev/null; then + if sudo -n true 2>/dev/null; then + HAS_SUDO=true + else + # sudo exists but may need password — we'll try it and let it prompt + HAS_SUDO=true + fi +fi + +info() { echo "[INFO] $*"; } +ok() { echo "[OK] $*"; } +fail() { echo "[FAIL] $*" >&2; } +manual() { + echo "[MANUAL] $*" >&2 + echo " Cannot install automatically. Please install manually and retry." >&2 + exit 2 +} + +# --- Helper: install via system package manager (needs sudo on Linux) --- +pkg_install() { + local pkg="$1" + case "$PKG_MANAGER" in + brew) + info "Installing $pkg via Homebrew..." + brew install "$pkg" + ;; + apt) + if [[ "$HAS_SUDO" == true ]]; then + info "Installing $pkg via apt..." + sudo apt-get update -qq && sudo apt-get install -y -qq "$pkg" + else + manual "Run: sudo apt-get install $pkg" + fi + ;; + dnf) + if [[ "$HAS_SUDO" == true ]]; then + info "Installing $pkg via dnf..." + sudo dnf install -y "$pkg" + else + manual "Run: sudo dnf install $pkg" + fi + ;; + pacman) + if [[ "$HAS_SUDO" == true ]]; then + info "Installing $pkg via pacman..." + sudo pacman -S --noconfirm "$pkg" + else + manual "Run: sudo pacman -S $pkg" + fi + ;; + *) + manual "No supported package manager found. Install $pkg manually." + ;; + esac +} + +# --- Helper: download a file --- +download() { + local url="$1" dest="$2" + if command -v curl &>/dev/null; then + curl -fsSL -o "$dest" "$url" + elif command -v wget &>/dev/null; then + wget -q -O "$dest" "$url" + else + fail "Neither curl nor wget available." + return 1 + fi +} + +# --- Helper: get latest GitHub release tag --- +gh_latest_tag() { + local repo="$1" + local url="https://api.github.com/repos/$repo/releases/latest" + if command -v curl &>/dev/null; then + curl -fsSL "$url" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/' + elif command -v wget &>/dev/null; then + wget -q -O - "$url" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/' + fi +} + +# --- Helper: add a line to shell profile if not already present --- +add_to_profile() { + local line="$1" + local profile="" + if [[ -f "$HOME/.zshrc" ]]; then + profile="$HOME/.zshrc" + elif [[ -f "$HOME/.bashrc" ]]; then + profile="$HOME/.bashrc" + elif [[ -f "$HOME/.profile" ]]; then + profile="$HOME/.profile" + fi + + if [[ -n "$profile" ]]; then + if ! grep -qF "$line" "$profile" 2>/dev/null; then + echo "$line" >> "$profile" + info "Added to $profile: $line" + info "Run 'source $profile' or start a new shell to apply." + fi + else + info "Add this to your shell profile: $line" + fi +} + +# ===================================================================== +# Dependency installers +# ===================================================================== + +install_java() { + if command -v java &>/dev/null; then + local ver + ver=$(java -version 2>&1 | head -1 | sed -n 's/.*"\([0-9]*\)\..*/\1/p') + if [[ -n "$ver" ]] && (( ver >= 17 )); then + ok "Java $ver already installed" + return 0 + fi + fi + + info "Installing Java JDK 17+..." + case "$PKG_MANAGER" in + brew) brew install openjdk@17 ;; + apt) pkg_install "openjdk-17-jdk" ;; + dnf) pkg_install "java-17-openjdk-devel" ;; + pacman) pkg_install "jdk17-openjdk" ;; + *) manual "Install Java JDK 17+ from https://adoptium.net/" ;; + esac + + # Verify + if command -v java &>/dev/null; then + ok "Java installed: $(java -version 2>&1 | head -1)" + else + fail "Java installation may require PATH update." + if [[ "$PKG_MANAGER" == "brew" ]]; then + add_to_profile 'export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH"' + fi + exit 1 + fi +} + +install_jadx() { + if command -v jadx &>/dev/null; then + ok "jadx already installed: $(jadx --version 2>/dev/null || echo 'unknown')" + return 0 + fi + + # Try brew first (cleanest) + if [[ "$PKG_MANAGER" == "brew" ]]; then + info "Installing jadx via Homebrew..." + brew install jadx + ok "jadx installed via Homebrew" + return 0 + fi + + # User-local install from GitHub releases (no sudo needed) + info "Installing jadx from GitHub releases..." + local tag + tag=$(gh_latest_tag "skylot/jadx") + if [[ -z "$tag" ]]; then + fail "Could not determine latest jadx version." + manual "Download from https://github.com/skylot/jadx/releases/latest" + fi + + local version="${tag#v}" + local url="https://github.com/skylot/jadx/releases/download/${tag}/jadx-${version}.zip" + local tmp_zip + tmp_zip=$(mktemp /tmp/jadx-XXXXXX.zip) + + info "Downloading jadx $version..." + download "$url" "$tmp_zip" + + local install_dir="$HOME/.local/share/jadx" + rm -rf "$install_dir" + mkdir -p "$install_dir" + unzip -qo "$tmp_zip" -d "$install_dir" + rm -f "$tmp_zip" + chmod +x "$install_dir/bin/jadx" "$install_dir/bin/jadx-gui" 2>/dev/null || true + + # Add to PATH + mkdir -p "$HOME/.local/bin" + ln -sf "$install_dir/bin/jadx" "$HOME/.local/bin/jadx" + ln -sf "$install_dir/bin/jadx-gui" "$HOME/.local/bin/jadx-gui" + export PATH="$HOME/.local/bin:$PATH" + add_to_profile 'export PATH="$HOME/.local/bin:$PATH"' + + if command -v jadx &>/dev/null; then + ok "jadx $version installed to $install_dir" + else + ok "jadx $version installed to $install_dir" + info "Run: export PATH=\"\$HOME/.local/bin:\$PATH\" to use it now" + fi +} + +install_vineflower() { + # Check if already available + if command -v vineflower &>/dev/null || command -v fernflower &>/dev/null; then + ok "Vineflower/Fernflower CLI already installed" + return 0 + fi + for candidate in \ + "${FERNFLOWER_JAR_PATH:-}" \ + "$HOME/vineflower/vineflower.jar" \ + "$HOME/fernflower/fernflower.jar" \ + "$HOME/fernflower/build/libs/fernflower.jar" \ + "$HOME/vineflower/build/libs/vineflower.jar"; do + if [[ -n "$candidate" ]] && [[ -f "$candidate" ]]; then + ok "Vineflower/Fernflower JAR already exists: $candidate" + return 0 + fi + done + + # Try brew + if [[ "$PKG_MANAGER" == "brew" ]]; then + info "Installing vineflower via Homebrew..." + if brew install vineflower 2>/dev/null; then + ok "Vineflower installed via Homebrew" + return 0 + fi + info "Homebrew formula not available, falling back to direct download." + fi + + # Download JAR from GitHub releases (no sudo needed) + info "Installing Vineflower from GitHub releases..." + local tag + tag=$(gh_latest_tag "Vineflower/vineflower") + if [[ -z "$tag" ]]; then + fail "Could not determine latest Vineflower version." + manual "Download from https://github.com/Vineflower/vineflower/releases/latest" + fi + + local version="${tag#v}" + local url="https://github.com/Vineflower/vineflower/releases/download/${tag}/vineflower-${version}.jar" + local install_dir="$HOME/.local/share/vineflower" + mkdir -p "$install_dir" + + info "Downloading Vineflower $version..." + download "$url" "$install_dir/vineflower.jar" + + # Create wrapper script + mkdir -p "$HOME/.local/bin" + cat > "$HOME/.local/bin/vineflower" <<'WRAPPER' +#!/usr/bin/env bash +exec java -jar "$HOME/.local/share/vineflower/vineflower.jar" "$@" +WRAPPER + chmod +x "$HOME/.local/bin/vineflower" + + export PATH="$HOME/.local/bin:$PATH" + export FERNFLOWER_JAR_PATH="$install_dir/vineflower.jar" + add_to_profile 'export PATH="$HOME/.local/bin:$PATH"' + add_to_profile "export FERNFLOWER_JAR_PATH=\"$install_dir/vineflower.jar\"" + + ok "Vineflower $version installed to $install_dir/vineflower.jar" + info "FERNFLOWER_JAR_PATH set to $install_dir/vineflower.jar" +} + +install_dex2jar() { + if command -v d2j-dex2jar &>/dev/null || command -v d2j-dex2jar.sh &>/dev/null; then + ok "dex2jar already installed" + return 0 + fi + + # Try brew + if [[ "$PKG_MANAGER" == "brew" ]]; then + info "Installing dex2jar via Homebrew..." + if brew install dex2jar 2>/dev/null; then + ok "dex2jar installed via Homebrew" + return 0 + fi + info "Homebrew formula not available, falling back to direct download." + fi + + # Download from GitHub (no sudo needed) + info "Installing dex2jar from GitHub releases..." + local tag + tag=$(gh_latest_tag "pxb1988/dex2jar") + if [[ -z "$tag" ]]; then + # Fallback: pxb1988 hasn't released in a while, try known version + tag="v2.4" + fi + + local version="${tag#v}" + local url="https://github.com/pxb1988/dex2jar/releases/download/${tag}/dex-tools-${version}.zip" + local tmp_zip + tmp_zip=$(mktemp /tmp/dex2jar-XXXXXX.zip) + + info "Downloading dex2jar $version..." + if ! download "$url" "$tmp_zip"; then + # Try alternate naming + url="https://github.com/pxb1988/dex2jar/releases/download/${tag}/dex-tools-v${version}.zip" + download "$url" "$tmp_zip" || { + fail "Download failed." + manual "Download from https://github.com/pxb1988/dex2jar/releases/latest" + } + fi + + local install_dir="$HOME/.local/share/dex2jar" + rm -rf "$install_dir" + mkdir -p "$install_dir" + unzip -qo "$tmp_zip" -d "$install_dir" + rm -f "$tmp_zip" + + # The zip may contain a top-level directory — find the actual bin location + local bin_dir="" + if [[ -f "$install_dir/d2j-dex2jar.sh" ]]; then + bin_dir="$install_dir" + else + bin_dir=$(find "$install_dir" -name "d2j-dex2jar.sh" -exec dirname {} \; | head -1) + fi + + if [[ -z "$bin_dir" ]]; then + fail "Could not find d2j-dex2jar.sh in extracted archive." + manual "Download and extract manually from https://github.com/pxb1988/dex2jar/releases" + fi + + chmod +x "$bin_dir"/*.sh 2>/dev/null || true + + mkdir -p "$HOME/.local/bin" + for script in "$bin_dir"/d2j-*.sh; do + local name + name=$(basename "$script" .sh) + ln -sf "$script" "$HOME/.local/bin/$name" + done + + export PATH="$HOME/.local/bin:$PATH" + add_to_profile 'export PATH="$HOME/.local/bin:$PATH"' + + ok "dex2jar $version installed to $install_dir" +} + +install_apktool() { + if command -v apktool &>/dev/null; then + ok "apktool already installed" + return 0 + fi + + case "$PKG_MANAGER" in + brew) info "Installing apktool via Homebrew..."; brew install apktool ;; + apt) pkg_install "apktool" ;; + *) manual "Install apktool from https://apktool.org/docs/install" ;; + esac + + if command -v apktool &>/dev/null; then + ok "apktool installed" + else + fail "apktool installation may have failed." + exit 1 + fi +} + +install_adb() { + if command -v adb &>/dev/null; then + ok "adb already installed" + return 0 + fi + + case "$PKG_MANAGER" in + brew) info "Installing adb via Homebrew..."; brew install android-platform-tools ;; + apt) pkg_install "adb" ;; + dnf) pkg_install "android-tools" ;; + pacman) pkg_install "android-tools" ;; + *) manual "Install Android SDK Platform Tools from https://developer.android.com/tools/releases/platform-tools" ;; + esac + + if command -v adb &>/dev/null; then + ok "adb installed" + else + fail "adb installation may have failed." + exit 1 + fi +} + +# ===================================================================== +# Dispatch +# ===================================================================== + +case "$DEP" in + java) install_java ;; + jadx) install_jadx ;; + vineflower|fernflower) install_vineflower ;; + dex2jar) install_dex2jar ;; + apktool) install_apktool ;; + adb) install_adb ;; + *) + echo "Error: Unknown dependency '$DEP'" >&2 + echo "Available: java, jadx, vineflower, dex2jar, apktool, adb" >&2 + exit 1 + ;; +esac