APK Downloader
·11 min read

Reverse Engineering an APK: What the Tools Actually Show You (And What's Legal)

An APK is a zip file. Unpacking it is standard static analysis, not magic. The four-tool chain — apksigner, aapt2, apktool, jadx — and exactly what each step tells you about a package you downloaded.

androidapksecurityreverse-engineering

“Reverse engineering an APK” sounds like it needs a special setup. It doesn’t. An APK is a zip archive with a compiled manifest, a Dalvik bytecode file, and some resources. Everything you need ships in four command-line tools, three of which install in one brewline. The real skill is knowing which tool answers which question — and stopping as soon as you have your answer.

Core idea: Reverse engineering an APK is a read-first workflow, not an edit-first workflow. Most real-world use cases are answered by signature verification and the permission manifest, before you ever decompile a line of code.

What’s inside an APK

Rename .apk to .zip and open it:

app.apk
├── AndroidManifest.xml     # compiled binary XML — not readable as text
├── classes.dex             # Dalvik bytecode (may continue as classes2.dex, ...)
├── resources.arsc          # compiled resource index
├── res/                    # compiled layouts, drawables, values
├── lib/
│   ├── arm64-v8a/*.so      # native libraries per ABI
│   └── armeabi-v7a/*.so
├── assets/                 # raw files, not compiled
└── META-INF/               # signing material

Two things surprise people. First, AndroidManifest.xml and everything under res/ is binary XML, which is why unzipshows garbage and why apktool exists. Second, there is no Java source inside — only Dalvik bytecode, which maps to something Java-like but is not the original source.

The four-tool chain

apksigner — “is this the real app?”

apksigner verify --print-certs app.apk

The SHA-256 digest is stable across every versionthe same developer publishes. Compare it against a known-good install — pull the installed APK with adb shell pm path com.example.app then adb pull— and you have a definitive answer in ten seconds. A mismatch means the package was rebuilt and re-signed by someone else. This single step eliminates most malicious repackages.

aapt2 — “what does it claim to need?”

aapt2 dump badging app.apk | head -30
aapt2 dump permissions app.apk
aapt2 dump permissions suspect.apk > suspect.txt
aapt2 dump permissions official.apk > official.txt
diff official.txt suspect.txt

Extra permissions that matter:

PermissionWhat it enables
READ_SMS / RECEIVE_SMSSteals 2FA codes — no legitimate reason for most apps
READ_CONTACTSBulk contact exfiltration
BIND_ACCESSIBILITY_SERVICERead screen content, simulate taps, keylog — the most abused permission on Android
BIND_DEVICE_ADMINPrevent uninstall, lock the device
SYSTEM_ALERT_WINDOWOverlay attacks — draw a fake login UI over a real app
REQUEST_INSTALL_PACKAGESSilently drop and install a second payload

A downloaded “mod” showing three of those is not a mod. It is a dropper.

apktool — “what’s configured and what resources ship?”

apktool d app.apk -o app_src
grep -rn "https://" app_src/res/values/strings.xml
grep -n 'android:exported="true"' app_src/AndroidManifest.xml
grep -rn "loadLibrary\|DexClassLoader" app_src/smali

You get human-readable AndroidManifest.xml, res/ XML, and a smali/ tree. An android:exported="true" component you did not expect is worth understanding: exported components can be triggered by any other app on the device, which is real attack surface.

jadx — “how does it actually work?”

jadx -d out_java app.apk
jadx-gui app.apk   # far more usable: global search, jump to definition

jadx decompiles dex into readable Java. Not perfect Java — control flow is reconstructed and some methods fail — but good enough to follow business logic. Search by string constant to find where a URL or error message is used, then jump to definition to walk the call graph.

Case study 1: is this download tampered with?

  1. Signature. apksigner verify --print-certs and compare the digest. A mismatch means stop.
  2. Permissions. Diff against official. New SMS, accessibility, or install_packages means stop.
  3. Injected libraries. List lib/arm64-v8a/ for .so files that do not belong. Native payloads hide here because they are harder to inspect than dex.
  4. Assets. Anything file cannot identify is worth a look. Encrypted payload blobs typically appear as .dat or .bin files in the tens-to-hundreds of KB range reported as plain data, decrypted at runtime by a matching .so.

Case study 2: how does feature X work?

Search jadx for the API method name, or for a class that appears in logcatwhen you trigger the feature. “Find usages” walks you to the implementation: thresholds, debounce strategies, retry logic — the parts that usually are not in the docs.

The line here is real: learning the approach and writing your own implementation is fine. Copying decompiled code into your project is copyright infringement, and if the app ships obfuscated you are probably also violating its terms of service. Understand, then build.

Case study 3: recovering your own lost source

apktool d myapp.apk -o recovered
RecoverableNot recoverable
Layout XML, menus, animationsOriginal Java/Kotlin source (only decompiled approximations)
Drawables — icons, colors, stylesComments
strings.xml— all UI copyOriginal identifiers (obfuscated to a, b, c)
Manifest: permissions, components, intent filtersThird-party library sources
Everything under assets/Build config, Gradle files

For an unobfuscated app, jadx output is often readable enough to port business logic by hand. Budget more time than you expect: decompiled code loses type information in generic-heavy sections and needs manual cleanup.

Obfuscation vs. hardening

Obfuscation (R8 / ProGuard)

Classes and methods are renamed to short identifiers, but structure survives. Lots of single-letter packages in the output is the tell — jadx still shows full control flow, you just infer intent from strings, API calls, and data flow. It slows reading down significantly but does not stop it.

Hardening (packers)

The real dex is encrypted and decrypted in memory at runtime. Symptoms: classes.dex is suspiciously small (a few hundred KB) while the APK is 30+ MB, jadx shows only a loader stub, and native libraries are huge with names like libjiagu.so. Getting past this requires dynamic analysis — Frida hooks, a debugger attached to the running process. For most purposes the correct answer at this point is to stop and treat the app as opaque.

Legal boundaries

ActionStatus
Decompiling your own appFine
Studying an implementation, then writing your ownFine
Analyzing a suspicious APK before installingFine
Analyzing malwareFine, and useful to everyone
Removing a paywall or licence check for your own useViolates ToS; illegal in many jurisdictions
Modifying and redistributing someone’s appCopyright infringement
Publishing a modified app to an app storeClearly illegal

The tools are neutral. The verification workflow above is the same one a security researcher uses on malware — you are just pointing it at a package you downloaded.

Common questions

Can decompiled output be recompiled into a working app?

Not really. Decompilation is lossy: control flow survives, but type information, generics, and resource references do not round-trip. For modifications, patch smali and rebuild:

apktool b app_src -o modified.apk
keytool -genkey -v -keystore test.keystore -alias test -keyalg RSA -keysize 2048 -validity 10000
apksigner sign --ks test.keystore --ks-pass pass:123456 modified.apk

The resigned APK cannot install over the original because the certificate differs — that is the OS enforcing integrity, not a bug.

The short version

QuestionToolTime
Is this the genuine app?apksigner --print-certs10 sec
What does it ask for?aapt2 dump permissions5 sec
What’s configured?apktool d1 min
How does the code work?jadx-gui10+ min

Start at the top. Most questions never reach the fourth row — and the first row alone would prevent the majority of malicious sideloads. If you just need a Play-distributed APK without trusting a mirror site, use gptoapk’s download tool, which resolves through Google Play’s own distribution path.