mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-01-08 21:47:59 -05:00
GP-6260: a few more bits
GP-6260: from review GP-6260: fix for load GP-6260: working but... GP-6260: new provider logic
This commit is contained in:
@@ -22,14 +22,15 @@ import java.util.stream.Stream;
|
||||
import db.Transaction;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.debug.gui.action.DebuggerReadsMemoryTrait;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.debug.api.target.Target;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.features.base.memsearch.bytesource.AddressableByteSource;
|
||||
import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
||||
import ghidra.features.base.memsearch.bytesource.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
|
||||
@@ -49,6 +50,7 @@ public class DebuggerByteSource implements AddressableByteSource {
|
||||
private final TraceProgramView view;
|
||||
private final Target target;
|
||||
private final DebuggerReadsMemoryTrait readsMem;
|
||||
private final DebuggerStaticMappingService mappingService;
|
||||
|
||||
public DebuggerByteSource(PluginTool tool, TraceProgramView view, Target target,
|
||||
DebuggerReadsMemoryTrait readsMem) {
|
||||
@@ -56,6 +58,7 @@ public class DebuggerByteSource implements AddressableByteSource {
|
||||
this.view = view;
|
||||
this.target = target;
|
||||
this.readsMem = readsMem;
|
||||
this.mappingService = tool.getService(DebuggerStaticMappingService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,4 +112,17 @@ public class DebuggerByteSource implements AddressableByteSource {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getCanonicalLocation(Address address) {
|
||||
ProgramLocation loc = AddressableByteSource.generateProgramLocation(view, address);
|
||||
return mappingService.getStaticLocationFromDynamic(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address rebaseFromCanonical(ProgramLocation location) {
|
||||
ProgramLocation newloc = mappingService.getDynamicLocationFromStatic(view, location);
|
||||
return newloc == null ? null : newloc.getAddress();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -163,6 +163,15 @@
|
||||
reduced based on the values that changed.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4>"Compare to..." Button:</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>This button instantiates a new Memory Search Window using a new choice of program.
|
||||
It rebases the existing results and initiates a scan of the byte values in all the matches
|
||||
in the new copy of the results table. Depending on the selected scan option,
|
||||
the set of matches in the table may be reduced based on the values that changed.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4>Scan Option Radio Buttons</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
@@ -18,6 +18,8 @@ package ghidra.features.base.memsearch.bytesource;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* Interface for reading bytes from a program. This provides a level of indirection for reading the
|
||||
@@ -56,4 +58,23 @@ public interface AddressableByteSource {
|
||||
*/
|
||||
public void invalidate();
|
||||
|
||||
/**
|
||||
* Convert byte source address to the canonical (static) location
|
||||
* @param address address to be converted
|
||||
* @return canonical location
|
||||
*/
|
||||
public ProgramLocation getCanonicalLocation(Address address);
|
||||
|
||||
/**
|
||||
* Rebase a canonical location in the current byte source
|
||||
* @param location location to be rebased
|
||||
* @return address for new byte source
|
||||
*/
|
||||
public Address rebaseFromCanonical(ProgramLocation location);
|
||||
|
||||
static ProgramLocation generateProgramLocation(Program pgm, Address address) {
|
||||
// A simpler constructor would be nice, but they all use getCodeUnitAddress
|
||||
return new ProgramLocation(pgm, address, address, new int[0], address, 0, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package ghidra.features.base.memsearch.bytesource;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* Implementation for an empty {@link AddressableByteSource}
|
||||
@@ -39,4 +40,15 @@ public enum EmptyByteSource implements AddressableByteSource {
|
||||
public void invalidate() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getCanonicalLocation(Address address) {
|
||||
return AddressableByteSource.generateProgramLocation(null, address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address rebaseFromCanonical(ProgramLocation location) {
|
||||
return location.getAddress();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
/**
|
||||
* {@link AddressableByteSource} implementation for a Ghidra {@link Program}
|
||||
@@ -53,4 +54,18 @@ public class ProgramByteSource implements AddressableByteSource {
|
||||
// nothing to do in the static case
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getCanonicalLocation(Address address) {
|
||||
return AddressableByteSource.generateProgramLocation(getProgram(), address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address rebaseFromCanonical(ProgramLocation location) {
|
||||
long offset = location.getByteAddress().subtract(location.getProgram().getImageBase());
|
||||
return getProgram().getImageBase().add(offset);
|
||||
}
|
||||
|
||||
public Program getProgram() {
|
||||
return memory.getProgram();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,11 @@ public class MemoryMatchTableModel extends AddressBasedTableModel<MemoryMatch> {
|
||||
loader = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startInitialLoad() {
|
||||
// Don't start up a load
|
||||
}
|
||||
|
||||
void setLoader(MemoryMatchTableLoader loader) {
|
||||
this.loader = loader;
|
||||
reload();
|
||||
|
||||
@@ -35,6 +35,7 @@ public class MemoryScanControlPanel extends JPanel {
|
||||
private boolean hasResults;
|
||||
private boolean isBusy;
|
||||
private JButton scanButton;
|
||||
private JButton compareButton;
|
||||
|
||||
MemoryScanControlPanel(MemorySearchProvider provider) {
|
||||
super();
|
||||
@@ -50,6 +51,16 @@ public class MemoryScanControlPanel extends JPanel {
|
||||
"those that don't meet the selected change criteria");
|
||||
|
||||
add(scanButton);
|
||||
|
||||
add(Box.createHorizontalStrut(20));
|
||||
|
||||
compareButton = new JButton("Compare to...");
|
||||
compareButton.setEnabled(false);
|
||||
compareButton.setToolTipText("Create a new search using a new program, " +
|
||||
"remapping the current results");
|
||||
|
||||
add(compareButton);
|
||||
|
||||
add(Box.createHorizontalStrut(20));
|
||||
add(buildButtonPanel());
|
||||
|
||||
@@ -57,6 +68,7 @@ public class MemoryScanControlPanel extends JPanel {
|
||||
helpService.registerHelp(this, new HelpLocation(HelpTopics.SEARCH, "Scan_Controls"));
|
||||
|
||||
scanButton.addActionListener(e -> provider.scan(selectedScanner));
|
||||
compareButton.addActionListener(e -> provider.generateNewProvider(selectedScanner));
|
||||
}
|
||||
|
||||
private JComponent buildButtonPanel() {
|
||||
@@ -77,6 +89,7 @@ public class MemoryScanControlPanel extends JPanel {
|
||||
this.hasResults = hasResults;
|
||||
this.isBusy = isBusy;
|
||||
updateScanButton();
|
||||
updateCompareButton();
|
||||
}
|
||||
|
||||
private void updateScanButton() {
|
||||
@@ -87,4 +100,8 @@ public class MemoryScanControlPanel extends JPanel {
|
||||
return hasResults && !isBusy;
|
||||
}
|
||||
|
||||
private void updateCompareButton() {
|
||||
compareButton.setEnabled(canScan());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,9 +17,8 @@ package ghidra.features.base.memsearch.gui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -36,8 +35,9 @@ import docking.widgets.OptionDialogBuilder;
|
||||
import docking.widgets.table.actions.DeleteTableRowAction;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.context.NavigatableActionContext;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.nav.NavigatableRemovalListener;
|
||||
import ghidra.app.nav.*;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
import ghidra.app.script.AskDialog;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.features.base.memsearch.bytesource.AddressableByteSource;
|
||||
import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
||||
@@ -80,6 +80,7 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||
private Navigatable navigatable;
|
||||
private Program program;
|
||||
private AddressableByteSource byteSource;
|
||||
private SearchHistory searchHistory;
|
||||
|
||||
private JComponent mainComponent;
|
||||
private JPanel controlPanel;
|
||||
@@ -107,7 +108,7 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||
|
||||
// used to show a temporary message over the table
|
||||
private GGlassPaneMessage glassPaneMessage;
|
||||
|
||||
|
||||
public MemorySearchProvider(MemorySearchPlugin plugin, Navigatable navigatable,
|
||||
SearchSettings settings, MemorySearchOptions options, SearchHistory history) {
|
||||
super(plugin.getTool(), "Memory Search", plugin.getName());
|
||||
@@ -116,6 +117,7 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||
this.options = options;
|
||||
this.program = navigatable.getProgram();
|
||||
this.byteSource = navigatable.getByteSource();
|
||||
this.searchHistory = history;
|
||||
|
||||
// always initially use the byte ordering of the program, regardless of previous searches
|
||||
if (settings == null) {
|
||||
@@ -725,4 +727,44 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
||||
glassPaneMessage.showCenteredMessage(message);
|
||||
}
|
||||
|
||||
public void generateNewProvider(Scanner scanner) {
|
||||
List<Navigatable> navigatables = NavigatableRegistry.getRegisteredNavigatables(tool);
|
||||
Map<String, Navigatable> programMap = new HashMap<>();
|
||||
for (Navigatable nav : navigatables) {
|
||||
if (nav instanceof CodeViewerProvider listing) {
|
||||
String key = listing.getTitle();
|
||||
if (listing.getSubTitle() != null) {
|
||||
key += ": " + listing.getSubTitle();
|
||||
}
|
||||
programMap.put(key, listing);
|
||||
}
|
||||
}
|
||||
ArrayList<String> choices = new ArrayList<String>(programMap.keySet());
|
||||
AskDialog<String> dialog = new AskDialog<String>(null, "Compare to...", "Program", AskDialog.STRING, choices, null);
|
||||
if (dialog.isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigatable next = programMap.get(dialog.getChoiceValue());
|
||||
MemorySearchProvider nextProvider = new MemorySearchProvider(plugin, next, model.getSettings(), options, new SearchHistory(searchHistory));
|
||||
AddressableByteSource nextByteSource = nextProvider.byteSource;
|
||||
nextProvider.setSearchInput(this.getSearchInput());
|
||||
nextProvider.showScanPanel(true);
|
||||
|
||||
List<MemoryMatch> searchResults = getSearchResults();
|
||||
List<MemoryMatch> rebasedResults = new ArrayList<>();
|
||||
for (MemoryMatch match : searchResults) {
|
||||
ProgramLocation canonicalLocation = byteSource.getCanonicalLocation(match.getAddress());
|
||||
Address rebase = nextByteSource.rebaseFromCanonical(canonicalLocation);
|
||||
if (rebase != null) {
|
||||
MemoryMatch nextMatch = new MemoryMatch(rebase, match.getBytes(), match.getByteMatcher());
|
||||
rebasedResults.add(nextMatch);
|
||||
}
|
||||
}
|
||||
|
||||
MemorySearchResultsPanel nextResultsPanel = nextProvider.getResultsPanel();
|
||||
nextProvider.setBusy(true);
|
||||
nextResultsPanel.refreshAndMaybeScanForChanges(nextByteSource, scanner, rebasedResults);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -119,6 +119,11 @@ public class MemorySearchResultsPanel extends JPanel {
|
||||
TaskLauncher.launch(task);
|
||||
}
|
||||
|
||||
public void refreshAndMaybeScanForChanges(AddressableByteSource byteSource, Scanner scanner, List<MemoryMatch> previousResults) {
|
||||
RefreshAndScanTask task = new RefreshAndScanTask(byteSource, scanner, previousResults);
|
||||
TaskLauncher.launch(task);
|
||||
}
|
||||
|
||||
private MemoryMatchTableLoader createLoader(MemorySearcher searcher, Combiner combiner) {
|
||||
if (!hasResults()) {
|
||||
hasDeleted = false;
|
||||
@@ -229,11 +234,17 @@ public class MemorySearchResultsPanel extends JPanel {
|
||||
|
||||
private AddressableByteSource byteSource;
|
||||
private Scanner scanner;
|
||||
private List<MemoryMatch> matchList;
|
||||
|
||||
public RefreshAndScanTask(AddressableByteSource byteSource, Scanner scanner) {
|
||||
this(byteSource, scanner, tableModel.getModelData());
|
||||
}
|
||||
|
||||
public RefreshAndScanTask(AddressableByteSource byteSource, Scanner scanner, List<MemoryMatch> matches) {
|
||||
super("Refreshing", true, true, true);
|
||||
this.byteSource = byteSource;
|
||||
this.scanner = scanner;
|
||||
this.matchList = matches;
|
||||
}
|
||||
|
||||
private void tableLoadComplete(MemoryMatch match) {
|
||||
@@ -250,16 +261,13 @@ public class MemorySearchResultsPanel extends JPanel {
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
List<MemoryMatch> matches = tableModel.getModelData();
|
||||
|
||||
if (refreshByteValues(monitor, matches) && scanner != null) {
|
||||
performScanFiltering(monitor, matches);
|
||||
if (refreshByteValues(monitor, matchList) && scanner != null) {
|
||||
performScanFiltering(monitor, matchList);
|
||||
}
|
||||
else {
|
||||
tableModel.fireTableDataChanged(); // some data bytes may have changed, repaint
|
||||
provider.refreshAndScanCompleted(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean refreshByteValues(TaskMonitor monitor, List<MemoryMatch> matches) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
||||
import ghidra.features.base.memsearch.matcher.ByteMatcher;
|
||||
import ghidra.features.base.memsearch.matcher.RegExByteMatcher;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.datastruct.Accumulator;
|
||||
import ghidra.util.datastruct.ListAccumulator;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -329,6 +330,16 @@ public class MemSearcherTest {
|
||||
public void invalidate() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getCanonicalLocation(Address address) {
|
||||
return AddressableByteSource.generateProgramLocation(null, address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address rebaseFromCanonical(ProgramLocation location) {
|
||||
return location.getAddress();
|
||||
}
|
||||
}
|
||||
|
||||
private Address addr(long offset) {
|
||||
|
||||
Reference in New Issue
Block a user