DefendIO Android Crackmes – Jumbled

–[ Introduction ]

It has been some time since I’ve posted anything, so I thought I’d look at the Android crackmes posted by DefendIO (link)! The link to the crackmes has got a number of challenges, of different levels. There are a lot of starter challenges (level 1), a level 2 and a level 4 challenge!

This is the writeup of the level 2 challenge, called ‘jumbled cme’!

–[ The App ]

The app is a pretty simple app, essentially a mobile crackme, like the traditional crackmes for desktop. It asks you to provide a password and it boo’s you if it’s incorrect. The screen below shows the main screen of the application!

So let’s see what lives in that APK! To decompile the apk we’ll be using apktool to decompile the APK file and the AndroidManifest.xml file and unzip to decompress APK file, and then dex2jar in order to convert classes.dex to classes.jar. We shall be inspecting the JAR file with ByteCodeViewer!

I’ve solved some of the level 1 challenges prior to this, so I’ve had some vague intuition on the layout of the application. Firstly, the manifest. The contents of the AndroidManifest.xml are:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="net.defendio.cme.jumbled" platformBuildVersionCode="22" platformBuildVersionName="5.1.1-1819727">
    <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity android:label="@string/app_name" android:name="net.defendio.cme.jumbled.CmeActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:label="@string/title_activity_check_code" android:name="net.defendio.cme.jumbled.CheckCodeActivity" android:parentActivityName="net.defendio.cme.jumbled.CmeActivity">
            <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="net.defendio.cme.jumbled.CmeActivity"/>
        </activity>
        <activity android:label="about" android:name="net.defendio.cmelib.CmeAboutActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
</manifest>

–[ CmeActivity ]

This activity contains an EditText element and a button, as shown below:

Figure 1: Application main activity.
Figure 1: Application main activity.

Interestingly, looking at the CmeActivity.java file, we can see no programmatic assigment of the click listener for the button that will result in CheckCodeActivity to be called! The answer comes from inspecting the CmeActivity’s onCreate method:

protected void onCreate(Bundle paramBundle){
    super.onCreate(paramBundle);
    setContentView(2130968602);
}

The setContentView call contains what looks like a long identifier. Usually in Android the onCreate call is where you instantiate your views and click listeners and generally the UI-related setup. Based on the method’s name and the fact that in Adnroid identifiers for resources are integers, we should be able to find a link between this Java class and a layout resource! Looking inside the decompiled JAR, we find the following  R$layout.class:

package net.defendio.cme.jumbled;

public final class R$layout{
    // other identifiers here
    public static final int activity_cme = 2130968602;
    //yet more here
}

Looking for a file activity_cme.xml under res/layout is where our missing connection is! Under the definition of the Button element, we can find the click listener residing there!

<Button /*other stuff*/ android:onClick="sendMessage" />

Clicking this button will call the sendMessage function and instantiate the CheckCodeActivity, with our input text!

–[ CheckCodeActivity ]

This class appears to be the one responsible for verifying that our code is correct! Since this is an Activity, let’s take it from it’s onCreate method:

public void onCreate(Bundle paramBundle){
    super.onCreate(paramBundle);
    paramBundle = getIntent().getStringExtra("net.defendio.cme.jumbled.MESSAGE");
    TextView localTextView = new TextView(this);
    localTextView.setTextSize(20.0F);
    if (paramBundle.length() != 4) {
        paramBundle = "the code must be 4 characters long, try again";
    }
    for (;;){
        localTextView.setText(paramBundle);
        setContentView(localTextView);
        return;
        try{
            paramBundle = loadFromNetwork(paramBundle);
        }catch (IOException paramBundle){
            paramBundle = "error: couldn't access internets, try again later";
        }
    }
}

 

The onCreate method is taking our input from the Bundle passed to it by CmeActivity and checking that its length is 4. After this it’s calling a method called loadFromNetwork, with the Bundle as an argument. The loadFromNetwork method is a bit convoluted and ugly to read due to some simple obfuscation going on, containing infiniloops, non-descriptive variable names and manipulations of variables meant to confuse you! The main code responsible for checking that our code is correct is found inside an catch block, and it looks like this:

catch (Exception localException){
    for (;;){
        localObject3 = localObject1;
        int k = paramString.charAt(0);
        localObject3 = localObject1;
        int j = paramString.charAt(1);
        localObject3 = localObject1;
        int m = paramString.charAt(2);
        localObject3 = localObject1;
        int n = paramString.charAt(3);
        int i = k + j;
        k = j + k;
        j = m + n;
        m = n + m;
        localObject3 = localObject1;
        int i1 = k / 13;
        localObject3 = localObject1;
        int i2 = i / 11;
        localObject3 = localObject1;
        n = j / 17;
        paramString = str;
        if (i % 11 + k % 13 == 13){
            paramString = str;
            if (i1 + m % 23 == 15){
                paramString = str;
                if (n + m % 23 == 12){
                    paramString = str;
                    if (i2 + j % 17 == 21){
                        localObject3 = localObject1;
                        paramString = getString(2131099671);
                    }
                }
            }
        }
        localObject3 = paramString;
        if (localObject1 != null){
            localObject1.notify();
            localObject3 = paramString;
        }
    }
}

