Reverse engineering, Windows internals, x86 magic, low level programming, and everything else I feel like writing about.

CrySyS SecChallenge 2020: Super battle droids! Take them down!

Take down these super battle droids before the droidekas arrive! Actually, there is only one droid, so this challenge should be easy for you.

I wonder if you are a static or a dynamic person…

Categories

Offensive, Reverse Engineering

Files

Solution

Let’s start off with unpacking all the stuff from the apk with the usual tools:

user@ubuntu:~$ apktool superbattledroids.apk
user@ubuntu:~$ unzip superbattledroids.apk classes.dex
user@ubuntu:~$ d2j-dex2jar classes.dex
user@ubuntu:~$ java -jar cfr-0.148.jar classes-dex2jar.jar --outputdir decompiled

When traversing the newly made directories it’s hard not to notice hu/secchallenge20/superbattledroids/HiddenActivity.java, and the files in res/raw/ named secret1.png, secret2.txt and secret3.txt. I commented the code of HiddenActivity.java with the resource names and values for the resource identifiers, by following the number through res/values/public.xml and res/values/strings.xml.

/*
 * Decompiled with CFR 0.148.
 *
 * Could not load the following classes:
 *  android.content.res.Resources
 *  android.graphics.Bitmap
 *  android.graphics.BitmapFactory
 *  android.graphics.BitmapFactory$Options
 *  android.graphics.Color
 *  android.os.Bundle
 *  android.view.View
 *  android.widget.TextView
 */
package hu.secchallenge20.superbattledroids;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;

public class HiddenActivity
extends v {
    public static boolean p = false;

    /*
     * Enabled aggressive block sorting
     * Enabled unnecessary exception pruning
     * Enabled aggressive exception aggregation
     */
    @Override
    public void onCreate(Bundle object) {
        Bitmap bitmap;
        int n10;
        Object object2;
        int n11;
        String[] arrstring;
        ArrayList<Integer> arrayList;
        block10: {
            block9: {
                super.onCreate((Bundle)object);
                this.setContentView(2131427356);
                object = new BitmapFactory.Options();
                n11 = 0;
                ((BitmapFactory.Options)object).inScaled = false;
                bitmap = BitmapFactory.decodeResource((Resources)this.getResources(), (int)2131558400, (BitmapFactory.Options)object); // secret1.png
                arrstring = this.getResources().openRawResource(2131558401);
                object2 = new BufferedReader(new InputStreamReader((InputStream)arrstring));
                arrayList = null;
                try {
                    object = ((BufferedReader)object2).readLine();
                }
                catch (IOException iOException) {
                    object = null;
                    break block9;
                }
                try {
                    arrstring.close();
                    ((BufferedReader)object2).close();
                    break block10;
                }
                catch (IOException iOException) {}
            }
            ((Throwable)object2).printStackTrace();
        }
        arrstring = ((String)object).replace("[", "").replace("]", "").split(", ");  // secret2.txt
        InputStream inputStream = this.getResources().openRawResource(2131558402);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        object = arrayList;
        try {
            object = object2 = bufferedReader.readLine(); // object = secret3.txt
            inputStream.close();
            object = object2;
            bufferedReader.close();
            object = object2;
        }
        catch (IOException iOException) {
            iOException.printStackTrace();
        }
        arrayList = new ArrayList<Integer>();
        int n12 = arrstring.length;
        int n13 = 0;
        for (n10 = 0; n10 < n12; ++n10) {
            arrayList.add(Color.red((int)bitmap.getPixel(n13 += Integer.parseInt(arrstring[n10]), 0)));
        }
        object2 = this.getResources().getString(2131624000); // "start" = "cd20{"
        n10 = n11;
        do {
            n13 = arrayList.size();
            object2 = wc.a((String)object2);
            if (n10 >= n13) break;
            n13 = Integer.parseInt(this.getResources().getString(2131623999)); // "size" = "300"
            n11 = (Integer)arrayList.get(n10);
            ((StringBuilder)object2).append(((String)object).charAt(Integer.parseInt(this.getResources().getString(2131623988 /* "life" = "42" */)) + (n11 + n13 * n10)));
            object2 = ((StringBuilder)object2).toString();
            ++n10;
        } while (true);
        ((StringBuilder)object2).append(this.getResources().getString(2131623982)); // "end" = "}"
        object = ((StringBuilder)object2).toString();
        object2 = (TextView)this.findViewById(2131230788);
        if (!p) {
            object = "Calculations are done!";
        }
        object2.setText((CharSequence)object);
    }
}

Time to get the resources into an easier to use format: convert the image with GIMP into C source file and add this convenience function:

unsigned char get_pixel(long idx)
{
  return gimp_image.pixel_data[idx * 3];
}

And add the other two text files straight into the program with minor modifications:

int secret2[] = { 12, 122, 247, 231, 51, 65, 102, 178, 102, 127, 10, 55, 82, 36, 7, 76, 101, 156, 126, 225, 90, 73, 151, 117, 168, 241, 179, 113 };
const char secret3[] = R"qqq(<contents of secret3.txt>)qqq";

As a last step, we C++-ify the java code for decryption and run it:

int main()
{
  std::vector<int> arrayList;
  int n12 = std::size(secret2);
  int n13 = 0;
  for (int n10 = 0; n10 < n12; ++n10) {
    arrayList.push_back(get_pixel(n13 += secret2[n10]));
  }
  std::string object2 = "cd20{";
  int n10 = 0;
  do {
    n13 = arrayList.size();
    //object2 = wc.a((String)object2);
    if (n10 >= n13) break;
    n13 = 300;
    auto n11 = arrayList[n10];
    object2 += secret3[(42 + (n11 + n13 * n10))];
    ++n10;
  } while (true);
  object2 += "}";
  std::cout << object2;
  return 0;
}

Output:

cd20{D0_0r_d0_n0t_Th3r3_1s_n0_try}