When to use / when not to use
- Use: obtained an Android hardened (Legu/360/Bangbang/AiJiami, etc.) App and need to restore the real DEX to continue static analysis
- Use: [[re-apk]] has identified hardening and a unpacking path needs to be chosen
- Use: unpacked output has empty method bodies / instruction extraction and needs repair
- Do not use: hardening is unconfirmed—first confirm statically with [[re-apk]]; ordinary Apps can just use jadx directly
- Do not use: strong shells mainly based on VMP virtualization—switch to [[re-anti-analysis]] ([[re-unpack-advanced]] approach)
- Do not use: need runtime hook / bypass detection (that is [[re-frida]])
Tool preparation
Dynamic unpacking is by default executed within a controlled device / emulator snapshot ([[re-analyze/platform-tips]] highest principle—default sandbox). Verify all tools before use.
frida + frida-dexdump —— main force for runtime unpacking
- frida-tools: all platforms
pip install frida-tools(Python 3.8+; venv recommended); the frida-server version must match the host and be pushed to the device (for the complete installation and anti-detection process see [[re-frida]] tool preparation) - frida-dexdump: all platforms
pip install frida-dexdump(Python 3) —— searches the DEX magic in memory at runtime and dumps it - Verify:
frida --version;frida-dexdump -h(can see the-U/-f/-dparameter) - Usage:
frida-dexdump -U -f <包名>(spawn to start unpacking),frida-dexdump -U <pid>(attach)、frida-dexdump -U -f <包名> -d(deep search, covers multiple DEX / partial extraction)
BlackDex —— static unpacking machine (no frida required, the preferred alternative for ordinary hardening)
- Install: download the APK from the GitHub release——
https://github.com/CodingGay/BlackDex/releases; 32-bit and 64-bit are two separate APKs (if the target App does not appear in the list, switch to the other architecture version) - Environment: ordinary Android phone or emulator, Android 5.0~12, no root / customized environment required
- Capability: covers three types of shells: landed loading / in-memory loading / instruction extraction; deep mode backfills method bodies for instruction extraction (experimental, may take several minutes or fail)
- Output:
hook_*.dex(obtained by hooking system APIs) andcookie_*.dex(DexFile cookie technique) - Verify: after installation it can list the target App and complete unpacking
Youpk —— active-call unpacking machine (the strongest path for extraction hardening)
- Source/ROM:
https://github.com/Youlor/Youpk——based on secondary development of Android 7.1.2_r33,only supports Google Pixel 1: after building with Android Studioflash-all.shflash the custom ROM - Usage:
adb shell "echo <包名> >> /data/local/tmp/unpacker.config"→ launch the target App to automatically perform active-call unpacking (see "unpack end" in the log) →adb pull /data/data/<包名>/unpacker - Repair:
java -jar dexfixer.jar ./unpacker ./out(accompanying DEX repair, backfills method instructions) - Verify:
adb shell ls /data/data/<包名>/unpackercan see dex/method output
jadx —— decompile after unpacking (reuse [[re-apk]])
- For cross-OS installation and verification see [[re-apk]] tool preparation; jadx supports direct analysis of multiple DEX and avoids manual merging
adb —— device interaction
- Included with Android SDK platform-tools; verify
adb --version; for emulator scenariosadb shellis enough - dexdump: bundled with the Android SDK build-tools (used in step 4 for DEX integrity checks); verify
dexdump -h
Memory dump ([[re-memdump]] approach)
- frida/objection
memory dumpor follow [[re-memdump]] to grep the DEX magic from the dumpdex\n0xxextraction—the fallback path when frida-dexdump is unavailable
Procedure
Execute in order, archive each step's artifact (DEX path + sha256) (see [[re-triage]]).
-
Packer identification (confirm before acting):
- Class-name features: jadx decompilation only shows wrapper classes—
com.stub.StubApp(ijiami),com.secneo.apkwrapper/com.bangcle.*(Bangcle), etc.; the entry Application is replaced by a wrapper class - Wrapper so features:
ls out/lib/——libjiagu.so/libprotectClass.so(360 hardening),libDexHelper.so/libexec.so(ijiami),libsecexe.so/libsecmain.so(Bangcle) - DEX size:
ls -la out/classes*.dex—the real DEX is stored encrypted (in assets/ or decrypted at runtime), and classes.dex inside the wrapper is abnormally small - Record the identified packer name to decide the path: conventional whole/ extraction hardening → this skill; virtualization-dominant → [[re-anti-analysis]]
- Class-name features: jadx decompilation only shows wrapper classes—
-
Runtime unpacking (frida-dexdump, default path):
adb shell monkey -p <包名> 1 # 先启动 App,让壳把真 dex 加载进内存 frida-dexdump -U -f <包名> # 或 spawn 起步 frida-dexdump -U <pid> # 已运行 attach frida-dexdump -U -f <包名> -d # 深度搜索(多 DEX / 部分抽取场景)Artifacts are in the current directory
<包名>/<时间戳>/*.dex。Timing: dump after the App has reached the business page ([[re-analyze/platform-tips]] key experience—dump timing); dumping right at startup only gets the packer's initial state. -
Static unpacking (BlackDex / Youpk, the alternative path when frida is detected):
- BlackDex: install the APK for the corresponding architecture → select the target App → unpack (a few seconds for ordinary packers; enable deep mode for extraction packers, takes several minutes and risks failure) → export artifacts
- Youpk (preferred for extraction hardening): flash Pixel 1 ROM → write the package-name configuration → launch the App and wait for automatic unpacking → pull
unpacker/directory →java -jar dexfixer.jar ./unpacker ./out - Cross-validation: compare artifacts from the two paths (the same method body should match); if they differ, take the more complete one
-
DEX repair and integrity check:
file out/*.dex # 应见 "Dalvik dex file version 035"(magic: dex\n035\0) dexdump out/classes.dex 2>/dev/null | head -30 jadx -d java-out out/ # 反编译验证:业务类是否齐全- Empty method bodies (CodeItem all nop / direct throw) → instruction-extraction packer did not backfill: re-unpack with dexfixer (bundled with Youpk artifacts) or BlackDex deep mode; if still failing → actively call methods to trigger execution and then dump ([[re-memdump]] approach)
- Multiple DEX: check whether classes.dex / classes2.dex / ... are complete; frida-dexdump
-ddeep search as a fallback; jadx analyzes multiple DEX directly - After repair, you must decompile and spot-check before proceeding to business analysis (see pitfall 3)
-
Post-unpacking analysis (follow the full [[re-apk]] workflow):
- manifest entry/permissions → jadx decompile business code → locate the target logic (keys/protocol/checks)
- Need to change behavior → smali patch + repackage and sign ([[re-apk]] step 4)
- Native so (packer so / JNI business so) → [[re-binary-core]] ([[re-format-elf]] + [[re-ghidra]]); the packer's own anti-debugging/verification countermeasures → [[re-anti-analysis]]
Cross-domain combination
- [[re-mobile]]: step 5 of the workflow, the hardening branch, always calls this skill (transferred after [[re-apk]] identification)
- [[re-apk]]: packer identification (step 5) and post-unpacking static analysis (manifest / jadx / smali)
- [[re-frida]]: runtime unpacking execution environment + anti-frida detection countermeasures (rename / change port / gadget)
- [[re-memdump]]: In-memory DEX extraction approach (dump timing, magic scanning, multi-DEX fallback)
- [[re-anti-analysis]]: VMP / virtualization hardening countermeasures (extraction + virtualization combined packer to strong packer workflow)
- [[re-binary-core]]: Packer so / JNI native logic analysis ([[re-format-elf]] / [[re-ghidra]])
- [[re-analyze/platform-tips]]: Default sandbox (emulator snapshot), key experience on dump timing
- This skill is referenced by the triage "Mobile App Analysis" path of [[re-analyze]] (re-mobile → re-apk identify hardening → re-mobile-pack)
Common pitfalls and traps
- VMP-level hardening (extraction + instruction virtualization) is very difficult to unpack: Symptoms — after frida-dexdump / BlackDex produce a DEX, method bodies are largely nop / empty shells, and some method logic can never be found in the dex; Cause — besides extracting instructions, the packer virtualizes key methods into the so (custom VM interpreted execution), and this part cannot be restored at the DEX layer; Countermeasures — when identified as an extraction + virtualization combination: for the extracted part use Youpk active invocation repair, for the virtualized part abandon DEX restoration and instead use dynamic [[re-frida]] hook to observe inputs and outputs, or restore the VM interpreter (high cost, mark the scope and switch to the [[re-anti-analysis]] strong packer approach)
- Anti-frida detection: Symptoms — crash on spawn, frida-dexdump cannot connect or dumps empty results; Cause — the packer detects fingerprints such as the frida-server port 27042 / path / characteristic thread (gum-js-loop); Countermeasures — rename + change port according to the anti-detection section of [[re-frida]]; if still detected → switch to BlackDex / Youpk (which do not depend on frida) or frida-gadget injection
- Incomplete DEX repair (method bodies empty shells): Symptoms — when jadx opens the unpacked DEX, half the methods have no decompiled content or directly throw; Cause — the CodeItem of the instruction-extraction packer was not backfilled (dump timing earlier than method execution / the repairer did not handle it); Countermeasures — dexfixer repair (bundled with Youpk output) or BlackDex deep mode re-unpack; trigger method execution with active invocation and then [[re-memdump]] dump; after repair do dexdump spot checks + jadx decompilation verification before proceeding to analysis
- Multi-DEX merge/loss: Symptoms — only a single classes.dex is dumped, jadx reports missing classes / nonexistent references; Cause — after hardening, multiple DEX are loaded by the packer at runtime, and the default frida-dexdump search may miss them; Countermeasures —
-ddeep search, hook ClassLoader to enumerate loaded dex ([[re-frida]] enumeration), memory dump to extract all; jadx directly loads all dex for analysis - Dump timing too early: Symptoms — the dumped DEX cannot be opened / is the packer's initial data; Cause — the real dex has not yet been decrypted and loaded; Countermeasures — wait until the App enters a business page before dumping ([[re-analyze/platform-tips]] key experience on dump timing, the opposite end from "save immediately when decrypted data is seen")
- PairIP (Google Play automatic protection) three gates: Symptoms — in JADX the key methods are only left with
VMRunner.invoke("...")empty shells, and on the emulator it exits after running for a few seconds; Cause — the three-piece hardening automatically applied on Play listing: ①code virtualization: logic is extracted into an encrypted VM blob (random-named extensionless file in assets,\x00IAPheader as decryption criterion) + native interpreterlibpairipcore.so(executeVMand other symbol names are decoded at runtime from an XOR table and cannot be found via strings); ②TEE key: the blob is AES/GCM ciphertext, the wrapping key is stored in the device TEE (AndroidKeyStore), and Play delivers it viaimportWrappedKey(SecureKeyWrapper ASN.1) — three hard preconditions: Play installation (gpdekusplit carrier), passes Play Integrity, has a Google account logged in; missing any one means the Finsky log shows FAILURE → local forgeryisEncryptionKeyPresent()is also useless; ③ runtime anti-tampering:dl_iterate_phdr/opendir scans maps and finds an injected so → deliberately feeds an illegal length to std::vector →std::__throw_length_errorabort (tombstone fingerprintlength_error was thrown in -fno-exceptions mode) → if abort is neutralized, execution flow falls to the instruction after abort, stomps memory, SIGSEGV +SIG_DFLreset——signal-layer hooks can't stop it; the root cause is on the detection side (maps doesn't show its face); countermeasure——first pass Integrity (three-green real device or PlayIntegrityFix) + clear visible injection modules (restart after Zygisk disable) - PairIP plaintext extraction: call rather than hook: symptom——hooking
VMRunnerany method (getVmByteCode/executeVM/readByteCode) makes the process self-destruct immediately; cause——PairIP watches ArtMethod integrity: a frida hook rewritingentry_point_from_quick_compiled_codehits the detection; countermeasure——reflection directly callsthe decryption function (getVmByteCodeis private static, callable via frida reflection; whether the blob name carries theassets/prefix is inconsistent across versions, try both), with zero hooks and zero ArtMethod changes you can decrypt all blob plaintext——stripping PairIP doesn't need kernel-level stealth, only dynamic tracing of the interpreter requires it - Repackaging a hardened APK directly → black screen on real device: symptom——repackaging a hardened APK via apktool, after signing and installing the real device shows a black screen (process alive, no crash), while the same APK on an emulator "looks normal"; cause——repackaging rebuilds the DEX from smali, breaking the shell's layout and self-verification assumptions; the shell can't locate/verify the encrypted payload → no decryption → all platform channel handlers in the real DEX throw MissingPluginException → uncaught exceptions tear down rendering; on the emulator the shell's verification is relaxed, "runs on emulator" is not evidence; countermeasure——before repackaging, first do hardening detection (shell runtime so in assets (
lib*sec*/lib*shell*style), dynamic DEX loader so, shell stub entry class, a single oversized classes.dex with no business strings, smali→dex round-trip splitting out multiple empty stub DEXs); if hardening is confirmed, abandon apktool repackaging; dumping memory must hit the decryption window——the shell transiently self-unloads,/proc/self/mapsyou may not even see the shell in it, too early/too late and you can't get the decrypted DEX; if the app itself also self-checks "whether the shell exists", unpacking needs to neutralize that check as well (source: reverse-skills (inliver233), MIT) - Hardened target delivery paths: unpack-and-repackage / repack-free injection / resident gadget:Phenomenon—the unpacked target runs abnormally after being directly unpacked and repackaged, or it is unclear which modification/delivery path to choose; Cause—after unpacking and repackaging, the packer artifacts are not completely cleaned up, and the packer runtime conflicts with the plaintext DEX; Countermeasure—choose according to the target state: Path a, unpack-and-repackage (high fidelity): blackdex / FART / frida-dexdump retrieve the real DEX from the live process during the decryption window and then repackage it (the packer is no longer needed); before repackaging, clear all packer artifacts (packer runtime so, packer directory, DEX loader); Path b, repackaging-free injection (do not reinstall the DEX, the packer and self-check remain completely untouched, low risk to the packer, preferred): replace libapp.so in place + use an LSPatch-like tool to inject a Java hook; note that the target may detect the injection framework itself (detection strings are common), which is an integrity signal that must be handled as well; Path c, persistent gadget (zero file changes): inject frida-gadget + a boot auto-start script to perform runtime signature adaptation and in-memory patch of libapp; note that the gadget can be detected, and runtime flipping of coupled check flags may backfire; not recommended to reverse-engineer the packer's verification logic to forcibly pass apktool repackaging—high difficulty and low payoff (Source: reverse-skills (inliver233), MIT)