# APK is just a zip — unzip to inspect raw contents unzip -l challenge.apk unzip challenge.apk -d apk_raw/ # Key files: # AndroidManifest.xml — permissions, activities, package name # classes.dex — compiled Java/Kotlin bytecode # classes2.dex ... — multidex (large apps) # res/ — resources (layouts, strings, drawables) # assets/ — raw files bundled with app # lib/ — native .so libraries (arm64-v8a, x86…) # META-INF/ — signature files # Check for interesting files immediately find apk_raw/ -name "*.db" -o -name "*.sqlite" # databases find apk_raw/ -name "*.key" -o -name "*.pem" # crypto material find apk_raw/ -name "*.so" # native libs cat apk_raw/res/values/strings.xml | grep -i "flag\|secret\|key\|pass"
# strings across entire APK strings challenge.apk | grep -i "picoCTF\|flag\|secret" # strings in native libraries find apk_raw/lib/ -name "*.so" -exec strings {} \; | grep -i "flag" # Identify certificate / signer apksigner verify --verbose challenge.apk keytool -printcert -jarfile challenge.apk # File type check (may not be .apk extension) file challenge.apk
# Install # Download: https://github.com/skylot/jadx/releases # chmod +x jadx-1.x.x/bin/jadx # Decompile APK to Java source jadx -d output/ challenge.apk # Decompile with resources jadx -d output/ --show-bad-code challenge.apk # Search in decompiled code grep -r "flag" output/sources/ grep -ri "picoCTF\|secret\|password\|encrypt" output/sources/ # Output: output/sources/com/example/... (Java files) # output/resources/ (AndroidManifest, etc.) # Launch GUI jadx-gui challenge.apk
# jadx-gui keyboard shortcuts: # Ctrl+F search in current file # Ctrl+Shift+F search all (text search) # N rename variable # X show usages (cross-reference) # Ctrl+G go to line # F5 decompile / refresh # Key places to look: # MainActivity.onCreate() — app entry point # Any class named: Check, Verify, Flag, License # BuildConfig class — may have hardcoded strings # R.string — string resources # Export all strings from resources cat output/resources/res/values/strings.xml
# Install sudo apt install apktool # or: https://apktool.org/ # Decompile (to smali + resources) apktool d challenge.apk -o decompiled/ apktool d challenge.apk -o decompiled/ -f # force overwrite # Decompiled structure: # decompiled/smali/ — Dalvik assembly # decompiled/res/ — resources (decoded XML) # decompiled/AndroidManifest.xml # Search in smali grep -r "flag\|picoCTF" decompiled/smali/ grep -r "const-string" decompiled/smali/ | grep -i "key\|secret" # Recompile after patching apktool b decompiled/ -o patched.apk # Sign (required to install) keytool -genkey -v -keystore ctf.jks -keyalg RSA -keysize 2048 -validity 365 -alias ctf apksigner sign --ks ctf.jks patched.apk
# smali = Dalvik assembly (one file per class) # Common patch: make check() always return true # Find check method grep -r "checkFlag\|verify\|isCorrect" decompiled/smali/ # In .smali file, change return value: # Original: return-boolean v0 (returns variable) # Patched: const/4 v0, 0x1 (v0 = true) # return-boolean v0 # Common smali instructions: # const/4 v0, 0x1 → v0 = true (1) # const/4 v0, 0x0 → v0 = false (0) # return-boolean v0 → return bool # return-void → return nothing # const-string v0, "..." → v0 = string literal # Log to Logcat (for debugging) # invoke-static {v0}, Landroid/util/Log;->d(Ljava/lang/String;)I
# Connect / list devices adb devices adb -s emulator-5554 shell # specific device # Start emulator (AVD) emulator -avd Pixel_5_API_31 & # Install APK adb install challenge.apk adb install -r patched.apk # replace existing # File transfer adb push local_file /sdcard/ adb pull /sdcard/file ./ adb pull /data/data/com.example.app/files/ # app data (root) # Shell adb shell adb shell 'cat /data/data/com.example.app/shared_prefs/*.xml' adb shell 'ls /data/data/com.example.app/'
# Logcat — watch app output adb logcat adb logcat *:E # errors only adb logcat -s "MainActivity" # specific tag adb logcat | grep -i "flag\|picoCTF\|secret" # Clear logcat adb logcat -c # App data locations # /data/data/<package>/shared_prefs/ — SharedPreferences (XML) # /data/data/<package>/databases/ — SQLite DBs # /data/data/<package>/files/ — internal files # /sdcard/Android/data/<package>/ — external storage # Pull database adb shell 'cp /data/data/com.app/databases/app.db /sdcard/' adb pull /sdcard/app.db ./ sqlite3 app.db '.dump'
# Push frida-server to device adb push frida-server /data/local/tmp/ adb shell 'chmod +x /data/local/tmp/frida-server' adb shell '/data/local/tmp/frida-server &' # List running apps frida-ps -U # Hook Java method frida -U -f com.example.app -l hook.js # hook.js — hook checkFlag method Java.perform(() => { const Main = Java.use('com.example.app.MainActivity'); Main.checkFlag.implementation = function(flag) { console.log('Flag attempt:', flag); const result = this.checkFlag(flag); console.log('Result:', result); return true; // always succeed }; });
# Common bypass scripts (Frida) # Root detection bypass # https://codeshare.frida.re/@dzonerzy/fridantiroot/ frida -U -f com.app --codeshare dzonerzy/fridantiroot # SSL pinning bypass # https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/ frida -U -f com.app --codeshare pcipolloni/universal-android-ssl-pinning-bypass-with-frida # Objection: automated bypass framework pip install objection objection -g com.example.app explore # Inside objection shell: android sslpinning disable android root disable android hooking list classes android hooking watch class_method com.example.app.MainActivity.checkFlag --dump-args --dump-return
# 1. Strings hunt (30 seconds) strings challenge.apk | grep -i "picoCTF\|flag" unzip -p challenge.apk res/values/strings.xml | grep -i "flag\|key\|secret" # 2. Decompile with jadx, search source jadx -d out/ challenge.apk grep -ri "picoCTF\|flag\|encrypt\|decode\|base64" out/sources/ # 3. Check resources cat out/resources/res/values/strings.xml find out/resources/ -type f | xargs file | grep -v "XML\|PNG\|GIF" # unusual files # 4. Native libraries find apk_raw/lib/ -name "*.so" -exec strings {} \; | grep -i "flag" # Open .so in Ghidra or radare2 for deeper analysis # 5. Run and monitor adb logcat | grep -i "flag\|picoCTF" # 6. Hook with Frida — intercept comparisons # Hook String.equals, strcmp, all check functions # 7. Patch smali — bypass check → always return true → app reveals flag apktool d challenge.apk -o dec/ # Edit smali: change return-boolean to const/4 v0, 0x1; return-boolean v0 apktool b dec/ -o patched.apk && apksigner sign ...
strings apk | grep picoCTF
② jadx -d out/ app.apk → grep -ri flag out/sources/
③ cat out/resources/res/values/strings.xml
④ find lib/ -name "*.so" -exec strings {} \;
⑤ Frida hook check methods → dump args → flag revealed
⑥ smali patch: force return true → app shows flag