Sometimes you need to modify an application’s code to access hidden information, bypass checks, or understand obfuscated logic. This page covers the complete workflow: decompile → modify → recompile → sign.
APK Decompilers
jadx The recommended choice. Decompiles DEX to readable Java. jadx app.apk # CLI decompilation
jadx app.apk -d ./output --no-res # No resources
jadx-gui # Launch GUI
JD-Gui Pioneering GUI Java decompiler. Open the APK directly in JD-Gui to inspect code.
Bytecode-Viewer Analyze using multiple decompilers simultaneously for cross-verification.
GDA Windows-only with extensive Android reverse engineering features.
CFR Handles modern Java features well. java -jar ./cfr.jar "app.jar" --outputdir "output/"
java -Xmx4G -jar ./cfr.jar "app.jar" --outputdir "output/"
frida-DEXdump Dumps the DEX of a running APK from memory — bypasses static obfuscation removed at runtime.
Fastest workflow: Use Visual Studio Code with the APKLab extension to automatically decompile, modify, recompile, sign, and install without running any commands. Also useful: apk.sh .
The Smali Workflow
This gives you Smali code and resources. Key files to inspect:
res/values/strings.xml (and all XMLs under res/values/*)
AndroidManifest.xml
Any .sqlite or .db files
If apktool has trouble decoding, try apktool d APP.apk -r (skip resource decoding).
Step 2 — Modify Smali Code
Smali is the human-readable representation of Dalvik bytecode. Reference for opcodes: http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
Hello World Example
Java source:
public static void printHelloWorld () {
System . out . println ( "Hello World" )
}
Equivalent Smali:
.method public static printHelloWorld()V
.registers 2
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello World"
invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
return-void
.end method
Common Light Modifications
# Modify constant values
const v9, 0xf4240
const/4 v8, 0x1
const-string v5, "wins"
# Math operations
add-int/lit8 v0, v2, 0x1 # v2 + 1, store in v0
mul-int v0,v2,0x2 # v2 * 2, store in v0
# Move values
move v1,v2
# Conditionals
if-ge # Greater or equals
if-le # Less or equals
if-eq # Equals
# Get/set object attributes
iget v0, p0, Lcom/example/GameActivity;->score:I # this.score → v0
iput v0, p0, Lcom/example/GameActivity;->score:I # v0 → this.score
# Jumps
:goto_6
if-ne v0, v9, :goto_6
goto :goto_6
Adding Log Output
# Log "wins: <number>"
iget v5, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I
invoke-static {v5}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
move-result-object v1
const-string v5, "wins"
invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
Injecting System.loadLibrary() Early
To preload a native library in a static initializer:
.class public Lcom/example/App;
.super Landroid/app/Application;
.method static constructor <clinit>()V
.registers 1
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
return-void
.end method
Alternatively in Application.onCreate():
.method public onCreate()V
.locals 1
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
invoke-super {p0}, Landroid/app/Application;->onCreate()V
return-void
.end method
Step 3 — Recompile
# Run from the folder generated by apktool d
apktool b .
# Output appears in ./dist/
Step 4 — Sign the APK
# Generate a signing key
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 \
-validity 10000 -alias myalias
# Sign with jarsigner
jarsigner -keystore key.jks path/to/dist/app.apk myalias
# Align with zipalign
zipalign -v 4 infile.apk outfile.apk
# Or sign with apksigner (after zipalign)
apksigner sign --ks key.jks ./dist/mycompiled.apk
Sign either with jarsigner (before zipalign) or with apksigner (after zipalign) — not both. Using both will break the signature.
Understanding Dalvik / ART
Android apps are written in Java or Kotlin and compiled to Dalvik Executable (DEX) bytecode
The Android Runtime (ART) uses Ahead-of-Time (AOT) compilation of DEX to native code
Smali is the assembly language for DEX — the human-readable form
baksmali disassembles DEX to Smali; smali assembles Smali back to DEX
References