├── .github └── workflows │ ├── automatic_release.yml │ └── pr.yml ├── .gitignore ├── LICENSE ├── README.md ├── pom.xml ├── src ├── java │ └── com │ │ └── javadeobfuscator │ │ └── deobfuscator │ │ └── ui │ │ ├── GuiConfig.java │ │ ├── SwingWindow.java │ │ ├── TransformerSpecificConfigDialog.java │ │ ├── TransformerWithConfig.java │ │ ├── component │ │ ├── SwingConfiguration.java │ │ ├── SynchronousJFXCaller.java │ │ ├── SynchronousJFXFileChooser.java │ │ └── WrapLayout.java │ │ ├── util │ │ ├── ByteLoader.java │ │ ├── ExceptionUtil.java │ │ ├── FallbackException.java │ │ ├── InvalidJarException.java │ │ ├── MiniClassReader.java │ │ ├── Reflect.java │ │ ├── SwingUtil.java │ │ └── TransformerConfigUtil.java │ │ └── wrap │ │ ├── Config.java │ │ ├── Deobfuscator.java │ │ ├── Transformers.java │ │ └── WrapperFactory.java └── resources │ └── META-INF │ └── services │ ├── com.fasterxml.jackson.core.JsonFactory │ ├── com.fasterxml.jackson.core.ObjectCodec │ └── org.slf4j.spi.SLF4JServiceProvider └── swing.png /.github/workflows/automatic_release.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Automatic binary release - Java CI with Maven 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up JDK 8 17 | uses: actions/setup-java@v2 18 | with: 19 | java-version: '8' 20 | distribution: 'zulu' 21 | java-package: jdk+fx 22 | - name: Set Release version env variable 23 | run: echo "RELEASE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV 24 | - name: Cache local Maven repository 25 | uses: actions/cache@v2 26 | with: 27 | path: ~/.m2/repository 28 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 29 | restore-keys: | 30 | ${{ runner.os }}-maven- 31 | - name: Build with Maven 32 | run: mvn -B package --file pom.xml 33 | - name: Rename final file 34 | run: mv target/deobfuscator-gui-*-jar-with-dependencies.jar target/deobfuscator-gui.jar 35 | - name: Release 36 | uses: "marvinpinto/action-automatic-releases@latest" 37 | with: 38 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 39 | automatic_release_tag: "${{ env.RELEASE_VERSION }}" 40 | prerelease: false 41 | title: "v${{ env.RELEASE_VERSION }}" 42 | files: | 43 | LICENSE 44 | target/deobfuscator-gui.jar 45 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: PR integration check 5 | 6 | on: [pull_request] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up JDK 8 15 | uses: actions/setup-java@v2 16 | with: 17 | java-version: '8' 18 | distribution: 'zulu' 19 | java-package: jdk+fx 20 | - name: Cache local Maven repository 21 | uses: actions/cache@v2 22 | with: 23 | path: ~/.m2/repository 24 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 25 | restore-keys: | 26 | ${{ runner.os }}-maven- 27 | - name: Build with Maven 28 | run: mvn -B package --file pom.xml 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /lib/ 3 | /test/ 4 | /output/ 5 | /target/ 6 | /.metadata/ 7 | .settings/ 8 | 9 | .classpath 10 | .project 11 | 12 | */.project 13 | 14 | *.iml 15 | /.idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deobfuscator-gui [![downloads](https://img.shields.io/github/downloads/java-deobfuscator/deobfuscator-gui/total.svg)](https://github.com/java-deobfuscator/deobfuscator-gui/releases/latest) 2 | 3 | A GUI for a the popular [java-deobfuscator](https://github.com/java-deobfuscator/deobfuscator). 4 | 5 | ## What is Deobfuscator-GUI? 6 | Deobfuscator-GUI is a GUI for the command line deobfuscator. User interfaces are more intuitive to the average user, allowing more people to use the tool without needing to concern themselves with syntax or configuration files. 7 | Note that as of version 3.0 very old versions of deobfuscator will no longer be supported. 8 | 9 | ## How to Use 10 | A java installation with javafx is required. [Azul Zulu JDK+FX/JRE+FX builds](https://www.azul.com/downloads/?package=jdk-fx) are the easiest option to get these. 11 | 12 | 1. Download the deobfuscator.jar from https://github.com/java-deobfuscator/deobfuscator. Place it in the same folder as the GUI you will be downloading to avoid selecting the JAR when you open the GUI. 13 | 2. Download or build the GUI: 14 | * Download: [releases](https://github.com/java-deobfuscator/deobfuscator-gui/releases/latest) 15 | * Build: Clone the repository then run `mvn package` 16 | 3. Run the GUI: 17 | * Specify an input and output file, and then add the required libraries and select your transformers. 18 | 19 | Some transformers have their own configuration, right click a selected transformer to edit its config. 20 | 21 | ## Screenshots 22 | 23 | ![swing](swing.png) 24 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.javadeobfuscator 6 | deobfuscator-gui 7 | 4.2.1 8 | 9 | deobfuscator-gui 10 | UI front-end for deobfuscator 11 | https://github.com/java-deobfuscator/ 12 | 13 | 14 | 15 | 16 | com.github.weisj 17 | darklaf-core 18 | 3.0.2 19 | 20 | 21 | 22 | 23 | src/java 24 | 25 | 26 | src/resources 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-compiler-plugin 33 | 3.10.1 34 | 35 | 1.8 36 | 1.8 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-assembly-plugin 42 | 3.4.2 43 | 44 | 45 | package 46 | 47 | single 48 | 49 | 50 | 51 | 52 | 53 | 54 | com.javadeobfuscator.deobfuscator.ui.SwingWindow 55 | 56 | 57 | 58 | jar-with-dependencies 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/GuiConfig.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.io.InputStreamReader; 8 | import java.io.ObjectInputStream; 9 | import java.io.ObjectOutputStream; 10 | import java.io.OutputStreamWriter; 11 | import java.nio.charset.StandardCharsets; 12 | import java.nio.file.Files; 13 | import java.util.Base64; 14 | import java.util.Properties; 15 | 16 | import javax.swing.JOptionPane; 17 | 18 | import com.github.weisj.darklaf.settings.SettingsConfiguration; 19 | import com.github.weisj.darklaf.settings.ThemeSettings; 20 | 21 | public class GuiConfig 22 | { 23 | private static final File PROPERTY_FILE = new File("deobfuscator-gui.properties"); 24 | private static final Properties PROPERTIES = new Properties(); 25 | 26 | private static final String LIMIT_CONSOLE_LINES = "limit_console_lines"; 27 | private static final String STORE_CONFIG_ON_CLOSE = "store_config_on_close"; 28 | private static final String CONFIG = "config"; 29 | private static final String DARKLAF_ENABLED = "darklaf_enabled"; 30 | private static final String DARKLAF_SETTINGS = "darklaf_settings"; 31 | 32 | static 33 | { 34 | PROPERTIES.setProperty(LIMIT_CONSOLE_LINES, "false"); 35 | PROPERTIES.setProperty(STORE_CONFIG_ON_CLOSE, "true"); 36 | } 37 | 38 | public static void read() 39 | { 40 | if (!PROPERTY_FILE.exists()) 41 | { 42 | save(); 43 | return; 44 | } 45 | try (InputStreamReader reader = new InputStreamReader(Files.newInputStream(PROPERTY_FILE.toPath()), StandardCharsets.UTF_8)) 46 | { 47 | PROPERTIES.load(reader); 48 | } catch (IOException e) 49 | { 50 | e.printStackTrace(); 51 | PROPERTY_FILE.delete(); 52 | JOptionPane.showMessageDialog(null, "Gui config file " + PROPERTY_FILE.getName() + " could not be read and was replaced with " + 53 | "the default config.\n\n" + e.getClass().getName() + ": " + e.getMessage(), "Deobfuscator GUI", 54 | JOptionPane.WARNING_MESSAGE); 55 | save(); 56 | } 57 | } 58 | 59 | public static void save() 60 | { 61 | try (OutputStreamWriter writer = new OutputStreamWriter(Files.newOutputStream(PROPERTY_FILE.toPath()), StandardCharsets.UTF_8)) 62 | { 63 | PROPERTIES.store(writer, null); 64 | } catch (IOException e) 65 | { 66 | e.printStackTrace(); 67 | JOptionPane.showMessageDialog(null, "Gui config file " + PROPERTY_FILE.getName() + " could not be saved.\n\n" + 68 | e.getClass().getName() + ": " + e.getMessage(), "Deobfuscator GUI", JOptionPane.WARNING_MESSAGE); 69 | } 70 | } 71 | 72 | public static boolean isLimitConsoleLines() 73 | { 74 | return Boolean.parseBoolean(PROPERTIES.getProperty(LIMIT_CONSOLE_LINES)); 75 | } 76 | 77 | public static void setLimitConsoleLines(boolean state) 78 | { 79 | PROPERTIES.setProperty(LIMIT_CONSOLE_LINES, Boolean.toString(state)); 80 | } 81 | 82 | public static boolean getStoreConfigOnClose() 83 | { 84 | return Boolean.parseBoolean(PROPERTIES.getProperty(STORE_CONFIG_ON_CLOSE)); 85 | } 86 | 87 | public static void setStoreConfigOnClose(boolean state) 88 | { 89 | PROPERTIES.setProperty(STORE_CONFIG_ON_CLOSE, Boolean.toString(state)); 90 | } 91 | 92 | public static boolean isDarkLaf() 93 | { 94 | return Boolean.parseBoolean(PROPERTIES.getProperty(DARKLAF_ENABLED)); 95 | } 96 | 97 | public static void setDarkLaf(boolean state) 98 | { 99 | PROPERTIES.setProperty(DARKLAF_ENABLED, Boolean.toString(state)); 100 | } 101 | 102 | public static SettingsConfiguration getDarklafSettings() 103 | { 104 | String property = PROPERTIES.getProperty(DARKLAF_SETTINGS); 105 | if (property == null) 106 | { 107 | return ThemeSettings.getInstance().exportConfiguration(); 108 | } 109 | try 110 | { 111 | byte[] decode = Base64.getDecoder().decode(property); 112 | ByteArrayInputStream bain = new ByteArrayInputStream(decode); 113 | try (ObjectInputStream objectInputStream = new ObjectInputStream(bain)) 114 | { 115 | Object obj = objectInputStream.readObject(); 116 | if (obj instanceof SettingsConfiguration) 117 | { 118 | return (SettingsConfiguration) obj; 119 | } 120 | } catch (IOException | ClassNotFoundException e) 121 | { 122 | e.printStackTrace(); 123 | PROPERTIES.remove(DARKLAF_SETTINGS); 124 | } 125 | } catch (Throwable t) 126 | { 127 | t.printStackTrace(); 128 | } 129 | return ThemeSettings.getInstance().exportConfiguration(); 130 | } 131 | 132 | public static void setDarklafSettings(SettingsConfiguration state) 133 | { 134 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 135 | try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos)) 136 | { 137 | objectOutputStream.writeObject(state); 138 | objectOutputStream.flush(); 139 | PROPERTIES.setProperty(DARKLAF_SETTINGS, Base64.getEncoder().encodeToString(baos.toByteArray())); 140 | } catch (IOException e) 141 | { 142 | e.printStackTrace(); 143 | } 144 | } 145 | 146 | public static String getConfig() 147 | { 148 | return PROPERTIES.getProperty(CONFIG); 149 | } 150 | 151 | public static void setConfig(String config) 152 | { 153 | if (getStoreConfigOnClose()) 154 | { 155 | PROPERTIES.setProperty(CONFIG, config); 156 | } else 157 | { 158 | PROPERTIES.remove(CONFIG); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/SwingWindow.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui; 2 | 3 | import java.awt.Dialog; 4 | import java.awt.Dimension; 5 | import java.awt.FlowLayout; 6 | import java.awt.GridBagConstraints; 7 | import java.awt.GridBagLayout; 8 | import java.awt.Insets; 9 | import java.awt.Toolkit; 10 | import java.awt.datatransfer.StringSelection; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.KeyAdapter; 13 | import java.awt.event.KeyEvent; 14 | import java.awt.event.MouseAdapter; 15 | import java.awt.event.MouseEvent; 16 | import java.awt.event.WindowAdapter; 17 | import java.awt.event.WindowEvent; 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.io.OutputStream; 21 | import java.io.PrintStream; 22 | import java.io.PrintWriter; 23 | import java.io.StringWriter; 24 | import java.lang.reflect.Field; 25 | import java.lang.reflect.InvocationTargetException; 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.HashMap; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.Optional; 32 | import java.util.stream.Collectors; 33 | import java.util.stream.IntStream; 34 | 35 | import javax.swing.*; 36 | 37 | import com.github.weisj.darklaf.LafManager; 38 | import com.github.weisj.darklaf.settings.ThemeSettings; 39 | import com.javadeobfuscator.deobfuscator.ui.component.SwingConfiguration; 40 | import com.javadeobfuscator.deobfuscator.ui.component.SwingConfiguration.ConfigItem; 41 | import com.javadeobfuscator.deobfuscator.ui.component.SwingConfiguration.ItemType; 42 | import com.javadeobfuscator.deobfuscator.ui.component.SynchronousJFXCaller; 43 | import com.javadeobfuscator.deobfuscator.ui.component.SynchronousJFXFileChooser; 44 | import com.javadeobfuscator.deobfuscator.ui.component.WrapLayout; 45 | import com.javadeobfuscator.deobfuscator.ui.util.ExceptionUtil; 46 | import com.javadeobfuscator.deobfuscator.ui.util.FallbackException; 47 | import com.javadeobfuscator.deobfuscator.ui.util.InvalidJarException; 48 | import com.javadeobfuscator.deobfuscator.ui.util.TransformerConfigUtil; 49 | import com.javadeobfuscator.deobfuscator.ui.wrap.Config; 50 | import com.javadeobfuscator.deobfuscator.ui.wrap.Deobfuscator; 51 | import com.javadeobfuscator.deobfuscator.ui.wrap.Transformers; 52 | import com.javadeobfuscator.deobfuscator.ui.wrap.WrapperFactory; 53 | import javafx.stage.FileChooser; 54 | 55 | public class SwingWindow 56 | { 57 | 58 | private static Deobfuscator deob; 59 | public static Transformers trans; 60 | private static Config config; 61 | private static List> transformers; 62 | private static JCheckBoxMenuItem shouldLimitLines; 63 | private static JCheckBoxMenuItem storeConfigOnClose; 64 | private static JCheckBoxMenuItem enableDarkLaf; 65 | private static final Map, String> TRANSFORMER_TO_NAME = new HashMap<>(); 66 | private static final Map> NAME_TO_TRANSFORMER = new HashMap<>(); 67 | private static DefaultListModel transformerSelected; 68 | private static boolean swingLafLoaded = false; 69 | public static Thread mainThread; 70 | private static List configFieldsList; 71 | 72 | public static void main(String[] args) 73 | { 74 | try 75 | { 76 | Class.forName("javafx.stage.FileChooser"); 77 | } catch (ClassNotFoundException e) 78 | { 79 | e.printStackTrace(); 80 | ensureSwingLafLoaded(); 81 | ExceptionUtil.showFatalError("You need a JVM with JavaFX (non-headless installation).\n\n" + 82 | "Could not find class " + e.getMessage()); 83 | System.exit(1); 84 | return; 85 | } 86 | mainThread = Thread.currentThread(); 87 | Thread loaderThread = new Thread("Deobfuscator Jar Loader Thread") 88 | { 89 | @Override 90 | public void run() 91 | { 92 | loadWrappers(); 93 | } 94 | }; 95 | loaderThread.start(); 96 | Thread jfxInitThread = new Thread("JavaFX Init Thread") 97 | { 98 | @Override 99 | public void run() 100 | { 101 | initJFX(); 102 | } 103 | }; 104 | jfxInitThread.start(); 105 | GuiConfig.read(); 106 | ensureSwingLafLoaded(); 107 | if (GuiConfig.isDarkLaf()) 108 | { 109 | installDarkLaf(); 110 | } 111 | 112 | //Initial frame 113 | JFrame frame = new JFrame(); 114 | frame.setTitle("Deobfuscator GUI"); 115 | frame.setBounds(100, 100, 580, 650); 116 | frame.getContentPane().setLayout(new GridBagLayout()); 117 | frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 118 | 119 | //Menu 120 | JMenuBar menuBar = new JMenuBar(); 121 | JMenu menu = new JMenu("Options"); 122 | menuBar.add(menu); 123 | shouldLimitLines = new JCheckBoxMenuItem("Limit Console Lines"); 124 | menu.add(shouldLimitLines); 125 | storeConfigOnClose = new JCheckBoxMenuItem("Store transformer config on close", true); 126 | menu.add(storeConfigOnClose); 127 | enableDarkLaf = new JCheckBoxMenuItem(new AbstractAction("Enable DarkLaf Theme") 128 | { 129 | @Override 130 | public void actionPerformed(ActionEvent e) 131 | { 132 | if (enableDarkLaf.isSelected()) 133 | { 134 | installDarkLaf(); 135 | GuiConfig.setDarkLaf(true); 136 | } else 137 | { 138 | int i = JOptionPane.showConfirmDialog(frame, "Disabling DarkLaf requires application restart.\n" + 139 | "Close Deobfuscator now?", "Close Deobfuscator?", 140 | JOptionPane.YES_NO_OPTION); 141 | if (i != JOptionPane.YES_OPTION) 142 | { 143 | enableDarkLaf.setSelected(true); 144 | return; 145 | } 146 | GuiConfig.setDarkLaf(false); 147 | writeAndSaveGuiConfig(configFieldsList); 148 | System.exit(0); 149 | } 150 | } 151 | }); 152 | menu.add(enableDarkLaf); 153 | JMenuItem theme = new JMenuItem(new AbstractAction("DarkLaf Theme Options") 154 | { 155 | @Override 156 | public void actionPerformed(ActionEvent e) 157 | { 158 | if (!enableDarkLaf.isSelected()) 159 | { 160 | int i = JOptionPane.showConfirmDialog(frame, "To see DarkLaf Theme Options, DarkLaf Theme needs to be enabled first.\n" + 161 | "Enable DarkLaf Theme?", "Enable DarkLaf Theme?", JOptionPane.YES_NO_OPTION); 162 | if (i != JOptionPane.YES_OPTION) 163 | { 164 | return; 165 | } 166 | installDarkLaf(); 167 | enableDarkLaf.setSelected(true); 168 | GuiConfig.setDarkLaf(true); 169 | } 170 | ThemeSettings.showSettingsDialog(frame, Dialog.ModalityType.APPLICATION_MODAL); 171 | } 172 | }); 173 | menu.add(theme); 174 | frame.setJMenuBar(menuBar); 175 | 176 | //GuiConfig 177 | shouldLimitLines.setSelected(GuiConfig.isLimitConsoleLines()); 178 | storeConfigOnClose.setSelected(GuiConfig.getStoreConfigOnClose()); 179 | enableDarkLaf.setSelected(GuiConfig.isDarkLaf()); 180 | 181 | //Deobfuscator Input 182 | JPanel inputPnl = new JPanel(); 183 | { 184 | GridBagConstraints gbc = new GridBagConstraints(); 185 | gbc.fill = GridBagConstraints.HORIZONTAL; 186 | gbc.anchor = GridBagConstraints.WEST; 187 | gbc.insets = new Insets(15, 10, 0, 10); 188 | gbc.gridwidth = 2; 189 | gbc.weightx = 1; 190 | frame.getContentPane().add(inputPnl, gbc); 191 | } 192 | inputPnl.setLayout(new GridBagLayout()); 193 | 194 | int gridy = 0; 195 | 196 | try 197 | { 198 | loaderThread.join(); 199 | } catch (InterruptedException e) 200 | { 201 | ExceptionUtil.showFatalError("", new RuntimeException("Couldn't wait for jar loader thread!", e)); 202 | System.exit(1); 203 | } 204 | configFieldsList = new SwingConfiguration(config.get()).fieldsList; 205 | 206 | for (ConfigItem i : configFieldsList) 207 | { 208 | if (i.type != SwingConfiguration.ItemType.FILE) 209 | continue; 210 | JLabel label = new JLabel(i.getDisplayName() + ":"); 211 | { 212 | GridBagConstraints gbc = new GridBagConstraints(); 213 | gbc.anchor = GridBagConstraints.BASELINE; 214 | gbc.insets = new Insets(4, 2, 7, 2); 215 | gbc.gridx = 0; 216 | gbc.gridy = gridy; 217 | inputPnl.add(label, gbc); 218 | } 219 | JTextField textField = new JTextField(); 220 | label.setLabelFor(textField); 221 | i.component = textField; 222 | { 223 | GridBagConstraints gbc = new GridBagConstraints(); 224 | gbc.insets = new Insets(0, 2, 7, 2); 225 | gbc.gridx = 1; 226 | gbc.gridy = gridy; 227 | gbc.weightx = 1; 228 | gbc.fill = GridBagConstraints.HORIZONTAL; 229 | inputPnl.add(textField, gbc); 230 | } 231 | JButton button = new JButton("Select"); 232 | { 233 | GridBagConstraints gbc = new GridBagConstraints(); 234 | gbc.insets = new Insets(0, 7, 7, 2); 235 | gbc.gridx = 2; 236 | gbc.gridy = gridy; 237 | gbc.ipadx = 15; 238 | inputPnl.add(button, gbc); 239 | } 240 | button.addActionListener(e -> 241 | { 242 | SynchronousJFXFileChooser fileChooser = new SynchronousJFXFileChooser(() -> 243 | { 244 | FileChooser ch = new FileChooser(); 245 | ch.setTitle("Select " + i.getDisplayName()); 246 | Object value = i.getValue(); 247 | boolean setDir = false; 248 | boolean setFile = false; 249 | if (value instanceof String && !((String) value).trim().isEmpty()) 250 | { 251 | File f = new File((String) value).getAbsoluteFile(); 252 | if (f.exists()) 253 | { 254 | ch.setInitialFileName(f.getName()); 255 | setFile = true; 256 | } 257 | if (f.getParentFile().exists()) 258 | { 259 | ch.setInitialDirectory(f.getParentFile()); 260 | setDir = true; 261 | } 262 | } 263 | if (!setFile && i.getDisplayName().equals("Output")) 264 | { 265 | ch.setInitialFileName("output.jar"); 266 | } 267 | if (!setDir && i.getDisplayName().equals("Output")) 268 | { 269 | Optional input = configFieldsList.stream().filter(ci -> ci.getDisplayName().equals("Input")).findAny(); 270 | if (input.isPresent()) 271 | { 272 | Object value2 = input.get().getValue(); 273 | if (value2 instanceof String && !((String) value2).trim().isEmpty()) 274 | { 275 | File f = new File((String) value2).getAbsoluteFile(); 276 | if (f.getParentFile().exists()) 277 | { 278 | ch.setInitialDirectory(f.getParentFile()); 279 | setDir = true; 280 | } 281 | } 282 | } 283 | } 284 | if (!setDir) 285 | { 286 | ch.setInitialDirectory(new File("abc").getAbsoluteFile().getParentFile()); 287 | } 288 | ch.getExtensionFilters().addAll( 289 | new FileChooser.ExtensionFilter("Jar and Zip files", "*.jar", "*.zip"), 290 | new FileChooser.ExtensionFilter("Jar files", "*.jar"), 291 | new FileChooser.ExtensionFilter("Zip files", "*.zip"), 292 | new FileChooser.ExtensionFilter("All Files", "*.*")); 293 | return ch; 294 | }); 295 | File selectedFile; 296 | if (i.getDisplayName().equals("Input")) 297 | { 298 | selectedFile = fileChooser.showOpenDialog(); 299 | } else 300 | { 301 | selectedFile = fileChooser.showSaveDialog(); 302 | } 303 | if (selectedFile != null) 304 | { 305 | String path = selectedFile.toString(); 306 | textField.setText(path); 307 | } 308 | }); 309 | gridy++; 310 | } 311 | 312 | // Boolean options 313 | JPanel boolWrapPanel = new JPanel(new WrapLayout(FlowLayout.LEFT, 5, 2)); 314 | { 315 | GridBagConstraints gbc = new GridBagConstraints(); 316 | gbc.anchor = GridBagConstraints.WEST; 317 | gbc.insets = new Insets(5, 2, 2, 2); 318 | gbc.gridx = 0; 319 | gbc.gridy = gridy; 320 | gbc.gridwidth = GridBagConstraints.REMAINDER; 321 | inputPnl.add(boolWrapPanel, gbc); 322 | } 323 | 324 | for (ConfigItem i : configFieldsList) 325 | { 326 | if (i.type != ItemType.BOOLEAN) 327 | continue; 328 | JCheckBox checkBox = new JCheckBox(i.getDisplayName()); 329 | i.component = checkBox; 330 | boolWrapPanel.add(checkBox); 331 | } 332 | 333 | //Other Options 334 | JPanel optionsPnl = new JPanel(); 335 | optionsPnl.setLayout(new GridBagLayout()); 336 | { 337 | GridBagConstraints gbc = new GridBagConstraints(); 338 | gbc.fill = GridBagConstraints.BOTH; 339 | gbc.anchor = GridBagConstraints.WEST; 340 | gbc.insets = new Insets(15, 10, 20, 10); 341 | gbc.gridwidth = 2; 342 | gbc.gridy = 1; 343 | gbc.weightx = 1; 344 | gbc.weighty = 1; 345 | frame.getContentPane().add(optionsPnl, gbc); 346 | } 347 | 348 | //The tabbed pane 349 | JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP); 350 | { 351 | GridBagConstraints gbc = new GridBagConstraints(); 352 | gbc.fill = GridBagConstraints.BOTH; 353 | gbc.weightx = 1; 354 | gbc.weighty = 1; 355 | optionsPnl.add(tabbedPane, gbc); 356 | } 357 | 358 | //Transformers 359 | JPanel transformersPanel = new JPanel(); 360 | transformersPanel.setLayout(new GridBagLayout()); 361 | //First list (available) 362 | JScrollPane transformerListScroll = new JScrollPane(); 363 | transformerListScroll.setPreferredSize(new Dimension(200, 200)); 364 | DefaultListModel transformerList = new DefaultListModel<>(); 365 | for (Class clazz : transformers) 366 | { 367 | transformerList.addElement(toShortName(clazz)); 368 | } 369 | JList transformerJList = new JList<>(transformerList); 370 | transformerJList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 371 | transformerJList.setModel(transformerList); 372 | transformerListScroll.setViewportView(transformerJList); 373 | { 374 | GridBagConstraints gbc = new GridBagConstraints(); 375 | gbc.gridx = 0; 376 | gbc.gridy = 0; 377 | gbc.gridheight = 4; 378 | gbc.anchor = GridBagConstraints.WEST; 379 | gbc.fill = GridBagConstraints.BOTH; 380 | gbc.insets = new Insets(10, 10, 10, 0); 381 | gbc.weightx = 0.5; 382 | gbc.weighty = 1; 383 | transformersPanel.add(transformerListScroll, gbc); 384 | } 385 | //Second list (selected) 386 | JScrollPane transformerSelectedScroll = new JScrollPane(); 387 | transformerSelectedScroll.setPreferredSize(new Dimension(200, 200)); 388 | transformerSelected = new DefaultListModel<>(); 389 | JList selectedJList = new JList<>(transformerSelected); 390 | selectedJList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 391 | selectedJList.setModel(transformerSelected); 392 | transformerSelectedScroll.setViewportView(selectedJList); 393 | { 394 | GridBagConstraints gbc = new GridBagConstraints(); 395 | gbc.gridy = 0; 396 | gbc.gridx = 2; 397 | gbc.gridheight = 4; 398 | gbc.anchor = GridBagConstraints.SOUTHEAST; 399 | gbc.fill = GridBagConstraints.BOTH; 400 | gbc.insets = new Insets(10, 0, 10, 0); 401 | gbc.weightx = 0.5; 402 | gbc.weighty = 1; 403 | transformersPanel.add(transformerSelectedScroll, gbc); 404 | } 405 | transformerJList.addMouseListener(new MouseAdapter() 406 | { 407 | @Override 408 | public void mouseClicked(MouseEvent e) 409 | { 410 | if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) 411 | { 412 | JList list = (JList) e.getSource(); 413 | int index = list.locationToIndex(e.getPoint()); 414 | transformerSelected.addElement(new TransformerWithConfig(transformerList.getElementAt(index))); 415 | } 416 | } 417 | }); 418 | selectedJList.addMouseListener(new MouseAdapter() 419 | { 420 | @Override 421 | public void mouseClicked(MouseEvent e) 422 | { 423 | if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) 424 | { 425 | JList list = (JList) e.getSource(); 426 | int index = list.locationToIndex(e.getPoint()); 427 | transformerSelected.remove(index); 428 | } 429 | } 430 | 431 | @Override 432 | public void mouseReleased(MouseEvent e) 433 | { 434 | if (SwingUtilities.isRightMouseButton(e) && e.getClickCount() == 1) 435 | { 436 | JList list = (JList) e.getSource(); 437 | int index = list.locationToIndex(e.getPoint()); 438 | TransformerWithConfig tConfig = transformerSelected.get(index); 439 | if (tConfig.getConfig() == null) 440 | { 441 | Class tClass = NAME_TO_TRANSFORMER.get(tConfig.getShortName()); 442 | Object config = TransformerConfigUtil.getConfig(tClass); 443 | if (config == null) 444 | { 445 | return; 446 | } 447 | tConfig.setConfig(config); 448 | } 449 | 450 | String title = "Options for transformer " + (index + 1) + ": " + tConfig.getShortName(); 451 | TransformerSpecificConfigDialog.TypeMeta meta = TransformerSpecificConfigDialog.getTypeMeta(tConfig); 452 | JDialog jd = new JDialog(frame, title, Dialog.ModalityType.APPLICATION_MODAL); 453 | jd.setBounds(100, 200, meta.wideWindow ? 450 : 370, 60 + 40 * meta.count); 454 | jd.setLocationRelativeTo(frame); 455 | jd.getContentPane().setLayout(new GridBagLayout()); 456 | jd.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Close"); 457 | jd.getRootPane().getActionMap().put("Close", new AbstractAction() 458 | { 459 | @Override 460 | public void actionPerformed(ActionEvent e) 461 | { 462 | if (jd.isFocused() && jd.isActive()) 463 | { 464 | jd.dispose(); 465 | } 466 | } 467 | }); 468 | 469 | TransformerSpecificConfigDialog.fill(jd, tConfig); 470 | 471 | jd.setVisible(true); 472 | } 473 | } 474 | }); 475 | //Buttons 476 | //4 panels to position buttons correctly 477 | JPanel panel1 = new JPanel(); 478 | { 479 | GridBagConstraints gbc = new GridBagConstraints(); 480 | gbc.gridx = 1; 481 | gbc.gridy = 0; 482 | gbc.weighty = 0.25; 483 | transformersPanel.add(panel1, gbc); 484 | } 485 | JPanel panel2 = new JPanel(); 486 | { 487 | GridBagConstraints gbc = new GridBagConstraints(); 488 | gbc.gridx = 1; 489 | gbc.gridy = 3; 490 | gbc.weighty = 0.25; 491 | transformersPanel.add(panel2, gbc); 492 | } 493 | 494 | int buttonWidth = 30; 495 | JButton add = new JButton(">"); 496 | { 497 | Insets margin = add.getMargin(); 498 | add.setMargin(new Insets(margin.top + 30, 1, margin.bottom + 30, 1)); 499 | int prefHeight = add.getPreferredSize().height; 500 | add.setPreferredSize(new Dimension(buttonWidth, prefHeight)); 501 | add.setMinimumSize(new Dimension(buttonWidth, prefHeight)); 502 | add.setMaximumSize(new Dimension(buttonWidth, prefHeight)); 503 | add.addActionListener(e -> 504 | { 505 | for (String str : transformerJList.getSelectedValuesList()) 506 | { 507 | transformerSelected.addElement(new TransformerWithConfig(str)); 508 | } 509 | }); 510 | GridBagConstraints gbc = new GridBagConstraints(); 511 | gbc.gridx = 1; 512 | gbc.gridy = 1; 513 | gbc.insets = new Insets(5, 0, 5, 0); 514 | transformersPanel.add(add, gbc); 515 | } 516 | 517 | JButton remove = new JButton("<"); 518 | { 519 | Insets margin = remove.getMargin(); 520 | remove.setMargin(new Insets(margin.top + 30, 1, margin.bottom + 30, 1)); 521 | int prefHeight2 = remove.getPreferredSize().height; 522 | remove.setPreferredSize(new Dimension(buttonWidth, prefHeight2)); 523 | remove.setMinimumSize(new Dimension(buttonWidth, prefHeight2)); 524 | remove.setMaximumSize(new Dimension(buttonWidth, prefHeight2)); 525 | remove.addActionListener(e -> 526 | { 527 | int[] indexes = selectedJList.getSelectedIndices(); 528 | Arrays.sort(indexes); 529 | int[] reversed = IntStream.range(0, indexes.length).map(i -> indexes[indexes.length - i - 1]).toArray(); 530 | for (int i : reversed) 531 | { 532 | transformerSelected.remove(i); 533 | } 534 | }); 535 | GridBagConstraints gbc = new GridBagConstraints(); 536 | gbc.gridx = 1; 537 | gbc.gridy = 2; 538 | gbc.insets = new Insets(5, 2, 5, 2); 539 | transformersPanel.add(remove, gbc); 540 | } 541 | 542 | JButton up = new JButton("Ʌ"); 543 | { 544 | up.addActionListener(e -> 545 | { 546 | //method returns always ordered array 547 | int[] indexesArr = selectedJList.getSelectedIndices(); 548 | List indexes = Arrays.stream(indexesArr).boxed().collect(Collectors.toList()); 549 | //copy for iteration 550 | ArrayList indexesIter = new ArrayList<>(indexes); 551 | //contains target indices of elements already moved (prevents changing order of elements) 552 | List blocked = new ArrayList<>(); 553 | for (int size = indexesIter.size(), i = 0; i < size; i++) 554 | { 555 | int valI = indexesIter.get(i); 556 | int newIndex = valI - 1; 557 | if (blocked.contains(newIndex)) 558 | { 559 | //if target index is blocked, we cannot move, so our index is blocked too 560 | blocked.add(valI); 561 | continue; 562 | } 563 | //move up 564 | TransformerWithConfig t = transformerSelected.remove(valI); 565 | if (newIndex < 0) 566 | { 567 | newIndex = 0; 568 | //cannot move up, so block our index to prevent swapping with potential next selected element 569 | blocked.add(newIndex); 570 | } 571 | transformerSelected.add(newIndex, t); 572 | indexes.set(i, newIndex); 573 | } 574 | selectedJList.setSelectedIndices(indexes.stream().mapToInt(i -> i).toArray()); 575 | }); 576 | Insets margin = up.getMargin(); 577 | up.setMargin(new Insets(margin.top + 30, 1, margin.bottom + 30, 1)); 578 | int prefHeight3 = up.getPreferredSize().height; 579 | up.setPreferredSize(new Dimension(buttonWidth, prefHeight3)); 580 | up.setMinimumSize(new Dimension(buttonWidth, prefHeight3)); 581 | up.setMaximumSize(new Dimension(buttonWidth, prefHeight3)); 582 | GridBagConstraints gbc = new GridBagConstraints(); 583 | gbc.anchor = GridBagConstraints.EAST; 584 | gbc.gridx = 3; 585 | gbc.gridy = 1; 586 | gbc.insets = new Insets(5, 2, 5, 2); 587 | transformersPanel.add(up, gbc); 588 | } 589 | JButton down = new JButton("V"); 590 | { 591 | down.addActionListener(e -> 592 | { 593 | //method returns always ordered array 594 | int[] indexesArr = selectedJList.getSelectedIndices(); 595 | List indexes = Arrays.stream(indexesArr).boxed().collect(Collectors.toList()); 596 | //copy for iteration 597 | ArrayList indexesIter = new ArrayList<>(indexes); 598 | //contains target indices of elements already moved (prevents changing order of elements) 599 | List blocked = new ArrayList<>(); 600 | //iterate in reverse order to move last element down first 601 | for (int size = indexesIter.size(), i = size - 1; i >= 0; i--) 602 | { 603 | int valI = indexesIter.get(i); 604 | int newIndex = valI + 1; 605 | if (blocked.contains(newIndex)) 606 | { 607 | //if target index is blocked, we cannot move, so our index is blocked too 608 | blocked.add(valI); 609 | continue; 610 | } 611 | //move down 612 | TransformerWithConfig t = transformerSelected.remove(valI); 613 | if (newIndex > transformerSelected.size() - 1) 614 | { 615 | newIndex = transformerSelected.size(); 616 | //cannot move down, so block our index to prevent swapping with potential previous selected element 617 | blocked.add(newIndex); 618 | } 619 | transformerSelected.add(newIndex, t); 620 | indexes.set(i, newIndex); 621 | } 622 | selectedJList.setSelectedIndices(indexes.stream().mapToInt(i -> i).toArray()); 623 | }); 624 | Insets margin = down.getMargin(); 625 | down.setMargin(new Insets(margin.top + 30, 1, margin.bottom + 30, 1)); 626 | int prefHeightDown = down.getPreferredSize().height; 627 | down.setPreferredSize(new Dimension(buttonWidth, prefHeightDown)); 628 | down.setMinimumSize(new Dimension(buttonWidth, prefHeightDown)); 629 | down.setMaximumSize(new Dimension(buttonWidth, prefHeightDown)); 630 | GridBagConstraints gbc = new GridBagConstraints(); 631 | gbc.anchor = GridBagConstraints.EAST; 632 | gbc.gridx = 3; 633 | gbc.gridy = 2; 634 | gbc.insets = new Insets(5, 2, 5, 2); 635 | transformersPanel.add(down, gbc); 636 | } 637 | 638 | tabbedPane.addTab("Transformers", transformersPanel); 639 | 640 | //File lists (path, libraries) 641 | for (ConfigItem i : configFieldsList) 642 | { 643 | if (i.type != ItemType.FILELIST) 644 | continue; 645 | JPanel libPanel = new JPanel(); 646 | libPanel.setLayout(new GridBagLayout()); 647 | JScrollPane libListScroll = new JScrollPane(); 648 | DefaultListModel librariesList = new DefaultListModel<>(); 649 | i.component = librariesList; 650 | JList libJList = new JList<>(librariesList); 651 | libJList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 652 | libJList.setModel(librariesList); 653 | libListScroll.setViewportView(libJList); 654 | { 655 | GridBagConstraints gbc = new GridBagConstraints(); 656 | gbc.gridx = 0; 657 | gbc.gridy = 0; 658 | gbc.gridheight = 4; 659 | gbc.anchor = GridBagConstraints.WEST; 660 | gbc.fill = GridBagConstraints.BOTH; 661 | gbc.insets = new Insets(10, 10, 10, 10); 662 | gbc.weightx = 1; 663 | gbc.weighty = 1; 664 | libPanel.add(libListScroll, gbc); 665 | } 666 | 667 | libJList.addKeyListener(new KeyAdapter() 668 | { 669 | @Override 670 | public void keyPressed(KeyEvent event) 671 | { 672 | if (event.getKeyCode() == KeyEvent.VK_DELETE) 673 | { 674 | int[] indexes = libJList.getSelectedIndices(); 675 | Arrays.sort(indexes); 676 | int[] reversed = IntStream.range(0, indexes.length).map(i -> indexes[indexes.length - i - 1]) 677 | .toArray(); 678 | for (int i : reversed) 679 | { 680 | librariesList.remove(i); 681 | } 682 | } 683 | } 684 | }); 685 | 686 | //Buttons 687 | //4 panels to position buttons correctly 688 | JPanel paddingPanel1 = new JPanel(); 689 | { 690 | GridBagConstraints gbc = new GridBagConstraints(); 691 | gbc.gridx = 1; 692 | gbc.weighty = 0.5; 693 | libPanel.add(paddingPanel1, gbc); 694 | } 695 | JPanel addLibPanel = new JPanel(); 696 | addLibPanel.setLayout(new GridBagLayout()); 697 | { 698 | GridBagConstraints gbc = new GridBagConstraints(); 699 | gbc.gridx = 1; 700 | gbc.weighty = 0.5; 701 | libPanel.add(addLibPanel, gbc); 702 | } 703 | JPanel removeLibPanel = new JPanel(); 704 | removeLibPanel.setLayout(new GridBagLayout()); 705 | { 706 | GridBagConstraints gbc = new GridBagConstraints(); 707 | gbc.gridx = 1; 708 | gbc.weighty = 0.5; 709 | libPanel.add(removeLibPanel, gbc); 710 | } 711 | JPanel paddingPanel2 = new JPanel(); 712 | { 713 | GridBagConstraints gbc = new GridBagConstraints(); 714 | gbc.gridx = 1; 715 | gbc.weighty = 0.5; 716 | libPanel.add(paddingPanel2, gbc); 717 | } 718 | JButton addLib = new JButton(" Add "); 719 | 720 | addLib.addActionListener(e -> 721 | { 722 | List selectedFiles = new SynchronousJFXFileChooser(() -> 723 | { 724 | FileChooser ch = new FileChooser(); 725 | ch.setTitle("Select " + i.getDisplayName()); 726 | Object value = i.getValue(); 727 | if (value instanceof List && !((List) value).isEmpty()) 728 | { 729 | List list = (List) value; 730 | File f = new File(list.get(list.size() - 1)); 731 | if (f.getParentFile().exists()) 732 | ch.setInitialDirectory(f.getParentFile()); 733 | } 734 | ch.getExtensionFilters().addAll( 735 | new FileChooser.ExtensionFilter("Jar and Zip files", "*.jar", "*.zip"), 736 | new FileChooser.ExtensionFilter("Jar files", "*.jar"), 737 | new FileChooser.ExtensionFilter("Zip files", "*.zip"), 738 | new FileChooser.ExtensionFilter("All Files", "*.*")); 739 | return ch; 740 | }).showOpenMultipleDialog(); 741 | if (selectedFiles != null) 742 | { 743 | for (File selectedFile : selectedFiles) 744 | { 745 | String path = selectedFile.getPath(); 746 | librariesList.addElement(path); 747 | } 748 | } 749 | }); 750 | { 751 | GridBagConstraints gbc = new GridBagConstraints(); 752 | gbc.anchor = GridBagConstraints.CENTER; 753 | gbc.insets = new Insets(5, 5, 5, 20); 754 | addLibPanel.add(addLib, gbc); 755 | } 756 | 757 | JButton removeLib = new JButton("Remove"); 758 | removeLib.addActionListener(e -> 759 | { 760 | int[] indexes = libJList.getSelectedIndices(); 761 | Arrays.sort(indexes); 762 | int[] reversed = IntStream.range(0, indexes.length).map(i1 -> indexes[indexes.length - i1 - 1]) 763 | .toArray(); 764 | for (int i1 : reversed) 765 | { 766 | librariesList.remove(i1); 767 | } 768 | }); 769 | { 770 | GridBagConstraints gbc = new GridBagConstraints(); 771 | gbc.anchor = GridBagConstraints.CENTER; 772 | gbc.insets = new Insets(5, 5, 5, 20); 773 | removeLibPanel.add(removeLib, gbc); 774 | } 775 | 776 | tabbedPane.addTab(i.getDisplayName(), libPanel); 777 | } 778 | 779 | for (ConfigItem i : configFieldsList) 780 | { 781 | if (i.type != ItemType.STRINGLIST) 782 | continue; 783 | JPanel stringPanel = new JPanel(); 784 | stringPanel.setLayout(new GridBagLayout()); 785 | 786 | JPanel stringLeftPanel = new JPanel(new GridBagLayout()); 787 | { 788 | GridBagConstraints gbc = new GridBagConstraints(); 789 | gbc.gridx = 0; 790 | gbc.gridy = 0; 791 | gbc.gridheight = GridBagConstraints.REMAINDER; 792 | gbc.fill = GridBagConstraints.BOTH; 793 | gbc.insets = new Insets(10, 0, 10, 0); 794 | gbc.weightx = 1; 795 | gbc.weighty = 1; 796 | stringPanel.add(stringLeftPanel, gbc); 797 | } 798 | 799 | JScrollPane stringListScroll = new JScrollPane(); 800 | DefaultListModel stringList = new DefaultListModel<>(); 801 | i.component = stringList; 802 | JList stringJList = new JList<>(stringList); 803 | stringJList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 804 | stringJList.setModel(stringList); 805 | stringListScroll.setViewportView(stringJList); 806 | { 807 | GridBagConstraints gbc = new GridBagConstraints(); 808 | gbc.gridx = 0; 809 | gbc.gridy = 0; 810 | gbc.gridheight = 4; 811 | gbc.anchor = GridBagConstraints.WEST; 812 | gbc.fill = GridBagConstraints.BOTH; 813 | gbc.insets = new Insets(0, 10, 0, 10); 814 | gbc.weightx = 1; 815 | gbc.weighty = 1; 816 | stringLeftPanel.add(stringListScroll, gbc); 817 | } 818 | 819 | //Text pane 820 | JTextField textPane = new JTextField(); 821 | textPane.addKeyListener(new KeyAdapter() 822 | { 823 | 824 | @Override 825 | public void keyPressed(KeyEvent event) 826 | { 827 | if (event.getKeyCode() == KeyEvent.VK_ENTER && textPane.getText() != null && !textPane.getText().isEmpty()) 828 | { 829 | stringList.addElement(textPane.getText()); 830 | textPane.setText(""); 831 | } 832 | } 833 | }); 834 | { 835 | GridBagConstraints gbc = new GridBagConstraints(); 836 | gbc.gridx = 0; 837 | gbc.gridy = 4; 838 | gbc.anchor = GridBagConstraints.WEST; 839 | gbc.fill = GridBagConstraints.HORIZONTAL; 840 | gbc.insets = new Insets(0, 10, 0, 10); 841 | gbc.weightx = 1; 842 | stringLeftPanel.add(textPane, gbc); 843 | } 844 | 845 | stringJList.addKeyListener(new KeyAdapter() 846 | { 847 | 848 | @Override 849 | public void keyPressed(KeyEvent event) 850 | { 851 | if (event.getKeyCode() == KeyEvent.VK_DELETE) 852 | { 853 | int[] indexes = stringJList.getSelectedIndices(); 854 | Arrays.sort(indexes); 855 | int[] reversed = IntStream.range(0, indexes.length).map(i -> indexes[indexes.length - i - 1]) 856 | .toArray(); 857 | for (int i : reversed) 858 | { 859 | stringList.remove(i); 860 | } 861 | } 862 | } 863 | }); 864 | 865 | //Buttons 866 | //4 panels to position buttons correctly 867 | JPanel paddingPanel1 = new JPanel(); 868 | { 869 | GridBagConstraints gbc = new GridBagConstraints(); 870 | gbc.gridx = 1; 871 | gbc.weighty = 0.5; 872 | stringPanel.add(paddingPanel1, gbc); 873 | } 874 | JPanel addStringPanel = new JPanel(); 875 | addStringPanel.setLayout(new GridBagLayout()); 876 | { 877 | GridBagConstraints gbc = new GridBagConstraints(); 878 | gbc.gridx = 1; 879 | gbc.weighty = 0.5; 880 | stringPanel.add(addStringPanel, gbc); 881 | } 882 | JPanel removeStringPanel = new JPanel(); 883 | removeStringPanel.setLayout(new GridBagLayout()); 884 | { 885 | GridBagConstraints gbc = new GridBagConstraints(); 886 | gbc.gridx = 1; 887 | gbc.weighty = 0.5; 888 | stringPanel.add(removeStringPanel, gbc); 889 | } 890 | JPanel paddingPanel2 = new JPanel(); 891 | { 892 | GridBagConstraints gbc = new GridBagConstraints(); 893 | gbc.gridx = 1; 894 | gbc.weighty = 0.5; 895 | stringPanel.add(paddingPanel2, gbc); 896 | } 897 | 898 | JButton addString = new JButton(" Add "); 899 | addString.addActionListener(e -> 900 | { 901 | if (textPane.getText() != null && !textPane.getText().isEmpty()) 902 | { 903 | stringList.addElement(textPane.getText()); 904 | textPane.setText(""); 905 | } 906 | }); 907 | { 908 | GridBagConstraints gbc = new GridBagConstraints(); 909 | gbc.anchor = GridBagConstraints.CENTER; 910 | gbc.insets = new Insets(5, 5, 5, 20); 911 | addStringPanel.add(addString, gbc); 912 | } 913 | 914 | JButton removeString = new JButton("Remove"); 915 | removeString.addActionListener(e -> 916 | { 917 | int[] indexes = stringJList.getSelectedIndices(); 918 | Arrays.sort(indexes); 919 | int[] reversed = IntStream.range(0, indexes.length).map(i12 -> indexes[indexes.length - i12 - 1]) 920 | .toArray(); 921 | for (int i12 : reversed) 922 | { 923 | stringList.remove(i12); 924 | } 925 | }); 926 | { 927 | GridBagConstraints gbc = new GridBagConstraints(); 928 | gbc.anchor = GridBagConstraints.CENTER; 929 | gbc.insets = new Insets(5, 5, 5, 20); 930 | removeStringPanel.add(removeString, gbc); 931 | } 932 | 933 | tabbedPane.addTab(i.getDisplayName(), stringPanel); 934 | } 935 | 936 | //Config and Run buttons 937 | JButton load = new JButton("Load Config"); 938 | { 939 | GridBagConstraints gbc = new GridBagConstraints(); 940 | gbc.gridy = 2; 941 | gbc.anchor = GridBagConstraints.WEST; 942 | gbc.insets = new Insets(0, 20, 20, 10); 943 | frame.getContentPane().add(load, gbc); 944 | } 945 | 946 | load.addActionListener(e -> 947 | { 948 | JDialog loadConfigFrame = new JDialog(frame, Dialog.ModalityType.APPLICATION_MODAL); 949 | loadConfigFrame.setTitle("Load Config"); 950 | loadConfigFrame.setBounds(100, 200, 450, 200); 951 | loadConfigFrame.setLocationRelativeTo(frame); 952 | loadConfigFrame.setResizable(true); 953 | loadConfigFrame.getContentPane().setLayout(new GridBagLayout()); 954 | 955 | JLabel yourConfiguration = new JLabel("Input your configuration below:"); 956 | { 957 | GridBagConstraints gbc = new GridBagConstraints(); 958 | gbc.anchor = GridBagConstraints.PAGE_START; 959 | gbc.insets = new Insets(15, 5, 5, 5); 960 | gbc.gridx = 0; 961 | gbc.gridy = 0; 962 | loadConfigFrame.getContentPane().add(yourConfiguration, gbc); 963 | } 964 | 965 | JScrollPane scrollPane = new JScrollPane(); 966 | JTextPane textPane = new JTextPane(); 967 | scrollPane.setViewportView(textPane); 968 | { 969 | GridBagConstraints gbc = new GridBagConstraints(); 970 | gbc.insets = new Insets(10, 10, 5, 10); 971 | gbc.gridx = 0; 972 | gbc.gridy = 1; 973 | gbc.weightx = 1; 974 | gbc.weighty = 1; 975 | gbc.fill = GridBagConstraints.BOTH; 976 | loadConfigFrame.getContentPane().add(scrollPane, gbc); 977 | } 978 | 979 | JButton submitButton = new JButton("Load"); 980 | { 981 | GridBagConstraints gbc = new GridBagConstraints(); 982 | gbc.insets = new Insets(0, 0, 10, 5); 983 | gbc.gridx = 0; 984 | gbc.gridy = 2; 985 | loadConfigFrame.getContentPane().add(submitButton, gbc); 986 | } 987 | submitButton.addActionListener(e13 -> 988 | { 989 | String args1 = textPane.getText(); 990 | readAndApplyConfig(configFieldsList, transformerSelected, args1); 991 | loadConfigFrame.dispose(); 992 | }); 993 | loadConfigFrame.setVisible(true); 994 | }); 995 | 996 | JButton save = new JButton("Save Config"); 997 | { 998 | GridBagConstraints gbc = new GridBagConstraints(); 999 | gbc.gridx = 1; 1000 | gbc.gridy = 2; 1001 | gbc.anchor = GridBagConstraints.WEST; 1002 | gbc.insets = new Insets(0, 10, 20, 20); 1003 | frame.getContentPane().add(save, gbc); 1004 | } 1005 | 1006 | save.addActionListener(e -> 1007 | { 1008 | JDialog saveConfigFrame = new JDialog(frame, Dialog.ModalityType.APPLICATION_MODAL); 1009 | saveConfigFrame.setTitle("Save Config"); 1010 | saveConfigFrame.setBounds(100, 200, 450, 200); 1011 | saveConfigFrame.setLocationRelativeTo(frame); 1012 | saveConfigFrame.setResizable(true); 1013 | saveConfigFrame.getContentPane().setLayout(new GridBagLayout()); 1014 | 1015 | JLabel yourConfiguration = new JLabel("Your current configuration is below."); 1016 | GridBagConstraints gbc_yourConfiguration = new GridBagConstraints(); 1017 | gbc_yourConfiguration.anchor = GridBagConstraints.PAGE_START; 1018 | gbc_yourConfiguration.insets = new Insets(15, 5, 5, 5); 1019 | gbc_yourConfiguration.gridx = 0; 1020 | gbc_yourConfiguration.gridy = 0; 1021 | saveConfigFrame.getContentPane().add(yourConfiguration, gbc_yourConfiguration); 1022 | 1023 | JScrollPane scrollPane = new JScrollPane(); 1024 | JTextPane textPane = new JTextPane(); 1025 | textPane.setEditable(false); 1026 | textPane.setToolTipText( 1027 | "Tip: If you copy this and paste it in the \"Load Config\" box, it will automatically input your configuration."); 1028 | scrollPane.setViewportView(textPane); 1029 | GridBagConstraints gbc_scrollPane = new GridBagConstraints(); 1030 | gbc_scrollPane.insets = new Insets(10, 10, 5, 10); 1031 | gbc_scrollPane.gridx = 0; 1032 | gbc_scrollPane.gridy = 1; 1033 | gbc_scrollPane.weightx = 1; 1034 | gbc_scrollPane.weighty = 1; 1035 | gbc_scrollPane.fill = GridBagConstraints.BOTH; 1036 | saveConfigFrame.getContentPane().add(scrollPane, gbc_scrollPane); 1037 | 1038 | //Write args 1039 | String t = createConfig(configFieldsList, transformerSelected); 1040 | textPane.setText(t); 1041 | 1042 | JButton copyButton = new JButton("Copy"); 1043 | GridBagConstraints gbc_copyButton = new GridBagConstraints(); 1044 | gbc_copyButton.insets = new Insets(0, 0, 10, 5); 1045 | gbc_copyButton.gridx = 0; 1046 | gbc_copyButton.gridy = 2; 1047 | saveConfigFrame.getContentPane().add(copyButton, gbc_copyButton); 1048 | copyButton.addActionListener(e12 -> Toolkit.getDefaultToolkit(). 1049 | getSystemClipboard().setContents(new StringSelection(textPane.getText()), null)); 1050 | saveConfigFrame.setVisible(true); 1051 | }); 1052 | 1053 | GridBagConstraints gbl_run = new GridBagConstraints(); 1054 | gbl_run.anchor = GridBagConstraints.SOUTHEAST; 1055 | gbl_run.gridx = 1; 1056 | gbl_run.gridy = 2; 1057 | gbl_run.ipadx = 15; 1058 | gbl_run.insets = new Insets(0, 10, 20, 20); 1059 | JButton run = new JButton("Run"); 1060 | 1061 | JTextArea area = new JTextArea(); 1062 | if (!enableDarkLaf.isSelected()) 1063 | { 1064 | area.setFont(area.getFont().deriveFont(12F)); 1065 | } 1066 | PrintStream print = new PrintStream(new DeobfuscatorOutputStream(System.out, area)); 1067 | System.setErr(print); 1068 | System.setOut(print); 1069 | deob.hookLogging(print); 1070 | 1071 | run.addActionListener(e -> 1072 | { 1073 | run.setEnabled(false); 1074 | // Start 1075 | JFrame newFrame = new JFrame(); 1076 | newFrame.setTitle("Console"); 1077 | area.setEditable(false); 1078 | JScrollPane outputScrollPane = new JScrollPane(area); 1079 | newFrame.getContentPane().add(outputScrollPane); 1080 | newFrame.pack(); 1081 | newFrame.setSize(1400, 600); 1082 | newFrame.setVisible(true); 1083 | Thread thread = new Thread(() -> 1084 | { 1085 | try 1086 | { 1087 | //Set fields 1088 | for (ConfigItem item : configFieldsList) 1089 | { 1090 | item.clearFieldValue(); 1091 | item.setFieldValue(); 1092 | } 1093 | List transformerConfigs = new ArrayList<>(); 1094 | for (Object o : transformerSelected.toArray()) 1095 | { 1096 | TransformerWithConfig transformerWithConfig = (TransformerWithConfig) o; 1097 | if (transformerWithConfig.getConfig() == null) 1098 | { 1099 | transformerConfigs.add(trans.getConfigFor(NAME_TO_TRANSFORMER.get(transformerWithConfig.getShortName()))); 1100 | } else 1101 | { 1102 | transformerConfigs.add(transformerWithConfig.getConfig()); 1103 | } 1104 | } 1105 | deob.getConfig().setTransformers(trans, transformerConfigs); 1106 | try 1107 | { 1108 | deob.run(); 1109 | } catch (InvocationTargetException e1) 1110 | { 1111 | if (e1.getTargetException().getClass().getName(). 1112 | equals("com.javadeobfuscator.deobfuscator.exceptions.NoClassInPathException")) 1113 | { 1114 | for (int i = 0; i < 5; i++) 1115 | { 1116 | System.out.println(); 1117 | } 1118 | System.out.println("** DO NOT OPEN AN ISSUE ON GITHUB **"); 1119 | System.out.println("Could not locate a class file."); 1120 | System.out.println("Have you added the necessary files to the -libraries argument?"); 1121 | System.out.println("The error was:"); 1122 | } else if (e1.getTargetException().getClass().getName(). 1123 | equals("com.javadeobfuscator.deobfuscator.exceptions.PreventableStackOverflowError")) 1124 | { 1125 | for (int i = 0; i < 5; i++) 1126 | { 1127 | System.out.println(); 1128 | } 1129 | System.out.println("** DO NOT OPEN AN ISSUE ON GITHUB **"); 1130 | System.out.println("A StackOverflowError occurred during deobfuscation, but it is preventable"); 1131 | System.out.println("Try increasing your stack size using the -Xss flag"); 1132 | System.out.println("The error was:"); 1133 | } else 1134 | { 1135 | for (int i = 0; i < 5; i++) 1136 | { 1137 | System.out.println(); 1138 | } 1139 | System.out.println("Deobfuscation failed. Please open a ticket on GitHub and provide the following error:"); 1140 | } 1141 | e1.getTargetException().printStackTrace(); 1142 | } 1143 | } catch (Throwable e1) 1144 | { 1145 | JFrame newFrame1 = new JFrame(); 1146 | newFrame1.setTitle("Error"); 1147 | newFrame1.setBounds(100, 100, 500, 400); 1148 | newFrame1.setResizable(true); 1149 | newFrame1.getContentPane().setLayout(new GridBagLayout()); 1150 | 1151 | JLabel yourConfiguration = new JLabel("An error occured while running deobfuscator."); 1152 | GridBagConstraints gbc_yourConfiguration = new GridBagConstraints(); 1153 | gbc_yourConfiguration.anchor = GridBagConstraints.PAGE_START; 1154 | gbc_yourConfiguration.insets = new Insets(15, 5, 5, 5); 1155 | gbc_yourConfiguration.gridx = 0; 1156 | gbc_yourConfiguration.gridy = 0; 1157 | newFrame1.getContentPane().add(yourConfiguration, gbc_yourConfiguration); 1158 | 1159 | JScrollPane scrollPane = new JScrollPane(); 1160 | JTextPane textPane = new JTextPane(); 1161 | textPane.setEditable(false); 1162 | scrollPane.setViewportView(textPane); 1163 | GridBagConstraints gbc_scrollPane = new GridBagConstraints(); 1164 | gbc_scrollPane.insets = new Insets(2, 10, 5, 10); 1165 | gbc_scrollPane.gridx = 0; 1166 | gbc_scrollPane.gridy = 1; 1167 | gbc_scrollPane.weightx = 1; 1168 | gbc_scrollPane.weighty = 1; 1169 | gbc_scrollPane.fill = GridBagConstraints.BOTH; 1170 | newFrame1.getContentPane().add(scrollPane, gbc_scrollPane); 1171 | StringWriter stringWriter = new StringWriter(); 1172 | PrintWriter writer = new PrintWriter(stringWriter); 1173 | e1.printStackTrace(writer); 1174 | textPane.setText(stringWriter.toString()); 1175 | Toolkit toolkit = Toolkit.getDefaultToolkit(); 1176 | Dimension screenSize = toolkit.getScreenSize(); 1177 | newFrame1.setLocation((screenSize.width - newFrame1.getWidth()) / 2, (screenSize.height - newFrame1.getHeight()) / 2); 1178 | newFrame1.setVisible(true); 1179 | } 1180 | deob.clearClasses(); 1181 | }); 1182 | thread.start(); 1183 | newFrame.addWindowListener(new WindowAdapter() 1184 | { 1185 | @Override 1186 | public void windowClosing(WindowEvent e) 1187 | { 1188 | print.flush(); 1189 | area.setText(null); 1190 | run.setEnabled(true); 1191 | if (thread.isAlive()) 1192 | { 1193 | thread.stop(); 1194 | deob.clearClasses(); 1195 | } 1196 | e.getWindow().dispose(); 1197 | } 1198 | }); 1199 | }); 1200 | 1201 | frame.getContentPane().add(run, gbl_run); 1202 | 1203 | if (GuiConfig.getStoreConfigOnClose()) 1204 | { 1205 | readAndApplyConfig(configFieldsList, transformerSelected, GuiConfig.getConfig()); 1206 | } 1207 | frame.addWindowListener(new WindowAdapter() 1208 | { 1209 | @Override 1210 | public void windowClosing(WindowEvent e) 1211 | { 1212 | writeAndSaveGuiConfig(configFieldsList); 1213 | } 1214 | }); 1215 | 1216 | try 1217 | { 1218 | jfxInitThread.join(); 1219 | } catch (InterruptedException e) 1220 | { 1221 | ExceptionUtil.showFatalError("", new RuntimeException("Couldn't wait for jfx init thread", e)); 1222 | } 1223 | frame.setVisible(true); 1224 | } 1225 | 1226 | public static void initJFX() 1227 | { 1228 | try 1229 | { 1230 | SynchronousJFXCaller.init(); 1231 | } catch (NoClassDefFoundError e) 1232 | { 1233 | e.printStackTrace(); 1234 | ensureSwingLafLoaded(); 1235 | ExceptionUtil.showFatalError("You need a JVM with JavaFX (non-headless installation).\n\n" + 1236 | "Could not find class " + e.getMessage()); 1237 | System.exit(1); 1238 | } 1239 | } 1240 | 1241 | public static synchronized void ensureSwingLafLoaded() 1242 | { 1243 | if (!swingLafLoaded) 1244 | { 1245 | swingLafLoaded = true; 1246 | try 1247 | { 1248 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 1249 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) 1250 | { 1251 | e.printStackTrace(); 1252 | } 1253 | } 1254 | } 1255 | 1256 | private static void installDarkLaf() 1257 | { 1258 | ThemeSettings.getInstance().setConfiguration(GuiConfig.getDarklafSettings()); 1259 | LafManager.install(); 1260 | ThemeSettings.getInstance().apply(); 1261 | } 1262 | 1263 | private static void writeAndSaveGuiConfig(List fields) 1264 | { 1265 | GuiConfig.setLimitConsoleLines(shouldLimitLines.isSelected()); 1266 | GuiConfig.setStoreConfigOnClose(storeConfigOnClose.isSelected()); 1267 | GuiConfig.setConfig(createConfig(fields, transformerSelected)); 1268 | if (GuiConfig.isDarkLaf()) 1269 | { 1270 | GuiConfig.setDarklafSettings(ThemeSettings.getInstance().exportConfiguration()); 1271 | } 1272 | GuiConfig.save(); 1273 | } 1274 | 1275 | private static void readAndApplyConfig(List fields, DefaultListModel transformerSelected, String args1) 1276 | { 1277 | List split = splitQuoteAware(args1, ' '); 1278 | 1279 | for (ConfigItem i : fields) 1280 | { 1281 | i.clearValue(); 1282 | } 1283 | transformerSelected.clear(); 1284 | for (int i = 0; i < split.size(); i++) 1285 | { 1286 | String arg = split.get(i); 1287 | for (ConfigItem item : fields) 1288 | { 1289 | if (arg.equals("-" + item.getFieldName())) 1290 | { 1291 | if (item.type == ItemType.BOOLEAN) 1292 | item.setValue(true); 1293 | else if (split.size() > i + 1) 1294 | { 1295 | String value = split.get(i + 1); 1296 | if (value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') 1297 | { 1298 | value = value.substring(1, value.length() - 1); 1299 | } 1300 | if (item.type == ItemType.FILE) 1301 | item.setValue(value); 1302 | else 1303 | ((DefaultListModel) item.component).addElement(value); 1304 | } 1305 | } 1306 | } 1307 | if (arg.equals("-transformer") && split.size() > i + 1) 1308 | { 1309 | String value = split.get(i + 1); 1310 | int pos = value.indexOf(":"); 1311 | if (pos != -1) 1312 | { 1313 | String transformerClass = value.substring(0, pos); 1314 | if (NAME_TO_TRANSFORMER.containsKey(transformerClass)) 1315 | { 1316 | Class clazz = NAME_TO_TRANSFORMER.get(transformerClass); 1317 | String shortenedName = TRANSFORMER_TO_NAME.get(clazz); 1318 | Object cfg = TransformerConfigUtil.getConfig(clazz); 1319 | if (cfg != null) 1320 | { 1321 | Class cfgClazz = cfg.getClass(); 1322 | try 1323 | { 1324 | String cfgStr = value.substring(pos + 1); 1325 | List opts = splitQuoteAware(cfgStr, ':'); 1326 | for (String opt : opts) 1327 | { 1328 | String[] optSplit = opt.split("=", 2); 1329 | if (optSplit.length != 2) 1330 | { 1331 | System.out.println("Transformer config option without value: " + opt); 1332 | continue; 1333 | } 1334 | String key = optSplit[0]; 1335 | String sval = optSplit[1]; 1336 | if (sval.charAt(0) == '"' && sval.charAt(sval.length() - 1) == '"') 1337 | { 1338 | sval = sval.substring(1, sval.length() - 1); 1339 | } 1340 | Field field = TransformerConfigUtil.getTransformerConfigFieldWithSuperclass(cfgClazz, key); 1341 | if (field == null) 1342 | { 1343 | System.out.println("Unknown transformer config option " + key); 1344 | continue; 1345 | } 1346 | Class fType = field.getType(); 1347 | field.setAccessible(true); 1348 | try 1349 | { 1350 | Object oval = TransformerConfigUtil.convertToObj(fType, sval); 1351 | if (oval == null) 1352 | { 1353 | System.out.println("GUI does not support config type " + fType + ", option name: " + key + " in " + 1354 | shortenedName); 1355 | continue; 1356 | } 1357 | field.set(cfg, oval); 1358 | } catch (NumberFormatException ex) 1359 | { 1360 | System.out.println("Could not convert " + sval + " to " + fType + ", option name: " + key + " in " + 1361 | shortenedName); 1362 | ex.printStackTrace(); 1363 | } 1364 | } 1365 | } catch (ReflectiveOperationException ex) 1366 | { 1367 | ex.printStackTrace(); 1368 | } 1369 | } 1370 | transformerSelected.addElement(new TransformerWithConfig(shortenedName, cfg)); 1371 | } 1372 | } else 1373 | { 1374 | if (NAME_TO_TRANSFORMER.containsKey(value)) 1375 | { 1376 | String shortenedName = TRANSFORMER_TO_NAME.get(NAME_TO_TRANSFORMER.get(value)); 1377 | transformerSelected.addElement(new TransformerWithConfig(shortenedName)); 1378 | } 1379 | } 1380 | } 1381 | } 1382 | } 1383 | 1384 | private static String createConfig(List fields, DefaultListModel transformerSelected) 1385 | { 1386 | StringBuilder builder = new StringBuilder(); 1387 | builder.append("java -jar deobfuscator.jar"); 1388 | for (ConfigItem i : fields) 1389 | { 1390 | if (i.type != ItemType.FILE) 1391 | continue; 1392 | if (((String) i.getValue()).split(" ").length > 1) 1393 | builder.append(" -").append(i.getFieldName()).append(" ").append("\"").append(i.getValue()).append("\""); 1394 | else if (!((String) i.getValue()).isEmpty()) 1395 | builder.append(" -").append(i.getFieldName()).append(" ").append(i.getValue()); 1396 | else 1397 | builder.append(" -").append(i.getFieldName()).append(" \"\""); 1398 | } 1399 | for (Object o : transformerSelected.toArray()) 1400 | { 1401 | TransformerWithConfig transformer = (TransformerWithConfig) o; 1402 | builder.append(" -transformer ").append(transformer.toExportString()); 1403 | } 1404 | for (ConfigItem i : fields) 1405 | { 1406 | if (i.type != ItemType.FILELIST && i.type != ItemType.STRINGLIST) 1407 | continue; 1408 | for (Object o : (List) i.getValue()) 1409 | { 1410 | if (((String) o).split(" ").length > 1) 1411 | builder.append(" -").append(i.getFieldName()).append(" ").append("\"").append(o).append("\""); 1412 | else if (!((String) o).isEmpty()) 1413 | builder.append(" -").append(i.getFieldName()).append(" ").append(o); 1414 | else 1415 | builder.append(" -").append(i.getFieldName()).append(" \"\""); 1416 | } 1417 | } 1418 | for (ConfigItem i : fields) 1419 | { 1420 | if (i.type != ItemType.BOOLEAN) 1421 | continue; 1422 | if ((Boolean) i.getValue()) 1423 | builder.append(" -").append(i.getFieldName()); 1424 | } 1425 | return builder.toString(); 1426 | } 1427 | 1428 | private static List splitQuoteAware(String args1, char splitChar) 1429 | { 1430 | if (args1 == null) 1431 | { 1432 | return new ArrayList<>(); 1433 | } 1434 | List split = new ArrayList<>(); 1435 | { 1436 | int start = 0; 1437 | boolean inQuotes = false; 1438 | for (int current = 0; current < args1.length(); current++) 1439 | { 1440 | if (args1.charAt(current) == '"') 1441 | { 1442 | inQuotes = !inQuotes; 1443 | } else if (args1.charAt(current) == splitChar && !inQuotes) 1444 | { 1445 | String str = args1.substring(start, current); 1446 | split.add(str.trim()); 1447 | start = current + 1; 1448 | } 1449 | } 1450 | split.add(args1.substring(start)); 1451 | } 1452 | return split; 1453 | } 1454 | 1455 | private static void loadWrappers() 1456 | { 1457 | try 1458 | { 1459 | WrapperFactory.setupJarLoader(false); 1460 | deob = WrapperFactory.getDeobfuscator(); 1461 | trans = WrapperFactory.getTransformers(); 1462 | config = deob.getConfig(); 1463 | transformers = trans.getTransformers(); 1464 | for (Class clazz : transformers) 1465 | { 1466 | TRANSFORMER_TO_NAME.put(clazz, toShortName(clazz)); 1467 | NAME_TO_TRANSFORMER.put(toShortName(clazz), clazz); 1468 | NAME_TO_TRANSFORMER.put(toShortNameLegacy(clazz), clazz); 1469 | } 1470 | } catch (FallbackException e) 1471 | { 1472 | config = null; 1473 | transformers = null; 1474 | fallbackLoad(e.path); 1475 | } 1476 | } 1477 | 1478 | private static void fallbackLoad(String path) 1479 | { 1480 | try 1481 | { 1482 | File file = new File(path); 1483 | if (!file.exists()) 1484 | throw new FallbackException("Loading error", "Path specified does not exist.", null); 1485 | try 1486 | { 1487 | WrapperFactory.setupJarLoader(file); 1488 | } catch (IOException e) 1489 | { 1490 | throw new FallbackException("Loading error", "IOException while reading file.", e); 1491 | } catch (InvalidJarException e) 1492 | { 1493 | throw new FallbackException("Loading error", "Invaild JAR selected. Note that old versions of deobfuscator are not supported!", e); 1494 | } 1495 | deob = WrapperFactory.getDeobfuscator(); 1496 | trans = WrapperFactory.getTransformers(); 1497 | config = deob.getConfig(); 1498 | transformers = trans.getTransformers(); 1499 | for (Class clazz : transformers) 1500 | { 1501 | TRANSFORMER_TO_NAME.put(clazz, toShortName(clazz)); 1502 | NAME_TO_TRANSFORMER.put(toShortName(clazz), clazz); 1503 | NAME_TO_TRANSFORMER.put(toShortNameLegacy(clazz), clazz); 1504 | } 1505 | } catch (FallbackException e) 1506 | { 1507 | config = null; 1508 | transformers = null; 1509 | fallbackLoad(e.path); 1510 | } 1511 | } 1512 | 1513 | private static String toShortName(Class clazz) 1514 | { 1515 | return clazz.getName().replace("com.javadeobfuscator.deobfuscator.transformers.", "") 1516 | .replace("Transformer", "") 1517 | .replace("general.peephole.", "peephole.") 1518 | .replace("general.removers.", "removers."); 1519 | } 1520 | 1521 | private static String toShortNameLegacy(Class clazz) 1522 | { 1523 | return clazz.getName().replace("com.javadeobfuscator.deobfuscator.transformers.", ""); 1524 | } 1525 | 1526 | private static class DeobfuscatorOutputStream extends OutputStream 1527 | { 1528 | 1529 | private final PrintStream sysOut; 1530 | private final JTextArea console; 1531 | 1532 | public DeobfuscatorOutputStream(PrintStream sysOut, JTextArea console) 1533 | { 1534 | this.console = console; 1535 | this.sysOut = sysOut; 1536 | } 1537 | 1538 | @Override 1539 | public void write(int b) throws IOException 1540 | { 1541 | sysOut.write(b); 1542 | console.append(String.valueOf((char) b)); 1543 | if (shouldLimitLines.isSelected() && console.getLineCount() > 100) 1544 | { 1545 | try 1546 | { 1547 | console.replaceRange("", 0, console.getLineEndOffset(0)); 1548 | } catch (Exception e) 1549 | { 1550 | e.printStackTrace(sysOut); 1551 | } 1552 | } 1553 | } 1554 | } 1555 | } 1556 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/TransformerSpecificConfigDialog.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui; 2 | 3 | import java.awt.Container; 4 | import java.awt.GridBagConstraints; 5 | import java.io.File; 6 | import java.lang.reflect.Field; 7 | import java.util.Set; 8 | import java.util.function.Predicate; 9 | 10 | import javax.swing.InputVerifier; 11 | import javax.swing.JButton; 12 | import javax.swing.JCheckBox; 13 | import javax.swing.JComboBox; 14 | import javax.swing.JComponent; 15 | import javax.swing.JDialog; 16 | import javax.swing.JLabel; 17 | import javax.swing.JTextField; 18 | import javax.swing.event.DocumentEvent; 19 | import javax.swing.event.DocumentListener; 20 | 21 | import com.javadeobfuscator.deobfuscator.ui.component.SynchronousJFXFileChooser; 22 | import com.javadeobfuscator.deobfuscator.ui.util.SwingUtil; 23 | import com.javadeobfuscator.deobfuscator.ui.util.TransformerConfigUtil; 24 | import javafx.stage.FileChooser; 25 | 26 | public class TransformerSpecificConfigDialog 27 | { 28 | private TransformerSpecificConfigDialog() 29 | { 30 | throw new UnsupportedOperationException(); 31 | } 32 | 33 | public static class TypeMeta { 34 | public int count; 35 | public boolean wideWindow; 36 | 37 | public TypeMeta(int count, boolean wideWindow) 38 | { 39 | this.count = count; 40 | this.wideWindow = wideWindow; 41 | } 42 | } 43 | 44 | public static TypeMeta getTypeMeta(TransformerWithConfig tconfig) { 45 | int count = 0; 46 | boolean wideWindow = false; 47 | Set fields = TransformerConfigUtil.getTransformerConfigFieldsWithSuperclass(tconfig.getConfig().getClass()); 48 | 49 | for (Field field : fields) 50 | { 51 | Class t = field.getType(); 52 | if (t.isEnum() || t == boolean.class || t == Boolean.class || t == byte.class 53 | || t == Byte.class || t == short.class || t == Short.class || t == int.class || t == Integer.class || t == long.class || t == Long.class 54 | || t == float.class || t == Float.class || t == double.class || t == Double.class) { 55 | count++; 56 | } else if (t == File.class || t == String.class || t == CharSequence.class) { 57 | count++; 58 | wideWindow = true; 59 | } 60 | } 61 | return new TypeMeta(count, wideWindow); 62 | } 63 | 64 | public static void fill(JDialog jd, TransformerWithConfig tconfig) 65 | { 66 | try 67 | { 68 | Container root = jd.getContentPane(); 69 | Object config = tconfig.getConfig(); 70 | Set fields = TransformerConfigUtil.getTransformerConfigFieldsWithSuperclass(config.getClass()); 71 | int gridY = 0; 72 | for (Field field : fields) 73 | { 74 | field.setAccessible(true); 75 | Class fType = field.getType(); 76 | if (fType == String.class || fType == CharSequence.class) 77 | { 78 | JLabel label = new JLabel(field.getName() + ':'); 79 | SwingUtil.registerGBC(root, label, 0, gridY); 80 | JTextField textField = new JTextField(1); 81 | SwingUtil.registerGBC(root, textField, 1, gridY, 0, 1); 82 | label.setLabelFor(textField); 83 | textField.getDocument().addDocumentListener(new DocumentListener() 84 | { 85 | @Override 86 | public void insertUpdate(DocumentEvent e) 87 | { 88 | changedUpdate(e); 89 | } 90 | 91 | @Override 92 | public void removeUpdate(DocumentEvent e) 93 | { 94 | changedUpdate(e); 95 | } 96 | 97 | @Override 98 | public void changedUpdate(DocumentEvent e) 99 | { 100 | try 101 | { 102 | field.set(config, TransformerConfigUtil.convertToObj(fType, textField.getText())); 103 | } catch (IllegalAccessException illegalAccessException) 104 | { 105 | illegalAccessException.printStackTrace(); 106 | } 107 | } 108 | }); 109 | } else if (fType == File.class) 110 | { 111 | JLabel label = new JLabel(field.getName()); 112 | SwingUtil.registerGBC(root, label, 0, gridY); 113 | 114 | File currFile = (File) field.get(config); 115 | String path = currFile == null ? "" : currFile.getAbsolutePath(); 116 | JTextField textField = new JTextField(path); 117 | SwingUtil.registerGBC(root, textField, 1, gridY, gbc -> 118 | { 119 | gbc.weightx = 1; 120 | gbc.fill = GridBagConstraints.HORIZONTAL; 121 | }); 122 | label.setLabelFor(textField); 123 | textField.getDocument().addDocumentListener(new DocumentListener() 124 | { 125 | @Override 126 | public void insertUpdate(DocumentEvent e) 127 | { 128 | changedUpdate(e); 129 | } 130 | 131 | @Override 132 | public void removeUpdate(DocumentEvent e) 133 | { 134 | changedUpdate(e); 135 | } 136 | 137 | @Override 138 | public void changedUpdate(DocumentEvent e) 139 | { 140 | try 141 | { 142 | field.set(config, TransformerConfigUtil.convertToObj(fType, textField.getText())); 143 | } catch (IllegalAccessException ex) 144 | { 145 | ex.printStackTrace(); 146 | } 147 | } 148 | }); 149 | 150 | JButton fileChooseButton = new JButton("Select"); 151 | SwingUtil.registerGBC(root, fileChooseButton, 2, gridY); 152 | fileChooseButton.addActionListener(e -> 153 | { 154 | SynchronousJFXFileChooser chooser = new SynchronousJFXFileChooser(() -> 155 | { 156 | FileChooser ch = new FileChooser(); 157 | ch.setTitle("Select " + field.getName()); 158 | ch.getExtensionFilters().addAll( 159 | new FileChooser.ExtensionFilter("Jar and Zip files", "*.jar", "*.zip"), 160 | new FileChooser.ExtensionFilter("Jar files", "*.jar"), 161 | new FileChooser.ExtensionFilter("Zip files", "*.zip"), 162 | new FileChooser.ExtensionFilter("Intermediary mapping", "mappings.tiny", "*.tiny"), 163 | new FileChooser.ExtensionFilter("SRG mapping", "*.srg", "*.tsrg"), 164 | new FileChooser.ExtensionFilter("CSV mapping", "*.csv"), 165 | new FileChooser.ExtensionFilter("All Files", "*.*")); 166 | try 167 | { 168 | File prev = (File) field.get(config); 169 | if (prev != null) 170 | { 171 | if (prev.exists()) 172 | ch.setInitialFileName(prev.getName()); 173 | if (prev.getParentFile().exists()) 174 | ch.setInitialDirectory(prev.getParentFile()); 175 | } 176 | } catch (IllegalAccessException ex) 177 | { 178 | ex.printStackTrace(); 179 | } 180 | return ch; 181 | }); 182 | File file = chooser.showOpenDialog(); 183 | if (file != null) 184 | { 185 | try 186 | { 187 | textField.setText(file.getAbsolutePath()); 188 | field.set(config, file); 189 | } catch (IllegalAccessException ex) 190 | { 191 | ex.printStackTrace(); 192 | } 193 | } 194 | }); 195 | } else if (fType == boolean.class || fType == Boolean.class) 196 | { 197 | JCheckBox checkBox = new JCheckBox(field.getName(), (Boolean) field.get(config)); 198 | SwingUtil.registerGBC(root, checkBox, 0, gridY, 0, 1); 199 | checkBox.addActionListener(e -> 200 | { 201 | try 202 | { 203 | field.set(config, checkBox.isSelected()); 204 | } catch (IllegalAccessException ex) 205 | { 206 | ex.printStackTrace(); 207 | } 208 | }); 209 | } else if (fType == byte.class || fType == Byte.class) 210 | { 211 | numberInput(root, config, gridY, field, fType, numberValidator(Byte::parseByte)); 212 | } else if (fType == short.class || fType == Short.class) 213 | { 214 | numberInput(root, config, gridY, field, fType, numberValidator(Short::parseShort)); 215 | } else if (fType == int.class || fType == Integer.class) 216 | { 217 | numberInput(root, config, gridY, field, fType, numberValidator(Integer::parseInt)); 218 | } else if (fType == long.class || fType == Long.class) 219 | { 220 | numberInput(root, config, gridY, field, fType, numberValidator(Long::parseLong)); 221 | } else if (fType == float.class || fType == Float.class) 222 | { 223 | numberInput(root, config, gridY, field, fType, numberValidator(Float::parseFloat)); 224 | } else if (fType == double.class || fType == Double.class) 225 | { 226 | numberInput(root, config, gridY, field, fType, numberValidator(Double::parseDouble)); 227 | } else if (fType.isEnum()) 228 | { 229 | JLabel label = new JLabel(field.getName() + ':'); 230 | SwingUtil.registerGBC(root, label, 0, gridY); 231 | JComboBox comboBox = new JComboBox<>(fType.getEnumConstants()); 232 | SwingUtil.registerGBC(root, comboBox, 1, gridY, 0, 1); 233 | label.setLabelFor(comboBox); 234 | comboBox.setEditable(false); 235 | comboBox.setSelectedItem(field.get(config)); 236 | comboBox.addActionListener(e -> { 237 | try 238 | { 239 | field.set(config, comboBox.getSelectedItem()); 240 | } catch (IllegalAccessException ex) 241 | { 242 | ex.printStackTrace(); 243 | } 244 | }); 245 | } else { 246 | System.out.println("Unknown config field type " + fType + " " + field.getName() + " in transformer " + tconfig.getShortName()); 247 | } 248 | ++gridY; 249 | } 250 | } catch (IllegalAccessException ex) 251 | { 252 | ex.printStackTrace(); 253 | } 254 | } 255 | 256 | public interface ThrowableFunction 257 | { 258 | R apply(I i) throws T; 259 | } 260 | 261 | private static Predicate numberValidator(ThrowableFunction fun) 262 | { 263 | return s -> 264 | { 265 | try 266 | { 267 | fun.apply(s); 268 | return true; 269 | } catch (NumberFormatException t) 270 | { 271 | return false; 272 | } 273 | }; 274 | } 275 | 276 | private static void numberInput(Container root, Object config, int gridY, Field field, Class fType, Predicate verifier) 277 | { 278 | JLabel label = new JLabel(field.getName()); 279 | SwingUtil.registerGBC(root, label, 0, gridY); 280 | 281 | JTextField textField = new JTextField(1); 282 | SwingUtil.registerGBC(root, textField, 1, gridY, 0, 1); 283 | textField.setInputVerifier(new InputVerifier() 284 | { 285 | @Override 286 | public boolean verify(JComponent input) 287 | { 288 | return verifier.test(textField.getText()); 289 | } 290 | }); 291 | label.setLabelFor(textField); 292 | textField.getDocument().addDocumentListener(new DocumentListener() 293 | { 294 | @Override 295 | public void insertUpdate(DocumentEvent e) 296 | { 297 | changedUpdate(e); 298 | } 299 | 300 | @Override 301 | public void removeUpdate(DocumentEvent e) 302 | { 303 | changedUpdate(e); 304 | } 305 | 306 | @Override 307 | public void changedUpdate(DocumentEvent e) 308 | { 309 | String text = textField.getText(); 310 | if (!verifier.test(text)) 311 | { 312 | return; 313 | } 314 | try 315 | { 316 | field.set(config, TransformerConfigUtil.convertToObj(fType, text)); 317 | } catch (IllegalAccessException ex) 318 | { 319 | ex.printStackTrace(); 320 | } 321 | } 322 | }); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/TransformerWithConfig.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.Field; 5 | import java.util.Set; 6 | 7 | import com.javadeobfuscator.deobfuscator.ui.util.TransformerConfigUtil; 8 | 9 | public class TransformerWithConfig 10 | { 11 | private final String shortName; 12 | private Object config; 13 | 14 | public TransformerWithConfig(String shortName) 15 | { 16 | this.shortName = shortName; 17 | } 18 | 19 | public TransformerWithConfig(String shortName, Object config) 20 | { 21 | this.shortName = shortName; 22 | this.config = config; 23 | } 24 | 25 | public String getShortName() 26 | { 27 | return shortName; 28 | } 29 | 30 | public Object getConfig() 31 | { 32 | return config; 33 | } 34 | 35 | public void setConfig(Object config) 36 | { 37 | this.config = config; 38 | } 39 | 40 | public String toExportString() 41 | { 42 | if (config == null) 43 | { 44 | return this.shortName; 45 | } 46 | Set fields = TransformerConfigUtil.getTransformerConfigFieldsWithSuperclass(config.getClass()); 47 | StringBuilder sb = new StringBuilder(this.shortName); 48 | for (Field field : fields) 49 | { 50 | field.setAccessible(true); 51 | try 52 | { 53 | Object val = field.get(config); 54 | if (val == null) 55 | { 56 | continue; 57 | } 58 | sb.append(':').append(field.getName()).append('='); 59 | if (val instanceof File) 60 | { 61 | sb.append('"').append(((File) val).getAbsolutePath()).append('"'); 62 | } else if (val instanceof Enum) 63 | { 64 | sb.append(((Enum) val).name()); 65 | } else 66 | { 67 | sb.append(val); 68 | } 69 | } catch (IllegalAccessException e) 70 | { 71 | e.printStackTrace(); 72 | } 73 | } 74 | return sb.toString(); 75 | } 76 | 77 | @Override 78 | public String toString() 79 | { 80 | return this.shortName; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/component/SwingConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.component; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.ParameterizedType; 6 | import java.lang.reflect.Type; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | import javax.swing.DefaultListModel; 14 | import javax.swing.JCheckBox; 15 | import javax.swing.JTextField; 16 | 17 | import com.javadeobfuscator.deobfuscator.ui.util.Reflect; 18 | 19 | public class SwingConfiguration 20 | { 21 | /** 22 | * Ignored field names. 23 | */ 24 | private final static Set IGNORED_VALUES = new HashSet<>(); 25 | 26 | static 27 | { 28 | IGNORED_VALUES.add("transformers"); 29 | } 30 | 31 | public List fieldsList; 32 | 33 | public SwingConfiguration(Object config) 34 | { 35 | //Fill fieldsList 36 | fieldsList = new ArrayList<>(); 37 | for (Field field : Reflect.fields(config.getClass())) 38 | { 39 | if (IGNORED_VALUES.contains(field.getName())) 40 | continue; 41 | fieldsList.add(new ConfigItem(config, field)); 42 | } 43 | } 44 | 45 | public static class ConfigItem 46 | { 47 | private final Field field; 48 | public final ItemType type; 49 | private Object instance; 50 | 51 | /** 52 | * The swing component that we'll use to call the set value 53 | */ 54 | public Object component; 55 | 56 | public ConfigItem(Object instance, Field field) 57 | { 58 | this.instance = instance; 59 | this.field = field; 60 | if (field.getType().equals(File.class)) 61 | type = ItemType.FILE; 62 | else if (field.getType().equals(boolean.class)) 63 | type = ItemType.BOOLEAN; 64 | else if (field.getType().equals(List.class)) 65 | { 66 | Type[] args = ((ParameterizedType) field.getGenericType()).getActualTypeArguments(); 67 | if (args.length > 0 && args[0].getTypeName().contains("File")) 68 | type = ItemType.FILELIST; 69 | else 70 | type = ItemType.STRINGLIST; 71 | } else 72 | type = null; 73 | } 74 | 75 | public String getFieldName() 76 | { 77 | return field.getName(); 78 | } 79 | 80 | public String getDisplayName() 81 | { 82 | String name = field.getName(); 83 | return name.substring(0, 1).toUpperCase() + name.substring(1); 84 | } 85 | 86 | /** 87 | * Gets the component value (used in save config) Returns string if file and list if defaultlistmodel. 88 | */ 89 | public Object getValue() 90 | { 91 | if (type == ItemType.FILE) 92 | return ((JTextField) component).getText(); 93 | if (type == ItemType.BOOLEAN) 94 | return ((JCheckBox) component).isSelected(); 95 | return Arrays.asList(((DefaultListModel) component).toArray()); 96 | } 97 | 98 | /** 99 | * Sets the component value. Must be either a string, boolean, or DefaultListModel of String. 100 | */ 101 | public void setValue(Object o) 102 | { 103 | if (type == ItemType.FILE) 104 | ((JTextField) component).setText((String) o); 105 | else if (type == ItemType.BOOLEAN) 106 | ((JCheckBox) component).setSelected((Boolean) o); 107 | else 108 | component = o; 109 | } 110 | 111 | /** 112 | * Clears the component value. 113 | */ 114 | public void clearValue() 115 | { 116 | if (type == ItemType.FILE) 117 | ((JTextField) component).setText(""); 118 | else if (type == ItemType.BOOLEAN) 119 | ((JCheckBox) component).setSelected(false); 120 | else 121 | { 122 | DefaultListModel listModel = (DefaultListModel) component; 123 | listModel.clear(); 124 | } 125 | } 126 | 127 | /** 128 | * Sets the field value with the component value. Used when run deobfuscator is clicked. 129 | */ 130 | public void setFieldValue() 131 | { 132 | Object o = getValue(); 133 | if (type == ItemType.FILE) 134 | o = new File((String) o); 135 | else if (type == ItemType.FILELIST) 136 | { 137 | List files = new ArrayList<>(); 138 | for (Object obj : (List) o) 139 | { 140 | files.add(new File((String) obj)); 141 | } 142 | o = files; 143 | } 144 | try 145 | { 146 | Reflect.setFieldO(instance, field.getName(), o); 147 | } catch (Exception e) 148 | { 149 | } 150 | } 151 | 152 | /** 153 | * Clears the field value. 154 | */ 155 | public void clearFieldValue() 156 | { 157 | try 158 | { 159 | if (type == ItemType.FILE) 160 | Reflect.setFieldO(instance, field.getName(), null); 161 | else if (type == ItemType.BOOLEAN) 162 | Reflect.setFieldO(instance, field.getName(), false); 163 | else 164 | Reflect.setFieldO(instance, field.getName(), new ArrayList<>()); 165 | } catch (Exception e) 166 | { 167 | } 168 | } 169 | } 170 | 171 | public enum ItemType 172 | { 173 | FILE, 174 | BOOLEAN, 175 | FILELIST, 176 | STRINGLIST; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/component/SynchronousJFXCaller.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.component; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.CountDownLatch; 5 | import java.util.concurrent.ExecutionException; 6 | import java.util.concurrent.FutureTask; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | 10 | import javax.swing.JDialog; 11 | import javax.swing.SwingUtilities; 12 | import javax.swing.WindowConstants; 13 | 14 | import javafx.application.Platform; 15 | import javafx.embed.swing.JFXPanel; 16 | 17 | /** 18 | * A utility class to execute a Callable synchronously on the JavaFX event thread. 19 | * 20 | * Source 21 | * 22 | * @param the return type of the callable 23 | */ 24 | public class SynchronousJFXCaller 25 | { 26 | private static volatile boolean initialized = false; 27 | 28 | public static synchronized void init() 29 | { 30 | if (initialized) 31 | { 32 | return; 33 | } 34 | new JFXPanel(); 35 | Platform.setImplicitExit(false); 36 | initialized = true; 37 | } 38 | 39 | private final Callable callable; 40 | 41 | /** 42 | * Constructs a new caller that will execute the provided callable. 43 | * 44 | * The callable is accessed from the JavaFX event thread, so it should either be immutable or at least its state shouldn't be changed randomly while the call() 45 | * method is in progress. 46 | * 47 | * @param callable the action to execute on the JFX event thread 48 | */ 49 | public SynchronousJFXCaller(Callable callable) 50 | { 51 | this.callable = callable; 52 | } 53 | 54 | /** 55 | * Executes the Callable. 56 | *

57 | * A specialized task is run using Platform.runLater(). The calling thread then waits first for the task to start, then for it to return a result. Any exception 58 | * thrown by the Callable will be rethrown in the calling thread. 59 | *

60 | * 61 | * @param startTimeout time to wait for Platform.runLater() to start the dialog-showing task 62 | * @param startTimeoutUnit the time unit of the startTimeout argument 63 | * @return whatever the Callable returns 64 | * @throws IllegalStateException if Platform.runLater() fails to start the task within the given timeout 65 | * @throws InterruptedException if the calling (this) thread is interrupted while waiting for the task to start or to get its result (note that the task will 66 | * still run anyway and its result will be ignored) 67 | */ 68 | public T call(long startTimeout, TimeUnit startTimeoutUnit) 69 | throws Exception 70 | { 71 | final CountDownLatch taskStarted = new CountDownLatch(1); 72 | // Can't use volatile boolean here because only finals can be accessed 73 | // from closures like the lambda expression below. 74 | final AtomicBoolean taskCancelled = new AtomicBoolean(false); 75 | // a trick to emulate modality: 76 | final JDialog modalBlocker = new JDialog(); 77 | modalBlocker.setModal(true); 78 | modalBlocker.setUndecorated(true); 79 | modalBlocker.setSize(0, 0); 80 | modalBlocker.setOpacity(0.0f); 81 | modalBlocker.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 82 | final CountDownLatch modalityLatch = new CountDownLatch(1); 83 | final FutureTask task = new FutureTask<>(() -> 84 | { 85 | synchronized (taskStarted) 86 | { 87 | if (taskCancelled.get()) 88 | { 89 | return null; 90 | } else 91 | { 92 | taskStarted.countDown(); 93 | } 94 | } 95 | try 96 | { 97 | return callable.call(); 98 | } finally 99 | { 100 | // Wait until the Swing thread is blocked in setVisible(): 101 | modalityLatch.await(); 102 | // and unblock it: 103 | SwingUtilities.invokeLater(modalBlocker::dispose); 104 | } 105 | }); 106 | Platform.runLater(task); 107 | if (!taskStarted.await(startTimeout, startTimeoutUnit)) 108 | { 109 | synchronized (taskStarted) 110 | { 111 | // the last chance, it could have been started just now 112 | if (!taskStarted.await(0, TimeUnit.MILLISECONDS)) 113 | { 114 | // Can't use task.cancel() here because it would 115 | // interrupt the JavaFX thread, which we don't own. 116 | taskCancelled.set(true); 117 | throw new IllegalStateException("JavaFX was shut down or is unresponsive"); 118 | } 119 | } 120 | } 121 | // a trick to notify the task AFTER we have been blocked 122 | // in setVisible() 123 | SwingUtilities.invokeLater(() -> 124 | { 125 | // notify that we are ready to get the result: 126 | modalityLatch.countDown(); 127 | }); 128 | modalBlocker.setVisible(true); // blocks 129 | modalBlocker.dispose(); // release resources 130 | try 131 | { 132 | return task.get(); 133 | } catch (ExecutionException ex) 134 | { 135 | Throwable ec = ex.getCause(); 136 | if (ec instanceof Exception) 137 | { 138 | throw (Exception) ec; 139 | } else if (ec instanceof Error) 140 | { 141 | throw (Error) ec; 142 | } else 143 | { 144 | throw new AssertionError("Unexpected exception type", ec); 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/component/SynchronousJFXFileChooser.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.component; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import java.util.concurrent.Callable; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.function.Function; 8 | import java.util.function.Supplier; 9 | 10 | import javafx.stage.FileChooser; 11 | 12 | /** 13 | * A utility class that summons JavaFX FileChooser from the Swing EDT. (Or anywhere else for that matter.) JavaFX should be initialized prior to using this class (e. 14 | * g. by creating a JFXPanel instance). It is also recommended to call Platform.setImplicitExit(false) after initialization to ensure that JavaFX platform keeps 15 | * running. Don't forget to call Platform.exit() when shutting down the application, to ensure that the JavaFX threads don't prevent JVM exit. 16 | * 17 | * Source 18 | */ 19 | public class SynchronousJFXFileChooser 20 | { 21 | private final Supplier fileChooserFactory; 22 | 23 | /** 24 | * Constructs a new file chooser that will use the provided factory. 25 | * 26 | * The factory is accessed from the JavaFX event thread, so it should either be immutable or at least its state shouldn't be changed randomly while one of the 27 | * dialog-showing method calls is in progress. 28 | * 29 | * The factory should create and set up the chooser, for example, by setting extension filters. If there is no need to perform custom initialization of the 30 | * chooser, FileChooser::new could be passed as a factory. 31 | * 32 | * Alternatively, the method parameter supplied to the showDialog() function can be used to provide custom initialization. 33 | * 34 | * @param fileChooserFactory the function used to construct new choosers 35 | */ 36 | public SynchronousJFXFileChooser(Supplier fileChooserFactory) 37 | { 38 | this.fileChooserFactory = fileChooserFactory; 39 | } 40 | 41 | /** 42 | * Shows the FileChooser dialog by calling the provided method. 43 | * 44 | * Waits for one second for the dialog-showing task to start in the JavaFX event thread, then throws an IllegalStateException if it didn't start. 45 | * 46 | * @param the return type of the method, usually File or List<File> 47 | * @param method a function calling one of the dialog-showing methods 48 | * @return whatever the method returns 49 | * @see #showDialog(java.util.function.Function, long, java.util.concurrent.TimeUnit) 50 | */ 51 | public T showDialog(Function method) 52 | { 53 | return showDialog(method, 1, TimeUnit.SECONDS); 54 | } 55 | 56 | /** 57 | * Shows the FileChooser dialog by calling the provided method. The dialog is created by the factory supplied to the constructor, then it is shown by calling 58 | * the 59 | * provided method on it, then the result is returned. 60 | *

61 | * Everything happens in the right threads thanks to {@link SynchronousJFXCaller}. The task performed in the JavaFX thread consists of two steps: construct a 62 | * chooser using the provided factory and invoke the provided method on it. Any exception thrown during these steps will be rethrown in the calling thread, 63 | * which 64 | * shouldn't normally happen unless the factory throws an unchecked exception. 65 | *

66 | *

67 | * If the calling thread is interrupted during either the wait for the task to start or for its result, then null is returned and the Thread interrupted status 68 | * is set. 69 | *

70 | * 71 | * @param return type (usually File or List<File>) 72 | * @param method a function that calls the desired FileChooser method 73 | * @param timeout time to wait for Platform.runLater() to start the dialog-showing task (once started, it is allowed to run as long as needed) 74 | * @param unit the time unit of the timeout argument 75 | * @return whatever the method returns 76 | * @throws IllegalStateException if Platform.runLater() fails to start the dialog-showing task within the given timeout 77 | */ 78 | public T showDialog(Function method, 79 | long timeout, TimeUnit unit) 80 | { 81 | Callable task = () -> 82 | { 83 | FileChooser chooser = fileChooserFactory.get(); 84 | return method.apply(chooser); 85 | }; 86 | SynchronousJFXCaller caller = new SynchronousJFXCaller<>(task); 87 | try 88 | { 89 | return caller.call(timeout, unit); 90 | } catch (RuntimeException | Error ex) 91 | { 92 | throw ex; 93 | } catch (InterruptedException ex) 94 | { 95 | Thread.currentThread().interrupt(); 96 | return null; 97 | } catch (Exception ex) 98 | { 99 | throw new AssertionError("Got unexpected checked exception from SynchronousJFXCaller.call()", ex); 100 | } 101 | } 102 | 103 | /** 104 | * Shows a FileChooser using FileChooser.showOpenDialog(). 105 | * 106 | * @return the return value of FileChooser.showOpenDialog() 107 | * @see #showDialog(java.util.function.Function, long, java.util.concurrent.TimeUnit) 108 | */ 109 | public File showOpenDialog() 110 | { 111 | return showDialog(chooser -> chooser.showOpenDialog(null)); 112 | } 113 | 114 | /** 115 | * Shows a FileChooser using FileChooser.showSaveDialog(). 116 | * 117 | * @return the return value of FileChooser.showSaveDialog() 118 | * @see #showDialog(java.util.function.Function, long, java.util.concurrent.TimeUnit) 119 | */ 120 | public File showSaveDialog() 121 | { 122 | return showDialog(chooser -> chooser.showSaveDialog(null)); 123 | } 124 | 125 | /** 126 | * Shows a FileChooser using FileChooser.showOpenMultipleDialog(). 127 | * 128 | * @return the return value of FileChooser.showOpenMultipleDialog() 129 | * @see #showDialog(java.util.function.Function, long, java.util.concurrent.TimeUnit) 130 | */ 131 | public List showOpenMultipleDialog() 132 | { 133 | return showDialog(chooser -> chooser.showOpenMultipleDialog(null)); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/component/WrapLayout.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.component; 2 | 3 | import java.awt.Component; 4 | import java.awt.Container; 5 | import java.awt.Dimension; 6 | import java.awt.FlowLayout; 7 | import java.awt.Insets; 8 | 9 | import javax.swing.JScrollPane; 10 | import javax.swing.SwingUtilities; 11 | 12 | /** 13 | * FlowLayout subclass that fully supports wrapping of components. 14 | */ 15 | public class WrapLayout extends FlowLayout 16 | { 17 | private Dimension preferredLayoutSize; 18 | 19 | /** 20 | * Constructs a new WrapLayout with a left alignment and a default 5-unit horizontal and vertical gap. 21 | */ 22 | public WrapLayout() 23 | { 24 | super(); 25 | } 26 | 27 | /** 28 | * Constructs a new FlowLayout with the specified alignment and a default 5-unit horizontal and vertical gap. The value of the alignment argument 29 | * must be one of 30 | * WrapLayout, WrapLayout, 31 | * or WrapLayout. 32 | * 33 | * @param align the alignment value 34 | */ 35 | public WrapLayout(int align) 36 | { 37 | super(align); 38 | } 39 | 40 | /** 41 | * Creates a new flow layout manager with the indicated alignment and the indicated horizontal and vertical gaps. 42 | *

43 | * The value of the alignment argument must be one of 44 | * WrapLayout, WrapLayout, 45 | * or WrapLayout. 46 | * 47 | * @param align the alignment value 48 | * @param hgap the horizontal gap between components 49 | * @param vgap the vertical gap between components 50 | */ 51 | public WrapLayout(int align, int hgap, int vgap) 52 | { 53 | super(align, hgap, vgap); 54 | } 55 | 56 | /** 57 | * Returns the preferred dimensions for this layout given the 58 | * visible components in the specified target container. 59 | * 60 | * @param target the component which needs to be laid out 61 | * @return the preferred dimensions to lay out the subcomponents of the specified container 62 | */ 63 | @Override 64 | public Dimension preferredLayoutSize(Container target) 65 | { 66 | return layoutSize(target, true); 67 | } 68 | 69 | /** 70 | * Returns the minimum dimensions needed to layout the visible components contained in the specified target container. 71 | * 72 | * @param target the component which needs to be laid out 73 | * @return the minimum dimensions to lay out the subcomponents of the specified container 74 | */ 75 | @Override 76 | public Dimension minimumLayoutSize(Container target) 77 | { 78 | Dimension minimum = layoutSize(target, false); 79 | minimum.width += (getHgap() * 2); 80 | return minimum; 81 | } 82 | 83 | /** 84 | * Returns the minimum or preferred dimension needed to layout the target container. 85 | * 86 | * @param target target to get layout size for 87 | * @param preferred should preferred size be calculated 88 | * @return the dimension to layout the target container 89 | */ 90 | private Dimension layoutSize(Container target, boolean preferred) 91 | { 92 | synchronized (target.getTreeLock()) 93 | { 94 | // Each row must fit with the width allocated to the containter. 95 | // When the container width = 0, the preferred width of the container 96 | // has not yet been calculated so lets ask for the maximum. 97 | 98 | Container container = target; 99 | 100 | while ((container == target || container.getSize().width == 0) && container.getParent() != null) 101 | { 102 | container = container.getParent(); 103 | } 104 | 105 | int targetWidth = container.getSize().width; 106 | 107 | if (targetWidth == 0) 108 | targetWidth = Integer.MAX_VALUE; 109 | 110 | int hgap = getHgap(); 111 | int vgap = getVgap(); 112 | Insets insets = target.getInsets(); 113 | int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); 114 | int maxWidth = targetWidth - horizontalInsetsAndGap; 115 | 116 | // Fit components into the allowed width 117 | 118 | Dimension dim = new Dimension(0, 0); 119 | int rowWidth = 0; 120 | int rowHeight = 0; 121 | 122 | int nmembers = target.getComponentCount(); 123 | 124 | for (int i = 0; i < nmembers; i++) 125 | { 126 | Component m = target.getComponent(i); 127 | 128 | if (m.isVisible()) 129 | { 130 | Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); 131 | 132 | // Can't add the component to current row. Start a new row. 133 | 134 | int compWidth = rowWidth + d.width + hgap * 2 + 2; 135 | if (compWidth > maxWidth) 136 | { 137 | addRow(dim, rowWidth, rowHeight); 138 | rowWidth = 0; 139 | rowHeight = 0; 140 | } 141 | 142 | // Add a horizontal gap 143 | rowWidth += hgap; 144 | 145 | rowWidth += d.width; 146 | rowHeight = Math.max(rowHeight, d.height); 147 | } 148 | } 149 | 150 | addRow(dim, rowWidth, rowHeight); 151 | 152 | dim.width += Math.max(horizontalInsetsAndGap, maxWidth); 153 | dim.height += insets.top + insets.bottom + vgap * 2; 154 | 155 | // When using a scroll pane or the DecoratedLookAndFeel we need to 156 | // make sure the preferred size is less than the size of the 157 | // target containter so shrinking the container size works 158 | // correctly. Removing the horizontal gap is an easy way to do this. 159 | 160 | Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); 161 | 162 | if (scrollPane != null && target.isValid()) 163 | { 164 | dim.width -= (hgap + 1); 165 | } 166 | 167 | return dim; 168 | } 169 | } 170 | 171 | /* 172 | * A new row has been completed. Use the dimensions of this row 173 | * to update the preferred size for the container. 174 | * 175 | * @param dim update the width and height when appropriate 176 | * @param rowWidth the width of the row to add 177 | * @param rowHeight the height of the row to add 178 | */ 179 | private void addRow(Dimension dim, int rowWidth, int rowHeight) 180 | { 181 | dim.width = Math.max(dim.width, rowWidth); 182 | 183 | if (dim.height > 0) 184 | { 185 | dim.height += getVgap(); 186 | } 187 | 188 | dim.height += rowHeight; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/util/ByteLoader.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.util; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | import com.javadeobfuscator.deobfuscator.ui.SwingWindow; 7 | 8 | /** 9 | * ClassLoader that can find classes via their bytecode. Allows loading of external jar files to be instrumented before loading. 10 | */ 11 | public final class ByteLoader extends ClassLoader 12 | { 13 | /** 14 | * Map of class names to their bytecode. 15 | */ 16 | private final Map classes; 17 | 18 | /** 19 | * Create the loader with the map of classes to load from. 20 | * 21 | * @param classes {@link #classes}. 22 | */ 23 | public ByteLoader(Map classes) 24 | { 25 | super(getSystemClassLoader()); 26 | SwingWindow.mainThread.setContextClassLoader(this); 27 | this.classes = classes; 28 | } 29 | 30 | @Override 31 | protected final Class findClass(String name) throws ClassNotFoundException 32 | { 33 | Class loadedClass = findLoadedClass(name); 34 | if (loadedClass != null) 35 | { 36 | return loadedClass; 37 | } 38 | 39 | byte[] bytes = classes.get(name); 40 | if (bytes == null) 41 | { 42 | throw new ClassNotFoundException(name); 43 | } 44 | return defineClass(name, bytes, 0, bytes.length); 45 | } 46 | 47 | public final Set getClassNames() 48 | { 49 | return classes.keySet(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/util/ExceptionUtil.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.util; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | 6 | import javax.swing.JOptionPane; 7 | 8 | import com.javadeobfuscator.deobfuscator.ui.SwingWindow; 9 | 10 | public class ExceptionUtil 11 | { 12 | public static void showFatalError(String message) { 13 | showFatalError(message, null); 14 | } 15 | 16 | public static void showFatalError(String message, Throwable t) 17 | { 18 | try 19 | { 20 | if (t != null) 21 | { 22 | t.printStackTrace(); 23 | message = message + "\n" + getStackTrace(t); 24 | } 25 | SwingWindow.ensureSwingLafLoaded(); 26 | JOptionPane.showMessageDialog(null, message, "Deobfuscator GUI - Error", JOptionPane.ERROR_MESSAGE); 27 | } catch (Throwable t2) 28 | { 29 | t2.printStackTrace(); 30 | } 31 | System.exit(1); 32 | throw new Error("exit", t); 33 | } 34 | 35 | public static String getStackTrace(Throwable t) 36 | { 37 | StringWriter sw = new StringWriter(); 38 | t.printStackTrace(new PrintWriter(sw)); 39 | return sw.toString(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/util/FallbackException.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.util; 2 | 3 | import java.awt.GridBagConstraints; 4 | import java.awt.GridBagLayout; 5 | import java.awt.Insets; 6 | import java.io.File; 7 | 8 | import javax.swing.JButton; 9 | import javax.swing.JFileChooser; 10 | import javax.swing.JLabel; 11 | import javax.swing.JOptionPane; 12 | import javax.swing.JPanel; 13 | import javax.swing.JTextArea; 14 | import javax.swing.JTextField; 15 | 16 | import com.javadeobfuscator.deobfuscator.ui.SwingWindow; 17 | import com.javadeobfuscator.deobfuscator.ui.component.SynchronousJFXFileChooser; 18 | import javafx.stage.FileChooser; 19 | 20 | public class FallbackException extends Exception 21 | { 22 | public String path; 23 | 24 | public FallbackException(String title, String msg, Throwable cause) 25 | { 26 | super(msg, cause); 27 | this.printStackTrace(); 28 | SwingWindow.ensureSwingLafLoaded(); 29 | SwingWindow.initJFX(); 30 | JPanel fallback = new JPanel(); 31 | fallback.setLayout(new GridBagLayout()); 32 | GridBagConstraints gbc = new GridBagConstraints(); 33 | gbc.gridx = 0; 34 | gbc.gridy = 0; 35 | gbc.gridwidth = GridBagConstraints.REMAINDER; 36 | gbc.insets = new Insets(0, 0, 4, 0); 37 | fallback.add(new JLabel(msg), gbc); 38 | if (cause != null) { 39 | gbc.gridy++; 40 | JTextArea area = new JTextArea(ExceptionUtil.getStackTrace(cause)); 41 | area.setEditable(false); 42 | fallback.add(area, gbc); 43 | } 44 | gbc.gridy++; 45 | fallback.add(new JLabel("Select deobfuscator.jar to try again:"), gbc); 46 | gbc.gridy++; 47 | JTextField textField = new JTextField(35); 48 | fallback.add(textField); 49 | gbc.gridx++; 50 | JButton button = new JButton("Select"); 51 | button.addActionListener(e -> 52 | { 53 | SynchronousJFXFileChooser chooser = new SynchronousJFXFileChooser(() -> { 54 | FileChooser ch = new FileChooser(); 55 | ch.setTitle("Select deobfuscator jar"); 56 | ch.setInitialDirectory(new File("abc").getAbsoluteFile().getParentFile()); 57 | ch.getExtensionFilters().addAll( 58 | new FileChooser.ExtensionFilter("Jar files", "*.jar"), 59 | new FileChooser.ExtensionFilter("Jar and Zip files", "*.jar", "*.zip"), 60 | new FileChooser.ExtensionFilter("Zip files", "*.zip"), 61 | new FileChooser.ExtensionFilter("All Files", "*.*")); 62 | return ch; 63 | }); 64 | File file = chooser.showOpenDialog(); 65 | if (file != null) 66 | { 67 | textField.setText(file.getAbsolutePath()); 68 | } 69 | }); 70 | GridBagConstraints gbc_Button = new GridBagConstraints(); 71 | gbc_Button.insets = new Insets(0, 2, 0, 0); 72 | fallback.add(button, gbc_Button); 73 | Object[] options = {"Ok", "Cancel"}; 74 | int result = JOptionPane.showOptionDialog(null, fallback, title, 75 | JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, 76 | null, options, null); 77 | if (result == JOptionPane.CLOSED_OPTION || result == JOptionPane.NO_OPTION) 78 | System.exit(0); 79 | path = textField.getText(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/util/InvalidJarException.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.util; 2 | 3 | /** 4 | * Exception to be thrown when a jar has been loaded for the deobfuscator 5 | * wrapper, but jar was not an instance of JavaDeobfuscator. 6 | */ 7 | public class InvalidJarException extends Exception { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/util/MiniClassReader.java: -------------------------------------------------------------------------------- 1 | // ASM: a very small and fast Java bytecode manipulation framework 2 | // Copyright (c) 2000-2011 INRIA, France Telecom 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions 7 | // are met: 8 | // 1. Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // 2. Redistributions in binary form must reproduce the above copyright 11 | // notice, this list of conditions and the following disclaimer in the 12 | // documentation and/or other materials provided with the distribution. 13 | // 3. Neither the name of the copyright holders nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | // THE POSSIBILITY OF SUCH DAMAGE. 28 | package com.javadeobfuscator.deobfuscator.ui.util; 29 | 30 | /** 31 | * Minified version of ClassReader from ASM, only allows for fetching of class name.
Minification / stripping used because the rest of the class is unused. 32 | */ 33 | public class MiniClassReader 34 | { 35 | private static final int CONSTANT_CLASS_TAG = 7; 36 | private static final int CONSTANT_FIELDREF_TAG = 9; 37 | private static final int CONSTANT_METHODREF_TAG = 10; 38 | private static final int CONSTANT_INTERFACE_METHODREF_TAG = 11; 39 | private static final int CONSTANT_STRING_TAG = 8; 40 | private static final int CONSTANT_INTEGER_TAG = 3; 41 | private static final int CONSTANT_FLOAT_TAG = 4; 42 | private static final int CONSTANT_LONG_TAG = 5; 43 | private static final int CONSTANT_DOUBLE_TAG = 6; 44 | private static final int CONSTANT_NAME_AND_TYPE_TAG = 12; 45 | private static final int CONSTANT_UTF8_TAG = 1; 46 | private static final int CONSTANT_METHOD_HANDLE_TAG = 15; 47 | private static final int CONSTANT_METHOD_TYPE_TAG = 16; 48 | private static final int CONSTANT_DYNAMIC_TAG = 17; 49 | private static final int CONSTANT_INVOKE_DYNAMIC_TAG = 18; 50 | private static final int CONSTANT_MODULE_TAG = 19; 51 | private static final int CONSTANT_PACKAGE_TAG = 20; 52 | /// ========================================================== 53 | private final byte[] b; 54 | private final int header; 55 | private int maxLen = 0; 56 | private final int[] offsets; 57 | 58 | public MiniClassReader(final byte[] buff) 59 | { 60 | this.b = buff; 61 | if (readShort(6) > 53) 62 | { 63 | throw new IllegalArgumentException("Unsupported class file major version " + readShort(6)); 64 | } 65 | int poolSize = readUnsignedShort(8); 66 | int currIndex = 1; 67 | int curOffset = 10; 68 | offsets = new int[poolSize]; 69 | while (currIndex < poolSize) 70 | { 71 | offsets[currIndex++] = curOffset + 1; 72 | int cpInfoSize; 73 | switch (buff[curOffset]) 74 | { 75 | case CONSTANT_FIELDREF_TAG: 76 | case CONSTANT_METHODREF_TAG: 77 | case CONSTANT_INTERFACE_METHODREF_TAG: 78 | case CONSTANT_INTEGER_TAG: 79 | case CONSTANT_FLOAT_TAG: 80 | case CONSTANT_NAME_AND_TYPE_TAG: 81 | case CONSTANT_INVOKE_DYNAMIC_TAG: 82 | case CONSTANT_DYNAMIC_TAG: 83 | cpInfoSize = 5; 84 | break; 85 | case CONSTANT_LONG_TAG: 86 | case CONSTANT_DOUBLE_TAG: 87 | cpInfoSize = 9; 88 | currIndex++; 89 | break; 90 | case CONSTANT_UTF8_TAG: 91 | cpInfoSize = 3 + readUnsignedShort(curOffset + 1); 92 | if (cpInfoSize > maxLen) 93 | { 94 | maxLen = cpInfoSize; 95 | } 96 | break; 97 | case CONSTANT_METHOD_HANDLE_TAG: 98 | cpInfoSize = 4; 99 | break; 100 | case CONSTANT_CLASS_TAG: 101 | case CONSTANT_STRING_TAG: 102 | case CONSTANT_METHOD_TYPE_TAG: 103 | case CONSTANT_PACKAGE_TAG: 104 | case CONSTANT_MODULE_TAG: 105 | cpInfoSize = 3; 106 | break; 107 | default: 108 | throw new IllegalArgumentException(); 109 | } 110 | curOffset += cpInfoSize; 111 | } 112 | this.header = curOffset; 113 | } 114 | 115 | public final String getClassName() 116 | { 117 | return readUTF8(offsets[readUnsignedShort(header + 2)], new char[maxLen]); 118 | } 119 | 120 | private final String readUTF8(final int offset, final char[] buf) 121 | { 122 | int poolIndex = readUnsignedShort(offset); 123 | if (offset == 0 || poolIndex == 0) 124 | { 125 | return null; 126 | } 127 | int utfOff = offsets[poolIndex]; 128 | return readUTF(utfOff + 2, readUnsignedShort(utfOff), buf); 129 | } 130 | 131 | private final String readUTF(final int off, final int len, final char[] buf) 132 | { 133 | int currOff = off; 134 | int endOff = currOff + len; 135 | int curLen = 0; 136 | while (currOff < endOff) 137 | { 138 | int cB = b[currOff++]; 139 | if ((cB & 0x80) == 0) 140 | { 141 | buf[curLen++] = (char) (cB & 0x7F); 142 | } else if ((cB & 0xE0) == 0xC0) 143 | { 144 | buf[curLen++] = (char) (((cB & 0x1F) << 6) + (b[currOff++] & 0x3F)); 145 | } else 146 | { 147 | buf[curLen++] = (char) (((cB & 0xF) << 12) + ((b[currOff++] & 0x3F) << 6) + (b[currOff++] & 0x3F)); 148 | } 149 | } 150 | return new String(buf, 0, curLen); 151 | } 152 | 153 | private final int readUnsignedShort(final int offset) 154 | { 155 | return ((b[offset] & 0xFF) << 8) | (b[offset + 1] & 0xFF); 156 | } 157 | 158 | private final short readShort(final int offset) 159 | { 160 | return (short) (((b[offset] & 0xFF) << 8) | (b[offset + 1] & 0xFF)); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/util/Reflect.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.util; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | /** 6 | * Reflection wrapper 7 | */ 8 | @SuppressWarnings("unchecked") 9 | public class Reflect 10 | { 11 | 12 | /** 13 | * Get all fields belonging to the given class. 14 | * 15 | * @param clazz Class containing fields. 16 | * @return Array of class's fields. 17 | */ 18 | public static Field[] fields(Class clazz) 19 | { 20 | return clazz.getDeclaredFields(); 21 | } 22 | 23 | /** 24 | * Get the value of the field by its name in the given object instance. 25 | * 26 | * @param instance Object instance. 27 | * @param fieldName Field name. 28 | * @return Field value. {@code null} if could not be reached. 29 | */ 30 | public static T get(Object instance, String fieldName) 31 | { 32 | try 33 | { 34 | Field field = instance.getClass().getDeclaredField(fieldName); 35 | field.setAccessible(true); 36 | return get(instance, field); 37 | } catch (NoSuchFieldException | SecurityException e) 38 | { 39 | return null; 40 | } 41 | } 42 | 43 | /** 44 | * Get the value of the field in the given object instance. 45 | * 46 | * @param instance Object instance. 47 | * @param field Field, assumed to be accessible. 48 | * @return Field value. {@code null} if could not be reached. 49 | */ 50 | public static T get(Object instance, Field field) 51 | { 52 | try 53 | { 54 | return (T) field.get(instance); 55 | } catch (Exception e) 56 | { 57 | return null; 58 | } 59 | } 60 | 61 | /** 62 | * Sets the value of the field in the given object instance. 63 | * 64 | * @param instance Object instance. 65 | * @param field Field, assumed to be accessible. 66 | * @param value Value to set. 67 | */ 68 | public static void set(Object instance, Field field, Object value) 69 | { 70 | try 71 | { 72 | field.set(instance, value); 73 | } catch (Exception e) 74 | { 75 | } 76 | } 77 | 78 | /** 79 | * Get instance field. 80 | * 81 | * @param instance 82 | * @param name 83 | * @return 84 | * @throws Exception 85 | */ 86 | public static T getFieldO(Object instance, String name) throws Exception 87 | { 88 | Field f = instance.getClass().getDeclaredField(name); 89 | f.setAccessible(true); 90 | // get 91 | return (T) f.get(instance); 92 | } 93 | 94 | /** 95 | * Get static field. 96 | * 97 | * @param clazz 98 | * @param name 99 | * @return 100 | * @throws Exception 101 | */ 102 | public static T getFieldS(Class clazz, String name) throws Exception 103 | { 104 | Field f = clazz.getDeclaredField(name); 105 | f.setAccessible(true); 106 | // get 107 | return (T) f.get(null); 108 | } 109 | 110 | /** 111 | * Set instance field. 112 | * 113 | * @param instance 114 | * @param name 115 | * @param value 116 | * @throws Exception 117 | */ 118 | public static void setFieldO(Object instance, String name, Object value) throws Exception 119 | { 120 | Field f = instance.getClass().getDeclaredField(name); 121 | f.setAccessible(true); 122 | // set 123 | f.set(instance, value); 124 | } 125 | 126 | /** 127 | * Set static field. 128 | * 129 | * @param clazz 130 | * @param name 131 | * @param value 132 | * @throws Exception 133 | */ 134 | public static void setFieldS(Class clazz, String name, Object value) throws Exception 135 | { 136 | Field f = clazz.getDeclaredField(name); 137 | f.setAccessible(true); 138 | // set 139 | f.set(null, value); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/util/SwingUtil.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.util; 2 | 3 | import java.awt.Component; 4 | import java.awt.Container; 5 | import java.awt.GridBagConstraints; 6 | import java.awt.Insets; 7 | import java.util.function.Consumer; 8 | 9 | public class SwingUtil 10 | { 11 | private SwingUtil() 12 | { 13 | throw new UnsupportedOperationException(); 14 | } 15 | 16 | public static void registerGBC(Container parent, Component component, int x, int y) 17 | { 18 | registerGBC(parent, component, x, y, null); 19 | } 20 | 21 | public static void registerGBC(Container parent, Component component, int x, int y, Consumer consumer) 22 | { 23 | registerGBC(parent, component, x, y, 1, 1, consumer); 24 | } 25 | 26 | public static void registerGBC(Container parent, Component component, int x, int y, int w, int h) 27 | { 28 | registerGBC(parent, component, x, y, w, h, null); 29 | } 30 | 31 | public static void registerGBC(Container parent, Component component, int x, int y, int w, int h, Consumer consumer) 32 | { 33 | GridBagConstraints gbc = new GridBagConstraints(); 34 | gbc.anchor = GridBagConstraints.WEST; 35 | gbc.insets = new Insets(10, 10, 10, 10); 36 | gbc.gridx = x; 37 | gbc.gridy = y; 38 | gbc.gridwidth = w; 39 | gbc.gridheight = h; 40 | if (consumer != null) 41 | { 42 | consumer.accept(gbc); 43 | } 44 | parent.add(component, gbc); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/util/TransformerConfigUtil.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.util; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.Field; 5 | import java.util.Collections; 6 | import java.util.LinkedHashSet; 7 | import java.util.Locale; 8 | import java.util.Set; 9 | 10 | import com.javadeobfuscator.deobfuscator.ui.SwingWindow; 11 | 12 | public class TransformerConfigUtil 13 | { 14 | private TransformerConfigUtil() 15 | { 16 | throw new UnsupportedOperationException(); 17 | } 18 | 19 | public static Field getTransformerConfigFieldWithSuperclass(Class cfgClazz, String fieldName) 20 | { 21 | if (cfgClazz == null || fieldName == null || cfgClazz.getName().equals("com.javadeobfuscator.deobfuscator.config.TransformerConfig")) 22 | { 23 | return null; 24 | } 25 | Field field = null; 26 | try 27 | { 28 | field = cfgClazz.getDeclaredField(fieldName); 29 | } catch (NoSuchFieldException ex) 30 | { 31 | Class superclass = cfgClazz.getSuperclass(); 32 | return getTransformerConfigFieldWithSuperclass(superclass, fieldName); 33 | } 34 | return field; 35 | } 36 | 37 | public static Set getTransformerConfigFieldsWithSuperclass(Class cfgClazz) 38 | { 39 | return getTransformerConfigFielddWithSuperclass0(cfgClazz, new LinkedHashSet<>()); 40 | } 41 | 42 | private static Set getTransformerConfigFielddWithSuperclass0(Class cfgClazz, Set result) 43 | { 44 | if (cfgClazz == null || cfgClazz.getName().equals("com.javadeobfuscator.deobfuscator.config.TransformerConfig")) 45 | { 46 | return result; 47 | } 48 | Collections.addAll(result, cfgClazz.getDeclaredFields()); 49 | return getTransformerConfigFielddWithSuperclass0(cfgClazz.getSuperclass(), result); 50 | } 51 | 52 | public static Object convertToObj(Class fType, String strVal) 53 | { 54 | Object oval = null; 55 | if (fType == String.class || fType == CharSequence.class) 56 | { 57 | oval = strVal; 58 | } else if (fType == File.class) 59 | { 60 | oval = new File(strVal); 61 | } else if (fType == boolean.class || fType == Boolean.class) 62 | { 63 | oval = Boolean.parseBoolean(strVal); 64 | } else if (fType == byte.class || fType == Byte.class) 65 | { 66 | oval = Byte.parseByte(strVal); 67 | } else if (fType == short.class || fType == Short.class) 68 | { 69 | oval = Short.parseShort(strVal); 70 | } else if (fType == int.class || fType == Integer.class) 71 | { 72 | oval = Integer.parseInt(strVal); 73 | } else if (fType == long.class || fType == Long.class) 74 | { 75 | oval = Long.parseLong(strVal); 76 | } else if (fType == float.class || fType == Float.class) 77 | { 78 | oval = Float.parseFloat(strVal); 79 | } else if (fType == double.class || fType == Double.class) 80 | { 81 | oval = Double.parseDouble(strVal); 82 | } else if (fType.isEnum()) 83 | { 84 | for (Object eObj : fType.getEnumConstants()) 85 | { 86 | Enum e = (Enum) eObj; 87 | if (e.name().toLowerCase(Locale.ROOT).equals(strVal.toLowerCase(Locale.ROOT))) 88 | { 89 | return e; 90 | } 91 | } 92 | } 93 | return oval; 94 | } 95 | 96 | public static Object getConfig(Class transformerClass) 97 | { 98 | try 99 | { 100 | Object cfg = SwingWindow.trans.getConfigFor(transformerClass); 101 | if (cfg != null && cfg.getClass().getName().equals("com.javadeobfuscator.deobfuscator.config.TransformerConfig")) 102 | { 103 | return null; 104 | } 105 | return cfg; 106 | } catch (Exception ex) 107 | { 108 | ex.printStackTrace(); 109 | } 110 | return null; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/wrap/Config.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.wrap; 2 | 3 | import java.util.List; 4 | 5 | import com.javadeobfuscator.deobfuscator.ui.util.Reflect; 6 | 7 | /** 8 | * Config wrapper that allows for easy reflection manipulation of fields. 9 | */ 10 | public class Config 11 | { 12 | 13 | private final Object instance; 14 | 15 | public Config(Object instance) 16 | { 17 | this.instance = instance; 18 | } 19 | 20 | public Object get() 21 | { 22 | return instance; 23 | } 24 | 25 | /** 26 | * Set transformers list. 27 | * 28 | * @param trans 29 | * @param transformerConfigs 30 | */ 31 | public void setTransformers(Transformers trans, List transformerConfigs) throws Exception 32 | { 33 | Reflect.setFieldO(instance, "transformers", transformerConfigs); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/wrap/Deobfuscator.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.wrap; 2 | 3 | import java.io.PrintStream; 4 | import java.lang.reflect.Constructor; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | import com.javadeobfuscator.deobfuscator.ui.util.ByteLoader; 11 | import com.javadeobfuscator.deobfuscator.ui.util.FallbackException; 12 | import com.javadeobfuscator.deobfuscator.ui.util.Reflect; 13 | 14 | public class Deobfuscator 15 | { 16 | /** 17 | * ClassLoader to load classes from deobfuscator jar. 18 | */ 19 | private final ByteLoader loader; 20 | /** 21 | * Config wrapper to use in deobfuscator. 22 | */ 23 | private Config config; 24 | /** 25 | * The deobfuscator instance. 26 | */ 27 | private Object instance; 28 | 29 | Deobfuscator(ByteLoader loader) throws FallbackException 30 | { 31 | if (loader == null) 32 | { 33 | throw new FallbackException("Loading Problem", "Could not create Config instance.", new NullPointerException("loader is null")); 34 | } 35 | this.loader = loader; 36 | } 37 | 38 | /** 39 | * Allow easy interception of logging calls. 40 | * 41 | * @param hook PrintStream to redirect log calls to. 42 | */ 43 | public void hookLogging(PrintStream hook) 44 | { 45 | try 46 | { 47 | hookLogger("com.javadeobfuscator.deobfuscator.Deobfuscator", hook); 48 | hookLogger("com.javadeobfuscator.deobfuscator.DeobfuscatorMain", hook); 49 | } catch (Exception e) 50 | { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | /** 56 | * @return Config wrapper. 57 | * @throws FallbackException 58 | */ 59 | public Config getConfig() throws FallbackException 60 | { 61 | if (config == null) 62 | { 63 | try 64 | { 65 | Class conf = loader.loadClass("com.javadeobfuscator.deobfuscator.config.Configuration"); 66 | config = new Config(conf.newInstance()); 67 | } catch (Exception e) 68 | { 69 | throw new FallbackException("Loading Problem", "Could not create Config instance.", e); 70 | } 71 | } 72 | return config; 73 | } 74 | 75 | /** 76 | * Runs the deobfuscator process. 77 | * 78 | * @throws Exception Thrown for any failure in the deobfuscator. 79 | */ 80 | public void run() throws Exception 81 | { 82 | Class main = loader.loadClass("com.javadeobfuscator.deobfuscator.Deobfuscator"); 83 | Config conf = getConfig(); 84 | Constructor con = main.getDeclaredConstructor(conf.get().getClass()); 85 | Object deob = con.newInstance(conf.get()); 86 | instance = deob; 87 | Method start = main.getMethod("start"); 88 | start.invoke(deob); 89 | } 90 | 91 | /** 92 | * Clears the classes in the main deobfuscator class. 93 | * 94 | * @throws Exception Thrown for any failure in the deobfuscator. 95 | */ 96 | public void clearClasses() 97 | { 98 | try 99 | { 100 | if (instance != null) 101 | { 102 | Class main = loader.loadClass("com.javadeobfuscator.deobfuscator.Deobfuscator"); 103 | Field cp = main.getDeclaredField("classpath"); 104 | cp.setAccessible(true); 105 | ((Map) cp.get(instance)).clear(); 106 | Field c = main.getDeclaredField("classes"); 107 | c.setAccessible(true); 108 | ((Map) c.get(instance)).clear(); 109 | Field h = main.getDeclaredField("hierachy"); 110 | h.setAccessible(true); 111 | ((Map) h.get(instance)).clear(); 112 | Field ip = main.getDeclaredField("inputPassthrough"); 113 | ip.setAccessible(true); 114 | ((Map) ip.get(instance)).clear(); 115 | Field cps = main.getDeclaredField("constantPools"); 116 | cps.setAccessible(true); 117 | ((Map) cps.get(instance)).clear(); 118 | Field r = main.getDeclaredField("readers"); 119 | r.setAccessible(true); 120 | ((Map) r.get(instance)).clear(); 121 | Field lc = main.getDeclaredField("libraryClassnodes"); 122 | lc.setAccessible(true); 123 | ((Set) lc.get(instance)).clear(); 124 | instance = null; 125 | } 126 | } catch (Exception e) 127 | { 128 | e.printStackTrace(); 129 | } 130 | } 131 | 132 | /** 133 | * Intercept logging calls. 134 | * 135 | * @param ownerName 136 | * @param hook 137 | * @throws Exception 138 | */ 139 | private void hookLogger(String ownerName, PrintStream hook) throws Exception 140 | { 141 | // unused, but required to load class, which sets up some important static fields 142 | getLogger(loader.loadClass(ownerName)); 143 | Class simpleLogger = loader.loadClass("org.slf4j.simple.SimpleLogger"); 144 | 145 | Object config = Reflect.getFieldS(simpleLogger, "CONFIG_PARAMS"); 146 | Object outChoice = Reflect.getFieldO(config, "outputChoice"); 147 | 148 | Class typeEnum = loader.loadClass("org.slf4j.simple.OutputChoice$OutputChoiceType"); 149 | Object enumChoice = Reflect.getFieldS(typeEnum, "FILE"); 150 | 151 | // hook 152 | Reflect.setFieldO(outChoice, "outputChoiceType", enumChoice); 153 | Reflect.setFieldO(outChoice, "targetPrintStream", hook); 154 | } 155 | 156 | private Object getLogger(Class loggerOwner) throws Exception 157 | { 158 | // LoggerFactory.getLogger(getClass()) 159 | Class factory = loader.loadClass("org.slf4j.LoggerFactory"); 160 | Method m = factory.getDeclaredMethod("getLogger", Class.class); 161 | return m.invoke(null, loggerOwner); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/wrap/Transformers.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.wrap; 2 | 3 | import java.lang.reflect.Method; 4 | import java.lang.reflect.Modifier; 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | import com.javadeobfuscator.deobfuscator.ui.util.ByteLoader; 10 | import com.javadeobfuscator.deobfuscator.ui.util.FallbackException; 11 | 12 | public class Transformers 13 | { 14 | // load with: 15 | // com/javadeobfuscator/deobfuscator/config/TransformerConfig.configFor(class) 16 | 17 | /** 18 | * List of all transformers. 19 | */ 20 | private static final List> transformers = new ArrayList<>(); 21 | /** 22 | * ClassLoader to load classes from deobfuscator jar. 23 | */ 24 | private final ByteLoader loader; 25 | 26 | public Transformers(ByteLoader loader) throws FallbackException 27 | { 28 | if (loader == null) 29 | { 30 | throw new FallbackException("Loading Problem", "Could not create Config instance.", new NullPointerException("loader is null")); 31 | } 32 | this.loader = loader; 33 | } 34 | 35 | /** 36 | * @return List of all transformer classes. 37 | * @throws FallbackException when an error occurs while reading transformer classes 38 | */ 39 | public List> getTransformers() throws FallbackException 40 | { 41 | if (transformers.isEmpty()) 42 | { 43 | try 44 | { 45 | Class transformer = loader.loadClass("com.javadeobfuscator.deobfuscator.transformers.Transformer"); 46 | Class transformerD = loader.loadClass("com.javadeobfuscator.deobfuscator.transformers.DelegatingTransformer"); 47 | List filtered = new ArrayList<>(); 48 | for (String name : loader.getClassNames()) 49 | { 50 | if (name.startsWith("com.javadeobfuscator.deobfuscator.transformers.") && !name.endsWith("package-info") && !name.endsWith("Config") 51 | && !name.contains("$")) 52 | { 53 | filtered.add(name); 54 | } 55 | } 56 | Collections.sort(filtered); 57 | for (String name : filtered) 58 | { 59 | Class clazz = loader.loadClass(name); 60 | if (!clazz.equals(transformer) && !clazz.equals(transformerD) && transformer.isAssignableFrom(clazz) 61 | && !Modifier.isAbstract(clazz.getModifiers())) 62 | { 63 | transformers.add(clazz); 64 | } 65 | } 66 | } catch (Exception e) 67 | { 68 | transformers.clear(); 69 | throw new FallbackException("Loading Problem", "Failed to parse transformer list.", e); 70 | } 71 | } 72 | return transformers; 73 | } 74 | 75 | /** 76 | * @param transClass Transformer class 77 | * @return Config instance for transformer class. 78 | */ 79 | public Object getConfigFor(Class transClass) throws Exception 80 | { 81 | Class confLoader = loader.loadClass("com.javadeobfuscator.deobfuscator.config.TransformerConfig"); 82 | Method configFor = confLoader.getDeclaredMethod("configFor", Class.class); 83 | return configFor.invoke(null, transClass); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/java/com/javadeobfuscator/deobfuscator/ui/wrap/WrapperFactory.java: -------------------------------------------------------------------------------- 1 | package com.javadeobfuscator.deobfuscator.ui.wrap; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.util.Enumeration; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.zip.ZipEntry; 11 | import java.util.zip.ZipFile; 12 | 13 | import com.javadeobfuscator.deobfuscator.ui.util.ByteLoader; 14 | import com.javadeobfuscator.deobfuscator.ui.util.FallbackException; 15 | import com.javadeobfuscator.deobfuscator.ui.util.InvalidJarException; 16 | import com.javadeobfuscator.deobfuscator.ui.util.MiniClassReader; 17 | 18 | public class WrapperFactory 19 | { 20 | /** 21 | * Buffer size to use for reading classes from deobfuscator jar. 22 | */ 23 | private final static int BUFF_SIZE = (int) Math.pow(2, 13); 24 | /** 25 | * Loader to use. 26 | */ 27 | private static ByteLoader loader; 28 | 29 | /** 30 | * @return Deobfuscator wrapper. 31 | */ 32 | public static Deobfuscator getDeobfuscator() throws FallbackException 33 | { 34 | return new Deobfuscator(loader); 35 | } 36 | 37 | /** 38 | * @return Transformers wrapper. 39 | */ 40 | public static Transformers getTransformers() throws FallbackException 41 | { 42 | return new Transformers(loader); 43 | } 44 | 45 | /** 46 | * Set load strategy to loading from specified jar. 47 | * 48 | * @param file Deobfuscator program jar. 49 | * @throws IOException Jar could not be read. 50 | * @throws InvalidJarException Thrown if jar loaded was not an instance of JavaDeobfuscator 51 | */ 52 | public static void setupJarLoader(File file) throws IOException, InvalidJarException 53 | { 54 | loader = fromJar(file); 55 | } 56 | 57 | /** 58 | * Set load strategy to loading from adjacent jar files. 59 | * 60 | * @param recursive Check sub-directories of adjacent folders. 61 | */ 62 | public static void setupJarLoader(boolean recursive) throws FallbackException 63 | { 64 | loader = auto(recursive); 65 | } 66 | 67 | /** 68 | * Create a loader for the deobfuscator jar. 69 | * 70 | * @param jar Deobfuscator program jar. 71 | * @return JavaDeobfuscator loader. 72 | * @throws IOException Jar could not be read. 73 | * @throws InvalidJarException Thrown if jar loaded was not an instance of JavaDeobfuscator 74 | */ 75 | private static ByteLoader fromJar(File jar) throws IOException, InvalidJarException 76 | { 77 | System.out.println("Loading deobfuscator from jar: " + jar.getAbsolutePath()); 78 | return new ByteLoader(readClasses(jar)); 79 | } 80 | 81 | /** 82 | * Create a wrapper from the deobfuscator by searching for it in adjacent files / sub-directories. 83 | * 84 | * @param recurse Check sub-directories of adjacent folders. 85 | * @return JavaDeobfuscator loader. {@code null} if no JavaDeobfuscator jar could be found. 86 | */ 87 | private static ByteLoader auto(boolean recurse) throws FallbackException 88 | { 89 | return iter(new File(System.getProperty("user.dir")), recurse); 90 | } 91 | 92 | /** 93 | * Iterate files to detect the Deobfuscator jar. 94 | * 95 | * @param dir directory to look in 96 | * @param recurse whether to recurse into subdirectories 97 | * @return JavaDeobfuscator loader. 98 | */ 99 | private static ByteLoader iter(File dir, boolean recurse) throws FallbackException 100 | { 101 | System.out.println("Searching for deobfuscator in " + dir.getAbsolutePath()); 102 | File[] files = dir.listFiles(); 103 | // return if no files exist in the directory 104 | if (files == null) 105 | { 106 | throw new FallbackException("Loading problem", "No files found in directory: " + dir.getAbsolutePath(), null); 107 | } 108 | // check for common names 109 | File deobfuscator = new File(dir, "deobfuscator.jar"); 110 | File deobfuscator100 = new File(dir, "deobfuscator-1.0.0.jar"); 111 | if (deobfuscator.exists()) 112 | try 113 | { 114 | return fromJar(deobfuscator); 115 | } catch (IOException | InvalidJarException e) 116 | { 117 | System.err.println("Failed to load deobfuscator from " + deobfuscator.getAbsolutePath()); 118 | e.printStackTrace(); 119 | } 120 | if (deobfuscator100.exists()) 121 | try 122 | { 123 | return fromJar(deobfuscator100); 124 | } catch (IOException | InvalidJarException e) 125 | { 126 | System.err.println("Failed to load deobfuscator from " + deobfuscator100.getAbsolutePath()); 127 | e.printStackTrace(); 128 | } 129 | for (File file : files) 130 | { 131 | // check sub-dirs 132 | if (recurse && file.isDirectory()) 133 | { 134 | try 135 | { 136 | return iter(file, true); 137 | } catch (FallbackException e) { 138 | // ignore 139 | } 140 | } 141 | // check files in the directory 142 | else if (file.getName().endsWith(".jar")) 143 | { 144 | try 145 | { 146 | return fromJar(file); 147 | } catch (IOException | InvalidJarException e) 148 | { 149 | System.err.println("Failed to load deobfuscator from " + file.getAbsolutePath()); 150 | e.printStackTrace(); 151 | } 152 | } 153 | } 154 | throw new FallbackException("Loading problem", "No deobfuscator jar found in directory: " + dir.getAbsolutePath(), null); 155 | } 156 | 157 | /** 158 | * Read a map from the given data file. 159 | * 160 | * @param jar File to read from. 161 | * @return Map of class names to their bytecode. 162 | * @throws IOException Jar could not be read. 163 | * @throws InvalidJarException Thrown if jar loaded was not an instance of JavaDeobfuscator 164 | */ 165 | private static Map readClasses(File jar) throws IOException, InvalidJarException 166 | { 167 | Map contents = new HashMap<>(); 168 | boolean found = false; 169 | try (ZipFile file = new ZipFile(jar)) 170 | { 171 | // iterate zip entries 172 | Enumeration entries = file.entries(); 173 | while (entries.hasMoreElements()) 174 | { 175 | ZipEntry entry = entries.nextElement(); 176 | // skip directories 177 | if (entry.isDirectory()) 178 | continue; 179 | // skip non-classes (Deobfuscator doesn't have any resources aside for META) 180 | String name = entry.getName(); 181 | if (!name.endsWith(".class")) 182 | { 183 | continue; 184 | } 185 | try (InputStream is = file.getInputStream(entry)) 186 | { 187 | // skip non-classes (Deobfuscator doesn't have any resources aside for 188 | // META, which we will bundle to appease SLF4J's ServiceLoader screwery.) 189 | byte[] value = from(is); 190 | String className; 191 | try 192 | { 193 | className = new MiniClassReader(value).getClassName(); 194 | } catch (Exception e) 195 | { 196 | continue; 197 | } 198 | // We know this class is in the deobfuscator jar, so if the jar does 199 | // not contain it, it is not the correct file. 200 | if (!found && className.startsWith("com/javadeobfuscator/deobfuscator/Deobfuscator")) 201 | { 202 | found = true; 203 | } 204 | contents.put(className.replace("/", "."), value); 205 | } 206 | } 207 | } 208 | // check to ensure expected content of jar file 209 | if (!found) 210 | { 211 | throw new InvalidJarException(); 212 | } 213 | return contents; 214 | } 215 | 216 | /** 217 | * Reads the bytes from the InputStream into a byte array. 218 | * 219 | * @param is InputStream to read from. 220 | * @return byte array representation of the input stream. 221 | * @throws IOException Thrown if the given input stream cannot be read from. 222 | */ 223 | private static byte[] from(InputStream is) throws IOException 224 | { 225 | try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) 226 | { 227 | int r; 228 | byte[] data = new byte[BUFF_SIZE]; 229 | while ((r = is.read(data, 0, data.length)) != -1) 230 | { 231 | buffer.write(data, 0, r); 232 | } 233 | buffer.flush(); 234 | return buffer.toByteArray(); 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/resources/META-INF/services/com.fasterxml.jackson.core.JsonFactory: -------------------------------------------------------------------------------- 1 | com.fasterxml.jackson.core.JsonFactory 2 | -------------------------------------------------------------------------------- /src/resources/META-INF/services/com.fasterxml.jackson.core.ObjectCodec: -------------------------------------------------------------------------------- 1 | com.fasterxml.jackson.databind.ObjectMapper 2 | -------------------------------------------------------------------------------- /src/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider: -------------------------------------------------------------------------------- 1 | org.slf4j.simple.SimpleServiceProvider -------------------------------------------------------------------------------- /swing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/java-deobfuscator/deobfuscator-gui/ea513c7b3a09689b98d800243bd2b0ff524cee71/swing.png --------------------------------------------------------------------------------