Archive for » October, 2008 «

Friday, October 31st, 2008 | Author: Tim

Happy Halloween everyone! As promised I’ve written up a small example program to show how to patch the android dex file, repackage as an apk and get it to work on your android device or emulator. This is actually [i]extremely[/i] easy once you understand the steps, or have the tools to do it for you!

For an example program I’ve attached “PatchTest.rar” and “PatchTest.apk”. These are both the original *unpatched* programs we are going to use. The rar file contains the source and project files so you can load them up in Eclipse and play around with them yourself. Both of these files have been tested in the emulator and on the G1 hardware device and work fine. Here is the bulk of the code in which we are looking at though:

/*
 * File:	Patchtest.java
 * Coded:	Timothy Strazzere
 * Date:	10/31/2008 (Happy Halloween!)
 *
 * 			This is an example of a brutally simple file for
 * 			the android OS that will simple start up, call
 * 			the function registered() and either keep the text
 * 			to "not registered" if registered() returns false.
 * 			Otherwise if registered() returns true it will
 * 			change the text to "registered".
 *
 * 			The way it is set up it should NEVER return registered,
 * 			so it is perfect fora simple example of how to
 * 			patch a file :)
 */

package com.Android.Patchtest;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.TextView;

public class Patchtest extends Activity {
	TextView status;
	TextView webaddress;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        status = (TextView)this.findViewById(R.id.status);

        // Call Registered Function, if true - change the text!
        if(registered()){
        	this.status.setText("Registered!");
        	this.status.setTextColor(Color.GREEN);
        }
    }

    // Stupid 'registered' function that should NEVER return true
    protected boolean registered() {
    	int x = 12345;
    	int y = 67890;

    	x %= y;

    	if(x == 0)
    		return (true);

    	return(false);
    }
}

So essentially this program starts, calls the “registered” function, if it returns true then it will change our TextView to “Registered!” and that is what we want. If we look at the registered function, it simple initializeds two values, performs a modulus operation on the two and judges the result. This means the program should [i]never[/i] return the result true, so we should never have the program show the text “Registered!”

Not Registered Screen

Not Registered Screen

Files:
Patchtest.apk - MD5: AF65D0D7399D3F1D4768AFB523981666
Patchtest.rar - MD5: B60FF4BE1B680365F483EB288278F8E5

Next up I’ll post the patched .apk file and an explanation on how to do it. Until then, try it yourself and use the tools I provided in previous posts to see if you can do it yourself! It has also been submitted to crackmes.de by a friend, maybe someone there will also have fun with it.

Thursday, October 30th, 2008 | Author: Tim
Successfully patching an android application

Successfully patching an android application

It had been bugging me tons since the application kept crashing. I knew the signature and checksum where correct since it wasn’t barfing on installation of the .apk file. So I kept thinking and thinking, finally I decided to do something useful… Look at the traces log! Here we can clearly see that an exception is being thrown… But why?

----- pid 210 at 2008-10-30 21:15:29 -----
Cmd line: com.android.KeyGenMe

DALVIK THREADS:
"main" prio=5 tid=3 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x400113a8
  | sysTid=210 nice=0 sched=0/0 handle=-1095390052
  at android.os.BinderProxy.transact(Native Method)
  at android.app.ActivityManagerProxy.handleApplicationError(ActivityManagerNative.java:2023)
  at com.android.internal.os.RuntimeInit.crash(RuntimeInit.java:302)
  at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:75)
  at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:853)
  at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:850)
  at dalvik.system.NativeStart.main(Native Method)

"Binder Thread #2" prio=5 tid=13 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x43360018
  | sysTid=215 nice=0 sched=0/0 handle=972800
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #1" prio=5 tid=11 NATIVE
  | group="main" sCount=1 dsCount=0 s=0 obj=0x4335efb8
  | sysTid=214 nice=0 sched=0/0 handle=972616
  at dalvik.system.NativeStart.run(Native Method)

"JDWP" daemon prio=5 tid=9 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x4335e2a0
  | sysTid=213 nice=0 sched=0/0 handle=799384
  at dalvik.system.NativeStart.run(Native Method)

"Signal Catcher" daemon prio=5 tid=7 RUNNABLE
  | group="system" sCount=0 dsCount=0 s=0 obj=0x4335e1e8
  | sysTid=212 nice=0 sched=0/0 handle=796600
  at dalvik.system.NativeStart.run(Native Method)

"HeapWorker" daemon prio=5 tid=5 VMWAIT
  | group="system" sCount=1 dsCount=0 s=0 obj=0x42533028
  | sysTid=211 nice=0 sched=0/0 handle=793976
  at dalvik.system.NativeStart.run(Native Method)

----- end 210 -----

I decided to do yet another smart thing, that I should’ve done - and redumped the dex file and see if it was making any sense… Of course! I edited the wrong opcode. Apparently in my overwhelming dumbness I tried changing the registers and a exception thrown for the statement. This is something the Dalvik-VM did not agree with, thus the barfing.

I’m going to recreate a nice little example with source code of a simple patch performed on a dex file, and I’ll outline the process used to do so. Hopefully I’ll have this posted sometime tomorrow!

