// Cracker.java
/*
Generates SHA hashes of short strings in parallel.
*/
import java.security.*;
import java.util.Arrays;
import java.util.concurrent.Semaphore;
public class Cracker {
// Array of chars used to produce strings
public static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz0123456789.,-!ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
public static final String ALGORITHM = "SHA";
private static byte[] target;
private static int maxLength;
private static int minLength;
private static boolean print;
private static Semaphore workersAreDone;
/*
Given a byte[] array, produces a hex String,
such as "234a6f". with 2 chars for each byte in the array.
(provided code)
*/
public static String hexToString(byte[] bytes) {
StringBuffer buff = new StringBuffer();
for (int i=0; i<bytes.length; i++) {
int val = bytes[i];
val = val & 0xff; // remove higher bits, sign
if (val<16) buff.append('0'); // leading 0
buff.append(Integer.toString(val, 16));
}
return buff.toString();
}
/*
Given a string of hex byte values such as "24a26f", creates
a byte[] array of those values, one byte value -128..127
for each 2 chars.
(provided code)
*/
public static byte[] hexToArray(String hex) {
byte[] result = new byte[hex.length()/2];
for (int i=0; i<hex.length(); i+=2) {
result[i/2] = (byte) Integer.parseInt(hex.substring(i, i+2), 16);
}
return result;
}
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Args: target length [workers]");
System.exit(1);
}
// args: targ len [num]
String targ = args[0];
int len = Integer.parseInt(args[1]);
int num = 1;
int minLen = 0;
if (args.length>2) {
num = Integer.parseInt(args[2]);
}
if(args.length > 3) {
minLen = Integer.parseInt(args[3]);
}
// a! 34800e15707fae815d7c90d49de44aca97e2d759
// xyz 66b27417d37e024c46526c2f6d358a754fc552f3
// 2k7! c5e478e7da53b70f0fabcdefa082e1d1c5a2bc6d
maxLength = len;
minLength = minLen;
print = targ.equalsIgnoreCase("print");
target = (print ? null : hexToArray(targ));
if(print) System.out.println("target: " + targ);
System.out.println("maximum length: "+len);
System.out.println("minimum length: "+minLen);
System.out.println();
workersAreDone = new Semaphore(0);
forkOffWorkers(num);
waitForWorkers(num);
System.out.println("all done");
}
// ----------------- Private ------------------- //
// forks off the specified number of workers
private static void forkOffWorkers(int num) {
int segment = CHARS.length/num;
for(int i = 0; i < num; i++) {
int end = (i == num - 1 ? CHARS.length : (i+1) * segment);
Thread worker = new Thread(new Worker(i*segment, end));
worker.start();
}
}
// waits until all of the workers finish
private static void waitForWorkers(int num) {
try {
workersAreDone.acquire(num);
} catch (Exception ignored) {}
}
// inner Worker class
private static class Worker implements Runnable {
private int begin;
private int end;
private MessageDigest digest;
public Worker(int begin, int end) {
this.begin = begin;
this.end = end;
try {
digest = MessageDigest.getInstance(ALGORITHM);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public void run() {
for(int i = begin; i < end; i++)
search(Character.toString(CHARS[i]));
workersAreDone.release();
}
// recursive search function that generates all possible string
// combinations given a starter string
private void search(String str) {
if(str.length() >= minLength) {
digest.reset();
digest.update(str.getBytes());
byte[] bytes = digest.digest();
if(print) printStr(str, bytes);
else if(MessageDigest.isEqual(bytes, target)) printStr("match:"+str, bytes);
}
if(str.length() >= maxLength) return;
for(int i = 0; i < CHARS.length; i++)
search(str + CHARS[i]);
}
// prints the given string and byte array
private void printStr(String str, byte[] bytes) {
System.out.println(str + " " + hexToString(bytes));
}
}
}