Skip to content

Commit

Permalink
add bytecode viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
GraxCode committed Apr 25, 2020
1 parent 5371f11 commit 1d50ba8
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 29 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
id 'eclipse'
}

version = '2.1.1'
version = '2.2.0'
sourceCompatibility = 1.8
targetCompatibility = 1.8

Expand All @@ -30,6 +30,7 @@ dependencies {
compile 'org.ow2.asm:asm:8.0.1'
compile 'org.ow2.asm:asm-tree:8.0.1'
compile 'org.ow2.asm:asm-analysis:8.0.1'
compile 'org.ow2.asm:asm-util:8.0.1'

compile 'org.benf:cfr:0.149'
compile 'com.fifesoft:rsyntaxtextarea:3.1.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,21 @@
import me.nov.threadtear.analysis.full.value.values.UnaryOpValue;
import me.nov.threadtear.analysis.full.value.values.UnknownInstructionValue;

public class CodeTracker extends Interpreter<CodeReferenceValue> implements Opcodes {
public class CodeRewriter extends Interpreter<CodeReferenceValue> implements Opcodes {

SuperInterpreter basic = new SuperInterpreter();

private CodeReferenceValue[] presetArgs;
private Type[] desc;

private ICodeReferenceHandler referenceHandler;
private ICRReferenceHandler referenceHandler;

public CodeTracker(ICodeReferenceHandler referenceHandler) {
public CodeRewriter(ICRReferenceHandler referenceHandler) {
super(ASM8);
this.referenceHandler = referenceHandler;
}

public CodeTracker(ICodeReferenceHandler referenceHandler, boolean isStatic, int localVariables, String descr, CodeReferenceValue[] args) {
public CodeRewriter(ICRReferenceHandler referenceHandler, boolean isStatic, int localVariables, String descr, CodeReferenceValue[] args) {
super(ASM8);
this.referenceHandler = referenceHandler;
this.desc = Type.getArgumentTypes(descr);
Expand Down Expand Up @@ -153,7 +153,7 @@ public CodeReferenceValue newEmptyValue(int local) {

@Override
public CodeReferenceValue unaryOperation(AbstractInsnNode insn, CodeReferenceValue value) throws AnalyzerException {
//TODO checkcast, instanceof
// TODO checkcast, instanceof
BasicValue v = basic.unaryOperation(insn, value.getType());
switch (insn.getOpcode()) {
case GETFIELD:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import me.nov.threadtear.analysis.full.value.CodeReferenceValue;

public interface ICodeReferenceHandler {
public interface ICRReferenceHandler {

Object getFieldValueOrNull(BasicValue v, String owner, String name, String desc);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,24 @@
import org.objectweb.asm.tree.analysis.Frame;

import me.nov.threadtear.analysis.full.CodeAnalyzer;
import me.nov.threadtear.analysis.full.CodeTracker;
import me.nov.threadtear.analysis.full.ICodeReferenceHandler;
import me.nov.threadtear.analysis.full.CodeRewriter;
import me.nov.threadtear.analysis.full.ICRReferenceHandler;
import me.nov.threadtear.analysis.full.value.CodeReferenceValue;
import me.nov.threadtear.execution.Execution;
import me.nov.threadtear.execution.ExecutionCategory;
import me.nov.threadtear.io.Clazz;
import me.nov.threadtear.util.asm.Access;
import me.nov.threadtear.util.asm.Instructions;

public class RemoveUnnecessary extends Execution implements ICodeReferenceHandler {
public class RemoveUnnecessary extends Execution implements ICRReferenceHandler {

public RemoveUnnecessary() {
super(ExecutionCategory.CLEANING, "<html><s>Remove unnecessary instructions</s>",
"Remove unnecessary instructions or flow obfuscation that can be optimized.<br>This could include number or flow obfuscation.<br><b>Do not run this, it is unfinished!</b>");
}

/*
* TODO Nothing done here yet, this class should simulate stack and
* simultaneously rewrite the code.
* TODO Nothing done here yet, this class should simulate stack and simultaneously rewrite the code.
*
* eg. ICONST_4 ICONST_1 IADD INVOKESTATIC ...
*
Expand All @@ -58,7 +57,7 @@ private InsnList simulateAndRewrite(ClassNode cn, MethodNode m) {
m.tryCatchBlocks.clear();
if (m.localVariables != null)
m.localVariables.clear();
CodeAnalyzer a = new CodeAnalyzer(new CodeTracker(this, Access.isStatic(m.access), m.maxLocals, m.desc, new CodeReferenceValue[0]));
CodeAnalyzer a = new CodeAnalyzer(new CodeRewriter(this, Access.isStatic(m.access), m.maxLocals, m.desc, new CodeReferenceValue[0]));
try {
a.analyze(cn.name, m);
} catch (AnalyzerException e) {
Expand Down
10 changes: 10 additions & 0 deletions src/me/nov/threadtear/io/Conversion.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package me.nov.threadtear.io;

import java.io.PrintWriter;
import java.io.StringWriter;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.TraceClassVisitor;

public class Conversion {
public static byte[] toBytecode(ClassNode cn, boolean useMaxs) {
Expand Down Expand Up @@ -38,4 +42,10 @@ public static ClassNode toNode(final byte[] bytez) {
cr = null;
return cn;
}

public static String textify(ClassNode cn) {
StringWriter out = new StringWriter();
new ClassReader(toBytecode0(cn)).accept(new TraceClassVisitor(new PrintWriter(out)), ClassReader.SKIP_DEBUG);
return out.toString();
}
}
43 changes: 43 additions & 0 deletions src/me/nov/threadtear/swing/frame/BytecodeFrame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package me.nov.threadtear.swing.frame;

import java.awt.BorderLayout;
import java.awt.FlowLayout;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

import org.objectweb.asm.tree.ClassNode;

import com.github.weisj.darklaf.icons.IconLoader;

import me.nov.threadtear.swing.Utils;
import me.nov.threadtear.swing.panel.BytecodePanel;

public class BytecodeFrame extends JFrame {
private static final long serialVersionUID = 1L;

public BytecodeFrame(ClassNode cn) {
setTitle("Bytecode: " + cn.name);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setBounds(100, 100, 1000, 600);
setLayout(new BorderLayout());
setIconImage(Utils.iconToImage(IconLoader.get().loadSVGIcon("res/bytecode.svg", 64, 64, false)));
setAlwaysOnTop(true);
JPanel cp = new JPanel(new BorderLayout());
cp.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
cp.add(new BytecodePanel(cn), BorderLayout.CENTER);
this.add(cp, BorderLayout.CENTER);
JPanel buttons = new JPanel();
buttons.setLayout(new FlowLayout(FlowLayout.RIGHT));
JButton close = new JButton("Close");
close.addActionListener(e -> {
dispose();
});
buttons.add(close);
getContentPane().add(buttons, BorderLayout.SOUTH);

}
}
30 changes: 16 additions & 14 deletions src/me/nov/threadtear/swing/list/ClassList.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import com.github.weisj.darklaf.icons.IconLoader;
Expand All @@ -23,6 +24,7 @@
import me.nov.threadtear.io.JarIO;
import me.nov.threadtear.swing.Utils;
import me.nov.threadtear.swing.dialog.JarAnalysis;
import me.nov.threadtear.swing.frame.BytecodeFrame;
import me.nov.threadtear.swing.frame.DecompilerFrame;
import me.nov.threadtear.swing.handler.ILoader;
import me.nov.threadtear.swing.handler.JarDropHandler;
Expand Down Expand Up @@ -67,27 +69,27 @@ private JPanel createButtons() {
}
});
panel.add(decompile);
JButton bytecode = new JButton("Bytecode", IconLoader.get().loadSVGIcon("res/bytecode.svg", false));
bytecode.addActionListener(l -> {
SortedTreeClassNode tn = (SortedTreeClassNode) tree.getLastSelectedPathComponent();
if (tn != null && tn.member != null) {
new BytecodeFrame(tn.member.node).setVisible(true);
}
});

panel.add(bytecode);
JButton ignore = new JButton("Ignore", IconLoader.get().loadSVGIcon("res/ignore.svg", false));
ignore.addActionListener(l -> {
SortedTreeClassNode node = (SortedTreeClassNode) tree.getLastSelectedPathComponent();
if (node != null) {
ignoreChilds(node);
refreshIgnored();
repaint();
tree.grabFocus();
TreePath[] paths = tree.getSelectionPaths();
for (int i = 0; i < paths.length; i++) {
SortedTreeClassNode tn = (SortedTreeClassNode) paths[i].getLastPathComponent();
ignoreChilds(tn);
}
});
panel.add(ignore);
JButton toggle = new JButton("Toggle all", IconLoader.get().loadSVGIcon("res/toggle.svg", false));
toggle.addActionListener(l -> {
ignoreChilds((SortedTreeClassNode) model.getRoot());
refreshIgnored();
repaint();
tree.grabFocus();
});

panel.add(toggle);
panel.add(ignore);
return panel;
}

Expand Down Expand Up @@ -121,7 +123,7 @@ public ClassTree() {
this.setCellRenderer(new ClassTreeCellRenderer());
model = new DefaultTreeModel(new SortedTreeClassNode(""));
this.setModel(model);
this.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
this.getSelectionModel().setSelectionMode(TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
Expand Down
119 changes: 119 additions & 0 deletions src/me/nov/threadtear/swing/panel/BytecodePanel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package me.nov.threadtear.swing.panel;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.Objects;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Document;
import javax.swing.text.Highlighter;

import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.tree.ClassNode;

import com.github.weisj.darklaf.icons.IconLoader;

import me.nov.threadtear.io.Conversion;
import me.nov.threadtear.swing.textarea.BytecodeTextArea;

public class BytecodePanel extends JPanel {
private static final long serialVersionUID = 1L;

private BytecodeTextArea textArea;

private int searchIndex = -1;
private String lastSearchText = null;

public BytecodePanel(ClassNode cn) {
this.setLayout(new BorderLayout(4, 4));
JPanel actionPanel = new JPanel();
actionPanel.setLayout(new GridBagLayout());
JButton reload = new JButton(IconLoader.get().loadSVGIcon("res/refresh.svg", false));
reload.addActionListener(l -> {
textArea.setText(Conversion.textify(cn));
});
JTextField search = new JTextField();
// search.putClientProperty(DarkTextUI.KEY_DEFAULT_TEXT, "Search...");
search.setPreferredSize(new Dimension(200, reload.getPreferredSize().height));
search.addActionListener(l -> {
try {
String text = search.getText();
if (text.isEmpty()) {
textArea.getHighlighter().removeAllHighlights();
return;
}
String searchText = text.toLowerCase();
if (!Objects.equals(searchText, lastSearchText)) {
searchIndex = -1;
lastSearchText = searchText;
}
String[] split = textArea.getText().split("\\r?\\n");
int firstIndex = -1;
boolean first = false;
Label: {
for (int i = 0; i < split.length; i++) {
String line = split[i];
if (line.toLowerCase().contains(searchText)) {
if (i > searchIndex) {
textArea.setCaretPosition(textArea.getDocument().getDefaultRootElement().getElement(i).getStartOffset());
searchIndex = i;
break Label;
} else if (!first) {
firstIndex = i;
first = true;
}
}
}
if (first) {
// go back to first line
textArea.setCaretPosition(textArea.getDocument().getDefaultRootElement().getElement(firstIndex).getStartOffset());
searchIndex = firstIndex;
}
}
hightlightText(searchText);
} catch (Exception e) {
e.printStackTrace();
}
});

actionPanel.add(search);
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.EAST;
actionPanel.add(reload, gbc);
JPanel topPanel = new JPanel();
topPanel.setBorder(new EmptyBorder(1, 5, 0, 1));
topPanel.setLayout(new BorderLayout());
topPanel.add(new JLabel("<html>Bytecode of <tt>" + cn.name.replace('/', '.') + "</tt>"), BorderLayout.WEST);
topPanel.add(actionPanel, BorderLayout.EAST);
this.add(topPanel, BorderLayout.NORTH);
this.textArea = new BytecodeTextArea();
textArea.setText(Conversion.textify(cn));
JScrollPane scp = new RTextScrollPane(textArea);
scp.getVerticalScrollBar().setUnitIncrement(16);
scp.setBorder(BorderFactory.createLoweredSoftBevelBorder());
this.add(scp, BorderLayout.CENTER);
}

private void hightlightText(String searchText) throws BadLocationException {
Highlighter highlighter = textArea.getHighlighter();
highlighter.removeAllHighlights();
Document document = textArea.getDocument();
String text = document.getText(0, document.getLength()).toLowerCase();
int pos = text.indexOf(searchText);
while (pos >= 0) {
highlighter.addHighlight(pos, pos + searchText.length(), (Highlighter.HighlightPainter) new DefaultHighlighter.DefaultHighlightPainter(new Color(0x0078d7)));
pos = text.indexOf(searchText, pos + searchText.length());
}
}
}
26 changes: 26 additions & 0 deletions src/me/nov/threadtear/swing/textarea/BytecodeTextArea.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package me.nov.threadtear.swing.textarea;

import java.awt.Font;
import java.io.IOException;

import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Theme;

public class BytecodeTextArea extends RSyntaxTextArea {
private static final long serialVersionUID = 1L;

public BytecodeTextArea() {
this.setSyntaxEditingStyle(SYNTAX_STYLE_CPLUSPLUS);
this.setCodeFoldingEnabled(true);
this.setAntiAliasingEnabled(true);
this.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
this.setEditable(false);

try {
Theme theme = Theme.load(getClass().getResourceAsStream("/res/rsta-theme.xml"));
theme.apply(this);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public DecompilerTextArea() {
this.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
this.setEditable(false);

// change theme to java
try {
Theme theme = Theme.load(getClass().getResourceAsStream("/res/rsta-theme.xml"));
theme.apply(this);
Expand Down
1 change: 1 addition & 0 deletions src/res/bytecode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/res/toggle.svg

This file was deleted.

0 comments on commit 1d50ba8

Please sign in to comment.