What a packed APK contains
Packing protects the executable classes rather than the code itself. The classes.dex file in the archive is a small stub or decryptor, and the real classes sit encrypted in an asset or inside a native library. At startup the stub decrypts them and hands the bytes to the runtime through a class loader, so the code exists only in the process memory of a running app.
Everything else in the archive is still there. Resources, assets, the manifest and the native libraries are readable, and they carry a surprising amount of the API surface on their own. The unpacking work is about the classes, and the rest of the archive is where you start.
Identify the packing stage statically
Read the manifest first and note the application class, the content providers and any components that start early. A packer usually needs to run before the real application class, so an unusual entry point or a provider that initialises quickly is a strong signal.
- The DEX count in the archive shows the shape of the build, since a stub holds a small number of classes while a normal app holds thousands.
- The asset list shows large or oddly named files that hold the encrypted payload.
- The native library list shows whether a custom decryptor ships in the build or the app relies on a platform library.
- Strings in the stub reveal the cipher, the loader class name and the asset it reads.
That gives you the stage to hook without unpacking anything, which matters because the runtime step is the expensive one.
Dump the real classes at runtime
The decrypted bytes must pass through a class loader, and that is the moment to copy them. Instrument the constructors used to hand bytes to the runtime, along with the method that defines a class from a byte array, and write the input to a file. Hooking the file read that supplies the asset catches payloads that are decrypted in native code before they reach the loader.
When instrumentation is blocked by detection, the alternatives are memory based. Dump the process memory and search for the structure each class file begins with, then carve the entries out. Another approach copies the application's private data directory after it has started, because some packers leave decrypted copies in a cache file.
Detection raises the cost of this stage rather than closing it. Hooks installed before the checks run, or a modified runtime, usually get the payload out, and the effort is bounded because the decryption happens once per process start.
Rebuild a readable APK
Once you have the dumped classes, assemble them into a file the decompiler can read. Replace the stub with the dumped set, then reopen the archive in a decompiler and confirm that the real package names appear. Keep the original archive as a reference, because the rebuilt one may not run: signature checks, resource references and the loader itself can all depend on the original layout.
A rebuilt archive is for reading rather than for shipping. Its purpose is to give you named classes, method signatures and string references in a tool that can search them.
What remains after unpacking
Unpacking reveals the build as the developer compiled it, and most release builds are obfuscated. Renaming has removed class and method names, string encryption hides the literals, and reflection hides which methods are called from where. None of that is caused by the packer, and all of it survives the dump.
Work from behaviour at this point. Strings recovered after decryption give the endpoint and header names, and captured traffic shows what a real request looks like. Where a value is computed, read the method that produces it, and where a call target is chosen at runtime, resolve it by observing the app rather than by reading the source.
Which parts of the API work packing blocks
Packing rarely blocks the endpoint inventory. Captures give paths and payloads, and strings recovered from the decrypted classes fill in the rest, so the shape of the API is available either way.
It blocks the work that needs the algorithm. When a signature is computed in the packed classes, nothing can reproduce it until those classes are readable, which makes unpacking a prerequisite rather than an optional step. SReverse works on protected builds and carries the recovered logic into a callable client. If the runtime checks are the obstacle rather than the packing, Frida detection analysis covers how they work, and Android obfuscation reverse engineering covers what is left after the dump.
Related work
Reviewed 28 September 2026 · SReverse research desk