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
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Pick the right tool first: jadxObservationI noticed the challenge provided a .apk file, which is an Android application archive containing Dalvik bytecode, and this format decompiles cleanly to Java using jadx, making it the most efficient entry point before reaching for heavier tools like Ghidra or apktool.For 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 2
Locate the flag in BuildConfigObservationI noticed that searching for the 'picoCTF' prefix across all decompiled sources is the most direct path to the flag, and because Android's build tools bake constants like VERSION_NAME into the auto-generated BuildConfig class at compile time, a recursive grep after decompilation reliably surfaces hard-coded strings without any runtime execution.After decompiling with apktool (or grepping the raw DEX), search for 'picoCTF' in the output. The flag is stored as the VERSION_NAME constant inside BuildConfig.smali, not in any runtime logic in MainActivity.bashapktool d timer.apkbashgrep -r 'picoCTF' timer/bash# OR: skip apktool entirely and search the raw bytecodebashstrings classes3.dex | grep picoCTFExpected output
picoCTF{t1m3r_r3v3rs3d_succ355fully_17496}What didn't work first
Tried: Unzip the APK and search the raw classes.dex with grep before running any decompiler.
grep on the binary .dex file produces no readable output because Dalvik bytecode is not plain text - string literals are encoded inside the DEX string pool. The strings command on classes.dex does surface them, but grepping the decompiled output after running apktool or jadx is more reliable and shows the full class and field context around the match.
Tried: Search for the flag only inside MainActivity.smali, assuming the countdown timer logic is where the flag is returned.
MainActivity contains the UI and countdown logic but never references the flag string. The flag is stored in BuildConfig.smali as the VERSION_NAME constant, a separate auto-generated class that the build system populates at compile time. Limiting grep to a single class file misses it entirely; running grep -r across the full decompiled output is necessary.
Learn more
Android's build system generates a
BuildConfigclass at compile time that records build-level constants such as the application ID, build type, and versionName. In this challenge the challenge author placed the flag there as the version name. After apktool decompiles the APK, the class appears as Dalvik assembly at:smali_classes3/com/example/timer/BuildConfig.smaliThe relevant section looks like this in smali:
.field public static final VERSION_NAME:Ljava/lang/String; = "picoCTF{t1m3r_r3v3rs3d_succ355fully_17496}"Because the flag is a static string constant compiled directly into the bytecode, no execution is needed. A plain
greporstringssearch finds it immediately.Why apktool here instead of jadx? Both tools work. jadx decompiles to Java and also exposes
BuildConfig.VERSION_NAME. apktool produces smali (Dalvik assembly) which is lower-level but keeps every field intact and is easier to grep. For challenges that hide the flag in build metadata rather than runtime logic, either approach reaches the same answer in one search command.When static analysis is not enough. If the flag were computed at runtime (encrypted byte arrays, reflection, native libraries), you would need dynamic analysis. That is when you switch to Frida to hook the running app and read values after they are decrypted in memory. This challenge does not require that.
Interactive tools
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
- File Magic IdentifierIdentify file types from magic numbers. Paste hex bytes or drop a file to detect PNG, JPEG, ZIP, PDF, ELF, PCAP, SQLite, and dozens of other formats.
Flag
Reveal flag
picoCTF{t1m3r_r3v3rs3d_succ355fully_17496}
No dynamic analysis required. The flag is stored as the VERSION_NAME constant in BuildConfig.smali and is recovered with a simple grep after decompiling.