Showing posts with label android. Show all posts
Showing posts with label android. Show all posts

Wednesday, August 31, 2011

New DEX disassembler

Checkout this new DEX file disassembler


https://code.google.com/p/smali/

it looks good and has lots of options

BR,

Lula

Tuesday, May 31, 2011

Obfuscate android builds using proguard

The new android SDK comes with proguard included in the build system. You can check how to use it at http://developer.android.com/guide/developing/tools/proguard.html

Not all the proguard options can be used, specially those used for optimizations.

You should keep all classes used in xml files. The best way to do it is to keep classes that extend from android.content.Context due to almost everything related to android UI and services belong to the android context.

After some research I give you the best proguard.cfg for almost any application.


-dontpreverify
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-optimizations !code/simplification/arithmetic
-optimizationpasses 5
-useuniqueclassmembernames
-dontusemixedcaseclassnames
-verbose
-target 1.6
# -repackageclasses mypackage

-keep public class * extends android.content.Context {
  *** (...);
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keepattributes Exceptions, InnerClasses, Signature

# Keep names - Native method names. Keep all native class/method names.
-keepclasseswithmembers,allowshrinking class * {
    native ;
}

-assumenosideeffects public class android.util.Log {
  *** *(...);
}

# Remove - System method calls. Remove all invocations of System
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.System {
    public static long currentTimeMillis();
    static java.lang.Class getCallerClass();
    public static int identityHashCode(java.lang.Object);
    public static java.lang.SecurityManager getSecurityManager();
    public static java.util.Properties getProperties();
    public static java.lang.String getProperty(java.lang.String);
    public static java.lang.String getenv(java.lang.String);
    public static java.lang.String mapLibraryName(java.lang.String);
    public static java.lang.String getProperty(java.lang.String,java.lang.String);
}

# Remove - Math method calls. Remove all invocations of Math
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.Math {
    public static double sin(double);
    public static double cos(double);
    public static double tan(double);
    public static double asin(double);
    public static double acos(double);
    public static double atan(double);
    public static double toRadians(double);
    public static double toDegrees(double);
    public static double exp(double);
    public static double log(double);
    public static double log10(double);
    public static double sqrt(double);
    public static double cbrt(double);
    public static double IEEEremainder(double,double);
    public static double ceil(double);
    public static double floor(double);
    public static double rint(double);
    public static double atan2(double,double);
    public static double pow(double,double);
    public static int round(float);
    public static long round(double);
    public static double random();
    public static int abs(int);
    public static long abs(long);
    public static float abs(float);
    public static double abs(double);
    public static int max(int,int);
    public static long max(long,long);
    public static float max(float,float);
    public static double max(double,double);
    public static int min(int,int);
    public static long min(long,long);
    public static float min(float,float);
    public static double min(double,double);
    public static double ulp(double);
    public static float ulp(float);
    public static double signum(double);
    public static float signum(float);
    public static double sinh(double);
    public static double cosh(double);
    public static double tanh(double);
    public static double hypot(double,double);
    public static double expm1(double);
    public static double log1p(double);
}

# Remove - Number method calls. Remove all invocations of Number
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.* extends java.lang.Number {
    public static java.lang.String toString(byte);
    public static java.lang.Byte valueOf(byte);
    public static byte parseByte(java.lang.String);
    public static byte parseByte(java.lang.String,int);
    public static java.lang.Byte valueOf(java.lang.String,int);
    public static java.lang.Byte valueOf(java.lang.String);
    public static java.lang.Byte decode(java.lang.String);
    public int compareTo(java.lang.Byte);
    public static java.lang.String toString(short);
    public static short parseShort(java.lang.String);
    public static short parseShort(java.lang.String,int);
    public static java.lang.Short valueOf(java.lang.String,int);
    public static java.lang.Short valueOf(java.lang.String);
    public static java.lang.Short valueOf(short);
    public static java.lang.Short decode(java.lang.String);
    public static short reverseBytes(short);
    public int compareTo(java.lang.Short);
    public static java.lang.String toString(int,int);
    public static java.lang.String toHexString(int);
    public static java.lang.String toOctalString(int);
    public static java.lang.String toBinaryString(int);
    public static java.lang.String toString(int);
    public static int parseInt(java.lang.String,int);
    public static int parseInt(java.lang.String);
    public static java.lang.Integer valueOf(java.lang.String,int);
    public static java.lang.Integer valueOf(java.lang.String);
    public static java.lang.Integer valueOf(int);
    public static java.lang.Integer getInteger(java.lang.String);
    public static java.lang.Integer getInteger(java.lang.String,int);
    public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer);
    public static java.lang.Integer decode(java.lang.String);
    public static int highestOneBit(int);
    public static int lowestOneBit(int);
    public static int numberOfLeadingZeros(int);
    public static int numberOfTrailingZeros(int);
    public static int bitCount(int);
    public static int rotateLeft(int,int);
    public static int rotateRight(int,int);
    public static int reverse(int);
    public static int signum(int);
    public static int reverseBytes(int);
    public int compareTo(java.lang.Integer);
    public static java.lang.String toString(long,int);
    public static java.lang.String toHexString(long);
    public static java.lang.String toOctalString(long);
    public static java.lang.String toBinaryString(long);
    public static java.lang.String toString(long);
    public static long parseLong(java.lang.String,int);
    public static long parseLong(java.lang.String);
    public static java.lang.Long valueOf(java.lang.String,int);
    public static java.lang.Long valueOf(java.lang.String);
    public static java.lang.Long valueOf(long);
    public static java.lang.Long decode(java.lang.String);
    public static java.lang.Long getLong(java.lang.String);
    public static java.lang.Long getLong(java.lang.String,long);
    public static java.lang.Long getLong(java.lang.String,java.lang.Long);
    public static long highestOneBit(long);
    public static long lowestOneBit(long);
    public static int numberOfLeadingZeros(long);
    public static int numberOfTrailingZeros(long);
    public static int bitCount(long);
    public static long rotateLeft(long,int);
    public static long rotateRight(long,int);
    public static long reverse(long);
    public static int signum(long);
    public static long reverseBytes(long);
    public int compareTo(java.lang.Long);
    public static java.lang.String toString(float);
    public static java.lang.String toHexString(float);
    public static java.lang.Float valueOf(java.lang.String);
    public static java.lang.Float valueOf(float);
    public static float parseFloat(java.lang.String);
    public static boolean isNaN(float);
    public static boolean isInfinite(float);
    public static int floatToIntBits(float);
    public static int floatToRawIntBits(float);
    public static float intBitsToFloat(int);
    public static int compare(float,float);
    public boolean isNaN();
    public boolean isInfinite();
    public int compareTo(java.lang.Float);
    public static java.lang.String toString(double);
    public static java.lang.String toHexString(double);
    public static java.lang.Double valueOf(java.lang.String);
    public static java.lang.Double valueOf(double);
    public static double parseDouble(java.lang.String);
    public static boolean isNaN(double);
    public static boolean isInfinite(double);
    public static long doubleToLongBits(double);
    public static long doubleToRawLongBits(double);
    public static double longBitsToDouble(long);
    public static int compare(double,double);
    public boolean isNaN();
    public boolean isInfinite();
    public int compareTo(java.lang.Double);
    public (byte);
    public (short);
    public (int);
    public (long);
    public (float);
    public (double);
    public (java.lang.String);
    public byte byteValue();
    public short shortValue();
    public int intValue();
    public long longValue();
    public float floatValue();
    public double doubleValue();
    public int compareTo(java.lang.Object);
    public boolean equals(java.lang.Object);
    public int hashCode();
    public java.lang.String toString();
}

