diff --git a/BytecodeViewer 2.5.2.jar b/BytecodeViewer 2.6.0.jar similarity index 86% rename from BytecodeViewer 2.5.2.jar rename to BytecodeViewer 2.6.0.jar index 2883c18b..78b758a7 100644 Binary files a/BytecodeViewer 2.5.2.jar and b/BytecodeViewer 2.6.0.jar differ diff --git a/README.txt b/README.txt index c8a17f9e..844103d2 100644 --- a/README.txt +++ b/README.txt @@ -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. @@ -236,4 +236,28 @@ Changelog: 01/06/2015 - Silenced the error connecting to update server for offline mode. 01/06/2015 - Fixed a search function with Android APKs. --- 2.5.2 ---: -01/06/2015 - Completely fixed the search function with Android APKs. \ No newline at end of file +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. \ No newline at end of file diff --git a/libs/baksmali-2.0.3.jar b/libs/baksmali-2.0.3.jar new file mode 100644 index 00000000..b29ded5d Binary files /dev/null and b/libs/baksmali-2.0.3.jar differ diff --git a/libs/cfr_0_92.jar b/libs/cfr_0_93.jar similarity index 80% rename from libs/cfr_0_92.jar rename to libs/cfr_0_93.jar index fcbec940..f14b2778 100644 Binary files a/libs/cfr_0_92.jar and b/libs/cfr_0_93.jar differ diff --git a/libs/cfr_0_94.jar b/libs/cfr_0_94.jar new file mode 100644 index 00000000..641ce1e7 Binary files /dev/null and b/libs/cfr_0_94.jar differ diff --git a/libs/fernflower2014.jar b/libs/fernflower2015.jar similarity index 52% rename from libs/fernflower2014.jar rename to libs/fernflower2015.jar index c456ff95..f0dd605c 100644 Binary files a/libs/fernflower2014.jar and b/libs/fernflower2015.jar differ diff --git a/libs/jgraphx.jar b/libs/jgraphx.jar new file mode 100644 index 00000000..e70d4c82 Binary files /dev/null and b/libs/jgraphx.jar differ diff --git a/libs/smali-2.0.3-obf-patched.jar b/libs/smali-2.0.3-obf-patched.jar new file mode 100644 index 00000000..81322e79 Binary files /dev/null and b/libs/smali-2.0.3-obf-patched.jar differ diff --git a/src/me/konloch/kontainer/io/HTTPRequest.java b/src/me/konloch/kontainer/io/HTTPRequest.java new file mode 100644 index 00000000..2f85b227 --- /dev/null +++ b/src/me/konloch/kontainer/io/HTTPRequest.java @@ -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>> 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>> 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 st; + + try { + setup(); + + st = new ArrayList(); + 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 st; + + try { + setup(); + + st = new ArrayList(); + 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; + } + +} \ No newline at end of file diff --git a/src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java b/src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java index 9d100cdb..65561c50 100644 --- a/src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java +++ b/src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java @@ -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 recentFiles = DiskReader.loadArrayList(filesName, false); private static ArrayList 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 readme = new ArrayList(); - 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 @@ -487,7 +489,16 @@ public class BytecodeViewer { viewer.setVisible(true); 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 getLoadedClasses() { @@ -511,75 +520,115 @@ public class BytecodeViewer { } return a; } + + //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; + } - public static void openFiles(File[] files, boolean recentFiles) { + 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; - - 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 (fn.endsWith(".jar") || fn.endsWith(".zip")) { - try { - JarUtils.put(f, BytecodeViewer.loadedClasses); - } catch (final Exception e) { - new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); - update = false; - } - - } else if (fn.endsWith(".class")) { - try { - byte[] bytes = JarUtils.getBytes(new FileInputStream(f)); - String cafebabe = String.format("%02X", bytes[0]) - + String.format("%02X", bytes[1]) - + String.format("%02X", bytes[2]) - + String.format("%02X", bytes[3]); - if(cafebabe.toLowerCase().equals("cafebabe")) { - final ClassNode cn = JarUtils.getNode(bytes); - BytecodeViewer.loadedClasses.put(cn.name, cn); - } else { - showMessage(fn+": Header does not start with CAFEBABE, ignoring."); + 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; - } - } catch (final Exception e) { - new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); - update = false; - } - } else if(fn.endsWith(".apk")) { - Thread t = new Thread() { - @Override - public void run() { - try { - String name = getRandomizedName()+".jar"; - File output = new File(tempDirectory + fs + name); - Dex2Jar.dex2Jar(f, output); - BytecodeViewer.viewer.setIcon(false); - openFiles(new File[]{output}, false); - } catch (final Exception e) { - new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); + 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 { + JarUtils.put(f, BytecodeViewer.loadedClasses); + } catch (final Exception e) { + new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); + update = false; + } + + } else if (fn.endsWith(".class")) { + try { + byte[] bytes = JarUtils.getBytes(new FileInputStream(f)); + String cafebabe = String.format("%02X", bytes[0]) + + String.format("%02X", bytes[1]) + + String.format("%02X", bytes[2]) + + String.format("%02X", bytes[3]); + if(cafebabe.toLowerCase().equals("cafebabe")) { + final ClassNode cn = JarUtils.getNode(bytes); + BytecodeViewer.loadedClasses.put(cn.name, cn); + } else { + showMessage(fn+": Header does not start with CAFEBABE, ignoring."); + update = false; + } + } catch (final Exception e) { + new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); + update = false; + } + } else if(fn.endsWith(".apk") || fn.endsWith(".dex")) { + try { + String name = getRandomizedName()+".jar"; + File output = new File(tempDirectory + fs + name); + Dex2Jar.dex2Jar(f, output); + BytecodeViewer.viewer.setIcon(false); + openFiles(new File[]{output}, false); + } catch (final Exception e) { + new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); + } + return; + } } } - }; - t.start(); - return; + } + } catch (final Exception e) { + new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); + } finally { + BytecodeViewer.viewer.setIcon(false); + + if(update) + try { + MainViewerGUI.getComponent(FileNavigationPane.class).updateTree(); + } catch(java.lang.NullPointerException e) { + } + } } - } - } - - BytecodeViewer.viewer.setIcon(false); - - if(update) - try { - MainViewerGUI.getComponent(FileNavigationPane.class).updateTree(); - } catch(java.lang.NullPointerException e) { - } + }; + t.start(); } public static void startPlugin(File plugin) { diff --git a/src/the/bytecode/club/bytecodeviewer/CoolClassAdapter.java b/src/the/bytecode/club/bytecodeviewer/CoolClassAdapter.java new file mode 100644 index 00000000..5e9f5d14 --- /dev/null +++ b/src/the/bytecode/club/bytecodeviewer/CoolClassAdapter.java @@ -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); + } + +} diff --git a/src/the/bytecode/club/bytecodeviewer/Dex2Jar.java b/src/the/bytecode/club/bytecodeviewer/Dex2Jar.java index 42fe49ea..8a79c331 100644 --- a/src/the/bytecode/club/bytecodeviewer/Dex2Jar.java +++ b/src/the/bytecode/club/bytecodeviewer/Dex2Jar.java @@ -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); } diff --git a/src/the/bytecode/club/bytecodeviewer/JarUtils.java b/src/the/bytecode/club/bytecodeviewer/JarUtils.java index 3565190c..d8cf05b7 100644 --- a/src/the/bytecode/club/bytecodeviewer/JarUtils.java +++ b/src/the/bytecode/club/bytecodeviewer/JarUtils.java @@ -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; diff --git a/src/the/bytecode/club/bytecodeviewer/Settings.java b/src/the/bytecode/club/bytecodeviewer/Settings.java index 73e0f879..08ccf7f8 100644 --- a/src/the/bytecode/club/bytecodeviewer/Settings.java +++ b/src/the/bytecode/club/bytecodeviewer/Settings.java @@ -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. } diff --git a/src/the/bytecode/club/bytecodeviewer/ZipUtils.java b/src/the/bytecode/club/bytecodeviewer/ZipUtils.java index fe5571a4..e8d692fe 100644 --- a/src/the/bytecode/club/bytecodeviewer/ZipUtils.java +++ b/src/the/bytecode/club/bytecodeviewer/ZipUtils.java @@ -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; @@ -13,6 +16,61 @@ import javax.swing.filechooser.FileFilter; * Rudimentary utility class for Zip archives creation. */ 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 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 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(); + } + } } \ No newline at end of file diff --git a/src/the/bytecode/club/bytecodeviewer/api/ASMUtil_OLD.java b/src/the/bytecode/club/bytecodeviewer/api/ASMUtil_OLD.java index 74aee917..9c9ef6cd 100644 --- a/src/the/bytecode/club/bytecodeviewer/api/ASMUtil_OLD.java +++ b/src/the/bytecode/club/bytecodeviewer/api/ASMUtil_OLD.java @@ -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 { } } } + + 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)); } - /* - * 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); } } - */ } } \ No newline at end of file diff --git a/src/the/bytecode/club/bytecodeviewer/api/BytecodeViewer.java b/src/the/bytecode/club/bytecodeviewer/api/BytecodeViewer.java index 4ee5bb49..8d41e019 100644 --- a/src/the/bytecode/club/bytecodeviewer/api/BytecodeViewer.java +++ b/src/the/bytecode/club/bytecodeviewer/api/BytecodeViewer.java @@ -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. * diff --git a/src/the/bytecode/club/bytecodeviewer/api/ExceptionUI.java b/src/the/bytecode/club/bytecodeviewer/api/ExceptionUI.java index 0b0c5698..94cb4788 100644 --- a/src/the/bytecode/club/bytecodeviewer/api/ExceptionUI.java +++ b/src/the/bytecode/club/bytecodeviewer/api/ExceptionUI.java @@ -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); } diff --git a/src/the/bytecode/club/bytecodeviewer/decompilers/java/CFRDecompiler.java b/src/the/bytecode/club/bytecodeviewer/decompilers/CFRDecompiler.java similarity index 97% rename from src/the/bytecode/club/bytecodeviewer/decompilers/java/CFRDecompiler.java rename to src/the/bytecode/club/bytecodeviewer/decompilers/CFRDecompiler.java index 1a7ef213..562e764b 100644 --- a/src/the/bytecode/club/bytecodeviewer/decompilers/java/CFRDecompiler.java +++ b/src/the/bytecode/club/bytecodeviewer/decompilers/CFRDecompiler.java @@ -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); diff --git a/src/the/bytecode/club/bytecodeviewer/decompilers/java/FernFlowerDecompiler.java b/src/the/bytecode/club/bytecodeviewer/decompilers/FernFlowerDecompiler.java similarity index 91% rename from src/the/bytecode/club/bytecodeviewer/decompilers/java/FernFlowerDecompiler.java rename to src/the/bytecode/club/bytecodeviewer/decompilers/FernFlowerDecompiler.java index c954ae6d..339b5894 100644 --- a/src/the/bytecode/club/bytecodeviewer/decompilers/java/FernFlowerDecompiler.java +++ b/src/the/bytecode/club/bytecodeviewer/decompilers/FernFlowerDecompiler.java @@ -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; @@ -60,12 +60,10 @@ public class FernFlowerDecompiler extends JavaDecompiler { public String decompileClassNode(final ClassNode cn) { final ClassWriter cw = new ClassWriter(0); cn.accept(cw); - - String fileStart = BytecodeViewer.tempDirectory + BytecodeViewer.fs - + "temp"; - int fileNumber = getClassNumber(fileStart, ".class"); - - final File tempClass = new File(fileStart + fileNumber + ".class"); + + String start = getUniqueName("", ".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 { diff --git a/src/the/bytecode/club/bytecodeviewer/decompilers/JavaDecompiler.java b/src/the/bytecode/club/bytecodeviewer/decompilers/JavaDecompiler.java new file mode 100644 index 00000000..f7ab1541 --- /dev/null +++ b/src/the/bytecode/club/bytecodeviewer/decompilers/JavaDecompiler.java @@ -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; + } +} diff --git a/src/the/bytecode/club/bytecodeviewer/decompilers/java/ProcyonDecompiler.java b/src/the/bytecode/club/bytecodeviewer/decompilers/ProcyonDecompiler.java similarity index 97% rename from src/the/bytecode/club/bytecodeviewer/decompilers/java/ProcyonDecompiler.java rename to src/the/bytecode/club/bytecodeviewer/decompilers/ProcyonDecompiler.java index 3d7fba4c..c014c1e3 100644 --- a/src/the/bytecode/club/bytecodeviewer/decompilers/java/ProcyonDecompiler.java +++ b/src/the/bytecode/club/bytecodeviewer/decompilers/ProcyonDecompiler.java @@ -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); diff --git a/src/the/bytecode/club/bytecodeviewer/decompilers/Smali.java b/src/the/bytecode/club/bytecodeviewer/decompilers/Smali.java new file mode 100644 index 00000000..8ab1a3f0 --- /dev/null +++ b/src/the/bytecode/club/bytecodeviewer/decompilers/Smali.java @@ -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; + } + +} diff --git a/src/the/bytecode/club/bytecodeviewer/decompilers/java/JavaDecompiler.java b/src/the/bytecode/club/bytecodeviewer/decompilers/java/JavaDecompiler.java deleted file mode 100644 index ac677a1d..00000000 --- a/src/the/bytecode/club/bytecodeviewer/decompilers/java/JavaDecompiler.java +++ /dev/null @@ -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; - } -} diff --git a/src/the/bytecode/club/bytecodeviewer/gui/ClassViewer.java b/src/the/bytecode/club/bytecodeviewer/gui/ClassViewer.java index 0e7d656f..b4ff90bb 100644 --- a/src/the/bytecode/club/bytecodeviewer/gui/ClassViewer.java +++ b/src/the/bytecode/club/bytecodeviewer/gui/ClassViewer.java @@ -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 lnData = new ArrayList(); 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) { @@ -704,6 +771,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; diff --git a/src/the/bytecode/club/bytecodeviewer/gui/ExportJar.java b/src/the/bytecode/club/bytecodeviewer/gui/ExportJar.java index 6558cce5..efd1b37d 100644 --- a/src/the/bytecode/club/bytecodeviewer/gui/ExportJar.java +++ b/src/the/bytecode/club/bytecodeviewer/gui/ExportJar.java @@ -41,10 +41,16 @@ public class ExportJar extends JFrame { btnNewButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { - BytecodeViewer.viewer.setC(true); - JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), jarPath, - mani.getText()); - BytecodeViewer.viewer.setC(false); + BytecodeViewer.viewer.setIcon(true); + Thread t = new Thread() { + @Override + public void run() { + JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), jarPath, + mani.getText()); + BytecodeViewer.viewer.setIcon(false); + } + }; + t.start(); dispose(); } }); diff --git a/src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java b/src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java index bdb45cdb..76c48091 100644 --- a/src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java +++ b/src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java @@ -242,36 +242,40 @@ public class FileNavigationPane extends VisibleComponent implements } public void updateTree() { - treeRoot.removeAllChildren(); - for (final Entry entry : BytecodeViewer.loadedClasses - .entrySet()) { - String name = entry.getKey(); - final String[] spl = name.split("\\/"); - if (spl.length < 2) { - treeRoot.add(new MyTreeNode(name)); - } else { - MyTreeNode parent = treeRoot; - for (final String s : spl) { - MyTreeNode child = null; - for (int i = 0; i < parent.getChildCount(); i++) { - if (((MyTreeNode) parent.getChildAt(i)).getUserObject() - .equals(s)) { - child = (MyTreeNode) parent.getChildAt(i); - break; + try { + treeRoot.removeAllChildren(); + for (final Entry entry : BytecodeViewer.loadedClasses + .entrySet()) { + String name = entry.getKey(); + final String[] spl = name.split("\\/"); + if (spl.length < 2) { + treeRoot.add(new MyTreeNode(name)); + } else { + MyTreeNode parent = treeRoot; + for (final String s : spl) { + MyTreeNode child = null; + for (int i = 0; i < parent.getChildCount(); i++) { + if (((MyTreeNode) parent.getChildAt(i)).getUserObject() + .equals(s)) { + child = (MyTreeNode) parent.getChildAt(i); + break; + } } + if (child == null) { + child = new MyTreeNode(s); + parent.add(child); + } + parent = child; } - if (child == null) { - child = new MyTreeNode(s); - parent.add(child); - } - parent = child; } } + + treeRoot.sort(); + tree.expandPath(new TreePath(tree.getModel().getRoot())); + tree.updateUI(); + } catch(java.util.ConcurrentModificationException e) { + //ignore, the last file will reset everything } - - treeRoot.sort(); - tree.expandPath(new TreePath(tree.getModel().getRoot())); - tree.updateUI(); // expandAll(tree, true); } @@ -310,18 +314,22 @@ public class FileNavigationPane extends VisibleComponent implements @Override public void paint(final Graphics g) { - super.paint(g); - if (m == null) { - m = new StringMetrics((Graphics2D) g); - } - if (treeRoot.getChildCount() < 1) { - g.setColor(new Color(0, 0, 0, 100)); - g.fillRect(0, 0, getWidth(), getHeight()); - g.setColor(Color.white); - String s = "Drag class/jar here"; - g.drawString(s, - ((int) ((getWidth() / 2) - (m.getWidth(s) / 2))), - getHeight() / 2); + try { + super.paint(g); + if (m == null) { + m = new StringMetrics((Graphics2D) g); + } + if (treeRoot.getChildCount() < 1) { + g.setColor(new Color(0, 0, 0, 100)); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.white); + String s = "Drag class/jar here"; + g.drawString(s, + ((int) ((getWidth() / 2) - (m.getWidth(s) / 2))), + getHeight() / 2); + } + } catch(java.lang.InternalError | java.lang.NullPointerException e) { + } } } diff --git a/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java b/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java index 3b392803..5950eeee 100644 --- a/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java +++ b/src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java @@ -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); - JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), - file.getAbsolutePath()); - BytecodeViewer.viewer.setC(false); + 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(), + 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,13 +647,17 @@ 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)); - BytecodeViewer.viewer.setIcon(false); + + 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) { @@ -749,47 +925,71 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier { mnNewMenu_6.add(mnNewMenu_7); 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); @@ -1006,9 +1206,7 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier { mnBytecodeDecompilerSettings.add(debugHelpers); 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); } @@ -1197,6 +1409,13 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier { path = path + ".class"; 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) { diff --git a/src/the/bytecode/club/bytecodeviewer/gui/WorkPane.java b/src/the/bytecode/club/bytecodeviewer/gui/WorkPane.java index d500ff16..6eb9ae8f 100644 --- a/src/the/bytecode/club/bytecodeviewer/gui/WorkPane.java +++ b/src/the/bytecode/club/bytecodeviewer/gui/WorkPane.java @@ -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); } } } diff --git a/src/the/bytecode/club/bytecodeviewer/plugins/CodeSequenceDiagram.java b/src/the/bytecode/club/bytecodeviewer/plugins/CodeSequenceDiagram.java new file mode 100644 index 00000000..7ba8f893 --- /dev/null +++ b/src/the/bytecode/club/bytecodeviewer/plugins/CodeSequenceDiagram.java @@ -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 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)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); + } + +} \ No newline at end of file