Friday, 23 November 2007

BF it!

Hey guys,

If you have been following my guide until now, probably you have known enough to start working with basic challenges. Let's get to some cracking. Today, I'll show you how to code a brute-force program.

I created this little crackme so we can have something to play with: CrackMe1. For simplicity, it's just a simple console application. If you run it, it will ask for a password and check if the password is correct.

Download the file and decompile it with jad, you'll see the code:

// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3)
// Source File Name: CrackMe1.java

import java.io.*;

public class CrackMe1
{

public CrackMe1()
{
}

public static boolean check(String s)
{
if(s.length() != 4)
return false;
int n = 1;
for(int i = 0; i < s.length(); i++)
n *= s.charAt(i);

n ^= s.hashCode();
return n == 0x705a69c;
}

public static void main(String args[])
throws IOException
{
System.out.print("Enter password: ");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = reader.readLine();
if(check(s))
System.out.println("Congrats!");
else
System.out.println("Go to hell!");
}
}

Good thing in Java all method names are meaningful. The code is self-explanation. The program reads a string from standard input, and check if it is the right password. The string must have a length of 4 characters. And if you take the product of the ASCII codes and xor with the hashCode of the string the result must be 0x705a69c (117810844).

Your first attempt to write a bf program should result in something like:

public class CrackMe1Solver {

public static boolean check(String s) {
// copy the decompiled code here
}

public static void main(String[] args) {

char[] c = new char[4];
// OK, let's assume the password is all lowercase
for (c[0] = 'a'; c[0] < 'z'; c[0]++)
for (c[1] = 'a'; c[1] < 'z'; c[1]++)
for (c[2] = 'a'; c[2] < 'z'; c[2]++)
for (c[3] = 'a'; c[3] < 'z'; c[3]++) {
// this is how you create a string from an array of characters
String s = new String(c);
if (check(s)) System.out.println(s);
}
}

}

This code works fine, try running it and you'll get the solution (a very famous word :P). However, that code is not the best. I'll show you how to make it better.

Firstly, it's kinda duplicate and inconvenient to use the same constants for bf range (a-z). A better practice is to declare them as constants so that you can change them easily. Thus:

// Constants are declared with the final keyword
// and conventionally written in uppercase
final char MIN = 'a';
final char MAX = 'z';
for (c[0] = MIN; c[0] < MAX; c[0]++)
...

One more thing, it's fortunate that the method check() is declared public and static. This means you can reuse it from another class instead of writing it again.

Here's the final code:

public class CrackMe1Solver {

public static void main(String[] args) {

char[] c = new char[4];
final char MIN = 'a';
final char MAX = 'z';
for (c[0] = MIN; c[0] < MAX; c[0]++)
for (c[1] = MIN; c[1] < MAX; c[1]++)
for (c[2] = MIN; c[2] < MAX; c[2]++)
for (c[3] = MIN; c[3] < MAX; c[3]++) {
String s = new String(c);
if (CrackMe1.check(s)) System.out.println(s);
}
}

}

7 comments:

ozehka said...

Great to have the java tutorial, finally!
Looking forward to the next ones :)

Nicoxx said...

Hi Quang, I successfully used your password cracking method in some challenges ;-). But I don't find a efficient way to do it if we don't know the lenght of the password we have to crack. What is the best method to use ? Thanks for your help.

quangntenemy said...

Read on to my post about dictionary attack. That's the second method for password cracking.

Kelvin said...

Hi.

In the check() method, n was intialized to 0, so no matter what you multiply it with, it's value is 0 after the loop is executed.

I mean i dont understand what the point of multiplying n with characters in the string is.

Why dont we just write it like this? It's the same thing.

public static boolean check(String s)
{
if(s.length() != 4)
return false;
int n = 0 ^ s.hashCode();
return n == 0x30f4bc;
}

Thank you very much :)

quangntenemy said...

Oops that's a bug in my example. It should have been 1. Fixing it. Thanks!

Anonymous said...

quangntenemy, with all due respect to your intellect I gotta say: the method used for bruteforcing is garbage from a professional software-developers point of few. Do you want to add yet another loop, or even worse, yet another function/method, for each character added to the password?

Recursion is the answer to this ugly piece of code and I doubt it'd have a performance-impact you'd experience on today's machines.

I also think that it'd be better to teach good software design than to recite api docs, like you did in an other post.

quangntenemy said...

Oops looks like it's been a long time.

I agree that the code is ugly but keep in mind the KISS principle.

Using multiple nested for loops is the most straightforward way to go about bruteforcing. It takes almost no time to code, and provides the best performance. When you're cracking a password, reducing coding time and execution time is an important factor.

Finally, when the need for another for loop arises, it's not the time to worry about coding style or software design. Since the complexity increases exponentially, you'll have to consider whether it's time to continue bruteforcing, or switching to another cracking method ;)