GP-4911 removed old memory search code

This commit is contained in:
ghidragon
2024-09-13 11:17:51 -04:00
parent 0348791f94
commit 5956b2d51f
29 changed files with 93 additions and 3926 deletions
@@ -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<Charset> 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;
}
}
@@ -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 <space> to add more.";
return false;
}
statusText = "";
for(int i=0;i<str.length();i++) {
if (VALID_CHARS.indexOf(str.charAt(i)) < 0) {
return false;
}
}
return true;
}
private byte getByte(String token) {
byte b = 0;
for(int i=0;i<token.length();i++) {
b <<= 1;
char c = token.charAt(i);
if (c == '1') {
b |= 1;
}
}
return b;
}
/**
* Return a mask byte that has a bit set to 1 for each bit that is not a wildcard. Any bits
* that aren't specified (i.e. token.lenght &lt; 8) are treated as valid test bits.
* @param token the string of bits to determine a mask for.
*/
private byte getMask(String token) {
byte b = 0;
for(int i=0;i<8;i++) {
b <<= 1;
if (i < token.length()) {
char c = token.charAt(i);
if (c == '1' || c == '0') {
b |= 1;
}
}
else {
b |= 1;
}
}
return b;
}
@Override
public boolean usesEndieness() {
return false;
}
}
@@ -1,235 +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.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import docking.widgets.button.GRadioButton;
import ghidra.util.HTMLUtilities;
import ghidra.util.exception.AssertException;
public class DecimalSearchFormat extends SearchFormat {
private static final String MINUS_SIGN = "-";
private static final int BYTE = 0;
private static final int WORD = 1;
private static final int DWORD = 2;
private static final int QWORD = 3;
private static final int FLOAT = 4;
private static final int DOUBLE = 5;
private int decimalFormat = WORD;
public DecimalSearchFormat(ChangeListener listener) {
super("Decimal", listener);
}
@Override
public String getToolTip() {
return HTMLUtilities.toHTML(
"Interpret values as a sequence of\n" + "decimal numbers, separated by spaces");
}
private void setDecimalFormat(int format) {
decimalFormat = format;
changeListener.stateChanged(new ChangeEvent(this));
}
@Override
public JPanel getOptionsPanel() {
ButtonGroup decimalGroup = new ButtonGroup();
GRadioButton decimalByte = new GRadioButton("Byte", false);
GRadioButton decimalWord = new GRadioButton("Word", true);
GRadioButton decimalDWord = new GRadioButton("DWord", false);
GRadioButton decimalQWord = new GRadioButton("QWord", false);
GRadioButton decimalFloat = new GRadioButton("Float", false);
GRadioButton decimalDouble = new GRadioButton("Double", false);
decimalByte.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ev) {
setDecimalFormat(BYTE);
}
});
decimalWord.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ev) {
setDecimalFormat(WORD);
}
});
decimalDWord.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ev) {
setDecimalFormat(DWORD);
}
});
decimalQWord.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ev) {
setDecimalFormat(QWORD);
}
});
decimalFloat.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ev) {
setDecimalFormat(FLOAT);
}
});
decimalDouble.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ev) {
setDecimalFormat(DOUBLE);
}
});
decimalGroup.add(decimalByte);
decimalGroup.add(decimalWord);
decimalGroup.add(decimalDWord);
decimalGroup.add(decimalQWord);
decimalGroup.add(decimalFloat);
decimalGroup.add(decimalDouble);
JPanel decimalOptionsPanel = new JPanel();
decimalOptionsPanel.setLayout(new GridLayout(3, 2));
decimalOptionsPanel.add(decimalByte);
decimalOptionsPanel.add(decimalWord);
decimalOptionsPanel.add(decimalDWord);
decimalOptionsPanel.add(decimalQWord);
decimalOptionsPanel.add(decimalFloat);
decimalOptionsPanel.add(decimalDouble);
decimalOptionsPanel.setBorder(BorderFactory.createTitledBorder("Format Options"));
return decimalOptionsPanel;
}
@Override
public SearchData getSearchData(String input) {
List<Byte> 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<Byte> 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<Byte> getBytes(long value, int n) {
List<Byte> 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<Byte> 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;
}
}
@@ -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<String> list = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(input);
while (st.hasMoreTokens()) {
String token = st.nextToken();
if (!isValidHex(token)) {
return SearchData.createInvalidInputSearchData(statusText);
}
List<String> 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<String> 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<String> list = new ArrayList<String>(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 <space> 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;
}
}
}
@@ -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<MemSearchResult, Address> {
@Override
public Address map(MemSearchResult rowObject, Program data, ServiceProvider serviceProvider) {
return rowObject.getAddress();
}
}
@@ -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<MemSearchResult, Function> {
@Override
public Function map(MemSearchResult rowObject, Program program,
ServiceProvider serviceProvider) {
FunctionManager functionManager = program.getFunctionManager();
return functionManager.getFunctionContaining(rowObject.getAddress());
}
}
@@ -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<MemSearchResult, ProgramLocation> {
@Override
public ProgramLocation map(MemSearchResult rowObject, Program program,
ServiceProvider serviceProvider) {
return new ProgramLocation(program, rowObject.getAddress());
}
}
@@ -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<MemSearchResult> {
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<MemSearchResult, ?, ?> column =
getColumn(primaryState.getColumnModelIndex());
String name = column.getColumnName();
if (AddressTableColumn.NAME.equals(name)) {
return true;
}
return false;
}
@Override
protected void doLoad(Accumulator<MemSearchResult> 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);
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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 );
}
@@ -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;
@@ -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<MemoryMatch> accumulator = new ListAccumulator<>();
searcher.findAll(accumulator, monitor);
//@formatter:off
List<Address> 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.
* <P>
* 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
* <p>
* The <code>byteString</code> 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<MemSearchResult> accumulator = new ListAccumulator<>();
searcher.search(accumulator, monitor);
//@formatter:off
List<Address> 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 <code>null</code> 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
* <code>currerntProgram</code>
*/
/**
* Creates a new {@link Namespace} with the given name contained inside the
* specified parent namespace.
* Pass <code>null</code> 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
* <code>currerntProgram</code>
*/
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.
@@ -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;
}
}
@@ -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<MemSearchResult> {
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);
}
}
@@ -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<MemSearchResult> 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<MemSearchResult> 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;
}
}
@@ -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<MemSearchResult> 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<MemSearchResult> accumulator = new ListAccumulator<>();
algorithm.search(accumulator, monitor);
this.results = accumulator.asList();
}
public List<MemSearchResult> getMatchingAddresses() {
return results;
}
}
@@ -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");
}
}
}
@@ -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<MemSearchResult> accumulator, TaskMonitor monitor);
}
@@ -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<MemSearchResult> 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<MemSearchResult> accumulator, TaskMonitor monitor, int progressCount) {
if (addressSet.getNumAddresses() <= Integer.MAX_VALUE) {
searchSubAddressSet(addressSet, accumulator, monitor, progressCount);
return;
}
List<AddressSet> sets = breakSetsByMemoryBlock(addressSet);
int searchLimit = searchInfo.getSearchLimit();
for (AddressSet set : sets) {
searchSubAddressSet(set, accumulator, monitor, progressCount);
if (accumulator.size() >= searchLimit) {
return;
}
}
}
private List<AddressSet> breakSetsByMemoryBlock(AddressSetView addressSet) {
Memory mem = program.getMemory();
List<AddressSet> 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<MemSearchResult> 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;
}
}
@@ -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;
}
}
@@ -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();
@@ -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);
@@ -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<MemSearchResult> accumulator = new ListAccumulator<>();
searcher.search(accumulator, TaskMonitor.DUMMY);
List<MemSearchResult> 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<MemSearchResult> accumulator = new ListAccumulator<>();
searcher.search(accumulator, TaskMonitor.DUMMY);
List<MemSearchResult> 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());
}
}
@@ -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<MemSearchResult> accumulator = new ListAccumulator<>();
searcher.search(accumulator, monitor);
List<MemSearchResult> results = accumulator.asList();
ListAccumulator<MemoryMatch> accumulator = new ListAccumulator<>();
searcher.findAll(accumulator, monitor);
List<MemoryMatch> 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<Program> cmd =
new CreateEHFuncInfoBackgroundCmd(address, validationOptions, applyOptions);
cmd.applyTo(program);
}