This is a post for Android hackers to try to do reverse engineering with Android applications (APK). The contents will help to understand how Dalvik bytecode instructions (in Android) look like, by showing a series of examples. Each consists of a triple, a piece of Java code, Java bytecode instructions, and Dalvik bytecode instructions, which are all the same meaning.
8. Array
8. Array
//
Java
void
createBuffer()
{
int
buffer[];
int
bufsz =
100;
int
value = 12;
buffer = new int[bufsz];
buffer[10] = value;
value = buffer[11];
}
//
Java Bytecode
Method
void createBuffer()
0 bipush
100 // Push int
constant 100 (bufsz)
2 istore_2 // Store bufsz
in local variable 2
3 bipush
12 // Push int
constant 12 (value)
5 istore_3 // Store value in local variable 3
6 iload_2 // Push bufsz...
7 newarray
int // ...and create new array of int
of that length
9 astore_1 // Store new array in buffer
10
aload_1 //
Push buffer
11
bipush
10 // Push int
constant 10
13
iload_3 //
Push value
14
iastore // Store value at buffer[10]
15
aload_1 //
Push buffer
16
bipush
11 // Push int
constant 11
18
iaload // Push value at buffer[11]...
19
istore_3 //
...and store it in value
20 return
//
DEX Bytecode
.method
createBuffer()V
.registers 5
.prologue
.line 613
const/16
v1, 0x64
.line 614
.local v1, bufsz:I
const/16
v2, 0xc
.line 615
.local v2, value:I
new-array v0, v1, [I
.line 616
.local v0, buffer:[I
const/16
v3, 0xa
aput
v2, v0, v3
.line 617
const/16
v3, 0xb
aget
v2, v0, v3
.line 618
return-void
.end
method
//
Java
void
createThreadArray()
{
Thread threads[];
int
count = 10;
threads = new Thread[count];
threads[0] = new Thread();
}
//
Java Bytecode
Method
void createThreadArray()
0 bipush
10 // Push int
constant 10
2 istore_2 // Initialize count to that
3 iload_2 // Push count, used by anewarray
4 anewarray
class #1 // Create new array of class
Thread
7 astore_1 // Store new array in threads
8 aload_1 // Push value of threads
9 iconst_0 // Push int
constant 0
10
new #1 //
Create instance of class Thread
13
dup //
Make duplicate reference...
14
invokespecial
#5 // ...to pass to instance
initialization method
// Method java.lang.Thread.<init>()V
17
aastore // Store new Thread in array at 0
18
return
//
DEX Bytecode
.method
createThreadArray()V
.registers 5
.prologue
.line 647
const/16
v0, 0xa
.line 648
.local v0, count:I
new-array v1, v0, [Ljava/lang/Thread;
.line 649
.local v1, threads:[Ljava/lang/Thread;
const/4
v2, 0x0
new-instance v3, Ljava/lang/Thread;
invoke-direct {v3}, Ljava/lang/Thread;-><init>()V
aput-object
v3, v1, v2
.line 650
return-void
.end
method
//
Java
int[][][]
create3DArray() {
int
grid[][][];
grid = new int[10][5][];
return grid;
}
//
Java Bytecode
Method
int
create3DArray()[][][]
0 bipush
10 // Push int
10 (dimension one)
2 iconst_5 // Push int
5 (dimension two)
3 multianewarray
#1 dim #2 // Class [[[I, a three
// dimensional int
array;
// only create first two
// dimensions
7 astore_1 // Store new array...
8 aload_1 // ...then prepare to return it
9 areturn
//
DEX Bytecode
.method
create3DArray()[[[I
.registers 4
.prologue
.line 676
const/16
v1, 0xa
const/4
v2, 0x5
filled-new-array {v1, v2}, [I
move-result-object v1
const-class
v2, [I
invoke-static {v2, v1}, Ljava/lang/reflect/Array;->newInstance(Ljava/lang/Class;[I)Ljava/lang/Object;
move-result-object v0
check-cast v0, [[[I
.line 677
.local v0, grid:[[[I
return-object v0
.end
method
9. Switch
//
Java
int
chooseNear(int
i) {
switch (i)
{
case 0:
return 0;
case 1:
return 1;
case 2:
return 2;
default: return -1;
}
}
//
Java Bytecode
Method
int chooseNear(int)
0 iload_1 // Push local variable 1 (argument i)
1 tableswitch
0 to 2: // Valid indices are 0 through 2
0: 28 //
If i is
0, continue at 28
1: 30 //
If i is
1, continue at 30
2: 32 //
If i is
2, continue at 32
default:34 //
Otherwise, continue at 34
28
iconst_0 //
i was
0; push int
constant 0...
29
ireturn // ...and return it
30
iconst_1 //
i was
1; push int
constant 1...
31
ireturn // ...and return it
32
iconst_2 //
i was
2; push int
constant 2...
33
ireturn // ...and return it
34
iconst_m1 // otherwise push int
constant -1...
35
ireturn // ...and return it
//
DEX Bytecode
.method
chooseNear(I)I
.registers 3
.parameter "i"
.prologue
.line 707
packed-switch p1, :pswitch_data_c
.line 711
const/4
v0, -0x1
:goto_4
return v0
.line 708
:pswitch_5
const/4
v0, 0x0
goto
:goto_4
.line 709
:pswitch_7
const/4
v0, 0x1
goto
:goto_4
.line 710
:pswitch_9
const/4
v0, 0x2
goto
:goto_4
.line 707
nop
:pswitch_data_c
.packed-switch 0x0
:pswitch_5
:pswitch_7
:pswitch_9
.end packed-switch
.end
method
//
Java (sparse switch)
int
chooseFar(int
i) {
switch (i)
{
case -100: return -1;
case 0: return 0;
case 100:
return 1;
default:
return -1;
}
}
//
Java Bytecode
Method
int chooseFar(int)
0 iload_1
1 lookupswitch
3:
-100: 36
0: 38
100: 40
default:42
36
iconst_m1
37
ireturn
38
iconst_0
39
ireturn
40
iconst_1
41 ireturn
42
iconst_m1
43
ireturn
//
DEX Bytecode
.method
chooseFar(I)I
.registers 3
.parameter "i"
.prologue
const/4
v0, -0x1
.line 740
sparse-switch p1, :sswitch_data_a
.line 744
:goto_4
:sswitch_4
return v0
.line 742
:sswitch_5
const/4
v0, 0x0
goto
:goto_4
.line 743
:sswitch_7
const/4
v0, 0x1
goto
:goto_4
.line 740
nop
:sswitch_data_a
.sparse-switch
-0x64 -> :sswitch_4
0x0 -> :sswitch_5
0x64 -> :sswitch_7
.end sparse-switch
.end
method
10. Operand Stack
//
Java
public
long nextIndex()
{
return index++;
}
private
long index = 0;
//
Java Bytecode
Method
long nextIndex()
0 aload_0 // Push this
1 dup // Make a copy of it
2 getfield
#4 // One of the copies of this is
consumed
// pushing long field index,
// above the original this
5 dup2_x1 // The long on top of the operand stack is
// inserted into the operand stack below the
// original this
6 lconst_1 // Push long constant 1
7 ladd // The index value is incremented...
8 putfield
#4 // ...and the result stored back in
the field
11
lreturn // The original value of index is left on
// top of the operand stack, ready to be
returned
//
DEX Bytecode
.method
public nextIndex()J
.registers 5
.prologue
.line 778
iget-wide
v0, p0, Lcom/java/test/Section7_11_1;->index:J
const-wide/16
v2, 0x1
add-long/2addr v2, v0
iput-wide
v2, p0, Lcom/java/test/Section7_11_1;->index:J
return-wide v0
.end
method
11. Exception
//
Java
void
cantBeZero(int
i)
throws TestExc
{
if (i
== 0) {
throw new TestExc();
}
}
//
Java Bytecode
Method
void cantBeZero(int)
// 0 iload_1 // Push argument 1 (i)
// 1 ifne
12 // If i==0,
allocate instance and throw
// 4 new
#1 // Create instance of TestExc
// 7 dup // One reference goes to the constructor
// 8 invokespecial
#7 // Method TestExc.<init>()V
// 11 athrow // Second reference is thrown
// 12 return // Never get here if we threw TestExc
//
DEX Bytecode
.method
cantBeZero(I)V
.registers 3
.parameter "i"
.annotation system Ldalvik/annotation/Throws;
value = {
Lcom/java/test/TestExc;
}
.end annotation
.prologue
.line 811
if-nez
p1, :cond_8
.line 812
new-instance v0, Lcom/java/test/TestExc;
invoke-direct {v0}, Lcom/java/test/TestExc;-><init>()V
throw v0
.line 814
:cond_8
return-void
.end
method
//
Java
void
catchOne()
{
try {
tryItOut();
} catch (TestExc
e) {
handleExc(e);
}
}
void
tryItOut()
throws TestExc
{
throw new TestExc();
}
void
handleExc(TestExc
e) { }
//
Java Bytecode
Method
void catchOne()
0 aload_0 // Beginning of try block
1 invokevirtual
#6 // Method Example.tryItOut()V
4 return // End of try block; normal return
5 astore_1 // Store thrown value in local variable 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual
#5 // Invoke handler method:
// Example.handleExc(LTestExc;)V
11
return //
Return after handling TestExc
//
DEX Bytecode
.method
catchOne()V
.registers 2
.prologue
.line 834
:try_start_0
invoke-virtual {p0}, Lcom/java/test/Section7_12_2;->tryItOut()V
:try_end_3
.catch Lcom/java/test/TestExc;
{:try_start_0 .. :try_end_3} :catch_4
.line 838
:goto_3
return-void
.line 835
:catch_4
move-exception v0
.line 836
.local v0, e:Lcom/java/test/TestExc;
invoke-virtual {p0, v0}, Lcom/java/test/Section7_12_2;->handleExc(Lcom/java/test/TestExc;)V
goto
:goto_3
.end
method
//
Java
void
catchTwo()
{
try {
tryItOut();
} catch (TestExc1 e) {
handleExc(e);
} catch (TestExc2 e) {
handleExc(e);
}
}
//
Java Bytecode
Method
void catchTwo()
0 aload_0 // Begin try block
1 invokevirtual
#5 // Method Example.tryItOut()V
4 return // End of try block; normal return
5 astore_1 // Beginning of handler for TestExc1;
// Store thrown value in local variable 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual
#7 // Invoke handler method:
// Example.handleExc(LTestExc1;)V
11
return //
Return after handling TestExc1
12
astore_1 //
Beginning of handler for TestExc2;
// Store thrown value in local variable 1
13
aload_0 //
Push this
14
aload_1 //
Push thrown value
15
invokevirtual
#7 // Invoke handler method:
// Example.handleExc(LTestExc2;)V
18
return //
Return after handling TestExc2
Exception
table:
From
To Target
Type
0 4 5 Class
TestExc1
0 4 12 Class
TestExc2
//
DEX Bytecode
.method
catchTwo()V
.registers 2
.prologue
.line 895
:try_start_0
invoke-virtual {p0}, Lcom/java/test/Section7_12_3;->tryItOut()V
:try_end_3
.catch Lcom/java/test/TestExc1;
{:try_start_0 .. :try_end_3} :catch_4
.catch Lcom/java/test/TestExc2;
{:try_start_0 .. :try_end_3} :catch_9
.line 901
:goto_3
return-void
.line 896
:catch_4
move-exception v0
.line 897
.local v0, e:Lcom/java/test/TestExc1;
invoke-virtual {p0, v0}, Lcom/java/test/Section7_12_3;->handleExc(Lcom/java/test/TestExc1;)V
goto
:goto_3
.line 898
.end local v0 #e:Lcom/java/test/TestExc1;
:catch_9
move-exception v0
.line 899
.local v0, e:Lcom/java/test/TestExc2;
invoke-virtual {p0, v0}, Lcom/java/test/Section7_12_3;->handleExc(Lcom/java/test/TestExc2;)V
goto
:goto_3
.end
method
//
Java
void
nestedCatch()
{
try {
try {
tryItOut();
} catch (TestExc1 e) {
handleExc1(e);
}
} catch (TestExc2 e) {
handleExc2(e);
}
}
//
Java Bytecode
Method
void nestedCatch()
0 aload_0 // Begin try block
1 invokevirtual
#8 // Method Example.tryItOut()V
4 return // End of try block; normal return
5 astore_1 // Beginning of handler for TestExc1;
// Store thrown value in local variable 1
6 aload_0 // Push this
7 aload_1 // Push thrown value
8 invokevirtual
#7 // Invoke handler method:
// Example.handleExc1(LTestExc1;)V
11
return //
Return after handling TestExc1
12
astore_1 //
Beginning of handler for TestExc2;
// Store thrown value in local variable 1
13
aload_0 //
Push this
14
aload_1 //
Push thrown value
15
invokevirtual
#6 // Invoke handler method:
// Example.handleExc2(LTestExc2;)V
18
return //
Return after handling TestExc2
Exception
table:
From
To Target
Type
0 4 5 Class TestExc1
0 12 12 Class TestExc2
//
DEX Bytecode
.method
nestedCatch()V
.registers 2
.prologue
.line 970
:try_start_0
invoke-virtual {p0}, Lcom/java/test/Section7_12_4;->tryItOut()V
:try_end_3
.catch Lcom/java/test/TestExc1;
{:try_start_0 .. :try_end_3} :catch_4
.catch Lcom/java/test/TestExc2;
{:try_start_0 .. :try_end_3} :catch_9
.line 977
:goto_3
return-void
.line 971
:catch_4
move-exception v0
.line 972
.local v0, e:Lcom/java/test/TestExc1;
:try_start_5
invoke-virtual {p0, v0}, Lcom/java/test/Section7_12_4;->handleExc1(Lcom/java/test/TestExc1;)V
:try_end_8
.catch Lcom/java/test/TestExc2;
{:try_start_5 .. :try_end_8} :catch_9
goto
:goto_3
.line 974
.end local v0 #e:Lcom/java/test/TestExc1;
:catch_9
move-exception v0
.line 975
.local v0, e:Lcom/java/test/TestExc2;
invoke-virtual {p0, v0}, Lcom/java/test/Section7_12_4;->handleExc2(Lcom/java/test/TestExc2;)V
goto
:goto_3
.end
method
//
Java
void
tryFinally()
{
try {
tryItOut();
} finally {
wrapItUp();
}
}
//
Java Bytecode
Method
void tryFinally()
0 aload_0 // Beginning of try block
1 invokevirtual
#6 // Method Example.tryItOut()V
4 jsr
14 // Call finally block
7 return // End of try block
8 astore_1 // Beginning of handler for any throw
9 jsr
14 // Call finally block
12
aload_1 //
Push thrown value
13
athrow // ...and rethrow
the value to the invoker
14
astore_2 //
Beginning of finally block
15
aload_0 //
Push this
16
invokevirtual
#5 // Method Example.wrapItUp()V
19
ret 2 //
Return from finally block
Exception
table:
From
To Target
Type
0 4 8 any
//
DEX Bytecode
.method
tryFinally()V
.registers 2
.prologue
.line 1034
:try_start_0
invoke-virtual {p0}, Lcom/java/test/Section7_13_1;->tryItOut()V
:try_end_3
.catchall {:try_start_0 .. :try_end_3}
:catchall_7
.line 1036
invoke-virtual {p0}, Lcom/java/test/Section7_13_1;->wrapItUp()V
.line 1038
return-void
.line 1035
:catchall_7
move-exception v0
.line 1036
invoke-virtual {p0}, Lcom/java/test/Section7_13_1;->wrapItUp()V
.line 1037
throw v0
.end
method
//
Java
void
tryCatchFinally()
throws TestExc
{
try {
tryItOut();
} finally {
wrapItUp();
}
}
//
Java Bytecode
Method
void tryCatchFinally()
0 aload_0 // Beginning of try block
1 invokevirtual
#4 // Method Example.tryItOut()V
4 goto
16 // Jump to finally block
7 astore_3 // Beginning of handler for TestExc;
// Store thrown value in local variable 3
8 aload_0 // Push this
9 aload_3 // Push thrown value
10
invokevirtual
#6 // Invoke handler method:
// Example.handleExc(LTestExc;)V
13
goto
16 // Huh???1
16
jsr
26 // Call finally block
19
return //
Return after handling TestExc
20
astore_1 //
Beginning of handler for exceptions
// other than TestExc,
or exceptions
// thrown while handling TestExc
21
jsr
26 // Call finally block
24
aload_1 //
Push thrown value...
25
athrow // ...and rethrow
the value to the invoker
26
astore_2 //
Beginning of finally block
27
aload_0 //
Push this
28
invokevirtual
#5 // Method Example.wrapItUp()V
31
ret 2 //
Return from finally block
Exception
table:
From
To Target
Type
0 4 7 Class TestExc
0 16 20 any
//
DEX Bytecode
.method
tryCatchFinally()V
.registers 2
.annotation system Ldalvik/annotation/Throws;
value = {
Lcom/java/test/TestExc;
}
.end annotation
.prologue
.line 1081
:try_start_0
invoke-virtual {p0}, Lcom/java/test/Section7_13_2;->tryItOut()V
:try_end_3
.catchall {:try_start_0 .. :try_end_3}
:catchall_7
.line 1083
invoke-virtual {p0}, Lcom/java/test/Section7_13_2;->wrapItUp()V
.line 1085
return-void
.line 1082
:catchall_7
move-exception v0
.line 1083
invoke-virtual {p0}, Lcom/java/test/Section7_13_2;->wrapItUp()V
.line 1084
throw v0
.end
method
12. Synchronization
//
Java
void
onlyMe(Foo
f) {
synchronized(f) {
doSomething();
}
}
//
Java Bytecode
Method
void onlyMe(Foo)
0 aload_1 // Push f
1 astore_2 // Store it in local variable 2
2 aload_2 // Push local variable 2 (f)
3 monitorenter // Enter the monitor associated with f
4 aload_0 // Holding the monitor, pass this and...
5 invokevirtual
#5 // ...call Example.doSomething()V
8 aload_2 // Push local variable 2 (f)
9 monitorexit // Exit the monitor associated with f
10 return //
Return normally
11
aload_2 //
In case of any throw, end up here
12
monitorexit // Be sure to exit monitor...
13
athrow // ...then rethrow
the value to the invoker
Exception
table:
From To Target
Type
4 8 11 any
//
DEX Bytecode
.method
onlyMe(Lcom/java/test/Foo;)V
.registers 3
.parameter "f"
.prologue
.line 1150
monitor-enter p1
.line 1151
:try_start_1
invoke-virtual {p0}, Lcom/java/test/Section7_14_1;->doSomething()V
.line 1150
monitor-exit p1
.line 1153
return-void
.line 1150
:catchall_6
move-exception v0
monitor-exit p1
:try_end_8
.catchall {:try_start_1 .. :try_end_8}
:catchall_6
throw v0
.end
method
13. Nested Classes and Interface
14. instanceof
//
Java
void
m()
{
subPeople
sub = new subPeople();
if(sub instanceof
subPeople)
{
System.out.println("This
is a subPeople
instance");
}
if(sub instanceof
people)
{
System.out.println("This
is a People instance");
}
if(sub instanceof
Object)
{
System.out.println("This
is an Object instance");
}
}
//
DEX Bytecode
.method
m()V
.registers 4
.prologue
.line 1203
new-instance v0, Lcom/java/test/Section7_16_1$subPeople;
invoke-direct {v0, p0}, Lcom/java/test/Section7_16_1$subPeople;-><init>(Lcom/java/test/Section7_16_1;)V
.line 1205
.local v0, sub:Lcom/java/test/Section7_16_1$subPeople;
instance-of v1, v0, Lcom/java/test/Section7_16_1$subPeople;
if-eqz
v1, :cond_10
.line 1207
sget-object
v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string
v2, "This is a subPeople
instance"
invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 1210
:cond_10
instance-of v1, v0, Lcom/java/test/Section7_16_1$people;
if-eqz
v1, :cond_1b
.line 1212
sget-object
v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string
v2, "This is a People instance"
invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 1215
:cond_1b
instance-of v1, v0, Ljava/lang/Object;
if-eqz
v1, :cond_26
.line 1217
sget-object
v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string
v2, "This is an Object instance"
invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 1219
:cond_26
return-void
.end
method