October 10, 2026
Smali By bithowl: Chapter 14 Fields
βThe Data Is There. But Who Put It There?β

By bithowl
7 min read
In the previous chapter, we learned about object operations.
We explored:
new-instance
instance-of
check-cast
We learned how objects are created, checked, and treated as specific types.But creating an object is only the beginning. Imagine an Android application creates a User object.
What happens next?
Where does the user's name go?
Where is the authentication token stored?
Where does the application keep the login status?
And when the application needs that data again, how does it retrieve it?
Consider this Java code:
User user = new User();
user.name = "bithowl";
user.isAdmin = false;
The object exists, but it also has fields that hold its data. In Smali, four important instructions help us read and write those fields:
iget
sget
iput
sput
One important detail: you'll often encounter variants such as iget-object, sget-object, iput-object, and sput-object. These handle object references, including strings.
Let's understand them.
# The Story β The Apartment With Two Kinds of Storage
Imagine an Android application is an apartment building. Each apartment represents an object.
Inside each apartment, you have storage compartments:
User Object
βββ name
βββ email
βββ token
βββ isAdmin
These compartments are called instance fields.
Every User object can have its own name, email, token, and admin status.
For example:
User A
βββ name = "Alice"
βββ isAdmin = false
User B
βββ name = "Bob"
βββ isAdmin = true
Now imagine the building also has a shared noticeboard. It belongs to the entire building, not to any single apartment. That represents a static field.
Session Class
βββ currentToken
The difference is simple:
- Instance field: belongs to an individual object.
- Static field: belongs to the class itself.
And Smali gives us different instructions to access them.
# What Is a Field?
A field is a variable declared inside a class but outside its methods.
For example:
public class User {
String name;
String token;
boolean isAdmin;
static String appVersion;
}
Here:
In Smali, fields are commonly declared like this:
.field public name:Ljava/lang/String;
.field public token:Ljava/lang/String;
.field public isAdmin:Z
.field public static appVersion:Ljava/lang/String;
Remember the descriptors from Chapter 6:
Ljava/lang/String; β String
Z β boolean
I β int
J β long
Now let's learn how to access these fields.
1. iget β Read an Instance Field
The iget family reads a field belonging to an object.
Suppose Java contains:
String name = user.name;
Smali might contain: iget-object v0, v1, Lcom/example/User;->name:Ljava/lang/String;
Let's decode it.
iget-object
β
Read an object-reference field
v0
β
Destination register
v1
β
Register containing the User object
User;->name
β
Field being read
The result:
v1.name
β
v0
In Java-style thinking:
v0 = v1.name;
The register v0 now contains the reference stored in the name field.
What about a normal integer field?
Java:
int age = user.age;
Smali:
iget v0, v1, Lcom/example/User;->age:I
Here:
- v1 contains the object reference.
- age:I identifies the integer field.
- v0 receives the field's value.
Conceptually:
user.age β v0
The instruction variant depends on the field's type.
Common examples:
iget # Single-width primitive
iget-object # Object or String reference
iget-wide # long or double
# Bug Hunter Perspective
Imagine you find:
iget-object v0, v1, Lcom/example/Session;->token:Ljava/lang/String;
You have discovered a token being read from a Session object.
Now ask:
- Where did v1 come from?
- Where is the token field populated?
- Does v0 reach an HTTP request?
- Is the token logged or exposed to another component?
- Is it stored securely and handled appropriately?
The field read is a clue. The surrounding code tells you whether there's a security issue.
2. sget β Read a Static Field
Now let's look at the shared noticeboard.
Java:
String token = Session.currentToken;
Smali:
sget-object v0, Lcom/example/Session;->currentToken:Ljava/lang/String;
Notice the difference.
There is no register holding a particular Session object.
Why?
Because currentToken is a static field.
It belongs to the class.
Conceptually:
v0 = Session.currentToken;
The data flow is:
Session.currentToken
β
v0
For an integer static field:
int debug = Config.DEBUG;
Smali might contain:
sget v0, Lcom/example/Config;->DEBUG:I
Common variants include:
sget # Single-width primitive
sget-object # Object or String reference
sget-wide # long or double
Why Is This Interesting?
Suppose you discover:
sget-object v0, Lcom/example/Session;->accessToken:Ljava/lang/String;
You now know that the application reads a static field named accessToken.
Trace where the value goes next.
For example:
sget-object v0, Lcom/example/Session;->accessToken:Ljava/lang/String;
invoke-static {v0}, Lcom/example/Api;->send(Ljava/lang/String;)V
The data flow is:
Session.accessToken
β
v0
β
Api.send()
That gives you a concrete place to investigate token handling.
But remember: a static field isn't automatically insecure. The question is how the value is protected, accessed, and used.
3. iput β Write an Instance Field
So far, we've only read data.
What if the application needs to save a value inside an object?
Java:
user.age = 25;
Smali:
const/16 v0, 0x19
iput v0, v1, Lcom/example/User;->age:I
Let's decode it.
v0
β
Value being written: 25
v1
β
Object receiving the value
User;->age
β
Destination field
The flow is:
v0 = 25
β
iput
β
user.age = 25
In Java-style thinking:
v1.age = v0;
The exact register names depend on the method. The important point is that iput writes a value into an instance field.
Writing a String Field
Java:
user.token = token;
Smali:
iput-object v0, v1, Lcom/example/User;->token:Ljava/lang/String;
Here:
- v0 contains the token reference.
- v1 contains the object reference.
- token is the field being written.
The flow:
Token in v0
β
iput-object
β
User.token
Other common variants include:
iput # Single-width primitive
iput-object # Object or String reference
iput-wide # long or double
# Bug Hunter Perspective β Find Where Sensitive Data Is Stored
Suppose you see:
invoke-static {}, Lcom/example/Auth;->getToken()Ljava/lang/String;
move-result-object v0
iput-object v0, v1, Lcom/example/User;->token:Ljava/lang/String;
Follow it:
getToken()
β
v0
β
iput-object
β
User.token
You've discovered where a returned token is assigned to a field.
our next task is to find where that field is read and whether the token is exposed, persisted insecurely, or sent to an unintended destination.
4. sput β Write a Static Field
Now let's update the shared noticeboard.
Java:
Session.currentToken = token;
Smali:
sput-object v0, Lcom/example/Session;->currentToken:Ljava/lang/String;
Conceptually:
Session.currentToken = v0;
The data flow:
v0
β
sput-object
β
Session.currentToken
For an integer static field:
Config.DEBUG = 1;
Smali could look like:
const/4 v0, 0x1
sput v0, Lcom/example/Config;->DEBUG:I
Now the value 1 is written into Config.DEBUG.
Common variants include:
sput # Single-width primitive
sput-object # Object or String reference
sput-wide # long or double
Why Does This Matter?
Imagine you find:
const-string v0, "example-token"
sput-object v0, Lcom/example/Session;->currentToken:Ljava/lang/String;
You've discovered a string being assigned to a static field.
Questions worth investigating:
- Is this a real credential or just test data?
- Who can access this field?
- Where is the value read?
- Is it cleared on logout?
- Can another execution path overwrite it?
- Does the application expose it through logs, components, or network requests?
Static storage and secure storage are not the same thing. A static field is a memory location associated with the class; it does not provide encryption or persistent storage by itself.
# Instance vs Static: The Four Instructions
Here's the simplest way to remember them.
Read data from a particular object.
iget v0, v1, Lcom/example/User;->age:I
iget v0, v1, Lcom/example/User;->age:I
Read data associated with a class.
sget v0, Lcom/example/Config;->DEBUG:I
sget v0, Lcom/example/Config;->DEBUG:I
Save a value inside a particular object.
iput v0, v1, Lcom/example/User;->age:I
iput v0, v1, Lcom/example/User;->age:I
Save a value into a class-level field.
sput v0, Lcom/example/Config;->DEBUG:I
sput v0, Lcom/example/Config;->DEBUG:I
One-line memory trick:
i = instance
s = static
get = read
put = write
Therefore:
iget β instance read
sget β static read
iput β instance write
sput β static write
# Follow the Field: A Realistic Bug Hunter Workflow
Let's put all four instructions together. Imagine an application stores a session token.
First, the application receives it:
invoke-static {}, Lcom/example/Auth;->getToken()Ljava/lang/String;
move-result-object v0
Then it saves the token:
sput-object v0, Lcom/example/Session;->currentToken:Ljava/lang/String;
Later, another method retrieves it:
sget-object v1, Lcom/example/Session;->currentToken:Ljava/lang/String;
Finally, it passes the token to a request:
invoke-static {v1}, Lcom/example/Api;->send(Ljava/lang/String;)V
The full story:
getToken()
β
v0
β
sput-object
β
Session.currentToken
β
sget-object
β
v1
β
Api.send()
This is field-based data-flow analysis.
You are connecting the places where data is created, stored, retrieved, and used. When investigating a suspected issue, you can now ask whether the token is handled safely throughout that journey.
# Practical Exercise β Trace the Fields
Study this example:
.class public Lcom/example/Session;
.super Ljava/lang/Object;
.field public static currentToken:Ljava/lang/String;
.field public username:Ljava/lang/String;
.field public age:I
Imagine a method contains:
const-string v0, "demo-token"
sput-object v0, Lcom/example/Session;->currentToken:Ljava/lang/String;
new-instance v1, Lcom/example/Session;
invoke-direct {v1}, Lcom/example/Session;->()V
const-string v2, "Alice"
iput-object v2, v1, Lcom/example/Session;->username:Ljava/lang/String;
const/16 v3, 0x19
iput v3, v1, Lcom/example/Session;->age:I
Let's decode it.
Step 1 β Create the token string
const-string v0, "demo-token"
v0 β "demo-token"
Step 2 β Store the static token
sput-object v0, Lcom/example/Session;->currentToken:Ljava/lang/String;
Session.currentToken = "demo-token"
Step 3 β Create the object
new-instance v1, Lcom/example/Session;
invoke-direct {v1}, Lcom/example/Session;->()V
v1 β Session object
Step 4 β Write the username
const-string v2, "Alice"
iput-object v2, v1, Lcom/example/Session;->username:Ljava/lang/String;
v1.username = "Alice"
Step 5 β Write the age
const/16 v3, 0x19
iput v3, v1, Lcom/example/Session;->age:I
v1.age = 25
The final state is conceptually:
Session class
βββ currentToken = "demo-token"
Session object (v1)
βββ username = "Alice"
βββ age = 25
Notice the distinction: currentToken belongs to the class, while username and age belong to the object.
# Bug Hunter Checklist
Whenever you find a field access, ask these questions:
When you see iget:
- Which object is being read?
- What data is stored in the field?
- Where did the object come from?
When you see sget:
- Which class owns the field?
- Is it configuration, session data, or a security flag?
- Where is the retrieved value used?
When you see iput:
- Where did the incoming value originate?
- Can an external input influence it?
- Which object receives the value?
When you see sput:
- Is shared state being updated?
- Can the value be overwritten unexpectedly?
- Does the change affect authentication or authorization?
And always search for both sides of the field:
WRITE β FIELD β READ β USE
A field's name can be helpful, but it isn't proof of what the application actually does. Follow the instructions and examine the surrounding control flow.
# Quick Recap
For reference types, you'll often see the corresponding -object variants. For long and double, you'll often see -wide variants.
Remember:
i = instance
s = static
get = read
put = write
# Final Thought
When you first open a Smali file, a field instruction might look like another strange line of machine code.
But now we can read:
sget-object v0, Lcom/example/Session;->currentToken:Ljava/lang/String;
and understand the story:
The class has a token.
β
The application reads it.
β
The token enters v0.
β
The next instruction determines where it goes.
That's a big step toward understanding an unfamiliar Android application.
Because when you're hunting bugs, finding where data is stored is only half the job. Finding who can write it, who can read it, and what they do with it is where the investigation gets interesting.
By β bithowl