Target: Coderess’s JCrackme#1
URL: http://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:
- Opens our keyfile.
- Checks the length of our keyfile via the checkLength-method.
- Reads all data to a string builder.
- Saves that character stored at length-101 in the string builder to a variable called endc.
- Deletes that character from the string builder.
- Checks that the characters read from the file = [A-Z0-9]
- 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.