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:
parent
1f33f0a8cf
commit
06ef9408a4
60 changed files with 7031 additions and 3 deletions
BIN
BytecodeViewer.jar
Normal file
BIN
BytecodeViewer.jar
Normal file
Binary file not shown.
21
README.md
21
README.md
|
@ -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
BIN
libs/asm-all-5.0.3.jar
Normal file
Binary file not shown.
BIN
libs/commons-codec-1.9.jar
Normal file
BIN
libs/commons-codec-1.9.jar
Normal file
Binary file not shown.
BIN
libs/commons-io-2.4.jar
Normal file
BIN
libs/commons-io-2.4.jar
Normal file
Binary file not shown.
BIN
libs/commons-lang3-3.3.2.jar
Normal file
BIN
libs/commons-lang3-3.3.2.jar
Normal file
Binary file not shown.
BIN
libs/fernflower_no_output.jar
Normal file
BIN
libs/fernflower_no_output.jar
Normal file
Binary file not shown.
BIN
libs/groovy-all-2.1.7.jar
Normal file
BIN
libs/groovy-all-2.1.7.jar
Normal file
Binary file not shown.
BIN
libs/jruby.jar
Normal file
BIN
libs/jruby.jar
Normal file
Binary file not shown.
BIN
libs/jsyntaxpane-0.9.5-b29 (1).jar
Normal file
BIN
libs/jsyntaxpane-0.9.5-b29 (1).jar
Normal file
Binary file not shown.
BIN
libs/jython-standalone-2.5.3.jar
Normal file
BIN
libs/jython-standalone-2.5.3.jar
Normal file
Binary file not shown.
14
plugins/Skeleton.gy
Normal file
14
plugins/Skeleton.gy
Normal 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
15
plugins/Skeleton.rb
Normal 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
13
plugins/skeleton.py
Normal 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")
|
289
src/com/jhe/hexed/JHexEditor.java
Normal file
289
src/com/jhe/hexed/JHexEditor.java
Normal 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++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
152
src/com/jhe/hexed/JHexEditorASCII.java
Normal file
152
src/com/jhe/hexed/JHexEditorASCII.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
178
src/com/jhe/hexed/JHexEditorHEX.java
Normal file
178
src/com/jhe/hexed/JHexEditorHEX.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
84
src/me/konloch/kontainer/io/DiskReader.java
Normal file
84
src/me/konloch/kontainer/io/DiskReader.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
176
src/me/konloch/kontainer/io/DiskWriter.java
Normal file
176
src/me/konloch/kontainer/io/DiskWriter.java
Normal 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
BIN
src/resources/1.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 714 B |
331
src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java
Normal file
331
src/the/bytecode/club/bytecodeviewer/BytecodeViewer.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
src/the/bytecode/club/bytecodeviewer/FileChangeNotifier.java
Normal file
14
src/the/bytecode/club/bytecodeviewer/FileChangeNotifier.java
Normal 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);
|
||||||
|
}
|
925
src/the/bytecode/club/bytecodeviewer/FileDrop.java
Normal file
925
src/the/bytecode/club/bytecodeviewer/FileDrop.java
Normal 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
|
135
src/the/bytecode/club/bytecodeviewer/JarUtils.java
Normal file
135
src/the/bytecode/club/bytecodeviewer/JarUtils.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
src/the/bytecode/club/bytecodeviewer/gui/AboutWindow.java
Normal file
28
src/the/bytecode/club/bytecodeviewer/gui/AboutWindow.java
Normal 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;
|
||||||
|
|
||||||
|
}
|
281
src/the/bytecode/club/bytecodeviewer/gui/ClassViewer.java
Normal file
281
src/the/bytecode/club/bytecodeviewer/gui/ClassViewer.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
54
src/the/bytecode/club/bytecodeviewer/gui/ExportJar.java
Normal file
54
src/the/bytecode/club/bytecodeviewer/gui/ExportJar.java
Normal 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;
|
||||||
|
}
|
366
src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java
Normal file
366
src/the/bytecode/club/bytecodeviewer/gui/FileNavigationPane.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
584
src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java
Normal file
584
src/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
217
src/the/bytecode/club/bytecodeviewer/gui/SearchingPane.java
Normal file
217
src/the/bytecode/club/bytecodeviewer/gui/SearchingPane.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
186
src/the/bytecode/club/bytecodeviewer/gui/TabbedPane.java
Normal file
186
src/the/bytecode/club/bytecodeviewer/gui/TabbedPane.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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) {}
|
||||||
|
|
||||||
|
}
|
156
src/the/bytecode/club/bytecodeviewer/gui/WorkPane.java
Normal file
156
src/the/bytecode/club/bytecodeviewer/gui/WorkPane.java
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/the/bytecode/club/bytecodeviewer/plugins/Plugin.java
Normal file
37
src/the/bytecode/club/bytecodeviewer/plugins/Plugin.java
Normal 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);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
106
src/the/bytecode/club/bytecodeviewer/plugins/PluginManager.java
Normal file
106
src/the/bytecode/club/bytecodeviewer/plugins/PluginManager.java
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
116
src/the/bytecode/club/bytecodeviewer/plugins/ReplaceStrings.java
Normal file
116
src/the/bytecode/club/bytecodeviewer/plugins/ReplaceStrings.java
Normal 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 + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;");
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue