diff --git a/src/main/java/bindiffhelper/BinDiffHelperPlugin.java b/src/main/java/bindiffhelper/BinDiffHelperPlugin.java index 23d5b27..7f97200 100644 --- a/src/main/java/bindiffhelper/BinDiffHelperPlugin.java +++ b/src/main/java/bindiffhelper/BinDiffHelperPlugin.java @@ -31,6 +31,7 @@ import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; import ghidra.app.services.CodeViewerService; +import ghidra.app.util.Option; import ghidra.app.util.exporter.Exporter; import ghidra.framework.model.DomainFile; import ghidra.framework.plugintool.PluginInfo; @@ -63,12 +64,14 @@ public class BinDiffHelperPlugin extends ProgramPlugin { Exporter binExportExporter; String binDiffBinary; String diffCommand; + boolean enableNamespace; protected String defaultBinPath; protected String defaultDiffCommand; Program program; public final static String BDBINPROPERTY = "de.ubfx.bindiffhelper.bindiffbinary"; public final static String DIFFCOMMAND = "de.ubfx.bindiffhelper.diffCommand"; + public final static String ENABLENAMESPACE = "de.ubfx.bindiffhelper.enableNamespace"; /** * Plugin constructor. * @@ -113,6 +116,7 @@ public BinDiffHelperPlugin(PluginTool tool) { binDiffBinary = Preferences.getProperty(BDBINPROPERTY, defaultBinPath); diffCommand = Preferences.getProperty(DIFFCOMMAND, defaultDiffCommand); + enableNamespace = Boolean.parseBoolean(Preferences.getProperty(ENABLENAMESPACE, "false")); provider = new BinDiffHelperProvider(this, this.getCurrentProgram()); provider.setTitle("BinDiffHelper"); @@ -130,6 +134,14 @@ File binExportDomainFile(DomainFile df) if (dof instanceof Program) { out = File.createTempFile(df.getName() + "_bdh", ".BinExport"); + + if (enableNamespace) { + binExportExporter.setOptions( + List.of( + new Option("Prepend Namespace to Function Names", true) + ) + ); + } if (binExportExporter.export(out, dof, null, TaskMonitor.DUMMY) == false) { @@ -182,26 +194,30 @@ public void callBinDiff(DomainFile df, Consumer callback) Msg.debug(this, "printing BD output for cmd: " + Arrays.toString(cmd)); Process p = null; try { - p = Runtime.getRuntime().exec(cmd); - var i = new BufferedReader(new InputStreamReader(p.getInputStream())); - var stderr = p.getErrorStream(); - while (!p.waitFor(1, TimeUnit.SECONDS)) { - // Empty stderr buffer - stderr.skip(stderr.available()); - - while (true) - { - String line = i.readLine(); - - if (line == null) - break; - - Msg.debug(this, ">" + line); + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.redirectErrorStream(true); + p = pb.start(); + + Process finalP = p; + Thread outputReader = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(finalP.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + Msg.debug(this, "> " + line); + } + } catch (IOException e) { + e.printStackTrace(); } - + }); + outputReader.start(); + + while (!p.waitFor(1, TimeUnit.SECONDS)) { d.checkCanceled(); } - + + outputReader.join(); + int exitCode = p.exitValue(); + Msg.debug(this, "Process exited with code: " + exitCode); Msg.debug(this, "end of output"); } catch (IOException | InterruptedException e) { @@ -293,6 +309,12 @@ public void updateDiffCommand(String cmd) Preferences.setProperty(DIFFCOMMAND, cmd); } + public void updateEnableNamespace(boolean enable) + { + enableNamespace = enable; + Preferences.setProperty(ENABLENAMESPACE, Boolean.toString(enable)); + } + @Override public void init() { super.init(); diff --git a/src/main/java/bindiffhelper/BinDiffHelperProvider.java b/src/main/java/bindiffhelper/BinDiffHelperProvider.java index 4e786fb..29634f0 100644 --- a/src/main/java/bindiffhelper/BinDiffHelperProvider.java +++ b/src/main/java/bindiffhelper/BinDiffHelperProvider.java @@ -313,7 +313,7 @@ protected void doDiffWork() { Set secFnSet = secondary.beFile.getFunctionAddressSet(); Set commonSecFnSet = new TreeSet(); - ctm = new ComparisonTableModel(); + ctm = new ComparisonTableModel(plugin.enableNamespace); try { Statement stmt = conn.createStatement(); diff --git a/src/main/java/bindiffhelper/ComparisonTableModel.java b/src/main/java/bindiffhelper/ComparisonTableModel.java index cde1513..e6bd8c9 100644 --- a/src/main/java/bindiffhelper/ComparisonTableModel.java +++ b/src/main/java/bindiffhelper/ComparisonTableModel.java @@ -19,8 +19,11 @@ public class ComparisonTableModel extends AbstractTableModel { private List data; - public ComparisonTableModel() { + private boolean showNamespace = false; + + public ComparisonTableModel(boolean showNamespace) { data = new ArrayList(); + this.showNamespace = showNamespace; } public void addEntry(Entry e) { @@ -54,8 +57,21 @@ public Object getValueAt(int row, int col) { return "0x" + Long.toHexString(e.primaryAddress.getUnsignedOffset()); return ""; case 2: - if (e.primaryFunctionSymbol != null) - return e.primaryFunctionSymbol.getName(); + if (e.primaryFunctionSymbol != null){ + if (showNamespace) { + var functionNameComponents = new ArrayList(); + var parentNamespace = e.primaryFunctionSymbol.getParentNamespace(); + while (parentNamespace != null && !"Global".equals(parentNamespace.getName())) { + functionNameComponents.add(0, parentNamespace.getName()); + parentNamespace = parentNamespace.getParentNamespace(); + } + // Add the name of the function as the last component. + functionNameComponents.add(e.primaryFunctionSymbol.getName()); + return String.join("::", functionNameComponents); + } else { + return e.primaryFunctionSymbol.getName(); + } + } return "No Symbol"; case 3: if (e.primaryFunctionNameDb != null) diff --git a/src/main/java/bindiffhelper/ImportFunctionNamesAction.java b/src/main/java/bindiffhelper/ImportFunctionNamesAction.java index 876c1d7..99bc547 100644 --- a/src/main/java/bindiffhelper/ImportFunctionNamesAction.java +++ b/src/main/java/bindiffhelper/ImportFunctionNamesAction.java @@ -8,7 +8,10 @@ import docking.action.DockingAction; import docking.action.MenuData; import docking.action.ToolBarData; +import ghidra.program.model.listing.Program; +import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.SourceType; +import ghidra.program.model.symbol.SymbolTable; import ghidra.util.HTMLUtilities; import ghidra.util.Msg; import resources.ResourceManager; @@ -32,6 +35,31 @@ protected boolean shouldImportEntry(ComparisonTableModel.Entry e) { return false; } + private Namespace getOrCreateNamespace(Program program, String namespacePath) throws Exception { + + SymbolTable symbolTable = program.getSymbolTable(); + Namespace currentNamespace = program.getGlobalNamespace(); + + if (namespacePath == null || namespacePath.isEmpty()) { + return currentNamespace; + } + + String[] namespaces = namespacePath.split("::"); + for (String namespaceName : namespaces) { + ghidra.program.model.symbol.Namespace nextNamespace = symbolTable.getNamespace( + namespaceName, currentNamespace); + + if (nextNamespace == null) { + nextNamespace = symbolTable.createNameSpace( + currentNamespace, namespaceName, SourceType.IMPORTED); + } + + currentNamespace = nextNamespace; + } + + return currentNamespace; + } + @Override public void actionPerformed(ActionContext arg0) { int trans = plugin.program.startTransaction("Rename functions"); @@ -46,7 +74,24 @@ public void actionPerformed(ActionContext arg0) { Exception exp = null; try { transformation = e.primaryFunctionSymbol.getName() + " -> " + e.secondaryFunctionName; - e.primaryFunctionSymbol.setName(e.secondaryFunctionName, SourceType.IMPORTED); + + if (e.secondaryFunctionName.contains("::")) { + String[] parts = e.secondaryFunctionName.split("::"); + String methodName = parts[parts.length - 1]; + + StringBuilder namespacePath = new StringBuilder(); + for (int i = 0; i < parts.length - 1; i++) { + if (i > 0) namespacePath.append("::"); + namespacePath.append(parts[i]); + } + + Namespace namespace = getOrCreateNamespace(plugin.program, namespacePath.toString()); + + e.primaryFunctionSymbol.setNameAndNamespace(methodName, namespace, SourceType.IMPORTED); + } else { + e.primaryFunctionSymbol.setName(e.secondaryFunctionName, SourceType.IMPORTED); + } + e.do_import = false; } catch (Exception ex) { exp = ex; diff --git a/src/main/java/bindiffhelper/SettingsDialog.java b/src/main/java/bindiffhelper/SettingsDialog.java index aaeb788..9845e0c 100644 --- a/src/main/java/bindiffhelper/SettingsDialog.java +++ b/src/main/java/bindiffhelper/SettingsDialog.java @@ -3,11 +3,7 @@ import java.awt.BorderLayout; import java.io.IOException; -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextField; +import javax.swing.*; import docking.DialogComponentProvider; import docking.widgets.filechooser.GhidraFileChooserPanel; @@ -18,6 +14,7 @@ public class SettingsDialog extends DialogComponentProvider { protected BinDiffHelperPlugin plugin; protected GhidraFileChooserPanel fileChooserPanel; private JTextField customTextField; + private JCheckBox enableNamespaceCheckBox; public SettingsDialog(BinDiffHelperPlugin plugin) { super("Settings"); @@ -51,7 +48,18 @@ public SettingsDialog(BinDiffHelperPlugin plugin) { diffCommandTextField.add(customTextField, BorderLayout.CENTER); diffCommandPanel.add(diffCommandTextField, BorderLayout.CENTER); - panel.add(diffCommandPanel, BorderLayout.SOUTH); + + JPanel namespacePanel = new JPanel(new BorderLayout()); + namespacePanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + enableNamespaceCheckBox = new JCheckBox("Enable export Namespace"); + enableNamespaceCheckBox.setSelected(plugin.enableNamespace); + namespacePanel.add(enableNamespaceCheckBox, BorderLayout.CENTER); + + JPanel southContainer = new JPanel(); + southContainer.setLayout(new BoxLayout(southContainer, BoxLayout.Y_AXIS)); + southContainer.add(diffCommandPanel); + southContainer.add(namespacePanel); + panel.add(southContainer, BorderLayout.SOUTH); addWorkPanel(panel); @@ -76,6 +84,7 @@ protected void okCallback() { } plugin.updateDiffCommand(customTextField.getText()); + plugin.updateEnableNamespace(enableNamespaceCheckBox.isSelected()); plugin.provider.generateWarnings(); } }