bcv-vf/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java

791 lines
32 KiB
Java
Raw Normal View History

package the.bytecode.club.bytecodeviewer;
import java.awt.event.KeyEvent;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
2021-04-12 22:31:22 +00:00
import java.util.Objects;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import me.konloch.kontainer.io.HTTPRequest;
import org.apache.commons.io.FileUtils;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bootloader.Boot;
import the.bytecode.club.bytecodeviewer.api.ClassNodeLoader;
2021-04-12 22:31:22 +00:00
import the.bytecode.club.bytecodeviewer.compilers.Compilers;
import the.bytecode.club.bytecodeviewer.gui.components.DecompilerViewComponent;
2021-06-26 12:38:58 +00:00
import the.bytecode.club.bytecodeviewer.gui.components.FileChooser;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourcePanelCompileMode;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.TabbedPane;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
2021-06-22 18:05:25 +00:00
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.gui.components.RunOptions;
import the.bytecode.club.bytecodeviewer.gui.resourcesearch.SearchBoxPane;
import the.bytecode.club.bytecodeviewer.gui.components.SystemErrConsole;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.WorkPaneMainComponent;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
import the.bytecode.club.bytecodeviewer.obfuscators.mapping.Refactorer;
import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
import the.bytecode.club.bytecodeviewer.util.*;
import static the.bytecode.club.bytecodeviewer.Constants.*;
/***************************************************************************
* 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/>. *
***************************************************************************/
/**
* A lightweight Java Reverse Engineering suite, developed by Konloch - http://konloch.me
*
* All you have to do is add a jar or class file into the workspace,
2019-04-17 06:45:15 +00:00
* select the file you want then it will start decompiling the class in the background.
* When it's done it will show the Source code, Bytecode and Hexcode of the class file you chose.
*
2019-04-17 06:45:15 +00:00
* There is also a plugin system that will allow you to interact with the loaded classfiles.
* For example you can write a String deobfuscator, a malicious code searcher,
* or anything else you can think of.
*
2019-04-17 06:45:15 +00:00
* You can either use one of the pre-written plugins, or write your own. It supports java scripting.
* Once a plugin is activated, it will send a ClassNode ArrayList of every single class loaded in the
* file system to the execute function, this allows the user to handle it completely using ASM.
*
2019-04-17 06:45:15 +00:00
* Are you a Java Reverse Engineer? Or maybe you want to learn Java Reverse Engineering?
2021-04-12 20:19:12 +00:00
* Join The Bytecode Club, we're noob friendly, and censorship free.
* http://the.bytecode.club
*
2021-06-26 02:59:43 +00:00
* TODO BUGS:
* + Removing a tab disrupts the tab order
* Tab index orders need to be recounted on removal probably
2021-06-26 12:15:53 +00:00
* + Last selected directory isn't set on most file chooser dialogues
2021-06-26 05:25:50 +00:00
* + Synchronized scrolling is broken
* + Spam-clicking the refresh button will cause the swing thread to deadlock (Quickly opening resources used to also do this)
2021-06-26 02:59:43 +00:00
* This is caused by the ctrlMouseWheelZoom code, a temporary patch is just removing it worst case
2021-06-26 05:25:50 +00:00
* + Open as folder doesn't actually work
* + Fix classfile searcher
* + Smali Assembly compile - Needs to be fixed
* + Krakatau Assembly compile - Needs to be fixed
2021-06-26 02:59:43 +00:00
*
* TODO IN-PROGRESS:
2021-06-26 05:25:50 +00:00
* + Finish dragging code
* + Finish right-click tab menu detection
* + Fix hook inject for EZ-Injection
2021-06-26 02:59:43 +00:00
*
* TODO FEATURES:
2021-06-26 05:25:50 +00:00
* + Add stackmapframes to bytecode decompiler
* + Add JEB decompiler optionally, requires them to add jeb library jar
* + Add https://github.com/ptnkjke/Java-Bytecode-Editor visualize as a plugin
* + Add https://github.com/exbin/bined as the replacement Hed Viewer/Editor
* + Make the decompilers launch in a separate process
* + Make it use that global last used inside of export as jar
* + Make zipfile not include the decode shit
* + Make ez-injection plugin console show all sys.out calls
* + Add decompile as zip for krakatau-bytecode, jd-gui and smali for CLI
* + Add decompile all as zip for CLI
2018-01-31 15:41:24 +00:00
*
* @author Konloch
2019-04-17 06:45:15 +00:00
* @author The entire BCV community
*/
public class BytecodeViewer
{
public static String[] args;
public static MainViewerGUI viewer = null;
public static ClassNodeLoader loader = new ClassNodeLoader(); //might be insecure due to assholes targeting BCV,
public static SecurityMan sm = new SecurityMan(); //might be insecure due to assholes targeting BCV, however
public static Refactorer refactorer = new Refactorer();
public static List<FileContainer> files = new ArrayList<>(); //all of BCV's loaded files/classes/etc
public static List<Process> createdProcesses = new ArrayList<>();
public static final DecompilerViewComponent krakatau = new DecompilerViewComponent("Krakatau", true);
2021-06-22 18:05:25 +00:00
public static final boolean EXPERIMENTAL_TAB_CODE = false;
2018-01-31 15:41:24 +00:00
/**
* The version checker thread
*/
private static final Thread versionChecker = new Thread(new VersionChecker());
2018-01-31 15:41:24 +00:00
/**
* Pings back to bytecodeviewer.com to be added into the total running statistics
*/
2021-06-21 11:32:07 +00:00
private static final Thread pingBack = new Thread(() -> {
2021-04-12 22:31:22 +00:00
try {
new HTTPRequest(new URL("https://bytecodeviewer.com/add.php")).read();
} catch (Exception e) {
Configuration.pingback = false;
2018-01-31 15:41:24 +00:00
}
2021-04-12 22:31:22 +00:00
});
2018-01-31 15:41:24 +00:00
/**
* Downloads & installs the krakatau & enjarify zips
*/
2021-06-21 11:32:07 +00:00
private static final Thread installFatJar = new Thread(() -> {
2021-04-12 22:31:22 +00:00
try {
if (OFFLINE_MODE) {
2021-04-12 22:31:22 +00:00
Boot.dropKrakatau();
Boot.dropEnjarify();
} else {
Boot.populateUrlList();
Boot.populateLibsDirectory();
Boot.downloadZipsOnly();
Boot.checkKrakatau();
Boot.checkEnjarify();
2018-01-31 15:41:24 +00:00
}
2021-04-12 22:31:22 +00:00
} catch (Exception e) {
e.printStackTrace();
2018-01-31 15:41:24 +00:00
}
2021-04-12 22:31:22 +00:00
});
2018-01-31 15:41:24 +00:00
/**
* Used to check incase booting failed for some reason, this kicks in as a fail safe
*/
private static final Thread bootCheck = new Thread(new BootCheck());
2018-01-31 15:41:24 +00:00
/**
* Main startup
*
* @param args files you want to open or CLI
*/
public static void main(String[] args) {
BytecodeViewer.args = args;
System.out.println("https://the.bytecode.club - Created by @Konloch - Bytecode Viewer " + VERSION + ", " + "Fat-Jar: " + FAT_JAR);
2018-01-31 15:41:24 +00:00
System.setSecurityManager(sm);
2018-01-31 15:41:24 +00:00
try {
//precache settings file
Settings.preloadSettingsFile();
//setup look and feel
Configuration.lafTheme.setLAF();
2019-04-17 06:45:15 +00:00
if (PREVIEW_COPY && !CommandLineInput.containsCommand(args))
2021-04-12 20:19:12 +00:00
showMessage("WARNING: This is a preview/dev copy, you WON'T be alerted when " + VERSION + " is "
+ "actually out if you use this." + nl +
"Make sure to watch the repo: https://github.com/Konloch/bytecode-viewer for " + VERSION + "'s release");
2018-01-31 15:41:24 +00:00
viewer = new MainViewerGUI();
2019-04-17 06:45:15 +00:00
Settings.loadSettings();
2018-01-31 15:41:24 +00:00
int CLI = CommandLineInput.parseCommandLine(args);
if (CLI == CommandLineInput.STOP)
return;
2019-04-17 06:45:15 +00:00
if (!FAT_JAR) {
2018-01-31 15:41:24 +00:00
bootCheck.start();
2021-04-12 20:19:12 +00:00
Boot.boot(args, CLI != CommandLineInput.OPEN_FILE);
2018-01-31 15:41:24 +00:00
} else
2021-06-21 11:32:07 +00:00
installFatJar.start();
2018-01-31 15:41:24 +00:00
if (CLI == CommandLineInput.OPEN_FILE)
2021-06-21 11:32:07 +00:00
BytecodeViewer.boot(false);
2018-01-31 15:41:24 +00:00
else {
2021-06-21 11:32:07 +00:00
BytecodeViewer.boot(true);
2018-01-31 15:41:24 +00:00
CommandLineInput.executeCommandLine(args);
}
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
/**
* Boot after all of the libraries have been loaded
*
* @param cli is it running CLI mode or not
*/
2021-06-21 11:32:07 +00:00
public static void boot(boolean cli) {
2018-01-31 15:41:24 +00:00
cleanup();
2021-04-12 22:31:22 +00:00
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
for (Process proc : createdProcesses)
proc.destroy();
Settings.saveSettings();
cleanup();
}));
2018-01-31 15:41:24 +00:00
viewer.calledAfterLoad();
2021-06-26 05:25:50 +00:00
Constants.resetRecentFilesMenu();
2018-01-31 15:41:24 +00:00
if (!Configuration.pingback) {
2021-06-21 11:32:07 +00:00
pingBack.start();
Configuration.pingback = true;
2018-01-31 15:41:24 +00:00
}
2021-06-21 22:45:00 +00:00
if (viewer.updateCheck.isSelected())
2018-01-31 15:41:24 +00:00
versionChecker.start();
if (!cli)
viewer.setVisible(true);
System.out.println("Start up took " + ((System.currentTimeMillis() - Configuration.start) / 1000) + " seconds");
2018-01-31 15:41:24 +00:00
if (!cli)
if (args.length >= 1)
for (String s : args)
2018-01-31 15:41:24 +00:00
openFiles(new File[]{new File(s)}, true);
}
/**
* because Smali and Baksmali System.exit if it failed
*
* @param i
*/
public static void exit(int i) {
}
/**
* Returns the java command it can use to launch the decompilers
*
* @return
*/
public static synchronized String getJavaCommand() {
try {
sm.stopBlocking();
ProcessBuilder pb = new ProcessBuilder("java", "-version");
2021-04-12 22:31:22 +00:00
pb.start();
2018-01-31 15:41:24 +00:00
sm.setBlocking();
2021-04-12 22:31:22 +00:00
return "java"; //java is set
2018-01-31 15:41:24 +00:00
} catch (Exception e) { //ignore
sm.setBlocking();
boolean empty = Configuration.java.isEmpty();
2018-01-31 15:41:24 +00:00
while (empty) {
showMessage("You need to set your Java path, this requires the JRE to be downloaded." + nl +
"(C:/Program Files/Java/JDK_xx/bin/java.exe)");
viewer.selectJava();
empty = Configuration.java.isEmpty();
2018-01-31 15:41:24 +00:00
}
}
return Configuration.java;
2018-01-31 15:41:24 +00:00
}
/**
* Returns the currently opened ClassNode
*
* @return the currently opened ClassNode
*/
public static ClassNode getCurrentlyOpenedClassNode() {
return viewer.workPane.getCurrentViewer().cn;
}
/**
* Returns the ClassNode by the specified name
*
* @param name the class name
* @return the ClassNode instance
*/
public static ClassNode getClassNode(String name) {
for (FileContainer container : files)
for (ClassNode c : container.classes)
if (c.name.equals(name))
return c;
return null;
}
2019-04-17 06:45:15 +00:00
public static FileContainer getFileContainer(String name) {
for (FileContainer container : files)
if (container.name.equals(name))
return container;
return null;
}
2019-06-01 01:04:07 +00:00
public static List<FileContainer> getFiles() {
return files;
}
2019-04-17 06:45:15 +00:00
public static ClassNode getClassNode(FileContainer container, String name) {
for (ClassNode c : container.classes)
if (c.name.equals(name))
return c;
return null;
}
2018-01-31 15:41:24 +00:00
/**
* Grabs the file contents of the loaded resources.
*
* @param name the file name
* @return the file contents as a byte[]
*/
public static byte[] getFileContents(String name) {
for (FileContainer container : files) {
HashMap<String, byte[]> files = container.files;
if (files.containsKey(name))
return files.get(name);
}
return null;
}
2021-06-26 12:38:58 +00:00
/**
* Grab the byte array from the loaded Class object
*
* @param clazz
* @return
* @throws IOException
*/
public static byte[] getClassFile(Class<?> clazz) throws IOException {
try (InputStream is = clazz.getResourceAsStream(
"/" + clazz.getName().replace('.', '/') + ".class");
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
int r;
byte[] buffer = new byte[8192];
while ((r = Objects.requireNonNull(is).read(buffer)) >= 0) {
baos.write(buffer, 0, r);
}
return baos.toByteArray();
}
}
2018-01-31 15:41:24 +00:00
/**
* Replaces an old node with a new instance
*
* @param oldNode the old instance
* @param newNode the new instance
*/
public static void updateNode(ClassNode oldNode, ClassNode newNode) {
for (FileContainer container : files) {
if (container.classes.remove(oldNode))
container.classes.add(newNode);
}
}
/**
* Gets all of the loaded classes as an array list
*
* @return the loaded classes as an array list
*/
public static ArrayList<ClassNode> getLoadedClasses() {
2021-04-12 22:31:22 +00:00
ArrayList<ClassNode> a = new ArrayList<>();
2018-01-31 15:41:24 +00:00
for (FileContainer container : files)
for (ClassNode c : container.classes)
if (!a.contains(c))
a.add(c);
return a;
}
/**
* Compile all of the compilable panes that're opened.
*
* @param message if it should send a message saying it's compiled sucessfully.
* @return true if no errors, false if it failed to compile.
*/
public static boolean compile(boolean message) {
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(true);
2018-01-31 15:41:24 +00:00
boolean actuallyTried = false;
for (java.awt.Component c : BytecodeViewer.viewer.workPane.getLoadedViewers())
{
if (c instanceof ClassViewer)
{
2018-01-31 15:41:24 +00:00
ClassViewer cv = (ClassViewer) c;
//compile smali assembly
if (cv.resourceViewPanel1.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel1.textArea.isEditable() ||
cv.resourceViewPanel2.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel2.textArea.isEditable() ||
cv.resourceViewPanel3.compileMode == ResourcePanelCompileMode.SMALI_ASSEMBLY && cv.resourceViewPanel3.textArea.isEditable())
{
2018-01-31 15:41:24 +00:00
actuallyTried = true;
2021-04-12 20:19:12 +00:00
Object[] smali = cv.getSmali();
if (smali != null)
{
2018-01-31 15:41:24 +00:00
ClassNode origNode = (ClassNode) smali[0];
String smaliText = (String) smali[1];
byte[] smaliCompiled = Compilers.smali.compile(smaliText, origNode.name);
if (smaliCompiled != null)
{
2021-04-12 20:19:12 +00:00
try {
2019-05-06 00:22:31 +00:00
ClassNode newNode = JarUtils.getNode(smaliCompiled);
BytecodeViewer.updateNode(origNode, newNode);
2021-04-12 20:19:12 +00:00
} catch (Exception e) {
2019-05-06 00:22:31 +00:00
e.printStackTrace();
}
}
else
{
2021-04-12 20:19:12 +00:00
BytecodeViewer.showMessage("There has been an error with assembling your Smali code, "
+ "please check this. Class: " + origNode.name);
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(false);
2018-01-31 15:41:24 +00:00
return false;
}
}
}
//compile krakatau assembly
if (cv.resourceViewPanel1.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel1.textArea.isEditable() ||
cv.resourceViewPanel2.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel2.textArea.isEditable() ||
cv.resourceViewPanel3.compileMode == ResourcePanelCompileMode.KRAKATAU_ASSEMBLY && cv.resourceViewPanel3.textArea.isEditable())
{
2018-01-31 15:41:24 +00:00
actuallyTried = true;
2021-04-12 20:19:12 +00:00
Object[] krakatau = cv.getKrakatau();
if (krakatau != null)
{
2018-01-31 15:41:24 +00:00
ClassNode origNode = (ClassNode) krakatau[0];
String krakatauText = (String) krakatau[1];
byte[] krakatauCompiled = Compilers.krakatau.compile(krakatauText, origNode.name);
if (krakatauCompiled != null)
{
2021-04-12 20:19:12 +00:00
try {
2019-05-06 00:22:31 +00:00
ClassNode newNode = JarUtils.getNode(krakatauCompiled);
BytecodeViewer.updateNode(origNode, newNode);
2021-04-12 20:19:12 +00:00
} catch (Exception e) {
2019-05-06 00:22:31 +00:00
e.printStackTrace();
}
}
else
{
2021-04-12 20:19:12 +00:00
BytecodeViewer.showMessage("There has been an error with assembling your Krakatau "
+ "Bytecode, please check this. Class: " + origNode.name);
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(false);
2018-01-31 15:41:24 +00:00
return false;
}
}
}
//default to java compiling
if (cv.resourceViewPanel1.textArea != null && cv.resourceViewPanel1.textArea.isEditable() ||
cv.resourceViewPanel2.textArea != null && cv.resourceViewPanel2.textArea.isEditable() ||
cv.resourceViewPanel3.textArea != null && cv.resourceViewPanel3.textArea.isEditable())
{
2018-01-31 15:41:24 +00:00
actuallyTried = true;
2021-04-12 20:19:12 +00:00
Object[] java = cv.getJava();
2018-01-31 15:41:24 +00:00
if (java != null) {
ClassNode origNode = (ClassNode) java[0];
String javaText = (String) java[1];
SystemErrConsole errConsole = new SystemErrConsole("Java Compile Issues");
2021-04-12 20:19:12 +00:00
errConsole.setText("Error compiling class: " + origNode.name + nl + "Keep in mind most "
+ "decompilers cannot produce compilable classes" + nl + nl);
2018-01-31 15:41:24 +00:00
byte[] javaCompiled = Compilers.java.compile(javaText, origNode.name);
if (javaCompiled != null)
{
2021-04-12 20:19:12 +00:00
try {
2019-05-06 00:22:31 +00:00
ClassNode newNode = JarUtils.getNode(javaCompiled);
BytecodeViewer.updateNode(origNode, newNode);
2021-04-12 20:19:12 +00:00
} catch (Exception e) {
2019-05-06 00:22:31 +00:00
e.printStackTrace();
}
2018-01-31 15:41:24 +00:00
errConsole.finished();
}
else
{
2018-01-31 15:41:24 +00:00
errConsole.pretty();
errConsole.setVisible(true);
errConsole.finished();
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(false);
2018-01-31 15:41:24 +00:00
return false;
}
}
}
}
}
if (message)
if (actuallyTried)
BytecodeViewer.showMessage("Compiled Successfully.");
else
BytecodeViewer.showMessage("You have no editable panes opened, make one editable and try again.");
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(false);
2018-01-31 15:41:24 +00:00
return true;
}
/**
* Opens a file, optional if it should append to the recent files menu
*
* @param files the file(s) you wish to open
* @param recentFiles if it should append to the recent files menu
*/
public static void openFiles(final File[] files, boolean recentFiles) {
if (recentFiles)
for (File f : files)
if (f.exists())
2021-06-26 05:25:50 +00:00
Constants.addRecentFile(f);
2018-01-31 15:41:24 +00:00
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(true);
Configuration.needsReDump = true;
Thread t = new Thread(new OpenFile(files));
2018-01-31 15:41:24 +00:00
t.start();
}
/**
* Starts the specified plugin
*
* @param file the file of the plugin
*/
public static void startPlugin(File file) {
if (!file.exists())
return;
try {
PluginManager.runPlugin(file);
} catch (Throwable e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
addRecentPlugin(file);
}
/**
* Send a message to alert the user
*
* @param message the message you need to send
*/
public static void showMessage(String message) {
JOptionPane.showMessageDialog(viewer, message);
}
/**
* Resets the workspace with optional user input required
*
* @param ask if should require user input or not
*/
2021-04-12 20:19:12 +00:00
public static void resetWorkSpace(boolean ask) {
if (ask) {
2018-01-31 15:41:24 +00:00
JOptionPane pane = new JOptionPane(
2021-04-12 20:19:12 +00:00
"Are you sure you want to reset the workspace?\n\rIt will also reset your file navigator and "
+ "search.");
2018-01-31 15:41:24 +00:00
Object[] options = new String[]{"Yes", "No"};
pane.setOptions(options);
JDialog dialog = pane.createDialog(viewer,
"Bytecode Viewer - Reset Workspace");
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;
2019-04-17 06:45:15 +00:00
if (result != 0)
return;
2018-01-31 15:41:24 +00:00
}
2019-04-17 06:45:15 +00:00
files.clear();
LazyNameUtil.reset();
2021-06-21 22:45:00 +00:00
Objects.requireNonNull(MainViewerGUI.getComponent(ResourceListPane.class)).resetWorkspace();
Objects.requireNonNull(MainViewerGUI.getComponent(WorkPaneMainComponent.class)).resetWorkspace();
2021-06-21 22:45:00 +00:00
Objects.requireNonNull(MainViewerGUI.getComponent(SearchBoxPane.class)).resetWorkspace();
2019-04-17 06:45:15 +00:00
the.bytecode.club.bytecodeviewer.api.BytecodeViewer.getClassNodeLoader().clear();
2018-01-31 15:41:24 +00:00
}
/**
* Clears the temp directory
*/
public static void cleanup() {
2021-04-12 22:31:22 +00:00
File tempF = new File(tempDirectory);
2018-01-31 15:41:24 +00:00
try {
FileUtils.deleteDirectory(tempF);
2021-04-12 22:31:22 +00:00
} catch (Exception ignored) {
2018-01-31 15:41:24 +00:00
}
while (!tempF.exists()) // keep making dirs
tempF.mkdir();
}
public static void refreshAllTabTitles()
{
for(int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
ResourceViewer viewer = ((TabbedPane) BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i)).resource;
viewer.refreshTitle();
}
}
2018-01-31 15:41:24 +00:00
/**
* Checks the hotkeys
*
* @param e
*/
public static void checkHotKey(KeyEvent e) {
if (System.currentTimeMillis() - Configuration.lastHotKeyExecuted <= (4000))
2018-01-31 15:41:24 +00:00
return;
if ((e.getKeyCode() == KeyEvent.VK_O) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
Configuration.lastHotKeyExecuted = System.currentTimeMillis();
2021-06-26 12:38:58 +00:00
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
"Select File or Folder to open in BCV",
"APKs, DEX, Class Files or Zip/Jar/War Archives",
Constants.SUPPORTED_FILE_EXTENSIONS);
2018-01-31 15:41:24 +00:00
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION) {
Configuration.lastDirectory = fc.getSelectedFile().getAbsolutePath();
2018-01-31 15:41:24 +00:00
try {
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(true);
2018-01-31 15:41:24 +00:00
BytecodeViewer.openFiles(new File[]{fc.getSelectedFile()}, true);
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(false);
2018-01-31 15:41:24 +00:00
} catch (Exception e1) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e1);
}
}
} else if ((e.getKeyCode() == KeyEvent.VK_N) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
Configuration.lastHotKeyExecuted = System.currentTimeMillis();
2018-01-31 15:41:24 +00:00
BytecodeViewer.resetWorkSpace(true);
} else if ((e.getKeyCode() == KeyEvent.VK_T) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
Configuration.lastHotKeyExecuted = System.currentTimeMillis();
2021-04-12 22:31:22 +00:00
Thread t = new Thread(() -> BytecodeViewer.compile(true));
2018-01-31 15:41:24 +00:00
t.start();
} else if ((e.getKeyCode() == KeyEvent.VK_R) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
Configuration.lastHotKeyExecuted = System.currentTimeMillis();
2018-01-31 15:41:24 +00:00
if (BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
new RunOptions().setVisible(true);
} else if ((e.getKeyCode() == KeyEvent.VK_S) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
Configuration.lastHotKeyExecuted = System.currentTimeMillis();
2018-01-31 15:41:24 +00:00
if (BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
2021-04-12 22:31:22 +00:00
Thread t = new Thread(() -> {
if (viewer.compileOnSave.isSelected() && !BytecodeViewer.compile(false))
return;
2021-06-26 12:38:58 +00:00
JFileChooser fc = new FileChooser(new File(Configuration.lastDirectory),
"Select Zip Export",
"Zip Archives",
"zip");
2021-04-12 22:31:22 +00:00
int returnVal = fc.showSaveDialog(viewer);
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;
2018-01-31 15:41:24 +00:00
}
2021-04-12 22:31:22 +00:00
}
2018-01-31 15:41:24 +00:00
2021-04-12 22:31:22 +00:00
final File file2 = file;
2018-01-31 15:41:24 +00:00
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(true);
2021-04-12 22:31:22 +00:00
Thread t1 = new Thread(() -> {
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(),
file2.getAbsolutePath());
2021-06-21 23:37:55 +00:00
BytecodeViewer.viewer.updateBusyStatus(false);
2021-04-12 22:31:22 +00:00
});
t1.start();
2018-01-31 15:41:24 +00:00
}
2021-04-12 22:31:22 +00:00
});
2018-01-31 15:41:24 +00:00
t.start();
} else if ((e.getKeyCode() == KeyEvent.VK_W) && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
Configuration.lastHotKeyExecuted = System.currentTimeMillis();
2018-01-31 15:41:24 +00:00
if (viewer.workPane.getCurrentViewer() != null)
viewer.workPane.tabs.remove(viewer.workPane.getCurrentViewer());
}
}
2021-06-26 12:38:58 +00:00
public static File[] dumpTempFile(FileContainer container)
{
2019-04-17 06:45:15 +00:00
File[] files = new File[2];
2021-06-26 12:38:58 +00:00
2019-04-17 06:45:15 +00:00
//currently won't optimize if you've got two containers with the same name, will need to add this later
2021-04-12 20:19:12 +00:00
if (!LazyNameUtil.SAME_NAME_JAR_WORKSPACE) {
if (Configuration.krakatauTempJar != null && !Configuration.krakatauTempJar.exists()) {
Configuration.needsReDump = true;
2019-04-17 06:45:15 +00:00
}
if (Configuration.needsReDump && Configuration.krakatauTempJar != null) {
Configuration.krakatauTempDir = null;
Configuration.krakatauTempJar = null;
2019-04-17 06:45:15 +00:00
}
2019-04-13 19:30:03 +00:00
2019-04-17 06:45:15 +00:00
boolean passes = false;
if (BytecodeViewer.viewer.viewPane1.getGroup().isSelected(BytecodeViewer.viewer.viewPane1.getKrakatau().getJava().getModel()))
2019-04-17 06:45:15 +00:00
passes = true;
else if (BytecodeViewer.viewer.viewPane1.getGroup().isSelected(BytecodeViewer.viewer.viewPane1.getKrakatau().getBytecode().getModel()))
2019-04-17 06:45:15 +00:00
passes = true;
else if (BytecodeViewer.viewer.viewPane2.getGroup().isSelected(BytecodeViewer.viewer.viewPane2.getKrakatau().getJava().getModel()))
2019-04-17 06:45:15 +00:00
passes = true;
else if (BytecodeViewer.viewer.viewPane2.getGroup().isSelected(BytecodeViewer.viewer.viewPane2.getKrakatau().getBytecode().getModel()))
2019-04-17 06:45:15 +00:00
passes = true;
else if (BytecodeViewer.viewer.viewPane3.getGroup().isSelected(BytecodeViewer.viewer.viewPane3.getKrakatau().getJava().getModel()))
2019-04-17 06:45:15 +00:00
passes = true;
else if (BytecodeViewer.viewer.viewPane3.getGroup().isSelected(BytecodeViewer.viewer.viewPane3.getKrakatau().getBytecode().getModel()))
2019-04-17 06:45:15 +00:00
passes = true;
if (Configuration.krakatauTempJar != null || !passes) {
files[0] = Configuration.krakatauTempJar;
files[1] = Configuration.krakatauTempDir;
2019-04-17 06:45:15 +00:00
return files;
}
}
Configuration.currentlyDumping = true;
Configuration.needsReDump = false;
Configuration.krakatauTempDir = new File(tempDirectory + fs + MiscUtils.randomString(32) + fs);
Configuration.krakatauTempDir.mkdir();
Configuration.krakatauTempJar = new File(tempDirectory + fs + "temp" + MiscUtils.randomString(32) + ".jar");
2021-04-12 22:31:22 +00:00
//krakatauTempJar = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + "temp" + MiscUtils
// .randomString(32) + ".jar."+container.name);
JarUtils.saveAsJarClassesOnly(container.classes, Configuration.krakatauTempJar.getAbsolutePath());
Configuration.currentlyDumping = false;
2021-04-12 22:31:22 +00:00
files[0] = Configuration.krakatauTempJar;
files[1] = Configuration.krakatauTempDir;
2019-04-17 06:45:15 +00:00
return files;
}
2021-04-12 20:19:12 +00:00
public synchronized static void rtCheck() {
if (Configuration.rt.isEmpty()) {
2021-04-12 20:19:12 +00:00
if (RT_JAR.exists()) {
Configuration.rt = RT_JAR.getAbsolutePath();
2021-04-12 20:19:12 +00:00
} else if (RT_JAR_DUMPED.exists()) {
Configuration.rt = RT_JAR_DUMPED.getAbsolutePath();
2021-04-12 20:19:12 +00:00
} else {
try {
2019-06-01 01:04:07 +00:00
JRTExtractor.extractRT(RT_JAR_DUMPED.getAbsolutePath());
Configuration.rt = RT_JAR_DUMPED.getAbsolutePath();
2021-04-12 20:19:12 +00:00
} catch (Throwable t) {
2019-06-01 01:04:07 +00:00
t.printStackTrace();
}
}
}
}
}