Skip to content

Commit

Permalink
decompiler updates
Browse files Browse the repository at this point in the history
  • Loading branch information
GraxCode committed May 7, 2020
1 parent d72d8c8 commit cd60cde
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 89 deletions.
66 changes: 7 additions & 59 deletions src/me/nov/threadtear/decompiler/CFR.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,70 +14,18 @@ public class CFR {
public static final HashMap<String, String> options = new HashMap<>();

static {
options.put("aexagg", "false");
options.put("allowcorrecting", "true");
options.put("arrayiter", "true");
options.put("caseinsensitivefs", "false");
options.put("clobber", "false");
options.put("collectioniter", "true");
options.put("commentmonitors", "false");
options.put("decodeenumswitch", "true");
options.put("decodefinally", "true");
options.put("decodelambdas", "true");
options.put("decodestringswitch", "true");
options.put("dumpclasspath", "false");
options.put("eclipse", "true");
options.put("elidescala", "false");
options.put("forcecondpropagate", "false");
options.put("forceexceptionprune", "false");
options.put("forcereturningifs", "false");
options.put("forcetopsort", "true");
options.put("forcetopsortaggress", "true");
options.put("forloopaggcapture", "false");
options.put("hidebridgemethods", "true");
options.put("hidelangimports", "true");
options.put("showversion", "false");
options.put("hidelongstrings", "true");
options.put("hideutf", "false");
options.put("ignoreexceptionsalways", "true");
options.put("innerclasses", "true");
options.put("j14classobj", "false");
options.put("labelledblocks", "true");
options.put("lenient", "false");
options.put("liftconstructorinit", "true");
options.put("override", "true");
options.put("pullcodecase", "false");
options.put("recover", "true");
options.put("recovertypeclash", "false");
options.put("recovertypehints", "false");
options.put("relinkconststring", "true");
options.put("removebadgenerics", "true");
options.put("removeboilerplate", "true");
options.put("removedeadmethods", "true");
options.put("removeinnerclasssynthetics", "false");
options.put("rename", "false");
options.put("renamedupmembers", "false");
options.put("renameenumidents", "false");
options.put("renameillegalidents", "false");
options.put("showinferrable", "false");
options.put("showversion", "false");
options.put("silent", "false");
options.put("stringbuffer", "true");
options.put("stringbuilder", "true");
options.put("sugarasserts", "true");
options.put("sugarboxing", "true");
options.put("sugarenums", "true");
options.put("tidymonitors", "true");
options.put("commentmonitors", "false");
options.put("tryresources", "true");
options.put("usenametable", "true");
options.put("analyseas", "CLASS");
options.put("innerclasses", "false");
options.put("forcetopsort", "true");
options.put("forcetopsortaggress", "true");
}

private static String decompiled;

public static String decompile(ClassNode cn) {
public static String decompile(String name, byte[] bytes) {
try {
byte[] bytes = Conversion.toBytecode0(cn);
decompiled = null;
OutputSinkFactory mySink = new OutputSinkFactory() {
@Override
Expand Down Expand Up @@ -113,7 +61,7 @@ public String getPossiblyRenamedPath(String path) {
@Override
public Pair<byte[], String> getClassFileContent(String path) throws IOException {
String name = path.substring(0, path.length() - 6);
if (name.equals(cn.name)) {
if (name.equals(name)) {
return Pair.make(bytes, name);
}
// cfr loads unnecessary classes. normally you should throw a FNF exception here, but this way, no long comment at the top of the code is created
Expand All @@ -129,7 +77,7 @@ public Collection<String> addJar(String arg0) {
}
};
CfrDriver cfrDriver = new CfrDriver.Builder().withClassFileSource(source).withOutputSink(mySink).withOptions(options).build();
cfrDriver.analyse(Arrays.asList(cn.name));
cfrDriver.analyse(Arrays.asList(name));
} catch (Exception e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
Expand Down
16 changes: 12 additions & 4 deletions src/me/nov/threadtear/io/Clazz.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package me.nov.threadtear.io;

import java.util.jar.JarEntry;
import java.io.*;
import java.util.jar.*;

import org.objectweb.asm.tree.ClassNode;

public class Clazz {

public boolean transform = true;
public ClassNode node;
public JarEntry oldEntry;
public final ClassNode node;
public final JarEntry oldEntry;
public final JarFile jar;

public Clazz(ClassNode node, JarEntry oldEntry) {
public Clazz(ClassNode node, JarEntry oldEntry, JarFile jar) {
super();
this.node = node;
this.oldEntry = oldEntry;
this.jar = jar;
}

public InputStream streamOriginal() throws IOException {
JarFile jf = new JarFile(jar.getName());
return jf.getInputStream(jf.getEntry(oldEntry.getName()));
}
}
2 changes: 1 addition & 1 deletion src/me/nov/threadtear/io/JarIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private static ArrayList<Clazz> readEntry(JarFile jar, JarEntry en, ArrayList<Cl
try {
final ClassNode cn = Conversion.toNode(bytes);
if (cn != null && (cn.superName != null || (cn.name != null && cn.name.equals("java/lang/Object")))) {
classes.add(new Clazz(cn, en));
classes.add(new Clazz(cn, en, jar));
}
} catch (Exception e) {
e.printStackTrace();
Expand Down
9 changes: 4 additions & 5 deletions src/me/nov/threadtear/swing/frame/DecompilerFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,25 @@

import javax.swing.*;

import org.objectweb.asm.tree.ClassNode;

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

import me.nov.threadtear.io.Clazz;
import me.nov.threadtear.swing.Utils;
import me.nov.threadtear.swing.panel.DecompilerPanel;

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

public DecompilerFrame(ClassNode cn) {
setTitle("Decompiler: " + cn.name);
public DecompilerFrame(Clazz clazz) {
setTitle("Decompiler: " + clazz.node.name);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setBounds(100, 100, 1000, 600);
setLayout(new BorderLayout());
setIconImage(Utils.iconToImage(IconLoader.get().loadSVGIcon("res/decompile.svg", 64, 64, false)));
setAlwaysOnTop(true);
JPanel cp = new JPanel(new BorderLayout());
cp.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
cp.add(new DecompilerPanel(cn), BorderLayout.CENTER);
cp.add(new DecompilerPanel(clazz), BorderLayout.CENTER);
this.add(cp, BorderLayout.CENTER);
JPanel buttons = new JPanel();
buttons.setLayout(new FlowLayout(FlowLayout.RIGHT));
Expand Down
121 changes: 105 additions & 16 deletions src/me/nov/threadtear/swing/panel/DecompilerPanel.java
Original file line number Diff line number Diff line change
@@ -1,40 +1,68 @@
package me.nov.threadtear.swing.panel;

import static org.objectweb.asm.Opcodes.*;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.Objects;
import java.util.stream.StreamSupport;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.*;

import org.apache.commons.io.IOUtils;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.*;

import com.github.weisj.darklaf.components.loading.LoadingIndicator;
import com.github.weisj.darklaf.icons.IconLoader;
import com.github.weisj.darklaf.ui.text.DarkTextUI;

import me.nov.threadtear.decompiler.CFR;
import me.nov.threadtear.io.*;
import me.nov.threadtear.swing.textarea.DecompilerTextArea;
import me.nov.threadtear.util.Strings;

public class DecompilerPanel extends JPanel {
public class DecompilerPanel extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;

private DecompilerTextArea textArea;

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

public DecompilerPanel(ClassNode cn) {
private Clazz clazz;

private RTextScrollPane scp;
private JComboBox<String> conversionMethod;
private JCheckBox ignoreTCB;
private JCheckBox ignoreMon;

public DecompilerPanel(Clazz cn) {
this.clazz = cn;
this.setLayout(new BorderLayout(4, 4));
JPanel actionPanel = new JPanel();
actionPanel.setLayout(new GridBagLayout());

JPanel leftActionPanel = new JPanel();
leftActionPanel.setLayout(new GridBagLayout());
leftActionPanel.add(new JLabel("<html><tt>CFR 0.149 "));
conversionMethod = new JComboBox<>(new String[] { "Use source", "Pass through ASM" });
conversionMethod.addActionListener(this);
conversionMethod.setEnabled(false);
leftActionPanel.add(conversionMethod);
leftActionPanel.add(ignoreTCB = new JCheckBox("Ingore try catch blocks"));
leftActionPanel.add(ignoreMon = new JCheckBox("Ignore synchronized"));
ignoreTCB.setEnabled(false);
ignoreTCB.addActionListener(this);
ignoreMon.setEnabled(false);
ignoreMon.addActionListener(this);
JPanel rightActionPanel = new JPanel();
rightActionPanel.setLayout(new GridBagLayout());
JButton reload = new JButton(IconLoader.get().loadSVGIcon("res/refresh.svg", false));
reload.addActionListener(l -> {
textArea.setText(CFR.decompile(cn));
});
reload.addActionListener(this);
JTextField search = new JTextField();
search.putClientProperty(DarkTextUI.KEY_DEFAULT_TEXT, "Search...");
search.putClientProperty(DarkTextUI.KEY_DEFAULT_TEXT, "Search for text or regex");
search.setPreferredSize(new Dimension(200, reload.getPreferredSize().height));
search.addActionListener(l -> {
try {
Expand All @@ -54,7 +82,7 @@ public DecompilerPanel(ClassNode cn) {
Label: {
for (int i = 0; i < split.length; i++) {
String line = split[i];
if (line.toLowerCase().contains(searchText)) {
if (Strings.containsRegex(line, searchText)) {
if (i > searchIndex) {
textArea.setCaretPosition(textArea.getDocument().getDefaultRootElement().getElement(i).getStartOffset());
searchIndex = i;
Expand All @@ -65,6 +93,7 @@ public DecompilerPanel(ClassNode cn) {
}
}
}
Toolkit.getDefaultToolkit().beep();
if (first) {
// go back to first line
textArea.setCaretPosition(textArea.getDocument().getDefaultRootElement().getElement(firstIndex).getStartOffset());
Expand All @@ -77,34 +106,94 @@ public DecompilerPanel(ClassNode cn) {
}
});

actionPanel.add(search);
rightActionPanel.add(search);
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.EAST;
actionPanel.add(reload, gbc);
rightActionPanel.add(reload, gbc);
JPanel topPanel = new JPanel();
topPanel.setBorder(new EmptyBorder(1, 5, 0, 1));
topPanel.setLayout(new BorderLayout());
topPanel.add(new JLabel("<html>CFR Decompiler 0.149 (<i>www.benf.org/other/cfr</i>)"), BorderLayout.WEST);
topPanel.add(actionPanel, BorderLayout.EAST);
topPanel.add(leftActionPanel, BorderLayout.WEST);
topPanel.add(rightActionPanel, BorderLayout.EAST);
this.add(topPanel, BorderLayout.NORTH);
LoadingIndicator loadingLabel = new LoadingIndicator("Decompiling class file... ", JLabel.CENTER);
loadingLabel.setRunning(true);
this.add(loadingLabel, BorderLayout.CENTER);
SwingUtilities.invokeLater(() -> {
new Thread(() -> {
this.textArea = new DecompilerTextArea();
this.textArea.setText(CFR.decompile(cn));
JScrollPane scp = new RTextScrollPane(textArea);
this.update();
scp = new RTextScrollPane(textArea);
scp.getVerticalScrollBar().setUnitIncrement(16);
scp.setBorder(BorderFactory.createLoweredSoftBevelBorder());
this.remove(loadingLabel);
this.add(scp, BorderLayout.CENTER);
conversionMethod.setEnabled(true);
invalidate();
validate();
repaint();
}, "decompile-thread").start();
});
}

@Override
public void actionPerformed(ActionEvent e) {
reload();
}

private void reload() {
LoadingIndicator loadingLabel = new LoadingIndicator("Decompiling class file... ", JLabel.CENTER);
loadingLabel.setRunning(true);
this.add(loadingLabel, BorderLayout.CENTER);
this.remove(scp);
invalidate();
validate();
SwingUtilities.invokeLater(() -> {
new Thread(() -> {
update();
this.remove(loadingLabel);
this.add(scp, BorderLayout.CENTER);
invalidate();
validate();
repaint();
}).start();
});
}

public void update() {
try {
byte[] bytes;
if (conversionMethod.getSelectedIndex() == 0) {
// use the original jar file to retrieve bytes
bytes = IOUtils.toByteArray(clazz.streamOriginal());
ignoreTCB.setEnabled(false);
ignoreMon.setEnabled(false);
} else {
System.out.println("decompile ASM " + conversionMethod.getSelectedIndex());
ignoreTCB.setEnabled(true);
ignoreMon.setEnabled(true);
ClassNode copy = Conversion.toNode(Conversion.toBytecode0(clazz.node));
// do some asm action here
if (ignoreTCB.isSelected()) {
copy.methods.forEach(m -> m.tryCatchBlocks = null);
}
if (ignoreMon.isSelected()) {
copy.methods.forEach(m -> StreamSupport.stream(m.instructions.spliterator(), false).filter(i -> i.getOpcode() == MONITORENTER || i.getOpcode() == MONITOREXIT)
.forEach(i -> m.instructions.set(i, new InsnNode(POP))));
}
bytes = Conversion.toBytecode0(copy);
}
String decompiled = CFR.decompile(clazz.node.name, bytes);
this.textArea.setText(decompiled);
} catch (IOException e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
this.textArea.setText(sw.toString());
}
}

private void hightlightText(String searchText) throws BadLocationException {
Highlighter highlighter = textArea.getHighlighter();
highlighter.removeAllHighlights();
Expand Down
4 changes: 2 additions & 2 deletions src/me/nov/threadtear/swing/tree/ClassTreePanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private JPanel createButtons() {
decompile.addActionListener(l -> {
SortedTreeClassNode tn = (SortedTreeClassNode) tree.getLastSelectedPathComponent();
if (tn != null && tn.member != null) {
new DecompilerFrame(tn.member.node).setVisible(true);
new DecompilerFrame(tn.member).setVisible(true);
}
});
panel.add(decompile);
Expand Down Expand Up @@ -142,7 +142,7 @@ public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
SortedTreeClassNode tn = (SortedTreeClassNode) getLastSelectedPathComponent();
if (tn != null && tn.member != null) {
new DecompilerFrame(tn.member.node).setVisible(true);
new DecompilerFrame(tn.member).setVisible(true);
}
}
}
Expand Down
Loading

0 comments on commit cd60cde

Please sign in to comment.