mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-09-19 18:21:26 +00:00
GP-1358 - Updated the Search Memory dialog to treat a single wildcard character as if 2 wildcards had been entered
Closes #3351
This commit is contained in:
parent
7e2cd85eac
commit
cd41a43ab8
|
@ -137,6 +137,19 @@
|
|||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
|
||||
<P><IMG alt="" border="0" src="../../shared/tip.png">
|
||||
As a convenience, if a user enters a single wildcard value within the search text, then
|
||||
the search string will be interpreted as if 2 consecutive wildcard characters were
|
||||
entered, meaning to match any byte value.
|
||||
</P>
|
||||
<P>
|
||||
Similarly, if the search string contains an odd number of characters, then a 0 is prepended
|
||||
to the search string, based on the assumption that a single hex digit implies a leading
|
||||
0 value.
|
||||
</P>
|
||||
|
||||
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.searchmem;
|
||||
|
||||
import ghidra.util.HTMLUtilities;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import ghidra.util.HTMLUtilities;
|
||||
|
||||
public class HexSearchFormat extends SearchFormat {
|
||||
|
||||
private static final String WILD_CARDS = ".?";
|
||||
|
@ -33,8 +33,8 @@ public class HexSearchFormat extends SearchFormat {
|
|||
|
||||
@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");
|
||||
return HTMLUtilities.toHTML("Interpret value as a sequence of\n" +
|
||||
"hex numbers, separated by spaces.\n" + "Enter '.' or '?' for a wildcard match");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,18 +65,32 @@ public class HexSearchFormat extends SearchFormat {
|
|||
}
|
||||
|
||||
private List<String> getByteStrings(String token) {
|
||||
if (token.length() % 2 != 0) {
|
||||
|
||||
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.";
|
||||
|
@ -92,19 +106,13 @@ public class HexSearchFormat extends SearchFormat {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the byte value to be used for the given hex bytes. Handles wildcard
|
||||
* characters by return treating them as 0s.
|
||||
* 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);
|
||||
if (WILD_CARDS.indexOf(c1) > 0) {
|
||||
c1 = '0';
|
||||
}
|
||||
if (WILD_CARDS.indexOf(c2) > 0) {
|
||||
c2 = '0';
|
||||
}
|
||||
|
||||
// note: the hexValueOf() method will turn wildcard chars into 0s
|
||||
return (byte) (hexValueOf(c1) * 16 + hexValueOf(c2));
|
||||
}
|
||||
|
||||
|
@ -117,13 +125,13 @@ public class HexSearchFormat extends SearchFormat {
|
|||
char c2 = tok.charAt(1);
|
||||
int index1 = WILD_CARDS.indexOf(c1);
|
||||
int index2 = WILD_CARDS.indexOf(c2);
|
||||
if ((index1 >= 0) && (index2 >= 0)) {
|
||||
if (index1 >= 0 && index2 >= 0) {
|
||||
return (byte) 0x00;
|
||||
}
|
||||
if ((index1 >= 0) && (index2 < 0)) {
|
||||
if (index1 >= 0 && index2 < 0) {
|
||||
return (byte) 0x0F;
|
||||
}
|
||||
if ((index1 < 0) && (index2 >= 0)) {
|
||||
if (index1 < 0 && index2 >= 0) {
|
||||
return (byte) 0xF0;
|
||||
}
|
||||
return (byte) 0xFF;
|
||||
|
@ -142,8 +150,9 @@ public class HexSearchFormat extends SearchFormat {
|
|||
else if ((c >= 'A') && (c <= 'F')) {
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
else
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* 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.
|
||||
|
@ -26,24 +25,23 @@ public class SearchData {
|
|||
private boolean isValidSearchData;
|
||||
|
||||
// valid input and search data with mask
|
||||
protected SearchData( String inputString, byte[] searchBytes, byte[] 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 ) {
|
||||
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) {
|
||||
public static SearchData createSearchData(String inputString, byte[] searchBytes, byte[] mask) {
|
||||
return new SearchData(inputString, searchBytes, mask);
|
||||
}
|
||||
|
||||
|
@ -54,33 +52,39 @@ public class SearchData {
|
|||
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() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
for(int i=0;i<bytes.length;i++) {
|
||||
String hexString = Integer.toHexString(bytes[i] & 0xff);
|
||||
if ( hexString.length() == 1 ) {
|
||||
buf.append( "0" );
|
||||
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(hexString);
|
||||
buf.append(" ");
|
||||
}
|
||||
return buf.toString();
|
||||
|
|
|
@ -68,8 +68,7 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
|||
MemoryBlock overlayBlock = builder.createOverlayMemory("otherOverlay", "OTHER:0", 100);
|
||||
|
||||
//create and disassemble a function
|
||||
builder.setBytes(
|
||||
"0x01002cf5",
|
||||
builder.setBytes("0x01002cf5",
|
||||
"55 8b ec 83 7d 14 00 56 8b 35 e0 10 00 01 57 74 09 ff 75 14 ff d6 8b f8 eb 02 33 " +
|
||||
"ff ff 75 10 ff d6 03 c7 8d 44 00 02 50 6a 40 ff 15 dc 10 00 01 8b f0 85 f6 74 27 " +
|
||||
"56 ff 75 14 ff 75 10 e8 5c ff ff ff ff 75 18 ff 75 0c 56 ff 75 08 ff 15 04 12 00 " +
|
||||
|
@ -93,7 +92,8 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
|||
builder.setBytes("0x1001004", "85 4f dc 77");
|
||||
builder.applyDataType("0x1001004", new Pointer32DataType(), 1);
|
||||
builder.createEncodedString("0x01001708", "Notepad", StandardCharsets.UTF_16BE, true);
|
||||
builder.createEncodedString("0x01001740", "something else", StandardCharsets.UTF_16BE, true);
|
||||
builder.createEncodedString("0x01001740", "something else", StandardCharsets.UTF_16BE,
|
||||
true);
|
||||
builder.createEncodedString("0x010013cc", "notepad.exe", StandardCharsets.US_ASCII, true);
|
||||
|
||||
//create some undefined data
|
||||
|
@ -117,7 +117,7 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
|||
assertTrue(searchAction.isEnabled());
|
||||
|
||||
// dig up the components of the dialog
|
||||
dialog = waitForDialogComponent(tool.getToolFrame(), MemSearchDialog.class, 2000);
|
||||
dialog = waitForDialogComponent(MemSearchDialog.class);
|
||||
assertNotNull(dialog);
|
||||
assertNotNull(valueComboBox);
|
||||
assertNotNull(hexLabel);
|
||||
|
@ -209,20 +209,20 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
|||
|
||||
//@formatter:off
|
||||
List<Address> addrs = addrs(
|
||||
0x01002d06,
|
||||
0x01002d11,
|
||||
0x01002d2c,
|
||||
0x01002d2f,
|
||||
0x01002d37,
|
||||
0x01002d3a,
|
||||
0x01002d3e,
|
||||
0x01002d52,
|
||||
0x01002d55,
|
||||
0x01002d58,
|
||||
0x01002d5b,
|
||||
0x010035fd,
|
||||
0x01004122,
|
||||
0x01004202,
|
||||
0x01002d06,
|
||||
0x01002d11,
|
||||
0x01002d2c,
|
||||
0x01002d2f,
|
||||
0x01002d37,
|
||||
0x01002d3a,
|
||||
0x01002d3e,
|
||||
0x01002d52,
|
||||
0x01002d55,
|
||||
0x01002d58,
|
||||
0x01002d5b,
|
||||
0x010035fd,
|
||||
0x01004122,
|
||||
0x01004202,
|
||||
0x01004249
|
||||
);
|
||||
//@formatter:on
|
||||
|
@ -353,7 +353,7 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
|||
Highlight[] h = getByteHighlights(addr(0x10029bd), "ff 15 d4 10 00 01");
|
||||
assertEquals(1, h.length);
|
||||
assertEquals(15, h[0].getStart());
|
||||
// end is not important since the match crosses code units
|
||||
// end is not important since the match crosses code units
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -366,7 +366,7 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
|||
Highlight[] h = getByteHighlights(addr(0x10029c3), "8b d8");
|
||||
assertEquals(1, h.length);
|
||||
assertEquals(3, h[0].getStart());
|
||||
// end is not important since the match crosses code units
|
||||
// end is not important since the match crosses code units
|
||||
|
||||
}
|
||||
|
||||
|
@ -506,6 +506,40 @@ public class MemSearchHexTest extends AbstractMemSearchTest {
|
|||
performSearchTest(addrs, "Next");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexWildcardSearch_SingleWildcardCharacter_QuestionMark() throws Exception {
|
||||
|
||||
//
|
||||
// This tests that a single wildcard character will get converted to a value of '00' and
|
||||
// a mast of 'FF'. This allows a single '?' character to be used in place of '??'.
|
||||
//
|
||||
|
||||
goTo(0x01001000);
|
||||
|
||||
List<Address> addrs = addrs(0x01001004, 0x01002d27);
|
||||
|
||||
setValueText("85 ?");
|
||||
|
||||
performSearchTest(addrs, "Next");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexWildcardSearch_SingleWildcardCharacter_Dot() throws Exception {
|
||||
|
||||
//
|
||||
// This tests that a single wildcard character will get converted to a value of '00' and
|
||||
// a mast of 'FF'. This allows a single '.' character to be used in place of '..'.
|
||||
//
|
||||
|
||||
goTo(0x01001000);
|
||||
|
||||
List<Address> addrs = addrs(0x01001004, 0x01002d27);
|
||||
|
||||
setValueText("85 .");
|
||||
|
||||
performSearchTest(addrs, "Next");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexWildcardSearchBackwards() throws Exception {
|
||||
|
||||
|
|
Loading…
Reference in a new issue