RecoveryPanel.java
package edu.jiangxin.apktoolbox.file.password.recovery;
import edu.jiangxin.apktoolbox.file.password.recovery.category.CategoryFactory;
import edu.jiangxin.apktoolbox.file.password.recovery.category.CategoryType;
import edu.jiangxin.apktoolbox.file.password.recovery.category.ICategory;
import edu.jiangxin.apktoolbox.file.password.recovery.checker.*;
import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdParty7ZipChecker;
import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdPartyRarChecker;
import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdPartyWinRarChecker;
import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
import edu.jiangxin.apktoolbox.swing.extend.filepanel.FilePanel;
import edu.jiangxin.apktoolbox.utils.Constants;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.File;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public final class RecoveryPanel extends EasyPanel {
private JPanel optionPanel;
private FilePanel recoveryFilePanel;
private JTabbedPane categoryTabbedPane;
private JPanel bruteForceCategoryPanel;
private JPanel dictionaryCategoryPanel;
private FilePanel dictionaryFilePanel;
private CategoryType currentCategoryType = CategoryType.UNKNOWN;
private JPanel operationPanel;
private JCheckBox numberCheckBox;
private JCheckBox lowercaseLetterCheckBox;
private JCheckBox uppercaseLetterCheckBox;
private JCheckBox userIncludedCheckBox;
private JTextField userIncludedTextField;
private JCheckBox userExcludedCheckBox;
private JTextField userExcludedTextField;
private JSpinner minSpinner;
private JSpinner maxSpinner;
private JCheckBox isUseMultiThreadCheckBox;
private JProgressBar progressBar;
private JComboBox<FileChecker> checkerTypeComboBox;
private FileChecker currentFileChecker;
private JButton startButton;
private JButton stopButton;
private JLabel currentStateLabel;
private JLabel currentPasswordLabel;
private JLabel currentSpeedLabel;
private int passwordTryCount = 0;
private NumberFormat numberFormat;
private State currentState = State.IDLE;
private Timer timer;
private final transient ExecutorService startExecutorService = Executors.newFixedThreadPool(1);
private final transient ExecutorService stopExecutorService = Executors.newFixedThreadPool(1);
public RecoveryPanel() {
super();
initBase();
}
private void initBase() {
numberFormat = NumberFormat.getPercentInstance();
numberFormat.setMinimumFractionDigits(3);
}
@Override
public void initUI() {
BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
setLayout(boxLayout);
createOptionPanel();
add(optionPanel);
add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
createOperationPanel();
add(operationPanel);
timer = new Timer(1000, e -> {
if (currentState == State.WORKING) {
int currentValue = progressBar.getValue();
int speed = currentValue - passwordTryCount;
passwordTryCount = currentValue;
currentSpeedLabel.setText("Speed: " + speed + " passwords/s");
}
});
}
private void createOptionPanel() {
optionPanel = new JPanel();
optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.Y_AXIS));
checkerTypeComboBox = new JComboBox<>();
checkerTypeComboBox.addItem(new ThirdParty7ZipChecker());
checkerTypeComboBox.addItem(new ThirdPartyWinRarChecker());
checkerTypeComboBox.addItem(new ThirdPartyRarChecker());
checkerTypeComboBox.addItem(new ZipChecker());
checkerTypeComboBox.addItem(new RarChecker());
checkerTypeComboBox.addItem(new SevenZipChecker());
checkerTypeComboBox.addItem(new PdfChecker());
checkerTypeComboBox.addItem(new XmlBasedOfficeChecker());
checkerTypeComboBox.addItem(new BinaryOfficeChecker());
checkerTypeComboBox.setSelectedIndex(0);
checkerTypeComboBox.addItemListener(e -> {
FileChecker fileChecker = (FileChecker) e.getItem();
if (fileChecker == null) {
logger.error("fileChecker is null");
return;
}
recoveryFilePanel.setDescriptionAndFileExtensions(
fileChecker.getFileDescription(), fileChecker.getFileExtensions());
});
FileChecker fileChecker = (FileChecker) checkerTypeComboBox.getSelectedItem();
if (fileChecker == null) {
logger.error("fileChecker is null");
return;
}
recoveryFilePanel = new FilePanel("Choose Recovery File");
recoveryFilePanel.setDescriptionAndFileExtensions(fileChecker.getFileDescription(), fileChecker.getFileExtensions());
categoryTabbedPane = new JTabbedPane();
createBruteForcePanel();
categoryTabbedPane.addTab("Brute Force", null, bruteForceCategoryPanel, "Brute Force");
categoryTabbedPane.setSelectedIndex(0);
createDictionaryPanel();
categoryTabbedPane.addTab("Dictionary", null, dictionaryCategoryPanel, "Dictionary");
progressBar = new JProgressBar();
progressBar.setStringPainted(true);
String text = numberFormat.format(0);
progressBar.setString(text);
optionPanel.add(checkerTypeComboBox);
optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
optionPanel.add(recoveryFilePanel);
optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
optionPanel.add(categoryTabbedPane);
optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
optionPanel.add(progressBar);
}
private void createBruteForcePanel() {
bruteForceCategoryPanel = new JPanel();
JPanel topLevelPanel = new JPanel();
topLevelPanel.setLayout(new BoxLayout(topLevelPanel, BoxLayout.Y_AXIS));
bruteForceCategoryPanel.add(topLevelPanel);
JPanel charsetPanel = new JPanel();
charsetPanel.setLayout(new BoxLayout(charsetPanel, BoxLayout.X_AXIS));
JPanel passwordLengthPanel = new JPanel();
passwordLengthPanel.setLayout(new BoxLayout(passwordLengthPanel, BoxLayout.X_AXIS));
topLevelPanel.add(charsetPanel);
topLevelPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
topLevelPanel.add(passwordLengthPanel);
numberCheckBox = new JCheckBox("Number");
numberCheckBox.setSelected(true);
lowercaseLetterCheckBox = new JCheckBox("Lowercase Letter");
uppercaseLetterCheckBox = new JCheckBox("Uppercase Letter");
userIncludedCheckBox = new JCheckBox("User-defined");
userIncludedTextField = new JTextField(10);
userExcludedCheckBox = new JCheckBox("User-excluded");
userExcludedTextField = new JTextField(10);
charsetPanel.add(numberCheckBox);
charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
charsetPanel.add(lowercaseLetterCheckBox);
charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
charsetPanel.add(uppercaseLetterCheckBox);
charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
charsetPanel.add(userIncludedCheckBox);
charsetPanel.add(userIncludedTextField);
charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
charsetPanel.add(userExcludedCheckBox);
charsetPanel.add(userExcludedTextField);
JLabel minLabel = new JLabel("Minimum Length: ");
JLabel maxLabel = new JLabel("Maximum Length: ");
minSpinner = new JSpinner();
minSpinner.setModel(new SpinnerNumberModel(1, 1, 9, 1));
minSpinner.setToolTipText("Minimum Length");
maxSpinner = new JSpinner();
maxSpinner.setModel(new SpinnerNumberModel(6, 1, 9, 1));
maxSpinner.setToolTipText("Maximum Length");
passwordLengthPanel.add(minLabel);
passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
passwordLengthPanel.add(minSpinner);
passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
passwordLengthPanel.add(maxLabel);
passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
passwordLengthPanel.add(maxSpinner);
}
private void createDictionaryPanel() {
dictionaryCategoryPanel = new JPanel();
JPanel topLevelPanel = new JPanel();
topLevelPanel.setLayout(new BoxLayout(topLevelPanel, BoxLayout.Y_AXIS));
dictionaryCategoryPanel.add(topLevelPanel);
dictionaryFilePanel = new FilePanel("Choose Dictionary File");
dictionaryFilePanel.setDescriptionAndFileExtensions("*.dic;*.txt", new String[]{"dic", "txt"});
JPanel threadPanel = new JPanel();
threadPanel.setLayout(new BoxLayout(threadPanel, BoxLayout.X_AXIS));
topLevelPanel.add(dictionaryFilePanel);
topLevelPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
topLevelPanel.add(threadPanel);
isUseMultiThreadCheckBox = new JCheckBox("Use Multi-thread");
isUseMultiThreadCheckBox.setSelected(true);
threadPanel.add(isUseMultiThreadCheckBox);
}
private void createOperationPanel() {
operationPanel = new JPanel();
operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
startButton = new JButton("Start");
stopButton = new JButton("Stop");
currentStateLabel = new JLabel("State: IDLE");
currentPasswordLabel = new JLabel("Trying: ");
currentSpeedLabel = new JLabel("Speed: 0 passwords/s");
operationPanel.add(startButton);
operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
operationPanel.add(stopButton);
operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER));
operationPanel.add(currentStateLabel);
operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER));
operationPanel.add(currentPasswordLabel);
operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER));
operationPanel.add(currentSpeedLabel);
operationPanel.add(Box.createHorizontalGlue());
startButton.addActionListener(e -> startExecutorService.submit(this::onStart));
stopButton.addActionListener(e -> stopExecutorService.submit(this::onStop));
setCurrentState(State.IDLE);
}
private void onStart() {
FileChecker fileChecker = (FileChecker) checkerTypeComboBox.getSelectedItem();
if (fileChecker == null) {
JOptionPane.showMessageDialog(this, "fileChecker is null!");
return;
}
if (!fileChecker.prepareChecker()) {
JOptionPane.showMessageDialog(this, "onStart failed: Checker condition does not been prepared!");
return;
}
File selectedFile = recoveryFilePanel.getFile();
if (!selectedFile.isFile()) {
JOptionPane.showMessageDialog(this, "file is not a file!");
return;
}
String extension = FilenameUtils.getExtension(recoveryFilePanel.getFile().getName());
if (!ArrayUtils.contains(fileChecker.getFileExtensions(), StringUtils.toRootLowerCase(extension))) {
JOptionPane.showMessageDialog(this, "invalid file!");
return;
}
fileChecker.attachFile(selectedFile);
currentFileChecker = fileChecker;
Component selectedPanel = categoryTabbedPane.getSelectedComponent();
if (selectedPanel.equals(bruteForceCategoryPanel)) {
currentCategoryType = CategoryType.BRUTE_FORCE;
} else if (selectedPanel.equals(dictionaryCategoryPanel)) {
if (isUseMultiThreadCheckBox.isSelected()) {
currentCategoryType = CategoryType.DICTIONARY_MULTI_THREAD;
} else {
currentCategoryType = CategoryType.DICTIONARY_SINGLE_THREAD;
}
} else {
currentCategoryType = CategoryType.UNKNOWN;
}
logger.info("onStart: {}", currentCategoryType);
if (currentCategoryType == CategoryType.UNKNOWN) {
JOptionPane.showMessageDialog(this, "onStart failed: Invalid category!");
return;
}
setCurrentState(State.WORKING);
passwordTryCount = 0;
timer.start();
ICategory category = CategoryFactory.getCategoryInstance(currentCategoryType);
category.start(this);
if (currentState == State.WORKING) {
onStop();
}
}
private void onStop() {
logger.info("onStop: currentState: {}, currentCategoryType: {}", currentState, currentCategoryType);
if (currentState != State.WORKING) {
logger.error("onStop failed: Not in working state!");
return;
}
if (currentCategoryType == CategoryType.UNKNOWN) {
logger.error("onStop failed: Invalid category!");
return;
}
timer.stop();
setCurrentState(State.STOPPING);
ICategory category = CategoryFactory.getCategoryInstance(currentCategoryType);
category.cancel();
setCurrentState(State.IDLE);
currentCategoryType = CategoryType.UNKNOWN;
}
public void showResultWithDialog(String password) {
if (password == null) {
logger.error("Can not find password");
JOptionPane.showMessageDialog(RecoveryPanel.this, "Can not find password");
} else {
logger.info("Find out the password: {}", password);
JPanel panel = createPasswordShowPanel(password);
JOptionPane.showMessageDialog(RecoveryPanel.this, panel, "Find out the password", JOptionPane.INFORMATION_MESSAGE);
}
}
private static JPanel createPasswordShowPanel(String password) {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
JTextField textField = new JTextField(password);
textField.setEditable(false);
textField.setColumns(20);
JButton button = new JButton("Copy");
button.addActionListener(e -> {
StringSelection stringSelection = new StringSelection(password);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);
});
panel.add(textField);
panel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
panel.add(button);
return panel;
}
public void setCurrentState(State currentState) {
SwingUtilities.invokeLater(() -> {
this.currentState = currentState;
if (currentStateLabel != null) {
currentStateLabel.setText("State: " + currentState.toString());
}
if (currentState == State.WORKING) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
startButton.setEnabled(false);
stopButton.setEnabled(true);
updateUiComponent(false);
} else if (currentState == State.STOPPING) {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
startButton.setEnabled(false);
stopButton.setEnabled(false);
updateUiComponent(false);
} else if (currentState == State.IDLE) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
startButton.setEnabled(true);
stopButton.setEnabled(false);
updateUiComponent(true);
}
});
}
private void updateUiComponent(boolean enable) {
for (Component component : getComponents(optionPanel)) {
component.setEnabled(enable);
}
}
private Component[] getComponents(Component container) {
List<Component> list;
try {
list = new ArrayList<>(Arrays.asList(
((Container) container).getComponents()));
for (int index = 0; index < list.size(); index++) {
list.addAll(Arrays.asList(getComponents(list.get(index))));
}
} catch (ClassCastException e) {
list = new ArrayList<>();
}
return list.toArray(new Component[0]);
}
public State getCurrentState() {
return currentState;
}
public void resetProgressMaxValue(int maxValue) {
SwingUtilities.invokeLater(() -> {
progressBar.setMaximum(maxValue);
setProgressBarValue(0);
});
}
public void increaseProgressBarValue() {
SwingUtilities.invokeLater(() -> setProgressBarValue(progressBar.getValue() + 1));
}
private void setProgressBarValue(int value) {
int properValue = Math.min(value, progressBar.getMaximum());
progressBar.setValue(properValue);
String text = numberFormat.format(((double) properValue) / progressBar.getMaximum());
progressBar.setString(text);
}
public void setCurrentPassword(String password) {
SwingUtilities.invokeLater(() -> currentPasswordLabel.setText("Trying: " + password));
}
public String getCharset() {
Set<Character> charsetSet = new HashSet<>();
if (numberCheckBox.isSelected()) {
CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("0123456789".toCharArray()));
}
if (lowercaseLetterCheckBox.isSelected()) {
CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("abcdefghijklmnopqrstuvwxyz".toCharArray()));
}
if (uppercaseLetterCheckBox.isSelected()) {
CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()));
}
if (userIncludedCheckBox.isSelected()) {
CollectionUtils.addAll(charsetSet, ArrayUtils.toObject(userIncludedTextField.getText().toCharArray()));
}
if (userExcludedCheckBox.isSelected()) {
for (char ch : userExcludedTextField.getText().toCharArray()) {
charsetSet.remove(ch);
}
}
return String.valueOf(ArrayUtils.toPrimitive(charsetSet.toArray(new Character[0])));
}
public File getDictionaryFile() {
return dictionaryFilePanel.getFile();
}
public int getMinLength() {
return (Integer) minSpinner.getValue();
}
public int getMaxLength() {
return (Integer) maxSpinner.getValue();
}
public FileChecker getCurrentFileChecker() {
return currentFileChecker;
}
}