ActionBruteForce.java

/*******************************************************************************
 * Copyhacked (H) 2012-2020.
 * This program and the accompanying materials
 * are made available under no term at all, use it like
 * you want, but share and discuss about it
 * every time possible with every body.
 *
 * Contributors:
 *      ron190 at ymail dot com - initial implementation
 *******************************************************************************/
package com.jsql.view.swing.manager.util;

import com.jsql.util.I18nUtil;
import com.jsql.util.LogLevelUtil;
import com.jsql.util.bruter.HashBruter;
import com.jsql.view.swing.manager.ManagerBruteForce;
import com.jsql.view.swing.util.I18nViewUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.swing.*;
import javax.swing.text.BadLocationException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Locale;

/**
 * Run a brute force attack.
 */
public class ActionBruteForce implements ActionListener, Runnable {
    
    /**
     * Log4j logger sent to view.
     */
    private static final Logger LOGGER = LogManager.getRootLogger();

    private final ManagerBruteForce bruteForceManager;
    
    private boolean isStopped = false;
    
    public ActionBruteForce(ManagerBruteForce bruteForceManager) {
        this.bruteForceManager = bruteForceManager;
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        if (this.bruteForceManager.getRun().getState() == StateButton.STOPPABLE) {
            
            this.bruteForceManager.getRun().setEnabled(false);
            this.isStopped = true;
            
        } else {

            if (StringUtils.isEmpty(this.bruteForceManager.getHash().getText())) {
                
                LOGGER.log(LogLevelUtil.CONSOLE_ERROR, () -> I18nUtil.valueByKey("BRUTEFORCE_EMPTY_HASH"));
                return;
                
            } else if (this.isRangeNotSelected()) {
                
                LOGGER.log(LogLevelUtil.CONSOLE_ERROR, () -> I18nUtil.valueByKey("BRUTEFORCE_CHARACTER_RANGE"));
                return;
                
            } else if (this.isLengthNotValid()) {
                
                LOGGER.log(LogLevelUtil.CONSOLE_ERROR, () -> I18nUtil.valueByKey("BRUTEFORCE_INCORRECT_MIN_MAX_LENGTH"));
                return;
            }

            new Thread(this, "ThreadDisplayBruteForce").start();
        }
    }

    private boolean isLengthNotValid() {
        return
            Integer.parseInt(this.bruteForceManager.getMaximumLength().getValue().toString())
            < Integer.parseInt(this.bruteForceManager.getMinimumLength().getValue().toString());
    }

    private boolean isRangeNotSelected() {
        return !this.bruteForceManager.getSpecialCharacters().isSelected()
            && !this.bruteForceManager.getUpperCaseCharacters().isSelected()
            && !this.bruteForceManager.getLowerCaseCharacters().isSelected()
            && !this.bruteForceManager.getNumericCharacters().isSelected();
    }

    @Override
    public void run() {
        
        // Reset the panel
        this.bruteForceManager.getRun().setText(I18nViewUtil.valueByKey("BRUTEFORCE_STOP"));
        this.bruteForceManager.getRun().setState(StateButton.STOPPABLE);
        this.bruteForceManager.getLoader().setVisible(true);
        this.bruteForceManager.getResult().setText(null);

        final var hashBruter = new HashBruter();

        this.initializeBruter(hashBruter);

        // Begin the reverse hashing process
        new Thread(hashBruter::tryBruteForce, "ThreadRunBruteForce").start();

        while (!hashBruter.isDone() && !hashBruter.isFound() && !this.isStopped) {
            
            hashBruter.setEndtime(System.nanoTime());

            try {
                Thread.sleep(1000);  // delay to update result panel
            } catch (InterruptedException e) {
                
                LOGGER.log(LogLevelUtil.IGNORE, e, e);
                Thread.currentThread().interrupt();
            }
            
            int selectionStart = this.bruteForceManager.getResult().getSelectionStart();
            int selectionEnd = this.bruteForceManager.getResult().getSelectionEnd();
            
            this.updateResult(hashBruter);

            this.bruteForceManager.getResult().setSelectionStart(selectionStart);
            this.bruteForceManager.getResult().setSelectionEnd(selectionEnd);
            
            if (this.isStopped) {
                
                hashBruter.setIsDone(true);
                hashBruter.setFound(true);
                
                break;
            }
        }

        this.displayResult(hashBruter);

        this.isStopped = false;
        this.bruteForceManager.getLoader().setVisible(false);
        this.bruteForceManager.getRun().setText(I18nViewUtil.valueByKey("BRUTEFORCE_START"));
        this.bruteForceManager.getRun().setEnabled(true);
        this.bruteForceManager.getRun().setState(StateButton.STARTABLE);
    }

