mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 05:32:14 +00:00
Merge remote-tracking branch 'origin/GP-2418-dragonmacher-instruction-search-add-button--SQUASHED'
This commit is contained in:
commit
9e589a451a
@ -46,36 +46,39 @@
|
||||
<H4>Instruction Table Toolbar</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG src="images/SearchInstructionPatternsInstructionTableToolbar.png" border="0" alt=
|
||||
""> </P>
|
||||
<P><IMG src="images/SearchInstructionPatternsInstructionTableToolbar.png" border="1" alt=
|
||||
""></P>
|
||||
|
||||
<P>These tools provide ways to manipulate the Instruction Table and are discussed in
|
||||
detail below:</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<UL>
|
||||
<LI>[ <IMG border="0" src="images/edit-clear.png"> ] Clears
|
||||
<LI>[ <IMG border="0" src="Icons.CLEAR_ICON"> ] Clears
|
||||
all masks.</LI>
|
||||
|
||||
<LI>[ <IMG border="0" src="images/DOSA_D.png"> ] Masks all
|
||||
<LI>[ <IMG border="0" src="icon.plugin.instructiontable.undefined"> ] Masks all
|
||||
data (non-instructions).</LI>
|
||||
|
||||
<LI>[ <IMG border="0" src="images/DOSA_O.png"> ] Masks all
|
||||
<LI>[ <IMG border="0" src="icon.plugin.instructiontable.operand"> ] Masks all
|
||||
operands.</LI>
|
||||
|
||||
<LI>[ <IMG border="0" src="images/DOSA_S.png"> ] Masks all
|
||||
<LI>[ <IMG border="0" src="icon.plugin.instructiontable.scalar"> ] Masks all
|
||||
scalar operands.</LI>
|
||||
|
||||
<LI>[ <IMG border="0" src="images/DOSA_A.png"> ] Masks all
|
||||
<LI>[ <IMG border="0" src="icon.plugin.instructiontable.address"> ] Masks all
|
||||
address operands.</LI>
|
||||
|
||||
<LI>[ <IMG border="0" src="images/reload.png"> ] Reloads the
|
||||
<LI>[ <IMG border="0" src="Icons.REFRESH_ICON"> ] Reloads the
|
||||
table from what is currently selected in the listing.</LI>
|
||||
|
||||
<LI>[ <IMG border="0" src="images/editbytes.gif"> ] Allows
|
||||
<LI>[ <IMG border="0" src="Icons.ADD_ICON"> ] Add to the table what is currently
|
||||
selected in the listing.</LI>
|
||||
|
||||
<LI>[ <IMG border="0" src="icon.plugin.instructiontable.manual.entry"> ] Allows
|
||||
users to manually enter bytes to be loaded.</LI>
|
||||
|
||||
<LI>[ <IMG border="0" src="images/go-home.png"> ] Navigates in
|
||||
<LI>[ <IMG border="0" src="Icons.HOME_ICON"> ] Navigates in
|
||||
the listing to the location defined by this set of instructions.</LI>
|
||||
</UL>
|
||||
</BLOCKQUOTE>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 30 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.3 KiB |
@ -144,7 +144,7 @@ public class InstructionSearchPlugin extends ProgramPlugin {
|
||||
// immediately return and display an error message if they do.
|
||||
if (selection.getNumAddresses() == 0) {
|
||||
dialog.displayMessage(
|
||||
"Select instructions from the listing (and hit reload) to populate the table.",
|
||||
"Select instructions from the listing (and hit reload/add) to populate the table.",
|
||||
Messages.NORMAL);
|
||||
return false;
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
/**
|
||||
* This is the data model that {@link InstructionSearchDialog} instances use
|
||||
@ -115,25 +115,72 @@ public class InstructionSearchData extends Observable {
|
||||
throw new InvalidInputException("No instructions found in selection.");
|
||||
}
|
||||
|
||||
TaskLauncher.launchModal("Loading Instructions", monitor -> {
|
||||
TaskLauncher.launch(new LoadInstructionsTask(program, addrSet));
|
||||
|
||||
modelChanged(UpdateType.RELOAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines the given addresses from the given program to extract all instructions; results are
|
||||
* stored in the local {@link InstructionMetadata} list.
|
||||
*
|
||||
* @param program the current program
|
||||
* @param addresses the addresses to load instructions for
|
||||
* @throws InvalidInputException if there's an error parsing the instructions
|
||||
*/
|
||||
public void add(Program program, AddressSetView addresses) throws InvalidInputException {
|
||||
|
||||
// Do some initial checks on the program and addresses we want to load instructions
|
||||
// for. If these are invalid, no need to proceed.
|
||||
if (program == null || addresses == null || addresses.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do a quick check to see if we have any valid code units in the selection. If not,
|
||||
// display an error message.
|
||||
Listing listing = program.getListing();
|
||||
CodeUnitIterator cuIter = listing.getCodeUnits(addresses, true);
|
||||
if (!cuIter.hasNext()) {
|
||||
throw new InvalidInputException("No instructions found in selection.");
|
||||
}
|
||||
|
||||
TaskLauncher.launch(new LoadInstructionsTask(program, addresses));
|
||||
|
||||
modelChanged(UpdateType.RELOAD);
|
||||
}
|
||||
|
||||
private class LoadInstructionsTask extends Task {
|
||||
|
||||
private Program program;
|
||||
private AddressSetView addresses;
|
||||
|
||||
public LoadInstructionsTask(Program program, AddressSetView addresses) {
|
||||
super("Loading Instructions", true, true, true);
|
||||
this.program = program;
|
||||
this.addresses = addresses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
SleighDebugLogger logger;
|
||||
monitor.setIndeterminate(true);
|
||||
|
||||
while (cuIter.hasNext()) {
|
||||
Listing listing = program.getListing();
|
||||
CodeUnitIterator it = listing.getCodeUnits(addresses, true);
|
||||
while (it.hasNext()) {
|
||||
if (monitor.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CodeUnit cu = cuIter.next();
|
||||
CodeUnit cu = it.next();
|
||||
|
||||
InstructionMetadata instructionMetadata;
|
||||
|
||||
// If this CU is an instruction, we can use the Sleigh debug logger to build the
|
||||
// mask info. If not, we don't need to create anything complex for masking - it's either
|
||||
// on or off.
|
||||
// mask info. If not, we don't need to create anything complex for masking - it's
|
||||
// either on or off.
|
||||
if (cu instanceof Instruction) {
|
||||
logger =
|
||||
SleighDebugLogger logger =
|
||||
new SleighDebugLogger(program, cu.getAddress(), SleighDebugMode.VERBOSE);
|
||||
if (logger.parseFailed()) {
|
||||
Msg.showError(this, null, "Parsing error",
|
||||
@ -162,9 +209,7 @@ public class InstructionSearchData extends Observable {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modelChanged(UpdateType.RELOAD);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -349,8 +394,11 @@ public class InstructionSearchData extends Observable {
|
||||
return;
|
||||
}
|
||||
|
||||
instructions.get(row).getOperands().get(col).setMasked(
|
||||
table.getCellData(row, col + 1).getState().equals(OperandState.MASKED));
|
||||
instructions.get(row)
|
||||
.getOperands()
|
||||
.get(col)
|
||||
.setMasked(
|
||||
table.getCellData(row, col + 1).getState().equals(OperandState.MASKED));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -674,10 +722,12 @@ public class InstructionSearchData extends Observable {
|
||||
|
||||
// Do a quick check to make sure the search bounds are within the bounds of the
|
||||
// program.
|
||||
if (searchBounds.getMinAddress().compareTo(
|
||||
plugin.getCurrentProgram().getMinAddress()) < 0 ||
|
||||
searchBounds.getMaxAddress().compareTo(
|
||||
plugin.getCurrentProgram().getMaxAddress()) > 0) {
|
||||
if (searchBounds.getMinAddress()
|
||||
.compareTo(
|
||||
plugin.getCurrentProgram().getMinAddress()) < 0 ||
|
||||
searchBounds.getMaxAddress()
|
||||
.compareTo(
|
||||
plugin.getCurrentProgram().getMaxAddress()) > 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Search bounds are not valid; must be within the bounds of the program.");
|
||||
}
|
||||
@ -734,8 +784,11 @@ public class InstructionSearchData extends Observable {
|
||||
while (currentPosition.compareTo(endAddress) < 0) {
|
||||
|
||||
// Search program memory for the given mask and val.
|
||||
currentPosition = plugin.getCurrentProgram().getMemory().findBytes(currentPosition,
|
||||
endAddress, maskContainer.getValue(), maskContainer.getMask(), true, taskMonitor);
|
||||
currentPosition = plugin.getCurrentProgram()
|
||||
.getMemory()
|
||||
.findBytes(currentPosition,
|
||||
endAddress, maskContainer.getValue(), maskContainer.getMask(), true,
|
||||
taskMonitor);
|
||||
|
||||
// If no match was found, currentPosition will be null.
|
||||
if (currentPosition == null) {
|
||||
@ -792,8 +845,11 @@ public class InstructionSearchData extends Observable {
|
||||
while (currentPosition.compareTo(endAddress) > 0) {
|
||||
|
||||
// Search program memory for the given mask and val.
|
||||
currentPosition = plugin.getCurrentProgram().getMemory().findBytes(currentPosition,
|
||||
endAddress, maskContainer.getValue(), maskContainer.getMask(), false, taskMonitor);
|
||||
currentPosition = plugin.getCurrentProgram()
|
||||
.getMemory()
|
||||
.findBytes(currentPosition,
|
||||
endAddress, maskContainer.getValue(), maskContainer.getMask(), false,
|
||||
taskMonitor);
|
||||
|
||||
// If no match was found, currentPosition will be null.
|
||||
if (currentPosition == null) {
|
||||
|
@ -52,7 +52,6 @@ public class InstructionTableDataObject {
|
||||
// Some cell attributes.
|
||||
private Color backgroundColor;
|
||||
private Color foregroundColor = Colors.FOREGROUND;
|
||||
private int fontStyle;
|
||||
|
||||
// The border style of the cell. This is used to facilitate the 3D look of the cells
|
||||
// (bevel-styling).
|
||||
@ -172,10 +171,6 @@ public class InstructionTableDataObject {
|
||||
return foregroundColor;
|
||||
}
|
||||
|
||||
public int getFontStyle() {
|
||||
return fontStyle;
|
||||
}
|
||||
|
||||
public OperandState getState() {
|
||||
return state;
|
||||
}
|
||||
|
@ -133,13 +133,12 @@ public class InstructionSearchDialog extends ReusableDialogComponentProvider imp
|
||||
*
|
||||
* @param selection the current selection
|
||||
* @param plugin the parent plugin
|
||||
* @throws InvalidInputException if there's a problem loading instructions
|
||||
*/
|
||||
public void loadInstructions(ProgramSelection selection, InstructionSearchPlugin plugin)
|
||||
throws InvalidInputException {
|
||||
public void loadInstructions(ProgramSelection selection, InstructionSearchPlugin plugin) {
|
||||
|
||||
if (selection == null && getMessagePanel() != null) {
|
||||
getMessagePanel().setMessageText(
|
||||
MessagePanel msg = getMessagePanel();
|
||||
if (selection == null && msg != null) {
|
||||
msg.setMessageText(
|
||||
"Select instructions from the listing (and hit reload) to populate the table.",
|
||||
Messages.NORMAL);
|
||||
}
|
||||
@ -156,6 +155,26 @@ public class InstructionSearchDialog extends ReusableDialogComponentProvider imp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the instructions in the given selection and displays them in the gui.
|
||||
*
|
||||
* @param selection the current selection
|
||||
* @param plugin the parent plugin
|
||||
*/
|
||||
public void addToInstructions(ProgramSelection selection, InstructionSearchPlugin plugin) {
|
||||
|
||||
MessagePanel msg = getMessagePanel();
|
||||
if (selection == null && msg != null) {
|
||||
msg.setMessageText(
|
||||
"Select instructions from the listing (and hit add) to update the table.",
|
||||
Messages.NORMAL);
|
||||
}
|
||||
|
||||
if (selection != null && plugin.isSelectionValid(selection, this)) {
|
||||
addToSearchData(plugin.getCurrentProgram(), selection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads instructions at the given program/selection and populates the search
|
||||
* data object.
|
||||
@ -177,6 +196,20 @@ public class InstructionSearchDialog extends ReusableDialogComponentProvider imp
|
||||
}
|
||||
}
|
||||
|
||||
private void addToSearchData(Program currentProgram, ProgramSelection selection) {
|
||||
|
||||
if (selection == null || currentProgram == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
getSearchData().add(currentProgram, selection);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
Msg.error(this, "Error adding to search data", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ControlPanel getControlPanel() {
|
||||
return controlPanel;
|
||||
}
|
||||
|
@ -139,6 +139,7 @@ public class InstructionTable extends AbstractInstructionTable {
|
||||
createMaskAddressesBtn(toolbar1);
|
||||
toolbar1.addSeparator();
|
||||
createReloadBtn(toolbar1);
|
||||
createAddBtn(toolbar1);
|
||||
toolbar1.addSeparator();
|
||||
createManualEditBtn(toolbar1);
|
||||
toolbar1.addSeparator();
|
||||
@ -301,6 +302,14 @@ public class InstructionTable extends AbstractInstructionTable {
|
||||
createToolbarButton(buttonToolbar, icon, action, "reload");
|
||||
}
|
||||
|
||||
private void createAddBtn(JToolBar buttonToolbar) {
|
||||
Icon icon = Icons.ADD_ICON;
|
||||
Icon scaledIcon = ResourceManager.getScaledIcon(icon, ICON_SIZE, ICON_SIZE);
|
||||
Action action =
|
||||
new AddAction("undefined", scaledIcon, "Add selected instructions from listing");
|
||||
createToolbarButton(buttonToolbar, icon, action, "add");
|
||||
}
|
||||
|
||||
private void createManualEditBtn(JToolBar buttonToolbar) {
|
||||
Icon icon = new GIcon("icon.plugin.instructiontable.manual.entry");
|
||||
Icon scaledIcon = ResourceManager.getScaledIcon(icon, ICON_SIZE, ICON_SIZE);
|
||||
@ -410,6 +419,19 @@ public class InstructionTable extends AbstractInstructionTable {
|
||||
}
|
||||
}
|
||||
|
||||
private class AddAction extends AbstractAction {
|
||||
|
||||
public AddAction(String text, Icon icon, String desc) {
|
||||
super(text, icon);
|
||||
putValue(SHORT_DESCRIPTION, desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
dialog.addToInstructions(plugin.getProgramSelection(), plugin);
|
||||
}
|
||||
}
|
||||
|
||||
private class ManualEntryAction extends AbstractAction {
|
||||
|
||||
public ManualEntryAction(String text, Icon icon, String desc) {
|
||||
|
@ -15,7 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.instructionsearch.ui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.SwingConstants;
|
||||
@ -73,8 +74,6 @@ public class InstructionTableCellRenderer extends GhidraTableCellRenderer {
|
||||
private void setForegroundAttributes(InstructionTableDataObject dataObject,
|
||||
JLabel theRenderer) {
|
||||
theRenderer.setForeground(dataObject.getForegroundColor());
|
||||
Font newFont = theRenderer.getFont().deriveFont(dataObject.getFontStyle());
|
||||
theRenderer.setFont(newFont);
|
||||
}
|
||||
|
||||
private void setBackgroundAttributes(boolean isSelected, boolean hasFocus,
|
||||
|
@ -82,7 +82,10 @@ public class SelectionScopeWidget extends ControlPanelWidget {
|
||||
|
||||
searchRanges.clear();
|
||||
AddressRangeIterator iterator =
|
||||
plugin.getCurrentProgram().getMemory().getLoadedAndInitializedAddressSet().getAddressRanges();
|
||||
plugin.getCurrentProgram()
|
||||
.getMemory()
|
||||
.getLoadedAndInitializedAddressSet()
|
||||
.getAddressRanges();
|
||||
while (iterator.hasNext()) {
|
||||
searchRanges.add(iterator.next());
|
||||
}
|
||||
@ -175,14 +178,6 @@ public class SelectionScopeWidget extends ControlPanelWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a radio button with the given attributes.
|
||||
*
|
||||
* @param action
|
||||
* @param name
|
||||
* @param tooltip
|
||||
* @return
|
||||
*/
|
||||
private JRadioButton createSearchRB(AbstractAction action, String name, String tooltip) {
|
||||
GRadioButton button = new GRadioButton(action);
|
||||
button.setName(name);
|
||||
|
@ -215,6 +215,28 @@ public class InstructionSearchTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
assertEquals("EAX", obj72.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddInstructions() throws Exception {
|
||||
|
||||
// start selection
|
||||
// loadSelection("0x004065e1", "0x004065f2");
|
||||
|
||||
// sanity check
|
||||
assertEquals(8, instructionTable.getRowCount());
|
||||
assertInstructionValue(0, "INC EDI");
|
||||
assertInstructionValue(7, "MOV dword ptr [EBP + -0x4] EAX");
|
||||
|
||||
// Now create a selection to add an instruction and call 'add'
|
||||
createSelection("0x004065e6", "0x004065e6");
|
||||
pressButtonByName(component, "add");
|
||||
waitForTasks();
|
||||
|
||||
// grab the rebuilt table
|
||||
instructionTable = dialog.getTablePanel().getTable();
|
||||
assertEquals(9, instructionTable.getRowCount());
|
||||
assertInstructionValue(8, "PUSH EAX");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the {@link PreviewTable} is properly loaded when instructions are selected.
|
||||
*/
|
||||
@ -768,6 +790,18 @@ public class InstructionSearchTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
* PRIVATE METHODS
|
||||
********************************************************************************************/
|
||||
|
||||
private void assertInstructionValue(int row, String expectedText) {
|
||||
InstructionTableDataObject cell1 =
|
||||
(InstructionTableDataObject) instructionTable.getModel().getValueAt(row, 0);
|
||||
InstructionTableDataObject cell2 =
|
||||
(InstructionTableDataObject) instructionTable.getModel().getValueAt(row, 1);
|
||||
InstructionTableDataObject cell3 =
|
||||
(InstructionTableDataObject) instructionTable.getModel().getValueAt(row, 2);
|
||||
String actualText = cell1.getData() + " " + cell2.getData() + " " + cell3.getData();
|
||||
assertEquals("Instruction value in table was not as expected", expectedText,
|
||||
actualText.trim());
|
||||
}
|
||||
|
||||
private void closeDialog() {
|
||||
runSwing(() -> dialog.close());
|
||||
waitForSwing();
|
||||
|
Loading…
Reference in New Issue
Block a user