Skip to content
This repository was archived by the owner on Jan 2, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
}

group 'cn.enaium'
version '0.3.0'
version '0.4.0'

sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/cn/enaium/joe/JavaOctetEditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import cn.enaium.joe.gui.panel.file.tree.FileTreePanel;
import cn.enaium.joe.gui.panel.menu.FileMenu;
import cn.enaium.joe.gui.panel.menu.HelpMenu;
import cn.enaium.joe.gui.panel.menu.SearchMenu;
import cn.enaium.joe.jar.Jar;
import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory;
import org.fife.ui.rsyntaxtextarea.TokenMakerFactory;
Expand Down Expand Up @@ -58,6 +59,7 @@ public void run() {
window.setJMenuBar(new JMenuBar() {{
add(new FileMenu());
add(new HelpMenu());
add(new SearchMenu());
}});

window.setContentPane(new JPanel(new BorderLayout()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,22 @@
* limitations under the License.
*/

package cn.enaium.joe.gui.panel;
package cn.enaium.joe.dialog;

import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Theme;
import cn.enaium.joe.gui.panel.search.Result;

import java.io.IOException;
import java.awt.*;

/**
* @author Enaium
*/
public class CodeArea extends RSyntaxTextArea {
public CodeArea() {
setCodeFoldingEnabled(true);
setEditable(false);
Theme theme = null;
try {
theme = Theme.load(getClass().getResourceAsStream("/org/fife/ui/rsyntaxtextarea/themes/dark.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
theme.apply(this);
public class SearchDialog extends Dialog {
public Result result = new Result();

public SearchDialog() {
super("Search");
setLayout(new BorderLayout());
setSize(400, 300);
add(result, BorderLayout.CENTER);
}
}
76 changes: 76 additions & 0 deletions src/main/java/cn/enaium/joe/dialog/search/SearchLdcDialog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2022 Enaium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cn.enaium.joe.dialog.search;

import cn.enaium.joe.JavaOctetEditor;
import cn.enaium.joe.dialog.SearchDialog;
import cn.enaium.joe.gui.panel.search.ResultNode;
import cn.enaium.joe.jar.Jar;
import cn.enaium.joe.util.ASyncUtil;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;

import javax.swing.*;
import java.awt.*;
import java.util.Map;

/**
* @author Enaium
*/
public class SearchLdcDialog extends SearchDialog {
public SearchLdcDialog() {
setTitle("Search LDC");
add(new JPanel(new FlowLayout()) {{
JTextField text = new JTextField(15);
add(text);
add(new JButton("Search") {{
addActionListener(e -> {
if (!text.getText().replace(" ", "").isEmpty()) {
Jar jar = JavaOctetEditor.getInstance().jar;
ASyncUtil.execute(() -> {
float loaded = 0;
float total = 0;
for (Map.Entry<String, ClassNode> stringClassNodeEntry : jar.classes.entrySet()) {
for (MethodNode method : stringClassNodeEntry.getValue().methods) {
total += method.instructions.size();
}
}

for (Map.Entry<String, ClassNode> stringClassNodeEntry : jar.classes.entrySet()) {
for (MethodNode method : stringClassNodeEntry.getValue().methods) {

for (AbstractInsnNode instruction : method.instructions) {
if (instruction instanceof LdcInsnNode) {
String ldc = ((LdcInsnNode) instruction).cst.toString();
if (ldc.contains(text.getText())) {
((DefaultListModel<ResultNode>) result.getList().getModel()).addElement(new ResultNode(stringClassNodeEntry.getValue(), ldc));
}
}
JavaOctetEditor.getInstance().bottomPanel.setProcess((int) ((loaded++ / total) * 100f));
}
}
}
JavaOctetEditor.getInstance().bottomPanel.setProcess(0);
});
}
});
}});
}}, BorderLayout.SOUTH);
}
}
99 changes: 99 additions & 0 deletions src/main/java/cn/enaium/joe/gui/panel/CodeAreaPanel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright 2022 Enaium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package cn.enaium.joe.gui.panel;

import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Theme;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.fife.ui.rtextarea.SearchContext;
import org.fife.ui.rtextarea.SearchEngine;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;

/**
* @author Enaium
*/
public class CodeAreaPanel extends JPanel implements ActionListener {

private RSyntaxTextArea textArea;
private JTextField searchField;
private JCheckBox regexCB;
private JCheckBox matchCaseCB;

public CodeAreaPanel() {
super(new BorderLayout());
textArea = new RSyntaxTextArea();
textArea.setCodeFoldingEnabled(true);
textArea.setEditable(false);
Theme theme = null;
try {
theme = Theme.load(getClass().getResourceAsStream("/org/fife/ui/rsyntaxtextarea/themes/dark.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
theme.apply(textArea);

add(new RTextScrollPane(textArea), BorderLayout.CENTER);
JToolBar toolBar = new JToolBar();
searchField = new JTextField(30);
toolBar.add(searchField);
final JButton nextButton = new JButton("Find Next");
nextButton.setActionCommand("FindNext");
nextButton.addActionListener(this);
toolBar.add(nextButton);
searchField.addActionListener(e -> nextButton.doClick(0));
JButton prevButton = new JButton("Find Previous");
prevButton.setActionCommand("FindPrev");
prevButton.addActionListener(this);
toolBar.add(prevButton);
regexCB = new JCheckBox("Regex");
toolBar.add(regexCB);
matchCaseCB = new JCheckBox("Match Case");
toolBar.add(matchCaseCB);
add(toolBar, BorderLayout.NORTH);
}

@Override
public void actionPerformed(ActionEvent e) {

// "FindNext" => search forward, "FindPrev" => search backward
String command = e.getActionCommand();
boolean forward = "FindNext".equals(command);

// Create an object defining our search parameters.
SearchContext context = new SearchContext();
String text = searchField.getText();
if (text.length() == 0) {
return;
}
context.setSearchFor(text);
context.setMatchCase(matchCaseCB.isSelected());
context.setRegularExpression(regexCB.isSelected());
context.setSearchForward(forward);
context.setWholeWord(false);

SearchEngine.find(textArea, context);
}

public RSyntaxTextArea getTextArea() {
return textArea;
}
}
20 changes: 18 additions & 2 deletions src/main/java/cn/enaium/joe/gui/panel/LeftPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import cn.enaium.joe.JavaOctetEditor;
import cn.enaium.joe.gui.panel.file.tree.FileTreePanel;
import cn.enaium.joe.jar.Jar;
import cn.enaium.joe.util.JTreeUtil;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
Expand Down Expand Up @@ -50,10 +51,25 @@ public void keyPressed(KeyEvent e) {
if (!jTextField.getText().replace(" ", "").isEmpty()) {
Jar searchedJar = jar.copy();

searchedJar.classes = searchedJar.classes.entrySet().stream().filter(stringClassNodeEntry -> stringClassNodeEntry.getKey().toLowerCase(Locale.ROOT).contains(jTextField.getText().toLowerCase(Locale.ROOT))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
searchedJar.resources = searchedJar.resources.entrySet().stream().filter(stringEntry -> stringEntry.getKey().toLowerCase(Locale.ROOT).contains(jTextField.getText().toLowerCase(Locale.ROOT))).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
searchedJar.classes = searchedJar.classes.entrySet().stream().filter(stringClassNodeEntry -> {
String key = stringClassNodeEntry.getKey();

if (!key.contains("/")) {
key = key.substring(key.lastIndexOf("/") + 1);
}

return key.toLowerCase(Locale.ROOT).contains(jTextField.getText().toLowerCase(Locale.ROOT));
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
searchedJar.resources = searchedJar.resources.entrySet().stream().filter(stringEntry -> {
String key = stringEntry.getKey();
if (!key.contains("/")) {
key = key.substring(key.lastIndexOf("/") + 1);
}
return key.toLowerCase(Locale.ROOT).contains(jTextField.getText().toLowerCase(Locale.ROOT));
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

JavaOctetEditor.getInstance().fileTreePanel.refresh(searchedJar);
JTreeUtil.setTreeExpandedState(JavaOctetEditor.getInstance().fileTreePanel, true);
} else {
JavaOctetEditor.getInstance().fileTreePanel.refresh(jar);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package cn.enaium.joe.gui.panel.file.tabbed.tab;

import cn.enaium.joe.JavaOctetEditor;
import cn.enaium.joe.gui.panel.CodeArea;
import cn.enaium.joe.gui.panel.CodeAreaPanel;
import cn.enaium.joe.util.ASyncUtil;
import javassist.ClassPool;
import javassist.CtClass;
Expand All @@ -44,18 +44,7 @@ public class ASMifierTablePanel extends ClassNodeTabPanel {
public ASMifierTablePanel(ClassNode classNode) {
super(classNode);
setLayout(new BorderLayout());
CodeArea codeArea = new CodeArea();
codeArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
codeArea.setEditable(true);
StringWriter stringWriter = new StringWriter();
ASyncUtil.execute(() -> {
classNode.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(stringWriter)));
}, () -> {
String trim = getMiddle(getMiddle(stringWriter.toString())).trim();
codeArea.setText(trim.substring(0, trim.lastIndexOf("\n")));
codeArea.setCaretPosition(0);
});
add(new RTextScrollPane(codeArea) {{
CodeAreaPanel codeAreaPanel = new CodeAreaPanel(){{
getTextArea().addKeyListener(new KeyAdapter() {

boolean control = false;
Expand Down Expand Up @@ -87,7 +76,7 @@ public void keyPressed(KeyEvent e) {
classPool.importPackage("org.objectweb.asm.TypePath");
CtClass ctClass = classPool.makeClass(ASMifier.class.getSimpleName());
ctClass.addInterface(classPool.get("org.objectweb.asm.Opcodes"));
ctClass.addMethod(CtMethod.make("public static byte[] dump() throws Exception {" + codeArea.getText() + "return classWriter.toByteArray();}", ctClass));
ctClass.addMethod(CtMethod.make("public static byte[] dump() throws Exception {" + getTextArea().getText() + "return classWriter.toByteArray();}", ctClass));
byte[] dumps = (byte[]) new Loader(classPool).loadClass(ASMifier.class.getSimpleName()).getMethod("dump").invoke(null);
ClassNode newClassNode = new ClassNode();
new ClassReader(dumps).accept(newClassNode, ClassReader.EXPAND_FRAMES);
Expand All @@ -107,7 +96,18 @@ public void keyReleased(KeyEvent e) {
}
}
});
}});
}};
codeAreaPanel.getTextArea().setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
codeAreaPanel.getTextArea().setEditable(true);
StringWriter stringWriter = new StringWriter();
ASyncUtil.execute(() -> {
classNode.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(stringWriter)));
}, () -> {
String trim = getMiddle(getMiddle(stringWriter.toString())).trim();
codeAreaPanel.getTextArea().setText(trim.substring(0, trim.lastIndexOf("\n")));
codeAreaPanel.getTextArea().setCaretPosition(0);
});
add(codeAreaPanel);
}

public String getMiddle(String s) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package cn.enaium.joe.gui.panel.file.tabbed.tab;

import cn.enaium.joe.gui.panel.CodeArea;
import cn.enaium.joe.gui.panel.CodeAreaPanel;
import cn.enaium.joe.util.ASyncUtil;
import org.benf.cfr.reader.PluginRunner;
import org.benf.cfr.reader.api.ClassFileSource;
import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
import cn.enaium.joe.util.CfrUtil;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;

import java.awt.*;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;

/**
* @author Enaium
Expand All @@ -21,39 +15,12 @@ public class DecompileTabPanel extends ClassNodeTabPanel {
public DecompileTabPanel(ClassNode classNode) {
super(classNode);
setLayout(new BorderLayout());
CodeArea codeArea = new CodeArea();
codeArea.setSyntaxEditingStyle("text/java");
CodeAreaPanel codeAreaPanel = new CodeAreaPanel();
codeAreaPanel.getTextArea().setSyntaxEditingStyle("text/java");
ASyncUtil.execute(() -> {
ClassFileSource cfs = new ClassFileSource() {
@Override
public void informAnalysisRelativePathDetail(String a, String b) {
}

@Override
public String getPossiblyRenamedPath(String path) {
return path;
}

@Override
public Pair<byte[], String> getClassFileContent(String path) throws IOException {
String name = path.substring(0, path.length() - 6);
if (name.equals(classNode.name)) {
ClassWriter classWriter = new ClassWriter(0);
classNode.accept(classWriter);
return Pair.make(classWriter.toByteArray(), name);
}
return null;
}

@Override
public Collection<String> addJar(String arg0) {
throw new RuntimeException();
}
};

codeArea.setText(new PluginRunner(new HashMap<>(), cfs).getDecompilationFor(classNode.name));
codeAreaPanel.getTextArea().setText(CfrUtil.getSource(classNode));
});
codeArea.setCaretPosition(0);
add(new RTextScrollPane(codeArea));
codeAreaPanel.getTextArea().setCaretPosition(0);
add(codeAreaPanel);
}
}
Loading