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
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,11 @@ jobs:
- name: Build
run: ./gradlew buildExtension

- name: Check SLA timestamps
run: ls -l $GHIDRA_INSTALL_DIR/Ghidra/Processors/x86/data/languages/*

- name: Precompile slaspec
run: $GHIDRA_INSTALL_DIR/support/sleigh $GHIDRA_INSTALL_DIR/Ghidra/Processors/x86/data/languages/x86-64.slaspec

- name: Run tests
run: xvfb-run ./gradlew test --info
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ repositories {
dependencies {
// Any external dependencies added here will automatically be copied to the lib/ directory when
// this extension is built.
implementation 'io.github.java-diff-utils:java-diff-utils:4.12'
implementation 'org.json:json:20250107'
implementation "com.google.guava:guava:33.2.0-jre"
implementation group: 'com.fifesoft', name: 'rsyntaxtextarea', version: '3.5.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private void initializeUI(int minimumQueryLength) {
selectedItemsPanel.setLayout(new WrapLayout(FlowLayout.LEFT));

JScrollPane scrollPane = new JScrollPane(selectedItemsPanel);
scrollPane.setPreferredSize(new Dimension(0, 100));
scrollPane.setPreferredSize(new Dimension(0, 60));
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBorder(null); // Remove any border from the scroll pane
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@
import ai.reveng.toolkit.ghidra.core.services.api.types.GhidraFunctionMatchWithSignature;
import com.google.common.collect.BiMap;
import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitorComponent;
import ghidra.util.Msg;
import resources.ResourceManager;
Expand All @@ -28,8 +25,6 @@
import java.util.Set;
import java.util.stream.Collectors;

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

public abstract class AbstractFunctionMatchingDialog extends RevEngDialogComponentProvider {
protected final GhidraRevengService revengService;
protected final GhidraRevengService.AnalysedProgram analyzedProgram;
Expand All @@ -52,6 +47,9 @@ public abstract class AbstractFunctionMatchingDialog extends RevEngDialogCompone
protected Timer pollTimer;
protected JPanel renameButtonsPanel;

// Assembly comparison panel
protected AssemblyDiffPanel assemblyDiffPanel;

// Data
protected Basic analysisBasicInfo;
protected FunctionMatchingResponse functionMatchingResponse;
Expand All @@ -61,26 +59,6 @@ public abstract class AbstractFunctionMatchingDialog extends RevEngDialogCompone
// Polling configuration
protected static final int POLL_INTERVAL_MS = 2000; // Poll every 2 seconds

// /// Inner class to hold function match results
// /// @deprecated {@link ai.reveng.toolkit.ghidra.core.services.api.types.FunctionMatch}
// public record FunctionMatchResult(
// String virtualAddress,
// String functionName,
// String bestMatchName,
// String bestMatchMangledName,
// String similarity,
// String confidence,
// String matchedHash,
// String binary,
// Long matcherFunctionId
// ) {
// // Constructor for function-level dialog (without virtual address and function name)
// public FunctionMatchResult(String bestMatchName, String bestMatchMangledName, String similarity,
// String confidence, String matchedHash, String binary, Long matcherFunctionId) {
// this("", "", bestMatchName, bestMatchMangledName, similarity, confidence, matchedHash, binary, matcherFunctionId);
// }
// }

protected AbstractFunctionMatchingDialog(String title, Boolean isModal, GhidraRevengService revengService,
GhidraRevengService.AnalysedProgram analyzedProgram) {
super(title, isModal);
Expand All @@ -106,7 +84,7 @@ protected AbstractFunctionMatchingDialog(String title, Boolean isModal, GhidraRe
addWorkPanel(buildMainPanel());

// Set dialog size to be wider
setPreferredSize(1000, 800);
setPreferredSize(1200, 1000);

// Don't start function matching automatically - wait for user to click Match button
statusLabel.setText("Ready - adjust filters and click 'Match Functions' to begin search");
Expand Down Expand Up @@ -521,7 +499,25 @@ protected JPanel createResultsContainer() {
resultsTable.setAutoCreateRowSorter(true);
resultsScrollPane = new JScrollPane(resultsTable);
resultsScrollPane.setBorder(BorderFactory.createTitledBorder("Function Matching Results"));
resultsContainer.add(resultsScrollPane, BorderLayout.CENTER);

// Add selection listener for assembly comparison
resultsTable.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) {
onTableSelectionChanged();
}
});

// Create assembly comparison panel
assemblyDiffPanel = new AssemblyDiffPanel();

// Create vertical split pane with results table on top and assembly comparison below
JSplitPane mainSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
mainSplitPane.setTopComponent(resultsScrollPane);
mainSplitPane.setBottomComponent(assemblyDiffPanel);
mainSplitPane.setResizeWeight(0.4);
mainSplitPane.setDividerLocation(250);

resultsContainer.add(mainSplitPane, BorderLayout.CENTER);

// Rename buttons panel
renameButtonsPanel = createRenameButtonsPanel();
Expand All @@ -531,6 +527,29 @@ protected JPanel createResultsContainer() {
return resultsContainer;
}

protected void onTableSelectionChanged() {
int selectedRow = resultsTable.getSelectedRow();
if (selectedRow < 0) {
assemblyDiffPanel.clear();
return;
}

// Convert view index to model index (in case table is sorted)
int modelRow = resultsTable.convertRowIndexToModel(selectedRow);

// Get the appropriate results list
String filterText = functionFilterField != null ? functionFilterField.getText().trim() : "";
List<GhidraFunctionMatchWithSignature> resultsToShow = filterText.isEmpty() ?
functionMatchResults : filteredFunctionMatchResults;

if (modelRow >= resultsToShow.size()) {
return;
}

GhidraFunctionMatchWithSignature selectedMatch = resultsToShow.get(modelRow);
assemblyDiffPanel.showAssemblyFor(selectedMatch, revengService);
}

protected JPanel createRenameButtonsPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER));

Expand Down
Loading
Loading