    private void updateResult(final HashBruter hashBruter) {
        
        this.bruteForceManager.getResult().setText(I18nUtil.valueByKey("BRUTEFORCE_CURRENT_STRING") + ": " + hashBruter.getPassword());
        this.append(this.bruteForceManager.getResult(), I18nUtil.valueByKey("BRUTEFORCE_CURRENT_HASH") + ": " + hashBruter.getGeneratedHash() + "\n");
        this.append(this.bruteForceManager.getResult(), I18nUtil.valueByKey("BRUTEFORCE_POSSIBILITIES") + ": " + hashBruter.getNumberOfPossibilities());
        this.append(this.bruteForceManager.getResult(), I18nUtil.valueByKey("BRUTEFORCE_CHECKED_HASHES") + ": " + hashBruter.getCounter());
        this.append(this.bruteForceManager.getResult(), I18nUtil.valueByKey("BRUTEFORCE_ESTIMATED") + ": " + hashBruter.getRemainder());
        this.append(this.bruteForceManager.getResult(), I18nUtil.valueByKey("BRUTEFORCE_PERSECOND") + ": " + hashBruter.getPerSecond() + "\n");
        this.append(this.bruteForceManager.getResult(), hashBruter.calculateTimeElapsed());

        if (hashBruter.getPerSecond() != 0) {
            
            float remainingDuration = Float.parseFloat(Long.toString(hashBruter.getRemainder())) / hashBruter.getPerSecond();
            
            this.append(this.bruteForceManager.getResult(), (
                I18nUtil.valueByKey("BRUTEFORCE_TRAVERSING_REMAINING") + ": "
                + Math.round(Math.floor(remainingDuration / 60f / 60.0f / 24f)) + I18nUtil.valueByKey("BRUTEFORCE_DAYS") + StringUtils.SPACE
                + Math.round(Math.floor(remainingDuration / 60f / 60f % 24))    + I18nUtil.valueByKey("BRUTEFORCE_HOURS") + StringUtils.SPACE
                + Math.round(Math.floor(remainingDuration / 60f % 60))          + I18nUtil.valueByKey("BRUTEFORCE_MINUTES") + StringUtils.SPACE
                + Math.round(remainingDuration % 60)                            + I18nUtil.valueByKey("BRUTEFORCE_SECONDS")
            ));
        }

        this.append(
            this.bruteForceManager.getResult(),
            String.format(
                "%s: %s%%",
                I18nUtil.valueByKey("BRUTEFORCE_PERCENT_DONE"),
                100 * (float) hashBruter.getCounter() / hashBruter.getNumberOfPossibilities()
            )
        );
    }

    private void initializeBruter(final HashBruter hashBruter) {
        
        hashBruter.setMinLength(Integer.parseInt(this.bruteForceManager.getMinimumLength().getValue().toString()));
        hashBruter.setMaxLength(Integer.parseInt(this.bruteForceManager.getMaximumLength().getValue().toString()));

        if (this.bruteForceManager.getSpecialCharacters().isSelected()) {
            hashBruter.addSpecialCharacters();
        }
        
        if (this.bruteForceManager.getUpperCaseCharacters().isSelected()) {
            hashBruter.addUpperCaseLetters();
        }
        
        if (this.bruteForceManager.getLowerCaseCharacters().isSelected()) {
            hashBruter.addLowerCaseLetters();
        }
        
        if (this.bruteForceManager.getNumericCharacters().isSelected()) {
            hashBruter.addDigits();
        }
        
        if (StringUtils.isNotEmpty(this.bruteForceManager.getExclude().getText())) {
            hashBruter.excludeChars(this.bruteForceManager.getExclude().getText());
        }

        hashBruter.setType((String) this.bruteForceManager.getHashTypes().getSelectedItem());
        hashBruter.setHash(
            this.bruteForceManager
            .getHash()
            .getText()
            .toUpperCase(Locale.ROOT)
            .replaceAll("[^a-zA-Z0-9]", StringUtils.EMPTY)
            .trim()
        );
    }

    private void displayResult(final HashBruter hashBruter) {
        
        // Display the result
        if (this.isStopped) {
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, () -> I18nUtil.valueByKey("BRUTEFORCE_ABORTED"));
        } else if (hashBruter.isFound()) {
            
            this.append(
                this.bruteForceManager.getResult(),
                String.format(
                    "%n%s:%n%s => %s",
                    I18nUtil.valueByKey("BRUTEFORCE_FOUND_HASH"),
                    hashBruter.getGeneratedHash(),
                    hashBruter.getPassword()
                )
            );

            LOGGER.log(
                LogLevelUtil.CONSOLE_SUCCESS,
                "{}: {} => {}",
                () -> I18nUtil.valueByKey("BRUTEFORCE_FOUND_HASH"),
                hashBruter::getGeneratedHash,
                hashBruter::getPassword
            );
        } else if (hashBruter.isDone()) {
            
            this.append(
                this.bruteForceManager.getResult(),
                "\n"+ I18nUtil.valueByKey("BRUTEFORCE_HASH_NOT_FOUND")
            );
            
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, () -> I18nUtil.valueByKey("BRUTEFORCE_HASH_NOT_FOUND"));
        }
    }
    
    public void append(JTextPane textPane, String text) {
        try {
            textPane.getDocument().insertString(
                textPane.getDocument().getLength(),
                (textPane.getDocument().getLength() == 0 ? StringUtils.EMPTY : "\n") + text,
                null
            );
        } catch (BadLocationException e) {
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
        }
    }
}