Description
The hardest Android challenge. Multiple layers of protection in droids4.apk.
Setup
Download the APK file.
wget <url>/droids4.apkSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Static analysis: decompile and map the app
ObservationThis is the hardest droids challenge, with several protection layers stacked. Start broad: jadx static analysis to map the obfuscation (name mangling, string assembly, native calls) before attacking anything specific.Start with jadx decompilation. Understand the app structure: where is the password validation? Does it involve native code, obfuscated strings, reflection, or runtime class loading?bashjadx droids4.apk -d droids4_java/bashunzip droids4.apk -d droids4_raw/bashls droids4_raw/What didn't work first
Tried: Searching getFlag() for a hardcoded string literal and finding nothing readable
The password is built at runtime by character arithmetic: each character comes from adding an integer offset to a char literal, so no plaintext 'alphabetsoup' appears in the decompiled output. Evaluate each arithmetic expression in the jadx output yourself and concatenate the results.
Tried: Trying to patch out the password check in smali and skip straight to the flag display
The flag comes only from the native cardamom() method, which runs only after the password verifies. Bypassing the check branch in smali still leaves the native call running with the wrong internal state, so no flag appears. Reconstruct the password from the character arithmetic, enter it at runtime, and let cardamom() return the real string.
Learn more
Advanced Android protections may include: ProGuard/R8 obfuscation (class/method name mangling), string encryption (strings decrypted at runtime), reflection (dynamic method calls), DexGuard (commercial obfuscator), and APK split/multi-dex.
Start by finding the entry point (MainActivity), then trace all function calls from the button click handler inward to find the validation logic.
In droids4, the
getFlag()method inFlagstaffHillbuilds a password string through character-offset arithmetic: it adds integer offsets to character literals to assemble each character at runtime. Trace this arithmetic in the decompiled Java to reconstruct the plaintext password:alphabetsoup. Once you have the password, the smali patch replaces thegetFlag()return value with a call to the nativecardamom()method defined inFlagstaffHill, which is what actually produces the flag when the correct password is entered.Step 2Analyze native libraries
ObservationUnzipping the APK reveals a lib/ directory of .so files. So part of the flag validation is native JNI code, which jadx alone cannot fully decompile.Unzip the APK and examine any .so files in the lib/ directory. Load them in Ghidra and look for the JNI entry points. The flag validation may be entirely in native code.bashls droids4_raw/lib/bashfile droids4_raw/lib/x86/*.sobashstrings droids4_raw/lib/x86/*.so | grep -i picoWhat didn't work first
Tried: Running strings on the .so and grepping for 'pico' expecting to find the flag directly
The flag is not a static string inside the native library; cardamom() computes it at runtime from values derived from the correct password. A strings scan shows JNI method names and some constants, but not the result. Supply the reconstructed password to trigger the native function and capture its return value.
Tried: Loading only the arm64-v8a .so in Ghidra and not finding the expected JNI exports
Ghidra will load the wrong architecture slice if you pick the wrong lib/ subdirectory. The APK ships separate .so files under lib/x86/, lib/armeabi-v7a/, and lib/arm64-v8a/, and the device ABI decides which one actually loads. Analyze the slice matching your runtime ABI, usually x86 on an AVD, so the signatures match what Frida shows.
Learn more
Even in obfuscated native code, string literals are often stored in plain text in the .rodata section. The
stringscommand extracts printable sequences of 4+ characters from any file, which can quickly reveal hardcoded flags or keys.Step 3Dynamic analysis with Frida
ObservationThe native cardamom() method computes the flag at runtime, so static analysis alone will never show the result. Frida can hook the live method calls and capture what it actually returns.If static analysis is insufficient, use Frida to hook key methods at runtime. Hook the final comparison or any method that receives the flag string. Use frida-trace to automatically trace all method calls.bashfrida-trace -U -f com.hellocmu.picoctf -j '*!check*'bashfrida-trace -U -f com.hellocmu.picoctf -j '*!equals*'What didn't work first
Tried: Attaching frida-trace with -I flag instead of -j to hook Java methods
frida-trace's -I flag is include-imports, which pulls in the native functions a module imports, not Java classes. Java method hooks need -j with the 'Class!method' glob syntax (and its uppercase partner -J is exclude-java-method, so passing your pattern there silently hides the very methods you wanted). Point -I at a Java class name and you get no hooks and no output, and no error either.
Tried: Spawning frida-trace with -f but seeing 'unable to find process' because frida-server is not running on the device
frida-trace in USB mode (-U) needs frida-server running as root on the device or emulator (adb shell, then su, then ./frida-server &). Without it the client cannot attach to anything. The alternative is the Frida gadget embedded in a repackaged APK, which is a separate patching workflow.
Learn more
frida-trace automatically generates hook scripts for matched methods and prints their arguments. The
-jflag matches Java methods by class and method name pattern. This is useful when you need to discover which method to hook without reading all the source first.For APK patching: use apktool to decompile, modify the smali to bypass the check or log the expected value, recompile with
apktool b, sign with a debug certificate, and install the patched APK.
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.
- 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.
- 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.
Flag
Reveal flag
picoCTF{not.particularly.silly}
Combine static analysis (jadx + Ghidra) with dynamic analysis (Frida hooks) to extract the flag through multiple protection layers.