September 19, 2026
Smali By bithowl: Chapter 11 Register Instructions
βFollow the Dataβ

By bithowl
9 min read
In the previous chapter, we learned what can live inside a register.
We saw:
v0 β int
v1 β String reference
v2/v3 β long
v4 β object
v5 β array
But there is a new question.
How does data actually move around?
Imagine you're investigating a suspicious Android application.
You find this:
String token = "SECRET_TOKEN";
Easy.
But the real application might not look like that.
Instead, Smali might show:
const-string v0, "SECRET_TOKEN"
move-object v1, v0
invoke-static {v1}, Lcom/example/Auth;->send(Ljava/lang/String;)V
Now you need to answer:
Where did the token come from?
Which register contains it?
Where does it go next?
Does it reach the network?
Does it reach an authentication decision?
This is where register instructions become extremely important.
# The Story The Warehouse Conveyor Belt
Imagine an enormous warehouse.
There are thousands of boxes.
Each box has a small number: BOX 0, BOX 1, BOX 2, BOX 3, BOX 4
These are your registers. Now imagine workers moving boxes around.
One worker says: "Take whatever is in Box 0 and put it in Box 1."
That's: move v1, v0
Another worker says: "Put this new object into Box 2."
That's: new-instance v2, Lcom/example/User;
Another says: "Take the value from this object's field and put it into Box 3."
That's: iget v3, v2, Lcom/example/User;->id:I
And another says: "Take Box 3 and put its value back into the object's field."
That's: iput v3, v2, Lcom/example/User;->id:I
So Smali instructions are basically the warehouse workers.
Registers are the boxes.
Your job as a bug hunter?
Follow the box.
# What Are Register Instructions?
Register instructions manipulate the values stored in registers.
They allow Android code to:
- copy values
- create objects
- load constants
- load strings
- retrieve fields
- modify fields
- receive method results
- move objects
- move wide values
The important idea is:
Data β Register β Instruction β Register β Method β Register
Once you understand this flow, Smali becomes much easier to read.
1. move
Let's start with the simplest instruction.
move v1, v0
Meaning: Copy the value from v0 into v1.
For example:
const v0, 0x2A
move v1, v0
Conceptually:
Before:
v0 = 42
v1 = ?
After:
v0 = 42
v1 = 42
Notice something important. move copies the value. It does not empty v0.
So:
v0 β 42
v1 β 42
Both registers now contain the value.
# move Is Your First Data-Flow Clue
Suppose you discover:
const v0, 0x1
move v1, v0
move v2, v1
move v3, v2
A beginner might see four unrelated instructions.
A bug hunter sees: 42?
Actually, here:
v0 = 1
β
v1 = 1
β
v2 = 1
β
v3 = 1
The value is travelling through registers.
This is called data flow.
2. move-object
Objects use: move-object
Example: move-object v1, v0
Meaning: Copy the object reference in v0 into v1.
For example:
const-string v0, "hello"
move-object v1, v0
Now:
v0 β "hello"
v1 β "hello"
Technically, for objects and strings, the register holds a reference, not the entire object.
That's an important distinction.
# move vs move-object
Think:
move
β
normal 32-bit value
move-object
β
object/reference
For example:
move v1, v0
might move an integer.
While: move-object v1, v0
might move:
String
User
Activity
Context
Array
Exception
and other reference types.
3. move-wide
Now we reach wide values. Remember Chapter 10? long and double use two register slots.
Example:
const-wide v0, 0x1122334455667788
The value occupies:
v0 + v1
To move it:
move-wide v2, v0
Conceptually:
Before:
v0/v1 β long value
After:
v0/v1 β long value
v2/v3 β same long value
That's why you don't use normal move for wide values.
4. move-result
This one is extremely important.
Imagine Java:
int result = calculate();
Smali might look like:
invoke-static {}, Lcom/example/Calculator;->calculate()I
move-result v0
The first instruction calls the method.
The second retrieves its result.
Think:
method()
β
result
β
v0
So:
move-result v0
means:
Take the result returned by the previous method invocation and place it in v0.
# The Important Rule
move-result normally follows an invocation.
For example:
invoke-static {}, Lcom/example/Auth;->check()Z
move-result v0
The method returns:
boolean
because:
()Z
means:
() β boolean
So now:
v0 = true/false
This becomes extremely interesting for bug hunting.
# Security Decision Example
Suppose you see:
invoke-static {p0}, Lcom/example/Auth;->isLoggedIn(Landroid/content/Context;)Z
move-result v0
if-eqz v0, :not_logged_in
Follow the data:
isLoggedIn()
β
move-result
β
v0
β
if-eqz
β
security decision
That's exactly the kind of flow a bug hunter wants to identify.
5. move-result-object
What if the method returns an object?
Java:
String token = getToken();
Smali:
invoke-static {}, Lcom/example/Auth;->getToken()Ljava/lang/String;
move-result-object v0
Here:
getToken()
β
String reference
β
v0
Because the return type is:
Ljava/lang/String;
we use:
move-result-object
# Example: Token Tracking
Imagine:
invoke-static {}, Lcom/example/Session;->getToken()Ljava/lang/String;
move-result-object v0
invoke-static {v0}, Lcom/example/Network;->send(Ljava/lang/String;)V
Your eyes should immediately notice:
getToken()
β
v0
β
send(v0)
You've discovered a potential token data flow.
That's far more useful than simply reading individual instructions.
6. move-result-wide
What if the method returns a long or double?
Java:
long timestamp = getTimestamp();
Smali:
invoke-static {}, Lcom/example/Time;->getTimestamp()J
move-result-wide v0
The J tells us:
long
Therefore:
move-result-wide
is used.
The result occupies:
v0 + v1
7. const
Now let's create values.
Example:
const v0, 0x1
This places a constant integer value into v0.
Conceptually:
v0 = 1
Another example:
const v0, 0x0
means:
v0 = 0
You will see these everywhere.
# Security Flags Hide Here
Imagine:
const v0, 0x1
followed by:
if-eqz v0, :disabled
The register contains:
1
which may represent:
enabled
true
allowed
authenticated
debug mode
feature enabled
But don't assume the meaning from 1 alone. You need to trace where the value is used.
That's an important reverse-engineering habit:
Don't guess what a value means. Follow its data flow and context.
8. const-string
This is one of the most interesting instructions for Android bug hunters.
Example:
const-string v0, "admin"
Now:
v0 β "admin"
Another:
const-string v1, "https://api.example.com"
Now:
v1 β URL
Another:
const-string v2, "Authorization"
Now:
v2 β HTTP header name
# Why Bug Hunters Love const-string
Because strings often reveal application behaviour.
Search for:
http
https
api
token
password
secret
Authorization
Bearer
admin
debug
internal
You may discover:
const-string v0, "https://api.example.com"
const-string v1, "Authorization"
const-string v2, "Bearer "
Then follow those registers.
For example:
const-string v0, "Bearer "
invoke-static {}, Lcom/example/Session;->getToken()Ljava/lang/String;
move-result-object v1
Now:
v0 β "Bearer "
v1 β token
The next instructions may combine them.
That's where the investigation gets interesting.
9. new-instance
Now let's create an object.
Java:
User user = new User();
Smali:
new-instance v0, Lcom/example/User;
Conceptually:
v0 β new User object
Usually you'll then see a constructor invocation:
new-instance v0, Lcom/example/User;
invoke-direct {v0}, Lcom/example/User;->()V
Think:
new-instance
β
object created
β
constructor
β
object initialized
# Why new-instance Matters
When reverse engineering an app, object creation tells you where important structures are born.
You may see:
new-instance v0, Lcom/example/ApiClient;
or:
new-instance v1, Lcom/example/User;
or:
new-instance v2, Ljava/net/URL;
These are clues about what the application is doing.
10. iget / iput
Now we access object fields.
Suppose Java contains:
String token = user.token;
Smali might look like:
iget-object v0, v1, Lcom/example/User;->token:Ljava/lang/String;
Break it down:
iget-object
β
read an object field
v0
β
destination register
v1
β
object register
User;->token
β
field being read
So:
v1.user.token
β
v0
# iget = Read Instance Field
Think:
Object
β
field
β
register
Example:
iget v0, v1, Lcom/example/User;->id:I
Means approximately:
v0 = v1.id;
For an object field, you'll commonly encounter the object form:
iget-object
For wide fields, corresponding wide forms are used.
# iput = Write Instance Field
Now reverse the direction.
Java:
user.token = token;
Smali:
iput-object v0, v1, Lcom/example/User;->token:Ljava/lang/String;
Think:
register
β
object field
So:
v0 β token
β
User.token
# This Is Extremely Useful for Data Tracking
Suppose:
const-string v0, "SECRET"
iput-object v0, v1, Lcom/example/User;->token:Ljava/lang/String;
You can trace:
"SECRET"
β
v0
β
User.token
Later:
iget-object v2, v1, Lcom/example/User;->token:Ljava/lang/String;
Now:
User.token
β
v2
The value has travelled:
const-string
β
v0
β
iput-object
β
User.token
β
iget-object
β
v2
That's the kind of chain you want to build during reverse engineering.
11. sget / sput
What if the field is static?
Java:
String token = Session.token;
Smali:
sget-object v0, Lcom/example/Session;->token:Ljava/lang/String;
This means:
Read a static field.
sget = Read Static Field
Think:
Static Field
β
Register
Example:
sget v0, Lcom/example/Config;->DEBUG:I
Conceptually:
v0 = Config.DEBUG;
sput = Write Static Field
Example:
sput-object v0, Lcom/example/Session;->token:Ljava/lang/String;
Conceptually:
Session.token = v0;
Now the flow becomes:
v0
β
Session.token
# Instance vs Static Fields
Remember this simple rule:
Instance field
Belongs to an object:
iget
iput
Conceptually:
user.token
Static field
Belongs to a class:
sget
sput
Conceptually:
Session.token
A simple mental model:
INSTANCE
Object
β
βββ field
STATIC
Class
β
βββ field
# The Real Skill: Follow the Data
Knowing individual instructions is useful.
But memorizing instructions isn't the real goal.
The real goal is:
# DATA-FLOW ANALYSIS
Suppose you find:
const-string v0, "https://api.example.com"
move-object v1, v0
invoke-static {v1}, Lcom/example/Network;->connect(Ljava/lang/String;)V
Don't read it as three separate lines.
Read it as:
URL
β
v0
β
v1
β
connect()
That's the mindset.
# Bug Hunter Example #1 β Password
Suppose you find:
const-string v0, "password123"
move-object v1, v0
invoke-static {v1}, Lcom/example/Auth;->login(Ljava/lang/String;)V
Your data flow is:
"password123"
β
v0
β
v1
β
login()
Now investigate:
- Is this a test credential?
- Is it production?
- Is it sent over the network?
- Is it used for authentication?
- Is it exposed in logs?
- Is it only a dummy value?
Don't immediately call every hardcoded string a vulnerability.
Trace it first.
#Bug Hunter Example #2 β API Token
Imagine:
sget-object v0, Lcom/example/Session;->token:Ljava/lang/String;
move-object v1, v0
invoke-static {v1}, Lcom/example/Api;->request(Ljava/lang/String;)V
Follow it:
Session.token
β
v0
β
v1
β
Api.request()
Now you know:
A static token field reaches an API request.
That's a meaningful lead.
# Bug Hunter Example #3 β URL
You find:
const-string v0, "https://internal.example.com"
invoke-static {v0}, Lcom/example/Api;->open(Ljava/lang/String;)V
Flow:
internal URL
β
v0
β
open()
Now investigate:
Who controls the URL?
Can it be modified?
Is it trusted?
Is it used for redirects?
Is it used for API communication?
Is certificate validation performed?
The register instruction itself isn't necessarily the vulnerability.
It is the trailhead.
# Bug Hunter Example #4 β Security Flag
Consider:
sget v0, Lcom/example/Config;->DEBUG:I
if-eqz v0, :normal
invoke-static {}, Lcom/example/Debug;->enable()V
Data flow:
Config.DEBUG
β
v0
β
condition
β
security-sensitive branch
Now investigate where:
Config.DEBUG
is written.
You might find:
const v1, 0x1
sput v1, Lcom/example/Config;->DEBUG:I
Now you've connected:
1
β
v1
β
Config.DEBUG
β
v0
β
branch
β
debug functionality
This is how a few register instructions can reveal an entire execution path.
# The Register Tracking Technique
When analysing Smali, make a tiny table.
For example:
Register
Value
v0
API URL
v1
token
v2
authentication result
v3
User object
Then update it as you read.
Example:
const-string v0, "https://api.example.com"
You write:
v0 = API URL
Then:
move-object v1, v0
Update:
v1 = API URL
Then:
invoke-static {v1}, Lcom/example/Api;->connect(Ljava/lang/String;)V
Now you know:
API URL β connect()
This simple technique becomes incredibly powerful with larger methods.
# Practice β Objects
Java:
User user = new User();
Smali:
new-instance v0, Lcom/example/User;
invoke-direct {v0}, Lcom/example/User;->()V
Register map:
v0 β User object
# Practice β Field Access
Java:
String token = user.token;
Smali:
iget-object v1, v0, Lcom/example/User;->token:Ljava/lang/String;
Register map:
v0 β User object
User.token
β
v1
So:
v1 = user.token
Practice β Method Result
Java:
boolean authenticated = checkAuth();
Smali:
invoke-static {}, Lcom/example/Auth;->checkAuth()Z
move-result v0
Register map:
v0 β authenticated
# Practice β Put Everything Together
Java:
String token = getToken();
send(token);
Smali:
invoke-static {}, Lcom/example/Auth;->getToken()Ljava/lang/String;
move-result-object v0
invoke-static {v0}, Lcom/example/Api;->send(Ljava/lang/String;)V
Data flow:
getToken()
β
v0
β
send()
This is the exact mental model you should develop.
# Quick Reference
And remember the basic direction:
move
A β B
iget
object.field β register
iput
register β object.field
sget
Class.field β register
sput
register β Class.field
move-result
method() β register
new-instance
register β new Object
# Bug Hunter Checklist
When you encounter an interesting value, ask:
1. Where was it created?
Look for:
const
const-string
new-instance
move-result
2. Which register contains it?
Example:
v0
3. Where does the register go?
Look for:
move
move-object
invoke-*
iput
sput
4. Does it enter an object field?
Look for:
iput
iput-object
5. Does it come from a field?
Look for:
iget
iget-object
sget
sget-object
6. Does it reach a method?
Look for:
invoke-*
7. Does it influence a security decision?
Look for:
if-*
if-eqz
if-nez
packed-switch
sparse-switch
Those control-flow instructions will become much more important in the next chapters.
# Quick Recap
You now know how Smali moves data.
move
moves normal values.
move-object
moves references.
move-wide
moves wide values.
move-result
captures normal method results.
move-result-object
captures object results.
move-result-wide
captures wide results.
const
creates numeric constants.
const-string
loads strings.
new-instance
creates objects.
iget / iput
read and write instance fields.
sget / sput
read and write static fields.
But the most important lesson isn't any individual instruction.
It's this: Follow the data.
# Final Thought
A Smali method can look terrifying at first.
Hundreds of registers.
Dozens of instructions.
Strange descriptors.
Labels everywhere.
But don't try to understand everything at once.
Pick one interesting value.
Maybe: password
Maybe: token
Maybe: API URL
Maybe: isAdmin
Then follow it.
Where was it created?
β
Which register holds it?
β
Where is it moved?
β
Which field stores it?
β
Which method receives it?
β
Does it influence a security decision?
That is the beginning of real Smali analysis.
Because when you're hunting bugs, you don't need to understand every box in the warehouse.
You just need to know:
Which box contains the valuable package and where that package is going.
Next chapter: Control Flow in Smali "Which Door Does the Code Take?"
BY β bithowl