Description
The TIMER Android APK hides its flag within the Java source. Reverse the application to recover the hard-coded string before the countdown completes.
Setup
Install jadx (the easiest path is your package manager; otherwise grab the latest release).
Decompile timer.apk and search the decompiled sources for picoCTF or timer-related strings.
wget https://artifacts.picoctf.net/c/449/timer.apk# Pick whichever your platform supports:sudo apt install jadx # Debian/Ubuntubrew install jadx # macOS# Or grab the newest build from the releases page (URL in the context).jadx-gui timer.apkSolution
Walk me through it- Step 1Pick the right tool first: jadxFor nearly every Android CTF the right starting tool is jadx, which decompiles Dalvik bytecode straight to Java. Reach for apktool, dex2jar, or Ghidra only if jadx fails or the app contains native .so libraries.
Learn more
APK files are ZIP archives containing compiled Android app code. The code itself is in Dalvik bytecode (inside
classes.dex), which is a compact bytecode format for Android's Dalvik/ART virtual machine. Unlike native binaries, Dalvik bytecode decompiles cleanly back to human-readable Java, making Android apps significantly easier to reverse engineer than native C/C++ code.Tool ranking when reversing an APK:
- jadx first. Highest-fidelity Java decompilation. Try it before anything else. Get it from your distro,
brew install jadx, or the jadx releases page. - apktool second. When jadx gives unreadable output, drop down to smali (Dalvik assembly).
- dex2jar third. Converts
.dexto a regular.jarfor the Java tools you already know. - Ghidra last, and only for native
.solibraries bundled inlib/.
For full Ghidra workflow when you do reach a native library, see Ghidra Reverse Engineering.
- jadx first. Highest-fidelity Java decompilation. Try it before anything else. Get it from your distro,
- Step 2Inspect MainActivity and read the flagOpen MainActivity in jadx-gui. The flag is built character by character inside onCreate, but the literal characters are right there in the decompiled Java.
Learn more
MainActivity is always the entry point of an Android app, the first Activity launched when the user opens the app, so it is the natural starting point for analysis. The decompiled code looks roughly like this:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); StringBuilder flag = new StringBuilder(); flag.append('p'); flag.append('i'); flag.append('c'); flag.append('o'); flag.append('C'); flag.append('T'); flag.append('F'); flag.append('{'); flag.append('t'); flag.append('1'); flag.append('m'); flag.append('3'); flag.append('r'); flag.append('_'); flag.append('r'); flag.append('3'); /* ... */ flag.append('}'); // flag.toString() is never logged, but the characters // are all visible in the decompiled source. } }The "hidden" flag is just per-character
append()calls. Reading them top-to-bottom and concatenating produces the picoCTF string. No execution required.When jadx is not enough: escalate to runtime analysis. If the decompiled output shows reconstruction logic but no plaintext characters (for example, characters loaded from an encrypted byte array, decrypted at runtime, then assembled), you have hit the limits of static analysis. Symptoms include:
- Strings stored as encrypted byte arrays passed through a
decrypt()helper at runtime - Reflection-based string lookup (
Class.forName(),Method.invoke()) hiding what is actually called - Native
System.loadLibrary()calls that move flag logic into a.sofile - String resources fetched only after a server check passes
That is when you switch to Frida to hook the running app and read values after they are computed in memory. Search Java methods like
String.valueOfor the app's ownbuildFlag()helper, log every argument, and the assembled flag falls out as a side effect. - Strings stored as encrypted byte arrays passed through a
Flag
picoCTF{t1m3r_r3...496}
No dynamic analysis required, jadx reveals the flag literal when you search for picoCTF.