smali_documentation - Shuriken-Group/Shuriken-Analyzer GitHub Wiki
Smali files are files with .smali
extension, and used by the Smali assembler for generating the Dalvik files. Every Smali file corresponds to a Java class. Java packages are represented with /
instead of with .
, so for example `com.example.MyClass` would be represented like com/example/MyClass.smali
.
Smali files can be divided into sections:
The header contains several pieces of information. Smali files start with the .class
directive with the name of the class in internal name representation:
.class public Lcom/example/MyClass;
The class MyClass
from the package com.example
is declared as public
access modifier. The format of the class name is using type descriptor which starts by L
and ends with ;
for objects.
After the .class
, header can include optional directives to provide additional information. Example, we can use .super
directive for the parent class:
.super Ljava/lang/Object
There are other optional directives that can appear in the header:
-
.implements
:
Used to specify that the current class implements one or more interfaces. For example, to specify more than one implemented class:
.implements Ljava/io/Serializable;
.implements Landroid/os/Parelable;
-
.source
:
Directive used to specify the name of the original source that Smali code was generated from. Information can be useful for debugging purposes. Lambda-classes would point to lambda
:
.source "MyClass.java"
-
.debug
:
Used to enable debug information for the current class. When it is present, Smali assembler will include additional metadata in the final .dex
that can be used by debuggers:
.debug 0
Class annotations are used to provide metadata information about a class. Defined using .annotation
directive followed by their descriptor and elements. Example of class annotations:
.class public Lcom/example/MyClass;
.super Ljava/lang/Object;
.annotation runtime Ljava/lang/Deprecated;
.end annotation
.annotation system Ldalvik/annotation/EnclosingClass;
value = Lcom/example/OuterClass;
.end annotation
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x0008
name = "MyClass"
outer = Lcom/example/OuterClass;
.end annotation
# class definition and methods go here
We have 3 different annotations:
-
@Deprecated
: runtime annotation indicates class is deprecated and should no longer be used. Defined using the@java/lang/Deprecated
annotation. -
@EnclosingClass
: this annotation specifies the enclosing class of the current. Defined using@dalvik/annotation/EnclosingClass
descriptor. It specifies that the enclosing class ofMyClass
isOuterClass
. -
@InnerClass
: System annotation specifies that the class is an inner class. Defined using the@dalvik/annotation/InnerClass
descriptor. It specifies that the access flags for the class are0x0008
(means it is static), name of the class isMyClass
, and the other class isOuterClass
.
We can specify for example the Signature
annotation in an interface:
.class public interface abstract Lcom/example/MyClass;
.super Ljava/lang/Object;
# add the Signature annotation to the class
.annotation system Ldalvik/annotation/Signature;
value = {
"<T:",
"Ljava/lang/Object;
">",
"Ljava/lang/Object;"
}
.end annotation
In the example, MyClass
class is defined with a type parameter T using the signature annotation. In java this would be represented like:
public interface MyClass<T> {
/// ...
}
.field
directive is used to define a field within a class. General syntax:
.field <access_flags> <name>:<type_descriptor> [ = <value> ]
Different components of the field:
-
access_flags
: Access flags specify access modifiers for the field being defined. Values:
Name | Description |
---|---|
public |
The field can be accessed from anywhere within the application. |
protected |
The field can be accessed within the same package or by a subclass. |
private |
The field can only be accessed within the same class or by a non-static subclass. |
static |
The field belongs to the class, but not to any instance of the class. |
final |
The field cannot be modified once it has been initialized. |
synthetic |
The field is generated by the compiler and not present in the original source code. |
We can use a combination of the flags using their integer values.
-
name
: name of the field, starting with a letter and can contain letters, digits, underscores, and dollar signs. -
type_descriptor
: string representation of the type of the field. -
value
: value definition used when field should be assigned directly with a value.
Example:
.field private myField:I
A method definition consists of several components like access modifiers, return type, parameter types, and implementation code. The code block contains actual assembly'like code executed when the method is called. It can contain registers, instructions, labels, and exception handlers.
.method <access_flags> <name>(<parameter_types>)<return_type>
<code_block>
.end method
Breakdown of the different parts:
-
<access_flags>
: access modifiers for the method. Same than those for fields. -
<name>
: name of the method. Two special method names<init>
and<clinit>
. -
<parameter_types>
: type descriptors of the parameters of the method. -
<return_type>
: type descriptor of return type. -
<code_block>
: instructions of the method that performs functionality. Next sections are included:- Registers: used to store temporary data and intermediate results, specified using the letter
v
and a number (e.g.v0
,v1
,v2
). - Instructions: used to perform operations on registers or objects. Each instruction is represented by a mnemonic (e.g.
move
,add
,sub
, etc) followed by its operands. Operands can be registers, constants or labels. - Labels: used to mark specific locations in the code block. Specified using a colon
:
and a name:label1
,:label2
, etc. - Exception handlers: used to handle exceptions that may occur during execution of the method. Specified using
.catch
directive, followed by type of exception that is being caught and the label of the handler.
- Registers: used to store temporary data and intermediate results, specified using the letter
Example of a method with "Hello World"
:
.method public static main([Ljava/lang/String;)V
.registers 2
sget-object v0, Ljava/lang/System;->out:Ljava/lang/PrintStream;
const-string v1, "Hello World"
invoke-virtual {v0, v1}, Ljava/lang/PrintStream;->println(Ljava/lang/String;)V
return-void
.end method
The .annotation
directive is used to define an annotation. The structure of this directive is as follows:
.[sub]annotation <visibility> <annotation_type>
[ <properties> ]
.end [sub]annotation
Annotation directive starts with the .annotation
or .subannotation
followed by the visibility and the type descriptor for the annotation. Three possible values for the annotation's visibility:
-
runtime
: Annotation is visible at runtime and can be queried using reflection. -
system
: Annotation is a system annotation and is not visible to the user. -
build
: Special treating of the annotated value.
After the visibility and the annotation type descriptor, properties are defined. These are key-value pairs, where the key is the property's name and the value is the property's value. Properties are defined using syntax key = value
. Possible to define multiple properties on separate lines within one annotation directive.
Example:
.annotation runtime Lcom/example/MyAnnotation;
name = "John Doe"
age = .subannotation runtime Lcom/example/Age;
value = 30
.end subannotation
.end annotation
Registers are 32-bit and can support any type. Long/Double is represented by two registers. Two types: basic type and reference type (object and array).
Type | Explain |
---|---|
V | void, only used for return value types |
Z | boolean |
B | byte |
S | short |
C | char |
I | int |
J | long |
F | float |
D | double |
Type | Explain |
---|---|
L | Object type (Lpackage/ObjectName; ) |
[I |
Represents one dimensional array |
[Ljava/lang/String |
An array of objects representing a String |
Smali first example:
.method public smaliExpression()V
.locals 15
.line 16
const/4 v0, 0x1 #1
.line 17
.local v0, "a":I
const-wide/high16 v1, 0x4004000000000000L # 2.5
.line 18
.local v1, "b":D
int-to-double v3, v0 // Variable 1 of int type to 1.0 of double
add-double/2addr v3, v1 // Add to double types
.line 21
.local v3, "c":D
int-to-double v5, v0
sub-double v5, v1, v5 // Substract v5 = v1 - v5
.line 24
.local v5, "d":D
int-to-double v7, v0
mul-double/2addr v7, v1 // multiplication
.line 27
.local v7, "e":D
int-to-double v9, v0
div-double v9, v1, v9 // division
.line 30
.local v9, "f":D
const/4 v11, 0x3
.line 31
.local v11, "g":I
xor-int v12, v0, v11 // XOR
// Exclusive or statement
.line 34
.local v12, "h":I
int-to-double v13, v0
cmpl-double v13, v13, v1 // Cmpl double comparison
if-lez v13, :cond_0
move v13, v0
goto :goto_0
:cond_0
move v13, v11 // Give the value of register v11 to v13
.line 35
.local v13, "i":I
:goto_0
return-void
.end method
The translations are the next:
java operators | smali operator |
---|---|
addition | add-double/2addr |
subtraction | sub-double |
multiplication | mul-double/2addr |
division | div-double |
XOR | xor-int |
Smali Example:
.method public smaliIf()V
.locals 3
.line 38
const/4 v0, 0x1
.local v0, "a":I
const/4 v1, 0x2
.line 39
.local v1, "b":I
const/4 v2, 0x0
.line 40
.local v2, "c":I
if-le v0, v1, :cond_0
.line 41
move v2, v0
.line 43
:cond_0
if-ge v0, v1, :cond_1
.line 44
move v2, v1
.line 46
:cond_1
if-lt v0, v1, :cond_2
.line 47
move v2, v0
.line 49
:cond_2
if-gt v0, v1, :cond_3
.line 50
move v2, v1
.line 52
:cond_3
if-ne v0, v1, :cond_4
.line 53
move v2, v0
.line 55
:cond_4
if-eq v0, v1, :cond_5
.line 56
move v2, v1
.line 58
:cond_5
return-void
.end method
.method public smaliWhile()V
.locals 5
.line 62
const/4 v0, 0x0
move v1, v0
.line 63
.local v1, "a":I
:goto_0
const/4 v2, 0x3
if-gt v1, v2, :cond_0 //If a > 3, jump to cond u 0
.line 64
add-int/lit8 v1, v1, 0x1 //if condition above does not exist, a increases by 1
goto :goto_0 //Loop body, continue to execute downward from goto 0
.line 68
:cond_0
const/4 v3, 0x0
.line 69
.local v3, "b":I
move v4, v3
move v3, v0
.local v3, "i":I
.local v4, "b":I
:goto_1
if-ge v3, v2, :cond_1 // i >= 3
.line 70
add-int/lit8 v4, v4, 0x1 // b++;
.line 69
add-int/lit8 v3, v3, 0x1 //i++;
goto :goto_1 //Circulation main body
.line 74
.end local v3 # "i":I
:cond_1
nop //It means empty operation, do nothing
.line 76
.local v0, "c":I
:cond_2
add-int/lit8 v0, v0, 0x1
.line 77
if-le v0, v2, :cond_2
.line 78
return-void
.end method
.method public divideNumbers(II)Ljava/lang/String;
.registers 5
.line 4
const-string v0, "Execution of divideNumbers method is complete."
:try_start_2
div-int/2addr p1, p2
.line 5
new-instance p2, Ljava/lang/StringBuilder;
invoke-direct {p2}, Ljava/lang/StringBuilder;-><init>()V
const-string v1, "The result is: "
invoke-virtual {p2, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object p2
invoke-virtual {p2, p1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object p1
invoke-virtual {p1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object p1
:try_end_16
.catch Ljava/lang/ArithmeticException; {:try_start_2 .. :try_end_16} :catch_1e
.catchall {:try_start_2 .. :try_end_16} :catchall_1c
.line 9
sget-object p2, Ljava/lang/System;->out:Ljava/io/PrintStream;
invoke-virtual {p2, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 5
return-object p1
.line 9
:catchall_1c
move-exception p1
goto :goto_27
.line 6
:catch_1e
move-exception p1
.line 7
:try_start_1f
const-string p1, "Error: Division by zero is not allowed."
:try_end_21
.catchall {:try_start_1f .. :try_end_21} :catchall_1c
.line 9
sget-object p2, Ljava/lang/System;->out:Ljava/io/PrintStream;
invoke-virtual {p2, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 7
return-object p1
.line 9
:goto_27
sget-object p2, Ljava/lang/System;->out:Ljava/io/PrintStream;
invoke-virtual {p2, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 10
throw p1
.end method