Added Javap Disassembler

Solves #93
This commit is contained in:
Konloch 2021-07-11 09:41:33 -07:00
parent 46bee607aa
commit 7d09510194
14 changed files with 236 additions and 67 deletions

View file

@ -1,9 +1,6 @@
package me.konloch.kontainer.io; package me.konloch.kontainer.io;
import java.io.BufferedWriter; import java.io.*;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
/** /**
@ -132,13 +129,13 @@ public class DiskWriter {
* @param fileContents * @param fileContents
* @param debug * @param debug
*/ */
public static synchronized void replaceFile(String filename, public static synchronized void replaceFileBytes(String filename,
byte[] fileContents, boolean debug) { byte[] fileContents, boolean debug) {
new File(filename).getParentFile().mkdirs(); new File(filename).getParentFile().mkdirs();
File f = new File(filename); File f = new File(filename);
if (f.exists()) if (f.exists())
f.delete(); f.delete();
PrintWriter writer = null;
String original = filename; String original = filename;
int counter = 0; int counter = 0;
@ -146,10 +143,10 @@ public class DiskWriter {
int failSafe = 0; int failSafe = 0;
while (!saved && failSafe++ <= 42069) while (!saved && failSafe++ <= 42069)
{ {
try { try(FileOutputStream stream = new FileOutputStream(new File(filename)))
writer = new PrintWriter(new BufferedWriter(new FileWriter( {
filename, true))); stream.write(fileContents);
writer.println(Arrays.toString(fileContents)); stream.flush();
if (debug) if (debug)
System.out.println("Saved " + filename + " to disk"); System.out.println("Saved " + filename + " to disk");
saved = true; saved = true;
@ -164,7 +161,6 @@ public class DiskWriter {
counter++; counter++;
} }
} }
writer.close();
} }
/** /**

View file

@ -15,11 +15,14 @@ import java.io.File;
public class Configuration public class Configuration
{ {
public static String python2 = ""; public static String python2 = "";
public static String python2Extra = "";
public static String python3 = ""; public static String python3 = "";
public static String python3Extra = "";
public static String rt = ""; public static String rt = "";
public static String library = ""; public static String library = "";
public static String javac = ""; public static String javac = "";
public static String java = ""; public static String java = "";
public static String javaTools = "";
public static File krakatauTempDir; public static File krakatauTempDir;
public static File krakatauTempJar; public static File krakatauTempJar;
public static boolean displayParentInTab = false; //also change in the main GUI public static boolean displayParentInTab = false; //also change in the main GUI

View file

@ -191,6 +191,8 @@ public class SettingsSerializer
save(BytecodeViewer.viewer.viewPane1.isPaneEditable()); save(BytecodeViewer.viewer.viewPane1.isPaneEditable());
save(BytecodeViewer.viewer.viewPane2.isPaneEditable()); save(BytecodeViewer.viewer.viewPane2.isPaneEditable());
save(BytecodeViewer.viewer.viewPane3.isPaneEditable()); save(BytecodeViewer.viewer.viewPane3.isPaneEditable());
save(Configuration.javaTools);
} catch (Exception e) { } catch (Exception e) {
BytecodeViewer.handleException(e); BytecodeViewer.handleException(e);
} }
@ -348,6 +350,8 @@ public class SettingsSerializer
BytecodeViewer.viewer.viewPane1.setPaneEditable(asBoolean(131)); BytecodeViewer.viewer.viewPane1.setPaneEditable(asBoolean(131));
BytecodeViewer.viewer.viewPane2.setPaneEditable(asBoolean(132)); BytecodeViewer.viewer.viewPane2.setPaneEditable(asBoolean(132));
BytecodeViewer.viewer.viewPane3.setPaneEditable(asBoolean(133)); BytecodeViewer.viewer.viewPane3.setPaneEditable(asBoolean(133));
Configuration.javaTools = asString(134);
} catch (Exception e) { } catch (Exception e) {
//ignore because errors are expected, first start up and outdated settings. //ignore because errors are expected, first start up and outdated settings.
e.printStackTrace(); e.printStackTrace();

View file

@ -46,15 +46,8 @@ public class KrakatauAssembler extends InternalCompiler
@Override @Override
public byte[] compile(String contents, String name) { public byte[] compile(String contents, String name) {
if (Configuration.python2.isEmpty()) { if(!ExternalResources.getSingleton().hasSetPythonCommand())
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 null; return null;
}
String origName = MiscUtils.randomString(20); String origName = MiscUtils.randomString(20);

View file

@ -30,7 +30,7 @@ import the.bytecode.club.bytecodeviewer.decompilers.InternalDecompiler;
* *
* @author Thiakil * @author Thiakil
*/ */
public class ASMTextifierDecompiler extends InternalDecompiler public class ASMTextifierDisassembler extends InternalDecompiler
{ {
@Override @Override
public String decompileClassNode(ClassNode cn, byte[] b) { public String decompileClassNode(ClassNode cn, byte[] b) {

View file

@ -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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
/**
* 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) {
}
}

View file

@ -149,21 +149,15 @@ public class KrakatauDecompiler extends InternalDecompiler
@Override @Override
public String decompileClassNode(ClassNode cn, byte[] b) { public String decompileClassNode(ClassNode cn, byte[] b) {
if (Configuration.python2.isEmpty()) { if(!ExternalResources.getSingleton().hasSetPythonCommand())
BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); return "You need to set your Python 2.7 path!";
ExternalResources.getSingleton().selectPython2();
}
if (Configuration.rt.isEmpty()) { if (Configuration.rt.isEmpty()) {
BytecodeViewer.showMessage("You need to set your JRE RT Library." + BytecodeViewer.showMessage("You need to set your JRE RT Library." +
"\r\n(C:\\Program Files (x86)\\Java\\jre7\\lib\\rt.jar)"); "\r\n(C:\\Program Files (x86)\\Java\\jre7\\lib\\rt.jar)");
ExternalResources.getSingleton().selectJRERTLibrary(); ExternalResources.getSingleton().selectJRERTLibrary();
} }
if (Configuration.python2.isEmpty()) {
BytecodeViewer.showMessage("You need to set Python!");
return "Set your paths";
}
if (Configuration.rt.isEmpty()) { if (Configuration.rt.isEmpty()) {
BytecodeViewer.showMessage("You need to set RT.jar!"); BytecodeViewer.showMessage("You need to set RT.jar!");
return "Set your paths"; return "Set your paths";
@ -240,10 +234,8 @@ public class KrakatauDecompiler extends InternalDecompiler
@Override @Override
public void decompileToZip(String sourceJar, String zipName) { public void decompileToZip(String sourceJar, String zipName) {
if (Configuration.python2.isEmpty()) { if(!ExternalResources.getSingleton().hasSetPythonCommand())
BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); return;
ExternalResources.getSingleton().selectPython2();
}
ExternalResources.getSingleton().rtCheck(); ExternalResources.getSingleton().rtCheck();
if (Configuration.rt.isEmpty()) { if (Configuration.rt.isEmpty()) {

View file

@ -47,15 +47,8 @@ public class KrakatauDisassembler extends InternalDecompiler
{ {
@Override @Override
public String decompileClassNode(ClassNode cn, byte[] b) { public String decompileClassNode(ClassNode cn, byte[] b) {
if (Configuration.python2.isEmpty()) { if(!ExternalResources.getSingleton().hasSetPythonCommand())
BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); return "You need to set your Python 2.7 path!";
ExternalResources.getSingleton().selectPython2();
}
if (Configuration.python2.isEmpty()) {
BytecodeViewer.showMessage("You need to set Python!");
return "Set your paths";
}
String s = "Bytecode Viewer Version: " + VERSION + nl + nl + String s = "Bytecode Viewer Version: " + VERSION + nl + nl +
"Please send this to konloch@gmail.com. " + nl + nl; "Please send this to konloch@gmail.com. " + nl + nl;
@ -120,10 +113,8 @@ public class KrakatauDisassembler extends InternalDecompiler
@Override @Override
public void decompileToZip(String sourceJar, String zipName) { public void decompileToZip(String sourceJar, String zipName) {
if (Configuration.python2.isEmpty()) { if(!ExternalResources.getSingleton().hasSetPythonCommand())
BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path."); return;
ExternalResources.getSingleton().selectPython2();
}
String ran = MiscUtils.randomString(32); String ran = MiscUtils.randomString(32);
final File tempDirectory = new File(Constants.tempDirectory + fs + ran + fs); final File tempDirectory = new File(Constants.tempDirectory + fs + ran + fs);

View file

@ -83,6 +83,16 @@ public class JFrameConsolePrintStream extends JFrameConsole
System.setOut(Constants.OUT); System.setOut(Constants.OUT);
} }
public JTextAreaOutputStream getTextAreaOutputStreamErr()
{
return textAreaOutputStreamErr;
}
public JTextAreaOutputStream getTextAreaOutputStreamOut()
{
return textAreaOutputStreamOut;
}
private void update() private void update()
{ {
if(System.currentTimeMillis()-lastUpdate <= 50) if(System.currentTimeMillis()-lastUpdate <= 50)

View file

@ -55,4 +55,9 @@ public class JTextAreaOutputStream extends OutputStream
sb.append((char) b); sb.append((char) b);
og.write(b); og.write(b);
} }
public StringBuilder getBuffer()
{
return sb;
}
} }

View file

@ -37,11 +37,12 @@ public class DecompilerSelectionPane
private final DecompilerViewComponent smali = new DecompilerViewComponent("Smali", BYTECODE, Decompiler.SMALI_DISASSEMBLER); 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 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 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 //TODO when adding new decompilers insert the DecompilerViewComponent object into here
// also in the group, then finally the build menu // also in the group, then finally the build menu
public List<DecompilerViewComponent> components = new ArrayList<>(Arrays.asList( public List<DecompilerViewComponent> 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) public DecompilerSelectionPane(int paneID)
{ {
@ -82,16 +83,8 @@ public class DecompilerSelectionPane
{ {
//build the radiobutton group //build the radiobutton group
group.add(none); 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); group.add(hexcode);
components.forEach(decompilerViewComponent -> decompilerViewComponent.addToGroup(group));
//build the action commands //build the action commands
none.setActionCommand(Decompiler.NONE.name()); none.setActionCommand(Decompiler.NONE.name());
@ -137,6 +130,7 @@ public class DecompilerSelectionPane
menu.add(smali.getMenu()); menu.add(smali.getMenu());
menu.add(new JSeparator()); menu.add(new JSeparator());
menu.add(bytecode.getMenu()); menu.add(bytecode.getMenu());
menu.add(javap.getMenu());
menu.add(asmTextify.getMenu()); menu.add(asmTextify.getMenu());
menu.add(new JSeparator()); menu.add(new JSeparator());
menu.add(hexcode); menu.add(hexcode);

View file

@ -6,7 +6,10 @@ import the.bytecode.club.bytecodeviewer.SettingsSerializer;
import the.bytecode.club.bytecodeviewer.util.DialogueUtils; import the.bytecode.club.bytecodeviewer.util.DialogueUtils;
import the.bytecode.club.bytecodeviewer.util.JRTExtractor; import the.bytecode.club.bytecodeviewer.util.JRTExtractor;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Objects; import java.util.Objects;
import static the.bytecode.club.bytecodeviewer.Constants.*; import static the.bytecode.club.bytecodeviewer.Constants.*;
@ -83,6 +86,37 @@ public class ExternalResources
return Configuration.java; 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 * Check if the python command has been set
*/ */
@ -106,12 +140,18 @@ public class ExternalResources
//check using python CLI flag //check using python CLI flag
try 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"); ProcessBuilder pb = new ProcessBuilder("python", "-2", "--version");
pb.start(); Process p = pb.start();
p.waitFor();
Configuration.python2 = "python -2"; //python is set //set python path
return Configuration.python2; if(readProcess(p).toLowerCase().contains("python 2"))
{
Configuration.python2 = "python";
Configuration.python2Extra = "-2";
return Configuration.python2;
}
} }
catch (Exception e) { } //ignore catch (Exception e) { } //ignore
finally finally
@ -122,12 +162,17 @@ public class ExternalResources
//check if 'python' command is bound as python 2.X //check if 'python' command is bound as python 2.X
try try
{ {
//TODO read the version output to verify python 2 //read the version output to verify python 2
ProcessBuilder pb = new ProcessBuilder("python", "--version"); ProcessBuilder pb = new ProcessBuilder("python", "--version");
pb.start(); Process p = pb.start();
p.waitFor();
Configuration.python2 = "python"; //python is set //set python path
return Configuration.python2; if(readProcess(p).toLowerCase().contains("python 2"))
{
Configuration.python2 = "python";
return Configuration.python2;
}
} }
catch (Exception e) { } //ignore catch (Exception e) { } //ignore
finally finally
@ -217,6 +262,19 @@ public class ExternalResources
SettingsSerializer.saveSettingsAsync(); 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() public void selectOptionalLibraryFolder()
{ {
final File file = DialogueUtils.fileChooser("Select Library Folder", final File file = DialogueUtils.fileChooser("Select Library Folder",
@ -278,4 +336,21 @@ public class ExternalResources
return null; 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();
}
} }

View file

@ -78,7 +78,7 @@ public class XAPKResourceImporter implements Importer
public File exportTo(File original, String extension, byte[] bytes) public File exportTo(File original, String extension, byte[] bytes)
{ {
File file = new File(original.getAbsolutePath() + extension); File file = new File(original.getAbsolutePath() + extension);
DiskWriter.replaceFile(file.getAbsolutePath(), bytes, false); DiskWriter.replaceFileBytes(file.getAbsolutePath(), bytes, false);
return file; return file;
} }
} }

View file

@ -335,7 +335,7 @@ public class JarUtils
File f = new File(name); File f = new File(name);
f.mkdirs(); f.mkdirs();
DiskWriter.replaceFile(name, cw.toByteArray(), false); DiskWriter.replaceFileBytes(name, cw.toByteArray(), false);
} }
} catch (Exception e) { } catch (Exception e) {
BytecodeViewer.handleException(e); BytecodeViewer.handleException(e);