First Commit

First commit, beta 1.0, report all bugs to konloch@gmail.com or create a
thread on http://the.bytecode.club
This commit is contained in:
Kalen Kinloch 2014-10-18 16:39:09 -07:00
parent 1f33f0a8cf
commit 06ef9408a4
60 changed files with 7031 additions and 3 deletions

BIN
BytecodeViewer.jar Normal file

Binary file not shown.

View file

@ -1,4 +1,19 @@
bytecode-viewer Bytecode Viewer is a Java Bytecode Viewer, GUI FernFlower Java Decompiler, 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.
Bytecode Viewer Code from various projects has been used, including but not limited to:
J-RET by WaterWolf
JHexPane by Sam Koivu
JSynaxPane by Ayman Al
Commons IO by Apache
ASM by OW2
Features:
Java Decompiler - It uses a modified version of FernFlower.
Bytecode Decompiler - A modified version of J-RET's.
Hex Viewer - Powered by JHexPane.
Each Decompiler/Viewer is toggleable.
Fully Featured Search System.
A Plugin System With Built In Plugins. (Show All Strings, Malicious Code Scanner, String Decrypters, etc)
Recent Files.
And more! Give it a try for yourself!

BIN
libs/asm-all-5.0.3.jar Normal file

Binary file not shown.

BIN
libs/commons-codec-1.9.jar Normal file

Binary file not shown.

BIN
libs/commons-io-2.4.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
libs/groovy-all-2.1.7.jar Normal file

Binary file not shown.

BIN
libs/jruby.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

14
plugins/Skeleton.gy Normal file
View file

@ -0,0 +1,14 @@
import the.bytecode.club.bytecodeviewer.plugins.Plugin;
import the.bytecode.club.bytecodeviewer.plugins.PluginConsole;
import java.util.ArrayList;
import org.objectweb.asm.tree.ClassNode;
public class Skeleton extends Plugin {
@Override
public void execute(ArrayList<ClassNode> classNodesList) {
PluginConsole gui = new PluginConsole("Skeleton");
gui.setVisible(true);
gui.appendText("exceuted skeleton");
}
}

15
plugins/Skeleton.rb Normal file
View file

@ -0,0 +1,15 @@
require 'java'
java_import 'the.bytecode.club.bytecodeviewer.plugins.Plugin'
java_import 'the.bytecode.club.bytecodeviewer.plugins.PluginConsole'
java_import 'java.lang.System'
java_import 'java.util.ArrayList'
java_import 'org.objectweb.asm.tree.ClassNode'
class Skeleton < Plugin
def execute(classNodeList)
gui = PluginConsole.new "Skeleton"
gui.setVisible(true)
gui.appendText("exceuted skeleton")
end
end

13
plugins/skeleton.py Normal file
View file

@ -0,0 +1,13 @@
from the.bytecode.club.bytecodeviewer.plugins import Plugin
from the.bytecode.club.bytecodeviewer.plugins import PluginConsole
from java.lang import System
from java.lang import Boolean
from java.util import ArrayList
from org.objectweb.asm.tree import ClassNode
class skeleton(Plugin):
def execute(classNodeList, poop): #for some reason it requires a second arg
gui = PluginConsole("Skeleton")
gui.setVisible(Boolean.TRUE)
gui.appendText("exceuted skeleton")

View file

@ -0,0 +1,289 @@
package com.jhe.hexed;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* Created by IntelliJ IDEA.
* User: laullon
* Date: 08-abr-2003
* Time: 13:21:09
*/
public class JHexEditor extends JPanel implements FocusListener,AdjustmentListener,MouseWheelListener
{
private static final long serialVersionUID = 2289328616534802372L;
byte[] buff;
public int cursor;
protected static Font font=new Font("Monospaced",0,12);
protected int border=2;
public boolean DEBUG=false;
private JPanel panel;
private JScrollBar sb;
private int inicio=0;
private int lineas=10;
public JHexEditor(byte[] buff)
{
super();
this.buff=buff;
this.addMouseWheelListener(this);
sb=new JScrollBar(JScrollBar.VERTICAL);
sb.addAdjustmentListener(this);
sb.setMinimum(0);
sb.setMaximum(buff.length/getLineas());
JPanel p1,p2,p3;
//centro
p1=new JPanel(new BorderLayout(1,1));
p1.add(new JHexEditorHEX(this),BorderLayout.CENTER);
p1.add(new Columnas(),BorderLayout.NORTH);
// izq.
p2=new JPanel(new BorderLayout(1,1));
p2.add(new Filas(),BorderLayout.CENTER);
p2.add(new Caja(),BorderLayout.NORTH);
// der
p3=new JPanel(new BorderLayout(1,1));
p3.add(sb,BorderLayout.EAST);
p3.add(new JHexEditorASCII(this),BorderLayout.CENTER);
p3.add(new Caja(),BorderLayout.NORTH);
panel=new JPanel();
panel.setLayout(new BorderLayout(1,1));
panel.add(p1,BorderLayout.CENTER);
panel.add(p2,BorderLayout.WEST);
panel.add(p3,BorderLayout.EAST);
this.setLayout(new BorderLayout(1,1));
this.add(panel,BorderLayout.CENTER);
}
public void paint(Graphics g)
{
FontMetrics fn=getFontMetrics(font);
Rectangle rec=this.getBounds();
lineas=(rec.height/fn.getHeight())-1;
int n=(buff.length/16)-1;
if(lineas>n) { lineas=n; inicio=0; }
sb.setValues(getInicio(),+getLineas(),0,buff.length/16);
sb.setValueIsAdjusting(true);
super.paint(g);
}
protected void actualizaCursor()
{
int n=(cursor/16);
System.out.print("- "+inicio+"<"+n+"<"+(lineas+inicio)+"("+lineas+")");
if(n<inicio) inicio=n;
else if(n>=inicio+lineas) inicio=n-(lineas-1);
System.out.println(" - "+inicio+"<"+n+"<"+(lineas+inicio)+"("+lineas+")");
repaint();
}
protected int getInicio()
{
return inicio;
}
protected int getLineas()
{
return lineas;
}
protected void fondo(Graphics g,int x,int y,int s)
{
FontMetrics fn=getFontMetrics(font);
g.fillRect(((fn.stringWidth(" ")+1)*x)+border,(fn.getHeight()*y)+border,((fn.stringWidth(" ")+1)*s),fn.getHeight()+1);
}
protected void cuadro(Graphics g,int x,int y,int s)
{
FontMetrics fn=getFontMetrics(font);
g.drawRect(((fn.stringWidth(" ")+1)*x)+border,(fn.getHeight()*y)+border,((fn.stringWidth(" ")+1)*s),fn.getHeight()+1);
}
protected void printString(Graphics g,String s,int x,int y)
{
FontMetrics fn=getFontMetrics(font);
g.drawString(s,((fn.stringWidth(" ")+1)*x)+border,((fn.getHeight()*(y+1))-fn.getMaxDescent())+border);
}
public void focusGained(FocusEvent e)
{
this.repaint();
}
public void focusLost(FocusEvent e)
{
this.repaint();
}
public void adjustmentValueChanged(AdjustmentEvent e)
{
inicio=e.getValue();
if(inicio<0) inicio=0;
repaint();
}
public void mouseWheelMoved(MouseWheelEvent e)
{
inicio+=(e.getUnitsToScroll());
if((inicio+lineas)>=buff.length/16) inicio=(buff.length/16)-lineas;
if(inicio<0) inicio=0;
repaint();
}
public void keyPressed(KeyEvent e)
{
/*switch(e.getKeyCode())
{
case 33: // rep
if(cursor>=(16*lineas)) cursor-=(16*lineas);
actualizaCursor();
break;
case 34: // fin
if(cursor<(buff.length-(16*lineas))) cursor+=(16*lineas);
actualizaCursor();
break;
case 35: // fin
cursor=buff.length-1;
actualizaCursor();
break;
case 36: // ini
cursor=0;
actualizaCursor();
break;
case 37: // <--
if(cursor!=0) cursor--;
actualizaCursor();
break;
case 38: // <--
if(cursor>15) cursor-=16;
actualizaCursor();
break;
case 39: // -->
if(cursor!=(buff.length-1)) cursor++;
actualizaCursor();
break;
case 40: // -->
if(cursor<(buff.length-16)) cursor+=16;
actualizaCursor();
break;
}*/
}
private class Columnas extends JPanel
{
private static final long serialVersionUID = -1734199617526339842L;
public Columnas()
{
this.setLayout(new BorderLayout(1,1));
}
public Dimension getPreferredSize()
{
return getMinimumSize();
}
public Dimension getMinimumSize()
{
Dimension d=new Dimension();
FontMetrics fn=getFontMetrics(font);
int h=fn.getHeight();
int nl=1;
d.setSize(((fn.stringWidth(" ")+1)*+((16*3)-1))+(border*2)+1,h*nl+(border*2)+1);
return d;
}
public void paint(Graphics g)
{
Dimension d=getMinimumSize();
g.setColor(Color.white);
g.fillRect(0,0,d.width,d.height);
g.setColor(Color.black);
g.setFont(font);
for(int n=0;n<16;n++)
{
if(n==(cursor%16)) cuadro(g,n*3,0,2);
String s="00"+Integer.toHexString(n);
s=s.substring(s.length()-2);
printString(g,s,n*3,0);
}
}
}
private class Caja extends JPanel
{
private static final long serialVersionUID = -6124062720565016834L;
public Dimension getPreferredSize()
{
return getMinimumSize();
}
public Dimension getMinimumSize()
{
Dimension d=new Dimension();
FontMetrics fn=getFontMetrics(font);
int h=fn.getHeight();
d.setSize((fn.stringWidth(" ")+1)+(border*2)+1,h+(border*2)+1);
return d;
}
}
private class Filas extends JPanel
{
private static final long serialVersionUID = 8797347523486018051L;
public Filas()
{
this.setLayout(new BorderLayout(1,1));
}
public Dimension getPreferredSize()
{
return getMinimumSize();
}
public Dimension getMinimumSize()
{
Dimension d=new Dimension();
FontMetrics fn=getFontMetrics(font);
int h=fn.getHeight();
int nl=getLineas();
d.setSize((fn.stringWidth(" ")+1)*(8)+(border*2)+1,h*nl+(border*2)+1);
return d;
}
public void paint(Graphics g)
{
Dimension d=getMinimumSize();
g.setColor(Color.white);
g.fillRect(0,0,d.width,d.height);
g.setColor(Color.black);
g.setFont(font);
int ini=getInicio();
int fin=ini+getLineas();
int y=0;
for(int n=ini;n<fin;n++)
{
if(n==(cursor/16)) cuadro(g,0,y,8);
String s="0000000000000"+Integer.toHexString(n);
s=s.substring(s.length()-8);
printString(g,s,0,y++);
}
}
}
}

View file

@ -0,0 +1,152 @@
package com.jhe.hexed;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* Created by IntelliJ IDEA.
* User: laullon
* Date: 09-abr-2003
* Time: 12:47:18
*/
public class JHexEditorASCII extends JComponent implements MouseListener,KeyListener
{
private static final long serialVersionUID = 5505374841731053461L;
private JHexEditor he;
public JHexEditorASCII(JHexEditor he)
{
this.he=he;
addMouseListener(this);
addKeyListener(this);
addFocusListener(he);
}
public Dimension getPreferredSize()
{
debug("getPreferredSize()");
return getMinimumSize();
}
public Dimension getMinimumSize()
{
debug("getMinimumSize()");
Dimension d=new Dimension();
FontMetrics fn=getFontMetrics(JHexEditor.font);
int h=fn.getHeight();
int nl=he.getLineas();
d.setSize((fn.stringWidth(" ")+1)*(16)+(he.border*2)+1,h*nl+(he.border*2)+1);
return d;
}
public void paint(Graphics g)
{
debug("paint("+g+")");
debug("cursor="+he.cursor+" buff.length="+he.buff.length);
Dimension d=getMinimumSize();
g.setColor(Color.white);
g.fillRect(0,0,d.width,d.height);
g.setColor(Color.black);
g.setFont(JHexEditor.font);
//datos ascii
int ini=he.getInicio()*16;
int fin=ini+(he.getLineas()*16);
if(fin>he.buff.length) fin=he.buff.length;
int x=0;
int y=0;
for(int n=ini;n<fin;n++)
{
if(n==he.cursor)
{
g.setColor(Color.blue);
if(hasFocus()) he.fondo(g,x,y,1); else he.cuadro(g,x,y,1);
if(hasFocus()) g.setColor(Color.white); else g.setColor(Color.black);
} else
{
g.setColor(Color.black);
}
String s=""+new Character((char)he.buff[n]);
if((he.buff[n]<20)||(he.buff[n]>126)) s=""+(char)16;
he.printString(g,s,(x++),y);
if(x==16)
{
x=0;
y++;
}
}
}
private void debug(String s)
{
if(he.DEBUG) System.out.println("JHexEditorASCII ==> "+s);
}
// calcular la posicion del raton
public int calcularPosicionRaton(int x,int y)
{
FontMetrics fn=getFontMetrics(JHexEditor.font);
x=x/(fn.stringWidth(" ")+1);
y=y/fn.getHeight();
debug("x="+x+" ,y="+y);
return x+((y+he.getInicio())*16);
}
// mouselistener
public void mouseClicked(MouseEvent e)
{
debug("mouseClicked("+e+")");
he.cursor=calcularPosicionRaton(e.getX(),e.getY());
this.requestFocus();
he.repaint();
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
//KeyListener
public void keyTyped(KeyEvent e)
{
/*debug("keyTyped("+e+")");
he.buff[he.cursor]=(byte)e.getKeyChar();
if(he.cursor!=(he.buff.length-1)) he.cursor++;
he.repaint();*/
}
public void keyPressed(KeyEvent e)
{
debug("keyPressed("+e+")");
he.keyPressed(e);
}
public void keyReleased(KeyEvent e)
{
debug("keyReleased("+e+")");
}
public boolean isFocusTraversable()
{
return true;
}
}

View file

@ -0,0 +1,178 @@
package com.jhe.hexed;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* Created by IntelliJ IDEA.
* User: laullon
* Date: 09-abr-2003
* Time: 12:47:32
*/
public class JHexEditorHEX extends JComponent implements MouseListener,KeyListener
{
private static final long serialVersionUID = 1481995655372014571L;
private JHexEditor he;
private int cursor=0;
public JHexEditorHEX(JHexEditor he)
{
this.he=he;
addMouseListener(this);
addKeyListener(this);
addFocusListener(he);
}
/*public Dimension getPreferredSize()
{
debug("getPreferredSize()");
return getMinimumSize();
}*/
public Dimension getMaximumSize()
{
debug("getMaximumSize()");
return getMinimumSize();
}
/*public Dimension getMinimumSize()
{
debug("getMinimumSize()");
Dimension d=new Dimension();
FontMetrics fn=getFontMetrics(he.font);
int h=fn.getHeight();
int nl=he.getLineas();
d.setSize(((fn.stringWidth(" ")+1)*+((16*3)-1))+(he.border*2)+1,h*nl+(he.border*2)+1);
return d;
}*/
public void paint(Graphics g)
{
debug("paint("+g+")");
debug("cursor="+he.cursor+" buff.length="+he.buff.length);
Dimension d=getMinimumSize();
g.setColor(Color.white);
g.fillRect(0,0,d.width,d.height);
g.setColor(Color.black);
g.setFont(JHexEditor.font);
int ini=he.getInicio()*16;
int fin=ini+(he.getLineas()*16);
if(fin>he.buff.length) fin=he.buff.length;
//datos hex
int x=0;
int y=0;
for(int n=ini;n<fin;n++)
{
if(n==he.cursor)
{
if(hasFocus())
{
g.setColor(Color.black);
he.fondo(g,(x*3),y,2);
g.setColor(Color.blue);
he.fondo(g,(x*3)+cursor,y,1);
} else
{
g.setColor(Color.blue);
he.cuadro(g,(x*3),y,2);
}
if(hasFocus()) g.setColor(Color.white); else g.setColor(Color.black);
} else
{
g.setColor(Color.black);
}
String s=("0"+Integer.toHexString(he.buff[n]));
s=s.substring(s.length()-2);
he.printString(g,s,((x++)*3),y);
if(x==16)
{
x=0;
y++;
}
}
}
private void debug(String s)
{
if(he.DEBUG) System.out.println("JHexEditorHEX ==> "+s);
}
// calcular la posicion del raton
public int calcularPosicionRaton(int x,int y)
{
FontMetrics fn=getFontMetrics(JHexEditor.font);
x=x/((fn.stringWidth(" ")+1)*3);
y=y/fn.getHeight();
debug("x="+x+" ,y="+y);
return x+((y+he.getInicio())*16);
}
// mouselistener
public void mouseClicked(MouseEvent e)
{
debug("mouseClicked("+e+")");
he.cursor=calcularPosicionRaton(e.getX(),e.getY());
this.requestFocus();
he.repaint();
}
public void mousePressed(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
//KeyListener
public void keyTyped(KeyEvent e)
{
debug("keyTyped("+e+")");
/*char c=e.getKeyChar();
if(((c>='0')&&(c<='9'))||((c>='A')&&(c<='F'))||((c>='a')&&(c<='f')))
{
char[] str=new char[2];
String n="00"+Integer.toHexString((int)he.buff[he.cursor]);
if(n.length()>2) n=n.substring(n.length()-2);
str[1-cursor]=n.charAt(1-cursor);
str[cursor]=e.getKeyChar();
he.buff[he.cursor]=(byte)Integer.parseInt(new String(str),16);
if(cursor!=1) cursor=1;
else if(he.cursor!=(he.buff.length-1)){ he.cursor++; cursor=0;}
he.actualizaCursor();
}*/
}
public void keyPressed(KeyEvent e)
{
debug("keyPressed("+e+")");
he.keyPressed(e);
}
public void keyReleased(KeyEvent e)
{
debug("keyReleased("+e+")");
}
public boolean isFocusTraversable()
{
return true;
}
}

View file

@ -0,0 +1,84 @@
package me.konloch.kontainer.io;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
/**
* Used to load from the disk, optional caching
*
* @author Konloch
*
*/
public class DiskReader {
public static Random random = new Random();
public static HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
/**
* Used to load from file, allows caching
*/
public synchronized static ArrayList<String> loadArrayList(String fileName, boolean cache) {
ArrayList<String> array = new ArrayList<String>();
if(!map.containsKey(fileName)) {
try {
File file = new File(fileName);
BufferedReader reader = new BufferedReader(new FileReader(file));
String add;
while((add = reader.readLine()) != null)
array.add(add);
reader.close();
if(cache)
map.put(fileName, array);
} catch(Exception e) {
e.printStackTrace();
}
} else {
array = map.get(fileName);
}
return array;
}
/**
* Used to load a string via line number
* lineNumber = -1 means random.
*/
public static String loadString(String fileName, int lineNumber, boolean cache) throws Exception {
ArrayList<String> array;
if(!map.containsKey(fileName)) {
array = new ArrayList<String>();
File file = new File(fileName);
BufferedReader reader = new BufferedReader(new FileReader(file));
String add;
while((add = reader.readLine()) != null)
array.add(add);
reader.close();
if(cache)
map.put(fileName, array);
} else {
array = map.get(fileName);
}
if(lineNumber == -1) {
int size = array.size();
return array.get(random.nextInt(size));
} else
return array.get(lineNumber);
}
}

View file

@ -0,0 +1,176 @@
package me.konloch.kontainer.io;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
/**
* This method will save to disk, if it failed it will keep trying until
* it's saved to disk
*
* @author Konloch
*
*/
public class DiskWriter {
/**
* Used to insert a difference string with preserving the file extension
* @param fileName The file name
* @param difference Normally an integer
* @return The filename with the difference inserted and the file extension preserved
*/
public static String insertFileName(String fileName, String difference) {
String[] babe = fileName.split("\\.");
int count = 0;
int math = babe.length;
String m = "";
for(String s2 : babe) {
m += s2;
if(math-2 == count)
m += difference+".";
else if(math-1 != count)
m += ".";
count++;
}
return m;
}
/**
* Writes a new line to the file, if it doesn't exist it will automatically create it.
* @param filename
* @param fileContents
* @param debug
*/
public static synchronized void writeNewLine(String filename, byte[] fileContents, boolean debug) {
PrintWriter writer = null;
String original = filename;
int counter = 0;
boolean saved = false;
while(!saved) {
try {
writer = new PrintWriter(new BufferedWriter(new FileWriter(filename, true)));
writer.println(fileContents);
if(debug)
System.out.println("Saved " + filename + " to disk");
saved = true;
} catch(Exception e) {
if(debug)
System.out.println("Failed saving, trying to save as " + filename);
if(original.contains(".")) {
filename = insertFileName(original, ""+counter);
} else
filename = original + counter;
counter++;
}
}
writer.close();
}
/**
* Writes a string to the file
* @param filename
* @param lineToWrite
* @param debug
*/
public static synchronized void writeNewLine(String filename, String lineToWrite, boolean debug) {
PrintWriter writer = null;
String original = filename;
int counter = 0;
boolean saved = false;
while(!saved) {
try {
writer = new PrintWriter(new BufferedWriter(new FileWriter(filename, true)));
writer.println(lineToWrite);
if(debug)
System.out.println("Saved " + filename+">"+lineToWrite + " to disk");
saved = true;
} catch(Exception e) {
if(debug)
System.out.println("Failed saving, trying to save as " + filename);
if(original.contains(".")) {
filename = insertFileName(original, ""+counter);
} else
filename = original + counter;
counter++;
}
}
writer.close();
}
/**
* Deletes the original file if it exists, then writes the fileContents[] to the file.
* @param filename
* @param fileContents
* @param debug
*/
public static synchronized void replaceFile(String filename, byte[] fileContents, boolean debug) {
File f = new File(filename);
if(f.exists())
f.delete();
PrintWriter writer = null;
String original = filename;
int counter = 0;
boolean saved = false;
while(!saved) {
try {
writer = new PrintWriter(new BufferedWriter(new FileWriter(filename, true)));
writer.println(fileContents);
if(debug)
System.out.println("Saved " + filename + " to disk");
saved = true;
} catch(Exception e) {
if(debug)
System.out.println("Failed saving, trying to save as " + filename);
if(original.contains(".")) {
filename = insertFileName(original, ""+counter);
} else
filename = original + counter;
counter++;
}
}
writer.close();
}
/**
* Deletes the original file if it exists, then writes the lineToWrite to the file.
* @param filename
* @param lineToWrite
* @param debug
*/
public static synchronized void replaceFile(String filename, String lineToWrite, boolean debug) {
File f = new File(filename);
if(f.exists())
f.delete();
PrintWriter writer = null;
String original = filename;
int counter = 0;
boolean saved = false;
while(!saved) {
try {
writer = new PrintWriter(new BufferedWriter(new FileWriter(filename, true)));
writer.println(lineToWrite);
if(debug)
System.out.println("Saved " + filename+">"+lineToWrite + " to disk");
saved = true;
} catch(Exception e) {
if(debug)
System.out.println("Failed saving, trying to save as " + filename + "_");
if(original.contains(".")) {
filename = insertFileName(original, ""+counter);
} else
filename = original + counter;
counter++;
}
}
writer.close();
}
}

BIN
src/resources/1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

View file

@ -0,0 +1,331 @@
package the.bytecode.club.bytecodeviewer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import javax.swing.JDialog;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
import org.apache.commons.io.FileUtils;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.gui.FileNavigationPane;
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
import the.bytecode.club.bytecodeviewer.gui.SearchingPane;
import the.bytecode.club.bytecodeviewer.gui.WorkPane;
import the.bytecode.club.bytecodeviewer.plugins.PluginManager;
/**
* A lightweight Java Bytecode Viewer/GUI Decompiler, developed by Konloch - http://konloch.me
*
* Are you a Java Reverse Engineer? Or maybe you want to learn Java Reverse Engineering?
* Join The Bytecode Club - http://the.bytecode.club
* We're noob friendly, and censorship free.
*
* All you have to do is add a jar or class file into the workspace, select the file you want
* then it will start decompiling the class in the background, when it's done it will show
* the Source code, Bytecode and Hexcode of the class file you chose.
*
* 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.
* You can either use one of the pre-written plugins, or write your own. It supports groovy, python and
* ruby scripting. Once a plugin is activated, it will send a ClassNode ArrayList of every single
* class loaded in the file system to the execute function, this allows the user to handle it
* completely using ASM.
*
* Bytecode Decompiler, File Navigation Pane, Search Pane and Work Pane based off of J-RET by WaterWolf - https://github.com/Waterwolf/Java-ReverseEngineeringTool
* HexViewer pane based off of Re-Java's by Sami Koivu - http://rejava.sourceforge.net/
* Java Decompiler is a modified version of FernFlower
*
* TODO:
* Fix the fucking import jar method cause it's a bitch on memory (at the.bytecode.club.bytecodeviewer.JarUtils.getNode(JarUtils.java:83))
* JSyntaxPane can be horribly slow for really big classfiles, might need to find a work around to this (create the syntaxpane object in the thread, then pass it to the GUI)s
* Make the search results clickable
* Add a tool to build a flowchart of all the classes, and what methods execute what classes, and those method, read chatlog
* Middle mouse click should close tabs
* http://i.imgur.com/yHaai9D.png
*
* 10/4/2014 - Designed a POC GUI, still needs a lot of work.
* 10/4/2014 - Started importing J-RET's backend.
* 10/5/2014 - Finished importing J-RET's backend.
* 10/6/2014 - Started modifying J-RET's UI.
* 10/6/2014 - Added several FernFlower options.
* 10/6/2014 - Fixed the class search function so it doesn't require exact class names.
* 10/6/2014 - Added save as, it'll save all of the loaded classes into one jar file (GUI Jar-Jar now).
* 10/6/2014 - Centered the select jar text inside of the file navigator.
* 10/6/2014 - Properly threaded the open jar function, now fernflower/bytecode decompiler runs in the background.
* 10/6/2014 - Added a hex viewer (Instead of using Re-Java's, I've decided to use a modified version of JHexEditor).
* 10/6/2014 - Made all of the viewer (Sourcecode, Bytecode & Hexcode toggleable).
* 10/7/2014 - Fixed the search function.
* 10/7/2014 - You can now add new files without it creating a new workspace.
* 10/7/2014 - Added new workspace button underneath File, this will reset the workspace.
* 10/7/2014 - Renamed File>Open.. to File>Add..
* 10/7/2014 - Added recent files.
* 10/7/2014 - Did some bitch work, the project has no warnings now.
* 10/7/2014 - Added waiting cursors to anything that will require waiting or loading.
* 10/8/2014 - Searching now runs in a background thread.
* 10/8/2014 - Added File>About.
* 10/8/2014 - The main GUI now starts in the middle of your screen, same with the about window.
* 10/8/2014 - Made the File Navigator Pane, Workspace Pane & Search Pane a little sexier.
* 10/9/2014 - Started on a Plugin system
* 10/9/2014 - Added a malicious code scanner plugin, based off of the one from J-RET, this searches for a multitude of classes/packages that can be used for malicious purposes.
* 10/9/2014 - Added a show all strings plugin, this grabs all the declared strings and displays them in a nice little window.
* 10/9/2014 - Fixed a bug with Bytecode Decompiler, where it would it display \r and \n as return carriages.
* 10/9/2014 - Fixed the Bytecode Decompiler>Debug Instructions option.
* 10/9/2014 - Save Class Files As is now renamed to Save Files As.
* 10/9/2014 - Save Files As now saves jar resources, not just classfiles.
* 10/9/2014 - Added an 'Are you sure' pane when you click on File>New Workspace.
* 10/9/2014 - Save Files As is no longer dependent on the File System, now if you're on windows and you have a file called AA, and one called Aa, you're fine.
* 10/11/2014 - Modified the FernFlower library, it no longer spits out System.out.println's while processing a method, this has sped it up quite a lot.
* 10/12/2014 - Fix an issue when resizing.
* 10/12/2014 - Modified the core slighty to no longer have a modularized decompiling system (since there are only 2 decompilers anyways).
* 10/12/2014 - Fixed an issue with decompiling multiple files at once.
* 10/12/2014 - The Plugin Console now shows the plugin's name on the title.
* 10/12/2014 - Debug Helpers will now debug all jump instructions by showing what instruction is on the line it's suppose to goto, example: 90. goto 120 // line 120 is PUTFIELD Animable_Sub4.anInt1593 : I
* 10/12/2014 - Now when you select an already opened file, it will automatically go to that opened pane.
* 10/14/2014 - Added the option 'exact' to the class finder.
* 10/14/2014 - Added the option 'exact' to the searcher, now it'll search for .contains when unselected.
* 10/14/2014 - Stopped the use of StringBuffer, replaced all instances with StringBuilder.
* 10/14/2014 - Added Labels and Try-Catch blocks to the Bytecode Decompiler.
* 10/14/2014 - For panes that are not selected, the corresponding decompiler will not execute.
* 10/14/2014 - Added plugin Show Main Methods, this will show every single public static void main(String[]).
* 10/14/2014 - Plugins can no longer be ran when there is no loaded classes.
* 10/14/2014 - The Malicious Code Scanner now has gui option pane before you run it.
* 10/14/2014 - Added a java/io option to the Malicious Code Scanner.
* 10/14/2014 - Added save Java files as.
* 10/15/2014 - Added save as Jar file. (Export as Jar)
* 10/15/2014 - Added the option to ASCII only strings in the Bytecode Decompiler.
* 10/15/2014 - External plugins are now fully functional, same with recent plugins.
* 10/16/2014 - Removed all refences of 'ClassContainer'.
* 10/16/2014 - Rewrote the tempfile system.
* 10/16/2014 - Moved the file import to BytecodeViewer.class.
* 10/16/2014 - Fixed a jTree updating issue.
* 10/16/2014 - Now if you try search with an empty string, it won't search.
* 10/16/2014 - Added Replace Strings plugin.
* 10/16/2014 - Added a loading icon that displays whenever a background task is being executed.
*
* @author Konloch
*
*/
public class BytecodeViewer {
public static MainViewerGUI viewer = null;
public static HashMap<String, ClassNode> loadedClasses = new HashMap<String, ClassNode>();
public static HashMap<String, byte[]> loadedResources = new HashMap<String, byte[]>();
private static String filesName = "recentfiles.bcv";
private static String pluginsName = "recentplugins.bcv";
private static ArrayList<String> recentFiles = DiskReader.loadArrayList(filesName, false);
private static ArrayList<String> recentPlugins = DiskReader.loadArrayList(pluginsName, false);
private static int maxRecentFiles = 25;
public static String tempDirectory = "bcv_temp\\";
public static void main(String[] args) {
cleanup();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
cleanup();
}
});
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
viewer = new MainViewerGUI();
viewer.setVisible(true);
resetRecentFilesMenu();
}
public static ClassNode getClassNode(String name) {
if(loadedClasses.containsKey(name))
return loadedClasses.get(name);
return null;
}
public static ArrayList<ClassNode> getLoadedClasses() {
ArrayList<ClassNode> a = new ArrayList<ClassNode>();
if(loadedClasses != null)
for (Entry<String, ClassNode> entry : loadedClasses.entrySet()) {
Object value = entry.getValue();
ClassNode cln = (ClassNode)value;
a.add(cln);
}
return a;
}
public static void openFiles(File[] files) {
BytecodeViewer.viewer.setC(true);
BytecodeViewer.viewer.setIcon(true);
for (final File f : files) {
final String fn = f.getName();
if (fn.endsWith(".jar")) {
try {
JarUtils.put(f, BytecodeViewer.loadedClasses);
} catch (final IOException e) {
e.printStackTrace();
}
}
else if (fn.endsWith(".class")) {
try {
final ClassNode cn = JarUtils.getNode(JarUtils.getBytes(new FileInputStream(f)));
BytecodeViewer.loadedClasses.put(cn.name, cn);
} catch (final Exception e) {
e.printStackTrace();
}
}
}
for(File f : files)
BytecodeViewer.addRecentFile(f);
BytecodeViewer.viewer.setC(false);
BytecodeViewer.viewer.setIcon(false);
MainViewerGUI.getComponent(FileNavigationPane.class).updateTree();
}
public static void startPlugin(File plugin) {
if(!plugin.exists())
return;
try {
PluginManager.runPlugin(plugin);
} catch (Exception e) {
e.printStackTrace();
}
addRecentPlugin(plugin);
}
public static void showMessage(String message) {
JOptionPane.showMessageDialog(viewer, message);
}
@SuppressWarnings("deprecation")
public static void resetWorkSpace() {
JOptionPane pane = new JOptionPane("Are you sure you want to reset the workspace?\n\rIt will also reset your file navigator and search.");
Object[] options = new String[] { "Yes", "No" };
pane.setOptions(options);
JDialog dialog = pane.createDialog(viewer, "Bytecode Viewer - Reset Workspace");
dialog.show();
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) {
loadedResources.clear();
loadedClasses.clear();
MainViewerGUI.getComponent(FileNavigationPane.class).resetWorkspace();
MainViewerGUI.getComponent(WorkPane.class).resetWorkspace();
MainViewerGUI.getComponent(SearchingPane.class).resetWorkspace();
}
}
private static ArrayList<String> killList = new ArrayList<String>();
public static void addRecentFile(File f) {
for(int i = 0; i < recentFiles.size(); i++) { //remove dead strings
String s = recentFiles.get(i);
if(s.isEmpty() || i > maxRecentFiles)
killList.add(s);
}
if(!killList.isEmpty()) {
for(String s : killList)
recentFiles.remove(s);
killList.clear();
}
if(recentFiles.contains(f.getAbsolutePath())) //already added on the list
recentFiles.remove(f.getAbsolutePath());
if(recentFiles.size() >= maxRecentFiles)
recentFiles.remove(maxRecentFiles-1); //zero indexing
recentFiles.add(0, f.getAbsolutePath());
DiskWriter.replaceFile(filesName, quickConvert(recentFiles), false);
resetRecentFilesMenu();
}
private static ArrayList<String> killList2 = new ArrayList<String>();
public static void addRecentPlugin(File f) {
for(int i = 0; i < recentPlugins.size(); i++) { //remove dead strings
String s = recentPlugins.get(i);
if(s.isEmpty() || i > maxRecentFiles)
killList2.add(s);
}
if(!killList2.isEmpty()) {
for(String s : killList2)
recentPlugins.remove(s);
killList2.clear();
}
if(recentPlugins.contains(f.getAbsolutePath())) //already added on the list
recentPlugins.remove(f.getAbsolutePath());
if(recentPlugins.size() >= maxRecentFiles)
recentPlugins.remove(maxRecentFiles-1); //zero indexing
recentPlugins.add(0, f.getAbsolutePath());
DiskWriter.replaceFile(pluginsName, quickConvert(recentPlugins), false);
resetRecentFilesMenu();
}
public static void resetRecentFilesMenu() {
viewer.mnRecentFiles.removeAll();
for(String s : recentFiles)
if(!s.isEmpty()) {
JMenuItem m = new JMenuItem(s);
m.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuItem m = (JMenuItem)e.getSource();
openFiles(new File[]{new File(m.getText())});
}
});
viewer.mnRecentFiles.add(m);
}
viewer.mnRecentPlugins.removeAll();
for(String s : recentPlugins)
if(!s.isEmpty()) {
JMenuItem m = new JMenuItem(s);
m.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuItem m = (JMenuItem)e.getSource();
startPlugin(new File(m.getText()));
}
});
viewer.mnRecentPlugins.add(m);
}
}
private static File tempF = null;
public static void cleanup() {
tempF = new File(tempDirectory);
try {
FileUtils.deleteDirectory(tempF);
} catch (IOException e) {
e.printStackTrace();
}
tempF.mkdir();
}
private static String quickConvert(ArrayList<String> a) {
String s = "";
for(String r : a)
s += r+"\r";
return s;
}
}

