Taking an Android app apart
An APK is a zip, and most of what a mobile challenge turns on is in metadata rather than in code. The manifest rules that decide the attack surface, how to navigate DEX without a decompiler, and when to stop reading and start hooking.
Almost every Android challenge is one of three things: a flag sitting in a resource, a check written in Java that you have to read, or a check written in native code that you have to hook. Working out which one you have takes about two minutes, and doing it in the wrong order costs an hour.
It is a zip, so start with the listing
Unzip it before anything else - an APK is a ZIP, with everything that implies about what else can be hiding in the archive. The file list alone tells you a great deal: how many classes*.dex files there are (multidex means a large app, probably a real one with a library in it), whether there is a lib/ directory (native code, so the interesting logic may not be in Java at all), and what is in assets/ and res/raw/ - which is where a surprising number of challenges simply put the flag.
unzip -l app.apk | sort -k1 -n -r | head -20
# large entries first: the dex, the native libs, and anything unexpected
# Anything in assets that is not what its extension claims
file assets/*The manifest, and the export rule people get wrong
AndroidManifest.xml inside an APK is not XML - it is Android's binary chunk format, and a text editor shows you noise. Decoded, it is the single most informative file in the package, because the *attack surface* of an Android app is defined there and nowhere else.
- `android:debuggable="true"` - you can attach a debugger or Frida without root. Check this before writing any exploit; it frequently makes the rest of the challenge trivial.
- `android:allowBackup` defaults to true when absent, which is the opposite default to
exported. App data can be pulled withadb backupunless it was deliberately turned off. - An exported component with no `android:permission` is reachable by any other app on the device. Count those, and you have measured the surface.
- `android:usesCleartextTraffic` - not declared means permitted below targetSdk 28 and blocked at or above it, which decides whether an intercepting proxy will see anything.
Navigating DEX without decompiling it
Full Java decompilation is lovely when it works and frequently does not - obfuscated names, synthetic methods, and Kotlin's generated machinery all produce output that is harder to read than the smali. The good news is that you rarely need it, because the questions a mobile challenge asks are navigational rather than semantic.
- Where is this string used? A cross-reference from a string constant to its load site lands you in the method that checks it. This is the same reflex as
stringsplus xrefs on a native binary. - Who calls this method? Working backwards from a crypto call or a native method until you reach something the manifest exports gives you the whole reachable path.
- Which method matters? Rank by call sites. The catch is that the method nothing calls is often the entry point - Android calls it, not the app - so a zero-caller method with a large body is a candidate rather than dead code.
Obfuscated apps make names useless but not the structure. a.a.a() still calls javax.crypto.Cipher.doFinal, and the framework calls are the ones that cannot be renamed. Search for what the app must use rather than for what it chose to call things.
When the answer is not in Java
A lib/arm64-v8a/libnative.so and a System.loadLibrary call means the check has been pushed into C, usually precisely so you have to work for it. That is a normal ELF and everything you know about reversing one applies - with one Android-specific hint: JNI functions are named Java_com_example_Class_method, so the export table tells you which Java method each native function backs.
The signing block is sometimes the challenge
APK signing has three schemes, and which ones are present tells you what repackaging would take. v1 is a JAR signature over individual entries and does not cover the archive structure, so a v1-only APK can be modified in ways verification does not notice. v2 and v3 sign the whole file, which is what makes tampering detectable - and a challenge about repackaging is a challenge about exactly that distinction.
The order, condensed
- Unzip and read the file list. Check
assets/,res/raw/, and the number of dex files. - Decode the manifest. Find the exported components and the three flags.
- Grep the strings. Cross-reference anything interesting to its load site.
- Follow calls backwards from the interesting method until you reach an exported entry point.
- If the logic is native, treat the
.soas an ELF and use the JNI naming to map it back. - If it is debuggable and you have a device, hook it rather than reading it.