From cd41a43ab8565b083798ad5b96e2a54e0939c56a Mon Sep 17 00:00:00 2001
From: dragonmacher <48328597+dragonmacher@users.noreply.github.com>
Date: Wed, 6 Oct 2021 17:04:33 -0400
Subject: [PATCH] GP-1358 - Updated the Search Memory dialog to treat a single
wildcard character as if 2 wildcards had been entered
Closes #3351
---
.../help/help/topics/Search/Search_Memory.htm | 13 ++++
.../core/searchmem/HexSearchFormat.java | 47 +++++++-----
.../app/plugin/core/searchmem/SearchData.java | 30 ++++----
.../core/searchmem/MemSearchHexTest.java | 74 ++++++++++++++-----
4 files changed, 112 insertions(+), 52 deletions(-)
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_Memory.htm b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_Memory.htm
index 3cbeebcbb3..862728f466 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_Memory.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/Search/Search_Memory.htm
@@ -137,6 +137,19 @@
+
+
+ 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.
+
+
+ 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.
+
+
+
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
index 2785f08171..e4b564d590 100644
--- 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
@@ -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 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 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.";
@@ -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;
+ }
}
}
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
index a714e9a4f9..4bb94166c4 100644
--- 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
@@ -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 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 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 addrs = addrs(0x01001004, 0x01002d27);
+
+ setValueText("85 .");
+
+ performSearchTest(addrs, "Next");
+ }
+
@Test
public void testHexWildcardSearchBackwards() throws Exception {