View file

@ -0,0 +1,14 @@
package the.bytecode.club.bytecodeviewer;
import org.objectweb.asm.tree.ClassNode;
/**
* Used to represent whenever a file has been opened
*
* @author Konloch
*
*/
public interface FileChangeNotifier {
public void openClassFile(String name, ClassNode cn);
}

View file

@ -0,0 +1,925 @@
package the.bytecode.club.bytecodeviewer;
import java.awt.datatransfer.DataFlavor;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
/**
* This class makes it easy to drag and drop files from the operating system to
* a Java program. Any <tt>java.awt.Component</tt> can be dropped onto, but only
* <tt>javax.swing.JComponent</tt>s will indicate the drop event with a changed
* border.
* <p/>
* To use this class, construct a new <tt>FileDrop</tt> by passing it the target
* component and a <tt>Listener</tt> to receive notification when file(s) have
* been dropped. Here is an example:
* <p/>
* <code><pre>
* JPanel myPanel = new JPanel();
* new FileDrop( myPanel, new FileDrop.Listener()
* { public void filesDropped( java.io.File[] files )
* {
* // handle file drop
* ...
* } // end filesDropped
* }); // end FileDrop.Listener
* </pre></code>
* <p/>
* You can specify the border that will appear when files are being dragged by
* calling the constructor with a <tt>javax.swing.border.Border</tt>. Only
* <tt>JComponent</tt>s will show any indication with a border.
* <p/>
* You can turn on some debugging features by passing a <tt>PrintStream</tt>
* object (such as <tt>System.out</tt>) into the full constructor. A
* <tt>null</tt> value will result in no extra debugging information being
* output.
* <p/>
*
* <p>
* I'm releasing this code into the Public Domain. Enjoy.
* </p>
* <p>
* <em>Original author: Robert Harder, rharder@usa.net</em>
* </p>
* <p>
* 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
* </p>
*
* @author Robert Harder
* @author rharder@users.sf.net
* @version 1.0.1
*/
@SuppressWarnings({ "rawtypes", "unused", "unchecked" })
public class FileDrop {
private transient javax.swing.border.Border normalBorder;
private transient java.awt.dnd.DropTargetListener dropListener;
/** Discover if the running JVM is modern enough to have drag and drop. */
private static Boolean supportsDnD;
// Default border color
private static java.awt.Color defaultBorderColor = new java.awt.Color(0f,
0f, 1f, 0.25f);
/**
* Constructs a {@link FileDrop} with a default light-blue border and, if
* <var>c</var> is a {@link java.awt.Container}, recursively sets all
* elements contained within as drop targets, though only the top level
* container will change borders.
*
* @param c Component on which files will be dropped.
* @param listener Listens for <tt>filesDropped</tt>.
* @since 1.0
*/
public FileDrop(final java.awt.Component c, final Listener listener) {
this(null, // Logging stream
c, // Drop target
javax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2,
defaultBorderColor), // Drag border
true, // Recursive
listener);
} // end constructor
/**
* Constructor with a default border and the option to recursively set drop
* targets. If your component is a <tt>java.awt.Container</tt>, then each of
* its children components will also listen for drops, though only the
* parent will change borders.
*
* @param c Component on which files will be dropped.
* @param recursive Recursively set children as drop targets.
* @param listener Listens for <tt>filesDropped</tt>.
* @since 1.0
*/
public FileDrop(final java.awt.Component c, final boolean recursive,
final Listener listener) {
this(null, // Logging stream
c, // Drop target
javax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2,
defaultBorderColor), // Drag border
recursive, // Recursive
listener);
} // end constructor
/**
* Constructor with a default border and debugging optionally turned on.
* With Debugging turned on, more status messages will be displayed to
* <tt>out</tt>. A common way to use this constructor is with
* <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for the
* parameter <tt>out</tt> will result in no debugging output.
*
* @param out PrintStream to record debugging info or null for no debugging.
* @param out
* @param c Component on which files will be dropped.
* @param listener Listens for <tt>filesDropped</tt>.
* @since 1.0
*/
public FileDrop(final java.io.PrintStream out, final java.awt.Component c,
final Listener listener) {
this(out, // Logging stream
c, // Drop target
javax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2,
defaultBorderColor), false, // Recursive
listener);
} // end constructor
/**
* Constructor with a default border, debugging optionally turned on and the
* option to recursively set drop targets. If your component is a
* <tt>java.awt.Container</tt>, then each of its children components will
* also listen for drops, though only the parent will change borders. With
* Debugging turned on, more status messages will be displayed to
* <tt>out</tt>. A common way to use this constructor is with
* <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for the
* parameter <tt>out</tt> will result in no debugging output.
*
* @param out PrintStream to record debugging info or null for no debugging.
* @param out
* @param c Component on which files will be dropped.
* @param recursive Recursively set children as drop targets.
* @param listener Listens for <tt>filesDropped</tt>.
* @since 1.0
*/
public FileDrop(final java.io.PrintStream out, final java.awt.Component c,
final boolean recursive, final Listener listener) {
this(out, // Logging stream
c, // Drop target
javax.swing.BorderFactory.createMatteBorder(2, 2, 2, 2,
defaultBorderColor), // Drag border
recursive, // Recursive
listener);
} // end constructor
/**
* Constructor with a specified border
*
* @param c Component on which files will be dropped.
* @param dragBorder Border to use on <tt>JComponent</tt> when dragging
* occurs.
* @param listener Listens for <tt>filesDropped</tt>.
* @since 1.0
*/
public FileDrop(final java.awt.Component c,
final javax.swing.border.Border dragBorder, final Listener listener) {
this(null, // Logging stream
c, // Drop target
dragBorder, // Drag border
false, // Recursive
listener);
} // end constructor
/**
* Constructor with a specified border and the option to recursively set
* drop targets. If your component is a <tt>java.awt.Container</tt>, then
* each of its children components will also listen for drops, though only
* the parent will change borders.
*
* @param c Component on which files will be dropped.
* @param dragBorder Border to use on <tt>JComponent</tt> when dragging
* occurs.
* @param recursive Recursively set children as drop targets.
* @param listener Listens for <tt>filesDropped</tt>.
* @since 1.0
*/
public FileDrop(final java.awt.Component c,
final javax.swing.border.Border dragBorder,
final boolean recursive, final Listener listener) {
this(null, c, dragBorder, recursive, listener);
} // end constructor
/**
* Constructor with a specified border and debugging optionally turned on.
* With Debugging turned on, more status messages will be displayed to
* <tt>out</tt>. A common way to use this constructor is with
* <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for the
* parameter <tt>out</tt> will result in no debugging output.
*
* @param out PrintStream to record debugging info or null for no debugging.
* @param c Component on which files will be dropped.
* @param dragBorder Border to use on <tt>JComponent</tt> when dragging
* occurs.
* @param listener Listens for <tt>filesDropped</tt>.
* @since 1.0
*/
public FileDrop(final java.io.PrintStream out, final java.awt.Component c,
final javax.swing.border.Border dragBorder, final Listener listener) {
this(out, // Logging stream
c, // Drop target
dragBorder, // Drag border
false, // Recursive
listener);
} // end constructor
/**
* Full constructor with a specified border and debugging optionally turned
* on. With Debugging turned on, more status messages will be displayed to
* <tt>out</tt>. A common way to use this constructor is with
* <tt>System.out</tt> or <tt>System.err</tt>. A <tt>null</tt> value for the
* parameter <tt>out</tt> will result in no debugging output.
*
* @param out PrintStream to record debugging info or null for no debugging.
* @param c Component on which files will be dropped.
* @param dragBorder Border to use on <tt>JComponent</tt> when dragging
* occurs.
* @param recursive Recursively set children as drop targets.
* @param listener Listens for <tt>filesDropped</tt>.
* @since 1.0
*/
public FileDrop(final java.io.PrintStream out, final java.awt.Component c,
final javax.swing.border.Border dragBorder,
final boolean recursive, final Listener listener) {
if (supportsDnD()) { // Make a drop listener
dropListener = new java.awt.dnd.DropTargetListener() {
public void dragEnter(final java.awt.dnd.DropTargetDragEvent evt) {
log(out, "FileDrop: dragEnter event.");
// Is this an acceptable drag event?
if (isDragOk(out, evt)) {
// If it's a Swing component, set its border
if (c instanceof javax.swing.JComponent) {
final javax.swing.JComponent jc = (javax.swing.JComponent) c;
normalBorder = jc.getBorder();
log(out, "FileDrop: normal border saved.");
jc.setBorder(dragBorder);
log(out, "FileDrop: drag border set.");
} // end if: JComponent
// Acknowledge that it's okay to enter
// evt.acceptDrag(
// java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
evt.acceptDrag(java.awt.dnd.DnDConstants.ACTION_COPY);
log(out, "FileDrop: event accepted.");
} // end if: drag ok
else { // Reject the drag event
evt.rejectDrag();
log(out, "FileDrop: event rejected.");
} // end else: drag not ok
} // end dragEnter
public void dragOver(final java.awt.dnd.DropTargetDragEvent evt) { // This
// is
// called
// continually
// as
// long
// as
// the
// mouse
// is
// over
// the
// drag
// target.
} // end dragOver
public void drop(final java.awt.dnd.DropTargetDropEvent evt) {
log(out, "FileDrop: drop event.");
try { // Get whatever was dropped
final java.awt.datatransfer.Transferable tr = evt
.getTransferable();
// Is it a file list?
if (tr.isDataFlavorSupported(java.awt.datatransfer.DataFlavor.javaFileListFlavor)) {
// Say we'll take it.
// evt.acceptDrop (
// java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE );
evt.acceptDrop(java.awt.dnd.DnDConstants.ACTION_COPY);
log(out, "FileDrop: file list accepted.");
// Get a useful list
final java.util.List fileList = (java.util.List) tr
.getTransferData(java.awt.datatransfer.DataFlavor.javaFileListFlavor);
final java.util.Iterator iterator = fileList.iterator();
// Convert list to array
final java.io.File[] filesTemp = new java.io.File[fileList
.size()];
fileList.toArray(filesTemp);
final java.io.File[] files = filesTemp;
// Alert listener to drop.
if (listener != null) {
listener.filesDropped(files);
}
// Mark that drop is completed.
evt.getDropTargetContext().dropComplete(true);
log(out, "FileDrop: drop complete.");
} // end if: file list
else // this section will check for a reader flavor.
{
// Thanks, Nathan!
// BEGIN 2007-09-12 Nathan Blomquist -- Linux
// (KDE/Gnome) support added.
final DataFlavor[] flavors = tr
.getTransferDataFlavors();
boolean handled = false;
for (int zz = 0; zz < flavors.length; zz++) {
if (flavors[zz].isRepresentationClassReader()) {
// Say we'll take it.
// evt.acceptDrop (
// java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE
// );
evt.acceptDrop(java.awt.dnd.DnDConstants.ACTION_COPY);
log(out, "FileDrop: reader accepted.");
final Reader reader = flavors[zz]
.getReaderForText(tr);
final BufferedReader br = new BufferedReader(
reader);
if (listener != null) {
listener.filesDropped(createFileArray(
br, out));
}
// Mark that drop is completed.
evt.getDropTargetContext().dropComplete(
true);
log(out, "FileDrop: drop complete.");
handled = true;
break;
}
}
if (!handled) {
log(out,
"FileDrop: not a file list or reader - abort.");
evt.rejectDrop();
}
// END 2007-09-12 Nathan Blomquist -- Linux
// (KDE/Gnome) support added.
} // end else: not a file list
} // end try
catch (final java.io.IOException io) {
log(out, "FileDrop: IOException - abort:");
io.printStackTrace(out);
evt.rejectDrop();
} // end catch IOException
catch (final java.awt.datatransfer.UnsupportedFlavorException ufe) {
log(out,
"FileDrop: UnsupportedFlavorException - abort:");
ufe.printStackTrace(out);
evt.rejectDrop();
} // end catch: UnsupportedFlavorException
finally {
// If it's a Swing component, reset its border
if (c instanceof javax.swing.JComponent) {
final javax.swing.JComponent jc = (javax.swing.JComponent) c;
jc.setBorder(normalBorder);
log(out, "FileDrop: normal border restored.");
} // end if: JComponent
} // end finally
} // end drop
public void dragExit(final java.awt.dnd.DropTargetEvent evt) {
log(out, "FileDrop: dragExit event.");
// If it's a Swing component, reset its border
if (c instanceof javax.swing.JComponent) {
final javax.swing.JComponent jc = (javax.swing.JComponent) c;
jc.setBorder(normalBorder);
log(out, "FileDrop: normal border restored.");
} // end if: JComponent
} // end dragExit
public void dropActionChanged(
final java.awt.dnd.DropTargetDragEvent evt) {
log(out, "FileDrop: dropActionChanged event.");
// Is this an acceptable drag event?
if (isDragOk(out, evt)) { // evt.acceptDrag(
// java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE
// );
evt.acceptDrag(java.awt.dnd.DnDConstants.ACTION_COPY);
log(out, "FileDrop: event accepted.");
} // end if: drag ok
else {
evt.rejectDrag();
log(out, "FileDrop: event rejected.");
} // end else: drag not ok
} // end dropActionChanged
}; // end DropTargetListener
// Make the component (and possibly children) drop targets
makeDropTarget(out, c, recursive);
} // end if: supports dnd
else {
log(out, "FileDrop: Drag and drop is not supported with this JVM");
} // end else: does not support DnD
} // end constructor
private static boolean supportsDnD() { // Static Boolean
if (supportsDnD == null) {
boolean support = false;
try {
final Class arbitraryDndClass = Class
.forName("java.awt.dnd.DnDConstants");
support = true;
} // end try
catch (final Exception e) {
support = false;
} // end catch
supportsDnD = new Boolean(support);
} // end if: first time through
return supportsDnD.booleanValue();
} // end supportsDnD
// BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
private static String ZERO_CHAR_STRING = "" + (char) 0;
private static File[] createFileArray(final BufferedReader bReader,
final PrintStream out) {
try {
final java.util.List list = new java.util.ArrayList();
java.lang.String line = null;
while ((line = bReader.readLine()) != null) {
try {
// kde seems to append a 0 char to the end of the reader
if (ZERO_CHAR_STRING.equals(line)) {
continue;
}
final java.io.File file = new java.io.File(
new java.net.URI(line));
list.add(file);
} catch (final Exception ex) {
log(out, "Error with " + line + ": " + ex.getMessage());
}
}
return (java.io.File[]) list.toArray(new File[list.size()]);
} catch (final IOException ex) {
log(out, "FileDrop: IOException");
}
return new File[0];
}
// END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support added.
private void makeDropTarget(final java.io.PrintStream out,
final java.awt.Component c, final boolean recursive) {
// Make drop target
final java.awt.dnd.DropTarget dt = new java.awt.dnd.DropTarget();
try {
dt.addDropTargetListener(dropListener);
} // end try
catch (final java.util.TooManyListenersException e) {
e.printStackTrace();
log(out,
"FileDrop: Drop will not work due to previous error. Do you have another listener attached?");
} // end catch
// Listen for hierarchy changes and remove the drop target when the
// parent gets cleared out.
c.addHierarchyListener(new java.awt.event.HierarchyListener() {
public void hierarchyChanged(final java.awt.event.HierarchyEvent evt) {
log(out, "FileDrop: Hierarchy changed.");
final java.awt.Component parent = c.getParent();
if (parent == null) {
c.setDropTarget(null);
log(out, "FileDrop: Drop target cleared from component.");
} // end if: null parent
else {
new java.awt.dnd.DropTarget(c, dropListener);
log(out, "FileDrop: Drop target added to component.");
} // end else: parent not null
} // end hierarchyChanged
}); // end hierarchy listener
if (c.getParent() != null) {
new java.awt.dnd.DropTarget(c, dropListener);
}
if (recursive && (c instanceof java.awt.Container)) {
// Get the container
final java.awt.Container cont = (java.awt.Container) c;
// Get it's components
final java.awt.Component[] comps = cont.getComponents();
// Set it's components as listeners also
for (int i = 0; i < comps.length; i++) {
makeDropTarget(out, comps[i], recursive);
}
} // end if: recursively set components as listener
} // end dropListener
/** Determine if the dragged data is a file list. */
private boolean isDragOk(final java.io.PrintStream out,
final java.awt.dnd.DropTargetDragEvent evt) {
boolean ok = false;
// Get data flavors being dragged
final java.awt.datatransfer.DataFlavor[] flavors = evt
.getCurrentDataFlavors();
// See if any of the flavors are a file list
int i = 0;
while (!ok && i < flavors.length) {
// BEGIN 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support
// added.
// Is the flavor a file list?
final DataFlavor curFlavor = flavors[i];
if (curFlavor
.equals(java.awt.datatransfer.DataFlavor.javaFileListFlavor)
|| curFlavor.isRepresentationClassReader()) {
ok = true;
}
// END 2007-09-12 Nathan Blomquist -- Linux (KDE/Gnome) support
// added.
i++;
} // end while: through flavors
// If logging is enabled, show data flavors
if (out != null) {
if (flavors.length == 0) {
log(out, "FileDrop: no data flavors.");
}
for (i = 0; i < flavors.length; i++) {
log(out, flavors[i].toString());
}
} // end if: logging enabled
return ok;
} // end isDragOk
/** Outputs <tt>message</tt> to <tt>out</tt> if it's not null. */
private static void log(final java.io.PrintStream out, final String message) { // Log
// message
// if
// requested
if (out != null) {
out.println(message);
}
} // end log
/**
* Removes the drag-and-drop hooks from the component and optionally from
* the all children. You should call this if you add and remove components
* after you've set up the drag-and-drop. This will recursively unregister
* all components contained within <var>c</var> if <var>c</var> is a
* {@link java.awt.Container}.
*
* @param c The component to unregister as a drop target
* @since 1.0
*/
public static boolean remove(final java.awt.Component c) {
return remove(null, c, true);
} // end remove
/**
* Removes the drag-and-drop hooks from the component and optionally from
* the all children. You should call this if you add and remove components
* after you've set up the drag-and-drop.
*
* @param out Optional {@link java.io.PrintStream} for logging drag and drop
* messages
* @param c The component to unregister
* @param recursive Recursively unregister components within a container
* @since 1.0
*/
public static boolean remove(final java.io.PrintStream out,
final java.awt.Component c, final boolean recursive) { // Make sure
// we support
// dnd.
if (supportsDnD()) {
log(out, "FileDrop: Removing drag-and-drop hooks.");
c.setDropTarget(null);
if (recursive && (c instanceof java.awt.Container)) {
final java.awt.Component[] comps = ((java.awt.Container) c)
.getComponents();
for (int i = 0; i < comps.length; i++) {
remove(out, comps[i], recursive);
}
return true;
} // end if: recursive
else
return false;
} // end if: supports DnD
else
return false;
} // end remove
/* ******** I N N E R I N T E R F A C E L I S T E N E R ******** */
/**
* Implement this inner interface to listen for when files are dropped. For
* example your class declaration may begin like this: <code><pre>
* public class MyClass implements FileDrop.Listener
* ...
* public void filesDropped( java.io.File[] files )
* {
* ...
* } // end filesDropped
* ...
* </pre></code>
*
* @since 1.1
*/
public static interface Listener {
/**
* This method is called when files have been successfully dropped.
*
* @param files An array of <tt>File</tt>s that were dropped.
* @since 1.0
*/
public abstract void filesDropped(java.io.File[] files);
} // end inner-interface Listener
/* ******** I N N E R C L A S S ******** */
/**
* This is the event that is passed to the
* {@link FileDropListener#filesDropped filesDropped(...)} method in your
* {@link FileDropListener} when files are dropped onto a registered drop
* target.
*
* <p>
* I'm releasing this code into the Public Domain. Enjoy.
* </p>
*
* @author Robert Harder
* @author rob@iharder.net
* @version 1.2
*/
public static class Event extends java.util.EventObject {
private static final long serialVersionUID = -2175361562828864378L;
private final java.io.File[] files;
/**
* Constructs an {@link Event} with the array of files that were dropped
* and the {@link FileDrop} that initiated the event.
*
* @param files The array of files that were dropped
* @source The event source
* @since 1.1
*/
public Event(final java.io.File[] files, final Object source) {
super(source);
this.files = files;
} // end constructor
/**
* Returns an array of files that were dropped on a registered drop
* target.
*
* @return array of files that were dropped
* @since 1.1
*/
public java.io.File[] getFiles() {
return files;
} // end getFiles
} // end inner class Event
/* ******** I N N E R C L A S S ******** */
/**
* At last an easy way to encapsulate your custom objects for dragging and
* dropping in your Java programs! When you need to create a
* {@link java.awt.datatransfer.Transferable} object, use this class to wrap
* your object. For example:
*
* <pre>
* <code>
* ...
* MyCoolClass myObj = new MyCoolClass();
* Transferable xfer = new TransferableObject( myObj );
* ...
* </code>
* </pre>
*
* Or if you need to know when the data was actually dropped, like when
* you're moving data out of a list, say, you can use the
* {@link TransferableObject.Fetcher} inner class to return your object Just
* in Time. For example:
*
* <pre>
* <code>
* ...
* final MyCoolClass myObj = new MyCoolClass();
*
* TransferableObject.Fetcher fetcher = new TransferableObject.Fetcher()
* { public Object getObject(){ return myObj; }
* }; // end fetcher
*
* Transferable xfer = new TransferableObject( fetcher );
* ...
* </code>
* </pre>
*
* The {@link java.awt.datatransfer.DataFlavor} associated with
* {@link TransferableObject} has the representation class
* <tt>net.iharder.dnd.TransferableObject.class</tt> and MIME type
* <tt>application/x-net.iharder.dnd.TransferableObject</tt>. This data
* flavor is accessible via the static {@link #DATA_FLAVOR} property.
*
*
* <p>
* I'm releasing this code into the Public Domain. Enjoy.
* </p>
*
* @author Robert Harder
* @author rob@iharder.net
* @version 1.2
*/
public static class TransferableObject implements
java.awt.datatransfer.Transferable {
/**
* The MIME type for {@link #DATA_FLAVOR} is
* <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
*
* @since 1.1
*/
public final static String MIME_TYPE = "application/x-net.iharder.dnd.TransferableObject";
/**
* The default {@link java.awt.datatransfer.DataFlavor} for
* {@link TransferableObject} has the representation class
* <tt>net.iharder.dnd.TransferableObject.class</tt> and the MIME type
* <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
*
* @since 1.1
*/
public final static java.awt.datatransfer.DataFlavor DATA_FLAVOR = new java.awt.datatransfer.DataFlavor(
FileDrop.TransferableObject.class, MIME_TYPE);
private Fetcher fetcher;
private Object data;
private java.awt.datatransfer.DataFlavor customFlavor;
/**
* Creates a new {@link TransferableObject} that wraps <var>data</var>.
* Along with the {@link #DATA_FLAVOR} associated with this class, this
* creates a custom data flavor with a representation class determined
* from <code>data.getClass()</code> and the MIME type
* <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
*
* @param data The data to transfer
* @since 1.1
*/
public TransferableObject(final Object data) {
this.data = data;
this.customFlavor = new java.awt.datatransfer.DataFlavor(
data.getClass(), MIME_TYPE);
} // end constructor
/**
* Creates a new {@link TransferableObject} that will return the object
* that is returned by <var>fetcher</var>. No custom data flavor is set
* other than the default {@link #DATA_FLAVOR}.
*
* @see Fetcher
* @param fetcher The {@link Fetcher} that will return the data object
* @since 1.1
*/
public TransferableObject(final Fetcher fetcher) {
this.fetcher = fetcher;
} // end constructor
/**
* Creates a new {@link TransferableObject} that will return the object
* that is returned by <var>fetcher</var>. Along with the
* {@link #DATA_FLAVOR} associated with this class, this creates a
* custom data flavor with a representation class <var>dataClass</var>
* and the MIME type
* <tt>application/x-net.iharder.dnd.TransferableObject</tt>.
*
* @see Fetcher
* @param dataClass The {@link java.lang.Class} to use in the custom
* data flavor
* @param fetcher The {@link Fetcher} that will return the data object
* @since 1.1
*/
public TransferableObject(final Class dataClass, final Fetcher fetcher) {
this.fetcher = fetcher;
this.customFlavor = new java.awt.datatransfer.DataFlavor(dataClass,
MIME_TYPE);
} // end constructor
/**
* Returns the custom {@link java.awt.datatransfer.DataFlavor}
* associated with the encapsulated object or <tt>null</tt> if the
* {@link Fetcher} constructor was used without passing a
* {@link java.lang.Class}.
*
* @return The custom data flavor for the encapsulated object
* @since 1.1
*/
public java.awt.datatransfer.DataFlavor getCustomDataFlavor() {
return customFlavor;
} // end getCustomDataFlavor
/* ******** T R A N S F E R A B L E M E T H O D S ******** */
/**
* Returns a two- or three-element array containing first the custom
* data flavor, if one was created in the constructors, second the
* default {@link #DATA_FLAVOR} associated with
* {@link TransferableObject}, and third the
* {@link java.awt.datatransfer.DataFlavor.stringFlavor}.
*
* @return An array of supported data flavors
* @since 1.1
*/
public java.awt.datatransfer.DataFlavor[] getTransferDataFlavors() {
if (customFlavor != null)
return new java.awt.datatransfer.DataFlavor[] { customFlavor,
DATA_FLAVOR,
java.awt.datatransfer.DataFlavor.stringFlavor }; // end
// flavors
// array
else
return new java.awt.datatransfer.DataFlavor[] { DATA_FLAVOR,
java.awt.datatransfer.DataFlavor.stringFlavor }; // end
// flavors
// array
} // end getTransferDataFlavors
/**
* Returns the data encapsulated in this {@link TransferableObject}. If
* the {@link Fetcher} constructor was used, then this is when the
* {@link Fetcher#getObject getObject()} method will be called. If the
* requested data flavor is not supported, then the
* {@link Fetcher#getObject getObject()} method will not be called.
*
* @param flavor The data flavor for the data to return
* @return The dropped data
* @since 1.1
*/
public Object getTransferData(
final java.awt.datatransfer.DataFlavor flavor)
throws java.awt.datatransfer.UnsupportedFlavorException,
java.io.IOException {
// Native object
if (flavor.equals(DATA_FLAVOR))
return fetcher == null ? data : fetcher.getObject();
// String
if (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor))
return fetcher == null ? data.toString() : fetcher.getObject()
.toString();
// We can't do anything else
throw new java.awt.datatransfer.UnsupportedFlavorException(flavor);
} // end getTransferData
/**
* Returns <tt>true</tt> if <var>flavor</var> is one of the supported
* flavors. Flavors are supported using the <code>equals(...)</code>
* method.
*
* @param flavor The data flavor to check
* @return Whether or not the flavor is supported
* @since 1.1
*/
public boolean isDataFlavorSupported(
final java.awt.datatransfer.DataFlavor flavor) {
// Native object
if (flavor.equals(DATA_FLAVOR))
return true;
// String
if (flavor.equals(java.awt.datatransfer.DataFlavor.stringFlavor))
return true;
// We can't do anything else
return false;
} // end isDataFlavorSupported
/* ******** I N N E R I N T E R F A C E F E T C H E R ******** */
/**
* Instead of passing your data directly to the
* {@link TransferableObject} constructor, you may want to know exactly
* when your data was received in case you need to remove it from its
* source (or do anyting else to it). When the {@link #getTransferData
* getTransferData(...)} method is called on the
* {@link TransferableObject}, the {@link Fetcher}'s {@link #getObject
* getObject()} method will be called.
*
* @author Robert Harder
* @copyright 2001
* @version 1.1
* @since 1.1
*/
public static interface Fetcher {
/**
* Return the object being encapsulated in the
* {@link TransferableObject}.
*
* @return The dropped object
* @since 1.1
*/
public abstract Object getObject();
} // end inner interface Fetcher
} // end class TransferableObject
} // end class FileDrop

