diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/AsciiSearchFormat.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/AsciiSearchFormat.java deleted file mode 100644 index 15b6643937..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/AsciiSearchFormat.java +++ /dev/null @@ -1,169 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import javax.swing.*; -import javax.swing.border.TitledBorder; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import docking.widgets.checkbox.GCheckBox; -import docking.widgets.combobox.GComboBox; -import docking.widgets.label.GDLabel; -import ghidra.util.StringUtilities; - -public class AsciiSearchFormat extends SearchFormat { - private JLabel searchType; - private JComboBox encodingCB; - private JCheckBox caseSensitiveCkB; - private JCheckBox escapeSequencesCkB; - private Charset[] supportedCharsets = - { StandardCharsets.US_ASCII, StandardCharsets.UTF_8, StandardCharsets.UTF_16 }; - - public AsciiSearchFormat(ChangeListener listener) { - super("String", listener); - } - - @Override - public String getToolTip() { - return "Interpret value as a sequence of characters."; - } - - @Override - public JPanel getOptionsPanel() { - ActionListener al = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - changeListener.stateChanged(new ChangeEvent(this)); - } - }; - searchType = new GDLabel("Encoding: "); - - encodingCB = new GComboBox<>(supportedCharsets); - encodingCB.setName("Encoding Options"); - encodingCB.setSelectedIndex(0); - encodingCB.addActionListener(al); - - caseSensitiveCkB = new GCheckBox("Case Sensitive"); - caseSensitiveCkB.setToolTipText("Allows for case sensitive searching."); - caseSensitiveCkB.addActionListener(al); - - escapeSequencesCkB = new GCheckBox("Escape Sequences"); - escapeSequencesCkB.setToolTipText( - "Allows specifying control characters using escape sequences " + - "(i.e., allows \\n to be searched for as a single line feed character)."); - escapeSequencesCkB.addActionListener(al); - - JPanel stringOptionsPanel = new JPanel(); - stringOptionsPanel.setLayout(new BoxLayout(stringOptionsPanel, BoxLayout.Y_AXIS)); - stringOptionsPanel.setBorder(new TitledBorder("Format Options")); - JPanel encodingOptionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - encodingOptionsPanel.add(searchType); - encodingOptionsPanel.add(encodingCB); - stringOptionsPanel.add(encodingOptionsPanel); - JPanel caseSensitivePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - caseSensitivePanel.add(caseSensitiveCkB); - stringOptionsPanel.add(caseSensitivePanel); - JPanel escapeSequencesPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - escapeSequencesPanel.add(escapeSequencesCkB); - stringOptionsPanel.add(escapeSequencesPanel); - return stringOptionsPanel; - } - - @Override - public boolean usesEndieness() { - return encodingCB.getSelectedItem() == StandardCharsets.UTF_16; // Only UTF-16 uses Endianness. - } - - @Override - public SearchData getSearchData(String input) { - final byte MASK_BYTE = (byte) 0xdf; - - int inputLength = input.length(); - Charset encodingSelection = (Charset) encodingCB.getSelectedItem(); - if (encodingSelection == StandardCharsets.UTF_16) { - encodingSelection = - (isBigEndian) ? StandardCharsets.UTF_16BE : StandardCharsets.UTF_16LE; - } - - //Escape sequences in the "input" are 2 Characters long. - if (escapeSequencesCkB.isSelected() && inputLength >= 2) { - input = StringUtilities.convertEscapeSequences(input); - } - byte[] byteArray = input.getBytes(encodingSelection); - byte[] maskArray = new byte[byteArray.length]; - Arrays.fill(maskArray, (byte) 0xff); - - // Time to mask some bytes for case insensitive searching. - if (!caseSensitiveCkB.isSelected()) { - int i = 0; - while (i < byteArray.length) { - if (encodingSelection == StandardCharsets.US_ASCII && - Character.isLetter(byteArray[i])) { - maskArray[i] = MASK_BYTE; - i++; - } - else if (encodingSelection == StandardCharsets.UTF_8) { - int numBytes = bytesPerCharUTF8(byteArray[i]); - if (numBytes == 1 && Character.isLetter(byteArray[i])) { - maskArray[i] = MASK_BYTE; - } - i += numBytes; - } - // Assumes UTF-16 will return 2 Bytes for each character. - // 4-byte UTF-16 will never satisfy the below checks because - // none of their bytes can ever be 0. - else if (encodingSelection == StandardCharsets.UTF_16BE) { - if (byteArray[i] == (byte) 0x0 && Character.isLetter(byteArray[i + 1])) { // Checks if ascii character. - maskArray[i + 1] = MASK_BYTE; - } - i += 2; - } - else if (encodingSelection == StandardCharsets.UTF_16LE) { - if (byteArray[i + 1] == (byte) 0x0 && Character.isLetter(byteArray[i])) { // Checks if ascii character. - maskArray[i] = MASK_BYTE; - } - i += 2; - } - else { - i++; - } - } - } - - return SearchData.createSearchData(input, byteArray, maskArray); - } - - private int bytesPerCharUTF8(byte zByte) { - // This method is intended for UTF-8 encoding. - // The first byte in a sequence of UTF-8 bytes can tell - // us how many bytes make up a char. - int offset = 1; - // If the char is ascii, this loop will be skipped. - while ((zByte & 0x80) != 0x00) { - zByte <<= 1; - offset++; - } - return offset; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/BinarySearchFormat.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/BinarySearchFormat.java deleted file mode 100644 index 9b4f19934e..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/BinarySearchFormat.java +++ /dev/null @@ -1,116 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import java.util.*; - -import javax.swing.event.ChangeListener; - -import ghidra.util.HTMLUtilities; - -public class BinarySearchFormat extends SearchFormat { - private static final String VALID_CHARS = "01x?."; - private String statusText; - - public BinarySearchFormat(ChangeListener listener) { - super("Binary", listener); - } - - @Override - public String getToolTip() { - return HTMLUtilities.toHTML( - "Interpret value as a sequence of binary digits.\n"+ - "Spaces will start the next byte. Bit sequences less\n"+ - "than 8 bits are padded with 0's to the left. \n"+ - "Enter 'x', '.' or '?' for a wildcard bit"); - } - - @Override - public SearchData getSearchData(String input) { - StringTokenizer st = new StringTokenizer(input); - int n = st.countTokens(); - byte[] bytes = new byte[n]; - byte[] mask = new byte[n]; - - int index = 0; - while (st.hasMoreTokens()) { - String token = st.nextToken(); - - if (!isValidBinary(token)) { - return SearchData.createInvalidInputSearchData(statusText); - } - bytes[index] = getByte(token); - mask[index] = getMask(token); - index++; - } - return SearchData.createSearchData(input, bytes, mask); - } - - private boolean isValidBinary(String str) { - if (str.length() > 8) { - statusText = "Max group size exceeded. Enter to add more."; - return false; - } - statusText = ""; - for(int i=0;i bytesList = new ArrayList<>(); - StringTokenizer tokenizer = new StringTokenizer(input); - while (tokenizer.hasMoreTokens()) { - String tok = tokenizer.nextToken(); - if (tok.equals(MINUS_SIGN)) { - if (!input.endsWith(MINUS_SIGN)) { - return SearchData.createInvalidInputSearchData("Cannot have space after a '-'"); - } - return SearchData.createIncompleteSearchData(""); - } - try { - bytesList.addAll(getBytes(tok)); - } - catch (NumberFormatException ex) { - return SearchData.createInvalidInputSearchData(""); - } - catch (RuntimeException re) { - return SearchData.createInvalidInputSearchData(re.getMessage()); - } - } - return SearchData.createSearchData(input, getDataBytes(bytesList), null); - } - - private byte[] getDataBytes(List bytesList) { - byte[] bytes = new byte[bytesList.size()]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = (bytesList.get(i)).byteValue(); - } - return bytes; - } - - private List getBytes(long value, int n) { - List list = new ArrayList<>(); - - for (int i = 0; i < n; i++) { - byte b = (byte) value; - list.add(Byte.valueOf(b)); - value >>= 8; - } - if (isBigEndian) { - Collections.reverse(list); - } - return list; - } - - private void checkValue(long value, long min, long max) { - if (value < min || value > max) { - // I know, icky - throw new RuntimeException("Number must be in the range [" + min + "," + max + "]"); - } - } - - private List getBytes(String tok) { - switch (decimalFormat) { - case BYTE: - long value = Short.parseShort(tok); - checkValue(value, Byte.MIN_VALUE, 255); - return getBytes(value, 1); - case WORD: - value = Integer.parseInt(tok); - checkValue(value, Short.MIN_VALUE, 65535); - return getBytes(value, 2); - case DWORD: - value = Long.parseLong(tok); - checkValue(value, Integer.MIN_VALUE, 4294967295l); - return getBytes(value, 4); - case QWORD: - value = Long.parseLong(tok); - return getBytes(value, 8); - case FLOAT: - tok = preProcessFloat(tok); - float floatValue = Float.parseFloat(tok); - value = Float.floatToIntBits(floatValue); - return getBytes(value, 4); - case DOUBLE: - tok = preProcessFloat(tok); - double dvalue = Double.parseDouble(tok); - value = Double.doubleToLongBits(dvalue); - return getBytes(value, 8); - default: - throw new AssertException("Unexpected format type"); - } - } - - /** - * Checks for parsable characters that we don't want to allow (dDfF) and removes - * the start of an exponent expression (example 2.34e would become 2.34. So woudl 2.34-) - * @param the string that will be parsed into a float or double - * @return the parsable string - * @exception NumberFormatException thrown if the the tok contains any of "dDfF". - */ - private String preProcessFloat(String tok) { - if ((tok.indexOf('d') >= 0) || (tok.indexOf('D') >= 0) || (tok.indexOf('F') >= 0) || - (tok.indexOf('f') >= 0)) { - throw new NumberFormatException(); - } - if (tok.endsWith("E") || tok.endsWith("e")) { - tok = tok.substring(0, tok.length() - 1); - } - if (tok.endsWith("E-") || tok.endsWith("e-")) { - tok = tok.substring(0, tok.length() - 2); - } - - return tok; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/HexSearchFormat.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/HexSearchFormat.java deleted file mode 100644 index e4b564d590..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/HexSearchFormat.java +++ /dev/null @@ -1,158 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import java.util.*; - -import javax.swing.event.ChangeListener; - -import ghidra.util.HTMLUtilities; - -public class HexSearchFormat extends SearchFormat { - - private static final String WILD_CARDS = ".?"; - private static final String HEX_CHARS = "0123456789abcdefABCDEF" + WILD_CARDS; - private String statusText; - - public HexSearchFormat(ChangeListener listener) { - super("Hex", listener); - } - - @Override - public String getToolTip() { - return HTMLUtilities.toHTML("Interpret value as a sequence of\n" + - "hex numbers, separated by spaces.\n" + "Enter '.' or '?' for a wildcard match"); - } - - @Override - public SearchData getSearchData(String input) { - List list = new ArrayList(); - StringTokenizer st = new StringTokenizer(input); - while (st.hasMoreTokens()) { - String token = st.nextToken(); - - if (!isValidHex(token)) { - return SearchData.createInvalidInputSearchData(statusText); - } - - List byteList = getByteStrings(token); - if (!isBigEndian) { - Collections.reverse(byteList); - } - list.addAll(byteList); - } - byte[] bytes = new byte[list.size()]; - byte[] mask = new byte[list.size()]; - for (int i = 0; i < list.size(); i++) { - String byteString = list.get(i); - bytes[i] = getByte(byteString); - mask[i] = getMask(byteString); - } - return SearchData.createSearchData(input, bytes, mask); - } - - private List getByteStrings(String token) { - - if (isSingleWildCardChar(token)) { - // treat single wildcards as a double wildcard entry, as this is more intuitive to users - token += token; - } - else if (token.length() % 2 != 0) { - // pad an odd number of nibbles with 0; assuming users leave off leading 0 - token = "0" + token; - } - - int n = token.length() / 2; - List list = new ArrayList(n); - for (int i = 0; i < n; i++) { - list.add(token.substring(i * 2, i * 2 + 2)); - } - return list; - } - - private boolean isSingleWildCardChar(String token) { - if (token.length() == 1) { - char c = token.charAt(0); - return WILD_CARDS.indexOf(c) >= 0; - } - return false; - } - - private boolean isValidHex(String str) { - if (str.length() > 16) { - statusText = "Max group size exceeded. Enter to add more."; - return false; - } - statusText = ""; - for (int i = 0; i < str.length(); i++) { - if (HEX_CHARS.indexOf(str.charAt(i)) < 0) { - return false; - } - } - return true; - } - - /** - * Returns the byte value to be used for the given hex bytes. Handles wildcard characters by - * return treating them as 0s. - */ - private byte getByte(String tok) { - char c1 = tok.charAt(0); - char c2 = tok.charAt(1); - // note: the hexValueOf() method will turn wildcard chars into 0s - return (byte) (hexValueOf(c1) * 16 + hexValueOf(c2)); - } - - /** - * Returns the search mask for the given hex byte string. Normal hex digits result - * in a "1111" mask and wildcard digits result in a "0000" mask. - */ - private byte getMask(String tok) { - char c1 = tok.charAt(0); - char c2 = tok.charAt(1); - int index1 = WILD_CARDS.indexOf(c1); - int index2 = WILD_CARDS.indexOf(c2); - if (index1 >= 0 && index2 >= 0) { - return (byte) 0x00; - } - if (index1 >= 0 && index2 < 0) { - return (byte) 0x0F; - } - if (index1 < 0 && index2 >= 0) { - return (byte) 0xF0; - } - return (byte) 0xFF; - } - - /** - * Returns the value of the given hex digit character. - */ - private int hexValueOf(char c) { - if ((c >= '0') && (c <= '9')) { - return c - '0'; - } - else if ((c >= 'a') && (c <= 'f')) { - return c - 'a' + 10; - } - else if ((c >= 'A') && (c <= 'F')) { - return c - 'A' + 10; - } - else { - return 0; - } - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchDialog.java deleted file mode 100644 index 061e2e1598..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchDialog.java +++ /dev/null @@ -1,848 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import java.awt.*; -import java.awt.event.ActionListener; -import java.awt.event.ItemListener; -import java.util.*; -import java.util.List; - -import javax.swing.*; -import javax.swing.border.TitledBorder; -import javax.swing.event.ChangeListener; -import javax.swing.text.*; - -import docking.*; -import docking.widgets.button.GRadioButton; -import docking.widgets.checkbox.GCheckBox; -import docking.widgets.combobox.GhidraComboBox; -import docking.widgets.label.GDLabel; -import docking.widgets.label.GLabel; -import ghidra.app.util.HelpTopics; -import ghidra.framework.plugintool.PluginTool; -import ghidra.util.*; -import ghidra.util.exception.InvalidInputException; -import ghidra.util.layout.VariableRowHeightGridLayout; -import ghidra.util.layout.VerticalLayout; -import ghidra.util.search.memory.CodeUnitSearchInfo; -import ghidra.util.search.memory.SearchInfo; -import ghidra.util.task.Task; - -/** - * Dialog for MemSearch Plugin. - * Displays a set of formats for search string. - * The user can type in a hex, ascii or decimal string to search - * for among the memory bytes. The user can indicate forward or - * backward searching, proper endianness and whether to find just - * the next occurrence or all of them in the program. - */ -class MemSearchDialog extends ReusableDialogComponentProvider { - - static final String ADVANCED_BUTTON_NAME = "mem.search.advanced"; - private static final String CODE_UNIT_SCOPE_NAME = "Code Unit Scope"; - private static final int DEFAULT_MAX_ENTRIES = 10; - public static final String ENTER_TEXT_MESSAGE = "Please enter a search value"; - private static final SearchData DEFAULT_SEARCH_DATA = - SearchData.createInvalidInputSearchData(ENTER_TEXT_MESSAGE); - - // The fields that use this value take in user input. If the user input large, it affects - // the minimum and preferred size of the the fields. This, in turn, affects how this dialog - // gets packed when the Advanced button is toggled. Without using a size restriction, this - // dialog's contents may move as the dialog is re-packed. - private static final int INPUT_FIELD_MIN_SIZE_WIDTH = 140; - private static final int INPUT_FIELD_MIN_SIZE_HEIGHT = 25; - - MemSearchPlugin plugin; - boolean isMnemonic; - - private JButton nextButton; - private JButton previousButton; - private JButton allButton; - private GhidraComboBox valueComboBox; - private List history = new LinkedList<>(); - private JLabel hexSeqField; - private CardLayout formatOptionsLayout; - private JRadioButton searchSelectionRadioButton; - private JLabel alignLabel; - private JTextField alignField; - private JPanel formatOptionsPanel; - private JRadioButton loadedBlocks; - private JRadioButton allBlocks; - private boolean navigatableHasSelection; - - private ChangeListener changeListener = e -> updateDisplay(); - - private SearchData searchData = DEFAULT_SEARCH_DATA; - private SearchFormat[] allFormats = new SearchFormat[] { new HexSearchFormat(changeListener), - new AsciiSearchFormat(changeListener), new DecimalSearchFormat(changeListener), - new BinarySearchFormat(changeListener), new RegExSearchFormat(changeListener) }; - private SearchFormat currentFormat = allFormats[0]; - private JRadioButton littleEndian; - private JRadioButton bigEndian; - - private Container advancedPanel; - private JRadioButton searchAllRadioButton; - private List codeUnitTypesList; - private JPanel mainPanel; - private JToggleButton advancedButton; - - private boolean isSearching; - private boolean hasValidSearchData; - private boolean searchEnabled = true; - - MemSearchDialog(MemSearchPlugin plugin, boolean isBigEndian, boolean isMnemonic) { - super("Search Memory", false, true, true, true); - this.plugin = plugin; - this.isMnemonic = isMnemonic; - - setHelpLocation(new HelpLocation(HelpTopics.SEARCH, "Search_Memory")); - mainPanel = buildMainPanel(); - addWorkPanel(mainPanel); - buildButtons(); - setEndianness(isBigEndian); - setAlignment(1); - setUseSharedLocation(true); - } - - void setBytes(byte[] bytes) { - if (valueComboBox != null) { - valueComboBox.setText(null); - } - String convertBytesToString = NumericUtilities.convertBytesToString(bytes, " "); - valueComboBox.setText(convertBytesToString); - } - - void setAlignment(int alignment) { - alignField.setText("" + alignment); - } - - public void setSearchText(String maskedString) { - valueComboBox.setText(maskedString); - updateDisplay(); - } - - void setEndianness(boolean isBigEndian) { - if (isBigEndian) { - bigEndian.setSelected(isBigEndian); - } - else { - littleEndian.setSelected(true); - } - updateDisplay(); - } - - @Override - public void taskCancelled(Task task) { - super.taskCancelled(task); - isSearching = false; - updateSearchButtonEnablement(); - clearStatusText(); - } - - @Override - public void taskCompleted(Task task) { - super.taskCompleted(task); - isSearching = false; - updateSearchButtonEnablement(); - } - - private void setEndianEnabled(boolean enabled) { - littleEndian.setEnabled(enabled); - bigEndian.setEnabled(enabled); - } - - @Override - protected void executeProgressTask(Task task, int delay) { - super.executeProgressTask(task, delay); - } - - private void updateSearchButtonEnablement() { - nextButton.setEnabled(searchEnabled && !isSearching && hasValidSearchData); - previousButton.setEnabled(searchEnabled && !isSearching && hasValidSearchData && - currentFormat.supportsBackwardsSearch()); - allButton.setEnabled(searchEnabled && !isSearching && hasValidSearchData); - } - - void setHasSelection(boolean hasSelection, boolean autoRestrictSelection) { - searchSelectionRadioButton.setEnabled(hasSelection); - if (navigatableHasSelection == hasSelection) { - return; - } - if (autoRestrictSelection) { - navigatableHasSelection = hasSelection; - if (hasSelection && !isMnemonic) { - searchSelectionRadioButton.setSelected(true); - } - else { - searchAllRadioButton.setSelected(true); - } - } - } - - @Override - protected void dismissCallback() { - valueComboBox.setText(null); - hexSeqField.setText(null); - cancelCurrentTask(); - close(); - } - - void show(ComponentProvider provider) { - clearStatusText(); - valueComboBox.requestFocus(); - valueComboBox.selectAll(); - PluginTool tool = plugin.getTool(); - tool.showDialog(MemSearchDialog.this, provider); - } - - private void addToHistory(String input) { - history.remove(input); - history.add(0, input); - truncateHistoryAsNeeded(); - updateCombo(); - } - - private void updateCombo() { - String[] list = new String[history.size()]; - history.toArray(list); - valueComboBox.setModel(new DefaultComboBoxModel<>(list)); - } - - private void truncateHistoryAsNeeded() { - int maxEntries = DEFAULT_MAX_ENTRIES; - int historySize = history.size(); - - if (historySize > maxEntries) { - int numToRemove = historySize - maxEntries; - - for (int i = 0; i < numToRemove; i++) { - history.remove(history.size() - 1); - } - } - } - - private CodeUnitSearchInfo createCodeUnitSearchInfo() { - return new CodeUnitSearchInfo(codeUnitTypesList.get(0).isSelected(), - codeUnitTypesList.get(1).isSelected(), codeUnitTypesList.get(2).isSelected()); - } - - private void nextPreviousCallback(boolean forward) { - int alignment = 1; - try { - alignment = getAlignment(); - } - catch (InvalidInputException e) { - plugin.disableSearchAgain(); - setStatusText(e.getMessage()); - alignField.selectAll(); - return; - } - if (searchData.isValidSearchData()) { - if (plugin.searchOnce(new SearchInfo(searchData, 1, - searchSelectionRadioButton.isSelected(), forward, alignment, allBlocks.isSelected(), - createCodeUnitSearchInfo(), plugin.createTaskListener()))) { - addToHistory(valueComboBox.getText()); - setStatusText("Searching..."); - isSearching = true; - updateSearchButtonEnablement(); - } - } - else { - plugin.disableSearchAgain(); - setStatusText(searchData.getStatusMessage()); - } - } - - private void allCallback() { - int alignment = 1; - try { - alignment = getAlignment(); - } - catch (InvalidInputException e) { - plugin.disableSearchAgain(); - setStatusText(e.getMessage()); - alignField.selectAll(); - return; - } - if (searchData.isValidSearchData()) { - - if (plugin.searchAll(new SearchAllSearchInfo(searchData, plugin.getSearchLimit(), - searchSelectionRadioButton.isSelected(), true, alignment, allBlocks.isSelected(), - createCodeUnitSearchInfo()))) { - addToHistory(valueComboBox.getText()); - setStatusText("Searching..."); - isSearching = true; - updateSearchButtonEnablement(); - } - } - else { - plugin.disableSearchAgain(); - setStatusText(searchData.getStatusMessage()); - } - } - - private JPanel buildSearchPanel() { - JPanel labelPanel = new JPanel(); - labelPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); - labelPanel.setLayout(new GridLayout(0, 1)); - labelPanel.add(new GLabel("Search Value: ")); - labelPanel.add(new GLabel("Hex Sequence: ")); - - JPanel inputPanel = new JPanel(); - inputPanel.setLayout(new GridLayout(0, 1)); - valueComboBox = new GhidraComboBox<>(); - valueComboBox.setAutoCompleteEnabled(false); // we do our own completion with validation - valueComboBox.setEditable(true); - valueComboBox.setToolTipText(currentFormat.getToolTip()); - valueComboBox.setDocument(new RestrictedInputDocument()); - valueComboBox.addActionListener(ev -> { - if (nextButton.isEnabled()) { - nextPreviousCallback(true); - } - }); - - inputPanel.add(valueComboBox); - hexSeqField = new GDLabel(); - hexSeqField.setName("HexSequenceField"); - hexSeqField.setBorder(BorderFactory.createLoweredBevelBorder()); - - // see note for field minimum size field above - Dimension size = new Dimension(INPUT_FIELD_MIN_SIZE_WIDTH, INPUT_FIELD_MIN_SIZE_HEIGHT); - valueComboBox.setPreferredSize(size); - valueComboBox.setMinimumSize(size); - - hexSeqField.setPreferredSize(size); - hexSeqField.setMinimumSize(size); - - inputPanel.add(hexSeqField); - - JPanel searchPanel = new JPanel(new BorderLayout()); - searchPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); - searchPanel.add(labelPanel, BorderLayout.WEST); - searchPanel.add(inputPanel, BorderLayout.CENTER); - return searchPanel; - } - - private SearchFormat findFormat(String name) { - for (SearchFormat element : allFormats) { - if (element.getName().equals(name)) { - return element; - } - } - return allFormats[0]; - } - - private JPanel buildMainPanel() { - - JPanel newMainPanel = new JPanel(); - - newMainPanel.setLayout(new BorderLayout()); - newMainPanel.add(buildSearchPanel(), BorderLayout.NORTH); - newMainPanel.add(buildOptionsPanel(), BorderLayout.CENTER); - advancedPanel = buildAdvancedPanel(); - - JPanel searchOptionsPanel = new JPanel(new BorderLayout()); - newMainPanel.add(searchOptionsPanel, BorderLayout.SOUTH); - - return newMainPanel; - } - - private void setAdvancedPanelVisible(boolean visible) { - if (visible) { - mainPanel.add(advancedPanel, BorderLayout.EAST); - } - else { - mainPanel.remove(advancedPanel); - } - repack(); - } - - private Container createSeparatorPanel() { - JPanel panel = new JPanel(new GridLayout(1, 1)); - panel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 10)); - panel.add(new JSeparator(SwingConstants.VERTICAL)); - return panel; - } - - private Container buildAdvancedPanel() { - JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 10)); - panel.add(createSeparatorPanel(), BorderLayout.WEST); - panel.add(buildAdvancedPanelContents()); - return panel; - } - - private Container buildAdvancedPanelContents() { - JPanel panel = new JPanel(new VerticalLayout(5)); - panel.add(buildEndienessPanel()); - panel.add(buildCodeUnitTypesPanel()); - panel.add(buildAlignmentPanel()); - return panel; - } - - private Container buildEndienessPanel() { - ButtonGroup endianGroup = new ButtonGroup(); - littleEndian = new GRadioButton("Little Endian", true); - bigEndian = new GRadioButton("Big Endian", false); - endianGroup.add(bigEndian); - endianGroup.add(littleEndian); - - littleEndian.addActionListener(ev -> { - currentFormat.setEndieness(false); - updateDisplay(); - }); - bigEndian.addActionListener(ev -> { - currentFormat.setEndieness(true); - updateDisplay(); - }); - - JPanel endianPanel = new JPanel(); - endianPanel.setLayout(new BoxLayout(endianPanel, BoxLayout.Y_AXIS)); - endianPanel.add(littleEndian); - endianPanel.add(bigEndian); - endianPanel.setBorder(BorderFactory.createTitledBorder("Byte Order")); - - return endianPanel; - } - - private Container buildCodeUnitTypesPanel() { - final JCheckBox instructionsCheckBox = new GCheckBox("Instructions", true); - final JCheckBox definedCheckBox = new GCheckBox("Defined Data", true); - final JCheckBox undefinedCheckBox = new GCheckBox("Undefined Data", true); - - ItemListener stateListener = e -> validate(); - - codeUnitTypesList = new ArrayList<>(); - codeUnitTypesList.add(instructionsCheckBox); - codeUnitTypesList.add(definedCheckBox); - codeUnitTypesList.add(undefinedCheckBox); - - instructionsCheckBox.addItemListener(stateListener); - definedCheckBox.addItemListener(stateListener); - undefinedCheckBox.addItemListener(stateListener); - - JPanel codeUnitTypePanel = new JPanel(); - codeUnitTypePanel.setLayout(new BoxLayout(codeUnitTypePanel, BoxLayout.Y_AXIS)); - codeUnitTypePanel.add(instructionsCheckBox); - codeUnitTypePanel.add(definedCheckBox); - codeUnitTypePanel.add(undefinedCheckBox); - codeUnitTypePanel.setBorder(BorderFactory.createTitledBorder(CODE_UNIT_SCOPE_NAME)); - - return codeUnitTypePanel; - } - - private Component buildSelectionPanel() { - JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); - panel.setBorder(new TitledBorder("Selection Scope")); - - searchSelectionRadioButton = new GRadioButton("Search Selection"); - searchAllRadioButton = new GRadioButton("Search All"); - - ButtonGroup buttonGroup = new ButtonGroup(); - buttonGroup.add(searchSelectionRadioButton); - buttonGroup.add(searchAllRadioButton); - - searchAllRadioButton.setSelected(true); - - panel.add(searchAllRadioButton); - panel.add(searchSelectionRadioButton); - - JPanel selectionPanel = new JPanel(); - selectionPanel.setLayout(new BorderLayout()); - selectionPanel.add(panel, BorderLayout.NORTH); - return selectionPanel; - } - - private Component buildAlignmentPanel() { - alignLabel = new GDLabel("Alignment: "); - alignField = new JTextField(5); - alignField.setName("Alignment"); - alignField.setText("0"); - - JPanel alignPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - alignPanel.add(alignLabel); - alignPanel.add(alignField); - return alignPanel; - } - - /** - * Builds the basic format selection panel. - */ - private JPanel buildFormatPanel() { - JPanel formatPanel = new JPanel(); - formatPanel.setBorder(BorderFactory.createTitledBorder("Format")); - formatPanel.setLayout(new GridLayout(0, 1)); - - ButtonGroup formatGroup = new ButtonGroup(); - ActionListener formatButtonListener = ev -> { - String formatName = ((JRadioButton) ev.getSource()).getText(); - currentFormat = findFormat(formatName); - formatOptionsLayout.show(formatOptionsPanel, currentFormat.getName()); - updateDisplay(); - }; - - for (SearchFormat element : allFormats) { - GRadioButton formatButton = new GRadioButton(element.getName(), true); - formatButton.setToolTipText(element.getToolTip()); - - formatGroup.add(formatButton); - formatButton.addActionListener(formatButtonListener); - formatPanel.add(formatButton); - if (element.getName().equals("Binary") && isMnemonic) { - formatButton.setSelected(true); - currentFormat = element; - } - } - return formatPanel; - } - - private JPanel buildFormatOptionsPanel() { - formatOptionsPanel = new JPanel(); - formatOptionsLayout = new CardLayout(); - formatOptionsPanel.setLayout(formatOptionsLayout); - - for (SearchFormat element : allFormats) { - JPanel panel = element.getOptionsPanel(); - formatOptionsPanel.add(panel, element.getName()); - } - return formatOptionsPanel; - } - - /** - * builds the panel that contains the format Panel, options panel and the extras panel. - */ - private JPanel buildOptionsPanel() { - JPanel formatPanel = buildFormatPanel(); - formatOptionsPanel = buildFormatOptionsPanel(); - JPanel extrasPanel = buildExtrasPanel(); - - JPanel northPanel = new JPanel(); - northPanel.setLayout(new VariableRowHeightGridLayout(10, 10, 2)); - - northPanel.add(formatPanel); - northPanel.add(formatOptionsPanel); - northPanel.add(extrasPanel); - northPanel.add(buildSelectionPanel()); - - advancedButton = new JToggleButton("Advanced >>"); - advancedButton.setName(ADVANCED_BUTTON_NAME); - advancedButton.addActionListener(e -> { - boolean selected = advancedButton.isSelected(); - if (selected) { - advancedButton.setText("Advanced <<"); - } - else { - advancedButton.setText("Advanced >>"); - } - - setAdvancedPanelVisible(advancedButton.isSelected()); - }); - JPanel advancedButtonPanel = new JPanel(); - advancedButtonPanel.setLayout(new BoxLayout(advancedButtonPanel, BoxLayout.X_AXIS)); - advancedButtonPanel.add(Box.createHorizontalGlue()); - advancedButtonPanel.add(Box.createVerticalStrut(40)); - advancedButtonPanel.add(advancedButton); - - JPanel optionsPanel = new JPanel(); - optionsPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 20, 10)); - optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.Y_AXIS)); - optionsPanel.add(northPanel); - optionsPanel.add(advancedButtonPanel); - - return optionsPanel; - } - - /** - * builds the extras panel. - */ - private JPanel buildExtrasPanel() { - ButtonGroup memoryBlockGroup = new ButtonGroup(); - loadedBlocks = new GRadioButton("Loaded Blocks", true); - allBlocks = new GRadioButton("All Blocks", false); - memoryBlockGroup.add(loadedBlocks); - memoryBlockGroup.add(allBlocks); - - loadedBlocks.setToolTipText(HTMLUtilities - .toHTML("Only searches memory blocks that are loaded in a running executable.\n " + - "Ghidra now includes memory blocks for other data such as section headers.\n" + - "This option exludes these OTHER (non loaded) blocks.")); - allBlocks.setToolTipText( - "Searches all memory blocks including blocks that are not actually loaded in a running executable"); - - JPanel directionPanel = new JPanel(); - directionPanel.setLayout(new BoxLayout(directionPanel, BoxLayout.Y_AXIS)); - directionPanel.add(loadedBlocks); - directionPanel.add(allBlocks); - directionPanel.setBorder(BorderFactory.createTitledBorder("Memory Block Types")); - - JPanel extrasPanel = new JPanel(); - extrasPanel.setLayout(new BorderLayout()); - extrasPanel.add(directionPanel, BorderLayout.NORTH); - return extrasPanel; - } - - private void buildButtons() { - nextButton = new JButton("Next"); - nextButton.setMnemonic('N'); - nextButton.addActionListener(ev -> nextPreviousCallback(true)); - this.addButton(nextButton); - - previousButton = new JButton("Previous"); - previousButton.setMnemonic('P'); - previousButton.addActionListener(ev -> nextPreviousCallback(false)); - this.addButton(previousButton); - - allButton = new JButton("Search All"); - allButton.setMnemonic('A'); - allButton.addActionListener(ev -> allCallback()); - allButton.setName("Search All"); - this.addButton(allButton); - - addDismissButton(); - updateSearchButtonEnablement(); - - } - - private void updateSearchData(SearchData newSearchData) { - searchData = newSearchData; - hexSeqField.setText(searchData.getHexString()); - validate(); - } - - private void validate() { - - if (!searchData.isValidSearchData() || !searchData.isValidInputData()) { - setStatusText(searchData.getStatusMessage()); - hasValidSearchData = false; - } - else if (!isValidCodeUnitSearchType()) { - setStatusText("You must select at least one type of code unit to search in " + - CODE_UNIT_SCOPE_NAME); - hasValidSearchData = false; - } - else { - setStatusText(""); - hasValidSearchData = true; - } - updateSearchButtonEnablement(); - } - - private boolean isValidCodeUnitSearchType() { - for (JCheckBox checkBox : codeUnitTypesList) { - if (checkBox.isSelected()) { - return true; - } - } - return false; - } - - @Override - protected TaskScheduler getTaskScheduler() { - return super.getTaskScheduler(); - } - - private void updateDisplay() { - clearStatusText(); - - updateSearchData(); - - setEndianEnabled(currentFormat.usesEndieness()); - updateSearchButtonEnablement(); - valueComboBox.setToolTipText(currentFormat.getToolTip()); - } - - private void updateSearchData() { - currentFormat.setEndieness(bigEndian.isSelected()); - SearchData inputData = currentFormat.getSearchData(valueComboBox.getText()); - if (valueComboBox.getText().trim().length() != 0 && inputData.isValidInputData()) { - updateSearchData(inputData); - } - else { - valueComboBox.setText(""); - updateSearchData(DEFAULT_SEARCH_DATA); - } - } - - public int getAlignment() throws InvalidInputException { - String alignStr = alignField.getText(); - int len = 0; - try { - Integer ilen = Integer.decode(alignStr); - len = ilen.intValue(); - } - catch (NumberFormatException e) { - throw new InvalidInputException("The alignment must be a number greater than 0."); - } - if (len <= 0) { - throw new InvalidInputException("The alignment must be a number greater than 0."); - } - return len; - } - - /** - * Custom Document that validates user input on the fly. - */ - public class RestrictedInputDocument extends DefaultStyledDocument { - - /** - * Called before new user input is inserted into the entry text field. The super - * method is called if the input is accepted. - */ - @Override - public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { - clearStatusText(); - - // allow pasting numbers in the forms like 0xABC or ABCh - str = removeNumberBasePrefixAndSuffix(str); - - String currentText = getText(0, getLength()); - String beforeOffset = currentText.substring(0, offs); - String afterOffset = currentText.substring(offs, currentText.length()); - String proposedText = beforeOffset + str + afterOffset; - - // show history - String match = handleHistoryMatch(currentText, proposedText); - if (match != null) { - super.insertString(offs, match.substring(beforeOffset.length()), a); - valueComboBox.setSelectionStart(proposedText.length()); - valueComboBox.setSelectionEnd(match.length()); - return; - } - - // no history to show - SearchData inputData = currentFormat.getSearchData(proposedText); - if (inputData.isValidInputData()) { - updateSearchData(inputData); - super.insertString(offs, str, a); - } - else { - setStatusText(inputData.getStatusMessage()); - Toolkit.getDefaultToolkit().beep(); - } - } - - /** - * Called before the user deletes some text. If the result is valid, the super - * method is called. - */ - @Override - public void remove(int offs, int len) throws BadLocationException { - clearStatusText(); - - String currentText = getText(0, getLength()); - String beforeOffset = currentText.substring(0, offs); - String afterOffset = currentText.substring(len + offs, currentText.length()); - String proposedResult = beforeOffset + afterOffset; - - if (proposedResult.length() == 0) { - updateSearchData(DEFAULT_SEARCH_DATA); - super.remove(offs, len); - return; - } - - SearchData inputData = currentFormat.getSearchData(proposedResult); - if (inputData.isValidInputData()) { - super.remove(offs, len); - updateSearchData(inputData); - } - else { - Toolkit.getDefaultToolkit().beep(); - } - } - - private String handleHistoryMatch(String currentText, String proposedText) { - boolean textAppended = proposedText.startsWith(currentText); - String match = findHistoryMatchString(proposedText); - if (match != null && textAppended) { - SearchData matchData = currentFormat.getSearchData(match); - if (matchData.isValidInputData()) { - updateSearchData(matchData); - return match; - } - } - return null; - } - - private String findHistoryMatchString(String input) { - Iterator itr = history.iterator(); - while (itr.hasNext()) { - String historyString = itr.next(); - if (historyString.startsWith(input)) { - return historyString; - } - } - return null; - } - - private String removeNumberBasePrefixAndSuffix(String str) { - if (!(currentFormat instanceof HexSearchFormat || - currentFormat instanceof BinarySearchFormat)) { - return str; - } - - String numMaybe = str.strip(); - String lowercase = numMaybe.toLowerCase(); - if (currentFormat instanceof HexSearchFormat) { - if (lowercase.startsWith("0x")) { - numMaybe = numMaybe.substring(2); - } - else if (lowercase.startsWith("$")) { - numMaybe = numMaybe.substring(1); - } - else if (lowercase.endsWith("h")) { - numMaybe = numMaybe.substring(0, numMaybe.length() - 1); - } - } - else { - if (lowercase.startsWith("0b")) { - numMaybe = numMaybe.substring(2); - } - } - - // check if the resultant number looks valid for insertion (i.e. not empty) - if (!numMaybe.isEmpty()) { - return numMaybe; - } - return str; - } - } - - boolean getShowAdvancedOptions() { - return advancedButton.isSelected(); - } - - void setShowAdvancedOptions(boolean selected) { - if (advancedButton.isSelected() != selected) { - advancedButton.doClick(); - } - } - - void setSearchEnabled(boolean b) { - searchEnabled = b; - } - - void searchCompleted() { - isSearching = false; - updateSearchButtonEnablement(); - } - - String getSearchText() { - return valueComboBox.getText(); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchPlugin.java deleted file mode 100644 index 493b2689b6..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchPlugin.java +++ /dev/null @@ -1,907 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import java.awt.Color; -import java.util.*; - -import javax.swing.Icon; -import javax.swing.JComponent; - -import docking.*; -import docking.action.DockingAction; -import docking.action.MenuData; -import docking.tool.ToolConstants; -import docking.widgets.fieldpanel.support.Highlight; -import docking.widgets.table.threaded.*; -import generic.theme.GIcon; -import ghidra.GhidraOptions; -import ghidra.app.CorePluginPackage; -import ghidra.app.context.NavigatableActionContext; -import ghidra.app.context.NavigatableContextAction; -import ghidra.app.events.ProgramSelectionPluginEvent; -import ghidra.app.nav.Navigatable; -import ghidra.app.nav.NavigatableRemovalListener; -import ghidra.app.plugin.PluginCategoryNames; -import ghidra.app.plugin.core.table.TableComponentProvider; -import ghidra.app.services.*; -import ghidra.app.util.*; -import ghidra.app.util.query.TableService; -import ghidra.app.util.viewer.field.*; -import ghidra.app.util.viewer.proxy.ProxyObj; -import ghidra.framework.model.DomainObject; -import ghidra.framework.options.*; -import ghidra.framework.plugintool.*; -import ghidra.framework.plugintool.util.PluginStatus; -import ghidra.program.model.address.Address; -import ghidra.program.model.listing.CodeUnit; -import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.Memory; -import ghidra.program.model.mem.MemoryAccessException; -import ghidra.program.util.*; -import ghidra.util.HelpLocation; -import ghidra.util.Msg; -import ghidra.util.bean.opteditor.OptionsVetoException; -import ghidra.util.search.memory.*; -import ghidra.util.table.GhidraProgramTableModel; -import ghidra.util.task.*; - -/** - * Class to handle memory searching of code bytes in a program. - */ -//@formatter:off -@PluginInfo( - status = PluginStatus.DEPRECATED, - packageName = CorePluginPackage.NAME, - category = PluginCategoryNames.SEARCH, - shortDescription = "Search bytes in memory", - description = "This plugin searches bytes in memory; the search " + - "is based on a value entered as hex or decimal numbers, or strings." + - " The value may contain \"wildcards\" or regular expressions" + - " that will match any byte or nibble.", - servicesRequired = { ProgramManager.class, GoToService.class, TableService.class, CodeViewerService.class }, -// servicesProvided = { MemorySearchService.class }, - eventsConsumed = { ProgramSelectionPluginEvent.class } -) -//@formatter:on -public class MemSearchPlugin extends Plugin implements OptionsChangeListener, - DockingContextListener, NavigatableRemovalListener { - - /** Constant for read/writeConfig() for dialog options */ - private static final String SHOW_ADVANCED_OPTIONS = "Show Advanced Options"; - - private static final int MAX_PRE_POPULTATE_BYTE_COUNT = 20; - - private static final String PRE_POPULATE_MEM_SEARCH = "Pre-populate Memory Search"; - - private static final String AUTO_RESTRICT_SELECTION = - "Auto Restrict Memory Search on Selection"; - - private DockingAction searchAction; - private DockingAction searchAgainAction; - private MemSearchDialog searchDialog; - private GoToService goToService; - private int searchLimit; - private static final Icon SEARCH_MARKER_ICON = new GIcon("icon.base.search.marker"); - - private boolean doHighlight; - private int byteGroupSize; - private String byteDelimiter; - private boolean showAdvancedOptions; - private boolean prepopulateSearch; - private boolean autoRestrictSelection; - - private TableComponentProvider currentResultsTableProvider; - private TaskMonitor searchAllTaskMonitor; - private TableLoadingListener currentTableListener; - private volatile boolean waitingForSearchAll; - private SearchInfo searchInfo; - private Address lastMatchingAddress; - - private Navigatable navigatable; - private boolean isMnemonic = false; - - public MemSearchPlugin(PluginTool tool) { - super(tool); - - createActions(); - initializeOptionListeners(); - loadOptions(); - tool.addContextListener(this); - } - - @Override - public void dispose() { - tool.removeContextListener(this); - ToolOptions opt = tool.getOptions(ToolConstants.TOOL_OPTIONS); - opt.removeOptionsChangeListener(this); - - opt = tool.getOptions(SearchConstants.SEARCH_OPTION_NAME); - opt.removeOptionsChangeListener(this); - - if (searchAction != null) { - searchAction.dispose(); - searchAction = null; - } - if (searchAgainAction != null) { - searchAgainAction.dispose(); - searchAgainAction = null; - } - - if (searchAllTaskMonitor != null) { - searchAllTaskMonitor.cancel(); - } - - if (searchDialog != null) { - searchDialog.dispose(); - searchDialog = null; - } - goToService = null; - - super.dispose(); - } - - int getSearchLimit() { - return searchLimit; - } - - boolean searchAll(SearchInfo newSearchInfo) { - this.searchInfo = newSearchInfo; - return performSearch(newSearchInfo); - } - - boolean searchOnce(SearchInfo newSearchInfo) { - this.searchInfo = newSearchInfo; - return performSearch(searchInfo); - } - - private boolean performSearch(SearchInfo localSearchInfo) { - Program program = navigatable.getProgram(); - if (program == null) { - return false; - } - - searchAgainAction.setEnabled(true); - - if (localSearchInfo.isSearchAll()) { - waitingForSearchAll = true; - showIncrementalSearchResults(localSearchInfo); - } - else { - - Address start = getSearchStartAddress(localSearchInfo); - ProgramSelection selection = navigatable.getSelection(); - MemorySearchAlgorithm algorithm = - searchInfo.createSearchAlgorithm(program, start, selection); - MemSearcherTask task = new MemSearcherTask(searchInfo, algorithm); - searchDialog.executeProgressTask(task, 500); - } - return true; - } - - void disableSearchAgain() { - searchAgainAction.setEnabled(false); - } - - private Address getSearchStartAddress(SearchInfo localSearchInfo) { - ProgramLocation location = navigatable.getLocation(); - Address startAddress = location == null ? null : location.getAddress(); - if (startAddress == null) { - Program program = navigatable.getProgram(); - if (program == null) { - return null; - } - startAddress = localSearchInfo.isSearchForward() ? program.getMinAddress() - : program.getMaxAddress(); - } - - if (lastMatchingAddress == null) { - return startAddress; - } - - // start the search after the last matching search - CodeUnit cu = navigatable.getProgram().getListing().getCodeUnitContaining(startAddress); - if (cu.contains(lastMatchingAddress)) { - startAddress = localSearchInfo.isSearchForward() ? lastMatchingAddress.next() - : lastMatchingAddress.previous(); - } - return startAddress; - } - - protected void updateNavigatable(ActionContext context) { - if (context instanceof NavigatableActionContext) { - NavigatableActionContext navContext = ((NavigatableActionContext) context); - setNavigatable(navContext.getNavigatable()); - updateSelection(navContext); - } - } - - @Override - public void processEvent(PluginEvent event) { - - if (event instanceof ProgramSelectionPluginEvent) { - ProgramSelection selection = ((ProgramSelectionPluginEvent) event).getSelection(); - boolean hasSelection = !selection.isEmpty(); - - if (searchDialog != null) { - searchDialog.setHasSelection(hasSelection, autoRestrictSelection); - } - } - - } - -// @Override -// public void setIsMnemonic(boolean isMnemonic) { -// // provides the dialog with the knowledge of whether or not -// // the action being performed is a MnemonicSearchPlugin -// this.isMnemonic = isMnemonic; -// } - - private void setNavigatable(Navigatable newNavigatable) { - if (newNavigatable == navigatable) { - return; - } - if (navigatable != null) { - navigatable.removeNavigatableListener(this); - } - if (newNavigatable != null) { - newNavigatable.addNavigatableListener(this); - } - this.navigatable = newNavigatable; - - lastMatchingAddress = null; - if (searchDialog != null) { - searchDialog.setSearchEnabled(newNavigatable != null); - } - } - - @Override - protected void init() { - goToService = tool.getService(GoToService.class); - } - - private void invokeSearchDialog(NavigatableActionContext context) { - if (searchDialog == null) { - boolean isBigEndian = navigatable.getProgram().getLanguage().isBigEndian(); - searchDialog = new MemSearchDialog(this, isBigEndian, isMnemonic); - searchDialog.setShowAdvancedOptions(showAdvancedOptions); - } - else { - searchDialog.setEndianness(navigatable.getProgram().getLanguage().isBigEndian()); - searchDialog.close(); // close it to make sure it gets parented to the current focused window. - } - - byte[] searchBytes = getInitialSearchBytes(context); - if (searchBytes != null) { - searchDialog.setBytes(searchBytes); - } - - boolean hasSelection = context.hasSelection(); - searchDialog.setHasSelection(hasSelection, autoRestrictSelection); - searchDialog.show(context.getComponentProvider()); - } - - private byte[] getInitialSearchBytes(NavigatableActionContext context) { - if (!prepopulateSearch) { - return null; - } - - ProgramSelection selection = context.getSelection(); - if (selection == null || selection.isEmpty() || hasBigSelection(context)) { - return null; - } - // safe cast as size has already been checked. - int numAddresses = (int) selection.getNumAddresses(); - Address address = selection.getMinAddress(); - Memory memory = context.getProgram().getMemory(); - byte[] bytes = new byte[numAddresses]; - try { - int count = memory.getBytes(address, bytes); - if (count == numAddresses) { - return bytes; - } - } - catch (MemoryAccessException e) { - // fall through and return null - } - return null; - } - - private BytesFieldLocation getBytesFieldLocation(Address address) { - if (address == null) { - return null; - } - Program program = navigatable.getProgram(); - - return new BytesFieldLocation(program, address); - } - -// @Override -// public void search(byte[] bytes, NavigatableActionContext context) { -// setNavigatable(context.getNavigatable()); -// invokeSearchDialog(context); -// } -// -// @Override -// public void setSearchText(String maskedString) { -// searchDialog.setSearchText(maskedString); -// } - - private void createActions() { - searchAction = new NavigatableContextAction("Search Memory", getName(), false) { - @Override - public void actionPerformed(NavigatableActionContext context) { - setNavigatable(context.getNavigatable()); - invokeSearchDialog(context); - } - }; - searchAction.setHelpLocation(new HelpLocation(HelpTopics.SEARCH, searchAction.getName())); - String[] menuPath = new String[] { "&Search", "Memory (Deprecated)..." }; - searchAction.setMenuBarData(new MenuData(menuPath, "search")); - searchAction.setDescription("Search Memory for byte sequence"); - searchAction.addToWindowWhen(NavigatableActionContext.class); - tool.addAction(searchAction); - - searchAgainAction = new NavigatableContextAction("Repeat Memory Search", getName(), false) { - @Override - public void actionPerformed(NavigatableActionContext context) { - setNavigatable(context.getNavigatable()); - performSearch(searchInfo); - } - - @Override - protected boolean isEnabledForContext(NavigatableActionContext context) { - return searchInfo != null && super.isEnabledForContext(context); - } - }; - searchAgainAction - .setHelpLocation(new HelpLocation(HelpTopics.SEARCH, searchAgainAction.getName())); - menuPath = new String[] { "&Search", "Repeat Memory Search" }; - searchAgainAction.setMenuBarData(new MenuData(menuPath, "search")); - searchAgainAction.setDescription("Search Memory for byte sequence"); - searchAgainAction.addToWindowWhen(NavigatableActionContext.class); - tool.addAction(searchAgainAction); - - } - - private void initializeOptionListeners() { - ToolOptions opt = tool.getOptions(SearchConstants.SEARCH_OPTION_NAME); - opt.registerOption(PRE_POPULATE_MEM_SEARCH, true, null, - "Initializes memory search byte sequence from " + - "the current selection provided the selection is less than 10 bytes."); - opt.registerOption(AUTO_RESTRICT_SELECTION, true, null, - "Automactically adjusts memory searches restricted" + - " to the current selection, as selections comes and goes"); - opt.registerOption(SearchConstants.SEARCH_HIGHLIGHT_NAME, true, null, - "Toggles highlight search results"); - - opt.registerThemeColorBinding(SearchConstants.SEARCH_HIGHLIGHT_COLOR_OPTION_NAME, - SearchConstants.SEARCH_HIGHLIGHT_COLOR.getId(), null, - "The search result highlight color"); - opt.registerThemeColorBinding(SearchConstants.SEARCH_HIGHLIGHT_CURRENT_COLOR_OPTION_NAME, - SearchConstants.SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR.getId(), null, - "The search result highlight color for the currently selected match"); - - opt.addOptionsChangeListener(this); - - opt = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS); - opt.addOptionsChangeListener(this); - } - - private void loadOptions() { - - Options opt = tool.getOptions(SearchConstants.SEARCH_OPTION_NAME); - int newSearchLimit = - opt.getInt(GhidraOptions.OPTION_SEARCH_LIMIT, SearchConstants.DEFAULT_SEARCH_LIMIT); - if (newSearchLimit <= 0) { - throw new OptionsVetoException("Search limit must be greater than 0"); - } - searchLimit = newSearchLimit; - - if (searchInfo != null) { - searchInfo.setSearchLimit(newSearchLimit); - } - - prepopulateSearch = opt.getBoolean(PRE_POPULATE_MEM_SEARCH, true); - autoRestrictSelection = opt.getBoolean(AUTO_RESTRICT_SELECTION, true); - doHighlight = opt.getBoolean(SearchConstants.SEARCH_HIGHLIGHT_NAME, true); - - opt = tool.getOptions(GhidraOptions.CATEGORY_BROWSER_FIELDS); - byteGroupSize = opt.getInt(BytesFieldFactory.BYTE_GROUP_SIZE_MSG, 1); - byteDelimiter = opt.getString(BytesFieldFactory.DELIMITER_MSG, " "); - } - - @Override - public void optionsChanged(ToolOptions options, String optionName, Object oldValue, - Object newValue) { - loadOptions(); - } - - protected void updateSelection(NavigatableActionContext context) { - if (searchDialog != null) { - searchDialog.setHasSelection(context.hasSelection(), autoRestrictSelection); - } - } - - private boolean hasBigSelection(NavigatableActionContext context) { - if (!context.hasSelection()) { - return false; - } - ProgramSelection selection = context.getSelection(); - if (selection.getNumAddressRanges() > 1) { - return true; - } - return selection.getNumAddresses() > MAX_PRE_POPULTATE_BYTE_COUNT; - - } - - private void showIncrementalSearchResults(SearchInfo info) { - - Program program = navigatable.getProgram(); - - TableService query = tool.getService(TableService.class); - - searchDialog.setStatusText("Searching..."); - - MemSearchTableModel model = new MemSearchTableModel(tool, program, info, - getSearchStartAddress(info), navigatable.getSelection()); - - currentResultsTableProvider = - getTableResultsProvider(info.getSearchData(), program, model, query); - currentResultsTableProvider.installRemoveItemsAction(); - - currentTableListener = new TableLoadingListener(model); - model.addInitialLoadListener(currentTableListener); - - GThreadedTablePanel tablePanel = - currentResultsTableProvider.getThreadedTablePanel(); - searchAllTaskMonitor = tablePanel.getTaskMonitor(); - - installHighlightProvider(model, currentResultsTableProvider); - } - - private void searchFinished() { - searchDialog.searchCompleted(); - currentResultsTableProvider = null; - } - - @Override - protected boolean canCloseDomainObject(DomainObject dObj) { - if ((navigatable != null && navigatable.getProgram() == dObj) && isSearching()) { - tool.setStatusInfo("Can't close program while searching...", true); - return false; - } - return true; - } - - /*testing*/ boolean isSearching() { - if (waitingForSearchAll) { - return true; - } - if (searchDialog == null) { - return false; - } - - return searchDialog.getTaskScheduler().isBusy(); - } - - private TableComponentProvider getTableResultsProvider(SearchData searchData, - Program program, GhidraProgramTableModel model, - TableService tableService) { - - String searchString = searchDialog.getSearchText(); - String title = "Search Memory - \"" + searchString + "\""; - String type = "Search"; - if (navigatable.supportsMarkers()) { - return tableService.showTableWithMarkers(title, type, model, - SearchConstants.SEARCH_HIGHLIGHT_COLOR, SEARCH_MARKER_ICON, type, navigatable); - } - return tableService.showTable(title, type, model, type, navigatable); - } - - private void installHighlightProvider(MemSearchTableModel model, - TableComponentProvider provider) { - Program program = navigatable.getProgram(); - new SearchTableHighlightHandler(navigatable, model, provider, program); - } - - private void installHighlightProvider(MemSearcherTask searcher, - TableComponentProvider provider) { - Program program = navigatable.getProgram(); - new TaskHighlightHandler(navigatable, searcher, provider, program); - } - - @Override - public void readConfigState(SaveState saveState) { - showAdvancedOptions = saveState.getBoolean(SHOW_ADVANCED_OPTIONS, false); - if (searchDialog != null) { - searchDialog.setShowAdvancedOptions(showAdvancedOptions); - } - } - - @Override - public void writeConfigState(SaveState saveState) { - if (searchDialog != null) { - saveState.putBoolean(SHOW_ADVANCED_OPTIONS, searchDialog.getShowAdvancedOptions()); - } - } - - @Override - public void navigatableRemoved(Navigatable removedNavigatable) { - setNavigatable(null); - } - - @Override - public void contextChanged(ActionContext context) { - updateNavigatable(context); - } - - TaskListener createTaskListener() { - return new SearchOnceTaskListener(); - } - -//================================================================================================== -// Inner Classes -//================================================================================================== - - private class TableLoadingListener implements ThreadedTableModelListener { - - private ThreadedTableModel model; - - TableLoadingListener(ThreadedTableModel model) { - this.model = model; - } - - @Override - public void loadingFinished(boolean wasCancelled) { - if (isDisposed()) { - return; - } - - ComponentProvider provider = currentResultsTableProvider; - waitingForSearchAll = false; - searchFinished(); - if (wasCancelled) { - searchDialog.setStatusText("Search Cancelled"); - return; - } - - int matchCount = model.getRowCount(); - if (matchCount == 0) { - searchDialog.setStatusText("No matches found."); - return; - } - - if (matchCount >= searchLimit) { - // use this when showing the dialog below so that the provider does not get - // hidden behind the tool - JComponent resultsTable = provider.getComponent(); - Msg.showInfo(getClass(), resultsTable, "Search Limit Exceeded!", - "Stopped search after finding " + matchCount + " matches.\n" + - "The search limit can be changed at Edit->Tool Options, under Search."); - } - - // suggestion to not close search dialog. TODO remove next line in future versions. - // searchDialog.close(); - searchDialog.setStatusText("Done"); - } - - @Override - public void loadingStarted() { - // don't care - } - - @Override - public void loadPending() { - // don't care - } - } - - private abstract class SearchResultsHighlighter - implements ListingHighlightProvider, ComponentProviderActivationListener { - - private TableComponentProvider provider; - private Program highlightProgram; - private final Navigatable highlightNavigatable; - - SearchResultsHighlighter(Navigatable navigatable, - TableComponentProvider provider, Program program) { - highlightNavigatable = navigatable; - this.provider = provider; - this.highlightProgram = program; - - if (provider != null) { - provider.addActivationListener(this); - } - highlightNavigatable.setHighlightProvider(this, program); - } - - abstract List getMatches(); - - abstract void cleanup(); - - private List getAddressesFoundInRange(Address start, Address end) { - List data = getMatches(); - int startIndex = findFirstIndex(data, start, end); - if (startIndex < 0) { - return Collections.emptyList(); - } - - int endIndex = findIndexAtOrGreater(data, end); - if (endIndex < data.size() && ((data.get(endIndex)).addressEquals(end))) { - endIndex++; // exact match on end, so include it - } - - List resultList = data.subList(startIndex, endIndex); - return resultList; - } - - private int findFirstIndex(List list, Address start, Address end) { - - List data = getMatches(); - - int startIndex = findIndexAtOrGreater(data, start); - if (startIndex > 0) { // see if address before extends into this range. - MemSearchResult resultBefore = data.get(startIndex - 1); - Address beforeAddr = resultBefore.getAddress(); - int length = resultBefore.getLength(); - if (start.hasSameAddressSpace(beforeAddr) && start.subtract(beforeAddr) < length) { - return startIndex - 1; - } - } - - if (startIndex == data.size()) { - return -1; - } - - MemSearchResult result = data.get(startIndex); - Address addr = result.getAddress(); - if (end.compareTo(addr) >= 0) { - return startIndex; - } - return -1; - } - - private int findIndexAtOrGreater(List list, Address address) { - - MemSearchResult key = new MemSearchResult(address, 1); - int index = Collections.binarySearch(list, key); - if (index < 0) { - index = -index - 1; - } - return index; - } - - @Override - public Highlight[] createHighlights(String text, ListingField field, int cursorTextOffset) { - - Program program = navigatable != null ? navigatable.getProgram() : null; - Class fieldFactoryClass = field.getFieldFactory().getClass(); - if (fieldFactoryClass != BytesFieldFactory.class) { - return NO_HIGHLIGHTS; - } - if (checkRemoveHighlights()) { - return NO_HIGHLIGHTS; - } - - ProxyObj proxy = field.getProxy(); - Object obj = proxy.getObject(); - if (!(obj instanceof CodeUnit cu)) { - return NO_HIGHLIGHTS; - } - if (!doHighlight) { - return NO_HIGHLIGHTS; - } - - if (highlightProgram != program) { - return NO_HIGHLIGHTS; - } - - Address minAddr = cu.getMinAddress(); - Address maxAddr = cu.getMaxAddress(); - List results = getAddressesFoundInRange(minAddr, maxAddr); - - Highlight[] highlights = new Highlight[results.size()]; - for (int i = 0; i < highlights.length; i++) { - MemSearchResult result = results.get(i); - int highlightLength = result.getLength(); - Address addr = result.getAddress(); - Color highlightColor = getHighlightColor(addr, highlightLength); - int startByteOffset = (int) addr.subtract(minAddr); - int endByteOffset = startByteOffset + highlightLength - 1; - startByteOffset = Math.max(startByteOffset, 0); - highlights[i] = getHighlight(text, startByteOffset, endByteOffset, highlightColor); - } - - return highlights; - } - - private Highlight getHighlight(String text, int start, int end, Color color) { - int charStart = getCharPosition(text, start); - int charEnd = getCharPosition(text, end) + 1; - return new Highlight(charStart, charEnd, color); - - } - - private int getCharPosition(String text, int byteOffset) { - int groupSize = byteGroupSize * 2 + byteDelimiter.length(); - int groupIndex = byteOffset / byteGroupSize; - int groupOffset = byteOffset % byteGroupSize; - - int pos = groupIndex * groupSize + 2 * groupOffset; - return Math.min(text.length() - 1, pos); - } - - private Color getHighlightColor(Address highlightStart, int highlightLength) { - ProgramLocation location = navigatable != null ? navigatable.getLocation() : null; - if (!(location instanceof BytesFieldLocation)) { - return SearchConstants.SEARCH_HIGHLIGHT_COLOR; - } - - BytesFieldLocation byteLoc = (BytesFieldLocation) location; - Address byteAddress = byteLoc.getAddressForByte(); - if (highlightStart.hasSameAddressSpace(byteAddress)) { - long diff = byteAddress.subtract(highlightStart); - if (diff >= 0 && diff < highlightLength) { - // the current location is in the highlight - return SearchConstants.SEARCH_HIGHLIGHT_CURRENT_ADDR_COLOR; - } - } - - return SearchConstants.SEARCH_HIGHLIGHT_COLOR; - } - - private boolean checkRemoveHighlights() { - if (provider != null) { // search all - remove highlights when - if (!tool.isVisible(provider)) { // results are no longer showing - highlightNavigatable.removeHighlightProvider(this, highlightProgram); - cleanup(); - return true; - } - } - else if (!searchDialog.isVisible()) { - // single search - remove highlights when search dialog no longer showing - highlightNavigatable.removeHighlightProvider(this, highlightProgram); - cleanup(); - return true; - } - return false; - } - - @Override - public void componentProviderActivated(ComponentProvider componentProvider) { - // enable highlighting - highlightNavigatable.setHighlightProvider(this, highlightProgram); - } - - @Override - public void componentProviderDeactivated(ComponentProvider componentProvider) { - // only handle highlighting during activation - } - } - - private class SearchTableHighlightHandler extends SearchResultsHighlighter { - private final MemSearchTableModel model; - private List sortedResults; - - SearchTableHighlightHandler(Navigatable navigatable, MemSearchTableModel model, - TableComponentProvider provider, Program program) { - super(navigatable, provider, program); - this.model = model; - - model.addThreadedTableModelListener(new ThreadedTableModelListener() { - - @Override - public void loadingStarted() { - clearCache(); - } - - @Override - public void loadingFinished(boolean wasCancelled) { - // stub - } - - @Override - public void loadPending() { - clearCache(); - } - }); - } - - @Override - List getMatches() { - - if (sortedResults != null) { - return sortedResults; - } - - if (model.isBusy()) { - return Collections.emptyList(); - } - - List modelData = model.getModelData(); - if (model.isSortedOnAddress()) { - return modelData; - } - - sortedResults = new ArrayList<>(modelData); - Collections.sort(sortedResults); - - return sortedResults; - } - - @Override - void cleanup() { - clearCache(); - } - - private void clearCache() { - if (sortedResults != null) { - sortedResults.clear(); - sortedResults = null; - } - } - } - - private class TaskHighlightHandler extends SearchResultsHighlighter { - private final MemSearcherTask searchTask; - - TaskHighlightHandler(Navigatable navigatable, MemSearcherTask searcher, - TableComponentProvider provider, Program program) { - super(navigatable, provider, program); - this.searchTask = searcher; - } - - @Override - List getMatches() { - return searchTask.getMatchingAddresses(); - } - - @Override - void cleanup() { - // nothing to do - } - } - - private class SearchOnceTaskListener implements TaskListener { - @Override - public void taskCompleted(Task task) { - if (isDisposed()) { - return; - } - - MemSearcherTask searcher = (MemSearcherTask) task; - List results = searcher.getMatchingAddresses(); - if (results.isEmpty()) { - searchDialog.setStatusText("Not Found"); - return; - } - - searchDialog.setStatusText("Found"); - MemSearchResult result = results.get(0); - Address addr = result.getAddress(); - goToService.goTo(navigatable, getBytesFieldLocation(addr), navigatable.getProgram()); - lastMatchingAddress = addr; - installHighlightProvider(searcher, null); - } - - @Override - public void taskCancelled(Task task) { - // do nothing - } - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToAddressTableRowMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToAddressTableRowMapper.java deleted file mode 100644 index c6bc95e436..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToAddressTableRowMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import ghidra.framework.plugintool.ServiceProvider; -import ghidra.program.model.address.Address; -import ghidra.program.model.listing.Program; -import ghidra.util.search.memory.MemSearchResult; -import ghidra.util.table.ProgramLocationTableRowMapper; - -public class MemSearchResultToAddressTableRowMapper - extends ProgramLocationTableRowMapper { - - @Override - public Address map(MemSearchResult rowObject, Program data, ServiceProvider serviceProvider) { - return rowObject.getAddress(); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToFunctionTableRowMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToFunctionTableRowMapper.java deleted file mode 100644 index e4d4481562..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToFunctionTableRowMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import ghidra.framework.plugintool.ServiceProvider; -import ghidra.program.model.listing.*; -import ghidra.util.search.memory.MemSearchResult; -import ghidra.util.table.ProgramLocationTableRowMapper; - -public class MemSearchResultToFunctionTableRowMapper - extends ProgramLocationTableRowMapper { - - @Override - public Function map(MemSearchResult rowObject, Program program, - ServiceProvider serviceProvider) { - FunctionManager functionManager = program.getFunctionManager(); - return functionManager.getFunctionContaining(rowObject.getAddress()); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToProgramLocationTableRowMapper.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToProgramLocationTableRowMapper.java deleted file mode 100644 index 8b3daa82ba..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchResultToProgramLocationTableRowMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import ghidra.framework.plugintool.ServiceProvider; -import ghidra.program.model.listing.Program; -import ghidra.program.util.ProgramLocation; -import ghidra.util.search.memory.MemSearchResult; -import ghidra.util.table.ProgramLocationTableRowMapper; - -public class MemSearchResultToProgramLocationTableRowMapper - extends ProgramLocationTableRowMapper { - - @Override - public ProgramLocation map(MemSearchResult rowObject, Program program, - ServiceProvider serviceProvider) { - return new ProgramLocation(program, rowObject.getAddress()); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchTableModel.java deleted file mode 100644 index c900e65753..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/MemSearchTableModel.java +++ /dev/null @@ -1,106 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import docking.widgets.table.*; -import ghidra.framework.plugintool.ServiceProvider; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.Program; -import ghidra.program.util.*; -import ghidra.util.datastruct.Accumulator; -import ghidra.util.exception.CancelledException; -import ghidra.util.search.memory.*; -import ghidra.util.table.AddressBasedTableModel; -import ghidra.util.table.field.AddressTableColumn; -import ghidra.util.task.TaskMonitor; - -public class MemSearchTableModel extends AddressBasedTableModel { - - private SearchInfo searchInfo; - private ProgramSelection selection; - private Address startAddress; - private MemorySearchAlgorithm algorithm; - - MemSearchTableModel(ServiceProvider serviceProvider, Program program, SearchInfo searchInfo, - Address searchStartAddress, ProgramSelection programSelection) { - super("Memory Search", serviceProvider, program, null, true); - this.searchInfo = searchInfo; - this.startAddress = searchStartAddress; - this.selection = programSelection; - } - - public boolean isSortedOnAddress() { - TableSortState sortState = getTableSortState(); - if (sortState.isUnsorted()) { - return false; - } - - ColumnSortState primaryState = sortState.getAllSortStates().get(0); - DynamicTableColumn column = - getColumn(primaryState.getColumnModelIndex()); - String name = column.getColumnName(); - if (AddressTableColumn.NAME.equals(name)) { - return true; - } - return false; - } - - @Override - protected void doLoad(Accumulator accumulator, TaskMonitor monitor) - throws CancelledException { - algorithm = searchInfo.createSearchAlgorithm(getProgram(), startAddress, selection); - algorithm.search(accumulator, monitor); - } - - @Override - public ProgramLocation getProgramLocation(int row, int column) { - Program p = getProgram(); - if (p == null) { - return null; // we've been disposed - } - - ProgramLocation loc = super.getProgramLocation(row, column); - if (loc != null && p.getMemory().contains(loc.getByteAddress())) { - return new BytesFieldLocation(p, loc.getByteAddress()); - } - return null; - } - - @Override - public Address getAddress(int row) { - MemSearchResult result = getRowObject(row); - return result.getAddress(); - } - - @Override - public ProgramSelection getProgramSelection(int[] rows) { - AddressSet addressSet = new AddressSet(); - for (int row : rows) { - MemSearchResult result = getRowObject(row); - int addOn = result.getLength() - 1; - Address minAddr = getAddress(row); - Address maxAddr = minAddr; - try { - maxAddr = minAddr.addNoWrap(addOn); - addressSet.addRange(minAddr, maxAddr); - } - catch (AddressOverflowException e) { - // I guess we don't care--not sure why this is undocumented :( - } - } - return new ProgramSelection(addressSet); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/RegExSearchData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/RegExSearchData.java deleted file mode 100644 index 60c152ab01..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/RegExSearchData.java +++ /dev/null @@ -1,50 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -public class RegExSearchData extends SearchData { - private Pattern pattern; - - public static RegExSearchData createRegExSearchData( String inputString ) { - RegExSearchData regExSearchData = new RegExSearchData( inputString ); - if ( regExSearchData.errorMessage != null ) { - throw new IllegalArgumentException( "Problem creating search data: " + - regExSearchData.errorMessage ); - } - return regExSearchData; - } - - public RegExSearchData(String inputString) { - super(inputString, new byte[0], null); - try { - pattern = Pattern.compile(inputString, Pattern.DOTALL); - } catch (PatternSyntaxException pse) { - errorMessage = pse.getMessage(); - } - } - - @Override - public boolean isValidSearchData() { - return pattern != null; - } - public Pattern getRegExPattern() { - return pattern; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/RegExSearchFormat.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/RegExSearchFormat.java deleted file mode 100644 index 34dc49fb45..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/RegExSearchFormat.java +++ /dev/null @@ -1,44 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import javax.swing.event.ChangeListener; - -public class RegExSearchFormat extends SearchFormat { - - public RegExSearchFormat(ChangeListener listener) { - super("Regular Expression", listener); - } - - @Override - public String getToolTip() { - return "Interpret value as a regular expression."; - } - - @Override - public boolean usesEndieness() { - return false; - } - @Override - public boolean supportsBackwardsSearch() { - return false; - } - @Override - public SearchData getSearchData( String input ) { - return new RegExSearchData(input); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchAllSearchInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchAllSearchInfo.java deleted file mode 100644 index 099ac67a6e..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchAllSearchInfo.java +++ /dev/null @@ -1,55 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.Memory; -import ghidra.program.util.ProgramSelection; -import ghidra.util.search.memory.CodeUnitSearchInfo; -import ghidra.util.search.memory.SearchInfo; - -class SearchAllSearchInfo extends SearchInfo { - - public SearchAllSearchInfo(SearchData searchData, int matchLimit, boolean searchSelection, - boolean forwardSearch, int alignment, boolean includeNonLoadedBlocks, - CodeUnitSearchInfo codeUnitSearchInfo) { - super(searchData, matchLimit, searchSelection, forwardSearch, alignment, - includeNonLoadedBlocks, codeUnitSearchInfo, null /* search all uses a different listener mechanism */); - } - - @Override - protected AddressSetView getSearchableAddressSet(Program program, Address address, - ProgramSelection selection) { - - // in the search all case, we don't care about the starting address. - - Memory memory = program.getMemory(); - AddressSetView set = - this.includeNonLoadedBlocks ? memory.getAllInitializedAddressSet() - : memory.getLoadedAndInitializedAddressSet(); - if (searchSelection && selection != null && !selection.isEmpty()) { - set = set.intersect(selection); - } - return set; - } - - @Override - public boolean isSearchAll() { - return true; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchData.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchData.java deleted file mode 100644 index 4bb94166c4..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchData.java +++ /dev/null @@ -1,92 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -public class SearchData { - - private String inputString; - protected String errorMessage; - private byte[] bytes; - private byte[] mask; - private boolean isValidInputData; - private boolean isValidSearchData; - - // valid input and search data with mask - protected SearchData(String inputString, byte[] searchBytes, byte[] mask) { - this.isValidInputData = true; - this.isValidSearchData = true; - this.inputString = inputString; - this.bytes = searchBytes == null ? new byte[0] : searchBytes; - this.mask = mask; - } - - // valid input, bad search data - private SearchData(String errorMessage, boolean isValidInputData) { - this.isValidInputData = isValidInputData; - this.isValidSearchData = false; - bytes = new byte[0]; - this.errorMessage = errorMessage; - } - - public static SearchData createSearchData(String inputString, byte[] searchBytes, byte[] mask) { - return new SearchData(inputString, searchBytes, mask); - } - - public static SearchData createIncompleteSearchData(String errorMessage) { - return new SearchData(errorMessage, true); - } - - public static SearchData createInvalidInputSearchData(String errorMessage) { - return new SearchData(errorMessage, false); - } - - public byte[] getBytes() { - return bytes; - } - - public byte[] getMask() { - return mask; - } - - public boolean isValidInputData() { - return isValidInputData; - } - - public boolean isValidSearchData() { - return isValidSearchData; - } - - public String getInputString() { - return inputString; - } - - public String getStatusMessage() { - return errorMessage; - } - - public String getHexString() { - StringBuilder buf = new StringBuilder(); - for (byte element : bytes) { - String hexString = Integer.toHexString(element & 0xff); - if (hexString.length() == 1) { - buf.append("0"); - } - buf.append(hexString); - buf.append(" "); - } - return buf.toString(); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchFormat.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchFormat.java deleted file mode 100644 index 24b198a8ae..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/searchmem/SearchFormat.java +++ /dev/null @@ -1,57 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.app.plugin.core.searchmem; - -import javax.swing.BorderFactory; -import javax.swing.JPanel; -import javax.swing.event.ChangeListener; - -public abstract class SearchFormat { - private String name; - protected boolean isBigEndian; - protected ChangeListener changeListener; - - protected SearchFormat(String name, ChangeListener listener) { - this.name = name; - this.changeListener = listener; - } - public String getName() { - return name; - } - - public JPanel getOptionsPanel() { - JPanel noOptionsPanel = new JPanel(); - noOptionsPanel.setBorder(BorderFactory.createTitledBorder("Format Options")); - return noOptionsPanel; - } - - public void setEndieness(boolean isBigEndian) { - this.isBigEndian = isBigEndian; - } - public boolean usesEndieness() { - return true; - } - public boolean supportsBackwardsSearch() { - return true; - } - - public abstract String getToolTip(); - - public abstract SearchData getSearchData( String input ); - - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/searcher/MemorySearcher.java b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/searcher/MemorySearcher.java index 3985d83687..fc3a64e8b2 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/searcher/MemorySearcher.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/features/base/memsearch/searcher/MemorySearcher.java @@ -56,7 +56,7 @@ public class MemorySearcher { * @param searchLimit the max number of hits before stopping */ public MemorySearcher(AddressableByteSource byteSource, ByteMatcher matcher, - AddressSet addresses, int searchLimit) { + AddressSetView addresses, int searchLimit) { this(byteSource, matcher, addresses, searchLimit, DEFAULT_CHUNK_SIZE); } @@ -69,7 +69,7 @@ public class MemorySearcher { * @param chunkSize the maximum number of bytes to feed to the matcher at any one time. */ public MemorySearcher(AddressableByteSource byteSource, ByteMatcher matcher, - AddressSet addresses, int searchLimit, int chunkSize) { + AddressSetView addresses, int searchLimit, int chunkSize) { this.matcher = matcher; this.searchSet = addresses; this.searchLimit = searchLimit; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/flatapi/FlatProgramAPI.java b/Ghidra/Features/Base/src/main/java/ghidra/program/flatapi/FlatProgramAPI.java index 98bb4ec573..6061b49f31 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/flatapi/FlatProgramAPI.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/flatapi/FlatProgramAPI.java @@ -29,8 +29,14 @@ import ghidra.app.cmd.label.SetLabelPrimaryCmd; import ghidra.app.plugin.core.analysis.AutoAnalysisManager; import ghidra.app.plugin.core.clear.ClearCmd; import ghidra.app.plugin.core.clear.ClearOptions; -import ghidra.app.plugin.core.searchmem.RegExSearchData; import ghidra.app.script.GhidraScript; +import ghidra.features.base.memsearch.bytesource.AddressableByteSource; +import ghidra.features.base.memsearch.bytesource.ProgramByteSource; +import ghidra.features.base.memsearch.gui.SearchSettings; +import ghidra.features.base.memsearch.matcher.ByteMatcher; +import ghidra.features.base.memsearch.matcher.RegExByteMatcher; +import ghidra.features.base.memsearch.searcher.MemoryMatch; +import ghidra.features.base.memsearch.searcher.MemorySearcher; import ghidra.framework.main.AppInfo; import ghidra.framework.model.*; import ghidra.framework.plugintool.PluginTool; @@ -49,7 +55,6 @@ import ghidra.util.ascii.AsciiCharSetRecognizer; import ghidra.util.datastruct.Accumulator; import ghidra.util.datastruct.ListAccumulator; import ghidra.util.exception.*; -import ghidra.util.search.memory.*; import ghidra.util.task.TaskMonitor; /** @@ -771,15 +776,38 @@ public class FlatProgramAPI { public final Address[] findBytes(AddressSetView set, String byteString, int matchLimit, int alignment) { - return findBytes(set, byteString, matchLimit, alignment, false); + if (matchLimit <= 0) { + matchLimit = 500; + } + + SearchSettings settings = new SearchSettings().withAlignment(alignment); + ByteMatcher matcher = new RegExByteMatcher(byteString, settings); + AddressableByteSource byteSource = new ProgramByteSource(currentProgram); + Memory memory = currentProgram.getMemory(); + AddressSet intersection = memory.getLoadedAndInitializedAddressSet().intersect(set); + + MemorySearcher searcher = new MemorySearcher(byteSource, matcher, intersection, matchLimit); + Accumulator accumulator = new ListAccumulator<>(); + searcher.findAll(accumulator, monitor); + + //@formatter:off + List
addresses = + accumulator.stream() + .map(r -> r.getAddress()) + .collect(Collectors.toList()); + //@formatter:on + return addresses.toArray(new Address[addresses.size()]); } /** + * This method has been deprecated, use {@link #findBytes(Address, String, int, int)} instead. + * The concept of searching and finding matches that span gaps (address ranges where no memory + * blocks have been defined), is no longer supported. If this capability has value to anyone, + * please contact the Ghidra team and let us know. + *

* Finds a byte pattern within an addressSet. * - * Note: When searchAcrossAddressGaps is set to true, the ranges within the addressSet are - * treated as a contiguous set when searching. - * + * Note: The ranges within the addressSet are NOT treated as a contiguous set when searching *

* The byteString may contain regular expressions. The following * highlights some example search strings (note the use of double backslashes ("\\")): @@ -794,49 +822,21 @@ public class FlatProgramAPI { * @param byteString the byte pattern for which to search * @param matchLimit The number of matches to which the search should be restricted * @param alignment byte alignment to use for search starts. For example, a value of - * 1 searches from every byte. A value of 2 only matches runs that begin on a even - * address boundary. - * @param searchAcrossAddressGaps when set to 'true' searches for matches across the gaps - * of each addressRange contained in the addresSet. + * 1 searches from every byte. A value of 2 only matches runs that begin on a even + * address boundary. + * @param searchAcrossAddressGaps This parameter is no longer supported and its value is + * ignored. Previously, if true, match results were allowed to span non-continguous memory + * ranges. * @return the start addresses that contain byte patterns that match the given byteString * @throws IllegalArgumentException if the byteString is not a valid regular expression * @see #findBytes(Address, String) + * + * @deprecated see description for details. */ + @Deprecated(since = "11.3", forRemoval = true) public final Address[] findBytes(AddressSetView set, String byteString, int matchLimit, int alignment, boolean searchAcrossAddressGaps) { - - if (matchLimit <= 0) { - matchLimit = 500; - } - - RegExSearchData searchData = RegExSearchData.createRegExSearchData(byteString); - - //@formatter:off - SearchInfo searchInfo = new SearchInfo(searchData, - matchLimit, - false, // search selection - true, // search forward - alignment, - true, // include non-loaded blocks - null); - //@formatter:on - - Memory memory = currentProgram.getMemory(); - AddressSet intersection = memory.getLoadedAndInitializedAddressSet().intersect(set); - - RegExMemSearcherAlgorithm searcher = new RegExMemSearcherAlgorithm(searchInfo, intersection, - currentProgram, searchAcrossAddressGaps); - - Accumulator accumulator = new ListAccumulator<>(); - searcher.search(accumulator, monitor); - - //@formatter:off - List

addresses = - accumulator.stream() - .map(r -> r.getAddress()) - .collect(Collectors.toList()); - //@formatter:on - return addresses.toArray(new Address[addresses.size()]); + return findBytes(set, byteString, matchLimit, alignment); } /** @@ -1512,21 +1512,21 @@ public class FlatProgramAPI { public final Namespace getNamespace(Namespace parent, String namespaceName) { return currentProgram.getSymbolTable().getNamespace(namespaceName, parent); } - -/** - * Creates a new {@link Namespace} with the given name contained inside the - * specified parent namespace. - * Pass null for parent to indicate the global namespace. - * If a {@link Namespace} or {@link GhidraClass} with the given name already exists, the - * existing one will be returned. - * @param parent the parent namespace, or null for global namespace - * @param namespaceName the requested namespace's name - * @return the namespace with the given name - * @throws DuplicateNameException if a {@link Library} symbol exists with the given name - * @throws InvalidInputException if the name is invalid - * @throws IllegalArgumentException if parent Namespace does not correspond to - * currerntProgram - */ + + /** + * Creates a new {@link Namespace} with the given name contained inside the + * specified parent namespace. + * Pass null for parent to indicate the global namespace. + * If a {@link Namespace} or {@link GhidraClass} with the given name already exists, the + * existing one will be returned. + * @param parent the parent namespace, or null for global namespace + * @param namespaceName the requested namespace's name + * @return the namespace with the given name + * @throws DuplicateNameException if a {@link Library} symbol exists with the given name + * @throws InvalidInputException if the name is invalid + * @throws IllegalArgumentException if parent Namespace does not correspond to + * currerntProgram + */ public final Namespace createNamespace(Namespace parent, String namespaceName) throws DuplicateNameException, InvalidInputException { SymbolTable symbolTable = currentProgram.getSymbolTable(); @@ -1539,7 +1539,7 @@ public class FlatProgramAPI { } return symbolTable.createNameSpace(parent, namespaceName, SourceType.USER_DEFINED); } - + /** * Creates a new {@link GhidraClass} with the given name contained inside the * specified parent namespace. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/CodeUnitSearchInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/CodeUnitSearchInfo.java deleted file mode 100644 index 404b7367c2..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/CodeUnitSearchInfo.java +++ /dev/null @@ -1,46 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search.memory; - -public class CodeUnitSearchInfo { - - private final boolean searchInstructions; - private final boolean searchDefinedData; - private final boolean searchUndefinedData; - - public CodeUnitSearchInfo( boolean searchInstructions, boolean searchDefinedData, - boolean searchUndefinedData ) { - this.searchInstructions = searchInstructions; - this.searchDefinedData = searchDefinedData; - this.searchUndefinedData = searchUndefinedData; - } - - public boolean isSearchInstructions() { - return searchInstructions; - } - - public boolean isSearchDefinedData() { - return searchDefinedData; - } - - public boolean isSearchUndefinedData() { - return searchUndefinedData; - } - - public boolean searchAll() { - return searchInstructions && searchDefinedData && searchUndefinedData; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearchResult.java b/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearchResult.java deleted file mode 100644 index 2294301292..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearchResult.java +++ /dev/null @@ -1,104 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search.memory; - -import java.util.Objects; - -import ghidra.program.model.address.Address; -import ghidra.util.SystemUtilities; - -/** - * A class that represents a memory search hit at an address. - */ -public class MemSearchResult implements Comparable { - - private Address address; - private int length; - private byte[] bytes; - - public MemSearchResult(Address address, int length) { - this.address = Objects.requireNonNull(address); - - if (length <= 0) { - throw new IllegalArgumentException("Length must be greater than 0"); - } - this.length = length; - } - - public MemSearchResult(Address address, byte[] bytes) { - if (bytes == null || bytes.length < 1) { - throw new IllegalArgumentException("Must provide at least 1 byte"); - } - this.address = Objects.requireNonNull(address); - this.bytes = bytes; - this.length = bytes.length; - } - - public Address getAddress() { - return address; - } - - public int getLength() { - return length; - } - - public byte[] getBytes() { - return bytes; - } - - @Override - public int compareTo(MemSearchResult o) { - return address.compareTo(o.address); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((address == null) ? 0 : address.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - - MemSearchResult other = (MemSearchResult) obj; - return SystemUtilities.isEqual(address, other.address); - } - - @Override - public String toString() { - return address.toString(); - } - - /** - * Returns true if the given address equals the address of this search result - * @param a the other address - * @return true if the given address equals the address of this search result - */ - public boolean addressEquals(Address a) { - return address.equals(a); - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearcherAlgorithm.java b/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearcherAlgorithm.java deleted file mode 100644 index 258fa2dc10..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearcherAlgorithm.java +++ /dev/null @@ -1,138 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search.memory; - -import ghidra.app.plugin.core.searchmem.SearchData; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.*; -import ghidra.program.model.mem.Memory; -import ghidra.util.datastruct.Accumulator; -import ghidra.util.task.TaskMonitor; - -/** - * Search memory using the provided search text. - */ -public class MemSearcherAlgorithm implements MemorySearchAlgorithm { - - private boolean forwardSearch; - private SearchData searchData; - private AddressSetView searchSet; - protected int searchLimit; - private Program program; - private int alignment; - private CodeUnitSearchInfo codeUnitSearchInfo; - - MemSearcherAlgorithm(SearchInfo searchInfo, AddressSetView searchSet, Program program) { - - this.searchData = searchInfo.getSearchData(); - this.forwardSearch = searchInfo.isSearchForward(); - this.alignment = searchInfo.getAlignment(); - this.searchSet = searchSet; - this.searchLimit = searchInfo.getSearchLimit(); - this.program = program; - this.codeUnitSearchInfo = searchInfo.getCodeUnitSearchInfo(); - } - - @Override - public void search(Accumulator accumulator, TaskMonitor monitor) { - AddressRangeIterator addressRanges = searchSet.getAddressRanges(forwardSearch); - monitor.initialize(searchSet.getNumAddresses()); - int progressCount = 0; - - while (addressRanges.hasNext() && !monitor.isCancelled()) { - - AddressRange range = addressRanges.next(); - searchRange(accumulator, range, monitor, progressCount); - progressCount += range.getLength(); - monitor.setProgress(progressCount); - if (accumulator.size() >= searchLimit) { - return; - } - } - } - - private void searchRange(Accumulator accumulator, AddressRange range, - TaskMonitor monitor, int progressCount) { - - Memory mem = program.getMemory(); - Address startAddress = forwardSearch ? range.getMinAddress() : range.getMaxAddress(); - Address endAddress = forwardSearch ? range.getMaxAddress() : range.getMinAddress(); - int length = searchData.getBytes().length; - while (startAddress != null && !monitor.isCancelled()) { - Address matchAddress = mem.findBytes(startAddress, endAddress, searchData.getBytes(), - searchData.getMask(), forwardSearch, monitor); - - if (isMatchingAddress(matchAddress)) { - MemSearchResult result = new MemSearchResult(matchAddress, length); - accumulator.add(result); - if (accumulator.size() >= searchLimit) { - return; - } - monitor.setProgress(progressCount + getRangeDifference(range, matchAddress)); - } - startAddress = getNextAddress(matchAddress, range); - } - } - - private boolean isMatchingAddress(Address address) { - if (address == null) { - return false; - } - - if ((address.getOffset() % alignment) != 0) { - return false; // wrong alignment - } - - if (codeUnitSearchInfo.searchAll()) { - return true; - } - - Listing listing = program.getListing(); - CodeUnit codeUnit = listing.getCodeUnitContaining(address); - if (codeUnit instanceof Instruction) { - return codeUnitSearchInfo.isSearchInstructions(); - } - else if (codeUnit instanceof Data) { - Data data = (Data) codeUnit; - if (data.isDefined()) { - return codeUnitSearchInfo.isSearchDefinedData(); - } - return codeUnitSearchInfo.isSearchUndefinedData(); - } - - return true; - } - - private int getRangeDifference(AddressRange range, Address address) { - return (int) (forwardSearch ? address.subtract(range.getMinAddress()) - : range.getMaxAddress().subtract(address)); - } - - private Address getNextAddress(Address currentAddress, AddressRange range) { - if (currentAddress == null) { - return null; - } - - if (forwardSearch) { - return currentAddress.equals(range.getMaxAddress()) ? null : currentAddress.next(); - } - return currentAddress.equals(range.getMinAddress()) ? null : currentAddress.previous(); - } - - AddressSetView getSearchSet() { - return searchSet; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearcherTask.java b/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearcherTask.java deleted file mode 100644 index 348cf47768..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemSearcherTask.java +++ /dev/null @@ -1,47 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search.memory; - -import java.util.List; - -import ghidra.util.datastruct.ListAccumulator; -import ghidra.util.task.Task; -import ghidra.util.task.TaskMonitor; - -public class MemSearcherTask extends Task { - - private MemorySearchAlgorithm algorithm; - private List results; - - public MemSearcherTask(SearchInfo searchInfo, MemorySearchAlgorithm algorithm) { - super("Search Memory", true, true, false); - - this.algorithm = algorithm; - addTaskListener(searchInfo.getListener()); - } - - @Override - public void run(TaskMonitor monitor) { - - ListAccumulator accumulator = new ListAccumulator<>(); - algorithm.search(accumulator, monitor); - this.results = accumulator.asList(); - } - - public List getMatchingAddresses() { - return results; - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemoryAddressSetCharSequence.java b/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemoryAddressSetCharSequence.java deleted file mode 100644 index 6a33cab630..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemoryAddressSetCharSequence.java +++ /dev/null @@ -1,103 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search.memory; - -import ghidra.program.model.address.*; -import ghidra.program.model.mem.Memory; -import ghidra.program.model.mem.MemoryAccessException; -import ghidra.util.exception.AssertException; - -/** - * This class implements the CharSequence interface using Memory and an AddressSet. The - * idea is that each byte in memory at the addresses specified in the AddressSet will form - * a contiguous sequence of characters. - */ - -public class MemoryAddressSetCharSequence implements CharSequence { - - private final Memory memory; - private final AddressSetView set; - private final AddressSetMapping mapping; - - public MemoryAddressSetCharSequence(Memory memory, AddressSetView addressSet) - throws MemoryAccessException { - this.memory = memory; - this.set = addressSet; - - if (addressSet.getNumAddresses() > Integer.MAX_VALUE) { - throw new AssertException( - "The MemAddressSetCharSequence class only supports address sets of size <= 0x7ffffffff byte addresses."); - } - - if (!memory.getAllInitializedAddressSet().contains(addressSet)) { - throw new MemoryAccessException( - "Not all addresses in given address set are in memory!"); - } - - mapping = new AddressSetMapping(addressSet); - } - - public MemoryAddressSetCharSequence(Memory memory, Address start, Address end) - throws MemoryAccessException { - this(memory, new AddressSet(start, end)); - } - - /** - * Takes an index and returns the matching Address - * @param index index to search on - * @return Address address matched to index - */ - public Address getAddressAtIndex(int index) { - return mapping.getAddress(index); - } - - @Override - public int length() { - return (int) set.getNumAddresses(); //safe cast because we check in constructor - } - - @Override - public char charAt(int index) { - Address address = getAddressAtIndex(index); - - try { - byte b = memory.getByte(address); - return (char) (b & 0xff); - } - catch (MemoryAccessException e) { - throw new AssertException("Can't happen since we already checked in constructor"); - } - } - - @Override - public CharSequence subSequence(int start, int end) { - if (start < 0 || start >= length() || end < 0 || end >= length()) { - throw new IndexOutOfBoundsException("Start and end must be in [0, " + (length() - 1)); - } - - Address startAddress = getAddressAtIndex(start); - Address endAddress = getAddressAtIndex(end); - AddressSet intersectSet = set.intersectRange(startAddress, endAddress); - - try { - return new MemoryAddressSetCharSequence(memory, intersectSet); - } - catch (MemoryAccessException e) { - throw new AssertException("Can't happen since we already checked"); - } - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemorySearchAlgorithm.java b/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemorySearchAlgorithm.java deleted file mode 100644 index 77c3575df7..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/MemorySearchAlgorithm.java +++ /dev/null @@ -1,33 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search.memory; - -import ghidra.util.datastruct.Accumulator; -import ghidra.util.task.TaskMonitor; - -/** - * An interface to unify the different methods for searching memory. - */ -public interface MemorySearchAlgorithm { - - /** - * Perform the search - * - * @param accumulator the results accumulator - * @param monitor the monitor - */ - public void search(Accumulator accumulator, TaskMonitor monitor); -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/RegExMemSearcherAlgorithm.java b/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/RegExMemSearcherAlgorithm.java deleted file mode 100644 index 217557b975..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/RegExMemSearcherAlgorithm.java +++ /dev/null @@ -1,186 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search.memory; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import ghidra.app.plugin.core.searchmem.RegExSearchData; -import ghidra.app.plugin.core.searchmem.SearchData; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.*; -import ghidra.program.model.mem.*; -import ghidra.util.Msg; -import ghidra.util.datastruct.Accumulator; -import ghidra.util.task.TaskMonitor; - -/** - * Search memory using the provided regular expression. - */ -public class RegExMemSearcherAlgorithm implements MemorySearchAlgorithm { - - private SearchInfo searchInfo; - private AddressSetView searchSet; - private Program program; - private boolean spanAddressGaps; - private int alignment; - private CodeUnitSearchInfo codeUnitSearchInfo; - - public RegExMemSearcherAlgorithm(SearchInfo searchInfo, AddressSetView searchSet, - Program program, boolean searchAcrossAddressGaps) { - - SearchData data = searchInfo.getSearchData(); - if (!(data instanceof RegExSearchData)) { - throw new IllegalArgumentException( - "The given SearchInfo does not contain a RegExSearchData"); - } - - this.searchInfo = searchInfo; - this.searchSet = searchSet; - this.program = program; - this.spanAddressGaps = searchAcrossAddressGaps; - this.alignment = searchInfo.getAlignment(); - this.codeUnitSearchInfo = searchInfo.getCodeUnitSearchInfo(); - } - - @Override - public void search(Accumulator accumulator, TaskMonitor monitor) { - monitor.initialize(searchSet.getNumAddresses()); - - if (spanAddressGaps) { - searchAddressSet(searchSet, accumulator, monitor, 0); - } - else { - AddressRangeIterator rangeIterator = searchSet.getAddressRanges(); - int progress = 0; - int searchLimit = searchInfo.getSearchLimit(); - while (rangeIterator.hasNext()) { - AddressRange range = rangeIterator.next(); - searchAddressSet(new AddressSet(range), accumulator, monitor, progress); - progress += (int) range.getLength(); - monitor.setProgress(progress); - - if (accumulator.size() >= searchLimit) { - return; - } - } - } - } - - private void searchAddressSet(AddressSetView addressSet, - Accumulator accumulator, TaskMonitor monitor, int progressCount) { - - if (addressSet.getNumAddresses() <= Integer.MAX_VALUE) { - searchSubAddressSet(addressSet, accumulator, monitor, progressCount); - return; - } - List sets = breakSetsByMemoryBlock(addressSet); - int searchLimit = searchInfo.getSearchLimit(); - for (AddressSet set : sets) { - searchSubAddressSet(set, accumulator, monitor, progressCount); - - if (accumulator.size() >= searchLimit) { - return; - } - } - } - - private List breakSetsByMemoryBlock(AddressSetView addressSet) { - Memory mem = program.getMemory(); - List list = new ArrayList<>(); - MemoryBlock[] blocks = mem.getBlocks(); - for (MemoryBlock memoryBlock : blocks) { - - AddressSet set = - addressSet.intersectRange(memoryBlock.getStart(), memoryBlock.getEnd()); - if (!set.isEmpty()) { - list.add(set); - } - } - return list; - } - - private void searchSubAddressSet(AddressSetView addressSet, - Accumulator accumulator, TaskMonitor monitor, int progressCount) { - - SearchData searchData = searchInfo.getSearchData(); - Pattern pattern = ((RegExSearchData) searchData).getRegExPattern(); - Memory memory = program.getMemory(); - int searchLimit = searchInfo.getSearchLimit(); - - try { - MemoryAddressSetCharSequence charSet = - new MemoryAddressSetCharSequence(memory, addressSet); - Matcher matcher = pattern.matcher(charSet); - - int searchFrom = 0; - while (matcher.find(searchFrom) && !monitor.isCancelled()) { - int startIndex = matcher.start(); - int length = matcher.end() - startIndex; - Address address = charSet.getAddressAtIndex(startIndex); - if (isMatchingAddress(address, length)) { - - MemSearchResult result = new MemSearchResult(address, length); - accumulator.add(result); - monitor.setProgress(progressCount + startIndex); - - if (accumulator.size() >= searchLimit) { - return; - } - } - - // move forward by one byte to check for matches within matches - searchFrom = startIndex + 1; - } - } - catch (MemoryAccessException e) { - Msg.error(this, "Unexpected Exception: " + e.getMessage(), e); - monitor.setMessage("Error: Could not read memory"); - } - } - - protected boolean isMatchingAddress(Address address, long matchSize) { - if (address == null) { - return false; - } - - if ((address.getOffset() % alignment) != 0) { - return false; - } - - if (codeUnitSearchInfo.searchAll()) { - return true; - } - - Listing listing = program.getListing(); - CodeUnit codeUnit = listing.getCodeUnitContaining(address); - if (codeUnit instanceof Instruction) { - return codeUnitSearchInfo.isSearchInstructions(); - } - else if (codeUnit instanceof Data) { - Data data = (Data) codeUnit; - if (data.isDefined()) { - return codeUnitSearchInfo.isSearchDefinedData(); - } - return codeUnitSearchInfo.isSearchUndefinedData(); - } - - return false; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/SearchInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/SearchInfo.java deleted file mode 100644 index 8ea9a8ca4c..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/util/search/memory/SearchInfo.java +++ /dev/null @@ -1,133 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search.memory; - -import ghidra.app.plugin.core.searchmem.RegExSearchData; -import ghidra.app.plugin.core.searchmem.SearchData; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.Program; -import ghidra.program.model.mem.Memory; -import ghidra.program.util.ProgramSelection; -import ghidra.util.task.TaskListener; - -public class SearchInfo { - private final SearchData searchData; - private int searchLimit; - private final boolean forwardSearch; - protected final boolean searchSelection; - private final int alignment; - private final CodeUnitSearchInfo codeUnitSearchInfo; - private final TaskListener listener; - protected final boolean includeNonLoadedBlocks; - - public SearchInfo(SearchData searchData, int matchLimit, boolean searchSelection, - boolean forwardSearch, int alignment, boolean includeNonLoadedBlocks, - TaskListener listener) { - this(searchData, matchLimit, searchSelection, forwardSearch, alignment, - includeNonLoadedBlocks, new CodeUnitSearchInfo(true, true, true), listener); - } - - public SearchInfo(SearchData searchData, int searchLimit, boolean searchSelection, - boolean forwardSearch, int alignment, boolean includeNonLoadedBlocks, - CodeUnitSearchInfo codeUnitSearchInfo, TaskListener listener) { - this.searchData = searchData; - this.searchLimit = searchLimit; - this.searchSelection = searchSelection; - this.forwardSearch = forwardSearch; - this.alignment = alignment; - this.listener = listener; - this.codeUnitSearchInfo = codeUnitSearchInfo; - this.includeNonLoadedBlocks = includeNonLoadedBlocks; - } - - /** - * Generate an address set which only includes initialized memory - * - * @param program the program - * @param startAddress starting point for search or null to start from the top of memory - * @param selection addresses to be searched or null to search all memory - * @return searchable address set - */ - protected AddressSetView getSearchableAddressSet(Program program, Address startAddress, - ProgramSelection selection) { - - if (startAddress == null) { - return new AddressSet(); // special case if we are at the first address going backwards - // or the last address going forwards - } - - Memory memory = program.getMemory(); - AddressSetView set = includeNonLoadedBlocks ? memory.getAllInitializedAddressSet() - : memory.getLoadedAndInitializedAddressSet(); - if (searchSelection && selection != null && !selection.isEmpty()) { - set = set.intersect(selection); - } - Address start = forwardSearch ? startAddress : memory.getMinAddress(); - Address end = forwardSearch ? memory.getMaxAddress() : startAddress; - if (start.compareTo(end) > 0) { - return new AddressSet(); - } - AddressSet addressSet = program.getAddressFactory().getAddressSet(start, end); - return set.intersect(addressSet); - } - - public MemorySearchAlgorithm createSearchAlgorithm(Program p, Address start, - ProgramSelection selection) { - - AddressSetView asView = getSearchableAddressSet(p, start, selection); - - // note: this should probably be true--is there a reason not to do this? - // -also, shouldn't the non-regex searcher cross 'gaps' as well? - boolean searchAcrossGaps = false; - - if (searchData instanceof RegExSearchData) { - return new RegExMemSearcherAlgorithm(this, asView, p, searchAcrossGaps); - } - return new MemSearcherAlgorithm(this, asView, p); - } - - public boolean isSearchForward() { - return forwardSearch; - } - - public boolean isSearchAll() { - return false; - } - - public int getAlignment() { - return alignment; - } - - public TaskListener getListener() { - return listener; - } - - public SearchData getSearchData() { - return searchData; - } - - public CodeUnitSearchInfo getCodeUnitSearchInfo() { - return codeUnitSearchInfo; - } - - public int getSearchLimit() { - return searchLimit; - } - - public void setSearchLimit(int searchLimit) { - this.searchLimit = searchLimit; - } -} diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptRealProgramTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptRealProgramTest.java index 37befde70b..2365bbab53 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptRealProgramTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/script/GhidraScriptRealProgramTest.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. @@ -160,25 +160,6 @@ public class GhidraScriptRealProgramTest extends AbstractGhidraHeadedIntegration } - @Test - public void testFindBytesAcrossGap() throws Exception { - GhidraScript script = getScript(); - - AddressSet set = new AddressSet(); - - //Match charAt 0x010064db, 0x010064df - set.addRange(script.toAddr(0x10064d5), script.toAddr(0x010064db)); - set.addRange(script.toAddr(0x010064df), script.toAddr(0x010064e3)); - - String byteString = "\\x51\\x52"; - - Address[] results = script.findBytes(set, byteString, 20, 1, true); - - assertEquals(1, results.length); - assertEquals(script.toAddr(0x010064db), results[0]); - - } - @Test public void testFindText() throws Exception { GhidraScript script = getScript(); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/features/base/memsearch/MnemonicSearchPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/features/base/memsearch/MnemonicSearchPluginTest.java index aa954ad0a3..6b8464ce44 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/features/base/memsearch/MnemonicSearchPluginTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/features/base/memsearch/MnemonicSearchPluginTest.java @@ -26,8 +26,8 @@ import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin; import ghidra.app.plugin.core.marker.MarkerManagerPlugin; import ghidra.app.plugin.core.programtree.ProgramTreePlugin; -import ghidra.app.plugin.core.searchmem.MemSearchPlugin; import ghidra.app.services.ProgramManager; +import ghidra.features.base.memsearch.gui.MemorySearchPlugin; import ghidra.features.base.memsearch.gui.MemorySearchProvider; import ghidra.features.base.memsearch.mnemonic.MnemonicSearchPlugin; import ghidra.framework.plugintool.PluginTool; @@ -58,7 +58,7 @@ public class MnemonicSearchPluginTest extends AbstractGhidraHeadedIntegrationTes tool.addPlugin(ProgramTreePlugin.class.getName()); tool.addPlugin(CodeBrowserPlugin.class.getName()); tool.addPlugin(MarkerManagerPlugin.class.getName()); - tool.addPlugin(MemSearchPlugin.class.getName()); + tool.addPlugin(MemorySearchPlugin.class.getName()); tool.addPlugin(MnemonicSearchPlugin.class.getName()); plugin = env.getPlugin(MnemonicSearchPlugin.class); cb = env.getPlugin(CodeBrowserPlugin.class); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/search/RegExMemSearcherTaskTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/util/search/RegExMemSearcherTaskTest.java deleted file mode 100644 index 8202c9f1b5..0000000000 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/util/search/RegExMemSearcherTaskTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package ghidra.util.search; - -import static org.junit.Assert.assertEquals; - -import java.util.List; - -import org.junit.Test; - -import ghidra.app.plugin.core.searchmem.RegExSearchData; -import ghidra.program.database.ProgramBuilder; -import ghidra.program.model.address.AddressSetView; -import ghidra.program.model.listing.Program; -import ghidra.test.AbstractGhidraHeadlessIntegrationTest; -import ghidra.util.datastruct.ListAccumulator; -import ghidra.util.search.memory.*; -import ghidra.util.task.TaskMonitor; - -public class RegExMemSearcherTaskTest extends AbstractGhidraHeadlessIntegrationTest { - - private Program buildProgram() throws Exception { - ProgramBuilder builder = new ProgramBuilder("TestX86", ProgramBuilder._X86); - builder.createMemory(".text", Long.toHexString(0x1001000), 0x100); - builder.createMemory(".text1", Long.toHexString(0x1002000), 0x100); - return builder.getProgram(); - } - - private Program buildLargeProgram() throws Exception { - ProgramBuilder builder = new ProgramBuilder("TestX86", ProgramBuilder._X86); - builder.createMemory(".text", Long.toHexString(0x1001000), Integer.MAX_VALUE); - builder.createMemory(".text1", Long.toHexString(0x1001000 + Integer.MAX_VALUE), 0x100); - return builder.getProgram(); - } - - @Test - public void testFindMatchesWithinMatches() throws Exception { - - Program p = buildProgram(); - String regex = "\\x00\\x00\\x00\\x00"; - RegExSearchData searchData = new RegExSearchData(regex); - int max = 50; - SearchInfo searchInfo = new SearchInfo(searchData, max, false, true, 1, false, null); - AddressSetView addrs = p.getMemory().getLoadedAndInitializedAddressSet(); - - RegExMemSearcherAlgorithm searcher = - new RegExMemSearcherAlgorithm(searchInfo, addrs, p, false); - - ListAccumulator accumulator = new ListAccumulator<>(); - searcher.search(accumulator, TaskMonitor.DUMMY); - List results = accumulator.asList(); - - assertEquals(max, results.size()); - - assertEquals(0x1001000, results.get(0).getAddress().getOffset()); - assertEquals(0x1001001, results.get(1).getAddress().getOffset()); - assertEquals(0x1001002, results.get(2).getAddress().getOffset()); - } - - @Test - public void testFindMatchesWithinMatchesLargeProgram() throws Exception { - - Program p = buildLargeProgram(); - String regex = "\\x00\\x00\\x00\\x00"; - RegExSearchData searchData = new RegExSearchData(regex); - int max = 50; - SearchInfo searchInfo = new SearchInfo(searchData, max, false, true, 1, false, null); - AddressSetView addrs = p.getMemory().getLoadedAndInitializedAddressSet(); - - RegExMemSearcherAlgorithm searcher = - new RegExMemSearcherAlgorithm(searchInfo, addrs, p, false); - - ListAccumulator accumulator = new ListAccumulator<>(); - searcher.search(accumulator, TaskMonitor.DUMMY); - List results = accumulator.asList(); - - assertEquals(max, results.size()); - - assertEquals(0x1001000, results.get(0).getAddress().getOffset()); - assertEquals(0x1001001, results.get(1).getAddress().getOffset()); - assertEquals(0x1001002, results.get(2).getAddress().getOffset()); - } - -} diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/PEExceptionAnalyzer.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/PEExceptionAnalyzer.java index 5a376a67fe..8929cf7027 100644 --- a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/PEExceptionAnalyzer.java +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/PEExceptionAnalyzer.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. @@ -19,20 +19,26 @@ import java.util.List; import ghidra.app.cmd.data.exceptionhandling.CreateEHFuncInfoBackgroundCmd; import ghidra.app.cmd.data.exceptionhandling.EHFunctionInfoModel; -import ghidra.app.plugin.core.searchmem.RegExSearchData; import ghidra.app.services.*; import ghidra.app.util.datatype.microsoft.DataApplyOptions; import ghidra.app.util.datatype.microsoft.DataValidationOptions; import ghidra.app.util.importer.MessageLog; +import ghidra.features.base.memsearch.bytesource.AddressableByteSource; +import ghidra.features.base.memsearch.bytesource.ProgramByteSource; +import ghidra.features.base.memsearch.gui.SearchSettings; +import ghidra.features.base.memsearch.matcher.ByteMatcher; +import ghidra.features.base.memsearch.matcher.RegExByteMatcher; +import ghidra.features.base.memsearch.searcher.MemoryMatch; +import ghidra.features.base.memsearch.searcher.MemorySearcher; import ghidra.framework.cmd.Command; import ghidra.program.model.address.*; import ghidra.program.model.data.InvalidDataTypeException; import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.MemoryBlock; import ghidra.program.util.ProgramMemoryUtil; import ghidra.util.datastruct.ListAccumulator; import ghidra.util.exception.CancelledException; -import ghidra.util.search.memory.*; import ghidra.util.task.TaskMonitor; /** @@ -81,24 +87,26 @@ public class PEExceptionAnalyzer extends AbstractAnalyzer { "[\\x20,\\x21,\\x22]\\x05\\x93[\\x19,\\x39,\\x59,\\x79,\\x99,\\xb9,\\xd9,\\xf9]"; String bePattern = "[\\x19,\\x39,\\x59,\\x79,\\x99,\\xb9,\\xd9,\\xf9]\\x93\\x05[\\x20,\\x21,\\x22]"; - RegExSearchData regExSearchData = RegExSearchData.createRegExSearchData( - program.getLanguage().isBigEndian() ? bePattern : lePattern); + + String pattern = program.getLanguage().isBigEndian() ? bePattern : lePattern; int alignment = 4; - SearchInfo searchInfo = new SearchInfo(regExSearchData, MATCH_LIMIT, false, true, alignment, - false, new CodeUnitSearchInfo(false, true, true), null); - // Only want to search loaded and initialized addresses. - AddressSet intersection = - program.getMemory().getLoadedAndInitializedAddressSet().intersect(set); + SearchSettings settings = new SearchSettings().withAlignment(alignment); + settings = settings.withIncludeInstructions(false); // only search data + + ByteMatcher matcher = new RegExByteMatcher(pattern, settings); + AddressableByteSource byteSource = new ProgramByteSource(program); + Memory memory = program.getMemory(); + AddressSet addresses = memory.getLoadedAndInitializedAddressSet().intersect(set); + // Only want to search exception handling memory blocks. - intersection = getAddressSet(ehBlocks).intersect(intersection); + addresses = getAddressSet(ehBlocks).intersect(addresses); - RegExMemSearcherAlgorithm searcher = - new RegExMemSearcherAlgorithm(searchInfo, intersection, program, true); + MemorySearcher searcher = new MemorySearcher(byteSource, matcher, addresses, MATCH_LIMIT); - ListAccumulator accumulator = new ListAccumulator<>(); - searcher.search(accumulator, monitor); - List results = accumulator.asList(); + ListAccumulator accumulator = new ListAccumulator<>(); + searcher.findAll(accumulator, monitor); + List results = accumulator.asList(); // Establish the options to use when creating the exception handling data. // For now these are fixed. Later these may need to come from analysis options. @@ -109,14 +117,14 @@ public class PEExceptionAnalyzer extends AbstractAnalyzer { // Attempt to create data at each address if it appears to be valid for the data type. int count = 0; - for (MemSearchResult result : results) { + for (MemoryMatch match : results) { monitor.setProgress(count++); if (monitor.isCancelled()) { return false; } - Address address = result.getAddress(); + Address address = match.getAddress(); if (address.getOffset() % alignment != 0) { continue; // Skip non-aligned addresses. } @@ -130,7 +138,7 @@ public class PEExceptionAnalyzer extends AbstractAnalyzer { // Create FuncInfo data at the address of the magic number, if the data appears valid. // This can also create associated exception handling data based on the options. - Command cmd = + Command cmd = new CreateEHFuncInfoBackgroundCmd(address, validationOptions, applyOptions); cmd.applyTo(program); }