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 {