View file

@ -0,0 +1,135 @@
package the.bytecode.club.bytecodeviewer;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
/**
* Loading and saving jars
*
* @author Konloch
* @author WaterWolf
*
*/
public class JarUtils {
private static JarInputStream jis;
private static JarEntry entry;
public static void put(final File jarFile, final HashMap<String, ClassNode> clazzList) throws IOException {
jis = new JarInputStream(new FileInputStream(jarFile));
while ((entry = jis.getNextJarEntry()) != null) {
final String name = entry.getName();
if (!name.endsWith(".class")) {
BytecodeViewer.loadedResources.put(name, getBytes(jis));
jis.closeEntry();
continue;
}
final ClassNode cn = getNode(getBytes(jis));
clazzList.put(cn.name, cn);
jis.closeEntry();
}
jis.close();
}
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;
while ((a = is.read(buffer)) != -1) {
baos.write(buffer, 0, a);
}
baos.close();
buffer = null;
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();
cr.accept(cn, ClassReader.EXPAND_FRAMES);
cr = null;
return cn;
}
public static void saveAsJar(ArrayList<ClassNode> nodeList, String path, String manifest) {
try {
JarOutputStream out = new JarOutputStream(new FileOutputStream(path));
for (ClassNode cn : nodeList) {
ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
out.putNextEntry(new ZipEntry(cn.name + ".class"));
out.write(cw.toByteArray());
out.closeEntry();
}
out.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
out.write((manifest.trim()+"\r\n\r\n").getBytes());
out.closeEntry();
for (Entry<String, byte[]> entry : BytecodeViewer.loadedResources.entrySet()) {
String filename = entry.getKey();
if(!filename.startsWith("META-INF")) {
out.putNextEntry(new ZipEntry(filename));
out.write(entry.getValue());
out.closeEntry();
}
}
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void saveAsJar(ArrayList<ClassNode> nodeList, String path) {
try {
JarOutputStream out = new JarOutputStream(new FileOutputStream(path));
for (ClassNode cn : nodeList) {
ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
out.putNextEntry(new ZipEntry(cn.name + ".class"));
out.write(cw.toByteArray());
out.closeEntry();
}
for (Entry<String, byte[]> entry : BytecodeViewer.loadedResources.entrySet()) {
String filename = entry.getKey();
if(!filename.startsWith("META-INF")) {
out.putNextEntry(new ZipEntry(filename));
out.write(entry.getValue());
out.closeEntry();
}
}
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,381 @@
package the.bytecode.club.bytecodeviewer.decompilers.bytecode;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import org.apache.commons.lang3.StringEscapeUtils;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.searching.commons.InstructionSearcher;
/**
* A Bytecode decompiler
*
* @author Konloch
* @author WaterWolf
*
*/
public class BytecodeDecompiler {
public static String[] opcodeStrings;
public static String[] typeStrings;
static {
opcodeStrings = new String[256];
for (final Field f : Opcodes.class.getFields()) {
try {
final Object oo = f.get(null);
if (oo instanceof Integer) {
final int oi = ((Integer)oo);
if (oi < 256 && oi >= 0) {
opcodeStrings[oi] = f.getName().toLowerCase();
}
}
} catch (final IllegalArgumentException e) {
e.printStackTrace();
} catch (final IllegalAccessException e) {
e.printStackTrace();
}
}
typeStrings = new String[100];
for (final Field f : AbstractInsnNode.class.getFields()) {
if (!(f.getName().endsWith("_INSN"))) {
continue;
}
try {
final Object oo = f.get(null);
if (oo instanceof Integer) {
final int oi = ((Integer)oo);
if (oi < 256 && oi >= 0) {
typeStrings[oi] = f.getName().toLowerCase();
}
}
} catch (final IllegalArgumentException e) {
e.printStackTrace();
} catch (final IllegalAccessException e) {
e.printStackTrace();
}
}
}
@SuppressWarnings("unchecked")
public String decompileClassNode(final ClassNode cn) {
final StringBuilder classBuilder = new StringBuilder();
final ClassStringBuilder cb = new ClassStringBuilder(classBuilder);
final String cnm = cn.name;
String package_ = null;
String class_ = null;
if (cnm.contains("/")) {
package_ = cnm.substring(0, cnm.lastIndexOf("/"));
class_ = cnm.substring(cnm.lastIndexOf("/")+1);
}
else {
class_ = cnm;
}
if (package_ != null) {
cb.appendnl("package " + package_ + ";", 2);
}
cb.append(Modifier.toString(cn.access) + " class " + class_ + " ");
if (cn.superName != null) {
cb.append("extends " + cn.superName + " ");
}
if (cn.interfaces.size() > 0) {
cb.append("implements ");
final Iterator<String> sit = cn.interfaces.iterator();
while (sit.hasNext()) {
final String s = sit.next();
cb.append(s);
if (sit.hasNext()) {
cb.append(", ");
} else {
cb.append(" ");
}
}
}
cb.appendnl("{");
cb.increase();
cb.appendnl();
final Iterator<FieldNode> fni = cn.fields.iterator();
while (fni.hasNext()) {
final FieldNode fn = fni.next();
cb.appendnl(Modifier.toString(fn.access) + " " + Type.getType(fn.desc).getClassName() + " " + fn.name + ";");
}
cb.appendnl();
final Iterator<MethodNode> mni = cn.methods.iterator();
while (mni.hasNext()) {
final MethodNode mn = mni.next();
final String mnm = mn.name;
if (!mnm.equals("<clinit>")) {
cb.append(Modifier.toString(mn.access) + " ");
}
if (mnm.equals("<init>")) {
cb.append(class_);
}
else if (mnm.equals("<clinit>")) {
cb.append("static {");
if (BytecodeViewer.viewer.debugHelpers.isSelected())
cb.appendnl(" // <clinit>");
else
cb.appendnl();
}
else {
cb.append(Type.getReturnType(mn.desc).getClassName() + " ");
cb.append(mnm);
}
TypeAndName[] args = new TypeAndName[0];
if (!mnm.equals("<clinit>")) {
cb.append("(");
// TODO desc
final Type[] argTypes = Type.getArgumentTypes(mn.desc);
args = new TypeAndName[argTypes.length];
for (int i = 0;i < argTypes.length; i++) {
final Type type = argTypes[i];
final TypeAndName tan = new TypeAndName();
final String argName = "arg" + i;
tan.name = argName;
tan.type = type;
args[i] = tan;
cb.append(type.getClassName() + " " + argName + (i < argTypes.length-1 ? ", " : ""));
}
cb.appendnl(") {");
}
cb.increase();
try {
decompileMethod(cb, args, mn, cn);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
cb.decrease();
cb.appendnl("}");
cb.appendnl();
}
cb.decrease();
cb.appendnl("}");
return classBuilder.toString();
}
public void decompileMethod(final ClassStringBuilder builder, final TypeAndName[] args, final MethodNode mn, final ClassNode parent) throws UnsupportedEncodingException {
final InstructionSearcher is = new InstructionSearcher(mn);
//AbstractInsnNode next = is.getCurrent();
for(Object e : mn.tryCatchBlocks.toArray()) {
TryCatchBlockNode t = (TryCatchBlockNode)e;
String type = t.type;
LabelNode start = t.start;
LabelNode end = t.end;
LabelNode handler = t.handler;
builder.appendnl("trycatch block L" + start.hashCode() + " to L" + end.hashCode() + " handled by L" + handler.hashCode() + " exception type: " + type);
}
int index = 0;
for(AbstractInsnNode next : mn.instructions.toArray()) {
if (next.getOpcode() == -1) {
if(next instanceof LabelNode) {
LabelNode l = (LabelNode)next;
builder.appendnl(index++ + ". L" +l.hashCode());
} else {
builder.appendnl(index++ + ". nop //actually an unimplement opcode, please contact Konloch"); //lets just set it as nop for now.
}
//next = is.getNext();
continue;
}
builder.append(index++ + ". " + opcodeStrings[next.getOpcode()] + " ");
if (next instanceof FieldInsnNode) {
final FieldInsnNode fin = (FieldInsnNode) next;
builder.append(fin.owner + " " + fin.name + " " + fin.desc);
}
else if (next instanceof MethodInsnNode) {
final MethodInsnNode min = (MethodInsnNode) next;
builder.append(min.owner + " " + min.name + " " + min.desc);
}
else if (next instanceof VarInsnNode) {
final VarInsnNode vin = (VarInsnNode) next;
builder.append(vin.var);
if (BytecodeViewer.viewer.debugHelpers.isSelected()) {
if (vin.var == 0 && !Modifier.isStatic(mn.access)) {
builder.append(" // reference to self");
}
else {
final int refIndex = vin.var - (Modifier.isStatic(mn.access) ? 0 : 1);
if (refIndex >= 0 && refIndex < args.length-1) {
builder.append(" // reference to " + args[refIndex].name);
}
}
}
}
else if (next instanceof IntInsnNode) {
final IntInsnNode iin = (IntInsnNode) next;
builder.append(iin.operand);
}
else if (next instanceof JumpInsnNode) {
final JumpInsnNode jin = (JumpInsnNode) next;
builder.append(is.computePosition(jin.label));
switch (next.getOpcode()) {
case Opcodes.IF_ICMPLT:
builder.append(" // if val1 less than val2 jump");
break;
}
}
else if (next instanceof LdcInsnNode) {
final LdcInsnNode lin = (LdcInsnNode) next;
if(lin.cst instanceof String) {
String s = ((String)lin.cst).replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r").replaceAll("\\\"", "\\\\\"");
if(BytecodeViewer.viewer.chckbxmntmNewCheckItem.isSelected())
builder.append("\"" + StringEscapeUtils.escapeJava(s) + "\"");
else
builder.append("\"" + s + "\"");
} else {
String s = lin.cst.toString().replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r").replaceAll("\\\"", "\\\\\"");
if(BytecodeViewer.viewer.chckbxmntmNewCheckItem.isSelected())
builder.append("\"" + StringEscapeUtils.escapeJava(s) + "\"");
else
builder.append("\"" + s + "\"");
}
}
else if (next instanceof IincInsnNode) {
final IincInsnNode iin = (IincInsnNode) next;
builder.append("var " + iin.var + " by " + iin.incr);
}
else if (next instanceof TypeInsnNode) {
final TypeInsnNode tin = (TypeInsnNode) next;
builder.append(tin.desc);
}
else {
/*
switch (next.getOpcode()) {
case Opcodes.IF_ICMPLT:
buffer.append(" // ");
break;
}
*/
}
if (BytecodeViewer.viewer.debugInstructions.isSelected()) {
builder.append(" // " + typeStrings[next.getType()] + " ");
}
if (BytecodeViewer.viewer.debugHelpers.isSelected() &&
next instanceof JumpInsnNode)
{
final JumpInsnNode jin = (JumpInsnNode) next;
builder.append(" // line " + is.computePosition(jin.label) + " is " + printInstruction(is.computePosition(jin.label), mn, is).trim());
}
builder.appendnl();
}
}
public static String printInstruction(int line, MethodNode mn, InstructionSearcher is) {
for(int i = 0; i < mn.instructions.size(); i++) {
AbstractInsnNode next = mn.instructions.get(i);
if(line == i)
if(next.getOpcode() != -1) {
return beatifyAbstractInsnNode(next, is);
}
}
return "Unable to find, please contact konloch.";
}
public static String beatifyAbstractInsnNode(AbstractInsnNode next, InstructionSearcher is) {
String insn = "";
if(next.getOpcode() != -1)
insn =opcodeStrings[next.getOpcode()] + " ";
else if(next instanceof LabelNode) {
LabelNode l = (LabelNode)next;
insn = "L" +l.hashCode();
}
if (next instanceof FieldInsnNode) {
final FieldInsnNode fin = (FieldInsnNode) next;
insn += fin.owner + " " + fin.name + " " + fin.desc;
}
else if (next instanceof MethodInsnNode) {
final MethodInsnNode min = (MethodInsnNode) next;
insn += min.owner + " " + min.name + " " + min.desc;
}
else if (next instanceof VarInsnNode) {
final VarInsnNode vin = (VarInsnNode) next;
insn += vin.var;
}
else if (next instanceof IntInsnNode) {
final IntInsnNode iin = (IntInsnNode) next;
insn += iin.operand;
}
else if (next instanceof JumpInsnNode) {
final JumpInsnNode jin = (JumpInsnNode) next;
insn += is.computePosition(jin.label);
}
else if (next instanceof LdcInsnNode) {
final LdcInsnNode lin = (LdcInsnNode) next;
if(lin.cst instanceof String)
insn += "\"" + ((String) lin.cst).replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r") + "\"";
else
insn += "\"" + lin.cst + "\"";
}
else if (next instanceof IincInsnNode) {
final IincInsnNode iin = (IincInsnNode) next;
insn += "var " + iin.var + " by " + iin.incr;
}
else if (next instanceof TypeInsnNode) {
final TypeInsnNode tin = (TypeInsnNode) next;
insn += tin.desc;
}
else {
}
return insn;
}
}

View file

@ -0,0 +1,76 @@
package the.bytecode.club.bytecodeviewer.decompilers.bytecode;
/**
* The buffer where decompiler classes output generated source
*
* @author Waterwolf
*
*/
public class ClassStringBuilder {
private final StringBuilder builder;
public final IndentationLevel iLevel;
private static final String nl = System.getProperty("line.separator");
private static final int TAB_SPACES = 4;
private boolean isNewline = true;
public ClassStringBuilder(final StringBuilder builder) {
this.builder = builder;
this.iLevel = new IndentationLevel();
}
public void append(final Object obj) {
if (isNewline) {
for (int i = 0;i < TAB_SPACES*iLevel.indentation; i++) {
builder.append(" ");
}
}
builder.append(obj);
isNewline = false;
}
public void appendnl(final String s) {
appendnl(s, 1);
}
public void appendnl() {
appendnl("", 1);
}
public void appendnl(final String s, final int nlAmount) {
append(s);
for (int i = 0;i < nlAmount; i++) {
builder.append(nl);
}
if (nlAmount > 0) {
isNewline = true;
}
}
public int increase() {
return iLevel.increase();
}
public int decrease() {
return iLevel.decrease();
}
public int get() {
return iLevel.get();
}
public static class IndentationLevel {
private int indentation = 0;
public int increase() {
return ++indentation;
}
public int decrease() {
return --indentation;
}
public int get() {
return indentation;
}
}
}

View file

@ -0,0 +1,14 @@
package the.bytecode.club.bytecodeviewer.decompilers.bytecode;
import org.objectweb.asm.Type;
/**
* Container class for type and name. Used to pass arguments and local variables around
*
* @author Waterwolf
*
*/
public class TypeAndName {
public Type type = null;
public String name = null;
}

View file

@ -0,0 +1,152 @@
package the.bytecode.club.bytecodeviewer.decompilers.java;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.JarUtils;
/**
* A complete FernFlower launcher with all the options (except 2)
*
* @author Konloch
* @author WaterWolf
*
*/
public class FernFlowerDecompiler {
public void decompileToZip(String zipName) {
File tempZip = new File(BytecodeViewer.tempDirectory + "temp.zip");
if(tempZip.exists())
tempZip.delete();
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), tempZip.getAbsolutePath());
de.fernflower.main.decompiler.ConsoleDecompiler.main(new String[] {tempZip.getAbsolutePath(), BytecodeViewer.tempDirectory + "./temp/"});
File tempZip2 = new File(BytecodeViewer.tempDirectory + "./temp/"+tempZip.getName());
if(tempZip2.exists())
tempZip2.renameTo(new File(zipName));
tempZip.delete();
new File(BytecodeViewer.tempDirectory + "./temp/").delete();
}
public String decompileClassNode(final ClassNode cn) {
final ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
String fileStart = BytecodeViewer.tempDirectory + "temp";
int fileNumber = getClassNumber(fileStart, ".class");
final File tempClass = new File(fileStart+fileNumber+".class");
try {
final FileOutputStream fos = new FileOutputStream(tempClass);
fos.write(cw.toByteArray());
fos.close();
} catch (final IOException e) {
e.printStackTrace();
}
de.fernflower.main.decompiler.ConsoleDecompiler.main(generateMainMethod(tempClass.getAbsolutePath(), "."));
tempClass.delete();
final File outputJava = new File("temp"+fileNumber+".java");
if (outputJava.exists()) {
final String nl = System.getProperty("line.separator");
final StringBuffer javaSrc = new StringBuffer();
try {
final BufferedReader br = new BufferedReader(new FileReader(outputJava));
String line;
while ((line = br.readLine()) != null) {
javaSrc.append(line + nl);
}
br.close();
} catch (final IOException e) {
e.printStackTrace();
}
outputJava.delete();
return javaSrc.toString();
}
return "FernFlower error! Send the stacktrace to Konloch at http://the.bytecode.club or konloch@gmail.com";
}
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;
}
private String[] generateMainMethod(String className, String folder) {
boolean rbr = BytecodeViewer.viewer.rbr.isSelected();
boolean rsy = BytecodeViewer.viewer.rsy.isSelected();
boolean din = BytecodeViewer.viewer.din.isSelected();
boolean dc4 = BytecodeViewer.viewer.dc4.isSelected();
boolean das = BytecodeViewer.viewer.das.isSelected();
boolean hes = BytecodeViewer.viewer.hes.isSelected();
boolean hdc = BytecodeViewer.viewer.hdc.isSelected();
boolean dgs = BytecodeViewer.viewer.dgs.isSelected();
boolean ner = BytecodeViewer.viewer.ner.isSelected();
boolean den = BytecodeViewer.viewer.den.isSelected();
boolean rgn = BytecodeViewer.viewer.rgn.isSelected();
boolean bto = BytecodeViewer.viewer.bto.isSelected();
boolean nns = BytecodeViewer.viewer.nns.isSelected();
boolean uto = BytecodeViewer.viewer.uto.isSelected();
boolean udv = BytecodeViewer.viewer.udv.isSelected();
boolean rer = BytecodeViewer.viewer.rer.isSelected();
boolean fdi = BytecodeViewer.viewer.fdi.isSelected();
boolean asc = BytecodeViewer.viewer.asc.isSelected();
return new String[] {
"-rbr="+r(rbr),
"-rsy="+r(rsy),
"-din="+r(din),
"-dc4="+r(dc4),
"-das="+r(das),
"-hes="+r(hes),
"-hdc="+r(hdc),
"-dgs="+r(dgs),
"-ner="+r(ner),
"-den="+r(den),
"-rgn="+r(rgn),
"-bto="+r(bto),
"-nns="+r(nns),
"-uto="+r(uto),
"-udv="+r(udv),
"-rer="+r(rer),
"-fdi="+r(fdi),
"-asc="+r(asc),
className,
folder};
}
private String r(boolean b) {
if(b) {
return "1";
} else {
return "0";
}
}
}

