Thursday, November 5, 2009

How to manipulate bytecode in android

The following was tested successfully in Android SDK 1.1 and 2.0

Step 1: Create an android project in eclipse

Step 2: open the activity class and change the event onCreate with the following

boolean flag = false;

/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
TextView tv = new TextView(this);
} else{
tv.setText("NOT CRACKED YET... :(");

Check the new variable named flag, it is our main objective.

Step 3: run the project in the emulator, you will see a screen showing the text "NOT CRACKED YET... :("

Step 4: go to the folder YOUR_PROJECT\bin and remove the signature from the APK file. If you don't know how to do it read my previous entry here

Step 5
  • disassemble the file classes.dex. If you don't know how to do it read my previous entry here.
  • Search the text "onCreate" to find the body of the onCreate method. You will find something like this:

    Virtual methods -
    #0 : (in La/a/Start;)
    name : 'onCreate'
    type : '(Landroid/os/Bundle;)V'
    access : 0x0001 (PUBLIC)
    code -
    registers : 4
    ins : 2
    outs : 2
    insns size : 27 16-bit code units
    0003c4: |[0003c4] a.a.Start.onCreate:(Landroid/os/Bundle;)V
    0003d4: 6f20 0900 3200 |0000: invoke-super {v2, v3}, Landroid/app/Activity;.onCreate:(Landroid/os/Bundle;)V // method@0009
    0003da: 2200 0b00 |0003: new-instance v0, Landroid/widget/TextView; // class@000b
    0003de: 7020 0a00 2000 |0005: invoke-direct {v0, v2}, Landroid/widget/TextView;.:(Landroid/content/Context;)V // method@000a
    0003e4: 5521 0400 |0008: iget-boolean v1, v2, La/a/Start;.flag:Z // field@0004
    0003e8: 3801 0b00 |000a: if-eqz v1, 0015 // +000b
    0003ec: 1a01 0100 |000c: const-string v1, "CONGRATS! YOU CRACKED MY CODE! ;)" // string@0001
    0003f0: 6e20 0b00 1000 |000e: invoke-virtual {v0, v1}, Landroid/widget/TextView;.setText:(Ljava/lang/CharSequence;)V // method@000b
    0003f6: 6e20 0700 0200 |0011: invoke-virtual {v2, v0}, La/a/Start;.setContentView:(Landroid/view/View;)V // method@0007
    0003fc: 0e00 |0014: return-void
    0003fe: 1a01 1300 |0015: const-string v1, "NOT CRACKED YET... :(" // string@0013
    000402: 6e20 0b00 1000 |0017: invoke-virtual {v0, v1}, Landroid/widget/TextView;.setText:(Ljava/lang/CharSequence;)V // method@000b
    000408: 28f7 |001a: goto 0011 // -0009
    catches : (none)
    positions :
    0x0000 line=15
    0x0003 line=16
    0x0008 line=17
    0x000c line=18
    0x0011 line=22
    0x0014 line=23
    0x0015 line=20
    locals :
    0x0008 - 0x001b reg=0 tv Landroid/widget/TextView;
    0x0000 - 0x001b reg=2 this La/a/Start;
    0x0000 - 0x001b reg=3 savedInstanceState Landroid/os/Bundle;

    You need to understand how bytecode works. If you don't have any experience with bytecode manipulation please stop here.

  • I marked in red the line we want to change, because it is a jump that involves the lines of code showing texts. We must invert the jump so the screen will display another text.
  • edit in hexadecimal the file classes.dex and go to the offset 03E8, you will se the hex numbers 38 01 0b 00
    Why? Check the following line:

    0003e8: 3801 0b00 |000a: if-eqz v1, 0015 // +000b

    the first number is the offset in the classes.dex file, the following numbers are the opcode and parameters.
    If you check the opcode numbers HERE you can see the value 0x38 is for the opcode if-eqz, we must change this opcode to if-nez to invert the jump. Checking the opcodes list we see the value for if-nez is 0x39, so change the 38 value at offset 0x3E8 by 39

  • Now you must fix the checksum or you will have errors when installing the app. You can calculate the checksum with the following code:
public class FixDEXChecksum(){

* Calculates the checksum for the .dex file in the
* given array, and modify the array to contain it.
* @param bytes non-null; the bytes of the file
private static void calcChecksum(byte[] bytes) {
Adler32 a32 = new Adler32();

a32.update(bytes, 12, bytes.length - 12);

int sum = (int) a32.getValue();

bytes[8] = (byte) sum;
bytes[9] = (byte) (sum >> 8);
bytes[10] = (byte) (sum >> 16);
bytes[11] = (byte) (sum >> 24);

public static void main(String[] args) {
try {
File file = new File(args[0]);
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[fis.available()];
System.out.println("Reading DEX file");;
System.out.println("Calculating new checksum");
System.out.println("Making backup");
file.renameTo(new File(args[0]+".bak"));
System.out.println("Writing new DEX file with checksum "+ Integer.toHexString(data[8]) +" "+ Integer.toHexString(data[9]) +" "+ Integer.toHexString(data[10]) +" "+ Integer.toHexString(data[11]));
FileOutputStream fos = new FileOutputStream(new File(args[0]));
} catch (Exception e) {
// TODO Auto-generated catch block


  • Put the new classes.dex inside your unsigned APK
  • Sign the APK file again.
  • Run the program and voila! The text displayed is "CONGRATS! YOU CRACKED MY CODE! ;)"

I know it is a very small program and we know how the code works, but this is useful to see what can be done with bytecode manipulation.

Removing bytecode is not so simple, but you can try replacing the bytecode values in the lines you want to remove by zeros 00, it is the NOP bytecode.

Injecting bytecode is another story, lots of offsets must be modified in the classes.dex file. I will tell you later.

And I will test this in SDK 1.5, 1.6, but another day, i'm tired now :)



  1. nice article, but how to run the fixdex code? Im an experienced reverser and have modded my target but with no java experience the fixing the signature step has had me stuck for hours. thanks!

  2. Hi,

    I fixed the links in the article, were pointing to old pages.

    Maybe I can help you. What kind of problems do you have?