diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_Instruction_Patterns.htm b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_Instruction_Patterns.htm index f3529298f9..6c01549e03 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_Instruction_Patterns.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_Instruction_Patterns.htm @@ -46,36 +46,39 @@
-+
![]()
These tools provide ways to manipulate the Instruction Table and are discussed in detail below:
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatterns.png b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatterns.png index 106bd61756..7f2ac1a55d 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatterns.png and b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatterns.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTable.png b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTable.png index 332feed0c6..4df41f423b 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTable.png and b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTable.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTableToolbar.png b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTableToolbar.png index b3d5ebe951..29b4eea9fc 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTableToolbar.png and b/Ghidra/Features/Base/src/main/help/help/topics/Search/images/SearchInstructionPatternsInstructionTableToolbar.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/InstructionSearchPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/InstructionSearchPlugin.java index 2342ae5381..3189ec4c77 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/InstructionSearchPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/InstructionSearchPlugin.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/model/InstructionSearchData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/model/InstructionSearchData.java index 2f225cbe6f..11f21c083f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/model/InstructionSearchData.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/model/InstructionSearchData.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/model/InstructionTableDataObject.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/model/InstructionTableDataObject.java index f81ed99f13..b1d691f843 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/model/InstructionTableDataObject.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/model/InstructionTableDataObject.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionSearchDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionSearchDialog.java index 2053b27477..2599bcf382 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionSearchDialog.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionSearchDialog.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTable.java index 6499b49a69..32418ed56d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTable.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTable.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTableCellRenderer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTableCellRenderer.java index 6c745e4e05..874ca09384 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTableCellRenderer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/InstructionTableCellRenderer.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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, diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/SelectionScopeWidget.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/SelectionScopeWidget.java index 70ce48205a..951b44c34d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/SelectionScopeWidget.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/instructionsearch/ui/SelectionScopeWidget.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/instructionsearch/InstructionSearchTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/instructionsearch/InstructionSearchTest.java index 332c9f10a3..95d9ce4bc4 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/instructionsearch/InstructionSearchTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/instructionsearch/InstructionSearchTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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();-
- [
] Clears +
- [
-] Clears all masks.
- [
] Masks all +
- [
-] Masks all data (non-instructions).
- [
] Masks all +
- [
-] Masks all operands.
- [
] Masks all +
- [
-] Masks all scalar operands.
- [
] Masks all +
- [
-] Masks all address operands.
- [
] Reloads the +
- [
-] Reloads the table from what is currently selected in the listing.
- [
] Allows +
- [
+ +] Add to the table what is currently + selected in the listing.
- [
-] Allows users to manually enter bytes to be loaded.
- [
] Navigates in +
- [
] Navigates in the listing to the location defined by this set of instructions.