View file

@ -0,0 +1,28 @@
package the.bytecode.club.bytecodeviewer.gui;
import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.CardLayout;
import javax.swing.JTextArea;
import java.awt.Color;
public class AboutWindow extends JFrame {
public AboutWindow() {
setSize(new Dimension(403, 407));
setType(Type.UTILITY);
setTitle("Bytecode Viewer - About");
getContentPane().setLayout(new CardLayout(0, 0));
JTextArea txtrBytecodeViewerIs = new JTextArea();
txtrBytecodeViewerIs.setDisabledTextColor(Color.BLACK);
txtrBytecodeViewerIs.setWrapStyleWord(true);
getContentPane().add(txtrBytecodeViewerIs, "name_140466526081695");
txtrBytecodeViewerIs.setText("Bytecode Viewer is an open source program\r\ndeveloped by Konloch (konloch@gmail.com)\r\n\r\nIt uses code from the following:\r\n J-RET by WaterWolf\r\n JHexPane by Sam Koivu\r\n JSyntaxPane by Ayman Al\r\n Commons IO by Apache\r\n ASM by OW2\r\n\r\nLimitations:\r\n Syntax highlighting on files that are\r\nbigger than 10K lines can take a while to\r\nload, you may want to disable the syntax\r\nhighlighting for large files.\r\n\r\nIf you're interested in Java Reverse\r\nEngineering, join The Bytecode Club\r\nhttp://the.bytecode.club");
txtrBytecodeViewerIs.setEnabled(false);
this.setResizable(false);
this.setLocationRelativeTo(null);
}
private static final long serialVersionUID = -8230501978224923296L;
}

View file

@ -0,0 +1,281 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.util.ArrayList;
import static javax.swing.ScrollPaneConstants.*;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BoxView;
import javax.swing.text.ComponentView;
import javax.swing.text.Element;
import javax.swing.text.IconView;
import javax.swing.text.LabelView;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.ParagraphView;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import com.jhe.hexed.JHexEditor;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.BytecodeDecompiler;
import the.bytecode.club.bytecodeviewer.decompilers.java.FernFlowerDecompiler;
/**
* This represents the opened classfile.
*
* @author Konloch
* @author WaterWolf
*
*/
public class ClassViewer extends JPanel {
private boolean sourcePane = false, bytecodePane = false, hexPane = false;
/**
* Whoever wrote this function, THANK YOU!
* @param splitter
* @param proportion
* @return
*/
public static JSplitPane setDividerLocation(final JSplitPane splitter,
final double proportion) {
if (splitter.isShowing()) {
if(splitter.getWidth() > 0 && splitter.getHeight() > 0) {
splitter.setDividerLocation(proportion);
}
else {
splitter.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent ce) {
splitter.removeComponentListener(this);
setDividerLocation(splitter, proportion);
}
});
}
}
else {
splitter.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 &&
splitter.isShowing()) {
splitter.removeHierarchyListener(this);
setDividerLocation(splitter, proportion);
}
}
});
}
return splitter;
}
private static final long serialVersionUID = -8650495368920680024L;
ArrayList<MethodData> lnData = new ArrayList<MethodData>();
String name;
ClassNode cn;
JSplitPane sp;
JSplitPane sp2;
JEditorPane bytecode = new JEditorPane(), decomp = new JEditorPane();
JScrollPane bcScroll;
public ClassViewer(final String name, final ClassNode cn) {
sourcePane = BytecodeViewer.viewer.sourcePane.isSelected();
bytecodePane = BytecodeViewer.viewer.bytecodePane.isSelected();
hexPane = BytecodeViewer.viewer.hexPane.isSelected();
boolean bytecodeSyntax = BytecodeViewer.viewer.bycSyntax.isSelected();
boolean sourcecodeSyntax = BytecodeViewer.viewer.srcSyntax.isSelected();
this.name = name;
this.cn = cn;
this.setName(name);
this.setLayout(new BorderLayout());
final JPanel dcPanel = new JPanel(new BorderLayout());
final JScrollPane dcScroll = new JScrollPane(decomp);
if(sourcePane) {
dcPanel.add(dcScroll, BorderLayout.CENTER);
}
final JPanel bcPanel = new JPanel(new BorderLayout());
if(bytecodePane) {
bcScroll = new JScrollPane(bytecode);
} else {
bcScroll = new JScrollPane();
}
bcPanel.add(bcScroll, BorderLayout.CENTER);
if(bytecodePane && bytecodeSyntax)
bytecode.setContentType("text/java");
if(sourcePane && sourcecodeSyntax)
decomp.setContentType("text/java");
this.sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, dcPanel, bcPanel);
final ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
JHexEditor hex = new JHexEditor(cw.toByteArray());
JScrollPane penis;
if(hexPane) {
penis = new JScrollPane(hex);
} else {
penis = new JScrollPane();
}
penis.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_NEVER);
this.sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp, penis);
this.add(sp2, BorderLayout.CENTER);
hex.setMaximumSize(new Dimension(0, Integer.MAX_VALUE));
hex.setSize(0, Integer.MAX_VALUE);
resetDivider();
BytecodeViewer.viewer.setIcon(true);
bytecode.setText("Decompiling, please wait..");
decomp.setText("Decompiling, please wait..");
startPaneUpdater();
this.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
resetDivider();
}
});
}
public void resetDivider() {
if(!sourcePane) {
sp.setResizeWeight(0);
} else if(!bytecodePane) {
sp.setResizeWeight(1);
} else {
sp.setResizeWeight(0.5);
}
if(hexPane) {
if(!sourcePane && !bytecodePane)
sp2 = setDividerLocation(sp2, 0);
else
sp2 = setDividerLocation(sp2, 0.7);
} else {
sp2 = setDividerLocation(sp2, 1);
}
}
PaneUpdaterThread t;
public void startPaneUpdater() {
t = new PaneUpdaterThread(bytecode, decomp) {
@Override
public void doShit() {
final BytecodeDecompiler bc_dc = new BytecodeDecompiler();
final FernFlowerDecompiler ff_dc = new FernFlowerDecompiler();
final String b = bc_dc.decompileClassNode(cn);
final String s = ff_dc.decompileClassNode(cn);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if(bytecodePane)
p1.setText(b);
if(sourcePane)
p2.setText(s);
p1.setCaretPosition(0);
p2.setCaretPosition(0);
BytecodeViewer.viewer.setIcon(false);
}
});
}
};
t.start();
}
public static class MethodData {
public String name, desc;
public int srcLN, bytecodeLN;
@Override
public boolean equals(final Object o) {
return equals((MethodData) o);
}
public boolean equals(final MethodData md) {
return this.name.equals(md.name) && this.desc.equals(md.desc);
}
public String constructPattern() {
final StringBuffer pattern = new StringBuffer();
pattern.append(name + " *\\(");
final org.objectweb.asm.Type[] types = org.objectweb.asm.Type
.getArgumentTypes(desc);
pattern.append("(.*)");
for (int i = 0; i < types.length; i++) {
final Type type = types[i];
final String clazzName = type.getClassName();
pattern.append(clazzName.substring(clazzName.lastIndexOf(".") + 1)
+ "(.*)");
}
pattern.append("\\) *\\{");
return pattern.toString();
}
}
class WrapEditorKit extends StyledEditorKit {
private static final long serialVersionUID = 1719109651258205346L;
ViewFactory defaultFactory = new WrapColumnFactory();
@Override
public ViewFactory getViewFactory() {
return defaultFactory;
}
}
class WrapColumnFactory implements ViewFactory {
public View create(final Element elem) {
final String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ParagraphElementName))
return new NoWrapParagraphView(elem);
else if (kind.equals(AbstractDocument.SectionElementName))
return new BoxView(elem, View.Y_AXIS);
else if (kind.equals(StyleConstants.ComponentElementName))
return new ComponentView(elem);
else if (kind.equals(StyleConstants.IconElementName))
return new IconView(elem);
}
// default to text display
return new LabelView(elem);
}
}
public class NoWrapParagraphView extends ParagraphView {
public NoWrapParagraphView(final Element elem) {
super(elem);
}
@Override
public void layout(final int width, final int height) {
super.layout(Short.MAX_VALUE, height);
}
@Override
public float getMinimumSpan(final int axis) {
return super.getPreferredSpan(axis);
}
}
}

View file

@ -0,0 +1,54 @@
package the.bytecode.club.bytecodeviewer.gui;
import javax.swing.JFrame;
import java.awt.Dimension;
import javax.swing.JButton;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.JarUtils;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.BoxLayout;
import javax.swing.JScrollPane;
import javax.swing.JLabel;
import javax.swing.JTextArea;
public class ExportJar extends JFrame {
public ExportJar(final String jarPath) {
setSize(new Dimension(250, 277));
setResizable(false);
setTitle("Save As Jar..");
JButton btnNewButton = new JButton("Save As Jar..");
btnNewButton.setMaximumSize(new Dimension(999, 23));
btnNewButton.setMinimumSize(new Dimension(999, 23));
btnNewButton.setSize(new Dimension(999, 0));
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
JScrollPane scrollPane = new JScrollPane();
getContentPane().add(scrollPane);
JLabel lblMetainfmanifestmf = new JLabel("META-INF/MANIFEST.MF:");
scrollPane.setColumnHeaderView(lblMetainfmanifestmf);
final JTextArea mani = new JTextArea();
mani.setText("Manifest-Version: 1.0\r\nClass-Path: .\r\nMain-Class: ");
scrollPane.setViewportView(mani);
getContentPane().add(btnNewButton);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
BytecodeViewer.viewer.setC(true);
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), jarPath, mani.getText());
BytecodeViewer.viewer.setC(false);
dispose();
}
});
this.setLocationRelativeTo(null);
}
private static final long serialVersionUID = -2662514582647810868L;
}

View file

@ -0,0 +1,366 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BorderLayout;
import java.awt.font.FontRenderContext;
import java.awt.geom.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map.Entry;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.*;
@SuppressWarnings("serial")
public class FileNavigationPane extends VisibleComponent implements FileDrop.Listener {
FileChangeNotifier fcn;
JCheckBox exact = new JCheckBox("Exact");
MyTreeNode treeRoot = new MyTreeNode("Root");
MyTree tree;
public FileNavigationPane(final FileChangeNotifier fcn) {
super("ClassNavigation");
setTitle("Files");
this.fcn = fcn;
getContentPane().setLayout(new BorderLayout());
this.tree = new MyTree(treeRoot);
getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER);
this.tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(final TreeSelectionEvent arg0) {
final TreePath path = arg0.getPath();
if (((TreeNode)path.getLastPathComponent()).getChildCount() > 0)
return;
final StringBuffer nameBuffer = new StringBuffer();
for (int i = 1;i < path.getPathCount(); i++) {
nameBuffer.append(path.getPathComponent(i));
if (i < path.getPathCount()-1) {
nameBuffer.append("/");
}
}
final ClassNode cn = BytecodeViewer.getClassNode(nameBuffer.toString());
if (cn != null) {
openClassFileToWorkSpace(nameBuffer.toString(), cn);
}
}
});
final String quickSearchText = "Quick class search";
final JTextField quickSearch = new JTextField(quickSearchText);
quickSearch.setForeground(Color.gray);
quickSearch.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(final KeyEvent ke) {
if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
final String qt = quickSearch.getText();
quickSearch.setText("");
String[] path = null;
if (qt.contains(".")) {
path = qt.split("\\.");
}
else {
path = new String[] {qt};
}
MyTreeNode curNode = treeRoot;
pathLoop:
for (int i = 0;i < path.length; i++) {
final String pathName = path[i];
final boolean isLast = i == path.length-1;
for (int c = 0; c < curNode.getChildCount(); c++) {
final MyTreeNode child = (MyTreeNode) curNode.getChildAt(c);
if(!exact.isSelected()) {
if (((String)child.getUserObject()).toLowerCase().contains(pathName.toLowerCase())) {
curNode = child;
if (isLast) {
final TreePath pathn = new TreePath(curNode.getPath());
tree.setSelectionPath(pathn);
tree.makeVisible(pathn);
tree.scrollPathToVisible(pathn);
System.out.println("Found! " + curNode);
break pathLoop;
}
continue pathLoop;
}
} else {
if (((String)child.getUserObject()).equals(pathName)) {
curNode = child;
if (isLast) {
final TreePath pathn = new TreePath(curNode.getPath());
tree.setSelectionPath(pathn);
tree.makeVisible(pathn);
tree.scrollPathToVisible(pathn);
System.out.println("Found! " + curNode);
break pathLoop;
}
continue pathLoop;
}
}
}
System.out.println("Could not find " + pathName);
break;
}
}
}
});
quickSearch.addFocusListener(new FocusListener() {
@Override
public void focusGained(final FocusEvent arg0) {
if (quickSearch.getText().equals(quickSearchText)) {
quickSearch.setText("");
quickSearch.setForeground(Color.black);
}
}
@Override
public void focusLost(final FocusEvent arg0) {
if (quickSearch.getText().isEmpty()) {
quickSearch.setText(quickSearchText);
quickSearch.setForeground(Color.gray);
}
}
});
JPanel p2 = new JPanel();
p2.setLayout(new BorderLayout());
p2.add(quickSearch, BorderLayout.NORTH);
p2.add(exact, BorderLayout.SOUTH);
getContentPane().add(p2, BorderLayout.SOUTH);
this.setVisible(true);
new FileDrop(this, this);
}
public void openClassFileToWorkSpace(final String name, final ClassNode node) {
fcn.openClassFile(name, node);
}
@Override
public void filesDropped(final File[] files) {
if (files.length < 1)
return;
BytecodeViewer.openFiles(files);
}
public void updateTree() {
treeRoot.removeAllChildren();
for (final Entry<String, ClassNode> 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;
}
}
}
treeRoot.sort();
tree.expandPath(new TreePath(tree.getModel().getRoot()));
tree.updateUI();
//expandAll(tree, true);
}
// If expand is true, expands all nodes in the tree.
// Otherwise, collapses all nodes in the tree.
public void expandAll(final JTree tree, final boolean expand) {
final TreeNode root = (TreeNode) tree.getModel().getRoot();
// Traverse tree from root
expandAll(tree, new TreePath(root), expand);
}
@SuppressWarnings("rawtypes")
private void expandAll(final JTree tree, final TreePath parent,
final boolean expand) {
// Traverse children
final TreeNode node = (TreeNode) parent.getLastPathComponent();
if (node.getChildCount() >= 0) {
for (final Enumeration e = node.children(); e.hasMoreElements();) {
final TreeNode n = (TreeNode) e.nextElement();
final TreePath path = parent.pathByAddingChild(n);
expandAll(tree, path, expand);
}
}
// Expansion or collapse must be done bottom-up
if (expand) {
tree.expandPath(parent);
} else {
tree.collapsePath(parent);
}
}
public class MyTree extends JTree {
private static final long serialVersionUID = -2355167326094772096L;
DefaultMutableTreeNode treeRoot;
public MyTree(final DefaultMutableTreeNode treeRoot) {
super(treeRoot);
this.treeRoot = treeRoot;
}
StringMetrics m = null;
@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);
}
}
}
public class MyTreeNode extends DefaultMutableTreeNode {
private static final long serialVersionUID = -8817777566176729571L;
public MyTreeNode(final Object o) {
super(o);
}
@Override
public void insert(final MutableTreeNode newChild, final int childIndex) {
super.insert(newChild, childIndex);
}
public void sort() {
recursiveSort(this);
}
@SuppressWarnings("unchecked")
private void recursiveSort(final MyTreeNode node) {
Collections.sort(node.children, nodeComparator);
final Iterator<MyTreeNode> it = node.children.iterator();
while (it.hasNext()) {
final MyTreeNode nextNode = it.next();
if (nextNode.getChildCount() > 0) {
recursiveSort(nextNode);
}
}
}
protected Comparator<MyTreeNode> nodeComparator = new Comparator<MyTreeNode> () {
@Override
public int compare(final MyTreeNode o1, final MyTreeNode o2) {
// To make sure nodes with children are always on top
final int firstOffset = o1.getChildCount() > 0 ? -1000 : 0;
final int secondOffset = o2.getChildCount() > 0 ? 1000 : 0;
return o1.toString().compareToIgnoreCase(o2.toString()) + firstOffset + secondOffset;
}
@Override
public boolean equals(final Object obj) {
return false;
}
@Override
public int hashCode() {
final int hash = 7;
return hash;
}
};
}
/**
*
* @author http://stackoverflow.com/a/18450804
*
*/
class StringMetrics {
Font font;
FontRenderContext context;
public StringMetrics(Graphics2D g2) {
font = g2.getFont();
context = g2.getFontRenderContext();
}
Rectangle2D getBounds(String message) {
return font.getStringBounds(message, context);
}
double getWidth(String message) {
Rectangle2D bounds = getBounds(message);
return bounds.getWidth();
}
double getHeight(String message) {
Rectangle2D bounds = getBounds(message);
return bounds.getHeight();
}
}
public void resetWorkspace() {
treeRoot.removeAllChildren();
tree.repaint();
tree.updateUI();
}
}

View file

@ -0,0 +1,584 @@
package the.bytecode.club.bytecodeviewer.gui;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.BoxLayout;
import javax.swing.JMenuBar;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import javax.swing.JScrollPane;
import javax.swing.filechooser.FileFilter;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JCheckBoxMenuItem;
import org.apache.commons.codec.binary.Base64;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.FileChangeNotifier;
import the.bytecode.club.bytecodeviewer.JarUtils;
import the.bytecode.club.bytecodeviewer.decompilers.java.FernFlowerDecompiler;
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 java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.ArrayList;
public class MainViewerGUI extends JFrame implements FileChangeNotifier {
private static final long serialVersionUID = 1851409230530948543L;
public JCheckBoxMenuItem debugHelpers = new JCheckBoxMenuItem("Debug Helpers");
public JCheckBoxMenuItem debugInstructions = new JCheckBoxMenuItem("Debug Instructions");
private JSplitPane sp1;
private JSplitPane sp2;
static ArrayList<VisibleComponent> rfComps = new ArrayList<VisibleComponent>();
public JCheckBoxMenuItem rbr = new JCheckBoxMenuItem("Hide bridge methods");
public JCheckBoxMenuItem rsy = new JCheckBoxMenuItem("Hide synthetic class members");
public JCheckBoxMenuItem din = new JCheckBoxMenuItem("Decompile inner classes");
public JCheckBoxMenuItem dc4 = new JCheckBoxMenuItem("Collapse 1.4 class references");
public JCheckBoxMenuItem das = new JCheckBoxMenuItem("Decompile assertions");
public JCheckBoxMenuItem hes = new JCheckBoxMenuItem("Hide empty super invocation");
public JCheckBoxMenuItem hdc = new JCheckBoxMenuItem("Hide empty default constructor");
public JCheckBoxMenuItem dgs = new JCheckBoxMenuItem("Decompile generic signatures");
public JCheckBoxMenuItem ner = new JCheckBoxMenuItem("Assume return not throwing exceptions");
public JCheckBoxMenuItem den = new JCheckBoxMenuItem("Decompile enumerations");
public JCheckBoxMenuItem rgn = new JCheckBoxMenuItem("Remove getClass() invocation");
public JCheckBoxMenuItem bto = new JCheckBoxMenuItem("Interpret int 1 as boolean true");
public JCheckBoxMenuItem nns = new JCheckBoxMenuItem("Allow for not set synthetic attribute");
public JCheckBoxMenuItem uto = new JCheckBoxMenuItem("Consider nameless types as java.lang.Object");
public JCheckBoxMenuItem udv = new JCheckBoxMenuItem("Reconstruct variable names from debug info");
public JCheckBoxMenuItem rer = new JCheckBoxMenuItem("Remove empty exception ranges");
public JCheckBoxMenuItem fdi = new JCheckBoxMenuItem("Deinline finally structures");
public JCheckBoxMenuItem asc = new JCheckBoxMenuItem("Allow only ASCII characters in strings");
private final JSeparator separator_2 = new JSeparator();
public JCheckBoxMenuItem srcSyntax = new JCheckBoxMenuItem("Source Code Syntax");
public JCheckBoxMenuItem bycSyntax = new JCheckBoxMenuItem("Bytecode Syntax");
JCheckBoxMenuItem sourcePane = new JCheckBoxMenuItem("Source Pane");
JCheckBoxMenuItem bytecodePane = new JCheckBoxMenuItem("Bytecode Pane");
JCheckBoxMenuItem hexPane = new JCheckBoxMenuItem("Hex Pane");
private final JMenuItem mntmNewWorkspace = new JMenuItem("New Workspace");
public JMenu mnRecentFiles = new JMenu("Recent Files");
private final JMenuItem mntmNewMenuItem = new JMenuItem("Save Java Files As..");
private final JMenuItem mntmAbout = new JMenuItem("About");
private AboutWindow aboutWindow = new AboutWindow();
private final JSeparator separator_3 = new JSeparator();
private final JMenu mnNewMenu_1 = new JMenu("Plugins");
private final JMenuItem mntmStartExternalPlugin = new JMenuItem("Open Plugin..");
private final JSeparator separator_4 = new JSeparator();
public JMenu mnRecentPlugins = new JMenu("Recent Plugins");
private final JSeparator separator_5 = new JSeparator();
private final JMenuItem mntmStartZkmString = new JMenuItem("ZKM String Decrypter");
private final JMenuItem mntmNewMenuItem_1 = new JMenuItem("Malicious Code Scanner");
private final JMenuItem mntmNewMenuItem_2 = new JMenuItem("Allatori String Decrypter");
private final JMenuItem mntmShowAllStrings = new JMenuItem("Show All Strings");
private final JMenuItem mntmShowMainMethods = new JMenuItem("Show Main Methods");
private final JMenuItem mntmNewMenuItem_3 = new JMenuItem("Save As Jar..");
private JMenuBar menuBar = new JMenuBar();
public JCheckBoxMenuItem chckbxmntmNewCheckItem = new JCheckBoxMenuItem("Allow only ASCII characters in strings");
private final JMenuItem mntmReplaceStrings = new JMenuItem("Replace Strings");
private final JMenuItem mntmNewMenuItem_4 = new JMenuItem("");
public void setC(boolean busy) {
if(busy) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
for(Component c : this.getComponents())
c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
sp1.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
sp2.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
for(VisibleComponent c : rfComps) {
c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
if(c instanceof WorkPane) {
WorkPane w = (WorkPane)c;
for(Component c2 : w.tabs.getComponents())
c2.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
}
} else {
this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
for(Component c : this.getComponents())
c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
sp1.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
sp2.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
for(VisibleComponent c : rfComps) {
c.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
if(c instanceof WorkPane) {
WorkPane w = (WorkPane)c;
for(Component c2 : w.tabs.getComponents())
c2.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
}
}
public void setIcon(final boolean busy) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if(busy) {
try {
mntmNewMenuItem_4.setIcon(new ImageIcon(getClass().getResource("/resources/1.gif")));
} catch(NullPointerException e) {
mntmNewMenuItem_4.setIcon(new ImageIcon(b642IMG("R0lGODlhEAALAPQAAP///wAAANra2tDQ0Orq6gcHBwAAAC8vL4KCgmFhYbq6uiMjI0tLS4qKimVlZb6+vicnJwUFBU9PT+bm5tjY2PT09Dk5Odzc3PLy8ra2tqCgoMrKyu7u7gAAAAAAAAAAACH5BAkLAAAAIf4aQ3JlYXRlZCB3aXRoIGFqYXhsb2FkLmluZm8AIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAALAAAFLSAgjmRpnqSgCuLKAq5AEIM4zDVw03ve27ifDgfkEYe04kDIDC5zrtYKRa2WQgAh+QQJCwAAACwAAAAAEAALAAAFJGBhGAVgnqhpHIeRvsDawqns0qeN5+y967tYLyicBYE7EYkYAgAh+QQJCwAAACwAAAAAEAALAAAFNiAgjothLOOIJAkiGgxjpGKiKMkbz7SN6zIawJcDwIK9W/HISxGBzdHTuBNOmcJVCyoUlk7CEAAh+QQJCwAAACwAAAAAEAALAAAFNSAgjqQIRRFUAo3jNGIkSdHqPI8Tz3V55zuaDacDyIQ+YrBH+hWPzJFzOQQaeavWi7oqnVIhACH5BAkLAAAALAAAAAAQAAsAAAUyICCOZGme1rJY5kRRk7hI0mJSVUXJtF3iOl7tltsBZsNfUegjAY3I5sgFY55KqdX1GgIAIfkECQsAAAAsAAAAABAACwAABTcgII5kaZ4kcV2EqLJipmnZhWGXaOOitm2aXQ4g7P2Ct2ER4AMul00kj5g0Al8tADY2y6C+4FIIACH5BAkLAAAALAAAAAAQAAsAAAUvICCOZGme5ERRk6iy7qpyHCVStA3gNa/7txxwlwv2isSacYUc+l4tADQGQ1mvpBAAIfkECQsAAAAsAAAAABAACwAABS8gII5kaZ7kRFGTqLLuqnIcJVK0DeA1r/u3HHCXC/aKxJpxhRz6Xi0ANAZDWa+kEAA7"), ""));
}
} else
mntmNewMenuItem_4.setIcon(null);
mntmNewMenuItem_4.updateUI();
}
});
}
/**
* Decodes a Base64 String as a BufferedImage
*/
public BufferedImage b642IMG(String imageString) {
BufferedImage image = null;
byte[] imageByte;
try {
imageByte = Base64.decodeBase64(imageString);
ByteArrayInputStream bis = new ByteArrayInputStream(imageByte);
image = ImageIO.read(bis);
bis.close();
} catch (Exception e) {
e.printStackTrace();
}
return image;
}
public MainViewerGUI() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rbr.setSelected(true);
rsy.setSelected(false);
din.setSelected(true);
dc4.setSelected(true);
das.setSelected(true);
hes.setSelected(true);
hdc.setSelected(true);
dgs.setSelected(false);
ner.setSelected(true);
den.setSelected(true);
rgn.setSelected(true);
bto.setSelected(true);
nns.setSelected(true);
uto.setSelected(true);
udv.setSelected(true);
rer.setSelected(true);
fdi.setSelected(true);
asc.setSelected(false);
srcSyntax.setSelected(true);
bycSyntax.setSelected(true);
debugHelpers.setSelected(true);
sourcePane.setSelected(true);
bytecodePane.setSelected(true);
setJMenuBar(menuBar);
JMenu mnNewMenu = new JMenu("File");
menuBar.add(mnNewMenu);
final JFrame This = this;
mntmNewWorkspace.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
BytecodeViewer.resetWorkSpace();
}
});
JMenuItem mntmLoadJar = new JMenuItem("Add..");
mntmLoadJar.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new JarZipClassFileFilter());
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(This);
if (returnVal == JFileChooser.APPROVE_OPTION)
try {
BytecodeViewer.viewer.setC(true);
BytecodeViewer.openFiles(new File[]{fc.getSelectedFile()});
BytecodeViewer.viewer.setC(false);
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
mnNewMenu.add(mntmLoadJar);
mnNewMenu.add(mntmNewWorkspace);
JMenuItem mntmSave = new JMenuItem("Save Files As..");
mntmSave.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new ZipFileFilter());
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
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);
}
}
});
mnNewMenu.add(separator_3);
mntmNewMenuItem_3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new JarFileFilter());
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
new ExportJar(file.getAbsolutePath()).setVisible(true);
}
}
});
mnNewMenu.add(mntmNewMenuItem_3);
mnNewMenu.add(mntmSave);
mntmNewMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new ZipFileFilter());
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
BytecodeViewer.viewer.setC(true);
FernFlowerDecompiler d = new FernFlowerDecompiler();
d.decompileToZip(file.getAbsolutePath());
BytecodeViewer.viewer.setC(false);
}
}
});
mnNewMenu.add(mntmNewMenuItem);
JSeparator separator = new JSeparator();
mnNewMenu.add(separator);
mnNewMenu.add(mnRecentFiles);
JSeparator separator_1 = new JSeparator();
mnNewMenu.add(separator_1);
mntmAbout.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
aboutWindow.setVisible(true);
}
});
mnNewMenu.add(mntmAbout);
JMenuItem mntmExit = new JMenuItem("Exit");
mnNewMenu.add(mntmExit);
JMenu mnView = new JMenu("View");
menuBar.add(mnView);
mnView.add(sourcePane);
mnView.add(bytecodePane);
mnView.add(hexPane);
mnView.add(separator_2);
mnView.add(srcSyntax);
mnView.add(bycSyntax);
JMenu mnDecompilerSettings = new JMenu("Java Decompiler");
menuBar.add(mnDecompilerSettings);
mnDecompilerSettings.add(rbr);
mnDecompilerSettings.add(rsy);
mnDecompilerSettings.add(din);
mnDecompilerSettings.add(dc4);
mnDecompilerSettings.add(das);
mnDecompilerSettings.add(hes);
mnDecompilerSettings.add(hdc);
mnDecompilerSettings.add(dgs);
mnDecompilerSettings.add(ner);
mnDecompilerSettings.add(den);
mnDecompilerSettings.add(rgn);
mnDecompilerSettings.add(bto);
mnDecompilerSettings.add(nns);
mnDecompilerSettings.add(uto);
mnDecompilerSettings.add(udv);
mnDecompilerSettings.add(rer);
mnDecompilerSettings.add(fdi);
mnDecompilerSettings.add(asc);
JMenu mnBytecodeDecompilerSettings = new JMenu("Bytecode Decompiler");
menuBar.add(mnBytecodeDecompilerSettings);
mnBytecodeDecompilerSettings.add(debugHelpers);
mnBytecodeDecompilerSettings.add(debugInstructions);
mnBytecodeDecompilerSettings.add(chckbxmntmNewCheckItem);
menuBar.add(mnNewMenu_1);
mnNewMenu_1.add(mntmStartExternalPlugin);
mnNewMenu_1.add(separator_4);
mnNewMenu_1.add(mnRecentPlugins);
mnNewMenu_1.add(separator_5);
mnNewMenu_1.add(mntmNewMenuItem_1);
mnNewMenu_1.add(mntmShowMainMethods);
mnNewMenu_1.add(mntmShowAllStrings);
mntmReplaceStrings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if(!BytecodeViewer.loadedClasses.isEmpty())
new ReplaceStringsOptions().setVisible(true);
else
System.out.println("Plugin not ran, put some classes in first.");
}
});
mnNewMenu_1.add(mntmReplaceStrings);
mnNewMenu_1.add(mntmNewMenuItem_2);
mnNewMenu_1.add(mntmStartZkmString);
menuBar.add(mntmNewMenuItem_4);
mntmStartExternalPlugin.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new GroovyPythonRubyFileFilter());
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(This);
if (returnVal == JFileChooser.APPROVE_OPTION)
try {
BytecodeViewer.viewer.setC(true);
BytecodeViewer.startPlugin(fc.getSelectedFile());
BytecodeViewer.viewer.setC(false);
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
mntmStartZkmString.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PluginManager.runPlugin(new ZKMStringDecrypter());
}
});
mntmNewMenuItem_2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PluginManager.runPlugin(new AllatoriStringDecrypter());
}
});
mntmNewMenuItem_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!BytecodeViewer.loadedClasses.isEmpty())
new MaliciousCodeScannerOptions().setVisible(true);
else
System.out.println("Plugin not ran, put some classes in first.");
}
});
mntmShowAllStrings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PluginManager.runPlugin(new ShowAllStrings());
}
});
mntmShowMainMethods.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PluginManager.runPlugin(new ShowMainMethods());
}
});
setSize(new Dimension(800, 400));
setTitle("Bytecode Viewer - http://the.bytecode.club - @Konloch");
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.X_AXIS));
JScrollPane scrollPane = new JScrollPane();
scrollPane.setMaximumSize(new Dimension(12000, 32767));
//scrollPane.setViewportView(tree);
FileNavigationPane cn = new FileNavigationPane(this);
cn.setMinimumSize(new Dimension(200, 50));
//panel.add(cn);
SearchingPane s = new SearchingPane(this);
s.setPreferredSize(new Dimension(200, 50));
s.setMinimumSize(new Dimension(200, 50));
s.setMaximumSize(new Dimension(200, 2147483647));
//panel.add(s);
sp1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, cn, s);
//panel.add(sp1);
cn.setPreferredSize(new Dimension(200, 50));
cn.setMaximumSize(new Dimension(200, 2147483647));
WorkPane cv = new WorkPane(this);
sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp1, cv);
getContentPane().add(sp2);
sp2.setResizeWeight(0.05);
sp1.setResizeWeight(0.5);
rfComps.add(cn);
rfComps.add(s);
rfComps.add(cv);
this.setLocationRelativeTo(null);
}
@Override
public void openClassFile(final String name, final ClassNode cn) {
for (final VisibleComponent vc : rfComps) {
vc.openClassFile(name, cn);
}
}
@SuppressWarnings("unchecked")
public static <T> T getComponent(final Class<T> clazz) {
for (final VisibleComponent vc : rfComps) {
if (vc.getClass() == clazz)
return (T) vc;
}
return null;
}
public class GroovyPythonRubyFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = getExtension(f);
if (extension != null)
return (extension.equals("gy") || extension.equals("groovy") ||
extension.equals("py") || extension.equals("python") ||
extension.equals("rb") || extension.equals("ruby"));
return false;
}
@Override
public String getDescription() {
return "Groovy, Python or Ruby plugins.";
}
public String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1)
ext = s.substring(i+1).toLowerCase();
return ext;
}
}
public class JarZipClassFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = getExtension(f);
if (extension != null)
return (extension.equals("jar") || extension.equals("zip") || extension.equals("class"));
return false;
}
@Override
public String getDescription() {
return "Class Files or Zip/Jar Archives";
}
public String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1)
ext = s.substring(i+1).toLowerCase();
return ext;
}
}
public class ZipFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = getExtension(f);
if (extension != null)
return (extension.equals("zip"));
return false;
}
@Override
public String getDescription() {
return "Zip Archives";
}
public String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1)
ext = s.substring(i+1).toLowerCase();
return ext;
}
}
public class JarFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = getExtension(f);
if (extension != null)
return (extension.equals("jar"));
return false;
}
@Override
public String getDescription() {
return "Jar Archives";
}
public String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1)
ext = s.substring(i+1).toLowerCase();
return ext;
}
}
}

View file

@ -0,0 +1,78 @@
package the.bytecode.club.bytecodeviewer.gui;
import javax.swing.JFrame;
import java.awt.Dimension;
import javax.swing.JCheckBox;
import javax.swing.JButton;
import the.bytecode.club.bytecodeviewer.plugins.MaliciousCodeScanner;
import the.bytecode.club.bytecodeviewer.plugins.PluginManager;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class MaliciousCodeScannerOptions extends JFrame {
public MaliciousCodeScannerOptions() {
setSize(new Dimension(250, 277));
setResizable(false);
setTitle("Malicious Code Scanner Options");
getContentPane().setLayout(null);
final JCheckBox chckbxJavalangreflection = new JCheckBox("java/lang/reflection");
chckbxJavalangreflection.setSelected(true);
chckbxJavalangreflection.setBounds(6, 7, 232, 23);
getContentPane().add(chckbxJavalangreflection);
final JCheckBox chckbxJavanet = new JCheckBox("java/net");
chckbxJavanet.setSelected(true);
chckbxJavanet.setBounds(6, 59, 232, 23);
getContentPane().add(chckbxJavanet);
final JCheckBox chckbxJavaio = new JCheckBox("java/io");
chckbxJavaio.setBounds(6, 85, 232, 23);
getContentPane().add(chckbxJavaio);
final JCheckBox chckbxJavalangruntime = new JCheckBox("java/lang/Runtime");
chckbxJavalangruntime.setSelected(true);
chckbxJavalangruntime.setBounds(6, 33, 232, 23);
getContentPane().add(chckbxJavalangruntime);
final JCheckBox chckbxLdcContainswww = new JCheckBox("LDC contains 'www.'");
chckbxLdcContainswww.setSelected(true);
chckbxLdcContainswww.setBounds(6, 111, 232, 23);
getContentPane().add(chckbxLdcContainswww);
final JCheckBox chckbxLdcContainshttp = new JCheckBox("LDC contains 'http://'");
chckbxLdcContainshttp.setSelected(true);
chckbxLdcContainshttp.setBounds(6, 137, 232, 23);
getContentPane().add(chckbxLdcContainshttp);
final JCheckBox chckbxLdcContainshttps = new JCheckBox("LDC contains 'https://'");
chckbxLdcContainshttps.setSelected(true);
chckbxLdcContainshttps.setBounds(6, 163, 232, 23);
getContentPane().add(chckbxLdcContainshttps);
final JCheckBox chckbxLdcMatchesIp = new JCheckBox("LDC matches IP regex");
chckbxLdcMatchesIp.setSelected(true);
chckbxLdcMatchesIp.setBounds(6, 189, 232, 23);
getContentPane().add(chckbxLdcMatchesIp);
JButton btnNewButton = new JButton("Start Scanning");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
PluginManager.runPlugin(new MaliciousCodeScanner(chckbxJavalangreflection.isSelected(),
chckbxJavalangruntime.isSelected(), chckbxJavanet.isSelected(), chckbxJavaio.isSelected(),
chckbxLdcContainswww.isSelected(), chckbxLdcContainshttp.isSelected(), chckbxLdcContainshttps.isSelected(),
chckbxLdcMatchesIp.isSelected()));
dispose();
}
});
btnNewButton.setBounds(6, 219, 232, 23);
getContentPane().add(btnNewButton);
this.setLocationRelativeTo(null);
}
private static final long serialVersionUID = -2662514582647810868L;
}

View file

@ -0,0 +1,28 @@
package the.bytecode.club.bytecodeviewer.gui;
import javax.swing.JEditorPane;
/**
* Allows us to run a background thread then update the two JEditorPanes
*
* @author Konloch
*
*/
public abstract class PaneUpdaterThread extends Thread {
JEditorPane p1;
JEditorPane p2;
public PaneUpdaterThread(JEditorPane p1, JEditorPane p2) {
this.p1 = p1;
this.p2 = p2;
}
public abstract void doShit();
@Override
public void run() {
doShit();
}
}

View file

@ -0,0 +1,73 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import the.bytecode.club.bytecodeviewer.plugins.PluginManager;
import the.bytecode.club.bytecodeviewer.plugins.ReplaceStrings;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JCheckBox;
public class ReplaceStringsOptions extends JFrame {
public ReplaceStringsOptions() {
setSize(new Dimension(250, 176));
setResizable(false);
setTitle("Replace Strings");
getContentPane().setLayout(null);
JButton btnNewButton = new JButton("Start Replacing");
btnNewButton.setBounds(6, 115, 232, 23);
getContentPane().add(btnNewButton);
JLabel lblNewLabel = new JLabel("Original LDC:");
lblNewLabel.setBounds(6, 40, 67, 14);
getContentPane().add(lblNewLabel);
textField = new JTextField();
textField.setBounds(80, 37, 158, 20);
getContentPane().add(textField);
textField.setColumns(10);
JLabel lblNewLabel_1 = new JLabel("New LDC:");
lblNewLabel_1.setBounds(6, 65, 77, 14);
getContentPane().add(lblNewLabel_1);
textField_1 = new JTextField();
textField_1.setColumns(10);
textField_1.setBounds(80, 62, 158, 20);
getContentPane().add(textField_1);
JLabel lblNewLabel_2 = new JLabel("Class:");
lblNewLabel_2.setBounds(6, 90, 46, 14);
getContentPane().add(lblNewLabel_2);
textField_2 = new JTextField();
textField_2.setToolTipText("* will search all classes");
textField_2.setText("*");
textField_2.setBounds(80, 87, 158, 20);
getContentPane().add(textField_2);
textField_2.setColumns(10);
final JCheckBox chckbxNewCheckBox = new JCheckBox("Replace All Contains");
chckbxNewCheckBox.setToolTipText("If it's unticked, it will check if the string equals, if its ticked it will check if it contains, then replace the original LDC part of the string.");
chckbxNewCheckBox.setBounds(6, 7, 232, 23);
getContentPane().add(chckbxNewCheckBox);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
PluginManager.runPlugin(new ReplaceStrings(textField.getText(), textField_1.getText(), textField_2.getText(), chckbxNewCheckBox.isSelected()));
dispose();
}
});
this.setLocationRelativeTo(null);
}
private static final long serialVersionUID = -2662514582647810868L;
private JTextField textField;
private JTextField textField_1;
private JTextField textField_2;
}

View file

