diff --git a/src/main/java/me/konloch/kontainer/io/DiskWriter.java b/src/main/java/me/konloch/kontainer/io/DiskWriter.java index e3180ff0..eda3442b 100644 --- a/src/main/java/me/konloch/kontainer/io/DiskWriter.java +++ b/src/main/java/me/konloch/kontainer/io/DiskWriter.java @@ -1,9 +1,6 @@ package me.konloch.kontainer.io; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.PrintWriter; +import java.io.*; import java.util.Arrays; /** @@ -132,13 +129,13 @@ public class DiskWriter { * @param fileContents * @param debug */ - public static synchronized void replaceFile(String filename, - byte[] fileContents, boolean debug) { + public static synchronized void replaceFileBytes(String filename, + byte[] fileContents, boolean debug) { new File(filename).getParentFile().mkdirs(); File f = new File(filename); if (f.exists()) f.delete(); - PrintWriter writer = null; + String original = filename; int counter = 0; @@ -146,10 +143,10 @@ public class DiskWriter { int failSafe = 0; while (!saved && failSafe++ <= 42069) { - try { - writer = new PrintWriter(new BufferedWriter(new FileWriter( - filename, true))); - writer.println(Arrays.toString(fileContents)); + try(FileOutputStream stream = new FileOutputStream(new File(filename))) + { + stream.write(fileContents); + stream.flush(); if (debug) System.out.println("Saved " + filename + " to disk"); saved = true; @@ -164,7 +161,6 @@ public class DiskWriter { counter++; } } - writer.close(); } /** diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/Configuration.java b/src/main/java/the/bytecode/club/bytecodeviewer/Configuration.java index 84641403..586d5949 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/Configuration.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/Configuration.java @@ -15,11 +15,14 @@ import java.io.File; public class Configuration { public static String python2 = ""; + public static String python2Extra = ""; public static String python3 = ""; + public static String python3Extra = ""; public static String rt = ""; public static String library = ""; public static String javac = ""; public static String java = ""; + public static String javaTools = ""; public static File krakatauTempDir; public static File krakatauTempJar; public static boolean displayParentInTab = false; //also change in the main GUI diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/SettingsSerializer.java b/src/main/java/the/bytecode/club/bytecodeviewer/SettingsSerializer.java index 05287657..8147c65b 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/SettingsSerializer.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/SettingsSerializer.java @@ -191,6 +191,8 @@ public class SettingsSerializer save(BytecodeViewer.viewer.viewPane1.isPaneEditable()); save(BytecodeViewer.viewer.viewPane2.isPaneEditable()); save(BytecodeViewer.viewer.viewPane3.isPaneEditable()); + + save(Configuration.javaTools); } catch (Exception e) { BytecodeViewer.handleException(e); } @@ -348,6 +350,8 @@ public class SettingsSerializer BytecodeViewer.viewer.viewPane1.setPaneEditable(asBoolean(131)); BytecodeViewer.viewer.viewPane2.setPaneEditable(asBoolean(132)); BytecodeViewer.viewer.viewPane3.setPaneEditable(asBoolean(133)); + + Configuration.javaTools = asString(134); } catch (Exception e) { //ignore because errors are expected, first start up and outdated settings. e.printStackTrace(); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/compilers/impl/KrakatauAssembler.java b/src/main/java/the/bytecode/club/bytecodeviewer/compilers/impl/KrakatauAssembler.java index 84f4bba1..8b6fc3d5 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/compilers/impl/KrakatauAssembler.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/compilers/impl/KrakatauAssembler.java @@ -46,15 +46,8 @@ public class KrakatauAssembler extends InternalCompiler @Override public byte[] compile(String contents, String name) { - if (Configuration.python2.isEmpty()) { - BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); - ExternalResources.getSingleton().selectPython2(); - } - - if (Configuration.python2.isEmpty()) { - BytecodeViewer.showMessage("You need to set Python!"); + if(!ExternalResources.getSingleton().hasSetPythonCommand()) return null; - } String origName = MiscUtils.randomString(20); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/ASMTextifierDecompiler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/ASMTextifierDisassembler.java similarity index 96% rename from src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/ASMTextifierDecompiler.java rename to src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/ASMTextifierDisassembler.java index b8c5a400..e6ddf033 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/ASMTextifierDecompiler.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/ASMTextifierDisassembler.java @@ -30,7 +30,7 @@ import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler; * * @author Thiakil */ -public class ASMTextifierDecompiler extends InternalDecompiler +public class ASMTextifierDisassembler extends InternalDecompiler { @Override public String decompileClassNode(ClassNode cn, byte[] b) { diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JavapDisassembler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JavapDisassembler.java new file mode 100644 index 00000000..b8721088 --- /dev/null +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/JavapDisassembler.java @@ -0,0 +1,106 @@ +package the.bytecode.club.bytecodeviewer.decompilers.impl; + +import me.konloch.kontainer.io.DiskReader; +import me.konloch.kontainer.io.DiskWriter; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.util.Textifier; +import org.objectweb.asm.util.TraceClassVisitor; +import the.bytecode.club.bytecodeviewer.Configuration; +import the.bytecode.club.bytecodeviewer.Constants; +import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler; +import the.bytecode.club.bytecodeviewer.gui.components.SystemConsole; +import the.bytecode.club.bytecodeviewer.resources.ExternalResources; +import the.bytecode.club.bytecodeviewer.util.MiscUtils; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +import static the.bytecode.club.bytecodeviewer.Constants.fs; +import static the.bytecode.club.bytecodeviewer.api.ExceptionUI.KONLOCH; + +/*************************************************************************** + * 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 . * + ***************************************************************************/ + +/** + * Javap disassembler + * + * https://github.com/Konloch/bytecode-viewer/issues/93 + * + * @author Konloch + * @since 07/11/2021 + */ + +public class JavapDisassembler extends InternalDecompiler +{ + @Override + public String decompileClassNode(ClassNode cn, byte[] b) + { + if(!ExternalResources.getSingleton().hasJavaToolsSet()) + return "Set Java Tools Path!"; + + final File tempDirectory = new File(Constants.tempDirectory + fs + MiscUtils.randomString(32) + fs); + tempDirectory.mkdir(); + final File tempClass = new File(Constants.tempDirectory + fs + "temp" + MiscUtils.randomString(32) + ".class"); + + DiskWriter.replaceFileBytes(tempClass.getAbsolutePath(), b, false); + + SystemConsole sysOut = null; + try + { + URLClassLoader child = new URLClassLoader( + new URL[] {new File(Configuration.javaTools).toURI().toURL()}, + this.getClass().getClassLoader() + ); + //Class javap = child.loadClass("com.sun.tools.javap.Main"); + Class javap = Class.forName("com.sun.tools.javap.Main", true, child); + Method main = javap.getMethod("main", String[].class); + Object cl = javap.newInstance(); + + //pipe sys out + sysOut = new SystemConsole(""); + + //invoke Javap + main.invoke(cl, (Object) new String[]{"-c", "-l", "-constants", tempClass.getAbsolutePath()}); + } + catch (Exception e) + { + e.printStackTrace(); + } + finally + { + tempClass.delete(); + } + + if(sysOut != null) + { + sysOut.finished(); + return sysOut.getTextAreaOutputStreamOut().getBuffer().toString(); + } + + return KONLOCH; + } + + @Override + public void decompileToZip(String sourceJar, String zipName) { + + } +} diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/KrakatauDecompiler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/KrakatauDecompiler.java index 9c291717..269ae7ef 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/KrakatauDecompiler.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/KrakatauDecompiler.java @@ -149,21 +149,15 @@ public class KrakatauDecompiler extends InternalDecompiler @Override public String decompileClassNode(ClassNode cn, byte[] b) { - if (Configuration.python2.isEmpty()) { - BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); - ExternalResources.getSingleton().selectPython2(); - } + if(!ExternalResources.getSingleton().hasSetPythonCommand()) + return "You need to set your Python 2.7 path!"; + if (Configuration.rt.isEmpty()) { BytecodeViewer.showMessage("You need to set your JRE RT Library." + "\r\n(C:\\Program Files (x86)\\Java\\jre7\\lib\\rt.jar)"); ExternalResources.getSingleton().selectJRERTLibrary(); } - if (Configuration.python2.isEmpty()) { - BytecodeViewer.showMessage("You need to set Python!"); - return "Set your paths"; - } - if (Configuration.rt.isEmpty()) { BytecodeViewer.showMessage("You need to set RT.jar!"); return "Set your paths"; @@ -240,10 +234,8 @@ public class KrakatauDecompiler extends InternalDecompiler @Override public void decompileToZip(String sourceJar, String zipName) { - if (Configuration.python2.isEmpty()) { - BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); - ExternalResources.getSingleton().selectPython2(); - } + if(!ExternalResources.getSingleton().hasSetPythonCommand()) + return; ExternalResources.getSingleton().rtCheck(); if (Configuration.rt.isEmpty()) { diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/KrakatauDisassembler.java b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/KrakatauDisassembler.java index a9419468..7fc67bc5 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/KrakatauDisassembler.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/decompilers/impl/KrakatauDisassembler.java @@ -47,15 +47,8 @@ public class KrakatauDisassembler extends InternalDecompiler { @Override public String decompileClassNode(ClassNode cn, byte[] b) { - if (Configuration.python2.isEmpty()) { - BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); - ExternalResources.getSingleton().selectPython2(); - } - - if (Configuration.python2.isEmpty()) { - BytecodeViewer.showMessage("You need to set Python!"); - return "Set your paths"; - } + if(!ExternalResources.getSingleton().hasSetPythonCommand()) + return "You need to set your Python 2.7 path!"; String s = "Bytecode Viewer Version: " + VERSION + nl + nl + "Please send this to konloch@gmail.com. " + nl + nl; @@ -120,10 +113,8 @@ public class KrakatauDisassembler extends InternalDecompiler @Override public void decompileToZip(String sourceJar, String zipName) { - if (Configuration.python2.isEmpty()) { - BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); - ExternalResources.getSingleton().selectPython2(); - } + if(!ExternalResources.getSingleton().hasSetPythonCommand()) + return; String ran = MiscUtils.randomString(32); final File tempDirectory = new File(Constants.tempDirectory + fs + ran + fs); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/JFrameConsolePrintStream.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/JFrameConsolePrintStream.java index 37cadc35..99c67189 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/JFrameConsolePrintStream.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/JFrameConsolePrintStream.java @@ -83,6 +83,16 @@ public class JFrameConsolePrintStream extends JFrameConsole System.setOut(Constants.OUT); } + public JTextAreaOutputStream getTextAreaOutputStreamErr() + { + return textAreaOutputStreamErr; + } + + public JTextAreaOutputStream getTextAreaOutputStreamOut() + { + return textAreaOutputStreamOut; + } + private void update() { if(System.currentTimeMillis()-lastUpdate <= 50) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/JTextAreaOutputStream.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/JTextAreaOutputStream.java index 02ed534d..4897b261 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/JTextAreaOutputStream.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/JTextAreaOutputStream.java @@ -55,4 +55,9 @@ public class JTextAreaOutputStream extends OutputStream sb.append((char) b); og.write(b); } + + public StringBuilder getBuffer() + { + return sb; + } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/DecompilerSelectionPane.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/DecompilerSelectionPane.java index 9d32aee1..d06b3501 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/DecompilerSelectionPane.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/DecompilerSelectionPane.java @@ -37,11 +37,12 @@ public class DecompilerSelectionPane private final DecompilerViewComponent smali = new DecompilerViewComponent("Smali", BYTECODE, Decompiler.SMALI_DISASSEMBLER); private final DecompilerViewComponent bytecode = new DecompilerViewComponent("Bytecode", BYTECODE_NON_EDITABLE, Decompiler.BYTECODE_DISASSEMBLER); private final DecompilerViewComponent asmTextify = new DecompilerViewComponent("ASM Textify", BYTECODE_NON_EDITABLE, Decompiler.ASM_TEXTIFY_DISASSEMBLER); + private final DecompilerViewComponent javap = new DecompilerViewComponent("JavaP", BYTECODE_NON_EDITABLE, Decompiler.JAVAP_DISASSEMBLER); //TODO when adding new decompilers insert the DecompilerViewComponent object into here // also in the group, then finally the build menu public List components = new ArrayList<>(Arrays.asList( - procyon, CFR, JADX, JD, fern, krakatau, smali, bytecode, asmTextify)); + procyon, CFR, JADX, JD, fern, krakatau, smali, bytecode, asmTextify, javap)); public DecompilerSelectionPane(int paneID) { @@ -82,16 +83,8 @@ public class DecompilerSelectionPane { //build the radiobutton group group.add(none); - procyon.addToGroup(group); - CFR.addToGroup(group); - JADX.addToGroup(group); - JD.addToGroup(group); - fern.addToGroup(group); - krakatau.addToGroup(group); - smali.addToGroup(group); - bytecode.addToGroup(group); - asmTextify.addToGroup(group); group.add(hexcode); + components.forEach(decompilerViewComponent -> decompilerViewComponent.addToGroup(group)); //build the action commands none.setActionCommand(Decompiler.NONE.name()); @@ -137,6 +130,7 @@ public class DecompilerSelectionPane menu.add(smali.getMenu()); menu.add(new JSeparator()); menu.add(bytecode.getMenu()); + menu.add(javap.getMenu()); menu.add(asmTextify.getMenu()); menu.add(new JSeparator()); menu.add(hexcode); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/ExternalResources.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/ExternalResources.java index e95853d6..2f9bdde0 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/ExternalResources.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/ExternalResources.java @@ -6,7 +6,10 @@ import the.bytecode.club.bytecodeviewer.SettingsSerializer; import the.bytecode.club.bytecodeviewer.util.DialogueUtils; import the.bytecode.club.bytecodeviewer.util.JRTExtractor; +import java.io.BufferedReader; import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; import java.util.Objects; import static the.bytecode.club.bytecodeviewer.Constants.*; @@ -83,6 +86,37 @@ public class ExternalResources return Configuration.java; } + /** + * Check if the python command has been set + */ + public boolean hasJavaToolsSet() + { + return !getJavaTools(false).isEmpty(); + } + + /** + * Auto-detect the Java command + */ + public String getJavaTools(boolean blockTillSelected) + { + boolean empty = Configuration.javaTools.isEmpty(); + + if(!empty) + return Configuration.javaTools; + + //TODO auto-detect the Java path + boolean block = true; + while (Configuration.javaTools.isEmpty() && block) + { + BytecodeViewer.showMessage("You need to set your Java Tools path, this requires the JDK to be downloaded." + + nl + "(C:/Program Files/Java/JDK_xx/lib/tools.jar)"); + ExternalResources.getSingleton().selectJavaTools(); + block = !blockTillSelected; //signal block flag off + } + + return Configuration.javaTools; + } + /** * Check if the python command has been set */ @@ -106,12 +140,18 @@ public class ExternalResources //check using python CLI flag try { - //TODO read the version output to verify python 2 + //read the version output to verify python 2 ProcessBuilder pb = new ProcessBuilder("python", "-2", "--version"); - pb.start(); + Process p = pb.start(); + p.waitFor(); - Configuration.python2 = "python -2"; //python is set - return Configuration.python2; + //set python path + if(readProcess(p).toLowerCase().contains("python 2")) + { + Configuration.python2 = "python"; + Configuration.python2Extra = "-2"; + return Configuration.python2; + } } catch (Exception e) { } //ignore finally @@ -122,12 +162,17 @@ public class ExternalResources //check if 'python' command is bound as python 2.X try { - //TODO read the version output to verify python 2 + //read the version output to verify python 2 ProcessBuilder pb = new ProcessBuilder("python", "--version"); - pb.start(); + Process p = pb.start(); + p.waitFor(); - Configuration.python2 = "python"; //python is set - return Configuration.python2; + //set python path + if(readProcess(p).toLowerCase().contains("python 2")) + { + Configuration.python2 = "python"; + return Configuration.python2; + } } catch (Exception e) { } //ignore finally @@ -217,6 +262,19 @@ public class ExternalResources SettingsSerializer.saveSettingsAsync(); } + public void selectJavaTools() + { + final File file = DialogueUtils.fileChooser("Select Java Tools Jar", + "Java Tools Jar (Inside Of JDK 'C:/Program Files/Java/JDK_xx/lib/tools.jar')", + "everything"); + + if(file == null) + return; + + Configuration.javaTools = file.getAbsolutePath(); + SettingsSerializer.saveSettingsAsync(); + } + public void selectOptionalLibraryFolder() { final File file = DialogueUtils.fileChooser("Select Library Folder", @@ -278,4 +336,21 @@ public class ExternalResources return null; } + + /** + * @author https://stackoverflow.com/a/16714180 + */ + public String readProcess(Process process) throws IOException + { + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) + { + builder.append(line); + builder.append(System.getProperty("line.separator")); + } + + return builder.toString(); + } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/importing/impl/XAPKResourceImporter.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/importing/impl/XAPKResourceImporter.java index f0c4bfc6..61d02f79 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/importing/impl/XAPKResourceImporter.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/importing/impl/XAPKResourceImporter.java @@ -78,7 +78,7 @@ public class XAPKResourceImporter implements Importer public File exportTo(File original, String extension, byte[] bytes) { File file = new File(original.getAbsolutePath() + extension); - DiskWriter.replaceFile(file.getAbsolutePath(), bytes, false); + DiskWriter.replaceFileBytes(file.getAbsolutePath(), bytes, false); return file; } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/util/JarUtils.java b/src/main/java/the/bytecode/club/bytecodeviewer/util/JarUtils.java index 922f6f34..e1f97114 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/util/JarUtils.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/util/JarUtils.java @@ -335,7 +335,7 @@ public class JarUtils File f = new File(name); f.mkdirs(); - DiskWriter.replaceFile(name, cw.toByteArray(), false); + DiskWriter.replaceFileBytes(name, cw.toByteArray(), false); } } catch (Exception e) { BytecodeViewer.handleException(e);