2.6.0 Released

01/06/2015 - Now saves if maximized or not.
01/07/2015 - For all save as functions, it will now append the correct
extension if not added by the user.
01/07/2015 - You can no longer use use the save functions if no classes
are loaded (fixes a crash issue).
01/07/2015 - Moved the Update Check to the Settings menu.
01/08/2015 - Added an extremely basic code sqeuence diagram plugin.
01/08/2015 - Updated CFR to CFR_0.93.jar
01/08/2015 - Threaded the Add files function.
01/08/2015 - Finally implemented Kontainer's HTTPRequest wrapper now
that I've open sourced it.
01/08/2015 - Set the panes to be non-editable.
01/08/2015 - Sexified the view pane selection.
01/08/2015 - Started working on Smali Editing support, finished
decompiler so far.
01/09/2015 - Fixed a bug with saving.
01/09/2015 - Added add entire directory.
01/09/2015 - Fixed import .DEX files.
01/10/2015 - Finished Smali Editing.
01/10/2015 - Fixed a class opening issue with sychronization.
01/11/2015 - Threaded all of the save functions.
01/11/2015 - Removed all instances of the setCursor to busy.
01/11/2015 - Added are you sure you wish to overwrite this existing file
to all the other save functions.
01/11/2015 - All of the decompiling names are now randomly generated
instead of a counting number.
01/11/2015 - Updated CFR to CFR_0.94.jar
01/11/2015 - Updated to the latest version of FernFlower.
01/11/2015 - Fixed an extension appending issue with save Java file.
This commit is contained in:
Kalen Kinloch 2015-01-11 04:01:25 -08:00
parent 410196fcd0
commit 9d93f1ee0b
30 changed files with 1376 additions and 262 deletions

View file

@ -1,4 +1,4 @@
Bytecode Viewer is an Advanced Lightweight Java Bytecode Viewer, GUI APK Decompiler, GUI DEX Decompiler, GUI Procyon Java Decompiler, GUI CFR Java Decompiler, GUI FernFlower Java Decompiler, GUI Jar-Jar, Hex Viewer, Code Searcher, Debugger and more.
Bytecode Viewer is an Advanced Lightweight Java Bytecode Viewer, GUI APK Decompiler, GUI DEX Decompiler, GUI Procyon Java Decompiler, GUI CFR Java Decompiler, GUI FernFlower Java Decompiler, GUI DEX2Jar, GUI Jar2DEX, GUI Jar-Jar, Hex Viewer, Code Searcher, Debugger and more.
It's written completely in Java, and it's open sourced. It's currently being maintained and developed by Konloch.
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 something else you can think of.
@ -237,3 +237,27 @@ Changelog:
01/06/2015 - Fixed a search function with Android APKs.
--- 2.5.2 ---:
01/06/2015 - Completely fixed the search function with Android APKs.
--- 2.6.0 ---:
01/06/2015 - Now saves if maximized or not.
01/07/2015 - For all save as functions, it will now append the correct extension if not added by the user.
01/07/2015 - You can no longer use use the save functions if no classes are loaded (fixes a crash issue).
01/07/2015 - Moved the Update Check to the Settings menu.
01/08/2015 - Added an extremely basic code sqeuence diagram plugin.
01/08/2015 - Updated CFR to CFR_0.93.jar
01/08/2015 - Threaded the Add files function.
01/08/2015 - Finally implemented Kontainer's HTTPRequest wrapper now that I've open sourced it.
01/08/2015 - Set the panes to be non-editable.
01/08/2015 - Sexified the view pane selection.
01/08/2015 - Started working on Smali Editing support, finished decompiler so far.
01/09/2015 - Fixed a bug with saving.
01/09/2015 - Added add entire directory.
01/09/2015 - Fixed import .DEX files.
01/10/2015 - Finished Smali Editing.
01/10/2015 - Fixed a class opening issue with sychronization.
01/11/2015 - Threaded all of the save functions.
01/11/2015 - Removed all instances of the setCursor to busy.
01/11/2015 - Added are you sure you wish to overwrite this existing file to all the other save functions.
01/11/2015 - All of the decompiling names are now randomly generated instead of a counting number.
01/11/2015 - Updated CFR to CFR_0.94.jar
01/11/2015 - Updated to the latest version of FernFlower.
01/11/2015 - Fixed an extension appending issue with save Java file.

BIN
libs/baksmali-2.0.3.jar Normal file

Binary file not shown.

BIN
libs/cfr_0_94.jar Normal file

Binary file not shown.

BIN
libs/jgraphx.jar Normal file

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,253 @@
package me.konloch.kontainer.io;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
/**
* A wrapper for Java SE classes to write/read an HTTP Request
*
* @author Konloch
*
*/
public class HTTPRequest {
public URL url;
private int timeout = 30000;
private String cookie;
private String referer;
private String postData;
private String useragent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0";
private Proxy proxy;
private boolean setFollowRedirects = true;
private BufferedReader reader;
private DataOutputStream writer;
private HttpURLConnection connection;
private Set<Entry<String, List<String>>> lastConnectionHeaders;
/**
* Creates a new HTTPRequest object
* @param url
*/
public HTTPRequest(URL url) {
this.url = url;
}
/**
* Sets a referer to send to the web server
*/
public void setReferer(String referer) {
this.referer = referer;
}
/**
* Set a cookie string to send to the web server
*/
public void setCookie(String cookie) {
this.cookie = cookie;
}
/**
* Sets post data to send to the web server
*/
public void setPostData(String postData) {
this.postData = postData;
}
/**
* Sets a custom useragent, default 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0'
*/
public void setUseragent(String useragent) {
this.useragent = useragent;
}
/**
* Sets the seconds till timeout, default 30,000 milliseconds
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* Sets a proxy to connect through
*/
public void setProxy(Proxy proxy) {
this.proxy = proxy;
}
/**
* Used to get the headers the webserver sent on our last connection
*/
public Set<Entry<String, List<String>>> getLastConnectionHeaders() {
return lastConnectionHeaders;
}
/**
* By default follow redirects are enabled
*/
public void setFollowRedirects(boolean setFollowRedirects) {
this.setFollowRedirects = setFollowRedirects;
}
/**
* Used to set up the connection to read the content.
*/
private void setup() throws Exception {
if(proxy != null)
connection = (HttpURLConnection) url.openConnection(proxy);
else
connection = (HttpURLConnection) url.openConnection();
if(cookie != null)
connection.setRequestProperty("Cookie", cookie);
if(referer != null)
connection.addRequestProperty("Referer", referer);
connection.setRequestProperty("User-Agent", useragent);
connection.setReadTimeout(timeout);
connection.setConnectTimeout(timeout);
connection.setUseCaches(false);
HttpURLConnection.setFollowRedirects(setFollowRedirects);
if(postData != null) {
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
writer = new DataOutputStream(connection.getOutputStream());
writer.writeBytes(postData);
writer.flush();
}
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
}
/**
* Reads the entire page and returns a string array
* @return
* @throws Exception
*/
public String[] read() throws Exception {
ArrayList<String> st;
try {
setup();
st = new ArrayList<String>();
String s;
while((s = reader.readLine()) != null)
st.add(s);
lastConnectionHeaders = connection.getHeaderFields().entrySet();
} catch(Exception e) {
cleanup();
throw e;
} finally {
cleanup();
}
return st.toArray(new String[st.size()]);
}
/**
* Reads as many lines as expected unless it reaches the end.
* @param linesToRead
* @return
* @throws Exception
*/
public String[] read(int linesToRead) throws Exception {
ArrayList<String> st;
try {
setup();
st = new ArrayList<String>();
for(int i = 0; i < linesToRead; i++) {
String s = reader.readLine();
if(s != null)
st.add(s);
}
lastConnectionHeaders = connection.getHeaderFields().entrySet();
} catch(Exception e) {
cleanup();
throw e;
} finally {
cleanup();
}
return st.toArray(new String[st.size()]);
}
/**
* Only reads the first line
* @return
* @throws Exception
*/
public String readSingle() throws Exception {
String s;
try {
setup();
s = reader.readLine();
lastConnectionHeaders = connection.getHeaderFields().entrySet();
} catch(Exception e) {
cleanup();
throw e;
} finally {
cleanup();
}
return s;
}
/**
* Reads until it reaches the expected line then it returns it.
* @param linesToRead
* @return
* @throws Exception
*/
public String readSingle(int linesToRead) throws Exception {
String s;
try {
setup();
for(int i = 0; i < linesToRead-1; i++)
reader.readLine();
s = reader.readLine();
lastConnectionHeaders = connection.getHeaderFields().entrySet();
} catch(Exception e) {
cleanup();
throw e;
} finally {
cleanup();
}
return s;
}
/**
* Used to clean up the connection, closes the connections and nulls the objects
*/
private void cleanup() {
try { reader.close(); } catch(Exception e) {}
try { writer.close(); } catch(Exception e) {}
try { connection.disconnect(); } catch(Exception e) {}
reader = null;
writer = null;
connection = null;
}
}