@ -0,0 +1,217 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.*;
import the.bytecode.club.bytecodeviewer.searching.*;
/**
* A pane dedicating to searching the loaded files.
*
* @author Konloch
* @author WaterWolf
*
*/
@SuppressWarnings("rawtypes")
public class SearchingPane extends VisibleComponent {
private static final long serialVersionUID = -1098524689236993932L;
FileChangeNotifier fcn;
JCheckBox exact = new JCheckBox("Exact");
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Root");
JTree tree;
SearchType searchType = null;
JComboBox searchRadiusBox;
public JButton search = new JButton("Search");
BackgroundSearchThread t = new BackgroundSearchThread(true) {
@Override
public void doSearch() {
// empty
}
};
@SuppressWarnings("unchecked")
public SearchingPane(final FileChangeNotifier fcn) {
super("Search");
this.fcn = fcn;
final JPanel optionPanel = new JPanel(new BorderLayout());
final JPanel searchRadiusOpt = new JPanel(new BorderLayout());
final JPanel searchOpts = new JPanel(new GridLayout(2, 1));
searchRadiusOpt.add(new JLabel("Search from "), BorderLayout.WEST);
DefaultComboBoxModel model = new DefaultComboBoxModel();
for (final SearchRadius st : SearchRadius.values()) {
model.addElement(st);
}
searchRadiusBox = new JComboBox(model);
searchRadiusOpt.add(searchRadiusBox, BorderLayout.CENTER);
searchOpts.add(searchRadiusOpt);
model = new DefaultComboBoxModel();
for (final SearchType st : SearchType.values()) {
model.addElement(st);
}
final JComboBox typeBox = new JComboBox(model);
final JPanel searchOptPanel = new JPanel();
final ItemListener il = new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent arg0) {
searchOptPanel.removeAll();
searchType = (SearchType) typeBox.getSelectedItem();
searchOptPanel.add(searchType.details.getPanel());
searchOptPanel.revalidate();
searchOptPanel.repaint();
}
};
typeBox.addItemListener(il);
typeBox.setSelectedItem(SearchType.LDC);
il.itemStateChanged(null);
searchOpts.add(typeBox);
optionPanel.add(searchOpts, BorderLayout.NORTH);
JPanel p2 = new JPanel();
p2.setLayout(new BorderLayout());
p2.add(searchOptPanel, BorderLayout.NORTH);
p2.add(exact, BorderLayout.SOUTH);
optionPanel.add(p2, BorderLayout.CENTER);
search.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent arg0) {
treeRoot.removeAllChildren();
searchType = (SearchType) typeBox.getSelectedItem();
final SearchRadius radius = (SearchRadius) searchRadiusBox.getSelectedItem();
final SearchResultNotifier srn = new SearchResultNotifier() {
@Override
public void notifyOfResult(final ClassNode clazz,
final MethodNode method, final AbstractInsnNode insn) {
treeRoot.add(new DefaultMutableTreeNode(clazz.name + "." + method.name));
}
};
if (radius == SearchRadius.All_Classes) {
if(t.finished) {
t = new BackgroundSearchThread() {
@Override
public void doSearch() {
for (ClassNode cln : BytecodeViewer.getLoadedClasses())
searchType.details.search(cln, srn, exact.isSelected());
MainViewerGUI.getComponent(SearchingPane.class).search.setEnabled(true);
MainViewerGUI.getComponent(SearchingPane.class).search.setText("Search");
tree.expandPath(new TreePath(tree.getModel().getRoot()));
tree.updateUI();
}
};
MainViewerGUI.getComponent(SearchingPane.class).search.setEnabled(false);
MainViewerGUI.getComponent(SearchingPane.class).search.setText("Searching, please wait..");
t.start();
} else { //this should really never be called.
BytecodeViewer.showMessage("You currently have a search performing in the background, please wait for that to finish.");
}
}
else if (radius == SearchRadius.Current_Class) {
final ClassViewer cv = MainViewerGUI.getComponent(WorkPane.class).getCurrentClass();
if (cv != null) {
searchType.details.search(cv.cn, srn, exact.isSelected());
}
}
}
});
optionPanel.add(search, BorderLayout.SOUTH);
this.tree = new JTree(treeRoot);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(optionPanel, BorderLayout.NORTH);
getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER);
this.tree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(final TreeSelectionEvent arg0) {
final TreePath path = arg0.getPath();
if ( ((TreeNode)path.getLastPathComponent()).getChildCount() > 0)
return;
final String clazzName = path.getLastPathComponent().toString();
final ClassNode fN = BytecodeViewer.getClassNode(clazzName);
if (fN != null) {
MainViewerGUI.getComponent(FileNavigationPane.class).openClassFileToWorkSpace(clazzName, fN);
}
}
});
this.setVisible(true);
}
public enum SearchType {
LDC (new LDCSearch()),
Regex (new RegexSearch()),
MethodCall (new MethodCallSearch()),
FieldCall (new FieldCallSearch());
public final SearchTypeDetails details;
SearchType(final SearchTypeDetails details) {
this.details = details;
}
}
public enum SearchRadius {
All_Classes,
Current_Class;
}
public void resetWorkspace() {
treeRoot.removeAllChildren();
tree.updateUI();
}
}

View file

@ -0,0 +1,186 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.plaf.basic.BasicButtonUI;
/**
* Component to be used as tabComponent;
* Contains a JLabel to show the text and
* a JButton to close the tab it belongs to
*
* @author Konloch
* @author WaterWolf
*
*/
public class TabbedPane extends JPanel {
private static final long serialVersionUID = -4774885688297538774L;
private final JTabbedPane pane;
final JButton button = new TabButton();
public TabbedPane(final JTabbedPane pane) {
//unset default FlowLayout' gaps
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
if (pane == null)
throw new NullPointerException("TabbedPane is null");
this.pane = pane;
setOpaque(false);
//make JLabel read titles from JTabbedPane
final JLabel label = new JLabel() {
private static final long serialVersionUID = -5511025206527893360L;
@Override
public String getText() {
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1)
return pane.getTitleAt(i);
return null;
}
};
add(label);
//add more space between the label and the button
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
//tab button
add(button);
//add more space to the top of the component
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
pane.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent arg0) {
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
@Override
public void mousePressed(MouseEvent arg0) {
}
@Override
public void mouseReleased(MouseEvent e) {
//final Component component = e.getComponent();
// if(component instanceof JTabbedPane) {
if(e.getModifiers() == 8) {
for(Component c : pane.getComponents()) {
if(c.getMousePosition() != null && c instanceof JPanel) {
System.out.println("gotten here...");
/*BytecodeViewer.viewer.getComponent(WorkPane.class).tabs.remove(component);
final int i = BytecodeViewer.viewer.getComponent(WorkPane.class).tabs.indexOfTabComponent(c);
if (i != -1)
BytecodeViewer.viewer.getComponent(WorkPane.class).tabs.remove(i);
BytecodeViewer.viewer.getComponent(WorkPane.class).tabs.updateUI();
BytecodeViewer.viewer.getComponent(WorkPane.class).tabs.repaint();
*////if(c.getComponentAt((int)c.getMousePosition().getX(), (int)c.getMousePosition().getY())button.)
// button.doClick();
}
//System.out.println(c.getMousePosition() + ":" + e.getX());
//System.out.println(c.getWidth() + ":" + e.getX());
//if( e.getX() >= &&
// e.getY())
// button.doClick();
}
}
}
//}
});
}
private class TabButton extends JButton implements ActionListener {
private static final long serialVersionUID = -4492967978286454159L;
public TabButton() {
final int size = 17;
setPreferredSize(new Dimension(size, size));
setToolTipText("Close this tab");
//Make the button looks the same for all Laf's
setUI(new BasicButtonUI());
//Make it transparent
setContentAreaFilled(false);
//No need to be focusable
setFocusable(false);
setBorder(BorderFactory.createEtchedBorder());
setBorderPainted(false);
//Making nice rollover effect
//we use the same listener for all buttons
addMouseListener(buttonMouseListener);
setRolloverEnabled(true);
//Close the proper tab by clicking the button
addActionListener(this);
}
public void actionPerformed(final ActionEvent e) {
final int i = pane.indexOfTabComponent(TabbedPane.this);
if (i != -1) {
pane.remove(i);
}
}
//we don't want to update UI for this button
@Override
public void updateUI() {
}
//paint the cross
@Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g.create();
//shift the image for pressed buttons
if (getModel().isPressed()) {
g2.translate(1, 1);
}
g2.setStroke(new BasicStroke(2));
g2.setColor(Color.BLACK);
if (getModel().isRollover()) {
g2.setColor(Color.MAGENTA);
}
final int delta = 6;
g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1);
g2.dispose();
}
}
private final static MouseListener buttonMouseListener = new MouseAdapter() {
@Override
public void mouseEntered(final MouseEvent e) {
final Component component = e.getComponent();
if (component instanceof AbstractButton) {
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(true);
}
}
@Override
public void mouseExited(final MouseEvent e) {
final Component component = e.getComponent();
if (component instanceof AbstractButton) {
final AbstractButton button = (AbstractButton) component;
button.setBorderPainted(false);
}
}
};
}

View file

@ -0,0 +1,35 @@
package the.bytecode.club.bytecodeviewer.gui;
import javax.swing.JInternalFrame;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.FileChangeNotifier;
/**
* Used to represent all the panes inside of Bytecode Viewer, this is temp code
* that was included from porting in J-RET, this needs to be re-written.
*
* @author Konloch
* @author WaterWolf
*
*/
public abstract class VisibleComponent extends JInternalFrame implements FileChangeNotifier {
private static final long serialVersionUID = -6453413772343643526L;
public VisibleComponent(final String title) {
super(title, false, false, false, false);
this.setFrameIcon(null);
}
@SuppressWarnings("unused")
private VisibleComponent() { //because we want to enforce the title argument
}
@Override
public void openClassFile(final String name, final ClassNode cn) {}
}

View file

@ -0,0 +1,156 @@
package the.bytecode.club.bytecodeviewer.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.lang.reflect.Field;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import jsyntaxpane.DefaultSyntaxKit;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.FileChangeNotifier;
/**
* The pane that contains all of the classes as tabs.
*
* @author Konloch
* @author WaterWolf
*
*/
public class WorkPane extends VisibleComponent implements ActionListener {
private static final long serialVersionUID = 6542337997679487946L;
FileChangeNotifier fcn;
JTabbedPane tabs;
JPanel buttonPanel;
JButton refreshClass;
HashMap<String, Integer> workingOn = new HashMap<String, Integer>();
public static int SyntaxFontHeight = 12;
public WorkPane(final FileChangeNotifier fcn) {
super("WorkPanel");
setTitle("Work Space");
DefaultSyntaxKit.initKit();
Font defFont = null;
try {
final Field defFontField = DefaultSyntaxKit.class.getDeclaredField("DEFAULT_FONT");
defFontField.setAccessible(true);
defFont = (Font) defFontField.get(null);
} catch (final Exception e) {
e.printStackTrace();
}
SyntaxFontHeight = defFont.getSize();
this.tabs = new JTabbedPane();
this.fcn = fcn;
getContentPane().setLayout(new BorderLayout());
getContentPane().add(tabs, BorderLayout.CENTER);
buttonPanel = new JPanel(new FlowLayout());
refreshClass = new JButton("Refresh class");
refreshClass.addActionListener(this);
buttonPanel.add(refreshClass);
buttonPanel.setVisible(false);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
tabs.addContainerListener(new ContainerListener() {
@Override
public void componentAdded(final ContainerEvent e) {
}
@Override
public void componentRemoved(final ContainerEvent e) {
final Component c = e.getChild();
if (c instanceof ClassViewer) {
workingOn.remove(((ClassViewer)c).name);
}
}
});
tabs.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(final ChangeEvent arg0) {
buttonPanel.setVisible(tabs.getSelectedIndex() != -1);
}
});
this.setVisible(true);
}
int tabCount = 0;
public void addWorkingFile(final String name, final ClassNode cn) {
if(!BytecodeViewer.viewer.hexPane.isSelected() &&
!BytecodeViewer.viewer.sourcePane.isSelected() &&
!BytecodeViewer.viewer.bytecodePane.isSelected()) {
BytecodeViewer.showMessage("You currently have no viewing panes selected.");
return;
}
if (!workingOn.containsKey(name)) {
final Component tabComp = new ClassViewer(name, cn);
tabs.add(tabComp);
final int tabCount = tabs.indexOfComponent(tabComp);
workingOn.put(name, tabCount);
tabs.setTabComponentAt(tabCount, new TabbedPane(tabs));
tabs.setSelectedIndex(tabCount);
} else {
tabs.setSelectedIndex(workingOn.get(name));
}
}
@Override
public void openClassFile(final String name, final ClassNode cn) {
addWorkingFile(name, cn);
}
public ClassViewer getCurrentClass() {
return (ClassViewer) tabs.getSelectedComponent();
}
@Override
public void actionPerformed(final ActionEvent arg0) {
final JButton src = (JButton) arg0.getSource();
if (src == refreshClass) {
final Component tabComp = tabs.getSelectedComponent();
if (tabComp != null) {
BytecodeViewer.viewer.setC(true);
((ClassViewer)tabComp).startPaneUpdater();
BytecodeViewer.viewer.setC(false);
}
}
}
public void resetWorkspace() {
tabs.removeAll();
tabs.updateUI();
}
}

View file

@ -0,0 +1,23 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.util.ArrayList;
import org.objectweb.asm.tree.ClassNode;
/**
* Coming soon.
*
* @author Konloch
*
*/
public class AllatoriStringDecrypter extends Plugin {
@Override
public void execute(ArrayList<ClassNode> classNodeList) {
for(ClassNode classNode : classNodeList) {
}
}
}

View file

@ -0,0 +1,88 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.util.ArrayList;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/**
* The idea/core was based off of J-RET's Malicious Code Searcher
* I improved it, and added more stuff to search for.
*
* @author Konloch
* @author WaterWolf
*
*/
public class MaliciousCodeScanner extends Plugin {
public boolean
ORE,
ONE,
ORU,
OIO,
LWW,
LHT,
LHS,
LIP;
public MaliciousCodeScanner(boolean reflect, boolean runtime, boolean net, boolean io,
boolean www, boolean http, boolean https, boolean ip) {
ORE = reflect;
ONE = net;
ORU = runtime;
OIO = io;
LWW = www;
LHT = http;
LHS = https;
LIP = ip;
}
@Override
public void execute(ArrayList<ClassNode> classNodeList) {
PluginConsole frame = new PluginConsole("Malicious Code Scanner");
BytecodeViewer.viewer.setC(true);
for(ClassNode classNode : classNodeList) {
for(Object o : classNode.methods.toArray()) {
MethodNode m = (MethodNode) o;
InsnList iList = m.instructions;
for(AbstractInsnNode a : iList.toArray()) {
if (a instanceof MethodInsnNode) {
final MethodInsnNode min = (MethodInsnNode) a;
if ((ORE && min.owner.startsWith("java/lang/reflect")) ||
(ONE && min.owner.startsWith("java/net")) ||
(ORU && min.owner.equals("java/lang/Runtime")) ||
(OIO && min.owner.startsWith("java/io")))
{
frame.appendText("Found Method call to " + min.owner + "." + min.name + "(" + min.desc + ") at " + classNode.name + "." +m.name+"("+m.desc+")");
}
}
if (a instanceof LdcInsnNode) {
if(((LdcInsnNode)a).cst instanceof String) {
final String s = (String) ((LdcInsnNode)a).cst;
if ((LWW && s.contains("www.")) ||
(LHT && s.contains("http://")) ||
(LHS && s.contains("https://")) ||
(ORE && s.contains("java/lang/Runtime")) ||
(ORE && s.contains("java.lang.Runtime")) ||
(LIP && s.matches("\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b")))
{
frame.appendText("Found LDC \"" + s + "\" at " + classNode.name + "." +m.name+"("+m.desc+")");
}
}
}
}
}
}
BytecodeViewer.viewer.setC(false);
frame.setVisible(true);
}
}

View file

@ -0,0 +1,37 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.util.ArrayList;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/**
* A simple plugin class, it will run the plugin in a background thread.
*
* @author Konloch
*
*/
public abstract class Plugin extends Thread {
@Override
public void run() {
BytecodeViewer.viewer.setIcon(true);
try {
if(!BytecodeViewer.getLoadedClasses().isEmpty())
execute(BytecodeViewer.getLoadedClasses());
else
System.out.println("Plugin not ran, put some classes in first.");
} catch(Exception e) {
e.printStackTrace();
} finally {
finished = true;
BytecodeViewer.viewer.setIcon(false);
}
}
public boolean finished = false;
public abstract void execute(ArrayList<ClassNode> classNodeList);
}

View file

@ -0,0 +1,36 @@
package the.bytecode.club.bytecodeviewer.plugins;
import javax.swing.JFrame;
import java.awt.Dimension;
import javax.swing.JScrollPane;
import java.awt.BorderLayout;
import javax.swing.JTextArea;
/**
* A simple console GUI.
*
* @author Konloch
*
*/
public class PluginConsole extends JFrame {
JTextArea textArea = new JTextArea();
public PluginConsole(String pluginName) {
setTitle("Bytecode Viewer - Plugin Console - " + pluginName);
setSize(new Dimension(542, 316));
JScrollPane scrollPane = new JScrollPane();
getContentPane().add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(textArea);
this.setLocationRelativeTo(null);
}
public void appendText(String t) {
textArea.setText((textArea.getText().isEmpty() ? "" : textArea.getText()+"\r\n")+t);
textArea.setCaretPosition(textArea.getLineCount());
}
private static final long serialVersionUID = -6556940545421437508L;
}

View file

@ -0,0 +1,106 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/**
* Supports loading of groovy, python or ruby scripts.
*
* Only allows one plugin to be running at once.
*
* @author Konloch
*
*/
public class PluginManager {
private static Plugin pluginInstance;
public static void runPlugin(Plugin newPluginInstance) {
if(pluginInstance == null || pluginInstance.finished) {
pluginInstance = newPluginInstance;
pluginInstance.start(); //start the thread
} else if(!pluginInstance.finished) {
BytecodeViewer.showMessage("There is currently another plugin running right now, please wait for that to finish executing.");
}
}
public static void runPlugin(File f) throws Exception {
Plugin p = null;
if(f.getName().endsWith(".gy") || f.getName().endsWith(".groovy")) {
p = loadGroovyScript(f);
}
if(f.getName().endsWith(".py") || f.getName().endsWith(".python")) {
p = loadPythonScript(f);
}
if(f.getName().endsWith(".rb") || f.getName().endsWith(".ruby")) {
p = loadRubyScript(f);
}
if(p != null) {
runPlugin(p);
}
}
/**
* Loads a groovy file as a Script
* @param file
* @return
* @throws Exception
*/
private static Plugin loadGroovyScript(File file) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("groovy");
if(engine == null)
throw new Exception("Cannot find Groovy script engine! Please contact Konloch.");
Reader reader = new FileReader(file);
engine.eval(reader);
return (Plugin)engine.eval("new " + file.getName().replace(".gy", "").replace(".groovy", "") + "();");
}
/**
* Loads a python file as a Script
* @param file
* @return
* @throws Exception
*/
private static Plugin loadPythonScript(File file) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("python");
if(engine == null)
throw new Exception("Cannot find Jython script engine! Please contact Konloch.");
Reader reader = new FileReader(file);
engine.eval(reader);
return (Plugin)engine.eval(file.getName().replace(".py", "").replace(".python", "") + "()");
}
/**
* Loads a ruby file as a Script
* @param file
* @return
* @throws Exception
*/
private static Plugin loadRubyScript(File file) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
if(engine == null)
throw new Exception("Cannot find jRuby script engine! Please contact Konloch.");
Reader reader = new FileReader(file);
engine.eval(reader);
return (Plugin)engine.eval(file.getName().replace(".rb", "").replace(".ruby", "") + ".new");
}
}

View file

