Skip to main content

Triage an Android package

Triage answers three questions before expensive work begins: what container is this, where is the behavior, and what may interfere with inspection?

1. Preserve and identify the target​

Record the source, filename, size, and cryptographic hash. Work on a copy.

file sample.apk
sha256sum sample.apk
unzip -t sample.apk

Do not trust the extension. APK, XAPK, APKS, and APKM files are ZIP-based, while an AAB is a publishing bundle rather than a directly installable package.

2. Classify the container​

FormatTypical contentsFirst action
.apkOne installable packageInventory directly
.aabBundle modules and configurationUse bundletool to produce an APK set
.apksAPK set generated by bundletoolUnzip; identify base*.apk and target splits
.xapk / .apkmVendor bundle of base and split APKsUnzip; inspect metadata and base APK
Split APK directoryBase plus ABI/density/language splitsKeep relevant splits with the base
unzip -l sample.apk | sed -n '1,120p'

Look for:

  • classes.dex, classes2.dex, … — DEX count and scale;
  • lib/<abi>/*.so — native code and supported architectures;
  • assets/ — framework bundles, models, configuration, encrypted payloads;
  • res/ and resources.arsc — Android resources;
  • META-INF/ and signing blocks — packaging/signature context.

3. Read package metadata​

Decode or inspect the manifest:

apkanalyzer manifest print sample.apk > manifest.xml
# or
apktool d -f sample.apk -o output/apktool

Capture:

  • package ID, version name, and version code;
  • minSdkVersion and targetSdkVersion;
  • custom Application class;
  • launch activity;
  • permissions;
  • exported activities, services, receivers, and providers;
  • network security configuration and backup/debuggable flags.
Exported does not automatically mean vulnerable

Manifest exposure is a lead. Confirm permission gates, intent validation, caller checks, and reachable behavior before reporting a security issue.

4. Fingerprint compiler and protection​

apkid sample.apk

Treat fingerprints as signals, not verdicts. Corroborate with file structure and code:

  • Kotlin metadata and coroutine classes suggest Kotlin;
  • short identifiers and aggressive inlining suggest R8/ProGuard;
  • encrypted constants and reflection may indicate commercial obfuscation;
  • a tiny loader DEX plus large native libraries may indicate a packer;
  • known framework artifacts can make DEX secondary.

5. Locate the implementation layer​

High-value framework clues​

ArtifactRoute
lib/*/libapp.so, libflutter.so, assets/flutter_assets/Flutter
assets/index.android.bundle, Hermes magic/header, libhermes.soReact Native/Hermes
assemblies/*.dll, Mono runtime librariesXamarin/.NET
assets/bin/Data/Managed/*.dllUnity Mono
libil2cpp.so and global-metadata.datUnity IL2CPP
large application-specific .so with JNI bridge methodsNative/JNI

See Managed framework payloads for branch-specific tools.

6. Choose primary and verification tools​

For conventional Android, use two views with different strengths:

apktool d sample.apk -o output/apktool
jadx -d output/jadx sample.apk
  • apktool gives resources, a decoded manifest, and smali close to DEX semantics.
  • jadx gives readable Java/Kotlin-like output for navigation and search.

Add APKiD's report and raw archive inventory. If critical logic is confusing, use smali and a second decompiler before reaching a conclusion.

7. Define the next question​

Triage is complete when you can write:

Target: package/version/hash
Container: APK, bundle, or split set
Main layer: DEX, native, JavaScript, managed assembly, or mixed
Protection: none, optimizer, obfuscator, packer, unknown
Primary tool: selected tool and why
Verifier: independent representation or runtime observation
Goal: bounded behavior to investigate next

Do not decompile every artifact merely because it exists. Follow the shortest route to the authorized question.