Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.bytejmp.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Android apps are distributed as .apk files, ZIP archives containing compiled Dalvik bytecode (.dex), resources, and the manifest. Reverse engineering converts that bytecode back to readable Java/Kotlin source to find hardcoded secrets, logic flaws, and attack surface. Primary tool: JADX, decompiles .dex → Java source directly, no intermediate smali step required.

JADX

Install

# Package manager
sudo apt install jadx

# Or latest release
wget https://github.com/skylot/jadx/releases/latest/download/jadx-<VERSION>.zip
unzip jadx-<VERSION>.zip -d ~/tools/jadx
export PATH=$PATH:~/tools/jadx/bin
Verify:
jadx --version

Decompile: CLI

Decompile APK to Java source:
jadx -d output/ target.apk
Output structure:
output/
├── resources/
│   ├── AndroidManifest.xml     # decoded manifest
│   └── res/
├── sources/
│   └── com/example/app/
│       ├── MainActivity.java
│       ├── LoginActivity.java
│       └── ...
Decompile with debug info preserved:
jadx -d output/ --show-bad-code target.apk
Export as Gradle project (importable in Android Studio):
jadx -d output/ --export-gradle target.apk

Decompile: GUI

jadx-gui target.apk
Key GUI features:
  • Tree view of all packages/classes
  • Built-in search across all decompiled source (Ctrl+Shift+F)
  • Cross-reference view, see every caller of a method
  • Inline smali view alongside Java
  • Jump to declaration (Ctrl+Click)

Hunting for Secrets

Hardcoded Strings (CLI)

After decompilation, grep the source:
# API keys / tokens
grep -rn "api_key\|apikey\|API_KEY\|token\|secret\|password\|passwd" output/sources/

# AWS
grep -rn "AKIA\|aws_access\|aws_secret" output/sources/

# URLs and endpoints
grep -rn "http\|https\|ftp" output/sources/ | grep -v "//.*import"

# Firebase
grep -rn "firebaseio.com\|firebase" output/sources/

# Private keys / certs
grep -rn "BEGIN PRIVATE\|BEGIN CERTIFICATE\|BEGIN RSA" output/sources/

strings.xml and resources

cat output/resources/res/values/strings.xml | grep -i "key\|secret\|token\|pass\|url"

BuildConfig: often contains env flags and keys

find output/ -name "BuildConfig.java" -exec cat {} \;
Example find:
public final class BuildConfig {
    public static final String APPLICATION_ID = "com.example.app";
    public static final String BUILD_TYPE = "release";
    public static final String API_KEY = "AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";  // exposed
    public static final String BASE_URL = "https://api.example.com";
}

Analyzing Authentication Logic

Find Login Activity

grep -rn "login\|Login\|authenticate\|Authenticate" output/sources/ | grep "\.java:"
Example decompiled login:
// LoginActivity.java — decompiled by JADX
public void onLoginClicked(View view) {
    String username = this.usernameField.getText().toString();
    String password = this.passwordField.getText().toString();

    // Weak: local-only check, bypassable
    if (username.equals("admin") && password.equals("1234")) {
        startActivity(new Intent(this, DashboardActivity.class));
    }
}
Client-side authentication check → bypass via Frida or patching.

Find Crypto Usage

grep -rn "SecretKeySpec\|AES\|DES\|Cipher\|KeyStore\|MessageDigest\|MD5\|SHA" output/sources/
Example weak crypto find:
// Hardcoded key — MASWE-0013
private static final String KEY = "MyHardcodedKey12";

public static byte[] encrypt(String data) {
    SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), "AES");
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");  // ECB — MASWE-0023
    cipher.init(Cipher.ENCRYPT_MODE, keySpec);
    return cipher.doFinal(data.getBytes());
}

Find Network Calls / Endpoints

grep -rn "OkHttpClient\|Retrofit\|HttpURLConnection\|volley\|baseUrl\|URL(" output/sources/
Example Retrofit config:
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/v2/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();
Map all endpoints defined in the interface:
grep -rn "@GET\|@POST\|@PUT\|@DELETE\|@PATCH" output/sources/

Find Root / Tamper Detection

grep -rn "isRooted\|RootBeer\|checkRoot\|su\b\|superuser\|Superuser" output/sources/
Example detection logic to bypass:
public boolean isRooted() {
    String[] paths = {"/system/app/Superuser.apk", "/sbin/su", "/system/bin/su"};
    for (String path : paths) {
        if (new File(path).exists()) return true;
    }
    return false;
}
Bypass: hook isRooted() with Frida to always return false.

Repackaging (Patch + Resign)

Modify decompiled smali, repack, and sign to test logic bypasses: Step 1: Decode with apktool (smali level):
apktool d target.apk -o patched/
Step 2: Edit smali (e.g. bypass root check):
# Original
invoke-virtual {v0}, Lcom/example/RootCheck;->isRooted()Z
move-result v1
if-nez v1, :root_detected

# Patched — force isRooted to return false
const/4 v1, 0x0
# if-nez v1, :root_detected   <-- commented out
Step 3: Rebuild:
apktool b patched/ -o patched.apk
Step 4: Sign:
keytool -genkey -v -keystore test.keystore -alias test -keyalg RSA -keysize 2048 -validity 10000
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore test.keystore patched.apk test
Step 5: Install:
adb install patched.apk

Workflow Summary

target.apk

    ├── jadx -d output/          → Java source + decoded manifest
    │       │
    │       ├── grep secrets      → API keys, tokens, hardcoded creds
    │       ├── grep endpoints    → REST API surface
    │       ├── grep crypto       → weak/hardcoded keys
    │       └── grep auth logic   → client-side checks to bypass

    └── apktool d                → smali for patching

            └── patch → rebuild → sign → install