View Javadoc
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