// Cracker.java
/*
Generates SHA hashes of short strings in parallel.
*/
import java.security.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import javax.swing.SwingUtilities;
public class Cracker extends Thread {
public static void main(String[] args) {
String targets[] = {"c5e478e7da53b70f0fabcdefa082e1d1c5a2bc6d", "66b27417d37e024c46526c2f6d358a754fc552f3", "34800e15707fae815d7c90d49de44aca97e2d759"};
Cracker cracker = new Cracker(targets, 4, 0, 5, null);
cracker.start();
}
// Array of chars used to produce strings
public static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz0123456789.,-!ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
public static final String ALGORITHM = "SHA";
private ConcurrentHashMap<HashContainer, String> targets;
private int maxLength;
private int minLength;
private int numWorkers;
private Worker[] workers;
private Semaphore workersAreDone;
private HashCracker crackerGUI;
/*
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 Cracker(String[] targs, int maxLength, int minLength, int numWorkers, HashCracker crackerGUI) {
this.maxLength = maxLength;
this.minLength = minLength;
this.numWorkers = numWorkers;
this.crackerGUI = crackerGUI;
this.targets = new ConcurrentHashMap<HashContainer, String>();
for(int i = 0; i < targs.length; i++) {
this.targets.put(new HashContainer(hexToArray(targs[i])), targs[i]);
}
workersAreDone = new Semaphore(0);
}
public void run() {
forkOffWorkers();
waitForWorkers();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
crackerGUI.signalDone();
}
});
}
// ----------------- Private ------------------- //
// forks off the specified number of workers
private void forkOffWorkers() {
workers = new Worker[numWorkers];
int segment = CHARS.length/numWorkers;
for(int i = 0; i < numWorkers; i++) {
int end = (i == numWorkers - 1 ? CHARS.length : (i+1) * segment);
Worker worker = new Worker(i*segment, end);
workers[i] = worker;
worker.start();
}
}
// waits until all of the workers finish
private void waitForWorkers() {
try {
workersAreDone.acquire(numWorkers);
} catch (InterruptedException ignored) {
for(Worker worker : workers) {
worker.interrupt();
}
}
}
// inner Worker class
private class Worker extends Thread {
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(this.isInterrupted()) return;
if(str.length() >= minLength) {
digest.reset();
digest.update(str.getBytes());
byte[] bytes = digest.digest();
if(targets.get(new HashContainer(bytes)) != null) {
targets.remove(new HashContainer(bytes));
printStr(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(final String str, byte[] bytes) {
//System.out.println(str + " " + hexToString(bytes));
final String hash = hexToString(bytes);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
crackerGUI.addSolution(str, hash);
}
});
}
}
}