Nouvelle réglementation de cybersécurité 2026 en vigueur au Maroc. Obtenir un audit de conformité gratuit →
← Back to Blog
Mobile Security 2026-06-08 ⏱️ 14 min

Android APK Reverse Engineering and Security Analysis

Android applications are packaged as APK files — Android Package Kits — and understanding their internals is essential for every mobile security practitioner. Whether you are a penetration tester evaluating a third-party application, a developer hardening your own product, or a security researcher hunting for vulnerabilities, mastering APK reverse engineering gives you a decisive advantage. This guide walks through the entire process: from understanding what is inside an APK, through the toolchain used to disassemble and decompile it, to the vulnerabilities that are routinely discovered, and the defenses that stop them.

1. Anatomy of an APK

An APK is simply a ZIP archive. Renaming it to .zip and unzipping it reveals the following structure:

  • classes.dex — The compiled Dalvik bytecode that runs on Android's ART runtime. Large applications may have classes2.dex, classes3.dex, etc. (multidex).
  • AndroidManifest.xml — A binary-encoded XML file declaring every component the app exposes: Activities, Services, BroadcastReceivers, and ContentProviders. It also declares required permissions and the minimum SDK version.
  • resources.arsc — A compiled resource table mapping resource IDs (integers) to their values: strings, layouts, drawables.
  • lib/ — Native shared libraries (ELF .so files) compiled for ABI targets such as armeabi-v7a, arm64-v8a, and x86_64.
  • assets/ — Raw files bundled with the app: HTML, JavaScript for WebViews, configuration files, sometimes embedded databases.
  • META-INF/ — The signing certificate (CERT.RSA), manifest digest (MANIFEST.MF), and signature file (CERT.SF). This is how Android verifies package authenticity.

Understanding this structure tells you exactly where to look: credentials tend to hide in assets/ or hardcoded in classes.dex; native code secrets live in .so files; component exposure is declared in AndroidManifest.xml.

2. The Reverse Engineering Toolchain

Modern Android reverse engineering relies on a handful of battle-tested tools that complement each other:

apktool

apktool decodes the APK into smali (Dalvik assembly) and recovers the human-readable AndroidManifest.xml and resource files. It is the first step in almost every analysis.

apktool d app.apk -o output/

The resulting output/smali/ directory contains assembly-level representations of every class. They are readable but verbose — jadx is more practical for logic analysis.

jadx

jadx decompiles the .dex bytecode directly to Java source code, making business logic legible at a glance.

jadx -d jadx_out/ app.apk

jadx also ships a GUI (jadx-gui) that provides search, cross-references, and navigation — indispensable for large codebases.

dex2jar + JD-GUI

An older but still-used alternative: dex2jar converts the .dex to a .jar file, which JD-GUI then renders as Java. It handles some obfuscation differently from jadx and can serve as a cross-check.

d2j-dex2jar app.apk -o app.jar
jd-gui app.jar

strings on native libraries

Native .so binaries often embed hardcoded secrets that Java-layer obfuscation does not reach. Running strings against them frequently surfaces API endpoints, credentials, and cryptographic keys.

strings lib/arm64-v8a/libcore.so | grep -iE "key|secret|token|pass|api"

adb logcat

Runtime log analysis captures what the application actually does during execution. Many developers leave sensitive information in debug logs that are never stripped in production builds.

adb logcat -s "MyApp" | grep -iE "token|auth|password"

3. What Penetration Testers Look For

Hardcoded Secrets

After decompiling, grep the output directory for patterns that indicate embedded credentials. Real-world findings are surprisingly common:

// Example of what pentesters find during decompilation
private static final String API_KEY = "sk-prod-1234567890abcdef";
private static final String DB_PASSWORD = "P@ssw0rd123!";
private static final String FIREBASE_SECRET = "AIzaSy...";

These strings survive decompilation intact because Java string constants compile verbatim into the .dex constant pool. Obfuscation renames classes and methods but rarely encrypts string literals by default.

Exported Components

Any component declared with android:exported="true" in the manifest and lacking a custom permission can be invoked by any installed application or adb command — no user interaction required. Pentesters enumerate these immediately:

apktool d app.apk -o out/ && grep -n 'exported="true"' out/AndroidManifest.xml

An exported Activity that handles sensitive deeplinks without validation is a direct path to authentication bypass or data theft.

Insecure Data Storage

Android provides SharedPreferences, SQLite databases, and internal/external storage. Common mistakes include storing passwords or tokens in SharedPreferences (world-readable on rooted devices), using SQLite databases with no encryption, and writing sensitive data to external storage where other apps can read it.

// Insecure: storing a session token in unencrypted SharedPreferences
SharedPreferences prefs = getSharedPreferences("auth", MODE_WORLD_READABLE);
prefs.edit().putString("token", sessionToken).apply();

Weak Cryptography

Decompilation often reveals cryptographic anti-patterns: DES or 3DES where AES-256 is appropriate; MD5 or SHA-1 for password hashing instead of bcrypt or Argon2; AES in ECB mode which leaks plaintext patterns; static or predictable initialization vectors.

Insecure Logging

Android's Log class is globally readable by any app holding READ_LOGS on older APIs. Statements like the following are a data leak:

Log.d("AuthManager", "User token: " + token);
Log.v("Payment", "Card number: " + cardNumber);

4. SSL Pinning Bypass

