Coderess's JCrackme#1 Tutorial

September 13, 2015 - Reading time: 4 minutes

Target: Coderess's JCrackme#1

URLhttp://crackmes.de/users/coderess/jcrackme1/

Protection: Keyfile

Description: Crackme with a keyfile protection

Tools: Java decompiler, Python

First open the crackme in a Java decompiler and take a look at the JCrackme.class, there we will find the event handler for the "Check it!"-button.

    private void jButton1ActionPerformed(final ActionEvent evt) {
        if (checkFileLength("keyfile.txt") < 0) {
            JOptionPane.showMessageDialog(this, "Bad keyfile! Try more harder it is really easy!", "Error data", 0);
            return;
        }
        JOptionPane.showMessageDialog(this, "Your keyfile successfully adopted!\nOur Congratulations! Write a solution and keygen!", "Successfull data", 1);
    }

So the result from checkFileLength should be under 1 for us to have a valid key. Lets take a look at the checkFileLength-method.

    public static int checkFileLength(final String fileName) {
        final File f = new File(fileName);
        final StringBuilder sb = new StringBuilder();
        long length = 0L;
        if (!f.exists()) {
            return -1;
        }
        length = f.length();
        System.out.println(length);
        if (checkLength(length) == 1) {
            return -1;
        }
        try {
            final BufferedReader br = new BufferedReader(new FileReader(f.getAbsoluteFile()));
            try {
                String s;
                while ((s = br.readLine()) != null) {
                    sb.append(s);
                    sb.append("\n");
                }
                final int l1 = (int)length - 101;
                final char endc = sb.charAt(l1);
                sb.deleteCharAt(l1);
                System.out.println(endc);
                for (int i = 0; i <= l1; ++i) {
                    if (sb.charAt(i) != '\n') {
                        if ((sb.charAt(i) < 'A' || sb.charAt(i) > 'Z') && (sb.charAt(i) < '0' || sb.charAt(i) > '9')) {
                            if (sb.charAt(i) < 'A' || sb.charAt(i) > 'F') {
                                return -1;
                            }
                        }
                    }
                }
                int a = endc;
                a ^= 0x30;
                if (a == 19) {
                    return 0;
                }
            }
            finally {
                br.close();
            }
            System.out.println();
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
        return -1;
    }

So the keyfile check routine is doing the following:

  1. Opens our keyfile.
  2. Checks the length of our keyfile via the checkLength-method.
  3. Reads all data to a string builder.
  4. Saves that character stored at length-101 in the string builder to a variable called endc.
  5. Deletes that character from the string builder.
  6. Checks that the characters read from the file = [A-Z0-9]
  7. Finally checks if the character stored in endc equals 19 XOR 0x30 = 0x23 ('#')

Simple enough, but we still need to know what the checkLength-method does. Lets take a look at that.

    public static byte checkLength(final long x) {
        long b = 0L;
        long x_ = x + 4L;
        x_ <<= 3;
        x_ += 16L;
        x_ -= 7L;
        b = x_ - 80849L;
        if (b == 0L) {
            return 0;
        }
        return 1;
    }

Here we see that some calculations are made using the size of our keyfile, and that the result should be 0. Lets find out the expected length.

80849 + 7 - 16 = 80840
80840 >> 3 = 10105
10105 - 4 = 10101

So the expected file size is 10101 and should contain [A-Z0-9]10000 + '#' + [A-Z0-9]100.

Following is a keymaker in Python.

import string
import random

key_data = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(10000))
key_data += "#"
key_data += "".join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(100))

f = open('keyfile.txt','w')
f.write(key_data)
f.close()

When we run the crackme and validates the keyfile made by the keymaker we get the success-message.