# Remove - String method calls. Remove all invocations of String
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.String {
    public ();
    public (byte[]);
    public (byte[],int);
    public (byte[],int,int);
    public (byte[],int,int,int);
    public (byte[],int,int,java.lang.String);
    public (byte[],java.lang.String);
    public (char[]);
    public (char[],int,int);
    public (java.lang.String);
    public (java.lang.StringBuffer);
    public static java.lang.String copyValueOf(char[]);
    public static java.lang.String copyValueOf(char[],int,int);
    public static java.lang.String valueOf(boolean);
    public static java.lang.String valueOf(char);
    public static java.lang.String valueOf(char[]);
    public static java.lang.String valueOf(char[],int,int);
    public static java.lang.String valueOf(double);
    public static java.lang.String valueOf(float);
    public static java.lang.String valueOf(int);
    public static java.lang.String valueOf(java.lang.Object);
    public static java.lang.String valueOf(long);
    public boolean contentEquals(java.lang.StringBuffer);
    public boolean endsWith(java.lang.String);
    public boolean equalsIgnoreCase(java.lang.String);
    public boolean equals(java.lang.Object);
    public boolean matches(java.lang.String);
    public boolean regionMatches(boolean,int,java.lang.String,int,int);
    public boolean regionMatches(int,java.lang.String,int,int);
    public boolean startsWith(java.lang.String);
    public boolean startsWith(java.lang.String,int);
    public byte[] getBytes();
    public byte[] getBytes(java.lang.String);
    public char charAt(int);
    public char[] toCharArray();
    public int compareToIgnoreCase(java.lang.String);
    public int compareTo(java.lang.Object);
    public int compareTo(java.lang.String);
    public int hashCode();
    public int indexOf(int);
    public int indexOf(int,int);
    public int indexOf(java.lang.String);
    public int indexOf(java.lang.String,int);
    public int lastIndexOf(int);
    public int lastIndexOf(int,int);
    public int lastIndexOf(java.lang.String);
    public int lastIndexOf(java.lang.String,int);
    public int length();
    public java.lang.CharSequence subSequence(int,int);
    public java.lang.String concat(java.lang.String);
    public java.lang.String replaceAll(java.lang.String,java.lang.String);
    public java.lang.String replace(char,char);
    public java.lang.String replaceFirst(java.lang.String,java.lang.String);
    public java.lang.String[] split(java.lang.String);
    public java.lang.String[] split(java.lang.String,int);
    public java.lang.String substring(int);
    public java.lang.String substring(int,int);
    public java.lang.String toLowerCase();
    public java.lang.String toLowerCase(java.util.Locale);
    public java.lang.String toString();
    public java.lang.String toUpperCase();
    public java.lang.String toUpperCase(java.util.Locale);
    public java.lang.String trim();
}