View file

@ -4,15 +4,12 @@ import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
@ -28,12 +25,15 @@ import javax.swing.UIManager;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
import me.konloch.kontainer.io.HTTPRequest;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.imgscalr.Scalr;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.decompilers.Smali;
import the.bytecode.club.bytecodeviewer.gui.ClassViewer;
import the.bytecode.club.bytecodeviewer.gui.FileNavigationPane;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.gui.SearchingPane;
@ -65,11 +65,10 @@ import the.bytecode.club.bytecodeviewer.plugins.PluginManager;
*
* TODO:
* The import jar method eats up a lot of memory, look into some how reducing this.
* Add a tool to build a flowchart of all the classes, and what methods execute what classes, and those method, read chatlog
* Add obfuscation:
* Add integer boxing and other obfuscation methods contra implemented
* Insert unadded/debug opcodes to try to fuck up decompilers
* ClassAnylyzterAdapter
* - Add integer boxing and other obfuscation methods contra implemented
* - Insert unadded/debug opcodes to try to fuck up decompilers
* - ClassAnylyzterAdapter
* Add progress bars on saving all zips/java decompile jar
* Add the jump/save mark system Ida Pro has.
* Add class annotations to bytecode decompiler.
@ -259,6 +258,30 @@ import the.bytecode.club.bytecodeviewer.plugins.PluginManager;
* 01/06/2015 - Fixed a search function with Android APKs.
* -----2.5.2-----:
* 01/06/2015 - Fixed another issue with LDC searching for Android APKs.
* -----2.6.0-----:
* 01/06/2015 - Now saves if maximized or not.
* 01/07/2015 - For all save as functions, it will now append the correct extension if not added by the user.
* 01/07/2015 - You can no longer use use the save functions if no classes are loaded (fixes a crash issue).
* 01/07/2015 - Moved the Update Check to the Settings menu.
* 01/08/2015 - Added an extremely basic code sqeuence diagram plugin.
* 01/08/2015 - Updated CFR to CFR_0.93.jar
* 01/08/2015 - Threaded the Add files function.
* 01/08/2015 - Finally implemented Kontainer's HTTPRequest wrapper now that I've open sourced it.
* 01/08/2015 - Set the panes to be non-editable.
* 01/08/2015 - Sexified the view pane selection.
* 01/08/2015 - Started working on Smali Editing support, finished decompiler so far.
* 01/09/2015 - Fixed a bug with saving.
* 01/09/2015 - Added add entire directory.
* 01/09/2015 - Fixed import .DEX files.
* 01/10/2015 - Finished Smali Editing.
* 01/10/2015 - Fixed a class opening issue with sychronization.
* 01/11/2015 - Threaded all of the save functions.
* 01/11/2015 - Removed all instances of the setCursor to busy.
* 01/11/2015 - Added are you sure you wish to overwrite this existing file to all the other save functions.
* 01/11/2015 - All of the decompiling names are now randomly generated instead of a counting number.
* 01/11/2015 - Updated CFR to CFR_0.94.jar
* 01/11/2015 - Updated to the latest version of FernFlower.
* 01/11/2015 - Fixed an extension appending issue with save Java file.
*
* @author Konloch
*
@ -279,7 +302,7 @@ public class BytecodeViewer {
private static ArrayList<String> recentFiles = DiskReader.loadArrayList(filesName, false);
private static ArrayList<String> recentPlugins = DiskReader.loadArrayList(pluginsName, false);
public static boolean runningObfuscation = false;
public static String version = "2.5.2";
public static String version = "2.6.0";
private static long start = System.currentTimeMillis();
public static void main(String[] args) {
@ -312,30 +335,11 @@ public class BytecodeViewer {
@Override
public void run() {
try {
HttpURLConnection connection = (HttpURLConnection) new URL(
"https://raw.githubusercontent.com/Konloch/bytecode-viewer/master/VERSION")
.openConnection();
connection.setUseCaches(false);
connection.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0");
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
final String version = reader.readLine();
reader.close();
HTTPRequest r = new HTTPRequest(new URL("https://raw.githubusercontent.com/Konloch/bytecode-viewer/master/VERSION"));
final String version = r.readSingle();
if (!BytecodeViewer.version.equals(version)) {
connection = (HttpURLConnection) new URL(
"https://raw.githubusercontent.com/Konloch/bytecode-viewer/master/README.txt")
.openConnection();
connection.setUseCaches(false);
connection.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0");
reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
ArrayList<String> readme = new ArrayList<String>();
String s;
while((s = reader.readLine()) != null)
readme.add(s);
reader.close();
r = new HTTPRequest(new URL("https://raw.githubusercontent.com/Konloch/bytecode-viewer/master/VERSION"));
String[] readme = r.read();
String changelog = "Unable to load change log, please try again later."+nl;
boolean trigger = false;
@ -343,9 +347,7 @@ public class BytecodeViewer {
if(st.equals("--- "+version+" ---:")) {
changelog = "";
trigger = true;
}
if(trigger == true && !st.equals("--- "+version+" ---:")) {
} else if(trigger == true) {
if(st.startsWith("--- "))
trigger = false;
else
@ -488,6 +490,15 @@ public class BytecodeViewer {
System.out.println("Start up took " + ((System.currentTimeMillis() - start) / 1000) + " seconds");
}
//because Smali and Baksmali System.exit if it failed
public static void exit(int i) {
}
public static ClassNode getCurrentlyOpenedClassNode() {
return viewer.workPane.getCurrentClass().cn;
}
public static ClassNode getClassNode(String name) {
if (loadedClasses.containsKey(name))
return loadedClasses.get(name);
@ -495,10 +506,8 @@ public class BytecodeViewer {
}
public static void updateNode(ClassNode oldNode, ClassNode newNode) {
for (ClassNode c : BytecodeViewer.getLoadedClasses()) {
if (c.name.equals(oldNode.name))
c = newNode;
}
BytecodeViewer.loadedClasses.remove(oldNode.name);
BytecodeViewer.loadedClasses.put(oldNode.name, newNode);
}
public static ArrayList<ClassNode> getLoadedClasses() {
@ -512,19 +521,58 @@ public class BytecodeViewer {
return a;
}
public static void openFiles(File[] files, boolean recentFiles) {
//called whenever a save function is executed
public static boolean compileSmali(boolean message) {
if(getLoadedClasses().isEmpty())
return false;
for(java.awt.Component c : BytecodeViewer.viewer.workPane.getLoadedViewers()) {
if(c instanceof ClassViewer) {
ClassViewer cv = (ClassViewer) c;
Object smali[] = cv.getSmali();
ClassNode origNode = (ClassNode) smali[0];
String smaliText = (String) smali[1];
if(smali != null) {
byte[] smaliCompiled = Smali.compile(smaliText);
if(smaliCompiled != null) {
ClassNode newNode = JarUtils.getNode(smaliCompiled);
System.out.println(origNode.name+":"+newNode.name);
BytecodeViewer.updateNode(origNode, newNode);
} else {
BytecodeViewer.showMessage("There has been an error with assembling your Smali code, please check this. Class: " + origNode.name);
return false;
}
}
}
}
if(message)
BytecodeViewer.showMessage("Compiled Successfully.");
return true;
}
private static boolean update = true;
public static void openFiles(final File[] files, boolean recentFiles) {
if(recentFiles)
for (File f : files)
BytecodeViewer.addRecentFile(f);
BytecodeViewer.viewer.setIcon(true);
boolean update = true;
update = true;
Thread t = new Thread() {
@Override
public void run() {
try {
for (final File f : files) {
final String fn = f.getName();
if(!f.exists()) {
update = false;
showMessage("The file " + f.getAbsolutePath() + " could not be found.");
} else {
if(f.isDirectory()) {
openFiles(f.listFiles(), false);
} else {
if (fn.endsWith(".jar") || fn.endsWith(".zip")) {
try {
@ -552,10 +600,7 @@ public class BytecodeViewer {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
update = false;
}
} else if(fn.endsWith(".apk")) {
Thread t = new Thread() {
@Override
public void run() {
} else if(fn.endsWith(".apk") || fn.endsWith(".dex")) {
try {
String name = getRandomizedName()+".jar";
File output = new File(tempDirectory + fs + name);
@ -565,14 +610,14 @@ public class BytecodeViewer {
} catch (final Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
};
t.start();
return;
}
}
}
}
} catch (final Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
} finally {
BytecodeViewer.viewer.setIcon(false);
if(update)
@ -581,6 +626,10 @@ public class BytecodeViewer {
} catch(java.lang.NullPointerException e) {
}
}
}
};
t.start();
}
public static void startPlugin(File plugin) {
if (!plugin.exists())

View file

@ -0,0 +1,113 @@
package the.bytecode.club.bytecodeviewer;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
public class CoolClassAdapter extends ClassAdapter {
String oldName;
String newName;
public CoolClassAdapter(ClassVisitor cv, String oldName, String newName) {
super(cv);
this.oldName = oldName;
this.newName = newName;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
if(name != null)
name = name.replace(oldName, newName);
if(signature != null)
signature = signature.replace(oldName, newName);
if(superName != null)
superName = superName.replace(oldName, newName);
if(interfaces != null)
for(int i = 0; i < interfaces.length; i++)
interfaces[i] = interfaces[i].replace(oldName, newName);
cv.visit(49, access, name, signature, superName, interfaces);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if(desc != null)
desc = desc.replace(oldName, newName);
return cv.visitAnnotation(desc, visible);
}
@Override
public void visitAttribute(Attribute attr) {
}
@Override
public void visitEnd() {
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if(name != null)
name = name.replace(oldName, newName);
if(desc != null)
desc = desc.replace(oldName, newName);
if(signature != null)
signature = signature.replace(oldName, newName);
return cv.visitField(access, name, desc, signature, value);
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if(name != null)
name = name.replace(oldName, newName);
if(outerName != null)
outerName = outerName.replace(oldName, newName);
if(innerName != null)
innerName = innerName.replace(oldName, newName);
cv.visitInnerClass(name, outerName, innerName, access);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if(name != null)
name = name.replace(oldName, newName);
if(desc != null)
desc = desc.replace(oldName, newName);
if(signature != null)
signature = signature.replace(oldName, newName);
if(exceptions != null)
for(int i = 0; i < exceptions.length; i++)
exceptions[i] = exceptions[i].replace(oldName, newName);
//return cv.visitMethod(access, name, desc, signature, exceptions);
return null;
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
if(owner != null)
owner = owner.replace(oldName, newName);
if(name != null)
name = name.replace(oldName, newName);
if(desc != null)
desc = desc.replace(oldName, newName);
cv.visitOuterClass(owner, name, desc);
}
@Override
public void visitSource(String source, String debug) {
if(source != null)
source = source.replace(oldName, newName);
cv.visitSource(source, debug);
}
}

View file

@ -11,23 +11,29 @@ import java.io.File;
public class Dex2Jar {
public static void dex2Jar(File input, File output) {
public static synchronized void dex2Jar(File input, File output) {
try {
com.googlecode.dex2jar.tools.Dex2jarCmd.main(new String[]{"--force", input.getAbsolutePath()});
String realOutput = input.getName().replaceAll(".apk", "-dex2jar.jar");
String realOutput = input.getName().replaceAll(".dex", "-dex2jar.jar").replaceAll(".apk", "-dex2jar.jar");
File realOutputF = new File(realOutput);
realOutputF.renameTo(output);
File realOutputF2 = new File(realOutput);
while(realOutputF2.exists())
realOutputF2.delete();
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public static void saveAsDex(File input, File output) {
public static synchronized void saveAsDex(File input, File output) {
try {
com.googlecode.dex2jar.tools.Jar2Dex.main(new String[]{"--force", input.getAbsolutePath()});
String realOutput = input.getName().replaceAll(".jar", "-jar2dex.dex");
File realOutputF = new File(realOutput);
realOutputF.renameTo(output);
File realOutputF2 = new File(realOutput);
while(realOutputF2.exists())
realOutputF2.delete();
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}

View file

@ -64,14 +64,10 @@ public class JarUtils {
}
private static ByteArrayOutputStream baos = null;
private static byte[] buffer = null;
private static int a = 0;
public static byte[] getBytes(final InputStream is) throws IOException {
baos = new ByteArrayOutputStream();
buffer = new byte[1024];
a = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int a = 0;
while ((a = is.read(buffer)) != -1) {
baos.write(buffer, 0, a);
}
@ -80,16 +76,13 @@ public class JarUtils {
return baos.toByteArray();
}
private static ClassReader cr = null;
private static ClassNode cn = null;
public static ClassNode getNode(final byte[] bytez) {
cr = new ClassReader(bytez);
cn = new ClassNode();
ClassReader cr = new ClassReader(bytez);
ClassNode cn = new ClassNode();
try {
cr.accept(cn, ClassReader.EXPAND_FRAMES);
} catch (Exception e) {
cr.accept(cn, ClassReader.SKIP_FRAMES);
cr.accept(cn, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE);
}
cr = null;
return cn;

View file

@ -1,5 +1,7 @@
package the.bytecode.club.bytecodeviewer;
import javax.swing.JFrame;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
@ -107,6 +109,8 @@ public class Settings {
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "4", false);
else if(BytecodeViewer.viewer.panelGroup1.isSelected(BytecodeViewer.viewer.panel1Hexcode.getModel()))
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "5", false);
else if(BytecodeViewer.viewer.panelGroup1.isSelected(BytecodeViewer.viewer.panel1Smali.getModel()))
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "6", false);
if(BytecodeViewer.viewer.panelGroup2.isSelected(BytecodeViewer.viewer.panel2None.getModel()))
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "0", false);
@ -120,6 +124,8 @@ public class Settings {
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "4", false);
else if(BytecodeViewer.viewer.panelGroup2.isSelected(BytecodeViewer.viewer.panel2Hexcode.getModel()))
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "5", false);
else if(BytecodeViewer.viewer.panelGroup2.isSelected(BytecodeViewer.viewer.panel2Smali.getModel()))
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "6", false);
if(BytecodeViewer.viewer.panelGroup3.isSelected(BytecodeViewer.viewer.panel3None.getModel()))
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "0", false);
@ -133,8 +139,13 @@ public class Settings {
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "4", false);
else if(BytecodeViewer.viewer.panelGroup3.isSelected(BytecodeViewer.viewer.panel3Hexcode.getModel()))
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "5", false);
else if(BytecodeViewer.viewer.panelGroup3.isSelected(BytecodeViewer.viewer.panel3Smali.getModel()))
DiskWriter.writeNewLine(BytecodeViewer.settingsName, "6", false);
DiskWriter.writeNewLine(BytecodeViewer.settingsName, String.valueOf(BytecodeViewer.viewer.refreshOnChange.isSelected()), false);
DiskWriter.writeNewLine(BytecodeViewer.settingsName, String.valueOf(BytecodeViewer.viewer.isMaximized), false);
DiskWriter.writeNewLine(BytecodeViewer.settingsName, String.valueOf(BytecodeViewer.viewer.autoCompileSmali.isSelected()), false);
DiskWriter.writeNewLine(BytecodeViewer.settingsName, String.valueOf(BytecodeViewer.viewer.autoCompileOnRefresh.isSelected()), false);
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
@ -235,6 +246,9 @@ public class Settings {
BytecodeViewer.viewer.panelGroup1.setSelected(BytecodeViewer.viewer.panel1Bytecode.getModel(), true);
else if(decompiler == 5)
BytecodeViewer.viewer.panelGroup1.setSelected(BytecodeViewer.viewer.panel1Hexcode.getModel(), true);
else if(decompiler == 6)
BytecodeViewer.viewer.panelGroup1.setSelected(BytecodeViewer.viewer.panel1Smali.getModel(), true);
decompiler = Integer.parseInt(DiskReader.loadString(BytecodeViewer.settingsName, 82, false));
if(decompiler == 0)
BytecodeViewer.viewer.panelGroup2.setSelected(BytecodeViewer.viewer.panel2None.getModel(), true);
@ -248,6 +262,9 @@ public class Settings {
BytecodeViewer.viewer.panelGroup2.setSelected(BytecodeViewer.viewer.panel2Bytecode.getModel(), true);
else if(decompiler == 5)
BytecodeViewer.viewer.panelGroup2.setSelected(BytecodeViewer.viewer.panel2Hexcode.getModel(), true);
else if(decompiler == 6)
BytecodeViewer.viewer.panelGroup2.setSelected(BytecodeViewer.viewer.panel2Smali.getModel(), true);
decompiler = Integer.parseInt(DiskReader.loadString(BytecodeViewer.settingsName, 83, false));
if(decompiler == 0)
BytecodeViewer.viewer.panelGroup3.setSelected(BytecodeViewer.viewer.panel3None.getModel(), true);
@ -261,8 +278,18 @@ public class Settings {
BytecodeViewer.viewer.panelGroup3.setSelected(BytecodeViewer.viewer.panel3Bytecode.getModel(), true);
else if(decompiler == 5)
BytecodeViewer.viewer.panelGroup3.setSelected(BytecodeViewer.viewer.panel3Hexcode.getModel(), true);
else if(decompiler == 6)
BytecodeViewer.viewer.panelGroup3.setSelected(BytecodeViewer.viewer.panel3Smali.getModel(), true);
BytecodeViewer.viewer.refreshOnChange.setSelected(Boolean.parseBoolean(DiskReader.loadString(BytecodeViewer.settingsName, 84, false)));
boolean bool = Boolean.parseBoolean(DiskReader.loadString(BytecodeViewer.settingsName, 85, false));
if(bool) {
BytecodeViewer.viewer.setExtendedState(JFrame.MAXIMIZED_BOTH);
BytecodeViewer.viewer.isMaximized = true;
}
BytecodeViewer.viewer.autoCompileSmali.setSelected(Boolean.parseBoolean(DiskReader.loadString(BytecodeViewer.settingsName, 86, false)));
BytecodeViewer.viewer.autoCompileOnRefresh.setSelected(Boolean.parseBoolean(DiskReader.loadString(BytecodeViewer.settingsName, 87, false)));
} catch(Exception e) {
//ignore because errors are expected, first start up and outdated settings.
}

View file

@ -2,7 +2,10 @@ package the.bytecode.club.bytecodeviewer;
import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
@ -14,6 +17,61 @@ import javax.swing.filechooser.FileFilter;
*/
public final class ZipUtils {
public static final int BUFFER = 2048;
/**
* Unzip files to path.
*
* @param zipFileName the zip file name
* @param fileExtractPath the file extract path
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void unzipFilesToPath(String jarPath, String destinationDir) throws IOException {
File file = new File(jarPath);
JarFile jar = new JarFile(file);
// fist get all directories,
// then make those directory on the destination Path
for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements();) {
JarEntry entry = (JarEntry) enums.nextElement();
String fileName = destinationDir + File.separator + entry.getName();
File f = new File(fileName);
if (fileName.endsWith("/")) {
f.mkdirs();
}
}
//now create all files
for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements();) {
JarEntry entry = (JarEntry) enums.nextElement();
String fileName = destinationDir + File.separator + entry.getName();
File f = new File(fileName);
if (!fileName.endsWith("/")) {
InputStream is = jar.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(f);
// write contents of 'is' to 'fos'
while (is.available() > 0) {
fos.write(is.read());
}
fos.close();
is.close();
}
}
try {
jar.close();
} catch(Exception e) {
}
}
private static final String ZIP_FILE_EXTENSION = ".zip";
private static final FileFilter ZIP_FILE_FILTER = new FileFilter() {
@ -98,10 +156,10 @@ public final class ZipUtils {
throws IOException {
if (zipFile == null) {
throw new IllegalArgumentException("Cannot unzip a null file!");
} else if (!ZIP_FILE_FILTER.accept(zipFile)) {
} /*else if (!ZIP_FILE_FILTER.accept(zipFile)) {
throw new IllegalArgumentException(
"Given archive is not a valid Zip file!");
}
}*/
if (target == null) {
throw new IllegalArgumentException("Cannot unzip to a null target!");
}
@ -197,4 +255,28 @@ public final class ZipUtils {
return new File(file.getAbsolutePath().substring(parentPath.length()));
}
public static void zipFile(File inputFile, File outputZip) {
byte[] buffer = new byte[1024];
try {
FileOutputStream fos = new FileOutputStream(outputZip);
ZipOutputStream zos = new ZipOutputStream(fos);
ZipEntry ze= new ZipEntry(inputFile.getName());
zos.putNextEntry(ze);
FileInputStream in = new FileInputStream(inputFile);
int len;
while ((len = in.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
in.close();
zos.closeEntry();
zos.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}

View file

@ -2,6 +2,9 @@ package the.bytecode.club.bytecodeviewer.api;
import java.util.List;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
@ -13,6 +16,8 @@ import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.CoolClassAdapter;
import the.bytecode.club.bytecodeviewer.JarUtils;
/**
* Used to rename/replace methods/classes/fields
@ -164,16 +169,15 @@ public final class ASMUtil_OLD {
}
}
}
}
/*
* for(ClassNode oldClass : BytecodeViewer.getLoadedClasses()) { try {
* ClassReader cr = new ClassReader(oldClass.name); ClassWriter cw = new
* ClassWriter(0); cr.accept(new ClassVisitor(0) {
*
* @Override
*
* }, ClassReader.EXPAND_FRAMES); byte[] b = cw.toByteArray(); }
* catch(Exception e) { new ExceptionUI(e); } }
*/
ClassWriter cw2 = new ClassWriter(0);
c.accept(cw2);
ClassReader cr=new ClassReader(cw2.toByteArray());
ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassAdapter ca=new CoolClassAdapter(cw, oldName, newName);
cr.accept(ca, 0);
byte[] newClass = cw.toByteArray();
BytecodeViewer.updateNode(c, JarUtils.getNode(newClass));
}
}
}

View file

@ -61,6 +61,14 @@ public class BytecodeViewer {
the.bytecode.club.bytecodeviewer.BytecodeViewer.openFiles(files, recentFiles);
}
/**
* Returns the currently opened class node, if nothing is opened it'll return null.
* @return The opened class node or a null if nothing is opened
*/
public static ClassNode getCurrentlyOpenedClassNode() {
return the.bytecode.club.bytecodeviewer.BytecodeViewer.getCurrentlyOpenedClassNode();
}
/**
* Used to load a ClassNode.
*

View file

@ -58,7 +58,7 @@ public class ExceptionUI extends JFrame {
e.printStackTrace(new PrintWriter(sw));
e.printStackTrace();
txtrBytecodeViewerIs.setText(sw.toString());
txtrBytecodeViewerIs.setText("Bytecode Viewer Version: " + BytecodeViewer.version + BytecodeViewer.nl + BytecodeViewer.nl + sw.toString());
this.setLocationRelativeTo(null);
this.setVisible(true);
}

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.decompilers.java;
package the.bytecode.club.bytecodeviewer.decompilers;
import java.io.Closeable;
import java.io.File;
@ -43,9 +43,8 @@ public class CFRDecompiler extends JavaDecompiler {
String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs
+ "temp";
int fileNumber = getClassNumber(fileStart, ".class");
final File tempClass = new File(fileStart + fileNumber + ".class");
final File tempClass = new File(getUniqueName(fileStart, ".class") + ".class");
try {
final FileOutputStream fos = new FileOutputStream(tempClass);

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.decompilers.java;
package the.bytecode.club.bytecodeviewer.decompilers;
import java.io.File;
import java.io.FileOutputStream;
@ -61,11 +61,9 @@ public class FernFlowerDecompiler extends JavaDecompiler {
final ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs
+ "temp";
int fileNumber = getClassNumber(fileStart, ".class");
String start = getUniqueName("", ".class");
final File tempClass = new File(fileStart + fileNumber + ".class");
final File tempClass = new File(start + ".class");
try {
final FileOutputStream fos = new FileOutputStream(tempClass);
@ -82,7 +80,7 @@ public class FernFlowerDecompiler extends JavaDecompiler {
tempClass.delete();
final File outputJava = new File("temp" + fileNumber + ".java");
final File outputJava = new File(start + ".java");
if (outputJava.exists()) {
String s;
try {

View file

@ -0,0 +1,51 @@
package the.bytecode.club.bytecodeviewer.decompilers;
import java.io.File;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.MiscUtils;
/**
*
* @author Konloch
*
*/
public abstract class JavaDecompiler {
public abstract String decompileClassNode(ClassNode cn);
public abstract void decompileToZip(String zipName);
public abstract void decompileToClass(String className, String classNameSaved);
public static String getUniqueName(String start, String ext) {
String s = null;
boolean b = true;
File f = null;
String m = null;
while (b) {
m = MiscUtils.randomString(32);
f = new File(start + m + ext);
if (!f.exists()) {
s = start + m;
b = false;
}
}
return s;
}
public static int getClassNumber(String start, String ext) {
boolean b = true;
int i = 0;
while (b) {
File tempF = new File(start + i + ext);
if (!tempF.exists())
b = false;
else
i++;
}
return i;
}
}

View file

@ -1,4 +1,4 @@
package the.bytecode.club.bytecodeviewer.decompilers.java;
package the.bytecode.club.bytecodeviewer.decompilers;
import java.io.BufferedOutputStream;
import java.io.File;
@ -96,9 +96,8 @@ public class ProcyonDecompiler extends JavaDecompiler {
String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs
+ "temp";
int fileNumber = getClassNumber(fileStart, ".class");
final File tempClass = new File(fileStart + fileNumber + ".class");
final File tempClass = new File(getUniqueName(fileStart, ".class") + ".class");
try {
final FileOutputStream fos = new FileOutputStream(tempClass);

View file

@ -0,0 +1,131 @@
package the.bytecode.club.bytecodeviewer.decompilers;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Dex2Jar;
import the.bytecode.club.bytecodeviewer.ZipUtils;
public class Smali {
public static String decompileClassNode(ClassNode cn) {
final ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs
+ "temp";
String start = JavaDecompiler.getUniqueName(fileStart, ".class");
final File tempClass = new File(start + ".class");
final File tempZip = new File(start + ".jar");
final File tempDex = new File(start + ".dex");
final File tempSmali = new File(start + "-smali"); //output directory
try {
final FileOutputStream fos = new FileOutputStream(tempClass);
fos.write(cw.toByteArray());
fos.close();
} catch (final IOException e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
ZipUtils.zipFile(tempClass, tempZip);
Dex2Jar.saveAsDex(tempZip, tempDex);
try {
org.jf.baksmali.main.main(new String[]{"-o", tempSmali.getAbsolutePath(), "-x", tempDex.getAbsolutePath()});
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
File outputSmali = null;
boolean found = false;
File current = tempSmali;
while(!found) {
File f = current.listFiles()[0];
if(f.isDirectory())
current = f;
else {
outputSmali = f;
found = true;
}
}
try {
return DiskReader.loadAsString(outputSmali.getAbsolutePath());
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
return null;
}
public static byte[] compile(String contents) {
String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs
+ "temp";
int fileNumber = JavaDecompiler.getClassNumber(fileStart, ".dex");
final File tempSmaliFolder = new File(fileStart + fileNumber + "-smalifolder"+BytecodeViewer.fs);
tempSmaliFolder.mkdir();
File tempSmali = new File(tempSmaliFolder.getAbsolutePath() +BytecodeViewer.fs + fileNumber + ".smali");
File tempDex = new File(fileStart + fileNumber + ".dex");
File tempJar = new File(fileStart + fileNumber + ".jar");
File tempJarFolder = new File(fileStart + fileNumber + "-jar"+BytecodeViewer.fs);
try {
DiskWriter.replaceFile(tempSmali.getAbsolutePath(), contents, false);
} catch (final Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
try {
org.jf.smali.main.main(new String[]{tempSmaliFolder.getAbsolutePath(), "-o", tempDex.getAbsolutePath()});
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
Dex2Jar.dex2Jar(tempDex, tempJar);
try {
ZipUtils.unzipFilesToPath(tempJar.getAbsolutePath(), tempJarFolder.getAbsolutePath());
File outputClass = null;
boolean found = false;
File current = tempJarFolder;
try {
while(!found) {
File f = current.listFiles()[0];
if(f.isDirectory())
current = f;
else {
outputClass = f;
found = true;
}
}
return org.apache.commons.io.FileUtils.readFileToByteArray(outputClass);
} catch (java.lang.NullPointerException e) {
}
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
return null;
}
}

View file

@ -1,35 +0,0 @@
package the.bytecode.club.bytecodeviewer.decompilers.java;
import java.io.File;
import org.objectweb.asm.tree.ClassNode;
/**
*
* @author Konloch
*
*/
public abstract class JavaDecompiler {
public abstract String decompileClassNode(ClassNode cn);
public abstract void decompileToZip(String zipName);
public abstract void decompileToClass(String className, String classNameSaved);
File tempF = null;
public int getClassNumber(String start, String ext) {
boolean b = true;
int i = 0;
while (b) {
tempF = new File(start + i + ext);
if (!tempF.exists())
b = false;
else
i++;
}
return i;
}
}

View file

@ -45,10 +45,11 @@ import org.objectweb.asm.tree.ClassNode;
import com.jhe.hexed.JHexEditor;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.CFRDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.FernFlowerDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.ProcyonDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.Smali;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.ClassNodeDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.java.CFRDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.java.FernFlowerDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.java.ProcyonDecompiler;
/**
* This represents the opened classfile.
@ -99,7 +100,7 @@ public class ClassViewer extends JPanel {
private static final long serialVersionUID = -8650495368920680024L;
ArrayList<MethodData> lnData = new ArrayList<MethodData>();
String name;
ClassNode cn;
public ClassNode cn;
JSplitPane sp;
JSplitPane sp2;
public JPanel panel1Search = new JPanel(new BorderLayout());
@ -114,6 +115,9 @@ public class ClassViewer extends JPanel {
int pane1 = -1;
int pane2 = -1;
int pane3 = -1;
public RSyntaxTextArea smali1 = null;
public RSyntaxTextArea smali2 = null;
public RSyntaxTextArea smali3 = null;
/**
* This was really interesting to write.
@ -455,6 +459,7 @@ public class ClassViewer extends JPanel {
PaneUpdaterThread t;
public void startPaneUpdater(final JButton button) {
this.cn = BytecodeViewer.getClassNode(cn.name); //update the classnode
if (BytecodeViewer.viewer.panelGroup1
.isSelected(BytecodeViewer.viewer.panel1None.getModel()))
pane1 = 0;
@ -473,6 +478,9 @@ public class ClassViewer extends JPanel {
else if (BytecodeViewer.viewer.panelGroup1
.isSelected(BytecodeViewer.viewer.panel1Hexcode.getModel()))
pane1 = 5;
else if (BytecodeViewer.viewer.panelGroup1
.isSelected(BytecodeViewer.viewer.panel1Smali.getModel()))
pane1 = 6;
if (BytecodeViewer.viewer.panelGroup2
.isSelected(BytecodeViewer.viewer.panel2None.getModel()))
@ -492,6 +500,9 @@ public class ClassViewer extends JPanel {
else if (BytecodeViewer.viewer.panelGroup2
.isSelected(BytecodeViewer.viewer.panel2Hexcode.getModel()))
pane2 = 5;
else if (BytecodeViewer.viewer.panelGroup2
.isSelected(BytecodeViewer.viewer.panel2Smali.getModel()))
pane2 = 6;
if (BytecodeViewer.viewer.panelGroup3
.isSelected(BytecodeViewer.viewer.panel3None.getModel()))
@ -511,6 +522,9 @@ public class ClassViewer extends JPanel {
else if (BytecodeViewer.viewer.panelGroup3
.isSelected(BytecodeViewer.viewer.panel3Hexcode.getModel()))
pane3 = 5;
else if (BytecodeViewer.viewer.panelGroup3
.isSelected(BytecodeViewer.viewer.panel3Smali.getModel()))
pane3 = 6;
t = new PaneUpdaterThread() {
@Override
@ -519,6 +533,9 @@ public class ClassViewer extends JPanel {
panel1.removeAll();
panel2.removeAll();
panel3.removeAll();
smali1 = null;
smali2 = null;
smali3 = null;
if (pane1 != 0 && pane1 != 5)
panel1.add(panel1Search, BorderLayout.NORTH);
@ -536,6 +553,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(proc_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel1.add(scrollPane);
}
@ -548,6 +566,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(cfr_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel1.add(scrollPane);
}
@ -560,6 +579,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(ff_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel1.add(scrollPane);
}
@ -573,6 +593,7 @@ public class ClassViewer extends JPanel {
bytecodeArea);
bytecodeArea.setText(ClassNodeDecompiler.decompile(cn));
bytecodeArea.setCaretPosition(0);
bytecodeArea.setEditable(false);
panel1.add(bytecodeSPane);
}
@ -583,6 +604,20 @@ public class ClassViewer extends JPanel {
panel1.add(hex);
}
if (pane1 == 6) {// bytecode
RSyntaxTextArea bytecodeArea = new RSyntaxTextArea();
bytecodeArea
.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
bytecodeArea.setCodeFoldingEnabled(true);
bytecodeArea.setAntiAliasingEnabled(true);
RTextScrollPane bytecodeSPane = new RTextScrollPane(
bytecodeArea);
bytecodeArea.setText(Smali.decompileClassNode(cn));
bytecodeArea.setCaretPosition(0);
smali1 = bytecodeArea;
panel1.add(bytecodeSPane);
}
if (pane2 == 1) {
RSyntaxTextArea panelArea = new RSyntaxTextArea();
panelArea
@ -592,6 +627,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(proc_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel2.add(scrollPane);
}
@ -604,6 +640,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(cfr_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel2.add(scrollPane);
}
@ -616,6 +653,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(ff_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel2.add(scrollPane);
}
@ -627,6 +665,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(paneArea);
paneArea.setText(ClassNodeDecompiler.decompile(cn));
paneArea.setCaretPosition(0);
paneArea.setEditable(false);
panel2.add(scrollPane);
}
@ -637,6 +676,18 @@ public class ClassViewer extends JPanel {
panel2.add(hex);
}
if (pane2 == 6) {
RSyntaxTextArea paneArea = new RSyntaxTextArea();
paneArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
paneArea.setCodeFoldingEnabled(true);
paneArea.setAntiAliasingEnabled(true);
RTextScrollPane scrollPane = new RTextScrollPane(paneArea);
paneArea.setText(Smali.decompileClassNode(cn));
paneArea.setCaretPosition(0);
smali2 = paneArea;
panel2.add(scrollPane);
}
if (pane3 == 1) {
RSyntaxTextArea panelArea = new RSyntaxTextArea();
panelArea
@ -646,6 +697,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(proc_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel3.add(scrollPane);
}
@ -658,6 +710,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(cfr_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel3.add(scrollPane);
}
@ -670,6 +723,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(panelArea);
panelArea.setText(ff_dc.decompileClassNode(cn));
panelArea.setCaretPosition(0);
panelArea.setEditable(false);
panel3.add(scrollPane);
}
@ -681,6 +735,7 @@ public class ClassViewer extends JPanel {
RTextScrollPane scrollPane = new RTextScrollPane(paneArea);
paneArea.setText(ClassNodeDecompiler.decompile(cn));
paneArea.setCaretPosition(0);
paneArea.setEditable(false);
panel3.add(scrollPane);
}
@ -691,6 +746,18 @@ public class ClassViewer extends JPanel {
panel3.add(hex);
}
if (pane3 == 6) {
RSyntaxTextArea paneArea = new RSyntaxTextArea();
paneArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
paneArea.setCodeFoldingEnabled(true);
paneArea.setAntiAliasingEnabled(true);
RTextScrollPane scrollPane = new RTextScrollPane(paneArea);
paneArea.setText(Smali.decompileClassNode(cn));
paneArea.setCaretPosition(0);
smali3 = paneArea;
panel3.add(scrollPane);
}
resetDivider();
BytecodeViewer.viewer.setIcon(false);
} catch(Exception e) {
@ -705,6 +772,17 @@ public class ClassViewer extends JPanel {
t.start();
}
public Object[] getSmali() {
if(smali1 != null)
return new Object[]{cn, smali1.getText()};
if(smali2 != null)
return new Object[]{cn, smali2.getText()};
if(smali3 != null)
return new Object[]{cn, smali3.getText()};
return null;
}
public static class MethodData {
public String name, desc;
public int srcLN, bytecodeLN;

View file

@ -41,10 +41,16 @@ public class ExportJar extends JFrame {
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
BytecodeViewer.viewer.setC(true);
BytecodeViewer.viewer.setIcon(true);
Thread t = new Thread() {
@Override
public void run() {
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), jarPath,
mani.getText());
BytecodeViewer.viewer.setC(false);
BytecodeViewer.viewer.setIcon(false);
}
};
t.start();
dispose();
}
});

View file

@ -242,6 +242,7 @@ public class FileNavigationPane extends VisibleComponent implements
}
public void updateTree() {
try {
treeRoot.removeAllChildren();
for (final Entry<String, ClassNode> entry : BytecodeViewer.loadedClasses
.entrySet()) {
@ -272,6 +273,9 @@ public class FileNavigationPane extends VisibleComponent implements
treeRoot.sort();
tree.expandPath(new TreePath(tree.getModel().getRoot()));
tree.updateUI();
} catch(java.util.ConcurrentModificationException e) {
//ignore, the last file will reset everything
}
// expandAll(tree, true);
}
@ -310,6 +314,7 @@ public class FileNavigationPane extends VisibleComponent implements
@Override
public void paint(final Graphics g) {
try {
super.paint(g);
if (m == null) {
m = new StringMetrics((Graphics2D) g);
@ -323,6 +328,9 @@ public class FileNavigationPane extends VisibleComponent implements
((int) ((getWidth() / 2) - (m.getWidth(s) / 2))),
getHeight() / 2);
}
} catch(java.lang.InternalError | java.lang.NullPointerException e) {
}
}
}

View file

@ -14,6 +14,9 @@ import javax.swing.SwingUtilities;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.filechooser.FileFilter;
import javax.swing.JMenu;
@ -27,17 +30,13 @@ import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Dex2Jar;
import the.bytecode.club.bytecodeviewer.FileChangeNotifier;
import the.bytecode.club.bytecodeviewer.JarUtils;
import the.bytecode.club.bytecodeviewer.decompilers.java.CFRDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.java.FernFlowerDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.java.ProcyonDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.CFRDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.FernFlowerDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.ProcyonDecompiler;
import the.bytecode.club.bytecodeviewer.obfuscators.RenameClasses;
import the.bytecode.club.bytecodeviewer.obfuscators.RenameFields;
import the.bytecode.club.bytecodeviewer.obfuscators.RenameMethods;
import the.bytecode.club.bytecodeviewer.plugins.AllatoriStringDecrypter;
import the.bytecode.club.bytecodeviewer.plugins.PluginManager;
import the.bytecode.club.bytecodeviewer.plugins.ShowAllStrings;
import the.bytecode.club.bytecodeviewer.plugins.ShowMainMethods;
import the.bytecode.club.bytecodeviewer.plugins.ZKMStringDecrypter;
import the.bytecode.club.bytecodeviewer.plugins.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
@ -45,7 +44,6 @@ import java.io.File;
import java.util.ArrayList;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JCheckBox;
public class MainViewerGUI extends JFrame implements FileChangeNotifier {
@ -297,11 +295,11 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
public WorkPane workPane = new WorkPane(this);
private final JMenu mnSettings = new JMenu("Settings");
private final JSeparator separator_6 = new JSeparator();
public final JCheckBox refreshOnChange = new JCheckBox("Refresh On View Change");
public final JCheckBoxMenuItem refreshOnChange = new JCheckBoxMenuItem("Refresh On View Change");
final MainViewerGUI This = this;
public boolean isMaximized = false;
public void setC(boolean busy) {
public void removed(boolean busy) {
if (busy) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
for (Component c : this.getComponents())
@ -342,12 +340,25 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
ImageIcon busy = new ImageIcon(getClass().getResource("/resources/1.gif"));
ImageIcon busyB64 = new ImageIcon(BytecodeViewer.b642IMG("R0lGODlhEAALAPQAAP///wAAANra2tDQ0Orq6gcHBwAAAC8vL4KCgmFhYbq6uiMjI0tLS4qKimVlZb6+vicnJwUFBU9PT+bm5tjY2PT09Dk5Odzc3PLy8ra2tqCgoMrKyu7u7gAAAAAAAAAAACH5BAkLAAAAIf4aQ3JlYXRlZCB3aXRoIGFqYXhsb2FkLmluZm8AIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAALAAAFLSAgjmRpnqSgCuLKAq5AEIM4zDVw03ve27ifDgfkEYe04kDIDC5zrtYKRa2WQgAh+QQJCwAAACwAAAAAEAALAAAFJGBhGAVgnqhpHIeRvsDawqns0qeN5+y967tYLyicBYE7EYkYAgAh+QQJCwAAACwAAAAAEAALAAAFNiAgjothLOOIJAkiGgxjpGKiKMkbz7SN6zIawJcDwIK9W/HISxGBzdHTuBNOmcJVCyoUlk7CEAAh+QQJCwAAACwAAAAAEAALAAAFNSAgjqQIRRFUAo3jNGIkSdHqPI8Tz3V55zuaDacDyIQ+YrBH+hWPzJFzOQQaeavWi7oqnVIhACH5BAkLAAAALAAAAAAQAAsAAAUyICCOZGme1rJY5kRRk7hI0mJSVUXJtF3iOl7tltsBZsNfUegjAY3I5sgFY55KqdX1GgIAIfkECQsAAAAsAAAAABAACwAABTcgII5kaZ4kcV2EqLJipmnZhWGXaOOitm2aXQ4g7P2Ct2ER4AMul00kj5g0Al8tADY2y6C+4FIIACH5BAkLAAAALAAAAAAQAAsAAAUvICCOZGme5ERRk6iy7qpyHCVStA3gNa/7txxwlwv2isSacYUc+l4tADQGQ1mvpBAAIfkECQsAAAAsAAAAABAACwAABS8gII5kaZ7kRFGTqLLuqnIcJVK0DeA1r/u3HHCXC/aKxJpxhRz6Xi0ANAZDWa+kEAA7"));
private final JMenuItem mntmSaveAsApk = new JMenuItem("Save As DEX..");
private final JMenuItem mntmCodeSequenceDiagram = new JMenuItem("Code Sequence Diagram");
private final JSeparator separator_7 = new JSeparator();
private final JSeparator separator_8 = new JSeparator();
private final JSeparator separator_9 = new JSeparator();
private final JSeparator separator_10 = new JSeparator();
private final JSeparator separator_11 = new JSeparator();
private final JSeparator separator_12 = new JSeparator();
public final JRadioButtonMenuItem panel1Smali = new JRadioButtonMenuItem("Smali Editable");
public final JRadioButtonMenuItem panel2Smali = new JRadioButtonMenuItem("Smali Editable");
public final JRadioButtonMenuItem panel3Smali = new JRadioButtonMenuItem("Smali Editable");
public final JCheckBoxMenuItem autoCompileSmali = new JCheckBoxMenuItem("Compile Smali On Save");
private final JMenuItem mntmNewMenuItem_13 = new JMenuItem("Compile Smali");
public final JCheckBoxMenuItem autoCompileOnRefresh = new JCheckBoxMenuItem("Compile Smali On Refresh");
public void setIcon(final boolean busy) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (busy) {
try {
mntmNewMenuItem_4.setIcon(This.busy);
mntmNewMenuItem_4.setIcon(BytecodeViewer.viewer.busy);
} catch (NullPointerException e) {
mntmNewMenuItem_4.setIcon(busyB64);
}
@ -359,12 +370,32 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
}
public MainViewerGUI() {
mnNewMenu_5.setVisible(false);
this.addWindowStateListener(new WindowAdapter() {
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(BytecodeViewer.iconList);
panelGroup1.add(panel1None);
panelGroup1.add(panel1Fern);
panelGroup1.add(panel1Proc);
panelGroup1.add(panel1CFR);
panelGroup1.add(panel1Bytecode);
panelGroup1.add(panel1Smali);
panelGroup1.add(panel1Hexcode);
panelGroup1.setSelected(panel1Proc.getModel(), true);//my one true love
panelGroup2.add(panel2None);
@ -372,6 +403,7 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
panelGroup2.add(panel2Proc);
panelGroup2.add(panel2CFR);
panelGroup2.add(panel2Bytecode);
panelGroup2.add(panel2Smali);
panelGroup2.add(panel2Hexcode);
panelGroup2.setSelected(panel2Bytecode.getModel(), true);
panelGroup3.add(panel3None);
@ -379,6 +411,7 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
panelGroup3.add(panel3Proc);
panelGroup3.add(panel3CFR);
panelGroup3.add(panel3Bytecode);
panelGroup3.add(panel3Smali);
panelGroup3.add(panel3Hexcode);
panelGroup3.setSelected(panel3None.getModel(), true);
@ -400,18 +433,21 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
panel1Proc.addActionListener(listener);
panel1CFR.addActionListener(listener);
panel1Bytecode.addActionListener(listener);
panel1Smali.addActionListener(listener);
panel1Hexcode.addActionListener(listener);
panel2None.addActionListener(listener);
panel2Fern.addActionListener(listener);
panel2Proc.addActionListener(listener);
panel2CFR.addActionListener(listener);
panel2Bytecode.addActionListener(listener);
panel2Smali.addActionListener(listener);
panel2Hexcode.addActionListener(listener);
panel3None.addActionListener(listener);
panel3Fern.addActionListener(listener);
panel3Proc.addActionListener(listener);
panel3CFR.addActionListener(listener);
panel3Bytecode.addActionListener(listener);
panel3Smali.addActionListener(listener);
panel3Hexcode.addActionListener(listener);
obfuscatorGroup.add(strongObf);
obfuscatorGroup.add(lightObf);
@ -419,7 +455,6 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// procyon
/* none */
chckbxmntmNewCheckItem_12.setSelected(true);
setJMenuBar(menuBar);
@ -439,14 +474,14 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
fc.setFileFilter(new APKDEXJarZipClassFileFilter());
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(This);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try {
BytecodeViewer.viewer.setC(true);
BytecodeViewer.viewer.setIcon(true);
BytecodeViewer.openFiles(new File[] { fc
.getSelectedFile() }, true);
BytecodeViewer.viewer.setC(false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e1) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e1);
}
@ -459,6 +494,12 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
JMenuItem mntmSave = new JMenuItem("Save Files As..");
mntmSave.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
if(autoCompileSmali.isSelected() && !BytecodeViewer.compileSmali(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new ZipFileFilter());
fc.setFileHidingEnabled(false);
@ -466,10 +507,42 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
BytecodeViewer.viewer.setC(true);
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.getLoadedClasses(),
file.getAbsolutePath());
BytecodeViewer.viewer.setC(false);
file2.getAbsolutePath());
BytecodeViewer.viewer.setIcon(false);
}
};
t.start();
}
}
});
@ -477,6 +550,12 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
mnNewMenu.add(separator_3);
mntmNewMenuItem_3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
if(autoCompileSmali.isSelected() && !BytecodeViewer.compileSmali(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new JarFileFilter());
fc.setFileHidingEnabled(false);
@ -487,14 +566,49 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
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);
}
}
});
mntmNewMenuItem_13.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
BytecodeViewer.compileSmali(true);
}
});
mnNewMenu.add(mntmNewMenuItem_13);
mnNewMenu.add(mntmNewMenuItem_3);
mntmSaveAsApk.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
if(autoCompileSmali.isSelected() && !BytecodeViewer.compileSmali(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new DexFileFilter());
fc.setFileHidingEnabled(false);
@ -502,7 +616,13 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
final File file = fc.getSelectedFile();
if(file.exists()) {
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" };
@ -527,17 +647,21 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
@Override
public void run() {
BytecodeViewer.viewer.setIcon(true);
String input = BytecodeViewer.tempDirectory+BytecodeViewer.fs+BytecodeViewer.getRandomizedName()+".jar";
final String input = BytecodeViewer.tempDirectory+BytecodeViewer.fs+BytecodeViewer.getRandomizedName()+".jar";
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), input);
String output = file.getAbsolutePath();
if (!output.endsWith(".dex"))
output = output + ".dex";
Dex2Jar.saveAsDex(new File(input), new File(output));
Thread t = new Thread() {
@Override
public void run() {
Dex2Jar.saveAsDex(new File(input), file2);
BytecodeViewer.viewer.setIcon(false);
}
};
t.start();
}
};
t.start();
}
}
});
@ -545,6 +669,12 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
mnNewMenu.add(mntmSave);
mntmNewMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(BytecodeViewer.getLoadedClasses().isEmpty()) {
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
if(autoCompileSmali.isSelected() && !BytecodeViewer.compileSmali(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new ZipFileFilter());
fc.setFileHidingEnabled(false);
@ -552,6 +682,30 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
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 = appendZip(file);// cheap hax cause
// string is final
@ -621,9 +775,11 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
mntmNewMenuItem_12.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(workPane.getCurrentClass() == null) {
BytecodeViewer.showMessage("First open a class file.");
BytecodeViewer.showMessage("First open a class, jar, zip, apk or dex file.");
return;
}
if(autoCompileSmali.isSelected() && !BytecodeViewer.compileSmali(false))
return;
final String s = workPane.getCurrentClass().name;
JFileChooser fc = new JFileChooser();
@ -633,10 +789,32 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
BytecodeViewer.viewer.setIcon(true);
final String path = appendClass(file);// cheap hax cause
final String path = appendJava(file);// 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",
@ -719,8 +897,6 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
mnNewMenu.add(mntmAbout);
mnNewMenu.add(chckbxmntmNewCheckItem_12);
JMenuItem mntmExit = new JMenuItem("Exit");
mntmExit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
@ -750,46 +926,70 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
mnNewMenu_7.add(panel1None);
mnNewMenu_7.add(separator_7);
mnNewMenu_7.add(panel1Proc);
mnNewMenu_7.add(panel1CFR);
mnNewMenu_7.add(panel1Fern);
mnNewMenu_7.add(separator_8);
mnNewMenu_7.add(panel1Bytecode);
mnNewMenu_7.add(panel1Smali);
mnNewMenu_7.add(panel1Hexcode);
mnNewMenu_6.add(mnNewMenu_8);
mnNewMenu_8.add(panel2None);
mnNewMenu_8.add(separator_9);
mnNewMenu_8.add(panel2Proc);
mnNewMenu_8.add(panel2CFR);
mnNewMenu_8.add(panel2Fern);
mnNewMenu_8.add(separator_10);
mnNewMenu_8.add(panel2Bytecode);
mnNewMenu_8.add(panel2Smali);
mnNewMenu_8.add(panel2Hexcode);
mnNewMenu_6.add(mnNewMenu_9);
mnNewMenu_9.add(panel3None);
mnNewMenu_9.add(separator_11);
mnNewMenu_9.add(panel3Proc);
mnNewMenu_9.add(panel3CFR);
mnNewMenu_9.add(panel3Fern);
mnNewMenu_9.add(separator_12);
mnNewMenu_9.add(panel3Bytecode);
mnNewMenu_9.add(panel3Smali);
mnNewMenu_9.add(panel3Hexcode);
menuBar.add(mnSettings);
mnSettings.add(autoCompileSmali);
mnSettings.add(autoCompileOnRefresh);
mnSettings.add(chckbxmntmNewCheckItem_12);
chckbxmntmNewCheckItem_12.setSelected(true);
mnSettings.add(refreshOnChange);
mnSettings.add(separator_6);
@ -1007,8 +1207,6 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
mnBytecodeDecompilerSettings.add(chckbxmntmAppendBrackets);
mnNewMenu_5.setVisible(false);
menuBar.add(mnNewMenu_5);
mntmNewMenuItem_6.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
@ -1067,6 +1265,20 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
mnNewMenu_1.add(separator_4);
mnNewMenu_1.add(mnRecentPlugins);
mnNewMenu_1.add(separator_5);
mntmCodeSequenceDiagram.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (!BytecodeViewer.loadedClasses.isEmpty())
PluginManager.runPlugin(new CodeSequenceDiagram());
else {
System.out
.println("Plugin not ran, put some classes in first.");
BytecodeViewer
.showMessage("Plugin not ran, put some classes in first.");
}
}
});
mnNewMenu_1.add(mntmCodeSequenceDiagram);
mnNewMenu_1.add(mntmNewMenuItem_1);
mnNewMenu_1.add(mntmShowMainMethods);
mnNewMenu_1.add(mntmShowAllStrings);
@ -1109,13 +1321,13 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
fc.setFileFilter(new GroovyPythonRubyFileFilter());
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(This);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
if (returnVal == JFileChooser.APPROVE_OPTION)
try {
BytecodeViewer.viewer.setC(true);
BytecodeViewer.viewer.setIcon(true);
BytecodeViewer.startPlugin(fc.getSelectedFile());
BytecodeViewer.viewer.setC(false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e1) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e1);
}
@ -1198,6 +1410,13 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
return path;
}
public String appendJava(File file) {
String path = file.getAbsolutePath();
if (!path.endsWith(".java"))
path = path + ".java";
return path;
}
@Override
public void openClassFile(final String name, final ClassNode cn) {
for (final VisibleComponent vc : rfComps) {

View file

@ -113,16 +113,26 @@ public class WorkPane extends VisibleComponent implements ActionListener {
return (ClassViewer) tabs.getSelectedComponent();
}
public java.awt.Component[] getLoadedViewers() {
return (java.awt.Component[])tabs.getComponents();
}
@Override
public void actionPerformed(final ActionEvent arg0) {
if(BytecodeViewer.viewer.autoCompileOnRefresh.isSelected())
try {
BytecodeViewer.compileSmali(false);
} catch(java.lang.NullPointerException e) {
}
final JButton src = (JButton) arg0.getSource();
if (src == refreshClass) {
final Component tabComp = tabs.getSelectedComponent();
if (tabComp != null) {
src.setEnabled(false);
BytecodeViewer.viewer.setC(true);
BytecodeViewer.viewer.setIcon(true);
((ClassViewer) tabComp).startPaneUpdater(src);
BytecodeViewer.viewer.setC(false);
BytecodeViewer.viewer.setIcon(false);
}
}
}

View file

@ -0,0 +1,91 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.UIManager;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.api.Plugin;
/**
* A simple code sequence diagram.
*
* @author Konloch
*
*/
public class CodeSequenceDiagram extends Plugin {
@SuppressWarnings("unchecked")
@Override
public void execute(ArrayList<ClassNode> classNodeList) {
if(BytecodeViewer.viewer.workPane.getCurrentClass() == null) {
BytecodeViewer.showMessage("First open a class file.");
return;
}
ClassNode c = BytecodeViewer.viewer.workPane.getCurrentClass().cn;
JFrame frame = new JFrame("Code Sequence Diagram - " +c.name);
frame.setIconImages(BytecodeViewer.iconList);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(400, 320);
mxGraph graph = new mxGraph();
graph.setVertexLabelsMovable(false);
graph.setGridEnabled(true);
graph.setEnabled(false);
graph.setCellsEditable(false);
graph.setCellsSelectable(false);
graph.setCellsMovable(false);
graph.setCellsLocked(true);
Object parent = graph.getDefaultParent();
Font font = UIManager.getDefaults().getFont("TabbedPane.font");
AffineTransform affinetransform = new AffineTransform();
FontRenderContext frc = new FontRenderContext(affinetransform,true,true);
graph.getModel().beginUpdate();
try
{
int testX = 10;
int testY = 0;
double magicNumber = 5.8;
for(MethodNode m : (ArrayList<MethodNode>)c.methods) {
String mIdentifier = c.name+"."+m.name+m.desc;
Object node = graph.insertVertex(parent, null, mIdentifier, testX, testY, mIdentifier.length() * magicNumber, 30);
Object attach = node;
testX += (int) (font.getStringBounds(mIdentifier, frc).getWidth()) + 60;
for (AbstractInsnNode i : m.instructions.toArray()) {
if (i instanceof MethodInsnNode) {
MethodInsnNode mi = (MethodInsnNode) i;
String identifier = mi.owner+"."+mi.name+mi.desc;
Object node2 = graph.insertVertex(parent, null, identifier, testX, testY, identifier.length() * 5, 30);
testX += (int) (font.getStringBounds(identifier, frc).getWidth()) + 60;
graph.insertEdge(parent, null, null, attach, node2);
attach = node2;
}
}
testY += 60;
testX = 10;
}
} finally {
graph.getModel().endUpdate();
}
mxGraphComponent graphComponent = new mxGraphComponent(graph);
frame.getContentPane().add(graphComponent);
frame.setVisible(true);
}
}