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:
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
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:
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