1 package edu.jiangxin.apktoolbox.file.password.recovery; 2 3 import edu.jiangxin.apktoolbox.file.password.recovery.category.CategoryFactory; 4 import edu.jiangxin.apktoolbox.file.password.recovery.category.CategoryType; 5 import edu.jiangxin.apktoolbox.file.password.recovery.category.ICategory; 6 import edu.jiangxin.apktoolbox.file.password.recovery.checker.*; 7 import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdParty7ZipChecker; 8 import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdPartyRarChecker; 9 import edu.jiangxin.apktoolbox.file.password.recovery.checker.thirdparty.ThirdPartyWinRarChecker; 10 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel; 11 import edu.jiangxin.apktoolbox.swing.extend.filepanel.FilePanel; 12 import edu.jiangxin.apktoolbox.utils.Constants; 13 import org.apache.commons.collections4.CollectionUtils; 14 import org.apache.commons.io.FilenameUtils; 15 import org.apache.commons.lang3.ArrayUtils; 16 import org.apache.commons.lang3.StringUtils; 17 18 import javax.swing.*; 19 import java.awt.*; 20 import java.awt.datatransfer.Clipboard; 21 import java.awt.datatransfer.StringSelection; 22 import java.io.File; 23 import java.text.NumberFormat; 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.HashSet; 27 import java.util.List; 28 import java.util.Set; 29 import java.util.concurrent.ExecutorService; 30 import java.util.concurrent.Executors; 31 32 public final class RecoveryPanel extends EasyPanel { 33 private JPanel optionPanel; 34 35 private FilePanel recoveryFilePanel; 36 37 private JTabbedPane categoryTabbedPane; 38 39 private JPanel bruteForceCategoryPanel; 40 41 private JPanel dictionaryCategoryPanel; 42 43 private FilePanel dictionaryFilePanel; 44 45 private CategoryType currentCategoryType = CategoryType.UNKNOWN; 46 47 private JPanel operationPanel; 48 49 private JCheckBox numberCheckBox; 50 private JCheckBox lowercaseLetterCheckBox; 51 private JCheckBox uppercaseLetterCheckBox; 52 53 private JCheckBox userIncludedCheckBox; 54 55 private JTextField userIncludedTextField; 56 57 private JCheckBox userExcludedCheckBox; 58 59 private JTextField userExcludedTextField; 60 61 private JSpinner minSpinner; 62 private JSpinner maxSpinner; 63 64 private JCheckBox isUseMultiThreadCheckBox; 65 66 private JProgressBar progressBar; 67 68 private JComboBox<FileChecker> checkerTypeComboBox; 69 70 private FileChecker currentFileChecker; 71 72 private JButton startButton; 73 private JButton stopButton; 74 75 private JLabel currentStateLabel; 76 77 private JLabel currentPasswordLabel; 78 79 private JLabel currentSpeedLabel; 80 81 private int passwordTryCount = 0; 82 83 private NumberFormat numberFormat; 84 85 private State currentState = State.IDLE; 86 87 private Timer timer; 88 89 private final transient ExecutorService startExecutorService = Executors.newFixedThreadPool(1); 90 91 private final transient ExecutorService stopExecutorService = Executors.newFixedThreadPool(1); 92 93 public RecoveryPanel() { 94 super(); 95 initBase(); 96 } 97 98 private void initBase() { 99 numberFormat = NumberFormat.getPercentInstance(); 100 numberFormat.setMinimumFractionDigits(3); 101 } 102 103 @Override 104 public void initUI() { 105 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS); 106 setLayout(boxLayout); 107 108 createOptionPanel(); 109 add(optionPanel); 110 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 111 112 createOperationPanel(); 113 add(operationPanel); 114 115 timer = new Timer(1000, e -> { 116 if (currentState == State.WORKING) { 117 int currentValue = progressBar.getValue(); 118 int speed = currentValue - passwordTryCount; 119 passwordTryCount = currentValue; 120 currentSpeedLabel.setText("Speed: " + speed + " passwords/s"); 121 } 122 }); 123 } 124 125 private void createOptionPanel() { 126 optionPanel = new JPanel(); 127 optionPanel.setLayout(new BoxLayout(optionPanel, BoxLayout.Y_AXIS)); 128 129 checkerTypeComboBox = new JComboBox<>(); 130 checkerTypeComboBox.addItem(new ThirdParty7ZipChecker()); 131 checkerTypeComboBox.addItem(new ThirdPartyWinRarChecker()); 132 checkerTypeComboBox.addItem(new ThirdPartyRarChecker()); 133 checkerTypeComboBox.addItem(new ZipChecker()); 134 checkerTypeComboBox.addItem(new RarChecker()); 135 checkerTypeComboBox.addItem(new SevenZipChecker()); 136 checkerTypeComboBox.addItem(new PdfChecker()); 137 checkerTypeComboBox.addItem(new XmlBasedOfficeChecker()); 138 checkerTypeComboBox.addItem(new BinaryOfficeChecker()); 139 checkerTypeComboBox.setSelectedIndex(0); 140 checkerTypeComboBox.addItemListener(e -> { 141 FileChecker fileChecker = (FileChecker) e.getItem(); 142 if (fileChecker == null) { 143 logger.error("fileChecker is null"); 144 return; 145 } 146 recoveryFilePanel.setDescriptionAndFileExtensions( 147 fileChecker.getFileDescription(), fileChecker.getFileExtensions()); 148 }); 149 150 FileChecker fileChecker = (FileChecker) checkerTypeComboBox.getSelectedItem(); 151 if (fileChecker == null) { 152 logger.error("fileChecker is null"); 153 return; 154 } 155 156 recoveryFilePanel = new FilePanel("Choose Recovery File"); 157 recoveryFilePanel.setDescriptionAndFileExtensions(fileChecker.getFileDescription(), fileChecker.getFileExtensions()); 158 159 categoryTabbedPane = new JTabbedPane(); 160 161 createBruteForcePanel(); 162 categoryTabbedPane.addTab("Brute Force", null, bruteForceCategoryPanel, "Brute Force"); 163 categoryTabbedPane.setSelectedIndex(0); 164 165 createDictionaryPanel(); 166 categoryTabbedPane.addTab("Dictionary", null, dictionaryCategoryPanel, "Dictionary"); 167 168 progressBar = new JProgressBar(); 169 progressBar.setStringPainted(true); 170 String text = numberFormat.format(0); 171 progressBar.setString(text); 172 173 optionPanel.add(checkerTypeComboBox); 174 optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 175 optionPanel.add(recoveryFilePanel); 176 optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 177 optionPanel.add(categoryTabbedPane); 178 optionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 179 optionPanel.add(progressBar); 180 } 181 182 private void createBruteForcePanel() { 183 bruteForceCategoryPanel = new JPanel(); 184 185 JPanel topLevelPanel = new JPanel(); 186 topLevelPanel.setLayout(new BoxLayout(topLevelPanel, BoxLayout.Y_AXIS)); 187 bruteForceCategoryPanel.add(topLevelPanel); 188 189 JPanel charsetPanel = new JPanel(); 190 charsetPanel.setLayout(new BoxLayout(charsetPanel, BoxLayout.X_AXIS)); 191 192 JPanel passwordLengthPanel = new JPanel(); 193 passwordLengthPanel.setLayout(new BoxLayout(passwordLengthPanel, BoxLayout.X_AXIS)); 194 195 topLevelPanel.add(charsetPanel); 196 topLevelPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 197 topLevelPanel.add(passwordLengthPanel); 198 199 numberCheckBox = new JCheckBox("Number"); 200 numberCheckBox.setSelected(true); 201 lowercaseLetterCheckBox = new JCheckBox("Lowercase Letter"); 202 uppercaseLetterCheckBox = new JCheckBox("Uppercase Letter"); 203 userIncludedCheckBox = new JCheckBox("User-defined"); 204 userIncludedTextField = new JTextField(10); 205 userExcludedCheckBox = new JCheckBox("User-excluded"); 206 userExcludedTextField = new JTextField(10); 207 208 209 charsetPanel.add(numberCheckBox); 210 charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 211 charsetPanel.add(lowercaseLetterCheckBox); 212 charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 213 charsetPanel.add(uppercaseLetterCheckBox); 214 charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 215 charsetPanel.add(userIncludedCheckBox); 216 charsetPanel.add(userIncludedTextField); 217 charsetPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 218 charsetPanel.add(userExcludedCheckBox); 219 charsetPanel.add(userExcludedTextField); 220 221 JLabel minLabel = new JLabel("Minimum Length: "); 222 JLabel maxLabel = new JLabel("Maximum Length: "); 223 224 minSpinner = new JSpinner(); 225 minSpinner.setModel(new SpinnerNumberModel(1, 1, 9, 1)); 226 minSpinner.setToolTipText("Minimum Length"); 227 228 maxSpinner = new JSpinner(); 229 maxSpinner.setModel(new SpinnerNumberModel(6, 1, 9, 1)); 230 maxSpinner.setToolTipText("Maximum Length"); 231 232 passwordLengthPanel.add(minLabel); 233 passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 234 passwordLengthPanel.add(minSpinner); 235 passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 236 passwordLengthPanel.add(maxLabel); 237 passwordLengthPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 238 passwordLengthPanel.add(maxSpinner); 239 } 240 241 private void createDictionaryPanel() { 242 dictionaryCategoryPanel = new JPanel(); 243 244 JPanel topLevelPanel = new JPanel(); 245 topLevelPanel.setLayout(new BoxLayout(topLevelPanel, BoxLayout.Y_AXIS)); 246 dictionaryCategoryPanel.add(topLevelPanel); 247 248 dictionaryFilePanel = new FilePanel("Choose Dictionary File"); 249 dictionaryFilePanel.setDescriptionAndFileExtensions("*.dic;*.txt", new String[]{"dic", "txt"}); 250 251 JPanel threadPanel = new JPanel(); 252 threadPanel.setLayout(new BoxLayout(threadPanel, BoxLayout.X_AXIS)); 253 254 topLevelPanel.add(dictionaryFilePanel); 255 topLevelPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 256 topLevelPanel.add(threadPanel); 257 258 isUseMultiThreadCheckBox = new JCheckBox("Use Multi-thread"); 259 isUseMultiThreadCheckBox.setSelected(true); 260 261 threadPanel.add(isUseMultiThreadCheckBox); 262 } 263 264 private void createOperationPanel() { 265 operationPanel = new JPanel(); 266 operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS)); 267 268 startButton = new JButton("Start"); 269 stopButton = new JButton("Stop"); 270 currentStateLabel = new JLabel("State: IDLE"); 271 currentPasswordLabel = new JLabel("Trying: "); 272 currentSpeedLabel = new JLabel("Speed: 0 passwords/s"); 273 274 operationPanel.add(startButton); 275 operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 276 operationPanel.add(stopButton); 277 operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER)); 278 operationPanel.add(currentStateLabel); 279 operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER)); 280 operationPanel.add(currentPasswordLabel); 281 operationPanel.add(Box.createHorizontalStrut(2 * Constants.DEFAULT_X_BORDER)); 282 operationPanel.add(currentSpeedLabel); 283 operationPanel.add(Box.createHorizontalGlue()); 284 285 startButton.addActionListener(e -> startExecutorService.submit(this::onStart)); 286 stopButton.addActionListener(e -> stopExecutorService.submit(this::onStop)); 287 288 setCurrentState(State.IDLE); 289 } 290 291 private void onStart() { 292 FileChecker fileChecker = (FileChecker) checkerTypeComboBox.getSelectedItem(); 293 if (fileChecker == null) { 294 JOptionPane.showMessageDialog(this, "fileChecker is null!"); 295 return; 296 } 297 if (!fileChecker.prepareChecker()) { 298 JOptionPane.showMessageDialog(this, "onStart failed: Checker condition does not been prepared!"); 299 return; 300 } 301 302 File selectedFile = recoveryFilePanel.getFile(); 303 if (!selectedFile.isFile()) { 304 JOptionPane.showMessageDialog(this, "file is not a file!"); 305 return; 306 } 307 308 String extension = FilenameUtils.getExtension(recoveryFilePanel.getFile().getName()); 309 if (!ArrayUtils.contains(fileChecker.getFileExtensions(), StringUtils.toRootLowerCase(extension))) { 310 JOptionPane.showMessageDialog(this, "invalid file!"); 311 return; 312 } 313 314 fileChecker.attachFile(selectedFile); 315 currentFileChecker = fileChecker; 316 317 Component selectedPanel = categoryTabbedPane.getSelectedComponent(); 318 if (selectedPanel.equals(bruteForceCategoryPanel)) { 319 currentCategoryType = CategoryType.BRUTE_FORCE; 320 } else if (selectedPanel.equals(dictionaryCategoryPanel)) { 321 if (isUseMultiThreadCheckBox.isSelected()) { 322 currentCategoryType = CategoryType.DICTIONARY_MULTI_THREAD; 323 } else { 324 currentCategoryType = CategoryType.DICTIONARY_SINGLE_THREAD; 325 } 326 } else { 327 currentCategoryType = CategoryType.UNKNOWN; 328 } 329 logger.info("onStart: {}", currentCategoryType); 330 if (currentCategoryType == CategoryType.UNKNOWN) { 331 JOptionPane.showMessageDialog(this, "onStart failed: Invalid category!"); 332 return; 333 } 334 setCurrentState(State.WORKING); 335 passwordTryCount = 0; 336 timer.start(); 337 ICategory category = CategoryFactory.getCategoryInstance(currentCategoryType); 338 category.start(this); 339 if (currentState == State.WORKING) { 340 onStop(); 341 } 342 } 343 344 private void onStop() { 345 logger.info("onStop: currentState: {}, currentCategoryType: {}", currentState, currentCategoryType); 346 if (currentState != State.WORKING) { 347 logger.error("onStop failed: Not in working state!"); 348 return; 349 } 350 if (currentCategoryType == CategoryType.UNKNOWN) { 351 logger.error("onStop failed: Invalid category!"); 352 return; 353 } 354 timer.stop(); 355 setCurrentState(State.STOPPING); 356 ICategory category = CategoryFactory.getCategoryInstance(currentCategoryType); 357 category.cancel(); 358 setCurrentState(State.IDLE); 359 currentCategoryType = CategoryType.UNKNOWN; 360 } 361 362 public void showResultWithDialog(String password) { 363 if (password == null) { 364 logger.error("Can not find password"); 365 JOptionPane.showMessageDialog(RecoveryPanel.this, "Can not find password"); 366 } else { 367 logger.info("Find out the password: {}", password); 368 JPanel panel = createPasswordShowPanel(password); 369 JOptionPane.showMessageDialog(RecoveryPanel.this, panel, "Find out the password", JOptionPane.INFORMATION_MESSAGE); 370 } 371 } 372 373 private static JPanel createPasswordShowPanel(String password) { 374 JPanel panel = new JPanel(); 375 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 376 JTextField textField = new JTextField(password); 377 textField.setEditable(false); 378 textField.setColumns(20); 379 JButton button = new JButton("Copy"); 380 button.addActionListener(e -> { 381 StringSelection stringSelection = new StringSelection(password); 382 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 383 clipboard.setContents(stringSelection, null); 384 }); 385 panel.add(textField); 386 panel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER)); 387 panel.add(button); 388 return panel; 389 } 390 391 public void setCurrentState(State currentState) { 392 SwingUtilities.invokeLater(() -> { 393 this.currentState = currentState; 394 if (currentStateLabel != null) { 395 currentStateLabel.setText("State: " + currentState.toString()); 396 } 397 if (currentState == State.WORKING) { 398 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 399 startButton.setEnabled(false); 400 stopButton.setEnabled(true); 401 updateUiComponent(false); 402 } else if (currentState == State.STOPPING) { 403 setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 404 startButton.setEnabled(false); 405 stopButton.setEnabled(false); 406 updateUiComponent(false); 407 } else if (currentState == State.IDLE) { 408 setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 409 startButton.setEnabled(true); 410 stopButton.setEnabled(false); 411 updateUiComponent(true); 412 } 413 }); 414 } 415 416 private void updateUiComponent(boolean enable) { 417 for (Component component : getComponents(optionPanel)) { 418 component.setEnabled(enable); 419 } 420 } 421 422 private Component[] getComponents(Component container) { 423 List<Component> list; 424 425 try { 426 list = new ArrayList<>(Arrays.asList( 427 ((Container) container).getComponents())); 428 for (int index = 0; index < list.size(); index++) { 429 list.addAll(Arrays.asList(getComponents(list.get(index)))); 430 } 431 } catch (ClassCastException e) { 432 list = new ArrayList<>(); 433 } 434 435 return list.toArray(new Component[0]); 436 } 437 438 public State getCurrentState() { 439 return currentState; 440 } 441 442 public void resetProgressMaxValue(int maxValue) { 443 SwingUtilities.invokeLater(() -> { 444 progressBar.setMaximum(maxValue); 445 setProgressBarValue(0); 446 }); 447 } 448 449 public void increaseProgressBarValue() { 450 SwingUtilities.invokeLater(() -> setProgressBarValue(progressBar.getValue() + 1)); 451 } 452 453 private void setProgressBarValue(int value) { 454 int properValue = Math.min(value, progressBar.getMaximum()); 455 progressBar.setValue(properValue); 456 String text = numberFormat.format(((double) properValue) / progressBar.getMaximum()); 457 progressBar.setString(text); 458 } 459 460 public void setCurrentPassword(String password) { 461 SwingUtilities.invokeLater(() -> currentPasswordLabel.setText("Trying: " + password)); 462 } 463 464 public String getCharset() { 465 Set<Character> charsetSet = new HashSet<>(); 466 if (numberCheckBox.isSelected()) { 467 CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("0123456789".toCharArray())); 468 } 469 if (lowercaseLetterCheckBox.isSelected()) { 470 CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("abcdefghijklmnopqrstuvwxyz".toCharArray())); 471 } 472 if (uppercaseLetterCheckBox.isSelected()) { 473 CollectionUtils.addAll(charsetSet, ArrayUtils.toObject("ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray())); 474 } 475 if (userIncludedCheckBox.isSelected()) { 476 CollectionUtils.addAll(charsetSet, ArrayUtils.toObject(userIncludedTextField.getText().toCharArray())); 477 } 478 if (userExcludedCheckBox.isSelected()) { 479 for (char ch : userExcludedTextField.getText().toCharArray()) { 480 charsetSet.remove(ch); 481 } 482 } 483 return String.valueOf(ArrayUtils.toPrimitive(charsetSet.toArray(new Character[0]))); 484 } 485 486 public File getDictionaryFile() { 487 return dictionaryFilePanel.getFile(); 488 } 489 490 public int getMinLength() { 491 return (Integer) minSpinner.getValue(); 492 } 493 494 public int getMaxLength() { 495 return (Integer) maxSpinner.getValue(); 496 } 497 498 public FileChecker getCurrentFileChecker() { 499 return currentFileChecker; 500 } 501 } 502