# Remove - StringBuffer method calls. Remove all invocations of StringBuffer
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.StringBuffer {
    public ();
    public (int);
    public (java.lang.String);
    public (java.lang.CharSequence);
    public java.lang.String toString();
    public char charAt(int);
    public int capacity();
    public int codePointAt(int);
    public int codePointBefore(int);
    public int indexOf(java.lang.String,int);
    public int lastIndexOf(java.lang.String);
    public int lastIndexOf(java.lang.String,int);
    public int length();
    public java.lang.String substring(int);
    public java.lang.String substring(int,int);
}

# Remove - StringBuilder method calls. Remove all invocations of StringBuilder
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.StringBuilder {
    public ();
    public (int);
    public (java.lang.String);
    public (java.lang.CharSequence);
    public java.lang.String toString();
    public char charAt(int);
    public int capacity();
    public int codePointAt(int);
    public int codePointBefore(int);
    public int indexOf(java.lang.String,int);
    public int lastIndexOf(java.lang.String);
    public int lastIndexOf(java.lang.String,int);
    public int length();
    public java.lang.String substring(int);
    public java.lang.String substring(int,int);
}


BR,

Lula

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. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
if(flag){
tv.setText("CONGRATS! YOU CRACKED MY CODE! ;)");
} else{
tv.setText("NOT CRACKED YET... :(");
}
setContentView(tv);
}


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");
fis.read(data);
System.out.println("Calculating new checksum");
calcChecksum(data);
fis.close();
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]));
fos.write(data);
fos.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

}
  • 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 :)

Enjoy!

Wednesday, November 4, 2009

How to check the signature in APK files or JAR files

You can use the jarsigner tool from JDK.

JDKFOLDER\bin\jarsigner.exe -verify -verbose -certs myAndroidProgram.apk

or

JDKFOLDER\bin\jarsigner.exe -verify -verbose -certs myJavaProgram.jar

the output will be the list of files with a detail of the signature, as following:

sm 3366 Thu Nov 05 00:57:58 ART 2009 res/drawable/icon.png

X.509, CN=Android Debug, O=Android, C=US
[certificate is valid from 05/11/09 00:01 to 05/11/10 00:01]

sm 640 Thu Nov 05 00:57:58 ART 2009 res/layout/main.xml

X.509, CN=Android Debug, O=Android, C=US
[certificate is valid from 05/11/09 00:01 to 05/11/10 00:01]

sm 1248 Thu Nov 05 00:57:58 ART 2009 AndroidManifest.xml

X.509, CN=Android Debug, O=Android, C=US
[certificate is valid from 05/11/09 00:01 to 05/11/10 00:01]

sm 1012 Thu Nov 05 00:57:58 ART 2009 resources.arsc

X.509, CN=Android Debug, O=Android, C=US
[certificate is valid from 05/11/09 00:01 to 05/11/10 00:01]

sm 1984 Thu Nov 05 00:57:58 ART 2009 classes.dex

X.509, CN=Android Debug, O=Android, C=US
[certificate is valid from 05/11/09 00:01 to 05/11/10 00:01]

401 Thu Nov 05 00:57:58 ART 2009 META-INF/MANIFEST.MF
454 Thu Nov 05 00:57:58 ART 2009 META-INF/CERT.SF
771 Thu Nov 05 00:57:58 ART 2009 META-INF/CERT.RSA

s = signature was verified
m = entry is listed in manifest
k = at least one certificate was found in keystore
i = at least one certificate was found in identity scope

jar verified.


Cheers

Remove signature from APK file and sign it again

The APK file is just a zip file with another extension. To remove the signature just unzip it, remove the folder META-INF and zip it again.

but how to sign it again? You can use your own signature or the debug signature used by Android SDK named debug.keystore

You can copy the debug signature from C:\Documents and Settings\\Local Settings\Application Data\Android\

The data of the signature is the following:

* Keystore name: "debug.keystore"
* Keystore password: "android"
* Key alias: "androiddebugkey"
* Key password: "android"
* CN: "CN=Android Debug,O=Android,C=US"

for more details go to http://developer.android.com/guide/publishing/app-signing.html

Now, how to sign the apk again? Use the jarsigner.exe tool, this tool comes with the JDK.

JDKFOLDER\bin\jarsigner.exe -verbose -storepass android -keystore debug.keystore myprogram.apk androiddebugkey

replace myprogram.apk with your own unsigned apk.

Is this useful? Maybe, if you want to modify the build with reverse engineering or something similar, use your imagination.

voila!

Disassemble dex files

Android SDK comes with the tool dexdump.exe

To disassemble a dex file just use the following command:

dexdump.exe -d -f -h classes.dex >dexdump.txt

The output will be stored in the file dexdump.txt, and the format is dalvik bytecode, not java bytecode.

I will investigate how to modify this file.