Author Topic: [Java] ArchiveCracker  (Read 5224 times)

0 Members and 1 Guest are viewing this topic.

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
[Java] ArchiveCracker
« on: January 02, 2013, 03:28:47 pm »
TheAlchemist and Ragehottie asked me to create a zip cracker a while ago. I am sorry that it took so long, but I didn't forget your request. This program can actually crack other password protected archives too (i.e. rar).

This is a simple command line program by now. I commented the code so you can take it as example. The cracking is relatively slow, because I didn't use any threads. But I think this way it is more understandable for you.
I will add threads and a GUI later and make it a EZ release then. However, I attached the jar of this program too.
Don't forget to give credit if you use this code for your own programs.

Usage: java -jar archivecracker <archive> [<wordlist>]

wordlist is a file which contains passwords for a wordlist attack. If you don't give a wordlist, it will generate the words, but only a-z (you can change it, though).

Starting point of the program is the CLI.

Code: (Java) [Select]
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import net.sf.sevenzipjbinding.SevenZipException;

public class CLI {

    private final static String USAGE = "usage: java -jar archivecracker <archive> [<wordlist>]";
    private final static String VERSION = "ArchiveCracker v0.1 by Deque";

    public static void main(String[] args) throws SevenZipException,
            IOException {
        System.out.println(VERSION);
        System.out.println();
        if (args.length == 0) {
            System.out.println(USAGE);
        } else {
            char[] alphabet = WordlistGen.initAllowedCharacters('a', 'z');
            ArchiveCracker cracker = new ArchiveCracker(alphabet);
            String pass = crack(args, cracker);
            printResult(cracker, pass);
        }
    }

    private static void printResult(ArchiveCracker cracker, String pass) {
        System.out.println();
        if (pass != null) {
            System.out.println("password found: " + pass);
            System.out.println("words tried: " + cracker.getWordsTried());
        } else {
            System.out.println("no password found");
        }
    }

    private static String crack(String[] args, ArchiveCracker cracker)
            throws FileNotFoundException, SevenZipException, IOException {
        String pass = null;
        if (args.length == 1) {
            if (new File(args[0]).exists()) {
                pass = cracker.crack(args[0]);
            } else {
                System.err.println("file not found");
            }
        } else {
            if (new File(args[0]).exists() && new File(args[1]).exists()) {
                pass = cracker.crack(args[0], args[1]);
            } else {
                System.err.println("file not found");
            }
        }
        return pass;
    }
}

This class contains the main functionality, the ArchiveCracker.
Note that I had to use a little hack to get the functionality I wanted. The 7-Zip-JBinder library is not designed for checking passwords. It would actually try to extract the file if the password is correct, which I don't want. Because of that I throw a PasswordFoundException in the output stream instead of writing anything. This is not a good way, since exceptions are not there to define the program logic, but it is the only possible way with this library (and others too).

Code: (Java) [Select]
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;

import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ISevenZipInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;

/**
 *
 * @author Deque
 *
 */

public class ArchiveCracker {

    private static final int MAX_WORDLENGTH = 6;
    private static final int DOT_MARK = 100;
    private final char[] alphabet;
    private long words;
   
    /**
     * Prepares cracker with given alphabet.
     * @param alphabet
     */
    public ArchiveCracker(char[] alphabet) {
        this.alphabet = alphabet;
    }

    /**
     * Tries to crack the archive by generating words from the given alphabet.
     *
     * @param archivename
     * @return password if found, null otherwise
     * @throws FileNotFoundException
     * @throws SevenZipException
     */
    public String crack(String archivename) throws FileNotFoundException,
            SevenZipException {
        words = 0;
        for (int wordlength = 0; wordlength <= MAX_WORDLENGTH; wordlength++) {
            WordlistGen gen = new WordlistGen(alphabet, wordlength);
            while (gen.hasNext()) {
                String pass = gen.generateNext();
                words++;
                if (isValidPass(pass, archivename)) {
                    return pass;
                }
                if (words % DOT_MARK == 0) {
                    System.out.print(".");
                }
            }
        }
        return null;
    }

    /**
     *
     * @return number of passwords tried
     */
    public long getWordsTried() {
        return words;
    }

