Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
package ai.reveng.toolkit.ghidra.binarysimilarity.ui.about;

import ai.reveng.toolkit.ghidra.binarysimilarity.ui.dialog.RevEngDialogComponentProvider;
import ai.reveng.toolkit.ghidra.plugins.ReaiPluginPackage;
import docking.DialogComponentProvider;
import ghidra.framework.plugintool.PluginTool;
import resources.ResourceManager;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import ai.reveng.invoker.Configuration;

/**
* Shows a dialog with about information.
*/
public class AboutDialog extends DialogComponentProvider {
private final PluginTool tool;

public class AboutDialog extends RevEngDialogComponentProvider {
public AboutDialog(PluginTool tool) {
super(ReaiPluginPackage.WINDOW_PREFIX + "About", true);
this.tool = tool;

buildInterface(getPluginVersion());
setPreferredSize(300, 160);
setPreferredSize(300, 210);
}

private String getPluginVersion() {
Expand All @@ -39,34 +37,38 @@ private String getPluginVersion() {
}

private void buildInterface(String pluginVersion) {
JPanel mainPanel = new JPanel();
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JPanel mainPanel = new JPanel(new BorderLayout());

// Create title panel
JPanel titlePanel = createTitlePanel("Information about the plugin");
mainPanel.add(titlePanel, BorderLayout.NORTH);

// Create the about content
JPanel contentPanel = createAboutContent(pluginVersion);

// Make it scrollable
JScrollPane scrollPane = new JScrollPane(contentPanel);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBorder(null);

mainPanel.add(scrollPane, BorderLayout.CENTER);
mainPanel.add(contentPanel, BorderLayout.CENTER);

addWorkPanel(mainPanel);
addDismissButton();
}

private JPanel createAboutContent(String pluginVersion) {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

// Add padding at the top
panel.add(Box.createVerticalStrut(15));

// Plugin version label
JLabel pluginLabel = new JLabel("Plugin version: " + pluginVersion);
pluginLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(pluginLabel);

JLabel label = new JLabel("RevEng.AI Ghidra Plugin: " + pluginVersion);
label.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(Box.createVerticalStrut(5));

// MenuBar section
panel.add(label);
panel.add(Box.createVerticalStrut(10));
// SDK version label
JLabel sdkLabel = new JLabel("SDK version: " + Configuration.VERSION);
sdkLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(sdkLabel);

return panel;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package ai.reveng.toolkit.ghidra.binarysimilarity.ui.autounstrip;

import ai.reveng.toolkit.ghidra.binarysimilarity.ui.dialog.RevEngDialogComponentProvider;
import ai.reveng.toolkit.ghidra.core.services.api.GhidraRevengService;
import ai.reveng.toolkit.ghidra.core.services.api.types.AnalysisID;
import ai.reveng.toolkit.ghidra.core.services.api.types.AutoUnstripResponse;
import ai.reveng.toolkit.ghidra.core.types.ProgramWithBinaryID;
import docking.DialogComponentProvider;
import ai.reveng.toolkit.ghidra.plugins.ReaiPluginPackage;
import docking.widgets.label.GDLabel;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
Expand All @@ -13,14 +15,15 @@
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitorComponent;
import resources.ResourceManager;

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

import static ai.reveng.toolkit.ghidra.plugins.BinarySimilarityPlugin.REVENG_AI_NAMESPACE;

public class AutoUnstripDialog extends DialogComponentProvider {
public class AutoUnstripDialog extends RevEngDialogComponentProvider {
private final AnalysisID analysisID;
private final GhidraRevengService revengService;
private final Program program;
Expand All @@ -37,7 +40,7 @@ public class AutoUnstripDialog extends DialogComponentProvider {
private static final int POLL_INTERVAL_MS = 2000; // Poll every 2 seconds

public AutoUnstripDialog(PluginTool tool, ProgramWithBinaryID analysisID) {
super("Auto Unstrip", true);
super(ReaiPluginPackage.WINDOW_PREFIX + "Auto Unstrip", true);

this.analysisID = analysisID.analysisID();
this.program = analysisID.program();
Expand Down Expand Up @@ -162,23 +165,16 @@ private void stopPolling() {
private JComponent buildMainPanel() {
JPanel panel = new JPanel(new BorderLayout());

// Description at the top
JTextArea descriptionArea = new JTextArea(3, 60);
descriptionArea.setLineWrap(true);
descriptionArea.setWrapStyleWord(true);
descriptionArea.setEditable(false);
descriptionArea.setBackground(panel.getBackground());
descriptionArea.setText(
"""
Automatically rename unknown functions in your analysis.
The names are sourced by matching functions in your analysis to functions within the RevEng.AI dataset.
"""
);
panel.add(new JScrollPane(descriptionArea), BorderLayout.NORTH);
// Create title panel
JPanel titlePanel = createTitlePanel("Automatically rename unknown functions");
panel.add(titlePanel, BorderLayout.NORTH);

// Create content panel for description and progress
JPanel contentPanel = new JPanel(new BorderLayout());

// Progress panel in the center
JPanel progressPanel = createProgressPanel();
panel.add(progressPanel, BorderLayout.CENTER);
contentPanel.add(progressPanel, BorderLayout.CENTER);

// Error area at the bottom (initially hidden)
errorArea = new JTextArea(5, 60);
Expand All @@ -188,7 +184,9 @@ private JComponent buildMainPanel() {
errorArea.setBackground(Color.PINK);
errorArea.setBorder(BorderFactory.createTitledBorder("Error Details"));
errorArea.setVisible(false);
panel.add(new JScrollPane(errorArea), BorderLayout.SOUTH);
contentPanel.add(new JScrollPane(errorArea), BorderLayout.SOUTH);

panel.add(contentPanel, BorderLayout.CENTER);

return panel;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package ai.reveng.toolkit.ghidra.binarysimilarity.ui.dialog;

import docking.DialogComponentProvider;
import docking.widgets.label.GDLabel;
import resources.ResourceManager;

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

public class RevEngDialogComponentProvider extends DialogComponentProvider {
public RevEngDialogComponentProvider(String title, boolean isModal) {
super(title, isModal);
}

protected JPanel createTitlePanel(String title) {
// Load icon from resources
Icon dialogIcon = null;
try {
dialogIcon = ResourceManager.loadImage("images/icon_50.png");
} catch (Exception e) {
// If loading fails, fall back to no icon
}

// Create title label
JLabel titleLabel = new GDLabel(title);
// Make the title text bold
Font currentFont = titleLabel.getFont();
titleLabel.setFont(currentFont.deriveFont(Font.BOLD));

JPanel titlePanel = new JPanel(new BorderLayout());
titlePanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(),
BorderFactory.createEmptyBorder(5, 5, 5, 5)));

// Add icon to the left if available
if (dialogIcon != null) {
JLabel iconLabel = new JLabel(dialogIcon);
titlePanel.add(iconLabel, BorderLayout.WEST);
}

// Create a centered panel for the title text
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.X_AXIS));
centerPanel.add(Box.createHorizontalGlue());
centerPanel.add(titleLabel);
centerPanel.add(Box.createHorizontalGlue());

titlePanel.add(centerPanel, BorderLayout.CENTER);

return titlePanel;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ai.reveng.toolkit.ghidra.binarysimilarity.ui.help;

import ai.reveng.toolkit.ghidra.binarysimilarity.ui.dialog.RevEngDialogComponentProvider;
import ai.reveng.toolkit.ghidra.plugins.ReaiPluginPackage;
import docking.DialogComponentProvider;
import docking.widgets.label.GLabel;
Expand All @@ -12,12 +13,9 @@
/**
* Shows a dialog with help information.
*/
public class HelpDialog extends DialogComponentProvider {
private final PluginTool tool;

public class HelpDialog extends RevEngDialogComponentProvider {
public HelpDialog(PluginTool tool) {
super(ReaiPluginPackage.WINDOW_PREFIX + "Help", true);
this.tool = tool;

buildInterface();
setPreferredSize(700, 600);
Expand All @@ -27,6 +25,10 @@ private void buildInterface() {
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

// Create title panel
JPanel titlePanel = createTitlePanel("Help on using the plugin");
mainPanel.add(titlePanel, BorderLayout.NORTH);

// Create the help content
JPanel contentPanel = createHelpContent();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ public TaskMonitor getTaskMonitor() {
}

public void processEvent(RevEngAIAnalysisStatusChangedEvent event) {
// We don't need to display the log window when the user selects an existing analysis because it will be an
// already completed analysis.
var sourceName = event.getSourceName();
if (sourceName != null && sourceName.equals("Recent Analysis Dialog")) {
return;
}

this.setVisible(true);
switch (event.getStatus()) {
case Complete, Error -> {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ private void setupActions() {
.enabledWhen(context -> {
var currentProgram = tool.getService(ProgramManager.class).getCurrentProgram();
if (currentProgram == null) {
tool.getService(ReaiLoggingService.class).info("Create new action disabled: No current program");
// Disable the action if no program is open
return false;
}
boolean isKnown = revengService.isKnownProgram(currentProgram);
boolean shouldEnable = !isKnown;
tool.getService(ReaiLoggingService.class).info("Create new action enabled: " + shouldEnable + " (program: " + currentProgram.getName() + ", isKnown: " + isKnown + ")");

return shouldEnable;
})
.onAction(context -> {
Expand Down Expand Up @@ -178,13 +178,10 @@ private void setupActions() {
.enabledWhen(c -> {
var currentProgram = tool.getService(ProgramManager.class).getCurrentProgram();
if (currentProgram == null) {
tool.getService(ReaiLoggingService.class).info("Attach to existing action disabled: No current program");
return false;
}
boolean isKnown = revengService.isKnownProgram(currentProgram);
boolean shouldEnable = !isKnown;
tool.getService(ReaiLoggingService.class).info("Attach to existing action enabled: " + shouldEnable + " (program: " + currentProgram.getName() + ", isKnown: " + isKnown + ")");
return shouldEnable;
return !isKnown;
})
.onAction(context -> {
var currentProgram = tool.getService(ProgramManager.class).getCurrentProgram();
Expand All @@ -203,12 +200,10 @@ private void setupActions() {
.enabledWhen(c -> {
var currentProgram = tool.getService(ProgramManager.class).getCurrentProgram();
if (currentProgram == null) {
tool.getService(ReaiLoggingService.class).info("Detach action disabled: No current program");
// Disable the action if no program is open
return false;
}
boolean isKnown = revengService.isKnownProgram(currentProgram);
tool.getService(ReaiLoggingService.class).info("Detach action enabled: " + isKnown + " (program: " + currentProgram.getName() + ", isKnown: " + isKnown + ")");
return isKnown;
return revengService.isKnownProgram(currentProgram);
})
.onAction(context -> {
var program = tool.getService(ProgramManager.class).getCurrentProgram();
Expand Down Expand Up @@ -240,12 +235,10 @@ private void setupActions() {
.enabledWhen(context -> {
var currentProgram = tool.getService(ProgramManager.class).getCurrentProgram();
if (currentProgram == null) {
tool.getService(ReaiLoggingService.class).info("Check status action disabled: No current program");
// Disable the action if no program is open
return false;
}
boolean isKnown = revengService.isKnownProgram(currentProgram);
tool.getService(ReaiLoggingService.class).info("Check status action enabled: " + isKnown + " (program: " + currentProgram.getName() + ", isKnown: " + isKnown + ")");
return isKnown;
return revengService.isKnownProgram(currentProgram);
})
.onAction(context -> {
var currentProgram = tool.getService(ProgramManager.class).getCurrentProgram();
Expand All @@ -266,12 +259,10 @@ private void setupActions() {
.enabledWhen(context -> {
var currentProgram = tool.getService(ProgramManager.class).getCurrentProgram();
if (currentProgram == null) {
tool.getService(ReaiLoggingService.class).info("View in portal action disabled: No current program");
// Disable the action if no program is open
return false;
}
boolean isKnown = revengService.isKnownProgram(currentProgram);
tool.getService(ReaiLoggingService.class).info("View in portal action enabled: " + isKnown + " (program: " + currentProgram.getName() + ", isKnown: " + isKnown + ")");
return isKnown;
return revengService.isKnownProgram(currentProgram);
})
.onAction(context -> {
var currentProgram = tool.getService(ProgramManager.class).getCurrentProgram();
Expand Down Expand Up @@ -309,6 +300,7 @@ public void processEvent(PluginEvent event) {
super.processEvent(event);
// Forward the event to the analysis log component
if (event instanceof RevEngAIAnalysisStatusChangedEvent analysisEvent) {

analysisLogComponent.processEvent(analysisEvent);
if (analysisEvent.getStatus() == AnalysisStatus.Complete) {
// If the analysis is complete, we refresh the function signatures from the server
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/docking/wizard/WizardManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ public void componentResized(ComponentEvent e) {

// Create title label without icon - icon will be handled separately
titleLabel = new GDLabel(INIT_TITLE);
// Make the title text bold
Font currentFont = titleLabel.getFont();
titleLabel.setFont(currentFont.deriveFont(Font.BOLD));

JPanel titlePanel = new JPanel(new BorderLayout());
titlePanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(),
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/ai/reveng/UnstripTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public List<FunctionInfo> getFunctionInfo(BinaryID binaryID) {
performAction(action, false);

// Wait for the dialog to appear and interact with it
var dialog = waitForDialogComponent("Auto Unstrip");
var dialog = waitForDialogComponent("RevEng.AI: Auto Unstrip");
waitForSwing();

assertEquals("unstripped_function_name", func.getName());
Expand Down Expand Up @@ -151,7 +151,7 @@ public List<FunctionInfo> getFunctionInfo(BinaryID binaryID) {
performAction(action, false);

// Wait for the dialog to appear and interact with it
var dialog = waitForDialogComponent("Auto Unstrip");
var dialog = waitForDialogComponent("RevEng.AI: Auto Unstrip");
waitForSwing();

waitForCondition(() -> !responses.hasNext());
Expand Down
Loading