mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-01-09 22:17:55 -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 db.Transaction;
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.app.plugin.core.debug.gui.action.DebuggerReadsMemoryTrait;
|
import ghidra.app.plugin.core.debug.gui.action.DebuggerReadsMemoryTrait;
|
||||||
|
import ghidra.app.services.DebuggerStaticMappingService;
|
||||||
import ghidra.debug.api.target.Target;
|
import ghidra.debug.api.target.Target;
|
||||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||||
import ghidra.features.base.memsearch.bytesource.AddressableByteSource;
|
import ghidra.features.base.memsearch.bytesource.*;
|
||||||
import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.trace.model.memory.*;
|
import ghidra.trace.model.memory.*;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
import ghidra.trace.model.program.TraceProgramView;
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@ public class DebuggerByteSource implements AddressableByteSource {
|
|||||||
private final TraceProgramView view;
|
private final TraceProgramView view;
|
||||||
private final Target target;
|
private final Target target;
|
||||||
private final DebuggerReadsMemoryTrait readsMem;
|
private final DebuggerReadsMemoryTrait readsMem;
|
||||||
|
private final DebuggerStaticMappingService mappingService;
|
||||||
|
|
||||||
public DebuggerByteSource(PluginTool tool, TraceProgramView view, Target target,
|
public DebuggerByteSource(PluginTool tool, TraceProgramView view, Target target,
|
||||||
DebuggerReadsMemoryTrait readsMem) {
|
DebuggerReadsMemoryTrait readsMem) {
|
||||||
@@ -56,6 +58,7 @@ public class DebuggerByteSource implements AddressableByteSource {
|
|||||||
this.view = view;
|
this.view = view;
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.readsMem = readsMem;
|
this.readsMem = readsMem;
|
||||||
|
this.mappingService = tool.getService(DebuggerStaticMappingService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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>
|
reduced based on the values that changed.</P>
|
||||||
</BLOCKQUOTE>
|
</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>
|
<H4>Scan Option Radio Buttons</H4>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package ghidra.features.base.memsearch.bytesource;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.program.model.address.Address;
|
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
|
* 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();
|
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 java.util.List;
|
||||||
|
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation for an empty {@link AddressableByteSource}
|
* Implementation for an empty {@link AddressableByteSource}
|
||||||
@@ -39,4 +40,15 @@ public enum EmptyByteSource implements AddressableByteSource {
|
|||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
// nothing to do
|
// 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.listing.Program;
|
||||||
import ghidra.program.model.mem.Memory;
|
import ghidra.program.model.mem.Memory;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AddressableByteSource} implementation for a Ghidra {@link Program}
|
* {@link AddressableByteSource} implementation for a Ghidra {@link Program}
|
||||||
@@ -53,4 +54,18 @@ public class ProgramByteSource implements AddressableByteSource {
|
|||||||
// nothing to do in the static case
|
// 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;
|
loader = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startInitialLoad() {
|
||||||
|
// Don't start up a load
|
||||||
|
}
|
||||||
|
|
||||||
void setLoader(MemoryMatchTableLoader loader) {
|
void setLoader(MemoryMatchTableLoader loader) {
|
||||||
this.loader = loader;
|
this.loader = loader;
|
||||||
reload();
|
reload();
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public class MemoryScanControlPanel extends JPanel {
|
|||||||
private boolean hasResults;
|
private boolean hasResults;
|
||||||
private boolean isBusy;
|
private boolean isBusy;
|
||||||
private JButton scanButton;
|
private JButton scanButton;
|
||||||
|
private JButton compareButton;
|
||||||
|
|
||||||
MemoryScanControlPanel(MemorySearchProvider provider) {
|
MemoryScanControlPanel(MemorySearchProvider provider) {
|
||||||
super();
|
super();
|
||||||
@@ -50,6 +51,16 @@ public class MemoryScanControlPanel extends JPanel {
|
|||||||
"those that don't meet the selected change criteria");
|
"those that don't meet the selected change criteria");
|
||||||
|
|
||||||
add(scanButton);
|
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(Box.createHorizontalStrut(20));
|
||||||
add(buildButtonPanel());
|
add(buildButtonPanel());
|
||||||
|
|
||||||
@@ -57,6 +68,7 @@ public class MemoryScanControlPanel extends JPanel {
|
|||||||
helpService.registerHelp(this, new HelpLocation(HelpTopics.SEARCH, "Scan_Controls"));
|
helpService.registerHelp(this, new HelpLocation(HelpTopics.SEARCH, "Scan_Controls"));
|
||||||
|
|
||||||
scanButton.addActionListener(e -> provider.scan(selectedScanner));
|
scanButton.addActionListener(e -> provider.scan(selectedScanner));
|
||||||
|
compareButton.addActionListener(e -> provider.generateNewProvider(selectedScanner));
|
||||||
}
|
}
|
||||||
|
|
||||||
private JComponent buildButtonPanel() {
|
private JComponent buildButtonPanel() {
|
||||||
@@ -77,6 +89,7 @@ public class MemoryScanControlPanel extends JPanel {
|
|||||||
this.hasResults = hasResults;
|
this.hasResults = hasResults;
|
||||||
this.isBusy = isBusy;
|
this.isBusy = isBusy;
|
||||||
updateScanButton();
|
updateScanButton();
|
||||||
|
updateCompareButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateScanButton() {
|
private void updateScanButton() {
|
||||||
@@ -87,4 +100,8 @@ public class MemoryScanControlPanel extends JPanel {
|
|||||||
return hasResults && !isBusy;
|
return hasResults && !isBusy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateCompareButton() {
|
||||||
|
compareButton.setEnabled(canScan());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,8 @@ package ghidra.features.base.memsearch.gui;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.HashSet;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@@ -36,8 +35,9 @@ import docking.widgets.OptionDialogBuilder;
|
|||||||
import docking.widgets.table.actions.DeleteTableRowAction;
|
import docking.widgets.table.actions.DeleteTableRowAction;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
import ghidra.app.context.NavigatableActionContext;
|
import ghidra.app.context.NavigatableActionContext;
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.*;
|
||||||
import ghidra.app.nav.NavigatableRemovalListener;
|
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||||
|
import ghidra.app.script.AskDialog;
|
||||||
import ghidra.app.util.HelpTopics;
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.features.base.memsearch.bytesource.AddressableByteSource;
|
import ghidra.features.base.memsearch.bytesource.AddressableByteSource;
|
||||||
import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
import ghidra.features.base.memsearch.bytesource.SearchRegion;
|
||||||
@@ -80,6 +80,7 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
|||||||
private Navigatable navigatable;
|
private Navigatable navigatable;
|
||||||
private Program program;
|
private Program program;
|
||||||
private AddressableByteSource byteSource;
|
private AddressableByteSource byteSource;
|
||||||
|
private SearchHistory searchHistory;
|
||||||
|
|
||||||
private JComponent mainComponent;
|
private JComponent mainComponent;
|
||||||
private JPanel controlPanel;
|
private JPanel controlPanel;
|
||||||
@@ -107,7 +108,7 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
|||||||
|
|
||||||
// used to show a temporary message over the table
|
// used to show a temporary message over the table
|
||||||
private GGlassPaneMessage glassPaneMessage;
|
private GGlassPaneMessage glassPaneMessage;
|
||||||
|
|
||||||
public MemorySearchProvider(MemorySearchPlugin plugin, Navigatable navigatable,
|
public MemorySearchProvider(MemorySearchPlugin plugin, Navigatable navigatable,
|
||||||
SearchSettings settings, MemorySearchOptions options, SearchHistory history) {
|
SearchSettings settings, MemorySearchOptions options, SearchHistory history) {
|
||||||
super(plugin.getTool(), "Memory Search", plugin.getName());
|
super(plugin.getTool(), "Memory Search", plugin.getName());
|
||||||
@@ -116,6 +117,7 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
|||||||
this.options = options;
|
this.options = options;
|
||||||
this.program = navigatable.getProgram();
|
this.program = navigatable.getProgram();
|
||||||
this.byteSource = navigatable.getByteSource();
|
this.byteSource = navigatable.getByteSource();
|
||||||
|
this.searchHistory = history;
|
||||||
|
|
||||||
// always initially use the byte ordering of the program, regardless of previous searches
|
// always initially use the byte ordering of the program, regardless of previous searches
|
||||||
if (settings == null) {
|
if (settings == null) {
|
||||||
@@ -725,4 +727,44 @@ public class MemorySearchProvider extends ComponentProviderAdapter
|
|||||||
glassPaneMessage.showCenteredMessage(message);
|
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);
|
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) {
|
private MemoryMatchTableLoader createLoader(MemorySearcher searcher, Combiner combiner) {
|
||||||
if (!hasResults()) {
|
if (!hasResults()) {
|
||||||
hasDeleted = false;
|
hasDeleted = false;
|
||||||
@@ -229,11 +234,17 @@ public class MemorySearchResultsPanel extends JPanel {
|
|||||||
|
|
||||||
private AddressableByteSource byteSource;
|
private AddressableByteSource byteSource;
|
||||||
private Scanner scanner;
|
private Scanner scanner;
|
||||||
|
private List<MemoryMatch> matchList;
|
||||||
|
|
||||||
public RefreshAndScanTask(AddressableByteSource byteSource, Scanner scanner) {
|
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);
|
super("Refreshing", true, true, true);
|
||||||
this.byteSource = byteSource;
|
this.byteSource = byteSource;
|
||||||
this.scanner = scanner;
|
this.scanner = scanner;
|
||||||
|
this.matchList = matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tableLoadComplete(MemoryMatch match) {
|
private void tableLoadComplete(MemoryMatch match) {
|
||||||
@@ -250,16 +261,13 @@ public class MemorySearchResultsPanel extends JPanel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
List<MemoryMatch> matches = tableModel.getModelData();
|
if (refreshByteValues(monitor, matchList) && scanner != null) {
|
||||||
|
performScanFiltering(monitor, matchList);
|
||||||
if (refreshByteValues(monitor, matches) && scanner != null) {
|
|
||||||
performScanFiltering(monitor, matches);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
tableModel.fireTableDataChanged(); // some data bytes may have changed, repaint
|
tableModel.fireTableDataChanged(); // some data bytes may have changed, repaint
|
||||||
provider.refreshAndScanCompleted(null);
|
provider.refreshAndScanCompleted(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean refreshByteValues(TaskMonitor monitor, List<MemoryMatch> matches) {
|
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.ByteMatcher;
|
||||||
import ghidra.features.base.memsearch.matcher.RegExByteMatcher;
|
import ghidra.features.base.memsearch.matcher.RegExByteMatcher;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
import ghidra.util.datastruct.Accumulator;
|
import ghidra.util.datastruct.Accumulator;
|
||||||
import ghidra.util.datastruct.ListAccumulator;
|
import ghidra.util.datastruct.ListAccumulator;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
@@ -329,6 +330,16 @@ public class MemSearcherTest {
|
|||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
// ignore
|
// 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) {
|
private Address addr(long offset) {
|
||||||
|
|||||||
Reference in New Issue
Block a user