Let’s solve this!

–[ The Solution ]

The above crackme presents a challenge for many reasons. On the one hand, there’s a lot of variables being used in the checks that are a composite product of our pin. Secondly, the use of modular arithmetic (the % operator) means that there is more than one set of numbers satisfying a solution. In particular:

1%5 = 8%7 = 101%50 

Finally, from a mathematical standpoint, if we treat each if statement like an equation then we have a system of 4 equations with 8 unknowns and in general you can’t solve these. On the other hand, if we replace the variables in the nested if statements with their pin-originating components, we will have a system of four equations with four unknowns, something that is perfectly solvable. Here’s what I did, presented in “almost Java”:

if ((char(0) + char(1))%11 + (char(0) + char(1))%13== 13){
    if (((char(0) + char(1))/13) + (char(2)+char(3)) % 23 == 15){
        if (((char(2)+char(3))/17) + (char(2) + char(3)) % 23 == 12){
            if (((char(0)+char(1)) /11) + (char(2)+char(3)) % 17 == 21){
                //success!
            }
        }
    }
}

From this we can notice what the first if-statement is concerned only with the first two characters of the pin, the third if-statement is concerned with the third and fourth, and then the second and fourth if-statements are concerned with all four. To solve this challenge, I first generated a set of tuples that satisfy the first equation, a set of tuples that satisfy the third equation and then combined the two sets using the second, fourth and original test code in order to verify that the tests are correct!

Also, when I say solve you should understand “nested-for-loop-bruteforcing” 🙂 Here’s my cracking code (with some helper functions to construct strings and a helper class to simulate a tuple):

public class Tuple{
    private int item_1 = -1;
    private int item_2 = -1;
    
    public Tuple (int a, int b){
        item_1 = a;
        item_2 = b;        
    }        
    public int getA(){return item_1;}
    public int getB(){return item_2;}
    @Override 
    public String toString(){return item_1 + ", " + item_2;    }    
}

And the Cracker.java code:

import java.util.ArrayList;

public class Cracker{
    public ArrayList<Tuple> set01 = new ArrayList<Tuple>();
    public ArrayList<Tuple> set23 = new ArrayList<Tuple>();

    public static void main(String args[]){
        Cracker a = new Cracker();    
    }        

    public Cracker(){
        set01 = firstClause();
        set23 = thirdClause();
        combinedClauses(set01, set23);
    }

    public ArrayList<Tuple> firstClause(){
        ArrayList<Tuple> set01_t = new ArrayList<Tuple>();
        for (int i = 32 ; i < 127; i ++){
            for(int j = 32 ; j < 127; j++){
                if ((i + j)%11 + (i + j)%13== 13){
                    set01_t.add(new Tuple(i,j));
                }
            }
        }
        return set01_t;
    }

    public ArrayList<Tuple> thirdClause(){
        ArrayList<Tuple> set23_t = new ArrayList<Tuple>();
        for (int i = 32 ; i < 127; i ++){
            for(int j = 32 ; j < 127; j++){
                if (((i+j)/17) + (i + j) % 23 == 12){
                    set23_t.add(new Tuple(i,j));
                }
            }
        }
        return set23_t;
    }

    public void combinedClauses(ArrayList<Tuple> clause1, ArrayList<Tuple> clause3){
        for(Tuple t1 : clause1){
            for (Tuple t2: clause3){
                if (
                    ((t1.getA() + t1.getB())/13) + (t2.getA() + t2.getB()) % 23 == 15 &&
                    ((t1.getA()+ t1.getB()) /11) + (t2.getA() + t2.getB()) % 17 == 21 &&
                    realTest( getCode(t1,t2))
                ){
                    System.out.println(getCode(t1,t2));
                }
            }
        }
    }

    public String getCode(Tuple a , Tuple b){
        return new String( new char[]{    
                            (char) a.getA(), (char) a.getB(),
                            (char) b.getA(), (char) b.getB()
                        })
        ;
    }

    public boolean realTest(String paramString){
        int k = paramString.charAt(0);
        int j = paramString.charAt(1);
        int m = paramString.charAt(2);
        int n = paramString.charAt(3);
        int i = k + j;
        k = j + k;
        j = m + n;
        m = n + m;
        int i1 = k / 13;
        int i2 = i / 11;
        n = j / 17;
        if (i%11 + k%13== 13){
            if (i1 + m % 23 == 15){
                if (n + m % 23 == 12){
                    if (i2 + j % 17 == 21){
                        //success!
                        return true;
                    }
                }
            }
        }

        return false;
    }
}

Compiling both classes and running the Cracker yields a number of results. I was a bit skeptical about the correctness of my solution, but the android app thinks otherwise 🙂

Figure 2: The confirmation screen, informing us that we won!
Figure 2: The confirmation screen, informing us that we won!

–[ Conclusion ]

This crackme was very beginner-friendly and it demonstrated the basic workflow of how one would go about reversing an android application! Stay tuned for more 🙂 !

DefendIO Android Crackmes – Jumbled

2 thoughts on “DefendIO Android Crackmes – Jumbled

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.