    /**
     * Tries to crack the archive using the given wordlist.
     *
     * @param archivename
     * @param wordlist
     * @return password if found, null otherwise
     * @throws SevenZipException
     * @throws IOException
     */
    public String crack(String archivename, String wordlist)
            throws SevenZipException, IOException {
        words = 0;
        try (BufferedReader in = new BufferedReader(new FileReader(wordlist)) {
            String pass;
            while ((pass = in.readLine()) != null) { // read next line
                words++; // count the passwords tried
                if (isValidPass(pass, archivename)) { // validate password
                    return pass;
                }
                printDotIfMarkReached();
            }
        }
        return null;
    }

    /**
     * Prints a dot ever DOT_MARK words.
     */
    private void printDotIfMarkReached() {
        if (words % DOT_MARK == 0) {
            System.out.println(".");
        }
    }

    /**
     * Checks wether the given password is the correct password of the archive.
     *
     * @param password
     * @param filename
     * @return true if password is valid, false otherwise
     * @throws FileNotFoundException
     * @throws SevenZipException
     */
    private boolean isValidPass(String password, String filename)
            throws FileNotFoundException, SevenZipException {

        RandomAccessFile file = new RandomAccessFile(filename, "r");

        // call with null to use autodetection of archive type
        ISevenZipInArchive in = SevenZip.openInArchive(null,
                new RandomAccessFileInStream(file));

        ISimpleInArchive archiveInterface = in.getSimpleInterface();

        ISequentialOutStream out = new ISequentialOutStream() {
            @Override
            public int write(byte[] data) throws SevenZipException {
                // password was correct at this point
                // throw exception to get back instead of extracting the archive
                throw new PasswordFoundException();
            }
        };

        try {
            for (ISimpleInArchiveItem item : archiveInterface.getArchiveItems()) {
                // try to open archive item with given password
                // prepared out stream will throw PasswordFoundException instead
                // of extracting the archive
                item.extractSlow(out, password);
            }
        } catch (PasswordFoundException e) {
            return true;
        } finally {
            close(file, in);
        }
        return false;

    }

    /**
     * Closes the streams. Can't use try-with-resources statement here, because
     * these Streams do not implement Closable.
     *
     * @param file
     * @param in
     */
    private void close(RandomAccessFile file, ISevenZipInArchive in) {
        if (in != null) {
            try {
                in.close();
            } catch (SevenZipException e) {
                e.printStackTrace();
            }
        }
        if (file != null) {
            try {
                file.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @SuppressWarnings("serial")
    private class PasswordFoundException extends SevenZipException {}

}
« Last Edit: January 02, 2013, 03:33:06 pm by Deque »

Offline Deque

  • P.I.N.N.
  • Global Moderator
  • Overlord
  • *
  • Posts: 1203
  • Cookies: 518
  • Programmer, Malware Analyst
    • View Profile
Re: [Java] ArchiveCracker
« Reply #1 on: January 02, 2013, 03:33:45 pm »

Last but not least the word generation. I explained it in a tutorial here: http://evilzone.org/tutorials/%28tut%29-create-a-wordlist-generator-%28i-e-for-bruteforcing%29/msg36286/#msg36286
This is a slightly modified version. I made it more flexible, so it generates the next word only if needed.

Code: (Java) [Select]
public class WordlistGen {

    private int wordNumber;
    private final int wordlength;
    private final char[] alphabet;
    private final long maxWords;
    private final int radix;

    /**
     * Inits a wordlist generator with given alphabet and wordlength
     * @param alphabet
     * @param wordlength
     */
    public WordlistGen(char[] alphabet, int wordlength) {
        this.wordlength = wordlength;
        this.alphabet = alphabet;
        this.maxWords = (long) Math.pow(alphabet.length, wordlength);
        this.radix = alphabet.length;
    }

    /**
     *
     * @return next generated word, null if no word is left
     */
    public String generateNext() {
        if (hasNext()) {
            int[] indices = convertToRadix(wordNumber);
            char[] word = new char[wordlength];
            for (int k = 0; k < wordlength; k++) {
                word[k] = alphabet[indices[k]];
            }
            wordNumber++;
            return new String(word);
        }
        return null;
    }

    /**
     *
     * @return true if there are more words to generate, false otherwise
     */
    public boolean hasNext() {
        return (wordNumber < maxWords);
    }

    private int[] convertToRadix(long number) {
        int[] indices = new int[wordlength];
        for (int i = wordlength - 1; i >= 0; i--) {
            if (number > 0) {
                int rest = (int) (number % radix);
                number /= radix;
                indices[i] = rest;
            } else {
                indices[i] = 0;
            }

        }
        return indices;
    }

    /**
     * Shortcut to generate alphabet ASCII ranges
     * @param start
     * @param end
     * @return
     */
    public static char[] initAllowedCharacters(int start, int end) {
        char[] allowedCharacters = new char[end - start + 1];
        for (int i = start; i <= end; i++) {
            allowedCharacters[i - start] = (char) i;
        }
        return allowedCharacters;
    }

}

--------------------------------------------------------------------

Sorry for double post, but this last part of the text wasn't shown in my inital post.

Offline Ragehottie

  • Knight
  • **
  • Posts: 313
  • Cookies: -9
  • Hack to learn, not learn to hack.
    • View Profile
Re: [Java] ArchiveCracker
« Reply #2 on: January 02, 2013, 09:27:56 pm »
Very cool  :D  Thanks so much +1
Blog: rexmckinnon.tumblr.com