SSL/TLS certificate pinning prevents interception by refusing connections when the server certificate does not match an embedded public key or certificate hash. Bypassing it is a prerequisite for inspecting encrypted traffic with a proxy such as Burp Suite.

Frida-based bypass

Frida is a dynamic instrumentation toolkit that injects JavaScript into a running process, hooking any Java or native function at runtime. The standard invocation for an active analysis session:

frida -U -f com.target.app --no-pause -l bypass-ssl.js

A well-known Frida script for OkHttp3 pinning bypass hooks the CertificatePinner.check() method and makes it a no-op:

Java.perform(function() {
  var CertificatePinner = Java.use('okhttp3.CertificatePinner');
  CertificatePinner.check.overload('java.lang.String', 'java.util.List')
    .implementation = function(hostname, peerCertificates) {
      console.log('[*] SSL pinning bypassed for: ' + hostname);
      // return without throwing — pin check disabled
    };
});

Objection

Objection wraps Frida into a CLI explorer and automates common tasks like SSL pinning bypass, root detection bypass, and file system exploration:

objection -g com.target.app explore
# Then inside the objection prompt:
android sslpinning disable

5. Dynamic Analysis with Frida

Beyond SSL pinning, Frida enables full runtime manipulation of application logic. Pentesters use it to hook authentication checks, extract cryptographic keys from memory at the moment they are used, and trace sensitive data flows.

A practical example: hooking a login method to log credentials and force authentication success regardless of what the server returns:

Java.perform(function() {
  var LoginActivity = Java.use('com.example.app.LoginActivity');

  LoginActivity.checkPassword.implementation = function(pass) {
    console.log('[*] checkPassword called with: ' + pass);
    // Bypass the check — always return true
    return true;
  };

  LoginActivity.validateOtp.implementation = function(otp) {
    console.log('[*] OTP intercepted: ' + otp);
    return true;
  };
});

This pattern reveals plaintext credentials and demonstrates that authentication logic must never be trusted on the client side.

6. Key Vulnerabilities in Real-World APKs

Firebase Misconfiguration

Many applications embed Firebase configuration in google-services.json — a file that is bundled into the APK and easily extracted. If the Firebase project's Firestore or Realtime Database security rules allow unauthenticated reads, any attacker who retrieves this file gains access to user data. Pentesters routinely find databases with rules equivalent to: "read": true, "write": true.

Deeplink Hijacking via Exported Activities

If an exported Activity handles intent-filter schemes (custom deeplinks like myapp://reset-password?token=XYZ) without enforcing package verification, a malicious app can register the same scheme and intercept the deeplink, stealing password reset tokens or OAuth codes.

Tapjacking and Overlay Attacks

On older Android versions, a malicious overlay (a semi-transparent window drawn on top of a target app) can trick users into tapping UI elements they cannot see — approving permissions, submitting forms, or confirming payments. The defense is the filterTouchesWhenObscured XML attribute or setFilterTouchesWhenObscured(true) in code.

7. Defenses

ProGuard / R8 Obfuscation

Enable R8 in your release build. It shrinks, optimizes, and obfuscates the bytecode, renaming classes and methods to single-character names and removing dead code. While not impenetrable, it significantly raises the cost of analysis and prevents casual credential extraction.

Certificate Pinning

Implement pinning with OkHttp's CertificatePinner or the TrustKit-Android library. Pin to intermediate CA public key hashes (not leaf certificates) to survive cert rotation, and always include a backup pin:

CertificatePinner pinner = new CertificatePinner.Builder()
    .add("api.yourapp.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    .add("api.yourapp.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=") // backup
    .build();

Android Keystore for Cryptographic Keys

Never hardcode cryptographic keys in source code. The Android Keystore System stores keys in hardware-backed secure storage and never exposes the raw key material to the app process. Keys can be bound to require user authentication (biometric or PIN) before use.

Strip Debug Logs in Release Builds

Use ProGuard rules to remove all Log.* calls in release builds, or adopt a logging library (Timber, for example) that strips logs below a configured level in non-debug variants.

Restrict Component Exports

Every component in AndroidManifest.xml should explicitly declare android:exported="false" unless there is a deliberate reason to expose it. For components that must be exported (deeplink handlers, share targets), require a custom signature-level permission so only your own apps can invoke them without explicit user interaction.

Encrypt Local Storage

Use the Jetpack Security library (EncryptedSharedPreferences, EncryptedFile) to transparently encrypt sensitive data at rest. For databases, SQLCipher provides AES-256 encryption with minimal API changes.

Conclusion

Android APK reverse engineering is a mature discipline with a well-established toolchain and a predictable set of vulnerability classes. The same issues — hardcoded secrets, exported components, insecure storage, weak cryptography, absent pinning — appear repeatedly across applications of every size and category. Knowing how attackers approach an APK is the most direct path to building applications that withstand that analysis.

At Cayvora, our mobile security team performs thorough APK assessments covering static analysis, dynamic instrumentation, network traffic inspection, and backend API testing. If your Android application handles sensitive user data or financial transactions, a professional audit before release is not optional — it is the cost of operating responsibly.

Is Your Mobile App Secure?

Our experts perform end-to-end Android and iOS security assessments — static analysis, dynamic instrumentation, and API testing — to find what attackers would find before they do.

✉️ Request a Mobile Security Audit

Related Articles