Audience
Beginners, students, CTF players, and AppSec enthusiasts.
Scope & intent
This article documents a practical reverse-engineering attempt on a small Android game.
The goal is not to present a complete exploit, but to show how tooling, architecture, and app design affect what is realistically modifiable.
Why Reverse Engineering APKs Matters
Reverse engineering is the process of understanding what a system does without having access to its original source code.
In Android security, reverse engineering is used for:
● AppSec audits (logic flaws, exposed components)
● Malware analysis
● CTFs & crackmes
● Understanding proprietary apps
Reverse engineering isn’t limited to software. It applies to:
● Hardware
● Firmware
● Embedded devices
● Automotive systems
● Mobile applications
In this article, we focus on Android APK reverse engineering.What readers will learn by the end
Common beginner mistakes in Android reverse engineering, how app architecture (Flutter vs native) changes attack surfaces, and how to recognize dead ends early.What Is an APK?
An APK (Android Package) is the installable executable format for Android: similar to .exe on Windows.
Technically, an APK is just a ZIP archive.
If you unzip it, you’ll find:
Key Components
classes.dex
● Contains DEX bytecode executed by Android Runtime (ART)
● Primary target for reverse engineers
AndroidManifest.xml
● Defines:
○ Entry points (activities)
○ Permissions
○ Exported components
● First file attackers inspect
res/ & resources.arsc
● UI layouts, strings, images
● Hardcoded secrets often leak here
META-INF/
● App signatures & certificates
● Any modification breaks integrity unless re-signedStatic Analysis
Let’s do something interesting: reverse-engineer a small Android game and make it behave the way we want.
Spoiler: I thought this would be simple.
The plan was straightforward: grab a small Android game, reverse it, tweak a few things, and “win” at will. Easy, right?
Famous last words.
Target APK
APK: https://f-droid.org/en/packages/fr.odrevet.kingdomino_score_count/
Downloaded as: KingDomino.apk
Since this is from F-Droid, we can safely assume the app is non-obfuscated and cleanly built, perfect for a beginner reverse-engineering attempt.
a) Manifest Analysis
First stop: AndroidManifest.xml.
I unpacked the APK using apktool:
apktool d KingDomino.apk -o king
The manifest is always worth inspecting first: not because it tells you how the app works, but because it tells you what the app is allowed to do: permissions, exported components, entry points, and intent filters.
Here’s a minimal example of what we usually look for:
This immediately answers two questions:
● What capabilities does the app request?
● Where does execution start?
What this app’s manifest shows
In this case, the manifest was refreshingly boring:
● A single launcher activity
● No suspicious permissions
● No exported services or receivers doing anything shady
Here’s the relevant part (trimmed for clarity):
android:extractNativeLibs="true"
android:icon="@mipmap/ic_launcher"
android:label="Kingdomino Score">
android:name="com.example.kingdomino_score_count.MainActivity"
android:exported="true">
Nothing crazy here: just a game asking for local storage and declaring a main activity.
b) Decompiled Code (jadx)
Next step: jadx-gui.
Normally, this is where you start tracing logic from MainActivity, following method calls, hunting for score calculations or state variables.
…and that was it.
No logic. No methods. No state.
My brain hit pause.
First red flag
Scrolling through the imports and parent classes, something felt off. I kept seeing references to:
io.flutter.embedding.android.FlutterActivity
Flutter?
Wait, ...what?
This wasn’t a normal Java/Kotlin game. The Java/Kotlin code wasn’t the game at all: it was just a wrapper.
Exploring around different main files in root directories
window of signature panel
This panel shows signer name, signatures and hashes if this were a malicious apk, this is starting point to match hashes with known malwares
window of summary panel
This is a summary of what jadx GUI has found after decoding the apk. I wish I could have seen summary clearly it states the native libs which are only made by flutter application for builds.
jadx also shows kotlin-tooling-metadata, which is an easy trap to fall into.
Seeing Kotlin build metadata does not mean the app logic is written in Kotlin. Flutter apps still use:
● Gradle
● Kotlin plugins
● Android wrappers
This metadata only tells you how the Android shell was built, not where the game logic lives.
The real game logic wasn’t in Java or Kotlin at all. It was in Dart, compiled ahead-of-time into a native shared library: libapp.so. Everything I normally do: hunting strings, patching methods in Java was useless. The logic was literally hidden behind Flutter’s engine.
At that moment, I realized this was going to be a lesson in why Flutter apps are so resilient to beginner hacks. My plan to just “patch the logic” was about to hit a wall.
- Dynamic Analysis
Dart is AOT (ahead of time compilation) the game logic may not be stored anywhere in the whole decoded app, well it is but in lib/
These directories contains various architecture’s logic code for built, namely
Directory: C:\king\lib\arm64-v8a
Mode LastWriteTime Length Name
-a---- 20-12-2025 11:36 4785056 libapp.so
-a---- 20-12-2025 11:36 11277384 libflutter.so
Observe that? Its shared object file, we can't just “Ghidra” over it. (Ghidra will only show stripped native code with little semantic meaning) It's typically stripped and used in Dart AOT is compiled ahead-of-time on the phone.
What's the workaround?
- Frida (or any binary instrumentation tool)
- Hack (that works partially) We are going to talk about hack here, We have downloaded the apk from f-droid (important we can patch the Manifest to enable debugging)
Open AndroidManifest.xml add this exact line:
android:debuggable="true"
Like:
**Make sure you add this inside the application above the first activity tag. Save it
Okay pack the files and pack it, align it and sign it.
java -jar apktool_2.12.1.jar b .\king -o .\king_rebuilt.apk
.\zipalign.exe -v -p 4 .\king_rebuilt.apk .\king_aligned.apk
You may see files aligning and when successful
Verification successful.
.\apksigner.bat sign --ks .\debug.keystore --ks-pass pass:android --key-pass pass:android --out .\king_signed.apk .\king_aligned.apk
And now start adb shell
And run-as package name this can be found in manifest:
RE5C86L1:/data/user/0/fr.odrevet.kingdomino_score_count $ ls
app_flutter cache code_cache filesRE5C86L1:/data/user/0/fr.odrevet.kingdomino_score_count $
(if the game is persistent we can find the saved scores here or preferences but we can't find real time scores or anything else, we could change the scores here but this game particularly isn't persistent)
**Available in phone storage files too.
Dead end? Or ...
Well, let's talk about the supposedly dead end here,
1) Flutter apps are extremely resilient to stupid hacks like this, is this possible by frida? “Maybe, yes?”
Dart AOT literally stores the stuff of mobile’s RAM, moreover this game uses UI and canvas to score and count on the screen itself and never stores highest scores meaning this game isn't persistent at all.
This wasn’t only a Flutter problem.
2) The game itself is non-persistent. Scores are calculated and rendered at runtime, but never written to disk.
Even if this were a pure Java/Kotlin app, there would be no file, database, or preference to patch.
If the game stored high scores or coins locally, modifying them via run-as would have been trivial.
In this case, there was simply nothing to persist.
We can (in theory) find logics in library but its hard maybe usage of plugins helps but its very hard (anyways the game’s logic in this case doesn't matter the game is stupidly easy to understand after playing the game for few time its easy to internalize the logic)
We must have access to runtime storage or literal RAM (something in between the game and RAM: a middleman interpreting)
We all know this is impossible in unjailed/ non-rooted phones.
Before touching jadx, before hunting strings, before dreaming about patches: you need to know what you’re actually reversing.
Next time, I’ll check for Flutter first.
And maybe next time, I’ll bring Frida.
Stay tuned.
Written By: Akanksha Sawant
From THM Nagpur Core Team
Disclaimer
This article documents a practical reverse-engineering attempt and the conclusions drawn from it. While care has been taken to ensure technical accuracy, some interpretations may be incomplete or context-dependent. Constructive corrections or clarifications are welcome.
Top comments (0)