@ -0,0 +1,116 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.util.ArrayList;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/**
* Replaces all string and string[] instances with whatever.
*
* @author Konloch
*
*/
public class ReplaceStrings extends Plugin {
PluginConsole frame = new PluginConsole("Replace Strings");
String originalLDC;
String newLDC;
String className;
boolean contains;
public ReplaceStrings(String originalLDC, String newLDC, String className, boolean contains) {
this.originalLDC = originalLDC;
this.newLDC = newLDC;
this.className = className;
this.contains = contains;
}
@Override
public void execute(ArrayList<ClassNode> classNodeList) {
BytecodeViewer.viewer.setC(true);
if(!className.equals("*")) {
for(ClassNode classNode : classNodeList) {
if(classNode.name.equals(className))
scanClassNode(classNode);
}
} else {
for(ClassNode classNode : classNodeList) {
scanClassNode(classNode);
}
}
BytecodeViewer.viewer.setC(false);
frame.setVisible(true);
}
public void scanClassNode(ClassNode classNode) {
for(Object o : classNode.fields.toArray()) {
FieldNode f = (FieldNode) o;
Object v = f.value;
if(v instanceof String) {
String s = (String)v;
if(contains) {
if(s.contains(originalLDC))
f.value = ((String)f.value).replaceAll(originalLDC, newLDC);
} else {
if(s.equals(originalLDC))
f.value = newLDC;
}
}
if(v instanceof String[]) {
for(int i = 0; i < ((String[])v).length; i++) {
String s = ((String[])v)[i];
if(contains) {
if(s.contains(originalLDC)) {
f.value = ((String[])f.value)[i].replaceAll(originalLDC, newLDC);
String ugh = s.replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r");
frame.appendText(classNode.name + "." +f.name+""+f.desc+" -> \"" + ugh + "\" replaced with \"" + s.replaceAll(originalLDC, newLDC) + "\"");
}
} else {
if(s.equals(originalLDC)) {
((String[])f.value)[i] = newLDC;
String ugh = s.replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r");
frame.appendText(classNode.name + "." +f.name+""+f.desc+" -> \"" + ugh + "\" replaced with \"" + newLDC + "\"");
}
}
}
}
}
for(Object o : classNode.methods.toArray()) {
MethodNode m = (MethodNode) o;
InsnList iList = m.instructions;
for(AbstractInsnNode a : iList.toArray()) {
if (a instanceof LdcInsnNode) {
if(((LdcInsnNode)a).cst instanceof String) {
final String s = (String) ((LdcInsnNode)a).cst;
if(contains) {
if(s.contains(originalLDC)) {
((LdcInsnNode)a).cst = ((String)((LdcInsnNode)a).cst).replaceAll(originalLDC, newLDC);
String ugh = s.replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r");
frame.appendText(classNode.name + "." +m.name+""+m.desc+" -> \"" + ugh + "\" replaced with \"" + s.replaceAll(originalLDC, newLDC) + "\"");
}
} else {
if(s.equals(originalLDC)) {
((LdcInsnNode)a).cst = newLDC;
String ugh = s.replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r");
frame.appendText(classNode.name + "." +m.name+""+m.desc+" -> \"" + ugh + "\" replaced with \"" + newLDC + "\"");
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,64 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.util.ArrayList;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/**
* Simply shows all the non-empty strings in every single class
*
* @author Konloch
*
*/
public class ShowAllStrings extends Plugin {
@Override
public void execute(ArrayList<ClassNode> classNodeList) {
PluginConsole frame = new PluginConsole("Show All Strings");
BytecodeViewer.viewer.setC(true);
for(ClassNode classNode : classNodeList) {
for(Object o : classNode.fields.toArray()) {
FieldNode f = (FieldNode) o;
Object v = f.value;
if(v instanceof String) {
String s = (String)v;
if(!s.isEmpty())
frame.appendText(classNode.name + "." +f.name+""+f.desc+" -> \"" + s.replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r") + "\"");
}
if(v instanceof String[]) {
for(int i = 0; i < ((String[])v).length; i++) {
String s = ((String[])v)[i];
if(!s.isEmpty())
frame.appendText(classNode.name + "." +f.name+""+f.desc+"["+i+"] -> \"" + s.replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r") + "\"");
}
}
}
for(Object o : classNode.methods.toArray()) {
MethodNode m = (MethodNode) o;
InsnList iList = m.instructions;
for(AbstractInsnNode a : iList.toArray()) {
if (a instanceof LdcInsnNode) {
if(((LdcInsnNode)a).cst instanceof String) {
final String s = (String) ((LdcInsnNode)a).cst;
if(!s.isEmpty())
frame.appendText(classNode.name + "." +m.name+""+m.desc+" -> \"" + s.replaceAll("\\n", "\\\\n").replaceAll("\\r", "\\\\r") + "\"");
}
}
}
}
}
BytecodeViewer.viewer.setC(false);
frame.setVisible(true);
}
}

View file

@ -0,0 +1,35 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.util.ArrayList;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/**
* Simply shows all classes that have a public static void main(String[])
*
* @author Konloch
*
*/
public class ShowMainMethods extends Plugin {
@Override
public void execute(ArrayList<ClassNode> classNodeList) {
PluginConsole frame = new PluginConsole("Show Main Methods");
BytecodeViewer.viewer.setC(true);
for(ClassNode classNode : classNodeList) {
for(Object o : classNode.methods.toArray()) {
MethodNode m = (MethodNode) o;
if(m.name.equals("main") && m.desc.equals("([Ljava/lang/String;)V"))
frame.appendText(classNode.name + "." +m.name+""+m.desc);
}
}
BytecodeViewer.viewer.setC(false);
frame.setVisible(true);
}
}

View file

@ -0,0 +1,23 @@
package the.bytecode.club.bytecodeviewer.plugins;
import java.util.ArrayList;
import org.objectweb.asm.tree.ClassNode;
/**
* Coming soon.
*
* @author Konloch
*
*/
public class ZKMStringDecrypter extends Plugin {
@Override
public void execute(ArrayList<ClassNode> classNodeList) {
for(ClassNode classNode : classNodeList) {
}
}
}

View file

@ -0,0 +1,34 @@
package the.bytecode.club.bytecodeviewer.searching;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
/**
* A simple class to make searching run in a background thread.
*
* @author Konloch
*
*/
public abstract class BackgroundSearchThread extends Thread {
public BackgroundSearchThread() {
}
public BackgroundSearchThread(boolean finished) {
this.finished = finished;
}
public boolean finished = false;
public abstract void doSearch();
@Override
public void run() {
BytecodeViewer.viewer.setIcon(true);
doSearch();
finished = true;
BytecodeViewer.viewer.setIcon(false);
}
}

View file

@ -0,0 +1,100 @@
package the.bytecode.club.bytecodeviewer.searching;
import java.awt.GridLayout;
import java.util.Iterator;
import java.util.ListIterator;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
/**
* Field call searching
*
* @author Water Wolf
*
*/
public class FieldCallSearch implements SearchTypeDetails {
JTextField mOwner = new JTextField(""), mName = new JTextField(""), mDesc = new JTextField("");
JPanel myPanel = null;
@Override
public JPanel getPanel() {
if (myPanel == null) {
myPanel = new JPanel(new GridLayout(3, 2));
myPanel.add(new JLabel("Owner: "));
myPanel.add(mOwner);
myPanel.add(new JLabel("Name: "));
myPanel.add(mName);
myPanel.add(new JLabel("Desc: "));
myPanel.add(mDesc);
}
return myPanel;
}
@Override
public void search(final ClassNode node, final SearchResultNotifier srn, boolean exact) {
@SuppressWarnings("unchecked")
final Iterator<MethodNode> methods = node.methods.iterator();
String owner = mOwner.getText();
if (owner.isEmpty()) {
owner = null;
}
String name = mName.getText();
if (name.isEmpty()) {
name = null;
}
String desc = mDesc.getText();
if (desc.isEmpty()) {
desc = null;
}
while (methods.hasNext()) {
final MethodNode method = methods.next();
final InsnList insnlist = method.instructions;
@SuppressWarnings("unchecked")
final ListIterator<AbstractInsnNode> instructions = insnlist.iterator();
while (instructions.hasNext()) {
final AbstractInsnNode insnNode = instructions.next();
if (insnNode instanceof FieldInsnNode) {
final FieldInsnNode min = (FieldInsnNode) insnNode;
if(name == null && owner == null && desc == null)
continue;
if(exact) {
if (name != null && !name.equals(min.name)) {
continue;
}
if (owner != null && !owner.equals(min.owner)) {
continue;
}
if (desc != null && !desc.equals(min.desc)) {
continue;
}
srn.notifyOfResult(node, method, insnNode);
} else {
if (name != null && !name.contains(min.name)) {
continue;
}
if (owner != null && !owner.contains(min.owner)) {
continue;
}
if (desc != null && !desc.contains(min.desc)) {
continue;
}
srn.notifyOfResult(node, method, insnNode);
}
}
}
}
}
}

View file

@ -0,0 +1,66 @@
package the.bytecode.club.bytecodeviewer.searching;
import java.awt.GridLayout;
import java.util.Iterator;
import java.util.ListIterator;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
/**
* LDC Searching
*
* @author WaterWolf
*
*/
public class LDCSearch implements SearchTypeDetails {
JTextField searchText = new JTextField("");
JPanel myPanel = null;
@Override
public JPanel getPanel() {
if (myPanel == null) {
myPanel = new JPanel(new GridLayout(1, 2));
myPanel.add(new JLabel("Search String: "));
myPanel.add(searchText);
}
return myPanel;
}
@SuppressWarnings("unchecked")
@Override
public void search(final ClassNode node, final SearchResultNotifier srn, boolean exact) {
final Iterator<MethodNode> methods = node.methods.iterator();
final String srchText = searchText.getText();
if(srchText.isEmpty())
return;
while (methods.hasNext()) {
final MethodNode method = methods.next();
final InsnList insnlist = method.instructions;
final ListIterator<AbstractInsnNode> instructions = insnlist.iterator();
while (instructions.hasNext()) {
final AbstractInsnNode insnNode = instructions.next();
if (insnNode instanceof LdcInsnNode) {
final Object ldcObject = ((LdcInsnNode) insnNode).cst;
final String ldcString = ldcObject.toString();
if ((exact && ldcString.equals(srchText)) ||
(!exact && ldcString.contains(srchText)))
{
srn.notifyOfResult(node, method, insnNode);
}
}
}
}
}
}

View file

@ -0,0 +1,99 @@
package the.bytecode.club.bytecodeviewer.searching;
import java.awt.GridLayout;
import java.util.Iterator;
import java.util.ListIterator;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
/**
* Method call searching
*
* @author WaterWolf
*
*/
public class MethodCallSearch implements SearchTypeDetails {
JTextField mOwner = new JTextField(""), mName = new JTextField(""), mDesc = new JTextField("");
JPanel myPanel = null;
@Override
public JPanel getPanel() {
if (myPanel == null) {
myPanel = new JPanel(new GridLayout(3, 2));
myPanel.add(new JLabel("Owner: "));
myPanel.add(mOwner);
myPanel.add(new JLabel("Name: "));
myPanel.add(mName);
myPanel.add(new JLabel("Desc: "));
myPanel.add(mDesc);
}
return myPanel;
}
@SuppressWarnings("unchecked")
@Override
public void search(final ClassNode node, final SearchResultNotifier srn, boolean exact) {
final Iterator<MethodNode> methods = node.methods.iterator();
String owner = mOwner.getText();
if (owner.isEmpty()) {
owner = null;
}
String name = mName.getText();
if (name.isEmpty()) {
name = null;
}
String desc = mDesc.getText();
if (desc.isEmpty()) {
desc = null;
}
while (methods.hasNext()) {
final MethodNode method = methods.next();
final InsnList insnlist = method.instructions;
final ListIterator<AbstractInsnNode> instructions = insnlist.iterator();
while (instructions.hasNext()) {
final AbstractInsnNode insnNode = instructions.next();
if (insnNode instanceof MethodInsnNode) {
final MethodInsnNode min = (MethodInsnNode) insnNode;
if(name == null && owner == null && desc == null)
continue;
if(exact) {
if (name != null && !name.equals(min.name)) {
continue;
}
if (owner != null && !owner.equals(min.owner)) {
continue;
}
if (desc != null && !desc.equals(min.desc)) {
continue;
}
srn.notifyOfResult(node, method, insnNode);
} else {
if (name != null && !name.contains(min.name)) {
continue;
}
if (owner != null && !owner.contains(min.owner)) {
continue;
}
if (desc != null && !desc.contains(min.desc)) {
continue;
}
srn.notifyOfResult(node, method, insnNode);
}
}
}
}
}
}

View file

@ -0,0 +1,381 @@
package the.bytecode.club.bytecodeviewer.searching;
import java.rmi.UnexpectedException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
/**
* An instruction finder that finds regex patterns in a method's instruction
* list and returns an array with the found instructions.
*
* @author Frédéric Hannes
*
*/
public class RegexInsnFinder {
private static String[] opcodes = new String[] { "NOP", "ACONST_NULL",
"ICONST_M1", "ICONST_0", "ICONST_1", "ICONST_2", "ICONST_3",
"ICONST_4", "ICONST_5", "LCONST_0", "LCONST_1", "FCONST_0",
"FCONST_1", "FCONST_2", "DCONST_0", "DCONST_1", "BIPUSH", "SIPUSH",
"LDC", "LDC_W", "LDC2_W", "ILOAD", "LLOAD", "FLOAD", "DLOAD",
"ALOAD", "ILOAD_0", "ILOAD_1", "ILOAD_2", "ILOAD_3", "LLOAD_0",
"LLOAD_1", "LLOAD_2", "LLOAD_3", "FLOAD_0", "FLOAD_1", "FLOAD_2",
"FLOAD_3", "DLOAD_0", "DLOAD_1", "DLOAD_2", "DLOAD_3", "ALOAD_0",
"ALOAD_1", "ALOAD_2", "ALOAD_3", "IALOAD", "LALOAD", "FALOAD",
"DALOAD", "AALOAD", "BALOAD", "CALOAD", "SALOAD", "ISTORE",
"LSTORE", "FSTORE", "DSTORE", "ASTORE", "ISTORE_0", "ISTORE_1",
"ISTORE_2", "ISTORE_3", "LSTORE_0", "LSTORE_1", "LSTORE_2",
"LSTORE_3", "FSTORE_0", "FSTORE_1", "FSTORE_2", "FSTORE_3",
"DSTORE_0", "DSTORE_1", "DSTORE_2", "DSTORE_3", "ASTORE_0",
"ASTORE_1", "ASTORE_2", "ASTORE_3", "IASTORE", "LASTORE",
"FASTORE", "DASTORE", "AASTORE", "BASTORE", "CASTORE", "SASTORE",
"POP", "POP2", "DUP", "DUP_X1", "DUP_X2", "DUP2", "DUP2_X1",
"DUP2_X2", "SWAP", "IADD", "LADD", "FADD", "DADD", "ISUB", "LSUB",
"FSUB", "DSUB", "IMUL", "LMUL", "FMUL", "DMUL", "IDIV", "LDIV",
"FDIV", "DDIV", "IREM", "LREM", "FREM", "DREM", "INEG", "LNEG",
"FNEG", "DNEG", "ISHL", "LSHL", "ISHR", "LSHR", "IUSHR", "LUSHR",
"IAND", "LAND", "IOR", "LOR", "IXOR", "LXOR", "IINC", "I2L", "I2F",
"I2D", "L2I", "L2F", "L2D", "F2I", "F2L", "F2D", "D2I", "D2L",
"D2F", "I2B", "I2C", "I2S", "LCMP", "FCMPL", "FCMPG", "DCMPL",
"DCMPG", "IFEQ", "IFNE", "IFLT", "IFGE", "IFGT", "IFLE",
"IF_ICMPEQ", "IF_ICMPNE", "IF_ICMPLT", "IF_ICMPGE", "IF_ICMPGT",
"IF_ICMPLE", "IF_ACMPEQ", "IF_ACMPNE", "GOTO", "JSR", "RET",
"TABLESWITCH", "LOOKUPSWITCH", "IRETURN", "LRETURN", "FRETURN",
"DRETURN", "ARETURN", "RETURN", "GETSTATIC", "PUTSTATIC",
"GETFIELD", "PUTFIELD", "INVOKEVIRTUAL", "INVOKESPECIAL",
"INVOKESTATIC", "INVOKEINTERFACE", "INVOKEDYNAMIC", "NEW",
"NEWARRAY", "ANEWARRAY", "ARRAYLENGTH", "ATHROW", "CHECKCAST",
"INSTANCEOF", "MONITORENTER", "MONITOREXIT", "WIDE",
"MULTIANEWARRAY", "IFNULL", "IFNONNULL", "GOTO_W", "JSR_W" };
private static String[] opcodesVar = new String[] { "ILOAD", "LLOAD",
"FLOAD", "DLOAD", "ALOAD", "ISTORE", "LSTORE", "FSTORE", "DSTORE",
"ASTORE", "RET" };
private static String opcodeVars = buildRegexItems(opcodesVar);
private static String[] opcodesInt = new String[] { "BIPUSH", "SIPUSH",
"NEWARRAY" };
private static String opcodesInts = buildRegexItems(opcodesInt);
private static String[] opcodesField = new String[] { "GETSTATIC",
"PUTSTATIC", "GETFIELD", "PUTFIELD" };
private static String opcodesFields = buildRegexItems(opcodesField);
private static String[] opcodesMethod = new String[] { "INVOKEVIRTUAL",
"INVOKESPECIAL", "INVOKESTATIC", "INVOKEINTERFACE", "INVOKEDYNAMIC" };
private static String opcodesMethods = buildRegexItems(opcodesMethod);
private static String[] opcodesType = new String[] { "NEW", "ANEWARRAY",
"ARRAYLENGTH", "CHECKCAST", "INSTANCEOF" };
private static String opcodesTypes = buildRegexItems(opcodesType);
private static String[] opcodesIf = new String[] { "IFEQ", "IFNE", "IFLT",
"IFGE", "IFGT", "IFLE", "IF_ICMPEQ", "IF_ICMPNE", "IF_ICMPLT",
"IF_ICMPGE", "IF_ICMPGT", "IF_ICMPLE", "IF_ACMPEQ", "IF_ACMPNE" };
private static String opcodesIfs = buildRegexItems(opcodesIf, false, false);
private static String[] opcodesAny = new String[] { "NOP", "ACONST_NULL",
"ICONST_M1", "ICONST_0", "ICONST_1", "ICONST_2", "ICONST_3",
"ICONST_4", "ICONST_5", "LCONST_0", "LCONST_1", "FCONST_0",
"FCONST_1", "FCONST_2", "DCONST_0", "DCONST_1", "BIPUSH", "SIPUSH",
"LDC", "LDC_W", "LDC2_W", "ILOAD", "LLOAD", "FLOAD", "DLOAD",
"ALOAD", "IALOAD", "LALOAD", "FALOAD", "DALOAD", "AALOAD",
"BALOAD", "CALOAD", "SALOAD", "ISTORE", "LSTORE", "FSTORE",
"DSTORE", "ASTORE", "IASTORE", "LASTORE", "FASTORE", "DASTORE",
"AASTORE", "BASTORE", "CASTORE", "SASTORE", "POP", "POP2", "DUP",
"DUP_X1", "DUP_X2", "DUP2", "DUP2_X1", "DUP2_X2", "SWAP", "IADD",
"LADD", "FADD", "DADD", "ISUB", "LSUB", "FSUB", "DSUB", "IMUL",
"LMUL", "FMUL", "DMUL", "IDIV", "LDIV", "FDIV", "DDIV", "IREM",
"LREM", "FREM", "DREM", "INEG", "LNEG", "FNEG", "DNEG", "ISHL",
"LSHL", "ISHR", "LSHR", "IUSHR", "LUSHR", "IAND", "LAND", "IOR",
"LOR", "IXOR", "LXOR", "IINC", "I2L", "I2F", "I2D", "L2I", "L2F",
"L2D", "F2I", "F2L", "F2D", "D2I", "D2L", "D2F", "I2B", "I2C",
"I2S", "LCMP", "FCMPL", "FCMPG", "DCMPL", "DCMPG", "IFEQ", "IFNE",
"IFLT", "IFGE", "IFGT", "IFLE", "IF_ICMPEQ", "IF_ICMPNE",
"IF_ICMPLT", "IF_ICMPGE", "IF_ICMPGT", "IF_ICMPLE", "IF_ACMPEQ",
"IF_ACMPNE", "GOTO", "JSR", "RET", "TABLESWITCH", "LOOKUPSWITCH",
"IRETURN", "LRETURN", "FRETURN", "DRETURN", "ARETURN", "RETURN",
"GETSTATIC", "PUTSTATIC", "GETFIELD", "PUTFIELD", "INVOKEVIRTUAL",
"INVOKESPECIAL", "INVOKESTATIC", "INVOKEINTERFACE",
"INVOKEDYNAMIC", "NEW", "NEWARRAY", "ANEWARRAY", "ARRAYLENGTH",
"ATHROW", "CHECKCAST", "INSTANCEOF", "MONITORENTER", "MONITOREXIT",
"MULTIANEWARRAY", "IFNULL", "IFNONNULL" };
private static String opcodesAnys = buildRegexItems(opcodesAny, false, false);
private static String buildRegexItems(final String[] items, final boolean capture, final boolean stdRepl) {
if (items.length == 0)
return "()";
String result = (stdRepl ? "\\b" : "") + "(" + (capture ? "" : "?:") + items[0];
for (int i = 1; i < items.length; i++) {
result += "|" + items[i];
}
result += ")";
return result;
}
private static String buildRegexItems(final String[] items) {
return buildRegexItems(items, true, true);
}
private static String processRegex(final String regex) {
String result = regex.trim();
result = result.replaceAll("\\bANYINSN *", opcodesAnys);
result = result.replaceAll(opcodesInts + "\\\\\\{\\s*(\\d+)\\s*\\\\\\} *",
"$1\\\\{$2\\\\} ");
result = result.replaceAll(opcodesInts + " *", "$1\\\\{\\\\d+\\\\} ");
result = result.replaceAll("\\bLDC\\\\\\{(.*?)\\\\\\}(?<!\\\\\\\\\\}) *",
"LDC\\\\{$1\\\\}(?<!\\\\\\\\\\\\}) ");
result = result.replaceAll("\\bLDC *", "LDC\\\\{.*?\\\\}(?<!\\\\\\\\\\\\}) ");
result = result.replaceAll(opcodeVars + "(_\\d+) *", "$1$2 ");
result = result.replaceAll(opcodeVars + "(?!_) *", "$1_\\\\d+ ");
result = result.replaceAll("\\bIINC\\\\\\{\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\\\\\} *",
"IINC\\\\{$1,$2\\\\} ");
result = result.replaceAll("\\bIINC\\\\\\{\\s*(\\d+)\\s*\\\\\\} *",
"IINC\\\\{\\d+,$1\\\\} ");
result = result.replaceAll("\\bIINC *", "IINC\\\\{\\d+,\\d+\\\\} ");
result = result.replaceAll(opcodesFields +
"\\\\\\{\\s*(.*?)\\s*,\\s*(.*?)\\s*,\\s*(.*?)\\s*\\\\\\} *",
"$1\\\\{$2,$3,$4\\\\} ");
result = result.replaceAll(opcodesFields
+ "\\\\\\{((?:.(?!,))*)\\\\\\} *", "$1\\\\{$2,.*?,.*?\\\\} ");
result = result.replaceAll(opcodesFields + " *", "$1\\\\{.*?\\\\} ");
result = result.replaceAll(opcodesMethods
+ "\\\\\\{\\s*(.*?)\\s*,\\s*(.*?)\\s*,\\s*(.*?)\\s*\\\\\\} *",
"$1\\\\{$2,$3,$4\\\\} ");
result = result.replaceAll(opcodesMethods
+ "\\\\\\{((?:.(?!,))*)\\\\\\} *", "$1\\\\{$2,.*?,.*?\\\\} ");
result = result.replaceAll(opcodesMethods + " *",
"$1\\\\{.*?,.*?,.*?\\\\} ");
result = result.replaceAll(opcodesTypes + "\\\\\\{\\s*(.*?)\\s*\\\\\\} +",
"$1\\\\{$2\\\\} ");
result = result.replaceAll(opcodesTypes + " +", "$1\\\\{\\\\.*?\\\\} ");
result = result.replaceAll("\\bMULTIANEWARRAY\\\\\\{\\s*(\\d+)\\s*,\\s*(.*?)\\s*\\\\\\} *",
"MULTIANEWARRAY\\\\{$1,$2\\\\} ");
result = result.replaceAll("\\bMULTIANEWARRAY\\\\\\{\\s*(.*?)\\s*\\\\\\} *",
"MULTIANEWARRAY\\\\{\\d+,$1\\\\} ");
result = result.replaceAll("\\bMULTIANEWARRAY *", "MULTIANEWARRAY\\\\{\\\\\\d+,.*?\\\\} ");
result = result.replaceAll("\\bIFINSN *", opcodesIfs + " ");
return result;
}
private MethodNode mn;
private AbstractInsnNode[] origInstructions;
private AbstractInsnNode[] instructions;
private int[] offsets;
private String insnString;
public RegexInsnFinder (final ClassNode clazz, final MethodNode method) {
setMethod(clazz, method);
}
@SuppressWarnings("unchecked")
private AbstractInsnNode[] cleanInsn(final InsnList insnList) {
final List<AbstractInsnNode> il = new ArrayList<AbstractInsnNode>();
final Iterator<AbstractInsnNode> iIt = insnList.iterator();
while (iIt.hasNext()) {
final AbstractInsnNode node = iIt.next();
if (node.getOpcode() >= 0) {
il.add(node);
}
}
return il.toArray(new AbstractInsnNode[il.size()]);
}
/**
* Refreshes the internal instruction list when you have made changes to the method.
*/
public void refresh() {
origInstructions = cleanInsn(mn.instructions);
final List<AbstractInsnNode> il = new ArrayList<AbstractInsnNode>();
for (final AbstractInsnNode ain : mn.instructions.toArray())
if (ain.getOpcode() >= 0) {
il.add(ain);
}
instructions = il.toArray(new AbstractInsnNode[il.size()]);
offsets = new int[instructions.length];
insnString = "";
for (int i = 0; i < instructions.length; i++) {
offsets[i] = -1;
final AbstractInsnNode ain = instructions[i];
if (ain.getOpcode() >= 0) {
if (ain.getOpcode() >= opcodes.length) {
try {
throw new UnexpectedException(
"Unknown opcode encountered: "
+ ain.getOpcode());
} catch (final UnexpectedException e) {
e.printStackTrace();
}
}
offsets[i] = insnString.length();
insnString += opcodes[ain.getOpcode()];
switch (ain.getType()) {
case AbstractInsnNode.INT_INSN:
final IntInsnNode iin = (IntInsnNode) ain;
insnString += "{" + iin.operand + "}";
break;
case AbstractInsnNode.LDC_INSN:
final LdcInsnNode lin = (LdcInsnNode) ain;
insnString += "{" + lin.cst.toString().replace("}", "\\}")
+ "}";
break;
case AbstractInsnNode.VAR_INSN:
final VarInsnNode vin = (VarInsnNode) ain;
insnString += "_" + vin.var;
break;
case AbstractInsnNode.IINC_INSN:
final IincInsnNode iiin = (IincInsnNode) ain;
insnString += "{" + iiin.var + "," + iiin.incr + "}";
break;
case AbstractInsnNode.FIELD_INSN:
final FieldInsnNode fin = (FieldInsnNode) ain;
insnString += "{" + fin.desc + "," + fin.owner + ","
+ fin.name + "}";
break;
case AbstractInsnNode.METHOD_INSN:
final MethodInsnNode min = (MethodInsnNode) ain;
insnString += "{" + min.desc + "," + min.owner + ","
+ min.name + "}";
break;
case AbstractInsnNode.TYPE_INSN:
final TypeInsnNode tin = (TypeInsnNode) ain;
insnString += "{" + tin.desc + "}";
break;
case AbstractInsnNode.MULTIANEWARRAY_INSN:
final MultiANewArrayInsnNode manain = (MultiANewArrayInsnNode) ain;
insnString += "{" + manain.dims + "," + manain.desc + "}";
break;
}
insnString += " ";
}
}
}
public void setMethod(final ClassNode ci, final MethodNode mi) {
this.mn = mi;
refresh();
}
private AbstractInsnNode[] makeResult(final int start, final int end) {
int startIndex = 0;
int endIndex = -1;
for (int i = 0; i < offsets.length - 1; i++) {
final int offset = offsets[i];
if (offset == start) {
startIndex = i;
}
if ((offset < end) && (offsets[i + 1] >= end)) {
endIndex = i;
break;
}
}
if (endIndex == -1) {
endIndex = offsets.length - 1;
}
final int length = endIndex - startIndex + 1;
final AbstractInsnNode[] result = new AbstractInsnNode[length];
System.arraycopy(origInstructions, startIndex, result, 0, length);
return result;
}
/**
* Searches for a regex in the instruction list and returns the first match.
* @param regex the regular expression
* @return the matching instructions
*/
public AbstractInsnNode[] find(final String regex) {
try {
final Matcher regexMatcher = Pattern.compile(processRegex(regex),
Pattern.MULTILINE).matcher(insnString);
if (regexMatcher.find())
return makeResult(regexMatcher.start(), regexMatcher.end());
} catch (final PatternSyntaxException ex) {
ex.printStackTrace();
}
return new AbstractInsnNode[0];
}
/**
* Searches a regex in an instruction list and returns all matches.
* @param regex the regular expression
* @return a list with all sets of matching instructions
*/
public List<AbstractInsnNode[]> findAll(final String regex) {
final List<AbstractInsnNode[]> results = new ArrayList<AbstractInsnNode[]>();
try {
final Matcher regexMatcher = Pattern.compile(processRegex(regex),
Pattern.MULTILINE).matcher(insnString);
while (regexMatcher.find()) {
results.add(makeResult(regexMatcher.start(), regexMatcher.end()));
}
} catch (final PatternSyntaxException ex) {
ex.printStackTrace();
}
return results;
}
/**
* Searches for a regex in the instruction list and returns all groups for the first match.
* @param regex the regular expression
* @return the groups with matching instructions
*/
public AbstractInsnNode[][] findGroups(final String regex) {
try {
final Matcher regexMatcher = Pattern.compile(processRegex(regex),
Pattern.MULTILINE).matcher(insnString);
if (regexMatcher.find()) {
final AbstractInsnNode[][] result = new AbstractInsnNode[regexMatcher.groupCount() + 1][0];
for (int i = 0; i <= regexMatcher.groupCount(); i++) {
result[i] = makeResult(regexMatcher.start(i), regexMatcher.end(i));
}
return result;
}
} catch (final PatternSyntaxException ex) {
ex.printStackTrace();
}
return new AbstractInsnNode[0][0];
}
/**
* Searches for a regex in the instruction list and returns all groups for all matches.
* @param regex the regular expression
* @return a list with all sets of groups with matching instructions
*/
public List<AbstractInsnNode[][]> findAllGroups(final String regex) {
final List<AbstractInsnNode[][]> results = new ArrayList<AbstractInsnNode[][]>();
try {
final Matcher regexMatcher = Pattern.compile(processRegex(regex),
Pattern.MULTILINE).matcher(insnString);
if (regexMatcher.find()) {
final AbstractInsnNode[][] result = new AbstractInsnNode[regexMatcher.groupCount() + 1][0];
for (int i = 0; i <= regexMatcher.groupCount(); i++) {
result[i] = makeResult(regexMatcher.start(i), regexMatcher.end(i));
}
results.add(result);
}
} catch (final PatternSyntaxException ex) {
ex.printStackTrace();
}
return results;
}
}

View file

@ -0,0 +1,61 @@
package the.bytecode.club.bytecodeviewer.searching;
import java.awt.GridLayout;
import java.util.Iterator;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/**
* Regex Searching
*
* @author WaterWolf
*
*/
public class RegexSearch implements SearchTypeDetails {
JTextField searchText = new JTextField("");
JPanel myPanel = null;
private static RegexInsnFinder regexFinder;
@Override
public JPanel getPanel() {
if (myPanel == null) {
myPanel = new JPanel(new GridLayout(1, 2));
myPanel.add(new JLabel("Search Regex: "));
myPanel.add(searchText);
}
return myPanel;
}
@SuppressWarnings("unchecked")
@Override
public void search(final ClassNode node, final SearchResultNotifier srn, boolean exact) {
final Iterator<MethodNode> methods = node.methods.iterator();
final String srchText = searchText.getText();
if(srchText.isEmpty())
return;
while (methods.hasNext()) {
final MethodNode method = methods.next();
if (regexFinder == null) {
regexFinder = new RegexInsnFinder(node, method);
}
else {
regexFinder.setMethod(node, method);
}
if (regexFinder.find(srchText).length > 0) {
srn.notifyOfResult(node, method, null);
}
}
}
}

View file

@ -0,0 +1,17 @@
package the.bytecode.club.bytecodeviewer.searching;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/**
* Used to update the search pane that there's been a result found.
*
* @author WaterWolf
*
*/
public interface SearchResultNotifier {
public void notifyOfResult(ClassNode clazz, MethodNode method,
AbstractInsnNode insn);
}

View file

@ -0,0 +1,18 @@
package the.bytecode.club.bytecodeviewer.searching;
import javax.swing.JPanel;
import org.objectweb.asm.tree.ClassNode;
/**
* Search type details
*
* @author WaterWolf
*
*/
public interface SearchTypeDetails {
public JPanel getPanel();
public void search(ClassNode node, SearchResultNotifier srn, boolean exact);
}

View file

@ -0,0 +1,489 @@
package the.bytecode.club.bytecodeviewer.searching.commons;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.IntInsnNode;
/**
*
* Class containing bytecode search conditions and couple of helper methods to make them
*
* @author Waterwolf
*
**/
public class AnalyzerFactory implements Opcodes {
public static InsnAnalyzer makeOpcodeCond(final int opcode) {
return new InsnAnalyzer() {
@Override
public boolean accept(final AbstractInsnNode node) {
return node.getOpcode() == opcode;
}
};
}
public static InsnAnalyzer makeFieldCond(final int opcode,
final String type) {
return new InsnAnalyzer() {
@Override
public boolean accept(final AbstractInsnNode node) {
if (node instanceof FieldInsnNode)
return node.getOpcode() == opcode && ((FieldInsnNode) node).desc.equals(type);
else
return false;
}
};
}
public static InsnAnalyzer makeFieldOwnerCond(final int opcode,
final String owner) {
return new InsnAnalyzer() {
@Override
public boolean accept(final AbstractInsnNode node) {
if (node instanceof FieldInsnNode)
return node.getOpcode() == opcode && ((FieldInsnNode) node).owner.equals(owner);
else
return false;
}
};
}
public static InsnAnalyzer makeFieldRegexCond(final int opcode,
final String regex) {
return new InsnAnalyzer() {
@Override
public boolean accept(final AbstractInsnNode node) {
if (node instanceof FieldInsnNode)
return node.getOpcode() == opcode && ((FieldInsnNode) node).desc.matches(regex);
else
return false;
}
};
}
public static InsnAnalyzer makeIntCond(final int opcode,
final int value) {
return new InsnAnalyzer() {
@Override
public boolean accept(final AbstractInsnNode node) {
if (node instanceof IntInsnNode)
return node.getOpcode() == opcode && ((IntInsnNode) node).operand == value;
else
return false;
}
};
}
/**
* An instruction condition for a GETFIELD instruction with signature Z.
*/
public final static InsnAnalyzer GETFIELD_Z =
makeFieldCond(GETFIELD, "Z");
/**
* An instruction condition for a PUTFIELD instruction with signature Z.
*/
public final static InsnAnalyzer PUTFIELD_Z =
makeFieldCond(PUTFIELD, "Z");
/**
* An instruction condition for a GETSTATIC instruction with signature Z.
*/
public final static InsnAnalyzer GETSTATIC_Z =
makeFieldCond(GETSTATIC, "Z");
/**
* An instruction condition for a PUTSTATIC instruction with signature Z.
*/
public final static InsnAnalyzer PUTSTATIC_Z =
makeFieldCond(PUTSTATIC, "Z");
/**
* An instruction condition for a GETFIELD instruction with signature [Z.
*/
public final static InsnAnalyzer GETFIELD_ZA =
makeFieldCond(GETFIELD, "[Z");
/**
* An instruction condition for a PUTFIELD instruction with signature [Z.
*/
public final static InsnAnalyzer PUTFIELD_ZA =
makeFieldCond(PUTFIELD, "[Z");
/**
* An instruction condition for a GETSTATIC instruction with signature [Z.
*/
public final static InsnAnalyzer GETSTATIC_ZA =
makeFieldCond(GETSTATIC, "[Z");
/**
* An instruction condition for a PUTSTATIC instruction with signature [Z.
*/
public final static InsnAnalyzer PUTSTATIC_ZA =
makeFieldCond(PUTSTATIC, "[Z");
/**
* An instruction condition for a GETFIELD instruction with signature [[Z.
*/
public final static InsnAnalyzer GETFIELD_ZAA =
makeFieldCond(GETFIELD, "[[Z");
/**
* An instruction condition for a PUTFIELD instruction with signature [[Z.
*/
public final static InsnAnalyzer PUTFIELD_ZAA =
makeFieldCond(PUTFIELD, "[[Z");
/**
* An instruction condition for a GETSTATIC instruction with signature [[Z.
*/
public final static InsnAnalyzer GETSTATIC_ZAA =
makeFieldCond(GETSTATIC, "[[Z");
/**
* An instruction condition for a PUTSTATIC instruction with signature [[Z.
*/
public final static InsnAnalyzer PUTSTATIC_ZAA =
makeFieldCond(PUTSTATIC, "[[Z");
/**
* An instruction condition for a GETFIELD instruction with signature B.
*/
public final static InsnAnalyzer GETFIELD_B =
makeFieldCond(GETFIELD, "B");
/**
* An instruction condition for a PUTFIELD instruction with signature B.
*/
public final static InsnAnalyzer PUTFIELD_B =
makeFieldCond(PUTFIELD, "B");
/**
* An instruction condition for a GETSTATIC instruction with signature B.
*/
public final static InsnAnalyzer GETSTATIC_B =
makeFieldCond(GETSTATIC, "B");
/**
* An instruction condition for a PUTSTATIC instruction with signature B.
*/
public final static InsnAnalyzer PUTSTATIC_B =
makeFieldCond(PUTSTATIC, "B");
/**
* An instruction condition for a GETFIELD instruction with signature [B.
*/
public final static InsnAnalyzer GETFIELD_BA =
makeFieldCond(GETFIELD, "[B");
/**
* An instruction condition for a PUTFIELD instruction with signature [B.
*/
public final static InsnAnalyzer PUTFIELD_BA =
makeFieldCond(PUTFIELD, "[B");
/**
* An instruction condition for a GETSTATIC instruction with signature [B.
*/
public final static InsnAnalyzer GETSTATIC_BA =
makeFieldCond(GETSTATIC, "[B");
/**
* An instruction condition for a PUTSTATIC instruction with signature [B.
*/
public final static InsnAnalyzer PUTSTATIC_BA =
makeFieldCond(PUTSTATIC, "[B");
/**
* An instruction condition for a GETFIELD instruction with signature [[B.
*/
public final static InsnAnalyzer GETFIELD_BAA =
makeFieldCond(GETFIELD, "[[B");
/**
* An instruction condition for a PUTFIELD instruction with signature [[B.
*/
public final static InsnAnalyzer PUTFIELD_BAA =
makeFieldCond(PUTFIELD, "[[B");
/**
* An instruction condition for a GETSTATIC instruction with signature [[B.
*/
public final static InsnAnalyzer GETSTATIC_BAA =
makeFieldCond(GETSTATIC, "[[B");
/**
* An instruction condition for a PUTSTATIC instruction with signature [[B.
*/
public final static InsnAnalyzer PUTSTATIC_BAA =
makeFieldCond(PUTSTATIC, "[[B");
/**
* An instruction condition for a GETFIELD instruction with signature C.
*/
public final static InsnAnalyzer GETFIELD_C =
makeFieldCond(GETFIELD, "C");
/**
* An instruction condition for a PUTFIELD instruction with signature C.
*/
public final static InsnAnalyzer PUTFIELD_C =
makeFieldCond(PUTFIELD, "C");
/**
* An instruction condition for a GETSTATIC instruction with signature C.
*/
public final static InsnAnalyzer GETSTATIC_C =
makeFieldCond(GETSTATIC, "C");
/**
* An instruction condition for a PUTSTATIC instruction with signature C.
*/
public final static InsnAnalyzer PUTSTATIC_C =
makeFieldCond(PUTSTATIC, "C");
/**
* An instruction condition for a GETFIELD instruction with signature [C.
*/
public final static InsnAnalyzer GETFIELD_CA =
makeFieldCond(GETFIELD, "[C");
/**
* An instruction condition for a PUTFIELD instruction with signature [C.
*/
public final static InsnAnalyzer PUTFIELD_CA =
makeFieldCond(PUTFIELD, "[C");
/**
* An instruction condition for a GETSTATIC instruction with signature [C.
*/
public final static InsnAnalyzer GETSTATIC_CA =
makeFieldCond(GETSTATIC, "[C");
/**
* An instruction condition for a PUTSTATIC instruction with signature [C.
*/
public final static InsnAnalyzer PUTSTATIC_CA =
makeFieldCond(PUTSTATIC, "[C");
/**
* An instruction condition for a GETFIELD instruction with signature [[C.
*/
public final static InsnAnalyzer GETFIELD_CAA =
makeFieldCond(GETFIELD, "[[C");
/**
* An instruction condition for a PUTFIELD instruction with signature [[C.
*/
public final static InsnAnalyzer PUTFIELD_CAA =
makeFieldCond(PUTFIELD, "[[C");
/**
* An instruction condition for a GETSTATIC instruction with signature [[C.
*/
public final static InsnAnalyzer GETSTATIC_CAA =
makeFieldCond(GETSTATIC, "[[C");
/**
* An instruction condition for a PUTSTATIC instruction with signature [[C.
*/
public final static InsnAnalyzer PUTSTATIC_CAA =
makeFieldCond(PUTSTATIC, "[[C");
/**
* An instruction condition for a GETFIELD instruction with signature S.
*/
public final static InsnAnalyzer GETFIELD_S =
makeFieldCond(GETFIELD, "S");
/**
* An instruction condition for a PUTFIELD instruction with signature S.
*/
public final static InsnAnalyzer PUTFIELD_S =
makeFieldCond(PUTFIELD, "S");
/**
* An instruction condition for a GETSTATIC instruction with signature S.
*/
public final static InsnAnalyzer GETSTATIC_S =
makeFieldCond(GETSTATIC, "S");
/**
* An instruction condition for a PUTSTATIC instruction with signature S.
*/
public final static InsnAnalyzer PUTSTATIC_S =
makeFieldCond(PUTSTATIC, "S");
/**
* An instruction condition for a GETFIELD instruction with signature [S.
*/
public final static InsnAnalyzer GETFIELD_SA =
makeFieldCond(GETFIELD, "[S");
/**
* An instruction condition for a PUTFIELD instruction with signature [S.
*/
public final static InsnAnalyzer PUTFIELD_SA =
makeFieldCond(PUTFIELD, "[S");
/**
* An instruction condition for a GETSTATIC instruction with signature [S.
*/
public final static InsnAnalyzer GETSTATIC_SA =
makeFieldCond(GETSTATIC, "[S");
/**
* An instruction condition for a PUTSTATIC instruction with signature [S.
*/
public final static InsnAnalyzer PUTSTATIC_SA =
makeFieldCond(PUTSTATIC, "[S");
/**
* An instruction condition for a GETFIELD instruction with signature [[S.
*/
public final static InsnAnalyzer GETFIELD_SAA =
makeFieldCond(GETFIELD, "[[S");
/**
* An instruction condition for a PUTFIELD instruction with signature [[S.
*/
public final static InsnAnalyzer PUTFIELD_SAA =
makeFieldCond(PUTFIELD, "[[S");
/**
* An instruction condition for a GETSTATIC instruction with signature [[S.
*/
public final static InsnAnalyzer GETSTATIC_SAA =
makeFieldCond(GETSTATIC, "[[S");
/**
* An instruction condition for a PUTSTATIC instruction with signature [[S.
*/
public final static InsnAnalyzer PUTSTATIC_SAA =
makeFieldCond(PUTSTATIC, "[[S");
public final static InsnAnalyzer GETFIELD_I =
makeFieldCond(GETFIELD, "I");
public final static InsnAnalyzer PUTFIELD_I =
makeFieldCond(PUTFIELD, "I");
public final static InsnAnalyzer GETSTATIC_I =
makeFieldCond(GETSTATIC, "I");
public final static InsnAnalyzer PUTSTATIC_I =
makeFieldCond(PUTSTATIC, "I");
public final static InsnAnalyzer GETFIELD_IA =
makeFieldCond(GETFIELD, "[I");
public final static InsnAnalyzer PUTFIELD_IA =
makeFieldCond(PUTFIELD, "[I");
public final static InsnAnalyzer GETSTATIC_IA =
makeFieldCond(GETSTATIC, "[I");
public final static InsnAnalyzer PUTSTATIC_IA =
makeFieldCond(PUTSTATIC, "[I");
public final static InsnAnalyzer GETFIELD_IAA =
makeFieldCond(GETFIELD, "[[I");
public final static InsnAnalyzer PUTFIELD_IAA =
makeFieldCond(PUTFIELD, "[[I");
public final static InsnAnalyzer GETSTATIC_IAA =
makeFieldCond(GETSTATIC, "[[I");
public final static InsnAnalyzer PUTSTATIC_IAA =
makeFieldCond(PUTSTATIC, "[[I");
public final static InsnAnalyzer GETFIELD_J =
makeFieldCond(GETFIELD, "J");
public final static InsnAnalyzer PUTFIELD_J =
makeFieldCond(PUTFIELD, "J");
public final static InsnAnalyzer GETSTATIC_J =
makeFieldCond(GETSTATIC, "J");
public final static InsnAnalyzer PUTSTATIC_J =
makeFieldCond(PUTSTATIC, "J");
public final static InsnAnalyzer GETFIELD_JA =
makeFieldCond(GETFIELD, "[J");
public final static InsnAnalyzer PUTFIELD_JA =
makeFieldCond(PUTFIELD, "[J");
public final static InsnAnalyzer GETSTATIC_JA =
makeFieldCond(GETSTATIC, "[J");
public final static InsnAnalyzer PUTSTATIC_JA =
makeFieldCond(PUTSTATIC, "[J");
public final static InsnAnalyzer GETFIELD_JAA =
makeFieldCond(GETFIELD, "[[J");
public final static InsnAnalyzer PUTFIELD_JAA =
makeFieldCond(PUTFIELD, "[[J");
public final static InsnAnalyzer GETSTATIC_JAA =
makeFieldCond(GETSTATIC, "[[J");
public final static InsnAnalyzer PUTSTATIC_JAA =
makeFieldCond(PUTSTATIC, "[[J");
public final static InsnAnalyzer GETFIELD_F =
makeFieldCond(GETFIELD, "F");
public final static InsnAnalyzer PUTFIELD_F =
makeFieldCond(PUTFIELD, "F");
public final static InsnAnalyzer GETSTATIC_F =
makeFieldCond(GETSTATIC, "F");
public final static InsnAnalyzer PUTSTATIC_F =
makeFieldCond(PUTSTATIC, "F");
public final static InsnAnalyzer GETFIELD_FA =
makeFieldCond(GETFIELD, "[F");
public final static InsnAnalyzer PUTFIELD_FA =
makeFieldCond(PUTFIELD, "[F");
public final static InsnAnalyzer GETSTATIC_FA =
makeFieldCond(GETSTATIC, "[F");
public final static InsnAnalyzer PUTSTATIC_FA =
makeFieldCond(PUTSTATIC, "[F");
public final static InsnAnalyzer GETFIELD_FAA =
makeFieldCond(GETFIELD, "[[F");
public final static InsnAnalyzer PUTFIELD_FAA =
makeFieldCond(PUTFIELD, "[[F");
public final static InsnAnalyzer GETSTATIC_FAA =
makeFieldCond(GETSTATIC, "[[F");
public final static InsnAnalyzer PUTSTATIC_FAA =
makeFieldCond(PUTSTATIC, "[[F");
public final static InsnAnalyzer GETFIELD_D =
makeFieldCond(GETFIELD, "D");
public final static InsnAnalyzer PUTFIELD_D =
makeFieldCond(PUTFIELD, "D");
public final static InsnAnalyzer GETSTATIC_D =
makeFieldCond(GETSTATIC, "D");
public final static InsnAnalyzer PUTSTATIC_D =
makeFieldCond(PUTSTATIC, "D");
public final static InsnAnalyzer GETFIELD_DA =
makeFieldCond(GETFIELD, "[D");
public final static InsnAnalyzer PUTFIELD_DA =
makeFieldCond(PUTFIELD, "[D");
public final static InsnAnalyzer GETSTATIC_DA =
makeFieldCond(GETSTATIC, "[D");
public final static InsnAnalyzer PUTSTATIC_DA =
makeFieldCond(PUTSTATIC, "[D");
public final static InsnAnalyzer GETFIELD_DAA =
makeFieldCond(GETFIELD, "[[D");
public final static InsnAnalyzer PUTFIELD_DAA =
makeFieldCond(PUTFIELD, "[[D");
public final static InsnAnalyzer GETSTATIC_DAA =
makeFieldCond(GETSTATIC, "[[D");
public final static InsnAnalyzer PUTSTATIC_DAA =
makeFieldCond(PUTSTATIC, "[[D");
public final static InsnAnalyzer GETFIELD_L =
makeFieldRegexCond(GETFIELD, "L.*;");
public final static InsnAnalyzer PUTFIELD_L =
makeFieldRegexCond(PUTFIELD, "L.*;");
public final static InsnAnalyzer GETSTATIC_L =
makeFieldRegexCond(GETSTATIC, "L.*;");
public final static InsnAnalyzer PUTSTATIC_L =
makeFieldRegexCond(PUTSTATIC, "L.*;");
public final static InsnAnalyzer GETFIELD_LA =
makeFieldRegexCond(GETFIELD, "\\[L.*;");
public final static InsnAnalyzer PUTFIELD_LA =
makeFieldRegexCond(PUTFIELD, "\\[L.*;");
public final static InsnAnalyzer GETSTATIC_LA =
makeFieldRegexCond(GETSTATIC, "\\[L.*;");
public final static InsnAnalyzer PUTSTATIC_LA =
makeFieldRegexCond(PUTSTATIC, "\\[L.*;");
public final static InsnAnalyzer GETFIELD_LAA =
makeFieldRegexCond(GETFIELD, "\\[\\[L.*;");
public final static InsnAnalyzer PUTFIELD_LAA =
makeFieldRegexCond(PUTFIELD, "\\[\\[L.*;");
public final static InsnAnalyzer GETSTATIC_LAA =
makeFieldRegexCond(GETSTATIC, "\\[\\[L.*;");
public final static InsnAnalyzer PUTSTATIC_LAA =
makeFieldRegexCond(PUTSTATIC, "\\[\\[L.*;");
public final static InsnAnalyzer GETFIELD_String =
makeFieldCond(GETFIELD, "Ljava/lang/String;");
public final static InsnAnalyzer PUTFIELD_String =
makeFieldCond(PUTFIELD, "Ljava/lang/String;");
public final static InsnAnalyzer GETSTATIC_String =
makeFieldCond(GETSTATIC, "Ljava/lang/String;");
public final static InsnAnalyzer PUTSTATIC_String =
makeFieldCond(PUTSTATIC, "Ljava/lang/String;");
public final static InsnAnalyzer GETFIELD_StringA =
makeFieldCond(GETFIELD, "[Ljava/lang/String;");
public final static InsnAnalyzer PUTFIELD_StringA =
makeFieldCond(PUTFIELD, "[Ljava/lang/String;");
public final static InsnAnalyzer GETSTATIC_StringA =
makeFieldCond(GETSTATIC, "[Ljava/lang/String;");
public final static InsnAnalyzer PUTSTATIC_StringA =
makeFieldCond(PUTSTATIC, "[Ljava/lang/String;");
public final static InsnAnalyzer GETFIELD_StringAA =
makeFieldCond(GETFIELD, "[[Ljava/lang/String;");
public final static InsnAnalyzer PUTFIELD_StringAA =
makeFieldCond(PUTFIELD, "[[Ljava/lang/String;");
public final static InsnAnalyzer GETSTATIC_StringAA =
makeFieldCond(GETSTATIC, "[[Ljava/lang/String;");
public final static InsnAnalyzer PUTSTATIC_StringAA =
makeFieldCond(PUTSTATIC, "[[Ljava/lang/String;");
}

View file

@ -0,0 +1,14 @@
package the.bytecode.club.bytecodeviewer.searching.commons;
import org.objectweb.asm.tree.AbstractInsnNode;
/**
*
* Bytecode instruction search baseclass
*
* @author Waterwolf
*
*/
public interface InsnAnalyzer {
public boolean accept(AbstractInsnNode node);
}

View file

@ -0,0 +1,171 @@
package the.bytecode.club.bytecodeviewer.searching.commons;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
/**
* Class that searches bytecode instructions using given search conditions.
*
* @author WaterWolf
* @author Matthew Bovard
*
*/
public class InstructionSearcher {
private final InsnList list;
private AbstractInsnNode current;
public InstructionSearcher(final MethodNode m) {
this.list = m.instructions;
this.current = list.getFirst();
}
public AbstractInsnNode getCurrent() {
return current;
}
public void setCurrent(final AbstractInsnNode in) {
current = in;
}
public AbstractInsnNode getNext(final int opcode) {
return getNext(AnalyzerFactory.makeOpcodeCond(opcode));
}
public AbstractInsnNode getNext(final InsnAnalyzer analyzer) {
while (current != null) {
if (analyzer.accept(current)) {
final AbstractInsnNode old = current;
current = current.getNext();
return old;
}
current = current.getNext();
}
return null;
}
public AbstractInsnNode getNext() {
if (current == null)
return null;
current = current.getNext();
while (current != null && current.getOpcode() == -1) {
current = current.getNext();
}
return current;
}
public AbstractInsnNode getPrevious(final InsnAnalyzer analyzer) {
while (current != null) {
if (analyzer.accept(current)) {
final AbstractInsnNode old = current;
current = current.getPrevious();
return old;
}
current = current.getPrevious();
}
return null;
}
public AbstractInsnNode getPrevious(final int opcode) {
return getPrevious(AnalyzerFactory.makeOpcodeCond(opcode));
}
public AbstractInsnNode getPrevious() {
current = current.getPrevious();
while (current.getOpcode() == -1) {
current = current.getPrevious();
}
return current;
}
public LdcInsnNode getNextLDC(final Object cst) {
AbstractInsnNode in;
while ((in = getNext(Opcodes.LDC)) != null) {
final LdcInsnNode ln = (LdcInsnNode) in;
if (ln.cst.equals(cst)) return ln;
}
return null;
}
public LdcInsnNode getPreviousLDC(final Object cst) {
AbstractInsnNode in;
while ((in = getPrevious(Opcodes.LDC)) != null) {
final LdcInsnNode ln = (LdcInsnNode) in;
if (ln.cst.equals(cst))
return ln;
}
return null;
}
public IntInsnNode getNextInt(final int opcode, final int i) {
return (IntInsnNode) this.getNext(AnalyzerFactory.makeIntCond(opcode, i));
}
public IntInsnNode getPreviousInt(final int opcode, final int i) {
return (IntInsnNode) this.getPrevious(AnalyzerFactory.makeIntCond(opcode, i));
}
/**
* @param opcode One of Opcodes.BIPUSH/Opcodes.SIPUSH
* @param value Value to look for
* @return
*/
public IntInsnNode getNextPush(final int opcode, final int value) {
AbstractInsnNode in;
while ((in = getNext(opcode)) != null) {
final IntInsnNode iin = (IntInsnNode) in;
if (iin.operand == value) return iin;
}
return null;
}
public List<AbstractInsnNode> analyze(final int opcode) {
reset();
final List<AbstractInsnNode> list = new ArrayList<AbstractInsnNode>();
AbstractInsnNode in;
while ((in = getNext(opcode)) != null) {
list.add(in);
}
return list;
}
public int getIndex() {
return list.indexOf(current);
}
public void setIndex(final int index) {
current = list.get(index);
}
/**
* Resets us back to the first instruction
*/
public void reset() {
current = list.getFirst();
}
public void resetToEnd() {
current = list.getLast();
}
public void insert(final AbstractInsnNode location, final AbstractInsnNode insn) {
this.list.insert(location, insn);
}
public int computePosition(final AbstractInsnNode node) {
AbstractInsnNode poller = list.getFirst();
int index = 0;
while ((poller = poller.getNext()) != null) {
if (poller.equals(node))
return index;
index++;
}
return -1;
}
}