Threat Research

Dalvik Executable (DEX) Embedded in another DEX!

By Axelle Apvrille | August 23, 2012

Tim Strazzere's Android CrackMe

It's Android challenge time, and Tim Strazzere provided an interesting Android CrackMe at BlackHat. As he agreed to my posting about it, here's my spoiler/solution below.

The package is named droid-with-a-big-ego.apk and APKTool and Baksmali have difficulties processing it:

I: Baksmaling...
Exception in thread "main" org.jf.dexlib.Util.ExceptionWithContext: The header size is not the expected value (0x70)
        at org.jf.dexlib.Util.ExceptionWithContext.withContext(

I worked around this issue using dex2jar but other tools such as ded or Androguard can be used as alternatives. The challenge's main class is APKryptActivity. One of the first things this class does is call a method named initializeMethods(), with an interesting start:

this.dexFile = new DexFile(getApplicationContext());
localClass = this.dexFile.getClass("dont/decompile/me/Crack");
Method[] arrayOfMethod = localClass.getMethods();
Class[] arrayOfClass1 = new Class[1];
arrayOfClass1[0] = Context.class;
this.isEmulator = localClass.getMethod("isEmulator", arrayOfClass1);
Class[] arrayOfClass2 = new Class[1];
arrayOfClass2[0] = String.class;
this.checkPassword = localClass.getMethod("checkPassword", arrayOfClass2);

Clearly, the challenge uses reflection to load methods isEmulator() and checkPassword() from a class named Crack. DexFile is a hand made class, let's look at it.

DexFile constructor

The constructor first instantiates a class named Worker, then it calls getPackedSectionLength() on it. The result is stored in an integer and an exception is thrown if value is negative or 0. Then, the constructor loads the standard class dalvik.system.DexFile and calls initializeMethods(). If we have a look at this method, it indeed loads methods from this class... but not public ones! In particular, it is looking for defineClass() and openDexFile() which are private native methods (see Android's source code at AOSP, and check file libcore/dalvik/src/main/java/dalvik/system/

Method[] arrayOfMethod = Class.forName("dalvik.system.DexFile").getDeclaredMethods();
// set defineClass (class field) to the method whose name matches "defineClass"
// bypass access to method

Then, the DexFile constructor calls getData() on the localWorker object. This method is interesting too, see below.

Worker.getData() method

This method reads from the current classes.dex, skipping the first 112 bytes, and decrypts what it reads applying XOR key 0xD1. We can do this manually:

$ dd if=classes.dex of=work.dex bs=1 skip=112
18491+0 records in
18491+0 records out
18491 bytes (18 kB) copied, 0.0651274 s, 284 kB/s
$ perl -ne 'print pack "C*", map {$_^0xd1} unpack "C*", $_' work.dex > decrypted.dex
$ hexdump -C decrypted.dex
00000000  64 65 78 0a 30 33 35 00  ca 5c c8 a5 08 01 d2 8d  |dex.035..\......|
00000010  9b 4c 50 5c 2d 8c ed 6d  6f 98 2c b9 09 2b cd 52  |.LP\,..+.R|

What do we find? Another Dex file! So, there was a Dex file embedded in a main dex file.

We can now proceed to analyzing this embedded Dex file. It contains a method named checkPassword, which unfortunately fails to decompile correctly:

checkPassword does not decompile correctly

Nevermind, we inspect the Smali code.

.method public static checkPassword(Ljava/lang/String;)Z
    .registers 7
    .parameter "password"

    .line 10
    const/16 v4, 0x11

    new-array v3, v4, [B

    fill-array-data v3, :array_3a

    .line 14
    .local v3, secret:[B
    const/4 v1, 0x0

This part initializes a byte array named secret with some data in array_3a :) Let's just parse the method a bit more to make sure there is no other trick.

.local v1, decompiling:Z
if-eqz v1, :cond_19

Check boolean decompiling. If false, jump to cond_19:

new-instance v4, Ljava/lang/String;
invoke-direct {v4, v3}, Ljava/lang/String;->([B)V
invoke-virtual {p0, v4}, Ljava/lang/String;->compareTo(Ljava/lang/String;)I
move-result v4
if-nez v4, :cond_38

This part compares the input string (the password the end-user provides) with the secret (v3). There is no decryption algorithm, so the secret is probably in plaintext:

    .array-data 0x1

In ASCII, this is : 'r@inbows n pwnies' (without the quotes).

You won't be able to enter that password on an Android emulator because the code detects emulators. This task is done by the static method isEmulator(), which checks the default values for a few properties such as ro.kernel.qemu, ro.hardware and Either you run the crackme on a real Ice Cream Sandwich (ICS) phone, or you hack the emulator. Knowing the password (and that I had plenty of other stuff to do...), I personnally stopped the fun here.

-- the Crypto Girl

Join the Discussion