mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
Merge remote-tracking branch 'origin/GT-3341_emteere_RTTIPerformance'
This commit is contained in:
commit
024a6190e0
35 changed files with 1692 additions and 907 deletions
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package ghidra.app.analyzers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
@ -51,7 +49,6 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
|||
private static final String DESCRIPTION =
|
||||
"Search for architecture specific byte patterns: typically starts of functions";
|
||||
private static final String PRE_FUNCTION_MATCH_PROPERTY_NAME = "PreFunctionMatch";
|
||||
private static final int RESTRICTED_PATTERN_BYTE_RANGE = 32;
|
||||
private final static String OPTION_NAME_DATABLOCKS = "Search Data Blocks";
|
||||
private static final String OPTION_DESCRIPTION_DATABLOCKS =
|
||||
"Search for byte patterns in blocks that are not executable";
|
||||
|
@ -214,7 +211,6 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
|||
applyActionToSet(program, addr, funcResult, match);
|
||||
}
|
||||
|
||||
|
||||
protected boolean checkPreRequisites(Program program, Address addr) {
|
||||
/**
|
||||
* If the match's mark point occurs in undefined data, schedule disassembly
|
||||
|
@ -254,10 +250,9 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected void applyActionToSet(Program program, Address addr, AddressSet resultSet,
|
||||
Match match) {
|
||||
|
@ -632,25 +627,34 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
|||
return false;
|
||||
}
|
||||
|
||||
AddressSet restrictedSet = removeNotSearchedAddresses(program, set);
|
||||
boolean doExecutableBlocksOnly = checkForExecuteBlock(program) && executableBlocksOnly;
|
||||
|
||||
// clear out any previous potential matches, because we are re-looking at these places
|
||||
// this will keep cruft from accumulating in the property map.
|
||||
getOrCreatePotentialMatchPropertyMap(program).remove(restrictedSet);
|
||||
getOrCreatePotentialMatchPropertyMap(program).remove(set);
|
||||
|
||||
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
||||
for (MemoryBlock block2 : blocks) {
|
||||
MemoryBlock block = block2;
|
||||
if (!restrictedSet.intersects(block.getStart(), block.getEnd())) {
|
||||
continue;
|
||||
MemoryBytePatternSearcher patternSearcher;
|
||||
patternSearcher = new MemoryBytePatternSearcher("Function Starts", root) {
|
||||
|
||||
@Override
|
||||
public void preMatchApply(MatchAction[] actions, Address addr) {
|
||||
contextValueList = null; // make sure, only context from these actions used
|
||||
}
|
||||
try {
|
||||
searchBlock(root, program, block, restrictedSet, monitor);
|
||||
|
||||
@Override
|
||||
public void postMatchApply(MatchAction[] actions, Address addr) {
|
||||
// Actions might have set context, check if postcondition failed first
|
||||
if (!postreqFailedResult.contains(addr)) {
|
||||
setCurrentContext(program, addr);
|
||||
}
|
||||
// get rid of the context list.
|
||||
contextValueList = null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.appendMsg("Unable to scan block " + block.getName() + " for function starts");
|
||||
}
|
||||
}
|
||||
};
|
||||
patternSearcher.setSearchExecutableOnly(doExecutableBlocksOnly);
|
||||
|
||||
patternSearcher.search(program, set, monitor);
|
||||
|
||||
AutoAnalysisManager analysisManager = AutoAnalysisManager.getAnalysisManager(program);
|
||||
if (!disassemResult.isEmpty()) {
|
||||
analysisManager.disassemble(disassemResult);
|
||||
|
@ -708,34 +712,17 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
|||
}
|
||||
|
||||
/**
|
||||
* Get rid of any blocks from the address set that shouldn't be searched.
|
||||
*
|
||||
* @param program
|
||||
* @param bset
|
||||
* @return
|
||||
* @return true - if there are any blocks marked executable
|
||||
*/
|
||||
private AddressSet removeNotSearchedAddresses(Program program, AddressSetView bset) {
|
||||
AddressSet restrictedSet = new AddressSet(bset);
|
||||
private boolean checkForExecuteBlock(Program program) {
|
||||
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
||||
boolean hasExecutable = false;
|
||||
|
||||
for (MemoryBlock block : blocks) {
|
||||
if (block.isExecute()) {
|
||||
hasExecutable = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (MemoryBlock block : blocks) {
|
||||
if (!block.isInitialized()) {
|
||||
restrictedSet.deleteRange(block.getStart(), block.getEnd());
|
||||
continue;
|
||||
}
|
||||
if (executableBlocksOnly && hasExecutable) {
|
||||
if (!block.isExecute()) {
|
||||
restrictedSet.deleteRange(block.getStart(), block.getEnd());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return restrictedSet;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -816,94 +803,6 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
|||
return patlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search through bytes of a memory block using the finite state machine -root-
|
||||
* Apply any additional rules for matching patterns.
|
||||
* @param program is the Program being searched
|
||||
* @param block is the specific block of bytes being searched
|
||||
* @throws IOException
|
||||
* @throws CancelledException
|
||||
*/
|
||||
private void searchBlock(SequenceSearchState root, Program program, MemoryBlock block,
|
||||
AddressSetView restrictSet, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
// if no restricted set, make restrict set the full block
|
||||
AddressSet doneSet = new AddressSet(restrictSet);
|
||||
if (doneSet.isEmpty()) {
|
||||
doneSet.addRange(block.getStart(), block.getEnd());
|
||||
}
|
||||
doneSet = doneSet.intersectRange(block.getStart(), block.getEnd());
|
||||
|
||||
Address blockStartAddr = block.getStart();
|
||||
|
||||
// pull each range off the restricted set
|
||||
AddressRangeIterator addressRanges = doneSet.getAddressRanges();
|
||||
while (addressRanges.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
AddressRange addressRange = addressRanges.next();
|
||||
|
||||
monitor.setMessage("Function Search");
|
||||
monitor.initialize(doneSet.getNumAddresses());
|
||||
monitor.setProgress(0);
|
||||
|
||||
ArrayList<Match> mymatches = new ArrayList<>();
|
||||
|
||||
long streamoffset = blockStartAddr.getOffset();
|
||||
|
||||
// Give block a starting/ending point before this address to search
|
||||
// patterns might start before, since they have a pre-pattern
|
||||
// TODO: this is dangerous, since pattern might be very big, but the set should be restricted
|
||||
// normally only when we are searching for more matching patterns that had a postrule that didn't satisfy
|
||||
// normally the whole memory blocks will get searched.
|
||||
long blockOffset = addressRange.getMinAddress().subtract(blockStartAddr);
|
||||
blockOffset = blockOffset - RESTRICTED_PATTERN_BYTE_RANGE;
|
||||
if (blockOffset <= 0) {
|
||||
// don't go before the block start
|
||||
blockOffset = 0;
|
||||
}
|
||||
|
||||
// compute number of bytes in the range + 1, and don't search more than that.
|
||||
long maxBlockSearchLength =
|
||||
addressRange.getMaxAddress().subtract(blockStartAddr) - blockOffset + 1;
|
||||
|
||||
InputStream data = block.getData();
|
||||
data.skip(blockOffset);
|
||||
|
||||
root.apply(data, maxBlockSearchLength, mymatches, monitor);
|
||||
monitor.checkCanceled();
|
||||
|
||||
monitor.setMessage("Function Search (Examine Matches)");
|
||||
monitor.initialize(mymatches.size());
|
||||
monitor.setProgress(0);
|
||||
|
||||
// TODO: DANGER there is much offset<-->address calculation here
|
||||
// should be OK, since they are all relative to the block.
|
||||
for (int i = 0; i < mymatches.size(); ++i) {
|
||||
monitor.checkCanceled();
|
||||
monitor.setProgress(i);
|
||||
Match match = mymatches.get(i);
|
||||
Address addr = blockStartAddr.add(match.getMarkOffset() + blockOffset);
|
||||
if (!match.checkPostRules(streamoffset + blockOffset)) {
|
||||
continue;
|
||||
}
|
||||
MatchAction[] matchactions = match.getMatchActions();
|
||||
contextValueList = null; // make sure, only context from these actions used
|
||||
for (MatchAction matchaction : matchactions) {
|
||||
matchaction.apply(program, addr, match);
|
||||
}
|
||||
// Actions might have set context, check if postcondition failed first
|
||||
if (!postreqFailedResult.contains(addr)) {
|
||||
setCurrentContext(program, addr);
|
||||
}
|
||||
else {
|
||||
// didn't apply it, get rid of the context list.
|
||||
contextValueList = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchAction getMatchActionByName(String nm) {
|
||||
if (nm.equals("funcstart")) {
|
||||
|
|
|
@ -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.util.bytesearch;
|
||||
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.XmlElement;
|
||||
import ghidra.xml.XmlPullParser;
|
||||
|
||||
public class AlignRule implements PostRule {
|
||||
|
||||
private int mark; // Position, relative to start of pattern, to check alignment at
|
||||
private int alignmask; // Mask of bits that must be zero
|
||||
|
||||
|
||||
public AlignRule(){
|
||||
}
|
||||
|
||||
public AlignRule(int mark, int alignmask){
|
||||
this.mark = mark;
|
||||
this.alignmask = alignmask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Pattern pat, long matchoffset) {
|
||||
int off = (int)matchoffset;
|
||||
return (((off + mark) & alignmask)==0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) {
|
||||
XmlElement el = parser.start("align");
|
||||
mark = SpecXmlUtils.decodeInt(el.getAttribute("mark"));
|
||||
int bits = SpecXmlUtils.decodeInt(el.getAttribute("bits"));
|
||||
alignmask = (1<<bits) - 1;
|
||||
parser.end();
|
||||
}
|
||||
|
||||
public int getAlignMask() {
|
||||
return alignmask;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,389 +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.bytesearch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import ghidra.xml.XmlPullParser;
|
||||
|
||||
public class DittedBitSequence {
|
||||
|
||||
//Given a byte 0-255 (NOT a signed byte), retrieves its popcount.
|
||||
public static int[] popcount = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, //0-15
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, //16-31
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, //32-47
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, //48-63
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, //64-79
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, //80-95
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, //96-111
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, //112-127
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, //128-143
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, //144-159
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, //160-175
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, //176-191
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, //192-207
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, //208-223
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, //224-239
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 //240-255
|
||||
};
|
||||
|
||||
protected int index; // Unique index assigned to this sequence
|
||||
private byte[] bits; // value bits contained in the sequence
|
||||
private byte[] dits; // a 1 indicates the bit is not ditted
|
||||
|
||||
public DittedBitSequence() {
|
||||
bits = null;
|
||||
dits = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor from a ditted-bit-sequence string where white space is ignored (e.g., "10..11.0");
|
||||
* @param dittedBitData
|
||||
* @throws IllegalArgumentException if invalid dittedBitData specified
|
||||
*/
|
||||
public DittedBitSequence(String dittedBitData) {
|
||||
initFromDittedStringData(dittedBitData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor from a ditted-bit string where white space is ignored. If there are no dits,
|
||||
* {@code hex} is true, and {@code hex} does not begin with {code 0x}, {@code 0x} will be
|
||||
* prepended to the string before constructing the {@link DittedBitSequence}.
|
||||
* @param dittedBitData
|
||||
* @param hex
|
||||
*/
|
||||
public DittedBitSequence(String dittedBitData, boolean hex) {
|
||||
if (hex && !dittedBitData.contains(".")) {
|
||||
if (!dittedBitData.startsWith("0x")) {
|
||||
dittedBitData = "0x" + dittedBitData;
|
||||
}
|
||||
}
|
||||
initFromDittedStringData(dittedBitData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy contructor
|
||||
* @param op2 is bit sequence being copied
|
||||
*/
|
||||
public DittedBitSequence(DittedBitSequence op2) {
|
||||
bits = op2.bits;
|
||||
dits = op2.dits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return value bytes
|
||||
*/
|
||||
public byte[] getValueBytes() {
|
||||
return bits.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mask bytes which correspond to value bytes
|
||||
*/
|
||||
public byte[] getMaskBytes() {
|
||||
return dits.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a sequence of bytes to search for. No bits are masked off.
|
||||
* @param bytes
|
||||
*/
|
||||
public DittedBitSequence(byte[] bytes) {
|
||||
bits = bytes;
|
||||
dits = new byte[bytes.length];
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
dits[i] = (byte) 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a bit pattern to search for consisting of
|
||||
* 0 bits, 1 bits, and don't care bits
|
||||
* @param bytes is an array of bytes indicating the 0 and 1 bits that are cared about
|
||||
* @param mask is an array of bytes masking off the bits that should be cared about, a 0 indicates a "don't care"
|
||||
*/
|
||||
public DittedBitSequence(byte[] bytes, byte[] mask) {
|
||||
bits = bytes;
|
||||
dits = mask;
|
||||
}
|
||||
|
||||
//Smallest ditted sequence commensurate with two other ditted sequences.
|
||||
public DittedBitSequence(DittedBitSequence s1, DittedBitSequence s2) {
|
||||
this.bits = new byte[s1.bits.length];
|
||||
this.dits = new byte[s1.bits.length];
|
||||
|
||||
for (int i = 0; i < this.bits.length; i++) {
|
||||
int tempInt = (s1.dits[i] & s2.dits[i] & (0xff ^ s1.bits[i] ^ s2.bits[i]));
|
||||
this.dits[i] = (byte) tempInt;
|
||||
this.bits[i] = (byte) (s1.bits[i] & s2.bits[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
CRC32 crc = new CRC32();
|
||||
crc.update(bits);
|
||||
crc.update(dits);
|
||||
return (int) crc.getValue();
|
||||
}
|
||||
|
||||
//TODO: this notion of equality requires to sequences to have the same value
|
||||
//on a particular bit even if that bit is ditted. Is this correct?
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
DittedBitSequence op2 = (DittedBitSequence) obj;
|
||||
if (bits.length != op2.bits.length) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < bits.length; ++i) {
|
||||
if (bits[i] != op2.bits[i]) {
|
||||
return false;
|
||||
}
|
||||
if (dits[i] != op2.dits[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public DittedBitSequence concatenate(DittedBitSequence op2) {
|
||||
DittedBitSequence res = new DittedBitSequence();
|
||||
res.bits = new byte[bits.length + op2.bits.length];
|
||||
res.dits = new byte[res.bits.length];
|
||||
for (int i = 0; i < bits.length; ++i) {
|
||||
res.bits[i] = bits[i];
|
||||
res.dits[i] = dits[i];
|
||||
}
|
||||
for (int i = 0; i < op2.bits.length; ++i) {
|
||||
res.bits[bits.length + i] = op2.bits[i];
|
||||
res.dits[bits.length + i] = op2.dits[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public boolean isMatch(int pos, int val) {
|
||||
if (pos >= bits.length) {
|
||||
return false;
|
||||
}
|
||||
return ((byte) (val & dits[pos])) == bits[pos];
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return bits.length;
|
||||
}
|
||||
|
||||
public int getNumFixedBits() {
|
||||
int popcnt = 0;
|
||||
for (byte dit : dits) {
|
||||
popcnt += popcount[0xff & dit];
|
||||
}
|
||||
return popcnt;
|
||||
}
|
||||
|
||||
//Return the number of dits.
|
||||
public int getNumUncertainBits() {
|
||||
int popcnt = 0;
|
||||
for (byte dit : dits) {
|
||||
popcnt += popcount[0xff & dit];
|
||||
}
|
||||
return 8 * dits.length - popcnt;
|
||||
}
|
||||
|
||||
public void writeBits(StringBuffer buf) {
|
||||
for (int chunk = 0; chunk < this.bits.length; chunk++) {
|
||||
buf.append(' ');
|
||||
byte dchomp = this.dits[chunk];
|
||||
int bchomp = this.bits[chunk];
|
||||
for (int pos = 128; pos > 0; pos >>>= 1) {
|
||||
if ((dchomp & pos) == 0) {
|
||||
buf.append('.');
|
||||
}
|
||||
else {
|
||||
buf.append((bchomp & pos) != 0 ? '1' : '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
writeBits(buf);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String getHexString() {
|
||||
String uncompressed = this.toString();
|
||||
String[] parts = uncompressed.trim().split(" ");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0, max = parts.length; i < max; ++i) {
|
||||
if (parts[i].contains(".")) {
|
||||
sb.append(parts[i]);
|
||||
if (i != (max - 1)) {
|
||||
sb.append(" ");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String hexByte = Integer.toHexString(Integer.parseUnsignedInt(parts[i].trim(), 2));
|
||||
if (hexByte.length() < 2) {
|
||||
hexByte = "0" + hexByte;
|
||||
}
|
||||
sb.append("0x");
|
||||
sb.append(hexByte);
|
||||
if (i != (max - 1)) {
|
||||
sb.append(" ");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected int restoreXmlData(XmlPullParser parser) throws IOException {
|
||||
parser.start("data");
|
||||
String text = parser.end().getText();
|
||||
try {
|
||||
return initFromDittedStringData(text);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IOException(
|
||||
"Bad <data> tag in at line " + parser.getLineNumber() + " : " + text);
|
||||
}
|
||||
}
|
||||
|
||||
private int initFromDittedStringData(String text) throws IllegalArgumentException {
|
||||
int markOffset = -1;
|
||||
int mode = -1; // -1: looking for start, -2: skip to EOL, 0: hex mode, 1: binary mode
|
||||
ArrayList<Byte> ditarray = new ArrayList<Byte>();
|
||||
ArrayList<Byte> bitarray = new ArrayList<Byte>();
|
||||
int i = 0;
|
||||
while (i < text.length()) {
|
||||
char c1, c2;
|
||||
c1 = text.charAt(i);
|
||||
if (mode == -2 && c1 != '\n') {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (Character.isWhitespace(c1)) {
|
||||
mode = -1;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (c1 == '#') { // start comment - skip remainder of line
|
||||
mode = -2;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (mode == -1) {
|
||||
if (c1 == '0') {
|
||||
c2 = text.charAt(i + 1);
|
||||
if (c2 == 'x') {
|
||||
mode = 0; // Normal hexdecimal mode
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (c1 == '*') {
|
||||
markOffset = ditarray.size(); // Set mark at current number of bytes specified
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
else if ((c1 == '0') || (c1 == '1') || (c1 == '.')) {
|
||||
mode = 1;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Bad ditted bit sequence");
|
||||
}
|
||||
}
|
||||
if (mode == 0) {
|
||||
c2 = text.charAt(i + 1);
|
||||
i += 2;
|
||||
int val = 0;
|
||||
int mask = 0xff;
|
||||
if (c1 == '.') {
|
||||
mask ^= 0xf0;
|
||||
}
|
||||
else {
|
||||
val = Character.getNumericValue(c1) << 4;
|
||||
}
|
||||
if (c2 == '.') {
|
||||
mask ^= 0xf;
|
||||
}
|
||||
else {
|
||||
val |= Character.getNumericValue(c2);
|
||||
}
|
||||
|
||||
bitarray.add(Byte.valueOf((byte) val));
|
||||
ditarray.add(Byte.valueOf((byte) mask));
|
||||
}
|
||||
else {
|
||||
int val = 0;
|
||||
int mask = 0;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
c1 = text.charAt(i + j);
|
||||
if (c1 == '0') {
|
||||
val <<= 1;
|
||||
mask <<= 1;
|
||||
mask |= 1;
|
||||
}
|
||||
else if (c1 == '.') {
|
||||
val <<= 1;
|
||||
mask <<= 1;
|
||||
}
|
||||
else {
|
||||
val <<= 1;
|
||||
val |= 1;
|
||||
mask <<= 1;
|
||||
mask |= 1;
|
||||
}
|
||||
}
|
||||
i += 8;
|
||||
bitarray.add(Byte.valueOf((byte) val));
|
||||
ditarray.add(Byte.valueOf((byte) mask));
|
||||
}
|
||||
}
|
||||
bits = new byte[bitarray.size()];
|
||||
dits = new byte[ditarray.size()];
|
||||
for (int k = 0; k < bits.length; ++k) {
|
||||
bits[k] = bitarray.get(k).byteValue();
|
||||
dits[k] = ditarray.get(k).byteValue();
|
||||
}
|
||||
return markOffset;
|
||||
}
|
||||
|
||||
public int getNumInitialFixedBits(int marked) {
|
||||
if (dits == null) {
|
||||
return 0;
|
||||
}
|
||||
if ((marked <= 0) || (marked > dits.length)) {
|
||||
return 0;//perhaps return -1 instead?
|
||||
}
|
||||
int popcnt = 0;
|
||||
for (int i = 0; i < marked; ++i) {
|
||||
popcnt += popcount[0xff & dits[i]];
|
||||
}
|
||||
return popcnt;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,35 +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.util.bytesearch;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.xml.XmlPullParser;
|
||||
|
||||
public class DummyMatchAction implements MatchAction {
|
||||
|
||||
@Override
|
||||
public void apply(Program program, Address addr, Match match) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreXml(XmlPullParser parser) {
|
||||
parser.discardSubTree();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,81 +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.bytesearch;
|
||||
|
||||
public class Match {
|
||||
private DittedBitSequence sequence; // Pattern that matches
|
||||
private long offset; // starting offset within bytestream of match
|
||||
|
||||
public Match(DittedBitSequence seq, long off) {
|
||||
sequence = seq;
|
||||
offset = off;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the sequence corresponds to a PatternPair, return the number of postbits
|
||||
* @return the number of post bits
|
||||
*/
|
||||
public int getNumPostBits() {
|
||||
if (!(sequence instanceof Pattern)) {
|
||||
return 0;
|
||||
}
|
||||
int marked = ((Pattern) sequence).getMarkOffset();
|
||||
if (marked == 0) {
|
||||
return sequence.getNumFixedBits();
|
||||
}
|
||||
return sequence.getNumFixedBits() - sequence.getNumInitialFixedBits(marked);
|
||||
}
|
||||
|
||||
public MatchAction[] getMatchActions() {
|
||||
return ((Pattern) sequence).getMatchActions();
|
||||
}
|
||||
|
||||
public int getSequenceSize() {
|
||||
return sequence.getSize();
|
||||
}
|
||||
|
||||
public int getSequenceIndex() {
|
||||
return sequence.getIndex();
|
||||
}
|
||||
|
||||
public long getMarkOffset() {
|
||||
return offset + ((Pattern) sequence).getMarkOffset();
|
||||
}
|
||||
|
||||
public long getMatchStart() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public boolean checkPostRules(long streamoffset) {
|
||||
long curoffset = streamoffset + offset;
|
||||
Pattern pattern = (Pattern) sequence;
|
||||
PostRule[] postRules = pattern.getPostRules();
|
||||
for (int i = 0; i < postRules.length; ++i) {
|
||||
if (!postRules[i].apply(pattern, curoffset)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getHexString() {
|
||||
return sequence.getHexString();
|
||||
}
|
||||
|
||||
public DittedBitSequence getSequence() {
|
||||
return sequence;
|
||||
}
|
||||
}
|
|
@ -1,31 +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.util.bytesearch;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.xml.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Action that should be applied to a Program at the Address a pattern matches
|
||||
*
|
||||
*/
|
||||
public interface MatchAction {
|
||||
public void apply(Program program,Address addr,Match match);
|
||||
|
||||
public void restoreXml(XmlPullParser parser);
|
||||
}
|
|
@ -1,210 +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.bytesearch;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.xml.sax.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.*;
|
||||
|
||||
public class Pattern extends DittedBitSequence {
|
||||
|
||||
private int markOffset; // Within pattern what is the 'marked' byte
|
||||
private PostRule[] postrule;
|
||||
private MatchAction[] actions;
|
||||
|
||||
public Pattern() {
|
||||
markOffset = 0;
|
||||
postrule = null;
|
||||
actions = null;
|
||||
|
||||
}
|
||||
|
||||
public Pattern(DittedBitSequence seq, int offset, PostRule[] postArray, MatchAction[] matchArray) {
|
||||
super(seq);
|
||||
markOffset = offset;
|
||||
postrule = postArray;
|
||||
actions = matchArray;
|
||||
}
|
||||
|
||||
public PostRule[] getPostRules() {
|
||||
return postrule;
|
||||
}
|
||||
|
||||
public MatchAction[] getMatchActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
public void setMatchActions(MatchAction[] actions){
|
||||
this.actions = actions;
|
||||
}
|
||||
|
||||
public int getMarkOffset() {
|
||||
return markOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the PostRule and the MatchAction tags
|
||||
* @param parser is the parser at the start of tags
|
||||
* @param pfactory is the factory for the PostRule and MatchAction objects
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void restoreXmlAttributes(ArrayList<PostRule> postrulelist,
|
||||
ArrayList<MatchAction> actionlist, XmlPullParser parser, PatternFactory pfactory)
|
||||
throws IOException {
|
||||
XmlElement el = parser.peek();
|
||||
while (el.isStart()) {
|
||||
PostRule newrule = pfactory.getPostRuleByName(el.getName());
|
||||
if (newrule != null) {
|
||||
newrule.restoreXml(parser);
|
||||
postrulelist.add(newrule);
|
||||
}
|
||||
else {
|
||||
MatchAction matchaction = pfactory.getMatchActionByName(el.getName());
|
||||
if (matchaction != null) {
|
||||
matchaction.restoreXml(parser);
|
||||
actionlist.add(matchaction);
|
||||
}
|
||||
else {
|
||||
throw new IOException("Bad <pattern> subtag");
|
||||
}
|
||||
}
|
||||
el = parser.peek();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void restoreXml(XmlPullParser parser, PatternFactory pfactory) throws IOException {
|
||||
markOffset = 0;
|
||||
ArrayList<PostRule> postrulelist = new ArrayList<PostRule>();
|
||||
ArrayList<MatchAction> actionlist = new ArrayList<MatchAction>();
|
||||
XmlElement el = parser.start("pattern");
|
||||
String markstring = el.getAttribute("mark");
|
||||
if (markstring != null) {
|
||||
markOffset = SpecXmlUtils.decodeInt(markstring);
|
||||
}
|
||||
int moff = restoreXmlData(parser); // Restore data portion of the tag
|
||||
if (moff >= 0) {
|
||||
markOffset = moff;
|
||||
}
|
||||
if (pfactory != null) {
|
||||
restoreXmlAttributes(postrulelist, actionlist, parser, pfactory);
|
||||
}
|
||||
parser.end();
|
||||
actions = new MatchAction[actionlist.size()];
|
||||
actionlist.toArray(actions);
|
||||
postrule = new PostRule[postrulelist.size()];
|
||||
postrulelist.toArray(postrule);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Read patterns from specified file
|
||||
* @param file pattern file
|
||||
* @param patlist list for patterns to be added to
|
||||
* @param pfactory optional factory for use in parsing PostRule and MatchAction elements.
|
||||
* If null such elements may not be present.
|
||||
* @throws SAXException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void readPatterns(ResourceFile file, ArrayList<Pattern> patlist,
|
||||
PatternFactory pfactory) throws SAXException, IOException {
|
||||
ErrorHandler handler = new ErrorHandler() {
|
||||
@Override
|
||||
public void error(SAXParseException exception) throws SAXException {
|
||||
throw new SAXException("Error: " + exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatalError(SAXParseException exception) throws SAXException {
|
||||
throw new SAXException("Fatal error: " + exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(SAXParseException exception) throws SAXException {
|
||||
throw new SAXException("Warning: " + exception);
|
||||
}
|
||||
};
|
||||
XmlPullParser parser;
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
parser = new NonThreadedXmlPullParserImpl(inputStream, file.getName(), handler, false);
|
||||
}
|
||||
parser.start("patternlist");
|
||||
XmlElement el = parser.peek();
|
||||
while (el.isStart()) {
|
||||
if (el.getName().equals("patternpairs")) {
|
||||
PatternPairSet pairset = new PatternPairSet();
|
||||
pairset.restoreXml(parser, pfactory);
|
||||
pairset.createFinalPatterns(patlist);
|
||||
}
|
||||
else {
|
||||
Pattern pat = new Pattern();
|
||||
pat.restoreXml(parser, pfactory);
|
||||
patlist.add(pat);
|
||||
}
|
||||
el = parser.peek();
|
||||
}
|
||||
parser.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read just the post patterns from the <patternpair> tags
|
||||
* @param file is the file to read from
|
||||
* @param patternlist collects the resulting Pattern objects
|
||||
* @param pfactory is the factory for constructing postrules and matchactions
|
||||
* @throws IOException
|
||||
* @throws SAXException
|
||||
*/
|
||||
public static void readPostPatterns(File file, ArrayList<Pattern> patlist,
|
||||
PatternFactory pfactory) throws SAXException, IOException {
|
||||
ErrorHandler handler = new ErrorHandler() {
|
||||
@Override
|
||||
public void error(SAXParseException exception) throws SAXException {
|
||||
throw new SAXException("Error: " + exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatalError(SAXParseException exception) throws SAXException {
|
||||
throw new SAXException("Fatal error: " + exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(SAXParseException exception) throws SAXException {
|
||||
throw new SAXException("Warning: " + exception);
|
||||
}
|
||||
};
|
||||
XmlPullParser parser = new NonThreadedXmlPullParserImpl(file, handler, false);
|
||||
parser.start("patternlist");
|
||||
XmlElement el = parser.peek();
|
||||
while (el.isStart()) {
|
||||
if (el.getName().equals("patternpairs")) {
|
||||
PatternPairSet pairset = new PatternPairSet();
|
||||
pairset.restoreXml(parser, pfactory);
|
||||
pairset.extractPostPatterns(patlist);
|
||||
}
|
||||
else {
|
||||
parser.next();
|
||||
}
|
||||
el = parser.peek();
|
||||
}
|
||||
parser.end();
|
||||
|
||||
}
|
||||
}
|
|
@ -1,23 +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.util.bytesearch;
|
||||
|
||||
public interface PatternFactory {
|
||||
public MatchAction getMatchActionByName(String nm);
|
||||
|
||||
public PostRule getPostRuleByName(String nm);
|
||||
}
|
|
@ -1,143 +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.bytesearch;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
import ghidra.xml.XmlElement;
|
||||
import ghidra.xml.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Two collections of patterns that are paired together to create larger patterns
|
||||
* The final large patterns all must first match a pattern from the "pre" pattern collection
|
||||
* followed immediately by a pattern from the "post" pattern collection
|
||||
*
|
||||
*/
|
||||
public class PatternPairSet {
|
||||
private int totalBitsOfCheck; // Minimum number of bits of check in final patterns
|
||||
private int postBitsOfCheck; // Minimum bits of check in "post" part of pattern
|
||||
private ArrayList<DittedBitSequence> preSequences;
|
||||
private ArrayList<Pattern> postPatterns;
|
||||
|
||||
public PatternPairSet() {
|
||||
preSequences = new ArrayList<DittedBitSequence>();
|
||||
postPatterns = new ArrayList<Pattern>();
|
||||
}
|
||||
|
||||
public void createFinalPatterns(ArrayList<Pattern> finalpats) {
|
||||
for(int i=0;i<postPatterns.size();++i) {
|
||||
Pattern postpattern = postPatterns.get(i);
|
||||
int postcheck = postpattern.getNumFixedBits();
|
||||
if (postcheck < postBitsOfCheck) {
|
||||
continue;
|
||||
}
|
||||
for(int j=0;j<preSequences.size();++j) {
|
||||
DittedBitSequence prepattern = preSequences.get(j);
|
||||
int precheck = prepattern.getNumFixedBits();
|
||||
if (precheck + postcheck < totalBitsOfCheck) {
|
||||
continue;
|
||||
}
|
||||
DittedBitSequence concat = prepattern.concatenate(postpattern);
|
||||
Pattern finalpattern = new Pattern(concat,prepattern.getSize(),postpattern.getPostRules(),postpattern.getMatchActions());
|
||||
finalpats.add(finalpattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void extractPostPatterns(ArrayList<Pattern> postpats) {
|
||||
for(int i=0;i<postPatterns.size();++i) {
|
||||
postpats.add(postPatterns.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public void restoreXml(XmlPullParser parser,PatternFactory pfactory) throws IOException {
|
||||
XmlElement el = parser.start("patternpairs");
|
||||
totalBitsOfCheck = SpecXmlUtils.decodeInt(el.getAttribute("totalbits"));
|
||||
postBitsOfCheck = SpecXmlUtils.decodeInt(el.getAttribute("postbits"));
|
||||
parser.start("prepatterns");
|
||||
el= parser.peek();
|
||||
while(el.isStart()) {
|
||||
DittedBitSequence preseq = new DittedBitSequence();
|
||||
preseq.restoreXmlData(parser);
|
||||
preSequences.add(preseq);
|
||||
el = parser.peek();
|
||||
}
|
||||
parser.end();
|
||||
while(parser.peek().isStart()) {
|
||||
parser.start("postpatterns");
|
||||
el = parser.peek();
|
||||
ArrayList<DittedBitSequence> postdit = new ArrayList<DittedBitSequence>();
|
||||
while(el.isStart() && el.getName().equals("data")) {
|
||||
DittedBitSequence postseq = new DittedBitSequence();
|
||||
postseq.restoreXmlData(parser);
|
||||
if (postseq.getNumFixedBits() >= postBitsOfCheck) {
|
||||
postdit.add(postseq);
|
||||
}
|
||||
el = parser.peek();
|
||||
}
|
||||
ArrayList<PostRule> postRuleArray = new ArrayList<PostRule>();
|
||||
ArrayList<MatchAction> matchActionArray = new ArrayList<MatchAction>();
|
||||
if (pfactory != null) {
|
||||
Pattern.restoreXmlAttributes(postRuleArray, matchActionArray, parser, pfactory);
|
||||
}
|
||||
PostRule[] postRules = new PostRule[postRuleArray.size()];
|
||||
postRuleArray.toArray(postRules);
|
||||
MatchAction[] matchActions = new MatchAction[matchActionArray.size()];
|
||||
matchActionArray.toArray(matchActions);
|
||||
for(int i=0;i<postdit.size();++i) {
|
||||
Pattern postpat = new Pattern(postdit.get(i),0,postRules,matchActions);
|
||||
postPatterns.add(postpat);
|
||||
}
|
||||
parser.end(); // End postpatterns
|
||||
}
|
||||
parser.end(); // End patternlist
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "pre" parts of the patterns
|
||||
* @return pre sequences
|
||||
*/
|
||||
public ArrayList<DittedBitSequence> getPreSequences() {
|
||||
return preSequences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "post" parts of the patterns
|
||||
* @return post patterns
|
||||
*/
|
||||
public ArrayList<Pattern> getPostPatterns() {
|
||||
return postPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required number of fixed bits after the prepattern
|
||||
* @return number of post bits
|
||||
*/
|
||||
public int getPostBitsOfCheck() {
|
||||
return postBitsOfCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required number of fixed bits in the whole pattern
|
||||
* @return number of total fixed bits
|
||||
*/
|
||||
public int getTotalBitsOfCheck() {
|
||||
return totalBitsOfCheck;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,25 +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.util.bytesearch;
|
||||
|
||||
import ghidra.xml.XmlPullParser;
|
||||
|
||||
public interface PostRule {
|
||||
public boolean apply(Pattern pat,long matchoffset);
|
||||
|
||||
public void restoreXml(XmlPullParser parser);
|
||||
}
|
|
@ -1,385 +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.bytesearch;
|
||||
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||
|
||||
private SequenceSearchState parent;
|
||||
private ArrayList<DittedBitSequence> possible; // Patterns that could still match in this state
|
||||
private ArrayList<DittedBitSequence> success; // Patterns that have matched successfully if we reached this state
|
||||
private SequenceSearchState[] trans; // State transitions based on next byte
|
||||
|
||||
public SequenceSearchState(SequenceSearchState par) {
|
||||
parent = par;
|
||||
possible = new ArrayList<DittedBitSequence>();
|
||||
success = null;
|
||||
trans = null;
|
||||
}
|
||||
|
||||
public int getMaxSequenceSize() {
|
||||
int max = 0;
|
||||
for(int i=0;i<possible.size();++i) {
|
||||
int val = possible.get(i).getSize();
|
||||
if (val > max)
|
||||
max = val;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
public void addSequence(DittedBitSequence pat,int pos) {
|
||||
possible.add(pat);
|
||||
if (pos == pat.getSize()) {
|
||||
if (success == null)
|
||||
success = new ArrayList<DittedBitSequence>();
|
||||
success.add(pat);
|
||||
}
|
||||
}
|
||||
|
||||
public void sortSequences() {
|
||||
Comparator<DittedBitSequence> comp = new Comparator<DittedBitSequence>() {
|
||||
@Override
|
||||
public int compare(DittedBitSequence o1, DittedBitSequence o2) {
|
||||
return o1.getIndex() - o2.getIndex();
|
||||
}
|
||||
};
|
||||
Collections.sort(possible,comp);
|
||||
if (success != null)
|
||||
Collections.sort(success,comp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(SequenceSearchState o) {
|
||||
int i=0;
|
||||
for(;;) {
|
||||
if (possible.size() <= i) {
|
||||
if (o.possible.size() <=i)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if (o.possible.size() <= i)
|
||||
return 1;
|
||||
int indus = possible.get(i).getIndex(); // Lexicographic compare an sequence of sequences
|
||||
int indthem = o.possible.get(i).getIndex();
|
||||
if (indus != indthem)
|
||||
return (indus < indthem) ? -1 : 1;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void buildSingleTransition(ArrayList<SequenceSearchState> all,int pos,int val) {
|
||||
SequenceSearchState newstate = null;
|
||||
for(int i=0;i<possible.size();++i) {
|
||||
DittedBitSequence curpat = possible.get(i);
|
||||
if (curpat.isMatch(pos, val)) {
|
||||
if (newstate == null)
|
||||
newstate = new SequenceSearchState(this);
|
||||
newstate.addSequence(curpat, pos+1);
|
||||
}
|
||||
}
|
||||
trans[val] = newstate;
|
||||
if (newstate != null) {
|
||||
newstate.sortSequences();
|
||||
all.add(newstate);
|
||||
}
|
||||
}
|
||||
|
||||
private void exportSuccess(ArrayList<Match> match,int offset) {
|
||||
for(int i=0;i<success.size();++i) { // If we found matches
|
||||
Match newmatch = new Match(success.get(i),offset);
|
||||
match.add(newmatch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge in -op- and this as a single state
|
||||
* @param op
|
||||
*/
|
||||
private void merge(SequenceSearchState op) {
|
||||
SequenceSearchState parent = op.parent;
|
||||
for(int i=0;i<256;++i) {
|
||||
if (parent.trans[i] == op) // Any references to -op- in parent
|
||||
parent.trans[i] = this; // Should be replaced with this
|
||||
}
|
||||
if (op.success != null) { // Merge
|
||||
if (success == null) {
|
||||
success = op.success;
|
||||
}
|
||||
else {
|
||||
ArrayList<DittedBitSequence> tmp = new ArrayList<DittedBitSequence>();
|
||||
int i=0;
|
||||
int j=0;
|
||||
int curpat = -1;
|
||||
int thispat = success.get(i).index;
|
||||
int oppat = op.success.get(j).index;
|
||||
while((i<success.size())||(j<op.success.size())) {
|
||||
if (thispat == oppat) {
|
||||
if (curpat != thispat) {
|
||||
tmp.add(success.get(i));
|
||||
curpat = thispat;
|
||||
}
|
||||
i += 1;
|
||||
j += 1;
|
||||
thispat = (i==success.size()) ? 10000000 : success.get(i).index;
|
||||
oppat = (j==op.success.size()) ? 10000000 : op.success.get(j).index;
|
||||
}
|
||||
else if (thispat < oppat) {
|
||||
if (curpat != thispat) {
|
||||
tmp.add(success.get(i));
|
||||
curpat = thispat;
|
||||
}
|
||||
i += 1;
|
||||
thispat = (i==success.size()) ? 10000000 : success.get(i).index;
|
||||
}
|
||||
else {
|
||||
if (curpat != oppat) {
|
||||
tmp.add(op.success.get(j));
|
||||
curpat = oppat;
|
||||
}
|
||||
j += 1;
|
||||
oppat = (j==op.success.size()) ? 10000000 : op.success.get(j).index;
|
||||
}
|
||||
}
|
||||
success = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sequenceMatch(byte[] bytearray,int numbytes,ArrayList<Match> match) {
|
||||
int subindex = 0;
|
||||
SequenceSearchState curstate = this;
|
||||
|
||||
do {
|
||||
if (curstate.success != null)
|
||||
curstate.exportSuccess(match, 0);
|
||||
if (subindex >= numbytes) return;
|
||||
curstate = curstate.trans[ 0xff & bytearray[subindex] ]; // Perform state transition based on next byte in buffer
|
||||
subindex += 1;
|
||||
} while(curstate != null);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for patterns in a byte array. All matches are returned.
|
||||
* @param buffer is the array of bytes to search
|
||||
* @param match is populated with a Match object for each pattern and position that matches
|
||||
*/
|
||||
public void apply(byte[] buffer,ArrayList<Match> match) {
|
||||
SequenceSearchState curstate;
|
||||
int subindex;
|
||||
for(int offset=0;offset<buffer.length;++offset) {
|
||||
curstate = this; // New starting offset -> Root state
|
||||
subindex = offset;
|
||||
do {
|
||||
if (curstate.success != null) // Check for any successful pattern matches for bytes up to this point
|
||||
curstate.exportSuccess(match, offset);
|
||||
if (subindex >= buffer.length) { // if we've run out of bytes, must restart at next offset
|
||||
break;
|
||||
}
|
||||
curstate = curstate.trans[ 0xff & buffer[subindex] ]; // Perform state transition based on next byte
|
||||
subindex += 1;
|
||||
} while(curstate != null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for pattern in the stream -in-.
|
||||
* @param in - The stream to scan for matches
|
||||
* @param match - Any matches are appended as Match records to this ArrayList
|
||||
* @param monitor - if non-null, check for user cancel, and maintain progress info
|
||||
* @throws IOException
|
||||
*/
|
||||
public void apply(InputStream in,ArrayList<Match> match,TaskMonitor monitor) throws IOException {
|
||||
apply(in,-1L,match,monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for pattern in the stream -in-.
|
||||
* @param in - The stream to scan for matches
|
||||
* @param maxBytes - The maximum number of bytes to scan forward in this stream
|
||||
* @param match - Any matches are appended as Match records to this ArrayList
|
||||
* @param monitor - if non-null, check for user cancel, and maintain progress info
|
||||
* @throws IOException
|
||||
*/
|
||||
public void apply(InputStream in, long maxBytes, ArrayList<Match> match,TaskMonitor monitor) throws IOException {
|
||||
int maxsize = getMaxSequenceSize()+1;
|
||||
if (maxsize <4096)
|
||||
maxsize = 4096;
|
||||
if (maxBytes > 0) {
|
||||
maxBytes += getMaxSequenceSize()+1;
|
||||
}
|
||||
byte[] firstbuf=new byte[maxsize];
|
||||
byte[] secondbuf=new byte[maxsize];
|
||||
byte[] curbuf;
|
||||
SequenceSearchState curstate;
|
||||
int fullbuffers; // Number of buffers that are completely full
|
||||
int ra = in.read(firstbuf);
|
||||
if (ra == firstbuf.length) {
|
||||
ra = in.read(secondbuf);
|
||||
if (ra == secondbuf.length) {
|
||||
fullbuffers = 2;
|
||||
}
|
||||
else {
|
||||
if (ra < 0)
|
||||
ra = 0;
|
||||
fullbuffers = 1;
|
||||
byte[] tmp = new byte[ra];
|
||||
for(int i=0;i<ra;++i)
|
||||
tmp[i] = secondbuf[i];
|
||||
secondbuf = tmp;
|
||||
}
|
||||
}
|
||||
else if (ra < 0)
|
||||
return; // No bytes at all were read
|
||||
else {
|
||||
byte[] tmp = new byte[ra];
|
||||
for(int i=0;i<ra;++i)
|
||||
tmp[i] = firstbuf[i];
|
||||
firstbuf = tmp;
|
||||
fullbuffers = 0;
|
||||
secondbuf = new byte[0];
|
||||
}
|
||||
int offset=0;
|
||||
int bufreloff=0;
|
||||
int subindex;
|
||||
while(fullbuffers == 2) {
|
||||
curstate = this; // New starting offset -> Root state
|
||||
subindex = bufreloff;
|
||||
curbuf = firstbuf;
|
||||
do {
|
||||
if (curstate.success != null) // Check for any successful pattern matches for bytes up to this point
|
||||
curstate.exportSuccess(match, offset);
|
||||
if (subindex >= curbuf.length) { // check that we have enough bytes in current buffer
|
||||
curbuf = secondbuf; // If not, switch to secondary buffer
|
||||
subindex = 0;
|
||||
}
|
||||
curstate = curstate.trans[ 0xff & curbuf[subindex] ]; // Perform state transition based on next byte in buffer
|
||||
subindex += 1;
|
||||
} while(curstate != null);
|
||||
offset += 1; // Advance to next starting offset
|
||||
if (maxBytes > 0 && offset > maxBytes) {
|
||||
break;
|
||||
}
|
||||
bufreloff += 1;
|
||||
if (bufreloff == firstbuf.length) { // If starting offset no longer falls in firstbuf
|
||||
byte[] tmp = firstbuf; // Switch firstbuf with secondbuf
|
||||
firstbuf = secondbuf;
|
||||
secondbuf = tmp;
|
||||
ra = in.read(secondbuf); // refill secondbuf (old firstbuf) with new bytes
|
||||
if (monitor!=null) {
|
||||
if (monitor.isCancelled()) return;
|
||||
monitor.setProgress(offset);
|
||||
}
|
||||
if (ra != secondbuf.length) {
|
||||
fullbuffers = 1;
|
||||
if (ra < 0)
|
||||
ra = 0;
|
||||
tmp = new byte[ra];
|
||||
for(int i=0;i<ra;++i)
|
||||
tmp[i] = secondbuf[i];
|
||||
secondbuf = tmp;
|
||||
}
|
||||
bufreloff = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while(fullbuffers >= 0 && (maxBytes <= 0 || offset < maxBytes)) {
|
||||
if (secondbuf.length == 0)
|
||||
fullbuffers = 0;
|
||||
curstate = this;
|
||||
subindex = bufreloff;
|
||||
curbuf = firstbuf;
|
||||
do {
|
||||
if (curstate.success != null)
|
||||
curstate.exportSuccess(match, offset);
|
||||
if (subindex >= curbuf.length) {
|
||||
if (curbuf == secondbuf) break; // Out of data, all pending patterns fail
|
||||
curbuf = secondbuf;
|
||||
subindex = 0;
|
||||
if (curbuf.length==0) break;
|
||||
}
|
||||
curstate = curstate.trans[ 0xff & curbuf[subindex] ];
|
||||
subindex += 1;
|
||||
} while(curstate != null);
|
||||
offset += 1;
|
||||
bufreloff += 1;
|
||||
if (bufreloff == firstbuf.length) {
|
||||
if (fullbuffers == 0) break;
|
||||
firstbuf = secondbuf;
|
||||
fullbuffers = 0;
|
||||
bufreloff = 0;
|
||||
secondbuf = new byte[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public ArrayList<SequenceSearchState> buildTransitionLevel(ArrayList<SequenceSearchState> prev,int pos) {
|
||||
ArrayList<SequenceSearchState> res = new ArrayList<SequenceSearchState>();
|
||||
Iterator<SequenceSearchState> iterator = prev.iterator();
|
||||
while(iterator.hasNext()) { // For each current state
|
||||
SequenceSearchState next = iterator.next();
|
||||
next.trans = new SequenceSearchState[256];
|
||||
for(int i=0;i<256;++i) { // Try every byte transition
|
||||
next.buildSingleTransition(res, pos, i);
|
||||
}
|
||||
}
|
||||
if (res.isEmpty()) return res;
|
||||
// Prepare to dedup the states
|
||||
Collections.sort(res);
|
||||
ArrayList<SequenceSearchState> finalres = new ArrayList<SequenceSearchState>();
|
||||
Iterator<SequenceSearchState> iter = res.iterator();
|
||||
SequenceSearchState curpat = iter.next();
|
||||
finalres.add(curpat);
|
||||
while(iter.hasNext()) {
|
||||
SequenceSearchState nextpat = iter.next();
|
||||
int comp = curpat.compareTo(nextpat);
|
||||
if (comp == 0) { // Identical states
|
||||
curpat.merge(nextpat);
|
||||
}
|
||||
else {
|
||||
curpat = nextpat;
|
||||
finalres.add(curpat);
|
||||
}
|
||||
}
|
||||
return finalres;
|
||||
}
|
||||
|
||||
static public SequenceSearchState buildStateMachine(ArrayList<? extends DittedBitSequence> patterns) {
|
||||
SequenceSearchState root = new SequenceSearchState(null);
|
||||
for(int i=0;i<patterns.size();++i) {
|
||||
DittedBitSequence pat = patterns.get(i);
|
||||
pat.index = i;
|
||||
root.addSequence(pat, 0);
|
||||
}
|
||||
root.sortSequences();
|
||||
ArrayList<SequenceSearchState> statelevel = new ArrayList<SequenceSearchState>();
|
||||
statelevel.add(root);
|
||||
int level = 0;
|
||||
do {
|
||||
statelevel = buildTransitionLevel(statelevel, level);
|
||||
level += 1;
|
||||
} while(!statelevel.isEmpty());
|
||||
return root;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue