Smali Isn’t as Hard as You Think
Understand what an app is really doing, before you break it
.بسم الله والصلاة والسلام على أشرف خلق الله سيدنا محمد ﷺ وعلى آله وصحبه أجمعين
As we know, the first thing we do in static analysis is disassemble the APK. You can use APKTool or any similar tool, it doesn’t matter.
It’s easy. One command, and you get this:
Eight items. Seven of them are friendly. One of them is not. 👀
That one is smali, and it’s something we all tend to see and close right away.
But here’s the secret: it looks way scarier than it really is. In this article, we’ll make it simple, and you’ll learn how to read it. Ready?
What You Get After Disassembling an APK
Before we jump into Smali, let’s quickly look at what’s in the folder. You don’t need to open everything. You just need to know what each item is, so nothing feels strange.
Old friends from the roadmap article:
AndroidManifest.xml: the app’s permissions and componentsres: layouts, strings, imagesassets: raw files bundled with the applib: native.solibraries
Helper items (nothing to worry about):
apktool.yml: info APKTool saves so it can rebuild the app lateroriginal: a copy of the original signing infounknown: files that APKTool doesn’t know how to classify
And the star of this article:
smali: the app’s code, in a form you can read
Where Does Smali Come From?
You’ll feel much more comfortable with Smali once you know where it comes from. It’s not magic, it’s just one step in a journey.
Here’s what happens when an Android app is built:
- You write the code in Java or Kotlin.
- The compiler turns it into Java bytecode (
.classfiles). - Then it’s translated into Dalvik bytecode and packed into
.dexfiles. - Finally, everything is packaged into one file: the APK.
Now, when we disassemble the APK, a tool called baksmali (used by APKTool) turns the .dex bytecode into text we can read. That text is Smali.
So Smali is not a new language you need to fear. It’s just the DEX file, written in a way humans can read.
Why Does Smali Matter?
You might be asking: “Why should I bother? Can’t I just use a decompiler?” Fair question. Here’s why Smali is worth your time:
- The original Java/Kotlin code is not inside the APK. Smali is the closest readable form of it.
- You can understand the app’s real logic, without trusting anyone’s description of it.
- Sometimes tools like
jadxfail on parts of the code. Smali is always there. - You can modify it. This is called Patching, and we’ll touch on it at the end.
What Can You Find Inside Smali?
Okay, but what’s in there for you as a hacker? A lot. Here’s what you might find:
- Hardcoded secrets (API keys, passwords, tokens)
- URLs and backend endpoints
- Root and emulator detection
- SSL pinning logic
- License and premium checks
- Login logic and validation
- Encryption keys and algorithms
- Debug code and hidden features
Exciting, right? To find these things, you just need to be able to read Smali. So let’s do that.
Your First Smali File
The best way to stop fearing something is to look at it closely. So let’s take a tiny Java class and see how it looks in Smali.
Here’s the Java code:
package com.hackthebox.myapp;public class calculate {
public static int add(int a, int b) {
return a + b;
}
}And here’s the Smali version:
.class public Lcom/hackthebox/myapp/calculate;
.super Ljava/lang/Object;
.source "calculate.java"# direct methods
.method public constructor <init>()V
.locals 0 .line 3
invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void
.end method.method public static add(II)I
.locals 0 add-int/2addr p0, p1 return p0
.end method
Looks like a lot? Let’s go through it line by line:
.class public Lcom/hackthebox/myapp/calculate;declares the public classcalculatein this package.method public constructor <init>()Vis the default constructor.method public static add(II)Iis a static method calledadd, with two integers in and one integer out.locals 0means no extra registers are neededinvoke-direct {p0}, Ljava/lang/Object;-><init>()Vcalls the parent class constructoradd-int/2addr p0, p1addsp1top0and stores the result inp0return p0returns that result
See? The whole add method is just two lines: add, then return. That’s the same as return a + b;.
Data Types
You saw weird letters like I and V in the example. These are Smali’s shortcuts for data types, and once you know them, a lot of the code starts to make sense.
Get Veen0o21’s stories in your inbox
Join Medium for free to get updates from this writer.
Primitive types:
V: void (returns nothing)Z: boolean (true or false)B: byteS: shortC: charI: intJ: longF: floatD: double
Objects and arrays:
L: an object of a class. For example,Ljava/lang/String;is a String[: an array. For example,[Iis an array of integers
J (long) and D (double) take two registers each, because they’re 64-bit. Everything else takes one.
Methods and Fields
Methods and fields are where the real logic lives. If you can read them, you can follow what the app does.
Take this line: .method public static add(II)I
addis the method name(II)means it takes two intsIafter the brackets means it returns an int
Methods are called using invoke- instructions, like invoke-virtual, invoke-static, invoke-direct, invoke-super, and invoke-interface. For e
const v0, 5
const v1, 3
invoke-static {v0, v1}, Lpackage/name/MyClass;->add(II)IThis reads like: “Put 5 and 3 in registers, then call add from MyClass with them.”
Fields:
A field is just a variable that belongs to a class. This line declares one:
.field public myString:Ljava/lang/String;It reads: a public field called myString, of type String.
Registers
Registers are the part that looks the scariest, but the idea is simple. Instead of using variables, Smali stores values in small boxes called registers. Each one is 32 bits.
const v0, 10This simply means: put the number 10 in the box called v0.
There are two kinds of registers:
vregisters are for local variables and temporary work inside a methodpregisters are for the method’s parameters
In a non-static method, p0 is always this (the object itself). Then p1, p2, and so on are the parameters. In a static method, p0 is simply the first parameter, like in our add example.
For example, take a non-static method MyMethod(IJZ)V:
p0:thisp1: the intp2andp3: the long (it takes two registers)p4: the boolean
At the top of each method, you’ll see either .locals or .registers:
.locals 5means 5 registers for local variables, not counting the parameters.registers 5means 5 registers in total, including the parameters
That’s all you need to read Smali. You don’t need to memorize anything.
Putting It All Together
Whenever you open a Smali method, you can use this simple checklist:
- Class: which class am I in? (
.class) - Method: which method is this, and what does it take and return? (
.method) - Registers: how many registers does it use? (
.localsor.registers) - Instructions: what is it doing, line by line, from top to bottom?
Let’s try it on our add method:
.method public static add(II)I
.locals 0
add-int/2addr p0, p1
return p0
.end method- It’s a static method called
add, taking two ints and returning an int - It needs no extra registers
- It adds the two numbers, and returns the result
Done. You just read Smali.
The same steps work on any method, even a big one. You just go slowly, one line at a time.
What’s Next?
Now that you can read Smali, a whole new world opens up. You can follow the app’s logic, find where the secrets and checks live, and understand what’s really happening inside.
See you in the next article, Insha’Allah.









