From fe9a1f05a1b1e5e2a15e447646c501902329e143 Mon Sep 17 00:00:00 2001 From: LaurieWired <123765654+LaurieWired@users.noreply.github.com> Date: Sun, 31 Dec 2023 15:16:08 -0800 Subject: [PATCH] Merge jadxecute code into new version --- .../plugins/jadxecute/AddCommentHelper.java | 260 +++++++++++++ .../jadxecute/ErrorHighlightingLinter.java | 5 + .../plugins/jadxecute/JadxecuteDialog.java | 350 ++++++++++++++++++ .../plugins/jadxecute/RenameObjectHelper.java | 257 +++++++++++++ .../gui/plugins/jadxecute/UserCodeLoader.java | 127 +++++++ .../src/main/java/jadx/gui/ui/MainWindow.java | 13 + .../src/main/resources/icons/ui/jadxecute.svg | 2 + .../jadxecute/codeexamples.properties | 324 ++++++++++++++++ .../jadxecute/codeexamples.properties.bak | 325 ++++++++++++++++ 9 files changed, 1663 insertions(+) create mode 100644 jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/AddCommentHelper.java create mode 100644 jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/ErrorHighlightingLinter.java create mode 100644 jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/JadxecuteDialog.java create mode 100644 jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/RenameObjectHelper.java create mode 100644 jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/UserCodeLoader.java create mode 100644 jadx-with-jadxecute/jadx-gui/src/main/resources/icons/ui/jadxecute.svg create mode 100644 jadx-with-jadxecute/jadx-gui/src/main/resources/jadxecute/codeexamples.properties create mode 100644 jadx-with-jadxecute/jadx-gui/src/main/resources/jadxecute/codeexamples.properties.bak diff --git a/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/AddCommentHelper.java b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/AddCommentHelper.java new file mode 100644 index 0000000..1c70726 --- /dev/null +++ b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/AddCommentHelper.java @@ -0,0 +1,260 @@ +package jadx.gui.plugins.jadxecute; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.api.JavaNode; +import jadx.api.data.ICodeComment; +import jadx.api.data.impl.JadxCodeComment; +import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; +import jadx.api.data.impl.JadxNodeRef; +import jadx.gui.treemodel.JClass; +import jadx.gui.ui.MainWindow; +import jadx.gui.ui.codearea.CodeArea; +import jadx.gui.settings.JadxProject; + +import jadx.api.JavaVariable; +import jadx.core.utils.exceptions.JadxRuntimeException; +import jadx.gui.jobs.TaskStatus; +import jadx.gui.treemodel.JMethod; +import jadx.gui.treemodel.JNode; +import jadx.gui.treemodel.JPackage; +import jadx.gui.ui.TabbedPane; +import jadx.gui.ui.codearea.ClassCodeContentPanel; +import jadx.gui.ui.panel.ContentPanel; +import jadx.gui.utils.CacheObject; +import jadx.gui.utils.JNodeCache; + +public class AddCommentHelper { + private static final Logger LOG = LoggerFactory.getLogger(AddCommentHelper.class); + + private MainWindow mainWindow; + private CacheObject cache; + private String newCommentString; + private JavaNode node; + private boolean updateComment; + + public AddCommentHelper(MainWindow mainWindow, JavaNode node) { + this.mainWindow = mainWindow; + this.cache = mainWindow.getCacheObject(); + this.node = node; + } + + // To be implemented + /* + public String addVariableComment(String newCommentString, JavaVariable javaVariable) { + this.newCommentString = newCommentString; + + ICodeComment blankComment = new JadxCodeComment(JadxNodeRef.forJavaNode(node), JadxCodeRef.forVar(javaVariable), ""); + if (blankComment == null) { + return "Failed to add comment"; + } + + ICodeComment existComment = searchForExistComment(blankComment); + if (existComment != null) { + this.updateComment = true; + apply(existComment, javaVariable); + } else { + this.updateComment = false; + apply(blankComment, javaVariable); + } + + return "Added comment in " + node.getFullName() + " for variable " + javaVariable.getName(); + } + */ + + public String addInstructionComment(String newCommentString, int commentSmaliOffset) { + this.newCommentString = newCommentString; + + ICodeComment blankComment = new JadxCodeComment(JadxNodeRef.forJavaNode(node), JadxCodeRef.forInsn(commentSmaliOffset), ""); + if (blankComment == null) { + return "Failed to add comment"; + } + + ICodeComment existComment = searchForExistComment(blankComment); + if (existComment != null) { + this.updateComment = true; + apply(existComment, commentSmaliOffset); + } else { + this.updateComment = false; + apply(blankComment, commentSmaliOffset); + } + + return "Added comment in " + node.getFullName() + " at offset " + commentSmaliOffset; + } + + private void apply(ICodeComment comment, int commentOffset) { + if (newCommentString.isEmpty()) { + if (updateComment) { + updateCommentsData(list -> list.removeIf(c -> c == comment)); + } + return; + } + + ICodeComment newComment = new JadxCodeComment(JadxNodeRef.forJavaNode(node), JadxCodeRef.forInsn(commentOffset), newCommentString); + if (updateComment) { + updateCommentsData(list -> { + list.remove(comment); + list.add(newComment); + }); + } else { + updateCommentsData(list -> list.add(newComment)); + } + } + + private void apply(ICodeComment comment, JavaVariable javaVariable) { + if (newCommentString.isEmpty()) { + if (updateComment) { + updateCommentsData(list -> list.removeIf(c -> c == comment)); + } + return; + } + + ICodeComment newComment = new JadxCodeComment(JadxNodeRef.forJavaNode(node), JadxCodeRef.forVar(javaVariable), newCommentString); + if (updateComment) { + updateCommentsData(list -> { + list.remove(comment); + list.add(newComment); + }); + } else { + updateCommentsData(list -> list.add(newComment)); + } + } + + private ICodeComment searchForExistComment(ICodeComment blankComment) { + try { + JadxProject project = mainWindow.getProject(); + JadxCodeData codeData = project.getCodeData(); + if (codeData == null || codeData.getComments().isEmpty()) { + return null; + } + for (ICodeComment comment : codeData.getComments()) { + if (Objects.equals(comment.getNodeRef(), blankComment.getNodeRef()) + && Objects.equals(comment.getCodeRef(), blankComment.getCodeRef())) { + return comment; + } + } + } catch (Exception e) { + LOG.error("Error searching for exists comment", e); + } + return null; + } + + private String updateCommentsData(Consumer> updater) { + JadxProject project = mainWindow.getProject(); + JadxCodeData codeData = project.getCodeData(); + + try { + if (codeData == null) { + codeData = new JadxCodeData(); + } + List list = new ArrayList<>(codeData.getComments()); + + updater.accept(list); + Collections.sort(list); + codeData.setComments(list); + project.setCodeData(codeData); + mainWindow.getWrapper().reloadCodeData(); + } catch (Exception e) { + LOG.error("Comment action failed", e); + } + try { + refreshState(); + } catch (Exception e) { + LOG.error("Failed to reload code", e); + } + + String retval = ""; + + for (ICodeComment comment : codeData.getComments()) { + retval += comment.getComment() + "\n"; + } + return retval; + } + + private void refreshState() { + mainWindow.getWrapper().reInitRenameVisitor(); + + JNodeCache nodeCache = cache.getNodeCache(); + JavaNode javaNode = node; + + List toUpdate = new ArrayList<>(); + if (javaNode != null) { + toUpdate.add(javaNode); + if (node instanceof JMethod) { + toUpdate.addAll(((JMethod) node).getJavaMethod().getOverrideRelatedMethods()); + } + } else { + throw new JadxRuntimeException("Unexpected node type: " + node); + } + Set updatedTopClasses = toUpdate + .stream() + .map(JavaNode::getTopParentClass) + .map(nodeCache::makeFrom) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + LOG.debug("Classes to update: {}", updatedTopClasses); + + refreshTabs(mainWindow.getTabbedPane(), updatedTopClasses); + + if (!updatedTopClasses.isEmpty()) { + mainWindow.getBackgroundExecutor().execute("Refreshing", + () -> refreshClasses(updatedTopClasses), + (status) -> { + if (status == TaskStatus.CANCEL_BY_MEMORY) { + mainWindow.showHeapUsageBar(); + } + if (node instanceof JPackage) { + mainWindow.getTreeRoot().update(); + } + mainWindow.reloadTree(); + }); + } + } + + private void refreshClasses(Set updatedTopClasses) { + if (updatedTopClasses.size() < 10) { + // small batch => reload + LOG.debug("Classes to reload: {}", updatedTopClasses.size()); + for (JClass cls : updatedTopClasses) { + try { + cls.reload(cache); + } catch (Exception e) { + LOG.error("Failed to reload class: {}", cls.getFullName(), e); + } + } + } else { + // big batch => unload + LOG.debug("Classes to unload: {}", updatedTopClasses.size()); + for (JClass cls : updatedTopClasses) { + try { + cls.unload(cache); + } catch (Exception e) { + LOG.error("Failed to unload class: {}", cls.getFullName(), e); + } + } + } + } + + private void refreshTabs(TabbedPane tabbedPane, Set updatedClasses) { + for (Map.Entry entry : tabbedPane.getOpenTabs().entrySet()) { + JClass rootClass = entry.getKey().getRootClass(); + if (updatedClasses.remove(rootClass)) { + ClassCodeContentPanel contentPanel = (ClassCodeContentPanel) entry.getValue(); + CodeArea codeArea = (CodeArea) contentPanel.getJavaCodePanel().getCodeArea(); + codeArea.refreshClass(); + } + } + } +} diff --git a/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/ErrorHighlightingLinter.java b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/ErrorHighlightingLinter.java new file mode 100644 index 0000000..d32047c --- /dev/null +++ b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/ErrorHighlightingLinter.java @@ -0,0 +1,5 @@ +package jadx.gui.plugins.jadxecute; + +public class ErrorHighlightingLinter { + +} diff --git a/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/JadxecuteDialog.java b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/JadxecuteDialog.java new file mode 100644 index 0000000..6819c2d --- /dev/null +++ b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/JadxecuteDialog.java @@ -0,0 +1,350 @@ +package jadx.gui.plugins.jadxecute; + +import java.awt.Container; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.WindowConstants; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.BoxLayout; +import javax.swing.border.EmptyBorder; + +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; +import org.fife.ui.rsyntaxtextarea.SyntaxConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; + +import javax.swing.JFileChooser; +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; + +import jadx.gui.jobs.BackgroundExecutor; +import jadx.gui.settings.JadxSettings; +import jadx.gui.ui.MainWindow; +import jadx.gui.utils.UiUtils; + +public class JadxecuteDialog extends JDialog { + private static final Logger LOG = LoggerFactory.getLogger(JadxecuteDialog.class); + + private final transient JadxSettings settings; + private final transient MainWindow mainWindow; + + private static final int DEFAULT_FONT_SIZE = 12; + + public JadxecuteDialog(MainWindow mainWindow) { + super(mainWindow, "JADXexecute", true); + this.mainWindow = mainWindow; + this.settings = mainWindow.getSettings(); + + initUI(); + } + + private void initUI() { + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + mainPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + + // Input and output code areas + JLabel codeInputDescription = new JLabel("Java Input"); + codeInputDescription.setFont(new Font(null, Font.BOLD, DEFAULT_FONT_SIZE)); + codeInputDescription.setPreferredSize(new Dimension(100, 16)); + RSyntaxTextArea codeInputArea = new RSyntaxTextArea(getDefaultCodeInputText()); + codeInputArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); + JScrollPane codeInputScrollPanel = new JScrollPane(codeInputArea); + codeInputScrollPanel.setPreferredSize(new Dimension(600, 200)); + + JLabel consoleOutputDescription = new JLabel("Console Output"); + consoleOutputDescription.setFont(new Font(null, Font.BOLD, DEFAULT_FONT_SIZE)); + consoleOutputDescription.setPreferredSize(new Dimension(80, 16)); + consoleOutputDescription.setBorder(new EmptyBorder(10, 0, 10, 0)); + JTextArea consoleOutputArea = new JTextArea(" "); + consoleOutputArea.setEditable(false); + consoleOutputArea.setBackground(Color.BLACK); + consoleOutputArea.setForeground(Color.WHITE); + + JScrollPane consoleOutputScrollPanel = new JScrollPane(consoleOutputArea); + consoleOutputScrollPanel.setPreferredSize(new Dimension(550, 100)); + + // Input and output code areas + JPanel codePanel = initCodePanel(codeInputDescription, codeInputScrollPanel, consoleOutputDescription, consoleOutputScrollPanel); + JPanel filePanel = initFilePanel(codeInputArea); + + // Buttons for running and closing the jadxecute dialog + JPanel bottomPan = new JPanel(); + bottomPan.setLayout(new BorderLayout()); + JPanel buttonPane = new JPanel(); + JLabel statusLabel = new JLabel("Status: Ready"); + statusLabel.setPreferredSize(new Dimension(100, 16)); + JButton run = new JButton("Run"); + JButton close = new JButton("Close"); + close.addActionListener(event -> close()); + + // run code + run.addActionListener(event -> runUserCode(codeInputArea, consoleOutputArea, statusLabel, run)); + + buttonPane.add(run); + buttonPane.add(close); + bottomPan.add(statusLabel, BorderLayout.WEST); + bottomPan.add(buttonPane, BorderLayout.CENTER); + getRootPane().setDefaultButton(close); + + // Add file panel and code panel to a sub panel + JPanel leftPanel = new JPanel(); + leftPanel.setLayout(new BorderLayout()); + leftPanel.add(codePanel, BorderLayout.CENTER); + + // Add left panel and buttons panel to main panel + mainPanel.add(leftPanel, BorderLayout.WEST); + mainPanel.add(bottomPan, BorderLayout.SOUTH); + + // Add code examples panel to east position of main panel + mainPanel.add(initCodeExamplesPanel(codeInputArea, filePanel), BorderLayout.EAST); + finishUI(mainPanel); + + } + + private JPanel initCodePanel(JLabel codeInputDescription, JScrollPane codeInputScrollPanel, + JLabel consoleOutputDescription, JScrollPane consoleOutputScrollPanel) { + + JPanel codePanel = new JPanel(); + codePanel.setLayout(new BoxLayout(codePanel, BoxLayout.Y_AXIS)); + codePanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + codeInputDescription.setAlignmentX(Component.LEFT_ALIGNMENT); + codeInputScrollPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + consoleOutputDescription.setAlignmentX(Component.LEFT_ALIGNMENT); + consoleOutputScrollPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + codePanel.add(codeInputDescription); + codePanel.add(codeInputScrollPanel); + codePanel.add(consoleOutputDescription); + codePanel.add(consoleOutputScrollPanel); + + return codePanel; + } + + private JPanel initCodeExamplesPanel(RSyntaxTextArea codeInputArea, JPanel filePanel) { + // Add options on the right to display different code examples + JPanel codeExamplesPanel = new JPanel(); + codeExamplesPanel.setLayout(new BorderLayout()); + codeExamplesPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + JLabel scriptSelection = new JLabel("Select Template:"); + scriptSelection.setFont(new Font(null, Font.BOLD, DEFAULT_FONT_SIZE)); + + JScrollPane exampleScrollPane = initCodeExamplesListeners(codeInputArea); + JPanel southExamplesPanel = new JPanel(); + southExamplesPanel.setLayout(new BorderLayout()); + southExamplesPanel.add(scriptSelection, BorderLayout.NORTH); + southExamplesPanel.add(exampleScrollPane, BorderLayout.CENTER); + codeExamplesPanel.add(filePanel, BorderLayout.NORTH); + codeExamplesPanel.add(southExamplesPanel, BorderLayout.CENTER); + codeExamplesPanel.setPreferredSize(new Dimension(300, 400)); + + return codeExamplesPanel; + } + + private void finishUI(JPanel mainPanel) { + Container contentPane = getContentPane(); + contentPane.add(mainPanel); + + setTitle("JADXecute"); + pack(); + + // set modal size + setSize(1024, 768); + + setLocationRelativeTo(mainPanel); + this.setModal(false); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + // setModalityType(ModalityType.APPLICATION_MODAL); + UiUtils.addEscapeShortCutToDispose(this); + } + + private JScrollPane initCodeExamplesListeners(RSyntaxTextArea codeInputArea) { + // Populate code examples + Map keyValuePairs = addCodeExamplesList(); + String[] keyArray = keyValuePairs.keySet().toArray(new String[0]); + JList exampleList = new JList<>(keyArray); + // loop through key-value pair and create action listener for each + for (int i = 0; i < keyArray.length; i++) { + exampleList.addListSelectionListener(event -> { + if (!event.getValueIsAdjusting()) { + codeInputArea.setText(keyValuePairs.get(exampleList.getSelectedValue())); + } + }); + } + + exampleList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + return new JScrollPane(exampleList); + } + + private JPanel initFilePanel(RSyntaxTextArea codeInputArea) { + JPanel filePanel = new JPanel(); + filePanel.setLayout(new BorderLayout()); + filePanel.setBorder(new EmptyBorder(10, 0, 10, 0)); + + JLabel fileLabel = new JLabel("Input Java File: "); + fileLabel.setFont(new Font(null, Font.BOLD, DEFAULT_FONT_SIZE)); + JTextField fileField = new JTextField(20); + JButton fileButton = new JButton("Browse"); + fileButton.addActionListener(e -> { + JFileChooser fileChooser = new JFileChooser(); + int returnValue = fileChooser.showOpenDialog(null); + if (returnValue == JFileChooser.APPROVE_OPTION) { + File selectedFile = fileChooser.getSelectedFile(); + fileField.setText(selectedFile.getAbsolutePath()); + + // Now read in the file and populate the Java input area + try (BufferedReader br = new BufferedReader(new FileReader(selectedFile))) { + String line; + StringBuilder sb = new StringBuilder(); + while ((line = br.readLine()) != null) { + sb.append(line); + sb.append(System.lineSeparator()); + } + codeInputArea.setText(sb.toString()); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + }); + filePanel.add(fileLabel, BorderLayout.NORTH); + filePanel.add(fileField, BorderLayout.CENTER); + filePanel.add(fileButton, BorderLayout.EAST); + + return filePanel; + } + + private Map addCodeExamplesList() { + String resourceName = "/jadxecute/codeexamples.properties"; + try (InputStream is = JadxecuteDialog.class.getResourceAsStream(resourceName); + BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { + + Map keyValuePairs = new HashMap<>(); + + String line; + String currentKey = null; + StringBuilder currentValue = new StringBuilder(); + + boolean readingValue = false; + + while ((line = reader.readLine()) != null) { + if (line.startsWith("###")) { + // store the current key-value pair + if (currentKey != null && currentValue.length() > 0) { + keyValuePairs.put(currentKey, currentValue.toString()); + currentValue = new StringBuilder(); + } + // update the current key + currentKey = line.substring(3); + } else if ((readingValue || line.startsWith("```")) && currentKey != null) { + if (!readingValue) { + readingValue = true; + } else if(readingValue && line.startsWith("```")) { + readingValue = false; + } else if (readingValue) { + // add the line to the current value + currentValue.append(line + "\n"); + } + } + } + + // store the last key-value pair + if (currentKey != null && currentValue.length() > 0) { + keyValuePairs.put(currentKey, currentValue.toString()); + } + + // print the key-value pairs + for (Map.Entry entry : keyValuePairs.entrySet()) { + LOG.info("Key: " + entry.getKey()); + LOG.info("Value: " + entry.getValue()); + } + + return keyValuePairs; + + } catch (IOException e) { + LOG.error("Failed reading codeexamples.properties file."); + e.printStackTrace(); + } + + return null; + } + + // Set the default text to populate the code input field + private String getDefaultCodeInputText() { + String defaultText = "import jadx.gui.ui.MainWindow;\n" + + "\n" + + "public class UserCodeClass {\n" + + " public static String userCodeMain(MainWindow mainWindow) {\n" + + " String jadxecuteOutput = \"Example output...\";\n" + + "\n" + + " // Your code goes here!\n" + + "\n" + + " return jadxecuteOutput;\n" + + " }\n" + + "}\n"; + return defaultText; + } + + private void runUserCode(RSyntaxTextArea codeInputArea, JTextArea consoleOutputArea, JLabel statusLabel, JButton run) { + statusLabel.setText("Status: Running..."); + statusLabel.setForeground(Color.ORANGE); + + BackgroundExecutor executor = mainWindow.getBackgroundExecutor(); + executor.execute("Jadexecute Task", () -> executeBackgroundTask(codeInputArea, consoleOutputArea, statusLabel), + analysisStatus -> finishTask(consoleOutputArea, statusLabel)); + } + + private void finishTask(JTextArea consoleOutputArea, JLabel statusLabel) { + if (consoleOutputArea.getText().contains("Java compilation error")) { + statusLabel.setText("Status: Error"); + statusLabel.setForeground(Color.RED); + } else { + statusLabel.setText("Status: Done"); + statusLabel.setForeground(Color.GREEN); + } + } + + private void executeBackgroundTask(RSyntaxTextArea codeInputArea, JTextArea consoleOutputArea, JLabel statusLabel) { + String codeInput = codeInputArea.getText(); + + try{ + UserCodeLoader userCodeLoader = new UserCodeLoader(); + consoleOutputArea.setText(userCodeLoader.runInputCode(codeInput, mainWindow)); + } catch (Exception e) { + e.printStackTrace(); + consoleOutputArea.setText("Error running user code"); + statusLabel.setText("Status: Error"); + statusLabel.setForeground(Color.RED); + } + } + + private void close() { + dispose(); + } + + @Override + public void dispose() { + settings.saveWindowPos(this); + super.dispose(); + } +} diff --git a/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/RenameObjectHelper.java b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/RenameObjectHelper.java new file mode 100644 index 0000000..f56b4cf --- /dev/null +++ b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/RenameObjectHelper.java @@ -0,0 +1,257 @@ +package jadx.gui.plugins.jadxecute; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.api.JavaClass; +import jadx.api.JavaMethod; +import jadx.api.JavaNode; +import jadx.api.JavaVariable; +import jadx.api.data.ICodeRename; +import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; +import jadx.api.data.impl.JadxCodeRename; +import jadx.api.data.impl.JadxNodeRef; +import jadx.core.deobf.NameMapper; +import jadx.core.utils.exceptions.JadxRuntimeException; +import jadx.gui.jobs.TaskStatus; +import jadx.gui.settings.JadxProject; +import jadx.gui.treemodel.JClass; +import jadx.gui.treemodel.JField; +import jadx.gui.treemodel.JMethod; +import jadx.gui.treemodel.JNode; +import jadx.gui.treemodel.JPackage; +import jadx.gui.treemodel.JVariable; +import jadx.gui.ui.MainWindow; +import jadx.gui.ui.TabbedPane; +import jadx.gui.ui.codearea.ClassCodeContentPanel; +import jadx.gui.ui.codearea.CodeArea; +import jadx.gui.ui.panel.ContentPanel; +import jadx.gui.utils.CacheObject; +import jadx.gui.utils.JNodeCache; + +public class RenameObjectHelper { + private static final Logger LOG = LoggerFactory.getLogger(RenameObjectHelper.class); + + private MainWindow mainWindow; + private CacheObject cache; + private JNode source; + private JNode node; + private String newObjectName; + private String oldObjectName; + + // Rename class, method, or field + public String renameObject(MainWindow mainWindow, JNode node, String newObjectName) { + this.oldObjectName = node.getName(); + this.mainWindow = mainWindow; + this.cache = mainWindow.getCacheObject(); + this.source = node; + this.node = replaceNode(node); + this.newObjectName = newObjectName; + rename(newObjectName); + + return "Renamed " + oldObjectName + " to " + newObjectName; + } + + // Need the methods from jadx.gui.ui.dialog.RenameDialog but they + // are private and let's not change all the access modifiers + private JNode replaceNode(JNode node) { + if (node instanceof JMethod) { + JavaMethod javaMethod = ((JMethod) node).getJavaMethod(); + if (javaMethod.isClassInit()) { + throw new JadxRuntimeException("Can't rename class init method: " + node); + } + if (javaMethod.isConstructor()) { + // rename class instead constructor + return node.getJParent(); + } + } + return node; + } + + private void rename(String newName) { + if (!checkNewName()) { + return; + } + try { + updateCodeRenames(set -> processRename(node, newName, set)); + refreshState(); + } catch (Exception e) { + LOG.error("Rename failed", e); + } + } + + private boolean checkNewName() { + String newName = newObjectName; + if (newName.isEmpty()) { + // use empty name to reset rename (revert to original) + return true; + } + + boolean valid = NameMapper.isValidIdentifier(newName); + return valid; + } + + private void processRename(JNode node, String newName, Set renames) { + JadxCodeRename rename = buildRename(node, newName, renames); + renames.remove(rename); + JavaNode javaNode = node.getJavaNode(); + if (javaNode != null) { + javaNode.removeAlias(); + } + if (!newName.isEmpty()) { + renames.add(rename); + } + } + + @NotNull + private JadxCodeRename buildRename(JNode node, String newName, Set renames) { + if (node instanceof JMethod) { + JavaMethod javaMethod = ((JMethod) node).getJavaMethod(); + List relatedMethods = javaMethod.getOverrideRelatedMethods(); + if (!relatedMethods.isEmpty()) { + for (JavaMethod relatedMethod : relatedMethods) { + renames.remove(new JadxCodeRename(JadxNodeRef.forMth(relatedMethod), "")); + } + } + return new JadxCodeRename(JadxNodeRef.forMth(javaMethod), newName); + } + if (node instanceof JField) { + return new JadxCodeRename(JadxNodeRef.forFld(((JField) node).getJavaField()), newName); + } + if (node instanceof JClass) { + return new JadxCodeRename(JadxNodeRef.forCls(((JClass) node).getCls()), newName); + } + if (node instanceof JPackage) { + return new JadxCodeRename(JadxNodeRef.forPkg(((JPackage) node).getFullName()), newName); + } + if (node instanceof JVariable) { + JavaVariable javaVar = ((JVariable) node).getJavaVarNode(); + return new JadxCodeRename(JadxNodeRef.forMth(javaVar.getMth()), JadxCodeRef.forVar(javaVar), newName); + } + throw new JadxRuntimeException("Failed to build rename node for: " + node); + } + + private void updateCodeRenames(Consumer> updater) { + JadxProject project = mainWindow.getProject(); + JadxCodeData codeData = project.getCodeData(); + if (codeData == null) { + codeData = new JadxCodeData(); + } + Set set = new HashSet<>(codeData.getRenames()); + updater.accept(set); + List list = new ArrayList<>(set); + Collections.sort(list); + codeData.setRenames(list); + project.setCodeData(codeData); + mainWindow.getWrapper().reloadCodeData(); + } + + private void refreshState() { + mainWindow.getWrapper().reInitRenameVisitor(); + + JNodeCache nodeCache = cache.getNodeCache(); + JavaNode javaNode = node.getJavaNode(); + + List toUpdate = new ArrayList<>(); + if (source != null && source != node) { + toUpdate.add(source.getJavaNode()); + } + if (javaNode != null) { + toUpdate.add(javaNode); + toUpdate.addAll(javaNode.getUseIn()); + if (node instanceof JMethod) { + toUpdate.addAll(((JMethod) node).getJavaMethod().getOverrideRelatedMethods()); + } + } else if (node instanceof JPackage) { + processPackage(toUpdate); + } else { + throw new JadxRuntimeException("Unexpected node type: " + node); + } + Set updatedTopClasses = toUpdate + .stream() + .map(JavaNode::getTopParentClass) + .map(nodeCache::makeFrom) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + LOG.debug("Classes to update: {}", updatedTopClasses); + + refreshTabs(mainWindow.getTabbedPane(), updatedTopClasses); + + if (!updatedTopClasses.isEmpty()) { + mainWindow.getBackgroundExecutor().execute("Refreshing", + () -> refreshClasses(updatedTopClasses), + (status) -> { + if (status == TaskStatus.CANCEL_BY_MEMORY) { + mainWindow.showHeapUsageBar(); + } + if (node instanceof JPackage) { + mainWindow.getTreeRoot().update(); + } + mainWindow.reloadTree(); + }); + } + } + + private void processPackage(List toUpdate) { + String rawFullPkg = ((JPackage) node).getFullName(); + String rawFullPkgDot = rawFullPkg + "."; + for (JavaClass cls : mainWindow.getWrapper().getClasses()) { + String clsPkg = cls.getClassNode().getClassInfo().getPackage(); + // search all classes in package + if (clsPkg.equals(rawFullPkg) || clsPkg.startsWith(rawFullPkgDot)) { + toUpdate.add(cls); + // also include all usages (for import fix) + toUpdate.addAll(cls.getUseIn()); + } + } + } + + private void refreshClasses(Set updatedTopClasses) { + if (updatedTopClasses.size() < 10) { + // small batch => reload + LOG.debug("Classes to reload: {}", updatedTopClasses.size()); + for (JClass cls : updatedTopClasses) { + try { + cls.reload(cache); + } catch (Exception e) { + LOG.error("Failed to reload class: {}", cls.getFullName(), e); + } + } + } else { + // big batch => unload + LOG.debug("Classes to unload: {}", updatedTopClasses.size()); + for (JClass cls : updatedTopClasses) { + try { + cls.unload(cache); + } catch (Exception e) { + LOG.error("Failed to unload class: {}", cls.getFullName(), e); + } + } + } + } + + private void refreshTabs(TabbedPane tabbedPane, Set updatedClasses) { + for (Map.Entry entry : tabbedPane.getOpenTabs().entrySet()) { + JClass rootClass = entry.getKey().getRootClass(); + if (updatedClasses.remove(rootClass)) { + ClassCodeContentPanel contentPanel = (ClassCodeContentPanel) entry.getValue(); + CodeArea codeArea = (CodeArea) contentPanel.getJavaCodePanel().getCodeArea(); + codeArea.refreshClass(); + } + } + } +} + diff --git a/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/UserCodeLoader.java b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/UserCodeLoader.java new file mode 100644 index 0000000..cceae5c --- /dev/null +++ b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/plugins/jadxecute/UserCodeLoader.java @@ -0,0 +1,127 @@ +package jadx.gui.plugins.jadxecute; + +import javax.tools.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import jadx.gui.ui.MainWindow; + +public class UserCodeLoader { + public String runInputCode(String program, MainWindow mainWindow) throws Exception { + // Compile the input code + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + JavaFileObject compilationUnit = new StringJavaFileObject("UserCodeClass", program); + SimpleJavaFileManager fileManager = new SimpleJavaFileManager(compiler.getStandardFileManager(diagnostics, null, null)); + JavaCompiler.CompilationTask compilationTask = compiler.getTask(null, fileManager, diagnostics, null, null, Arrays.asList(compilationUnit)); + boolean success = compilationTask.call(); + CompiledClassLoader classLoader = new CompiledClassLoader(fileManager.getGeneratedOutputFiles()); + + // Check for compilation errors + if (!success) { + // Append error message to a string + // Need to using html tags for JPanel to respect newlines + String errorMessage = "Java compilation error:\n"; + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + errorMessage += diagnostic.getMessage(null) + "\n"; + } + return errorMessage; + } + + // Input code must implement method "public static String userCodeMain(MainWindow mainWindow)" + // Return string will be console output + // This method can invoke other methods of user code placed inside + Class userCodeClass = classLoader.loadClass("UserCodeClass"); + Method method = userCodeClass.getMethod("userCodeMain", MainWindow.class); + + return (String) method.invoke(null, mainWindow); + } + + private static class StringJavaFileObject extends SimpleJavaFileObject { + private final String code; + + public StringJavaFileObject(String name, String code) { + super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), + Kind.SOURCE); + this.code = code; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return code; + } + } + + private static class ClassJavaFileObject extends SimpleJavaFileObject { + private final ByteArrayOutputStream outputStream; + private final String className; + + protected ClassJavaFileObject(String className, Kind kind) { + super(URI.create("mem:///" + className.replace('.', '/') + kind.extension), kind); + this.className = className; + outputStream = new ByteArrayOutputStream(); + } + + @Override + public OutputStream openOutputStream() throws IOException { + return outputStream; + } + + public byte[] getBytes() { + return outputStream.toByteArray(); + } + + public String getClassName() { + return className; + } + } + + private static class SimpleJavaFileManager extends ForwardingJavaFileManager { + private final List outputFiles; + + protected SimpleJavaFileManager(JavaFileManager fileManager) { + super(fileManager); + outputFiles = new ArrayList(); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { + ClassJavaFileObject file = new ClassJavaFileObject(className, kind); + outputFiles.add(file); + return file; + } + + public List getGeneratedOutputFiles() { + return outputFiles; + } + } + + private static class CompiledClassLoader extends ClassLoader { + private final List files; + + private CompiledClassLoader(List files) { + this.files = files; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + Iterator itr = files.iterator(); + while (itr.hasNext()) { + ClassJavaFileObject file = itr.next(); + if (file.getClassName().equals(name)) { + itr.remove(); + byte[] bytes = file.getBytes(); + return super.defineClass(name, bytes, 0, bytes.length); + } + } + return super.findClass(name); + } + } +} \ No newline at end of file diff --git a/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 9f4b51b..4f35509 100644 --- a/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-with-jadxecute/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -98,6 +98,7 @@ import jadx.gui.jobs.TaskStatus; import jadx.gui.plugins.mappings.MappingExporter; import jadx.gui.plugins.quark.QuarkDialog; +import jadx.gui.plugins.jadxecute.JadxecuteDialog; import jadx.gui.settings.JadxProject; import jadx.gui.settings.JadxSettings; import jadx.gui.settings.JadxSettingsWindow; @@ -170,6 +171,7 @@ public class MainWindow extends JFrame { private static final ImageIcon ICON_BACK = UiUtils.openSvgIcon("ui/left"); private static final ImageIcon ICON_FORWARD = UiUtils.openSvgIcon("ui/right"); private static final ImageIcon ICON_QUARK = UiUtils.openSvgIcon("ui/quark"); + private static final ImageIcon ICON_JADXECUTE = UiUtils.openSvgIcon("ui/jadxecute"); private static final ImageIcon ICON_PREF = UiUtils.openSvgIcon("ui/settings"); private static final ImageIcon ICON_DEOBF = UiUtils.openSvgIcon("ui/helmChartLock"); private static final ImageIcon ICON_DECOMPILE_ALL = UiUtils.openSvgIcon("ui/runAll"); @@ -1068,6 +1070,14 @@ public void actionPerformed(ActionEvent e) { }; quarkAction.putValue(Action.SHORT_DESCRIPTION, "Quark Engine"); + Action jadxecuteAction = new AbstractAction("JADXecute", ICON_JADXECUTE) { + @Override + public void actionPerformed(ActionEvent e) { + new JadxecuteDialog(MainWindow.this).setVisible(true); + } + }; + jadxecuteAction.putValue(Action.SHORT_DESCRIPTION, "JADXecute"); + Action openDeviceAction = new AbstractAction(NLS.str("debugger.process_selector"), ICON_DEBUGGER) { @Override public void actionPerformed(ActionEvent e) { @@ -1122,6 +1132,7 @@ public void actionPerformed(ActionEvent e) { tools.add(decompileAllAction); tools.add(deobfMenuItem); tools.add(quarkAction); + tools.add(jadxecuteAction); tools.add(openDeviceAction); JMenu help = new JMenu(NLS.str("menu.help")); @@ -1177,6 +1188,7 @@ public void actionPerformed(ActionEvent e) { toolbar.addSeparator(); toolbar.add(deobfToggleBtn); toolbar.add(quarkAction); + toolbar.add(jadxecuteAction); toolbar.add(openDeviceAction); toolbar.addSeparator(); toolbar.add(logAction); @@ -1202,6 +1214,7 @@ public void actionPerformed(ActionEvent e) { decompileAllAction.setEnabled(loaded); deobfAction.setEnabled(loaded); quarkAction.setEnabled(loaded); + jadxecuteAction.setEnabled(loaded); return false; }); } diff --git a/jadx-with-jadxecute/jadx-gui/src/main/resources/icons/ui/jadxecute.svg b/jadx-with-jadxecute/jadx-gui/src/main/resources/icons/ui/jadxecute.svg new file mode 100644 index 0000000..e845f7b --- /dev/null +++ b/jadx-with-jadxecute/jadx-gui/src/main/resources/icons/ui/jadxecute.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/jadx-with-jadxecute/jadx-gui/src/main/resources/jadxecute/codeexamples.properties b/jadx-with-jadxecute/jadx-gui/src/main/resources/jadxecute/codeexamples.properties new file mode 100644 index 0000000..b6f3c91 --- /dev/null +++ b/jadx-with-jadxecute/jadx-gui/src/main/resources/jadxecute/codeexamples.properties @@ -0,0 +1,324 @@ +### Blank Template +``` +import jadx.gui.ui.MainWindow; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + + // Your code goes here! + + return jadxecuteOutput; + } +} +``` + +### JadxWrapper example +``` +import jadx.gui.ui.MainWindow; +import jadx.gui.JadxWrapper; +import jadx.api.JavaClass; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = "Example output..."; + + JadxWrapper wrapper = mainWindow.getWrapper(); + + // Here's an example of using the wrapper object. Update here! + for (JavaClass cls : wrapper.getClasses()) { + jadxecuteOutput += cls.getName() + "\n"; + } + + return jadxecuteOutput; + } +} +``` + +### Print all class names +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + jadxecuteOutput += cls.getFullName() + "\n"; + } + + return jadxecuteOutput; + } +} +``` + +### Print all method names +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + ClassNode selectedClassNode = null; + String searchClassName = "exampleClassName"; // Update this + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + jadxecuteOutput += "Found class: " + cls.getFullName() + "\n"; + selectedClassNode = cls; + } + } + + if (selectedClassNode != null) { + jadxecuteOutput += "Methods:\n"; + + // Print all methods in the selected class + for (MethodNode method : selectedClassNode.getMethods()) { + jadxecuteOutput += method.getAlias() + "\n"; // Use the alias since this includes user updates + } + } else { + jadxecuteOutput += "Could not find class " + searchClassName; + } + + return jadxecuteOutput; + } +} +``` + +### Print all field names +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.FieldNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + ClassNode selectedClassNode = null; + String searchClassName = "exampleClassName"; // Update this + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + jadxecuteOutput += "Found class: " + cls.getFullName() + "\n"; + selectedClassNode = cls; + } + } + + if (selectedClassNode != null) { + jadxecuteOutput += "Fields:\n"; + + // Print all field in the selected class + for (FieldNode field : selectedClassNode.getFields()) { + jadxecuteOutput += field.getAlias() + "\n"; // Use the alias since this includes user updates + } + } else { + jadxecuteOutput += "Could not find class " + searchClassName; + } + + return jadxecuteOutput; + } +} +``` + +### Print classes inheriting from class +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; +import jadx.core.dex.instructions.args.ArgType; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + String searchClassName = "IntentService"; // Update this + ArgType superClassType = null; + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + + jadxecuteOutput += "Classes extending " + searchClassName + ":\n"; + + for (ClassNode cls : classes) { + superClassType = cls.getSuperClass(); + + if (superClassType != null && superClassType.toString().contains(searchClassName)) { + jadxecuteOutput += cls.getFullName() + "\n"; + } + } + + return jadxecuteOutput; + } +} +``` + +### Print classes containing substring +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + String searchClassName = "exampleClassName"; // Update this + + // Loop through all classes and add desired name to return output + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + + // Example: finds all user-renamed classes renamed like "mw_MyClassA" + if (cls.getFullName().contains(searchClassName)) { + jadxecuteOutput += cls.getFullName() + "\n"; + } + } + + return jadxecuteOutput; + } +} +``` + +### Rename a class +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; +import jadx.gui.treemodel.JClass; +import jadx.api.JavaClass; +import jadx.gui.plugins.jadxecute.RenameObjectHelper; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + String searchClassName = "exampleClassName"; // Update this + + // Find desired class + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + RenameObjectHelper renameObjectHelper = new RenameObjectHelper(); + JClass jclass = new JClass(cls.getJavaNode()); + + // Rename found class to desired name + jadxecuteOutput += renameObjectHelper.renameObject(mainWindow, jclass, "newClassName"); + + // Optionally return here or you could add functionality to change all + // matched objects to different names and return out of the loop + return jadxecuteOutput; + } + } + + return jadxecuteOutput; + } +} +``` + +### Print Java code in a class +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; +import jadx.gui.treemodel.JClass; +import jadx.api.JavaClass; +import jadx.gui.plugins.jadxecute.RenameObjectHelper; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + String searchClassName = "exampleClassName"; // Update this + + // Find desired class + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + RenameObjectHelper renameObjectHelper = new RenameObjectHelper(); + + jadxecuteOutput += "Found class " + searchClassName + ":\n\n"; + jadxecuteOutput += cls.getJavaNode().getCodeInfo().toString(); + + return jadxecuteOutput; + } + } + + return jadxecuteOutput; + } +} +``` + +### Insert a new instruction comment +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; +import jadx.gui.plugins.jadxecute.AddCommentHelper; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + ClassNode selectedClassNode = null; + MethodNode selectedMethodNode = null; + String searchClassName = "exampleClassName"; // Update this + String searchMethodName = "exampleMethodName"; // Update this + String commentToAdd = "This is a new comment!"; // Update this + int smaliInstructionIndex = 0; // Update this + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + jadxecuteOutput += "Found class: " + cls.getFullName() + "\n"; + selectedClassNode = cls; + } + } + + if (selectedClassNode != null) { + for (MethodNode method : selectedClassNode.getMethods()) { + if (method.getAlias().contains(searchMethodName)) { + jadxecuteOutput += "Found method: " + method.getAlias() + "\n"; + selectedMethodNode = method; + } + } + + // Add the comment if the method was found + if (selectedMethodNode != null) { + AddCommentHelper addCommentHelper = new AddCommentHelper(mainWindow, selectedMethodNode.getJavaNode()); + jadxecuteOutput += addCommentHelper.addInstructionComment(commentToAdd, smaliInstructionIndex); + } + + } else { + jadxecuteOutput += "Could not find class " + searchClassName; + } + + return jadxecuteOutput; + } +} +``` + diff --git a/jadx-with-jadxecute/jadx-gui/src/main/resources/jadxecute/codeexamples.properties.bak b/jadx-with-jadxecute/jadx-gui/src/main/resources/jadxecute/codeexamples.properties.bak new file mode 100644 index 0000000..c6e9736 --- /dev/null +++ b/jadx-with-jadxecute/jadx-gui/src/main/resources/jadxecute/codeexamples.properties.bak @@ -0,0 +1,325 @@ +### Blank Template +``` +import jadx.gui.ui.MainWindow; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + + // Your code goes here! + + return jadxecuteOutput; + } +} +``` + +### JadxWrapper example +``` +import jadx.gui.ui.MainWindow; +import jadx.gui.JadxWrapper; +import jadx.api.JavaClass; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = "Example output..."; + + JadxWrapper wrapper = mainWindow.getWrapper(); + + // Here's an example of using the wrapper object. Update here! + for (JavaClass cls : wrapper.getClasses()) { + jadxecuteOutput += cls.getName() + "\n"; + } + + return jadxecuteOutput; + } +} +``` + +### Print all class names +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + jadxecuteOutput += cls.getFullName() + "\n"; + } + + return jadxecuteOutput; + } +} +``` + +### Print all method names +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + ClassNode selectedClassNode = null; + String searchClassName = "exampleClassName"; // Update this + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + jadxecuteOutput += "Found class: " + cls.getFullName() + "\n"; + selectedClassNode = cls; + } + } + + if (selectedClassNode != null) { + jadxecuteOutput += "Methods:\n"; + + // Print all methods in the selected class + for (MethodNode method : selectedClassNode.getMethods()) { + jadxecuteOutput += method.getAlias() + "\n"; // Use the alias since this includes user updates + } + } else { + jadxecuteOutput += "Could not find class " + searchClassName; + } + + return jadxecuteOutput; + } +} +``` + +### Print all field names +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.FieldNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + ClassNode selectedClassNode = null; + String searchClassName = "exampleClassName"; // Update this + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + jadxecuteOutput += "Found class: " + cls.getFullName() + "\n"; + selectedClassNode = cls; + } + } + + if (selectedClassNode != null) { + jadxecuteOutput += "Fields:\n"; + + // Print all field in the selected class + for (FieldNode field : selectedClassNode.getFields()) { + jadxecuteOutput += field.getAlias() + "\n"; // Use the alias since this includes user updates + } + } else { + jadxecuteOutput += "Could not find class " + searchClassName; + } + + return jadxecuteOutput; + } +} +``` + +### Print classes inheriting from class +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; +import jadx.core.dex.instructions.args.ArgType; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + String searchClassName = "IntentService"; // Update this + ArgType superClassType = null; + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + + jadxecuteOutput += "Classes extending " + searchClassName + ":\n"; + + for (ClassNode cls : classes) { + superClassType = cls.getSuperClass(); + + if (superClassType != null && superClassType.toString().contains(searchClassName)) { + jadxecuteOutput += cls.getFullName() + "\n"; + } + } + + return jadxecuteOutput; + } +} +``` + +### Print classes containing substring +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + String searchClassName = "exampleClassName"; // Update this + + // Loop through all classes and add desired name to return output + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + + // Example: finds all user-renamed classes renamed like "mw_MyClassA" + if (cls.getFullName().contains(searchClassName)) { + jadxecuteOutput += cls.getFullName() + "\n"; + } + } + + return jadxecuteOutput; + } +} +``` + +### Rename a class +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; +import jadx.gui.treemodel.JClass; +import jadx.api.JavaClass; +import jadx.gui.plugins.jadxecute.RenameObjectHelper; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + String searchClassName = "exampleClassName"; // Update this + + // Find desired class + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + RenameObjectHelper renameObjectHelper = new RenameObjectHelper(); + JClass jclass = new JClass(cls.getJavaNode()); + + // Rename found class to desired name + jadxecuteOutput += renameObjectHelper.renameObject(mainWindow, jclass, "newClassName"); + + // Optionally return here or you could add functionality to change all + // matched objects to different names and return out of the loop + return jadxecuteOutput; + } + } + + return jadxecuteOutput; + } +} +``` + +### Print Java code in a class +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; +import jadx.gui.treemodel.JClass; +import jadx.api.JavaClass; +import jadx.gui.plugins.jadxecute.RenameObjectHelper; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + String searchClassName = "exampleClassName"; // Update this + + // Find desired class + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + RenameObjectHelper renameObjectHelper = new RenameObjectHelper(); + + jadxecuteOutput += "Found class " + searchClassName + ":\n\n"; + jadxecuteOutput += cls.getJavaNode().getCodeInfo().toString(); + + return jadxecuteOutput; + } + } + + return jadxecuteOutput; + } +} +``` + +### Insert a new instruction comment +``` +import jadx.gui.ui.MainWindow; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; +import java.util.List; +import jadx.gui.plugins.jadxecute.AddCommentHelper; + +public class UserCodeClass { + public static String userCodeMain(MainWindow mainWindow) { + String jadxecuteOutput = ""; + ClassNode selectedClassNode = null; + MethodNode selectedMethodNode = null; + String searchClassName = "exampleClassName"; // Update this + String searchMethodName = "exampleMethodName"; // Update this + String commentToAdd = "This is a new comment!"; // Update this + int smaliInstructionIndex = 0; // Update this + + // Add all strings to the jadxecute output to be printed + RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); + List classes = root.getClasses(); + for (ClassNode cls : classes) { + if (cls.getFullName().contains(searchClassName)) { + jadxecuteOutput += "Found class: " + cls.getFullName() + "\n"; + selectedClassNode = cls; + } + } + + if (selectedClassNode != null) { + for (MethodNode method : selectedClassNode.getMethods()) { + if (method.getAlias().contains(searchMethodName)) { + jadxecuteOutput += "Found method: " + method.getAlias() + "\n"; + selectedMethodNode = method; + } + } + + // Add the comment if the method was found + if (selectedMethodNode != null) { + AddCommentHelper addCommentHelper = new AddCommentHelper(mainWindow, selectedMethodNode.getJavaNode()); + jadxecuteOutput += addCommentHelper.addInstructionComment(commentToAdd, smaliInstructionIndex); + } + + } else { + jadxecuteOutput += "Could not find class " + searchClassName; + } + + return jadxecuteOutput; + } +} + +``` +