Thursday, October 30th, 2008 | Author: Tim

DEX files, which are the compiled bytecode files that run on the Android OS. Essentially they are like the java bytecode, except they use a modified VM which is called “Dalvik” that was developed for the Android OS.

So long story short - I want to know the structure of these files, and how to edit or patch them. Why? I enjoy reverse engineering things! Anyway there is a wealth of information that I could find on the following sites;

Shane Isbell’s Weblog : http://www.jroller.com/random7/entry/android_s_dex_class_structure
RetroCode, Dex File Formate : http://www.retrodev.com/android/dexformat.html

These site both have great information, thoughmore so on the second link. What intrigued me was that they must have a ‘checksum’ and a ‘SHA-1′ signature. The best information I could find though only eluded to this (Retrocode);

[quote]Notes: All non-string fields are stored in little-endian format. It would appear that the checksum and signature fields are assumed to be zero when calculating the checksum and signature.[/quote]

Well that doesn’t really help me if I’d like to patch a DEX file and then redo the signature and checksum. Especially since we don’t know what exactly is being used to calculate either or what checksum is being used to well, calculate the checksum!

Good thing we can extract .jar files and decompile class files, even more so thank goodness google hasn’t used obstufication on any of the classes. An except from “dx\com\android\dx\dex\file\DexFile.class” after being decompiled into DexFile.jar.

private static void calcSignature(byte bytes[])
{
MessageDigest md;
try
{
md = MessageDigest.getInstance("SHA-1");
}
catch(NoSuchAlgorithmException ex)
{
throw new RuntimeException(ex);
}
md.update(bytes, 32, bytes.length - 32);
try
{
int amt = md.digest(bytes, 12, 20);
if(amt != 20)
throw new RuntimeException((new StringBuilder()).append("unexpected digest write:").append(amt).append("bytes").toString());
}
catch(DigestException ex)
{
throw new RuntimeException(ex);
}
}

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);
}

Ah hah! Now we know how to calculate the signature and the checksum. Essentially is reads in all the bytes of the program, and it disreguards the first 32 bytes and calculates the signature using SHA-1. Then it calculates the checksum disreguarding the first 12 bytes (so it includes the signature). Excellent, lets drop this code into our own program so we can recalculate those values;

/*
*         ReDEX.class
*         Coded: Timothy Strazzere
*
*         10/29/2008
*
*         Pass a .dex file as an argument and it will output the current
*         checksum and signature that is in the file, then it will output
*         the checksum and signature as it calculates them.
*
*/

import java.security.*;
import java.util.zip.Adler32;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class ReDEX {

public static void main(String[] args) {
if (args.length == 1) {
try {
File file = new File(args[0]);

byte[] barr = null;
barr = getBytesFromFile(file);

System.out.print(:Original Checksum: ");
for(int i = 8; i<12; i+=4)
System.out.printf("0x%02X%02X%02X%02X ", barr[i+3], barr[i+2], barr[i+1], barr[i]);
System.out.print("\nOriginal Signature: 0x");
for(int i = 12; i<32; i+=4)
System.out.printf"%02X%02X%02X%02X ", barr[i], barr[i+1], barr[i+2], barr[i+3]);

calcSignature(barr);
calcChecksum(barr);
System.out.print("\n\nNew Checksum: ");
for(int i = 8; i<12; i+=4)
System.out.printf("0x%02X%02X%02X%02X ", barr[i+3], barr[i+2], barr[i+1], barr[i]);
System.out.print("\nNew Signature: 0x");
for(int i = 12; i<32; i+=4)
System.out.printf("%02X%02X%02X%02X ", barr[i], barr[i+1], barr[i+2], barr[i+3]);
}
catch (Exception e) {
System.err.println("File input error");
}
}
else
System.out.println("Invalid parameters");
}

public static byte[] getBytesFromFile(File file) throws IOException {
InputStream is = new FileInputStream(file);

// Get the size of the file
long length = file.length();

if (length > Integer.MAX_VALUE) {
// File is too large
}

// Create the byte array to hold the data
byte[] bytes = new byte[(int)length];

// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
offset += numRead;
}

// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file "+file.getName());
}

// Close the input stream and return bytes
is.close();
return bytes;
}
private static void calcSignature(byte bytes[])
{
MessageDigest md;
try
{
md = MessageDigest.getInstance("SHA-1");
}
catch(NoSuchAlgorithmException ex)
{
throw new RuntimeException(ex);
}
md.update(bytes, 32, bytes.length - 32);
try
{
int amt = md.digest(bytes, 12, 20);
if(amt != 20)
throw new RuntimeException((new StringBuilder()).append("unexpected digest write:").append(amt).append("bytes").toString());
}
catch(DigestException ex)
{
throw new RuntimeException(ex);
}
}

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);
}

Excellent, now I can easily recalculate the signature and the checksum of the dex files. Note that the checksum is actually in little-endian so you need to reverse it when entering it in a hex editor.

So, now the Android OS will allow you to install this dex file, after signing it in the correct package (apk). Though as of right now the program still crashes upon launching the file… Hhhmmmmm we’ll have to look into this more I guess.