Friday, August 02, 2013

A Dalvik Bytecode Reference for Android Hackers (2)

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


// 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