BruteForceRunnable.java

package edu.jiangxin.apktoolbox.file.password.recovery.category.bruteforce;

import edu.jiangxin.apktoolbox.file.password.recovery.RecoveryPanel;
import edu.jiangxin.apktoolbox.file.password.recovery.checker.IChecker;
import edu.jiangxin.apktoolbox.file.password.recovery.exception.UnsupportedVersionException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class BruteForceRunnable implements Runnable {
    private static final Logger logger = LogManager.getLogger(BruteForceRunnable.class.getSimpleName());
    private final int taskId;
    private final BruteForceFuture bruteForceFuture;
    private final BruteForceTaskParam consts;

    RecoveryPanel panel;

    public BruteForceRunnable(int taskId, BruteForceTaskParam consts, BruteForceFuture bruteForceFuture, RecoveryPanel panel) {
        this.taskId = taskId;
        this.consts = consts;
        this.bruteForceFuture = bruteForceFuture;
        this.panel = panel;
    }

    @Override
    public void run() {
        long rangeBegin = taskId * consts.getPasswordSubRangeSize();
        long rangeEnd = (taskId + 1) * consts.getPasswordSubRangeSize() - 1;
        logger.info("rangeBegin: {}, rangeEnd: {}", rangeBegin, rangeEnd);
        String passwordOrNull = findPasswordInRange(rangeBegin, rangeEnd, consts.getChecker());
        bruteForceFuture.set(passwordOrNull);
    }

    /*
     * The findPasswordInRange method find the original password using md5 hash function
     * if a thread discovers the password, it returns original password string; otherwise, it returns null;
     */
    public String findPasswordInRange(long rangeBegin, long rangeEnd, IChecker checker) {
        int[] passwordIterator = new int[consts.getPasswordLength()];
        transformDecToBaseN(rangeBegin, passwordIterator, consts.getCharsSet().length());
        for (long iterator = rangeBegin; iterator <= rangeEnd; iterator++) {
            if (bruteForceFuture.isDone()) {
                logger.info("isDone: ThreadName: {}", Thread.currentThread().getName());
                return null;
            }
            if (bruteForceFuture.isCancelled()) {
                logger.info("isCancelled: ThreadName: {}", Thread.currentThread().getName());
                return null;
            }
            String password = transformIntToStr(passwordIterator, consts.getCharsSet());
            int result;
            try {
                result = checker.checkPassword(password) ? 1 : 0;
            } catch (UnsupportedVersionException e) {
                logger.error("UnsupportedVersionException, stop");
                result = -1;
            } catch (Exception e) {
                logger.error("Exception, stop", e);
                result = -1;
            } finally {
                panel.setCurrentPassword(password);
                panel.increaseProgressBarValue();
            }
            if (result == 0) {
                getNextCandidate(passwordIterator, consts.getCharsSet().length());
            } else if (result == 1) {
                return password;
            } else {
                return null;
            }
        }
        return null;
    }

    /*
     * The transformDecToBaseN transforms decimal into numArray that is base N number system
     * If you don't understand, refer to the homework01 overview
     */
    protected void transformDecToBaseN(long numInDec, int[] numArrayInBaseN, final int charSetLength) {
        for (int index = consts.getPasswordLength() - 1; index >= 0; index--) {
            numArrayInBaseN[index] = (int) (numInDec % charSetLength);
            numInDec = numInDec / charSetLength;
        }
    }

    /*
     * The getNextCandidate update the possible password represented by N base system
     */
    private void getNextCandidate(int[] candidateChars, final int charsSetLength) {
        int reminder = 1;
        for (int index = consts.getPasswordLength() - 1; index >= 0; index--) {
            candidateChars[index] += reminder;
            reminder = candidateChars[index] / charsSetLength;
            candidateChars[index] %= charsSetLength;
        }
    }

    /*
     * We assume that each character can be represented to a number : 0 (0) , 1 (1), 2 (2) ... a (10), b (11), c (12), ... x (33), y (34), z (35)
     * The transformIntToStr transforms int-array into string (numbers and lower-case alphabets)
     * int array is password represented by base-N system
     * return : password String
     *
     * For example, if you write the code like this,
     *     int[] pwdBase36 = {10, 11, 12, 13, 0, 1, 9, 2};
     *     String password = transfromIntoStr(pwdBase36);
     *     System.out.println(password);
     *     output is abcd0192.
     *
     */
    private static String transformIntToStr(int[] charsArray, final String charsSet) {
        char[] password = new char[charsArray.length];
        for (int i = 0; i < password.length; i++) {
            password[i] = charsSet.charAt(charsArray[i]);
        }
        return new String(password);
    }
}