package the.bytecode.club.bytecodeviewer.gui;
import org.apache.commons.io.FileUtils;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.*;
import the.bytecode.club.bytecodeviewer.decompilers.CFRDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.decompilers.FernFlowerDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.ProcyonDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.ClassNodeDecompiler;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameClasses;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameFields;
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameMethods;
import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
import the.bytecode.club.bytecodeviewer.plugin.preinstalled.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.*;
import java.util.List;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* The main file for the GUI.n
*
* @author Konloch
*/
public class MainViewerGUI extends JFrame implements FileChangeNotifier {
public void pythonC() {
new FileChooser(Settings.PYTHON2_LOCATION, "Python (Or PyPy for speed) 2.7 Executable").run();
}
public void javac() {
new FileChooser(Settings.JAVAC_LOCATION, "javac executable (Requires JDK 'C:/Program Files/Java/jdk_xx/bin/javac.exe')").run();
}
public void java() {
new FileChooser(Settings.JAVA_LOCATION, "Java Executable (Requires JRE/JDK 'C:/Program Files/Java/jre_xx/bin/java.exe')").run();
}
public void pythonC3() {
new FileChooser(Settings.PYTHON3_LOCATION, "Python (Or PyPy for speed) 3.x Executable").run();
}
public void rtC() {
new FileChooser(Settings.RT_LOCATION, "Java rt.jar").run();
}
public void library() {
final JTextField text = new JTextField();
text.setText(Settings.PATH.get());
final JDialog dialog = new JDialog();
dialog.setModal(true);
dialog.add(text);
dialog.setSize(500, 100);
dialog.setLocationRelativeTo(BytecodeViewer.viewer);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Settings.PATH.set(text.getText());
}
});
dialog.setVisible(true);
}
public static final long serialVersionUID = 1851409230530948543L;
private final ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (refreshOnChange.isSelected()) {
if (workPane.getCurrentViewer() == null) return;
workPane.refreshClass.doClick();
}
}
};
private JMenu generateDecompilerMenu(Decompiler decompiler, int panelId) {
ButtonGroup group = allPanes.get(panelId);
JMenu menu = new JMenu(decompiler.getName());
JRadioButtonMenuItem java = new JRadioButtonMenuItem(decompiler == Decompiler.SMALI ? "Smali" : "Java");
java.addActionListener(listener);
JRadioButtonMenuItem bytecode = new JRadioButtonMenuItem("Bytecode");
JCheckBoxMenuItem editable = new JCheckBoxMenuItem("Editable");
JSeparator separator = new JSeparator();
menu.add(java);
group.add(java);
allDecompilers.get(group).put(java, decompiler);
allDecompilersRev.get(group).put(decompiler, java);
if (decompiler == Decompiler.KRAKATAU) {
menu.add(bytecode);
group.add(bytecode);
bytecode.addActionListener(listener);
allDecompilers.get(group).put(bytecode, Decompiler.KRAKATAU_DA);
}
menu.add(separator);
menu.add(editable);
editButtons.get(group).put(decompiler, editable);
return menu;
}
private JMenu generatePane(int id) {
JMenu menu = new JMenu("Pane " + (id + 1));
JRadioButtonMenuItem none = new JRadioButtonMenuItem("None");
JRadioButtonMenuItem bytecode = new JRadioButtonMenuItem("Bytecode");
JRadioButtonMenuItem hexcode = new JRadioButtonMenuItem("Hexcode");
ButtonGroup group = allPanes.get(id);
group.add(none);
group.add(bytecode);
group.add(hexcode);
allDecompilers.get(group).put(none, null);
allDecompilersRev.get(group).put(null, none);
allDecompilers.get(group).put(bytecode, Decompiler.BYTECODE);
allDecompilersRev.get(group).put(Decompiler.BYTECODE, bytecode);
allDecompilers.get(group).put(hexcode, Decompiler.HEXCODE);
allDecompilersRev.get(group).put(Decompiler.HEXCODE, hexcode);
menu.add(none);
menu.add(new JSeparator());
menu.add(generateDecompilerMenu(Decompiler.PROCYON, id));
menu.add(generateDecompilerMenu(Decompiler.CFR, id));
menu.add(generateDecompilerMenu(Decompiler.JDGUI, id));
menu.add(generateDecompilerMenu(Decompiler.FERNFLOWER, id));
menu.add(generateDecompilerMenu(Decompiler.KRAKATAU, id));
menu.add(new JSeparator());
menu.add(generateDecompilerMenu(Decompiler.SMALI, id));
menu.add(new JSeparator());
menu.add(bytecode);
menu.add(hexcode);
return menu;
}
public class Test implements KeyEventDispatcher {
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
BytecodeViewer.checkHotKey(e);
return false;
}
}
public FileNavigationPane cn = new FileNavigationPane(this);
public boolean isMaximized = false;
public JSplitPane sp1;
public JSplitPane sp2;
static ArrayList<VisibleComponent> rfComps = new ArrayList<VisibleComponent>();
public final JMenuItem mntmNewWorkspace = new JMenuItem("New Workspace");
public JMenu mnRecentFiles = new JMenu("Recent Files");
public final JMenuItem mntmNewMenuItem = new JMenuItem("Decompile & Save All Classes..");
public final JMenuItem mntmAbout = new JMenuItem("About");
public final JMenuItem mntmStartExternalPlugin = new JMenuItem("Open Plugin..");
public JMenu mnRecentPlugins = new JMenu("Recent Plugins");
public final JMenuItem mntmStartZkmString = new JMenuItem("ZKM String Decrypter");
public final JMenuItem mntmNewMenuItem_1 = new JMenuItem("Malicious Code Scanner");
public final JMenuItem mntmNewMenuItem_2 = new JMenuItem("Allatori String Decrypter");
public final JMenuItem mntmShowAllStrings = new JMenuItem("Show All Strings");
public final JMenuItem mntmShowMainMethods = new JMenuItem("Show Main Methods");
public final JMenuItem mntmNewMenuItem_3 = new JMenuItem("Save As Runnable Jar..");
public final JMenuItem mntmReplaceStrings = new JMenuItem("Replace Strings");
public final JCheckBoxMenuItem chckbxmntmNewCheckItem_12 = new JCheckBoxMenuItem("Update Check");
public final JMenuItem mntmNewMenuItem_6 = new JMenuItem("Rename Fields");
public final JMenuItem mntmNewMenuItem_7 = new JMenuItem("Rename Methods");
public final JMenuItem mntmNewMenuItem_8 = new JMenuItem("Move All Classes Into Root Package");
public final JMenuItem mntmNewMenuItem_9 = new JMenuItem("Control Flow");
public final JMenuItem mntmNewMenuItem_10 = new JMenuItem("Junk Code");
public final ButtonGroup obfuscatorGroup = new ButtonGroup();
public final JRadioButtonMenuItem strongObf = new JRadioButtonMenuItem("Strong Obfuscation");
public final JRadioButtonMenuItem lightObf = new JRadioButtonMenuItem("Light Obfuscation");
public final JMenuItem mntmNewMenuItem_11 = new JMenuItem("Rename Classes");
public final JMenuItem mntmNewMenuItem_12 = new JMenuItem("Decompile & Save Opened Class..");
public WorkPane workPane = new WorkPane(this);
public final JCheckBoxMenuItem refreshOnChange = new JCheckBoxMenuItem("Refresh On View Change");
public AboutWindow aboutWindow = new AboutWindow();
public final JMenuItem mntmSaveAsApk = new JMenuItem("Save As DEX..");
public final JMenuItem mntmCodeSequenceDiagram = new JMenuItem("Code Sequence Diagram");
public final JCheckBoxMenuItem autoCompileSmali = new JCheckBoxMenuItem("Compile On Save");
public final JMenuItem mntmNewMenuItem_13 = new JMenuItem("Compile");
public final JCheckBoxMenuItem autoCompileOnRefresh = new JCheckBoxMenuItem("Compile On Refresh");
public final JMenuItem mntmSetPythonDirectory = new JMenuItem("Set Python 2.7 Executable");
public final JMenuItem mntmSetJreRt = new JMenuItem("Set JRE RT Library");
public final JMenuItem mntmZstringarrayDecrypter = new JMenuItem("ZStringArray Decrypter");
public final JMenuItem mntmRun = new JMenuItem("Run");
public final JCheckBoxMenuItem decodeAPKResources = new JCheckBoxMenuItem("Decode APK Resources");
public final ButtonGroup panelGroup1 = new ButtonGroup();
public final ButtonGroup panelGroup2 = new ButtonGroup();
public final ButtonGroup panelGroup3 = new ButtonGroup();
public final JCheckBox mnShowContainer = new JCheckBox("Show Containing File's Name");
private final JMenuItem mntmSetOpitonalLibrary = new JMenuItem("Set Optional Library Folder");
private final JMenuItem mntmPingback = new JMenuItem("Pingback");
private final JMenu mnFontSize = new JMenu("Font Size");
private final JCheckBoxMenuItem chckbxmntmDeleteForiegnoutdatedLibs = new JCheckBoxMenuItem("Delete Foreign/Outdated Libs");
private final JMenu mnApkConversion = new JMenu("APK Conversion");
public final ButtonGroup apkConversionGroup = new ButtonGroup();
public final JRadioButtonMenuItem apkConversionDex = new JRadioButtonMenuItem("Dex2Jar");
public final JRadioButtonMenuItem apkConversionEnjarify = new JRadioButtonMenuItem("Enjarify");
private final JMenuItem mntmSetPythonx = new JMenuItem("Set Python 3.X Executable");
private final JMenuItem mntmReloadResources = new JMenuItem("Reload Resources");
private final JMenuItem mntmSetJavacExecutable = new JMenuItem("Set Javac Executable");
public List<ButtonGroup> allPanes = Collections.unmodifiableList(Arrays.asList(panelGroup1, panelGroup2, panelGroup3));
public Map<ButtonGroup, Map<JRadioButtonMenuItem, Decompiler>> allDecompilers = new HashMap<>();
public Map<ButtonGroup, Map<Decompiler, JRadioButtonMenuItem>> allDecompilersRev = new HashMap<>();
public Map<ButtonGroup, Map<Decompiler, JCheckBoxMenuItem>> editButtons = new HashMap<>();
public MainViewerGUI() {
Decompiler.ensureInitted();
allDecompilers.put(panelGroup1, new HashMap<JRadioButtonMenuItem, Decompiler>());
allDecompilers.put(panelGroup2, new HashMap<JRadioButtonMenuItem, Decompiler>());
allDecompilers.put(panelGroup3, new HashMap<JRadioButtonMenuItem, Decompiler>());
allDecompilersRev.put(panelGroup1, new HashMap<Decompiler, JRadioButtonMenuItem>());
allDecompilersRev.put(panelGroup2, new HashMap<Decompiler, JRadioButtonMenuItem>());
allDecompilersRev.put(panelGroup3, new HashMap<Decompiler, JRadioButtonMenuItem>());
editButtons.put(panelGroup1, new HashMap<Decompiler, JCheckBoxMenuItem>());
editButtons.put(panelGroup2, new HashMap<Decompiler, JCheckBoxMenuItem>());
editButtons.put(panelGroup3, new HashMap<Decompiler, JCheckBoxMenuItem>());
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new Test());
this.addWindowStateListener(new WindowAdapter() {
@Override
public void windowStateChanged(WindowEvent evt) {
int oldState = evt.getOldState();
int newState = evt.getNewState();
if ((oldState & Frame.ICONIFIED) == 0 && (newState & Frame.ICONIFIED) != 0) {
//System.out.println("Frame was iconized");
} else if ((oldState & Frame.ICONIFIED) != 0 && (newState & Frame.ICONIFIED) == 0) {
//System.out.println("Frame was deiconized");
}
if ((oldState & Frame.MAXIMIZED_BOTH) == 0 && (newState & Frame.MAXIMIZED_BOTH) != 0) {
isMaximized = true;
} else if ((oldState & Frame.MAXIMIZED_BOTH) != 0 && (newState & Frame.MAXIMIZED_BOTH) == 0) {
isMaximized = false;
}
}
});
this.setIconImages(Resources.iconList);
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
JMenu viewMenu = new JMenu("View");
JMenu settingsMenu = new JMenu("Settings");
JMenu obfuscateMenu = new JMenu("Obfuscate");
JMenu pluginsMenu = new JMenu("Plugins");
obfuscateMenu.setVisible(false);
setJMenuBar(menuBar);
obfuscatorGroup.add(strongObf);
obfuscatorGroup.add(lightObf);
obfuscatorGroup.setSelected(strongObf.getModel(), true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
menuBar.add(fileMenu);
mntmNewWorkspace.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
BytecodeViewer.resetWorkSpace(true);
}
});
JMenuItem mntmLoadJar = new JMenuItem("Add..");
mntmLoadJar.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
try {
File f = new File(BytecodeViewer.lastDirectory);
if (f.exists()) fc.setSelectedFile(f);
} catch (Exception e2) {
}
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
if (f.isDirectory()) return true;
String extension = MiscUtils.extension(f.getAbsolutePath());
if (extension != null)
if (extension.equals("jar") || extension.equals("zip") || extension.equals("class") || extension.equals("apk") || extension.equals("dex"))
return true;
return false;
}
@Override
public String getDescription() {
return "APKs, DEX, Class Files or Zip/Jar Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION) {
BytecodeViewer.lastDirectory = fc.getSelectedFile().getAbsolutePath();
try {
BytecodeViewer.viewer.setIcon(true);
BytecodeViewer.openFiles(new File[]{fc.getSelectedFile()}, true);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e1) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e1);
}
}
}
});
fileMenu.add(mntmLoadJar);
fileMenu.add(new JSeparator());
fileMenu.add(mntmNewWorkspace);
JMenuItem mntmSave = new JMenuItem("Save As Zip..");
mntmSave.setActionCommand("");
mntmSave.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.getLoadedBytes().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
Thread t = new Thread() {
public void run() {
if (autoCompileSmali.isSelected() && !BytecodeViewer.compile(false)) return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
if (!file.getAbsolutePath().endsWith(".zip"))
file = new File(file.getAbsolutePath() + ".zip");
if (file.exists()) {
JOptionPane pane = new JOptionPane("Are you sure you wish to overwrite this existing file?");
Object[] options = new String[]{"Yes", "No"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Overwrite File");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
if (result == 0) {
file.delete();
} else {
return;
}
}
final File file2 = file;
BytecodeViewer.viewer.setIcon(true);
Thread t = new Thread() {
@Override
public void run() {
JarUtils.saveAsJar(BytecodeViewer.getLoadedBytes(), file2.getAbsolutePath());
BytecodeViewer.viewer.setIcon(false);
}
};
t.start();
}
}
};
t.start();
}
});
fileMenu.add(new JSeparator());
mntmReloadResources.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JOptionPane pane = new JOptionPane("Are you sure you wish to reload the resources?");
Object[] options = new String[]{"Yes", "No"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Reload Resources");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
if (result == 0) {
ArrayList<File> reopen = new ArrayList<File>();
for (FileContainer container : BytecodeViewer.files)
reopen.add(container.file);
BytecodeViewer.files.clear();
BytecodeViewer.openFiles(reopen.toArray(new File[reopen.size()]), false);
//refresh panes
}
}
});
fileMenu.add(mntmReloadResources);
fileMenu.add(new JSeparator());
mntmNewMenuItem_3.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (BytecodeViewer.getLoadedBytes().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
Thread t = new Thread() {
public void run() {
if (autoCompileSmali.isSelected() && !BytecodeViewer.compile(false)) return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
String path = file.getAbsolutePath();
if (!path.endsWith(".jar")) path = path + ".jar";
if (new File(path).exists()) {
JOptionPane pane = new JOptionPane("Are you sure you wish to overwrite this existing file?");
Object[] options = new String[]{"Yes", "No"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Overwrite File");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
if (result == 0) {
file.delete();
} else {
return;
}
}
new ExportJar(path).setVisible(true);
}
}
};
t.start();
}
});
mntmNewMenuItem_13.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
Thread t = new Thread() {
public void run() {
BytecodeViewer.compile(true);
}
};
t.start();
}
});
mntmRun.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (BytecodeViewer.getLoadedBytes().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
new RunOptions().setVisible(true);
}
});
fileMenu.add(mntmRun);
fileMenu.add(mntmNewMenuItem_13);
fileMenu.add(new JSeparator());
fileMenu.add(mntmNewMenuItem_3);
mntmSaveAsApk.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.getLoadedBytes().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
Thread t = new Thread() {
public void run() {
if (autoCompileSmali.isSelected() && !BytecodeViewer.compile(false)) return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("dex");
}
@Override
public String getDescription() {
return "Android DEX Files";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
final File file = fc.getSelectedFile();
String output = file.getAbsolutePath();
if (!output.endsWith(".dex")) output = output + ".dex";
final File file2 = new File(output);
if (file2.exists()) {
JOptionPane pane = new JOptionPane("Are you sure you wish to overwrite this existing file?");
Object[] options = new String[]{"Yes", "No"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Overwrite File");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
if (result == 0) {
file.delete();
} else {
return;
}
}
Thread t = new Thread() {
@Override
public void run() {
BytecodeViewer.viewer.setIcon(true);
final String input = BytecodeViewer.tempDir.getAbsolutePath() + BytecodeViewer.fs + BytecodeViewer.getRandomizedName() + ".jar";
JarUtils.saveAsJar(BytecodeViewer.getLoadedBytes(), input);
Thread t = new Thread() {
@Override
public void run() {
Dex2Jar.saveAsDex(new File(input), file2);
BytecodeViewer.viewer.setIcon(false);
}
};
t.start();
}
};
t.start();
}
}
};
t.start();
}
});
fileMenu.add(mntmSaveAsApk);
fileMenu.add(mntmSave);
mntmNewMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.files.isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
Thread t = new Thread() {
public void run() {
if (autoCompileSmali.isSelected() && !BytecodeViewer.compile(false)) return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
if (!file.getAbsolutePath().endsWith(".zip"))
file = new File(file.getAbsolutePath() + ".zip");
if (file.exists()) {
JOptionPane pane = new JOptionPane("Are you sure you wish to overwrite this existing file?");
Object[] options = new String[]{"Yes", "No"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Overwrite File");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
if (result == 0) {
file.delete();
} else {
return;
}
}
BytecodeViewer.viewer.setIcon(true);
final String path = MiscUtils.append(file, ".zip"); // cheap hax cause
// string is final
JOptionPane pane = new JOptionPane("What decompiler will you use?");
Object[] options = new String[]{"Procyon", "CFR", "Fernflower", "Krakatau", "JD-GUI", "Cancel"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Select Decompiler");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
if (result == 0) {
Thread t = new Thread() {
@Override
public void run() {
try {
Decompiler.PROCYON.decompileToZip(path);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
}
if (result == 1) {
Thread t = new Thread() {
@Override
public void run() {
try {
Decompiler.CFR.decompileToZip(path);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
}
if (result == 2) {
Thread t = new Thread() {
@Override
public void run() {
try {
Decompiler.FERNFLOWER.decompileToZip(path);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
}
if (result == 3) {
Thread t = new Thread() {
@Override
public void run() {
try {
Decompiler.KRAKATAU.decompileToZip(path);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
} else if (result == 4) {
new Thread() {
@Override
public void run() {
try {
Decompiler.JDGUI.decompileToZip(path);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
}.start();
} else {
BytecodeViewer.viewer.setIcon(false);
}
}
}
};
t.start();
}
});
mntmNewMenuItem_12.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (workPane.getCurrentViewer() == null) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
Thread t = new Thread() {
public void run() {
if (autoCompileSmali.isSelected() && !BytecodeViewer.compile(false)) return;
final String s = workPane.getCurrentViewer().name;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("java");
}
@Override
public String getDescription() {
return "Java Source Files";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
BytecodeViewer.viewer.setIcon(true);
final String path = MiscUtils.append(file, ".java"); // cheap hax cause
// string is final
if (new File(path).exists()) {
JOptionPane pane = new JOptionPane("Are you sure you wish to overwrite this existing file?");
Object[] options = new String[]{"Yes", "No"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Overwrite File");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
if (result == 0) {
file.delete();
} else {
return;
}
}
JOptionPane pane = new JOptionPane("What decompiler will you use?");
Object[] options = new String[]{"Procyon", "CFR", "Fernflower", "Krakatau", "Cancel"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Select Decompiler");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
final String containerName = BytecodeViewer.files.get(0).name;
if (result == 0) {
Thread t = new Thread() {
@Override
public void run() {
try {
ClassNode cn = BytecodeViewer.getClassNode(containerName, s);
byte[] bytes = BytecodeViewer.getClassBytes(containerName, s);
String contents = Decompiler.PROCYON.decompileClassNode(cn, bytes);
FileUtils.write(new File(path), contents, "UTF-8", false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
}
if (result == 1) {
Thread t = new Thread() {
@Override
public void run() {
try {
ClassNode cn = BytecodeViewer.getClassNode(containerName, s);
byte[] bytes = BytecodeViewer.getClassBytes(containerName, s);
String contents = Decompiler.CFR.decompileClassNode(cn, bytes);
FileUtils.write(new File(path), contents, "UTF-8", false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
}
if (result == 2) {
Thread t = new Thread() {
@Override
public void run() {
try {
ClassNode cn = BytecodeViewer.getClassNode(containerName, s);
byte[] bytes = BytecodeViewer.getClassBytes(containerName, s);
String contents = Decompiler.FERNFLOWER.decompileClassNode(cn, bytes);
FileUtils.write(new File(path), contents, "UTF-8", false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
}
if (result == 3) {
Thread t = new Thread() {
@Override
public void run() {
try {
ClassNode cn = BytecodeViewer.getClassNode(containerName, s);
byte[] bytes = BytecodeViewer.getClassBytes(containerName, s);
String contents = Decompiler.KRAKATAU.decompileClassNode(cn, bytes);
FileUtils.write(new File(path), contents, "UTF-8", false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
}
if (result == 4) {
BytecodeViewer.viewer.setIcon(false);
}
}
}
};
t.start();
}
});
fileMenu.add(mntmNewMenuItem_12);
fileMenu.add(mntmNewMenuItem);
fileMenu.add(new JSeparator());
fileMenu.add(mnRecentFiles);
fileMenu.add(new JSeparator());
mntmAbout.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
aboutWindow.setVisible(true);
}
});
fileMenu.add(mntmAbout);
JMenuItem mntmExit = new JMenuItem("Exit");
mntmExit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
JOptionPane pane = new JOptionPane("Are you sure you want to exit?");
Object[] options = new String[]{"Yes", "No"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(BytecodeViewer.viewer, "Bytecode Viewer - Exit");
dialog.setVisible(true);
Object obj = pane.getValue();
int result = -1;
for (int k = 0; k < options.length; k++)
if (options[k].equals(obj)) result = k;
if (result == 0) {
System.exit(0);
}
}
});
mntmPingback.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
BytecodeViewer.pingback();
}
});
fileMenu.add(mntmPingback);
fileMenu.add(mntmExit);
menuBar.add(viewMenu);
viewMenu.add(generatePane(0));
viewMenu.add(generatePane(1));
viewMenu.add(generatePane(2));
autoCompileSmali.setSelected(true);
settingsMenu.add(autoCompileSmali);
autoCompileOnRefresh.setSelected(true);
settingsMenu.add(autoCompileOnRefresh);
settingsMenu.add(refreshOnChange);
settingsMenu.add(new JSeparator());
decodeAPKResources.setSelected(true);
settingsMenu.add(decodeAPKResources);
settingsMenu.add(mnApkConversion);
mnApkConversion.add(apkConversionDex);
mnApkConversion.add(apkConversionEnjarify);
settingsMenu.add(new JSeparator());
chckbxmntmNewCheckItem_12.setSelected(true);
settingsMenu.add(chckbxmntmNewCheckItem_12);
chckbxmntmDeleteForiegnoutdatedLibs.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (!chckbxmntmDeleteForiegnoutdatedLibs.isSelected()) {
BytecodeViewer.showMessage("WARNING: With this being toggled off outdated libraries will NOT be removed. It's also a security issue. ONLY TURN IT OFF IF YOU KNOW WHAT YOU'RE DOING.");
}
BytecodeViewer.deleteForiegnLibraries = chckbxmntmDeleteForiegnoutdatedLibs.isSelected();
}
});
chckbxmntmDeleteForiegnoutdatedLibs.setSelected(true);
settingsMenu.add(chckbxmntmDeleteForiegnoutdatedLibs);
settingsMenu.add(new JSeparator());
mntmSetPythonDirectory.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
pythonC();
}
});
settingsMenu.add(mntmSetPythonDirectory);
mntmSetJreRt.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
rtC();
}
});
mntmSetPythonx.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
pythonC3();
}
});
settingsMenu.add(mntmSetPythonx);
settingsMenu.add(mntmSetJreRt);
mntmSetOpitonalLibrary.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
library();
}
});
settingsMenu.add(mntmSetOpitonalLibrary);
mntmSetJavacExecutable.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
javac();
}
});
settingsMenu.add(mntmSetJavacExecutable);
settingsMenu.add(new JSeparator());
JMenu cfrSettingsMenu = new JMenu("CFR");
DecompilerSettings cfrSettings = Decompiler.CFR.getSettings();
for (CFRDecompiler.Settings setting : CFRDecompiler.Settings.values()) {
cfrSettingsMenu.add(cfrSettings.getMenuItem(setting));
}
settingsMenu.add(cfrSettingsMenu);
JMenu fernflowerSettingMenu = new JMenu("FernFlower");
DecompilerSettings fernflowerSettings = Decompiler.FERNFLOWER.getSettings();
for (FernFlowerDecompiler.Settings setting : FernFlowerDecompiler.Settings.values()) {
fernflowerSettingMenu.add(fernflowerSettings.getMenuItem(setting));
}
settingsMenu.add(fernflowerSettingMenu);
JMenu procyonSettingsMenu = new JMenu("Procyon");
DecompilerSettings procyonSettings = Decompiler.PROCYON.getSettings();
for (ProcyonDecompiler.Settings setting : ProcyonDecompiler.Settings.values()) {
procyonSettingsMenu.add(procyonSettings.getMenuItem(setting));
}
settingsMenu.add(procyonSettingsMenu);
JMenu bytecodeSettingsMenu = new JMenu("Bytecode Decompiler");
DecompilerSettings bytecodeSettings = Decompiler.BYTECODE.getSettings();
for (ClassNodeDecompiler.Settings setting : ClassNodeDecompiler.Settings.values()) {
bytecodeSettingsMenu.add(bytecodeSettings.getMenuItem(setting));
}
settingsMenu.add(bytecodeSettingsMenu);
menuBar.add(settingsMenu);
menuBar.add(obfuscateMenu);
mntmNewMenuItem_6.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.runningObfuscation) {
BytecodeViewer.showMessage("You're currently running an obfuscation task, wait for this to finish.");
return;
}
new RenameFields().start();
workPane.refreshClass.doClick();
cn.tree.updateUI();
}
});
obfuscateMenu.add(strongObf);
obfuscateMenu.add(lightObf);
obfuscateMenu.add(new JSeparator());
mntmNewMenuItem_8.setEnabled(false);
obfuscateMenu.add(mntmNewMenuItem_8);
obfuscateMenu.add(mntmNewMenuItem_6);
mntmNewMenuItem_7.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.runningObfuscation) {
BytecodeViewer.showMessage("You're currently running an obfuscation task, wait for this to finish.");
return;
}
new RenameMethods().start();
workPane.refreshClass.doClick();
cn.tree.updateUI();
}
});
obfuscateMenu.add(mntmNewMenuItem_7);
mntmNewMenuItem_11.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.runningObfuscation) {
BytecodeViewer.showMessage("You're currently running an obfuscation task, wait for this to finish.");
return;
}
new RenameClasses().start();
workPane.refreshClass.doClick();
cn.tree.updateUI();
}
});
obfuscateMenu.add(mntmNewMenuItem_11);
mntmNewMenuItem_9.setEnabled(false);
obfuscateMenu.add(mntmNewMenuItem_9);
mntmNewMenuItem_10.setEnabled(false);
obfuscateMenu.add(mntmNewMenuItem_10);
menuBar.add(pluginsMenu);
pluginsMenu.add(mntmStartExternalPlugin);
pluginsMenu.add(new JSeparator());
pluginsMenu.add(mnRecentPlugins);
pluginsMenu.add(new JSeparator());
mntmCodeSequenceDiagram.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
PluginManager.runPlugin(new CodeSequenceDiagram());
}
});
pluginsMenu.add(mntmCodeSequenceDiagram);
pluginsMenu.add(mntmNewMenuItem_1);
pluginsMenu.add(mntmShowMainMethods);
pluginsMenu.add(mntmShowAllStrings);
mntmReplaceStrings.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
new ReplaceStringsOptions().setVisible(true);
}
});
pluginsMenu.add(mntmReplaceStrings);
pluginsMenu.add(mntmNewMenuItem_2);
pluginsMenu.add(mntmStartZkmString);
mntmZstringarrayDecrypter.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
PluginManager.runPlugin(new ZStringArrayDecrypter());
}
});
pluginsMenu.add(mntmZstringarrayDecrypter);
menuBar.add(spinnerMenu);
mntmStartExternalPlugin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(PluginManager.fileFilter());
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION) try {
BytecodeViewer.viewer.setIcon(true);
BytecodeViewer.startPlugin(fc.getSelectedFile());
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e1) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e1);
}
}
});
mntmStartZkmString.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PluginManager.runPlugin(new ZKMStringDecrypter());
}
});
mntmNewMenuItem_2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
new AllatoriStringDecrypterOptions().setVisible(true);
}
});
mntmNewMenuItem_1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
new MaliciousCodeScannerOptions().setVisible(true);
}
});
mntmShowAllStrings.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PluginManager.runPlugin(new ShowAllStrings());
}
});
mntmShowMainMethods.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PluginManager.runPlugin(new ShowMainMethods());
}
});
setSize(new Dimension(800, 400));
if (BytecodeViewer.previewCopy)
setTitle("Bytecode Viewer " + BytecodeViewer.version + " Preview - https://bytecodeviewer.com | https://the.bytecode.club - @Konloch");
else
setTitle("Bytecode Viewer " + BytecodeViewer.version + " - https://bytecodeviewer.com | https://the.bytecode.club - @Konloch");
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.X_AXIS));
// scrollPane.setViewportView(tree);
cn.setMinimumSize(new Dimension(200, 50));
// panel.add(cn);
SearchingPane s = new SearchingPane(this);
s.setPreferredSize(new Dimension(200, 50));
s.setMinimumSize(new Dimension(200, 50));
s.setMaximumSize(new Dimension(200, 2147483647));
// panel.add(s);
sp1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, cn, s);
// panel.add(sp1);
cn.setPreferredSize(new Dimension(200, 50));
cn.setMaximumSize(new Dimension(200, 2147483647));
sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp1, workPane);
getContentPane().add(sp2);
sp2.setResizeWeight(0.05);
sp1.setResizeWeight(0.5);
rfComps.add(cn);
rfComps.add(s);
rfComps.add(workPane);
apkConversionGroup.add(apkConversionDex);
apkConversionGroup.add(apkConversionEnjarify);
apkConversionGroup.setSelected(apkConversionDex.getModel(), true);
fontSpinner.setPreferredSize(new Dimension(42, 20));
fontSpinner.setSize(new Dimension(42, 20));
fontSpinner.setModel(new SpinnerNumberModel(12, 1, null, 1));
viewMenu.add(mnFontSize);
mnFontSize.add(fontSpinner);
viewMenu.add(mnShowContainer);
mnShowContainer.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
JTabbedPane tabs = workPane.tabs;
Component[] components = tabs.getComponents();
for (int i = 0; i < components.length; i++) {
Component c = components[i];
if (c instanceof Viewer) {
((Viewer) c).updateName();
int idx = tabs.indexOfComponent(c);
tabs.setTabComponentAt(idx, new TabbedPane(c.getName(), tabs));
workPane.tabs.setTitleAt(idx, c.getName());
}
}
}
});
panelGroup1.setSelected(allDecompilersRev.get(panelGroup1).get(Decompiler.FERNFLOWER).getModel(), true);
panelGroup2.setSelected(allDecompilersRev.get(panelGroup2).get(Decompiler.BYTECODE).getModel(), true);
panelGroup3.setSelected(allDecompilersRev.get(panelGroup3).get(null).getModel(), true);
this.setLocationRelativeTo(null);
}
public JSpinner fontSpinner = new JSpinner();
private JMenuItem spinnerMenu = new JMenuItem("");
public void setIcon(final boolean busy) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (busy) {
try {
spinnerMenu.setIcon(Resources.busyIcon);
} catch (NullPointerException e) {
spinnerMenu.setIcon(Resources.busyB64Icon);
}
} else spinnerMenu.setIcon(null);
spinnerMenu.updateUI();
}
});
}
public void calledAfterLoad() {
chckbxmntmDeleteForiegnoutdatedLibs.setSelected(BytecodeViewer.deleteForiegnLibraries);
}
@Override
public void openClassFile(final String name, String container, final ClassNode cn) {
for (final VisibleComponent vc : rfComps) {
vc.openClassFile(name, container, cn);
}
}
@Override
public void openFile(final String name, String container, byte[] content) {
for (final VisibleComponent vc : rfComps) {
vc.openFile(name, container, content);
}
}
public static <T> T getComponent(final Class<T> clazz) {
for (final VisibleComponent vc : rfComps) {
if (vc.getClass() == clazz) return clazz.cast(vc);
}
return null;
}
}