mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GT-3341_emteere_RTTIPerformance code review changes, refactored progress
monitor
This commit is contained in:
parent
b426f065f5
commit
b51a9d7ff4
17 changed files with 357 additions and 231 deletions
|
@ -20,20 +20,11 @@ import ghidra.xml.XmlElement;
|
||||||
import ghidra.xml.XmlPullParser;
|
import ghidra.xml.XmlPullParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ByteSearch post search rule when a pattern is found, The pattern must have a certain
|
* ByteSearch post search rule when a pattern is found. Used when a pattern must have a certain
|
||||||
* alignment at an offset from the location the pattern matches. The alignment is
|
* alignment at an offset from the location the pattern matches.
|
||||||
* specified by the mask bits that must be zero.
|
|
||||||
*
|
*
|
||||||
* mark is the offset in bytes from the start of the matching pattern.
|
* The pattern can be constructed or restored from XML of the form,
|
||||||
*
|
* where alignOffset=mark, alignmask=bits
|
||||||
* align 2 = 0x1 - lower bit must be zero
|
|
||||||
* align 4 = 0x3 - lower two bits must be zero
|
|
||||||
* align 8 = 0x7 - lower three bits must be zero
|
|
||||||
* align 16 = 0xF - lower four bits must be zero
|
|
||||||
* ....
|
|
||||||
* Other strange alignments could be specified, but most likely the above suffice.
|
|
||||||
*
|
|
||||||
* The pattern can be constructed or restored from XML of the form:
|
|
||||||
*
|
*
|
||||||
* <align mark="0" bits="1"/>
|
* <align mark="0" bits="1"/>
|
||||||
*
|
*
|
||||||
|
@ -41,27 +32,44 @@ import ghidra.xml.XmlPullParser;
|
||||||
|
|
||||||
public class AlignRule implements PostRule {
|
public class AlignRule implements PostRule {
|
||||||
|
|
||||||
private int mark; // Position, relative to start of pattern, to check alignment at
|
private int alignOffset; // Position, relative to start of pattern, to check alignment at
|
||||||
private int alignmask; // Mask of bits that must be zero
|
private int alignmask; // Mask of bits that must be zero
|
||||||
|
|
||||||
public AlignRule() {
|
public AlignRule() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AlignRule(int mark, int alignmask) {
|
/**
|
||||||
this.mark = mark;
|
* ByteSearch post search rule when a pattern is found. Used when a pattern must have a certain
|
||||||
|
* alignment at an offset from the location the pattern matches. The alignment is
|
||||||
|
* specified by the alignmask bits that must be zero.
|
||||||
|
*
|
||||||
|
* Normally alignOffset is 0, since most patterns will match at the address that must be aligned
|
||||||
|
* To align a match, use the following
|
||||||
|
*
|
||||||
|
* align to 2 = alignmask 0x1 - lower bit must be zero
|
||||||
|
* align to 4 = alignmask 0x3 - lower two bits must be zero
|
||||||
|
* align to 8 = alignmask 0x7 - lower three bits must be zero
|
||||||
|
* align to 16 = alignmask 0xF - lower four bits must be zero
|
||||||
|
* ....
|
||||||
|
* Other strange alignments could be specified, but most likely the above suffice.
|
||||||
|
* @param alignOffset - bytes offset from pattern to check for alignment
|
||||||
|
* @param alignmask - the mask where a 1 bit must be zero
|
||||||
|
*/
|
||||||
|
public AlignRule(int alignOffset, int alignmask) {
|
||||||
|
this.alignOffset = alignOffset;
|
||||||
this.alignmask = alignmask;
|
this.alignmask = alignmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Pattern pat, long matchoffset) {
|
public boolean apply(Pattern pat, long matchoffset) {
|
||||||
int off = (int) matchoffset;
|
int off = (int) matchoffset;
|
||||||
return (((off + mark) & alignmask) == 0);
|
return (((off + alignOffset) & alignmask) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restoreXml(XmlPullParser parser) {
|
public void restoreXml(XmlPullParser parser) {
|
||||||
XmlElement el = parser.start("align");
|
XmlElement el = parser.start("align");
|
||||||
mark = SpecXmlUtils.decodeInt(el.getAttribute("mark"));
|
alignOffset = SpecXmlUtils.decodeInt(el.getAttribute("mark"));
|
||||||
int bits = SpecXmlUtils.decodeInt(el.getAttribute("bits"));
|
int bits = SpecXmlUtils.decodeInt(el.getAttribute("bits"));
|
||||||
alignmask = (1 << bits) - 1;
|
alignmask = (1 << bits) - 1;
|
||||||
parser.end();
|
parser.end();
|
||||||
|
|
|
@ -29,6 +29,13 @@ import ghidra.program.model.data.DataType;
|
||||||
|
|
||||||
public class GenericByteSequencePattern<T> extends Pattern {
|
public class GenericByteSequencePattern<T> extends Pattern {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a sequence of bytes with no mask, and associated action
|
||||||
|
* to be called if this pattern matches.
|
||||||
|
*
|
||||||
|
* @param bytesSequence sequence of bytes to match
|
||||||
|
* @param action action to apply if the match succeeds
|
||||||
|
*/
|
||||||
public GenericByteSequencePattern(byte[] bytesSequence, GenericMatchAction<T> action) {
|
public GenericByteSequencePattern(byte[] bytesSequence, GenericMatchAction<T> action) {
|
||||||
super(new DittedBitSequence(bytesSequence), 0, new PostRule[0], new MatchAction[1]);
|
super(new DittedBitSequence(bytesSequence), 0, new PostRule[0], new MatchAction[1]);
|
||||||
|
|
||||||
|
@ -36,6 +43,14 @@ public class GenericByteSequencePattern<T> extends Pattern {
|
||||||
matchActions[0] = action;
|
matchActions[0] = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a sequence of bytes with a mask, and associated action
|
||||||
|
* to be called if this pattern matches.
|
||||||
|
*
|
||||||
|
* @param bytesSequence sequence of bytes to match
|
||||||
|
* @param mask mask, bits that are 1 must match the byteSequence bits
|
||||||
|
* @param action to apply if the match succeeds
|
||||||
|
*/
|
||||||
public GenericByteSequencePattern(byte[] bytesSequence, byte[] mask,
|
public GenericByteSequencePattern(byte[] bytesSequence, byte[] mask,
|
||||||
GenericMatchAction<DataType> action) {
|
GenericMatchAction<DataType> action) {
|
||||||
super(new DittedBitSequence(bytesSequence, mask), 0, new PostRule[0], new MatchAction[1]);
|
super(new DittedBitSequence(bytesSequence, mask), 0, new PostRule[0], new MatchAction[1]);
|
||||||
|
|
|
@ -26,8 +26,9 @@ package ghidra.util.bytesearch;
|
||||||
public class GenericMatchAction<T> extends DummyMatchAction {
|
public class GenericMatchAction<T> extends DummyMatchAction {
|
||||||
T matchValue;
|
T matchValue;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* construct with an appropriate object that can be used when the action is applied
|
* Construct a match action used when a match occurs for some GenericByteSequece
|
||||||
|
* @param matchValue specialized object used when match occurs
|
||||||
*/
|
*/
|
||||||
public GenericMatchAction(T matchValue) {
|
public GenericMatchAction(T matchValue) {
|
||||||
this.matchValue = matchValue;
|
this.matchValue = matchValue;
|
||||||
|
|
|
@ -24,9 +24,15 @@ package ghidra.util.bytesearch;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class Match {
|
public class Match {
|
||||||
private DittedBitSequence sequence; // Pattern that matches
|
private DittedBitSequence sequence; // Pattern that matched
|
||||||
private long offset; // starting offset within bytestream of match
|
private long offset; // Offset within bytestream where the match occurred
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a Match of a DittedBitSequence at an offset within a byte stream.
|
||||||
|
* Object normally used when a match occurs during a MemoryBytePatternSearch.
|
||||||
|
* @param sequence that matched
|
||||||
|
* @param offset from the start of byte stream where the matched occured
|
||||||
|
*/
|
||||||
public Match(DittedBitSequence sequence, long offset) {
|
public Match(DittedBitSequence sequence, long offset) {
|
||||||
this.sequence = sequence;
|
this.sequence = sequence;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
|
|
|
@ -23,7 +23,19 @@ import ghidra.xml.XmlPullParser;
|
||||||
* Interface for a match action to be taken for the Program@Address for a ditted bit seqence pattern
|
* Interface for a match action to be taken for the Program@Address for a ditted bit seqence pattern
|
||||||
*/
|
*/
|
||||||
public interface MatchAction {
|
public interface MatchAction {
|
||||||
|
/**
|
||||||
|
* Apply the match action to the program at the address.
|
||||||
|
*
|
||||||
|
* @param program program in which the match occurred
|
||||||
|
* @param addr where the match occured
|
||||||
|
* @param match information about the match that occurred
|
||||||
|
*/
|
||||||
public void apply(Program program, Address addr, Match match);
|
public void apply(Program program, Address addr, Match match);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action can be constructed from XML
|
||||||
|
*
|
||||||
|
* @param parser XML pull parser to restore action from XML
|
||||||
|
*/
|
||||||
public void restoreXml(XmlPullParser parser);
|
public void restoreXml(XmlPullParser parser);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Multi pattern/mask/action memory searcher
|
* Multi pattern/mask/action memory searcher
|
||||||
|
* Patterns must be supplied/added, or a pre-initialized searchState supplied
|
||||||
*
|
*
|
||||||
* Preload search patterns and actions, then call search method.
|
* Preload search patterns and actions, then call search method.
|
||||||
*/
|
*/
|
||||||
|
@ -35,13 +36,20 @@ import ghidra.util.task.TaskMonitor;
|
||||||
public class MemoryBytePatternSearcher {
|
public class MemoryBytePatternSearcher {
|
||||||
private static final long RESTRICTED_PATTERN_BYTE_RANGE = 32;
|
private static final long RESTRICTED_PATTERN_BYTE_RANGE = 32;
|
||||||
|
|
||||||
|
SequenceSearchState root = null;
|
||||||
|
|
||||||
ArrayList<Pattern> patternList;
|
ArrayList<Pattern> patternList;
|
||||||
|
|
||||||
private String searchName = "";
|
private String searchName = "";
|
||||||
|
|
||||||
|
private boolean doExecutableBlocksOnly = false; // only search executable blocks
|
||||||
|
|
||||||
|
private long numToSearch = 0;
|
||||||
|
private long numSearched = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create with pre-created patternList
|
* Create with pre-created patternList
|
||||||
*
|
* @param searchName name of search
|
||||||
* @param patternList - list of patterns(bytes/mask/action)
|
* @param patternList - list of patterns(bytes/mask/action)
|
||||||
*/
|
*/
|
||||||
public MemoryBytePatternSearcher(String searchName, ArrayList<Pattern> patternList) {
|
public MemoryBytePatternSearcher(String searchName, ArrayList<Pattern> patternList) {
|
||||||
|
@ -49,8 +57,19 @@ public class MemoryBytePatternSearcher {
|
||||||
this.patternList = new ArrayList<Pattern>(patternList);
|
this.patternList = new ArrayList<Pattern>(patternList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create with an initialized SequenceSearchState
|
||||||
|
* @param searchName name of search
|
||||||
|
* @param root search state pre-initialized
|
||||||
|
*/
|
||||||
|
public MemoryBytePatternSearcher(String searchName, SequenceSearchState root) {
|
||||||
|
this.searchName = searchName;
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create with no patternList, must add patterns before searching
|
* Create with no patternList, must add patterns before searching
|
||||||
|
* @param searchName name of search
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public MemoryBytePatternSearcher(String searchName) {
|
public MemoryBytePatternSearcher(String searchName) {
|
||||||
|
@ -66,6 +85,10 @@ public class MemoryBytePatternSearcher {
|
||||||
patternList.add(pattern);
|
patternList.add(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSearchExecutableOnly(boolean doExecutableBlocksOnly) {
|
||||||
|
this.doExecutableBlocksOnly = doExecutableBlocksOnly;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search initialized memory blocks for all patterns(bytes/mask/action).
|
* Search initialized memory blocks for all patterns(bytes/mask/action).
|
||||||
* Call associated action for each pattern matched.
|
* Call associated action for each pattern matched.
|
||||||
|
@ -78,23 +101,59 @@ public class MemoryBytePatternSearcher {
|
||||||
*/
|
*/
|
||||||
public void search(Program program, AddressSetView searchSet, TaskMonitor monitor)
|
public void search(Program program, AddressSetView searchSet, TaskMonitor monitor)
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
SequenceSearchState root = SequenceSearchState.buildStateMachine(patternList);
|
if (root == null) {
|
||||||
|
root = SequenceSearchState.buildStateMachine(patternList);
|
||||||
|
}
|
||||||
|
|
||||||
|
numToSearch = getNumToSearch(program, searchSet);
|
||||||
|
monitor.setMessage(searchName + " Search");
|
||||||
|
monitor.initialize(numToSearch);
|
||||||
|
|
||||||
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
||||||
for (MemoryBlock block2 : blocks) {
|
for (MemoryBlock block : blocks) {
|
||||||
MemoryBlock block = block2;
|
monitor.setProgress(numSearched);
|
||||||
if (!searchSet.intersects(block.getStart(), block.getEnd())) {
|
// check if entire block has anything that is searchable
|
||||||
|
if (!block.isInitialized()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (doExecutableBlocksOnly && !block.isExecute()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (searchSet != null && !searchSet.isEmpty() &&
|
||||||
|
!searchSet.intersects(block.getStart(), block.getEnd())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
searchBlock(root, program, block, searchSet, monitor);
|
searchBlock(root, program, block, searchSet, monitor);
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
Msg.error(this, "Unable to scan block " + block.getName() + " for patterns");
|
Msg.error(this, "Unable to scan block " + block.getName() + " for " + searchName);
|
||||||
}
|
}
|
||||||
|
numSearched += block.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getNumToSearch(Program program, AddressSetView searchSet) {
|
||||||
|
long numAddresses = 0;
|
||||||
|
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
||||||
|
for (MemoryBlock block : blocks) {
|
||||||
|
// check if entire block has anything that is searchable
|
||||||
|
if (!block.isInitialized()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (doExecutableBlocksOnly && !block.isExecute()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (searchSet != null && !searchSet.isEmpty() &&
|
||||||
|
!searchSet.intersects(block.getStart(), block.getEnd())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
numAddresses += block.getSize();
|
||||||
|
}
|
||||||
|
return numAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search through bytes of a memory block using the finite state machine -root-
|
* Search through bytes of a memory block using the finite state machine -root-
|
||||||
* Apply any additional rules for matching patterns.
|
* Apply any additional rules for matching patterns.
|
||||||
|
@ -110,23 +169,29 @@ public class MemoryBytePatternSearcher {
|
||||||
throws IOException, CancelledException {
|
throws IOException, CancelledException {
|
||||||
|
|
||||||
// if no restricted set, make restrict set the full block
|
// if no restricted set, make restrict set the full block
|
||||||
AddressSet doneSet = new AddressSet(restrictSet);
|
AddressSet doneSet;
|
||||||
if (doneSet.isEmpty()) {
|
if (restrictSet == null || restrictSet.isEmpty()) {
|
||||||
doneSet.addRange(block.getStart(), block.getEnd());
|
doneSet = new AddressSet(block.getStart(), block.getEnd());
|
||||||
}
|
}
|
||||||
doneSet = doneSet.intersectRange(block.getStart(), block.getEnd());
|
else {
|
||||||
|
doneSet = restrictSet.intersectRange(block.getStart(), block.getEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
long numInDoneSet = doneSet.getNumAddresses();
|
||||||
|
long numInBlock = block.getSize();
|
||||||
|
|
||||||
Address blockStartAddr = block.getStart();
|
Address blockStartAddr = block.getStart();
|
||||||
|
|
||||||
// pull each range off the restricted set
|
// pull each range off the restricted set
|
||||||
|
long progress = monitor.getProgress();
|
||||||
AddressRangeIterator addressRanges = doneSet.getAddressRanges();
|
AddressRangeIterator addressRanges = doneSet.getAddressRanges();
|
||||||
|
long numDone = 0;
|
||||||
while (addressRanges.hasNext()) {
|
while (addressRanges.hasNext()) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
AddressRange addressRange = addressRanges.next();
|
|
||||||
|
|
||||||
monitor.setMessage(searchName + " Search");
|
monitor.setMessage(searchName + " Search");
|
||||||
monitor.initialize(doneSet.getNumAddresses());
|
monitor.setProgress(progress + (long) (numInBlock * ((float) numDone / numInDoneSet)));
|
||||||
monitor.setProgress(0);
|
AddressRange addressRange = addressRanges.next();
|
||||||
|
long numAddressesInRange = addressRange.getLength();
|
||||||
|
|
||||||
ArrayList<Match> mymatches = new ArrayList<>();
|
ArrayList<Match> mymatches = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -155,25 +220,49 @@ public class MemoryBytePatternSearcher {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
monitor.setMessage(searchName + " (Examine Matches)");
|
monitor.setMessage(searchName + " (Examine Matches)");
|
||||||
monitor.initialize(mymatches.size());
|
|
||||||
monitor.setProgress(0);
|
|
||||||
|
|
||||||
// TODO: DANGER there is much offset<-->address calculation here
|
// TODO: DANGER there is much offset<-->address calculation here
|
||||||
// should be OK, since they are all relative to the block.
|
// should be OK, since they are all relative to the block.
|
||||||
|
long matchProgress = progress + (long) (numInBlock * ((float) numDone / numInDoneSet));
|
||||||
for (int i = 0; i < mymatches.size(); ++i) {
|
for (int i = 0; i < mymatches.size(); ++i) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
monitor.setProgress(i);
|
monitor.setProgress(
|
||||||
|
matchProgress + (long) (numAddressesInRange * ((float) i / mymatches.size())));
|
||||||
Match match = mymatches.get(i);
|
Match match = mymatches.get(i);
|
||||||
Address addr = blockStartAddr.add(match.getMarkOffset() + blockOffset);
|
Address addr = blockStartAddr.add(match.getMarkOffset() + blockOffset);
|
||||||
if (!match.checkPostRules(streamoffset + blockOffset)) {
|
if (!match.checkPostRules(streamoffset + blockOffset)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
MatchAction[] matchactions = match.getMatchActions();
|
|
||||||
|
|
||||||
|
MatchAction[] matchactions = match.getMatchActions();
|
||||||
|
preMatchApply(matchactions, addr);
|
||||||
for (MatchAction matchaction : matchactions) {
|
for (MatchAction matchaction : matchactions) {
|
||||||
matchaction.apply(program, addr, match);
|
matchaction.apply(program, addr, match);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postMatchApply(matchactions, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
numDone += numAddressesInRange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before any match rules are applied
|
||||||
|
* @param matchactions actions that matched
|
||||||
|
* @param addr address of match
|
||||||
|
*/
|
||||||
|
public void preMatchApply(MatchAction[] matchactions, Address addr) {
|
||||||
|
// override if any initialization needs to be done before rule application
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after any match rules are applied
|
||||||
|
* Can use for cross post rule matching state application and cleanup.
|
||||||
|
* @param matchactions actions that matched
|
||||||
|
* @param addr adress of match
|
||||||
|
*/
|
||||||
|
public void postMatchApply(MatchAction[] matchactions, Address addr) {
|
||||||
|
// override if any cleanup from rule match application is needed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,9 @@ public class Pattern extends DittedBitSequence {
|
||||||
private PostRule[] postrule;
|
private PostRule[] postrule;
|
||||||
private MatchAction[] actions;
|
private MatchAction[] actions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an empty pattern. Use XML to initialize
|
||||||
|
*/
|
||||||
public Pattern() {
|
public Pattern() {
|
||||||
markOffset = 0;
|
markOffset = 0;
|
||||||
postrule = null;
|
postrule = null;
|
||||||
|
@ -44,6 +47,15 @@ public class Pattern extends DittedBitSequence {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the pattern based on a DittedByteSequence a match offset, post matching rules,
|
||||||
|
* and a set of actions to take when the match occurs.
|
||||||
|
*
|
||||||
|
* @param seq DittedByteSequence
|
||||||
|
* @param offset offset from the actual match location to report a match
|
||||||
|
* @param postArray post set of rules to check for the match
|
||||||
|
* @param matchArray MatchActions to apply when a match occurs
|
||||||
|
*/
|
||||||
public Pattern(DittedBitSequence seq, int offset, PostRule[] postArray,
|
public Pattern(DittedBitSequence seq, int offset, PostRule[] postArray,
|
||||||
MatchAction[] matchArray) {
|
MatchAction[] matchArray) {
|
||||||
super(seq);
|
super(seq);
|
||||||
|
|
|
@ -19,7 +19,18 @@ package ghidra.util.bytesearch;
|
||||||
* Interface for factories that create Match Pattern classes
|
* Interface for factories that create Match Pattern classes
|
||||||
*/
|
*/
|
||||||
public interface PatternFactory {
|
public interface PatternFactory {
|
||||||
|
/**
|
||||||
|
* Get a named match action
|
||||||
|
*
|
||||||
|
* @param nm name of action to find
|
||||||
|
* @return match action with the given name, null otherwise
|
||||||
|
*/
|
||||||
public MatchAction getMatchActionByName(String nm);
|
public MatchAction getMatchActionByName(String nm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a named post match rule by name
|
||||||
|
* @param nm name of the post rule
|
||||||
|
* @return the post rule with the name, null otherwise
|
||||||
|
*/
|
||||||
public PostRule getPostRuleByName(String nm);
|
public PostRule getPostRuleByName(String nm);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,9 @@ public class PatternPairSet {
|
||||||
private ArrayList<DittedBitSequence> preSequences;
|
private ArrayList<DittedBitSequence> preSequences;
|
||||||
private ArrayList<Pattern> postPatterns;
|
private ArrayList<Pattern> postPatterns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an empty PatternPairSet. Use XML to initialize the pattern sets.
|
||||||
|
*/
|
||||||
public PatternPairSet() {
|
public PatternPairSet() {
|
||||||
preSequences = new ArrayList<DittedBitSequence>();
|
preSequences = new ArrayList<DittedBitSequence>();
|
||||||
postPatterns = new ArrayList<Pattern>();
|
postPatterns = new ArrayList<Pattern>();
|
||||||
|
@ -84,12 +87,22 @@ public class PatternPairSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add this PatternPairSets post patterns to an existing arraylist of patterns.
|
||||||
|
* @param postpats array to add this PatternPairSets post patterns into
|
||||||
|
*/
|
||||||
public void extractPostPatterns(ArrayList<Pattern> postpats) {
|
public void extractPostPatterns(ArrayList<Pattern> postpats) {
|
||||||
for (int i = 0; i < postPatterns.size(); ++i) {
|
for (int i = 0; i < postPatterns.size(); ++i) {
|
||||||
postpats.add(postPatterns.get(i));
|
postpats.add(postPatterns.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore PatternPairSet from XML pull parser
|
||||||
|
* @param parser XML pull parser
|
||||||
|
* @param pfactory pattern factory user to construct patterns
|
||||||
|
* @throws IOException if pull parsing fails
|
||||||
|
*/
|
||||||
public void restoreXml(XmlPullParser parser, PatternFactory pfactory) throws IOException {
|
public void restoreXml(XmlPullParser parser, PatternFactory pfactory) throws IOException {
|
||||||
XmlElement el = parser.start("patternpairs");
|
XmlElement el = parser.start("patternpairs");
|
||||||
totalBitsOfCheck = SpecXmlUtils.decodeInt(el.getAttribute("totalbits"));
|
totalBitsOfCheck = SpecXmlUtils.decodeInt(el.getAttribute("totalbits"));
|
||||||
|
|
|
@ -21,7 +21,18 @@ import ghidra.xml.XmlPullParser;
|
||||||
* Inteface for post match rules that are checked after a match is idenfied
|
* Inteface for post match rules that are checked after a match is idenfied
|
||||||
*/
|
*/
|
||||||
public interface PostRule {
|
public interface PostRule {
|
||||||
|
/**
|
||||||
|
* Apply a post rule given the matching pattern and offset into the byte stream.
|
||||||
|
* @param pat pattern that matched
|
||||||
|
* @param matchoffset offset of the match
|
||||||
|
* @return true if the PostRule is satisfied
|
||||||
|
*/
|
||||||
public boolean apply(Pattern pat, long matchoffset);
|
public boolean apply(Pattern pat, long matchoffset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can restore state of instance PostRule from XML
|
||||||
|
*
|
||||||
|
* @param parser XML pull parser
|
||||||
|
*/
|
||||||
public void restoreXml(XmlPullParser parser);
|
public void restoreXml(XmlPullParser parser);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,19 +27,27 @@ import ghidra.util.task.TaskMonitor;
|
||||||
*/
|
*/
|
||||||
public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
|
|
||||||
private static final int PATERN_ENDED = 10000000;
|
private static final int PATTERN_ENDED = Integer.MAX_VALUE;
|
||||||
private SequenceSearchState parent;
|
private SequenceSearchState parent;
|
||||||
private ArrayList<DittedBitSequence> possible; // Patterns that could still match in this state
|
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 ArrayList<DittedBitSequence> success; // Patterns that have matched successfully if we reached this state
|
||||||
private SequenceSearchState[] trans; // State transitions based on next byte
|
private SequenceSearchState[] trans; // State transitions based on next byte
|
||||||
|
|
||||||
public SequenceSearchState(SequenceSearchState par) {
|
/**
|
||||||
parent = par;
|
* Construct a sub sequence state with a parent sequence
|
||||||
|
*
|
||||||
|
* @param parent parent SequenceSearchState
|
||||||
|
*/
|
||||||
|
public SequenceSearchState(SequenceSearchState parent) {
|
||||||
|
this.parent = parent;
|
||||||
possible = new ArrayList<DittedBitSequence>();
|
possible = new ArrayList<DittedBitSequence>();
|
||||||
success = null;
|
success = null;
|
||||||
trans = null;
|
trans = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return maximum number of bytes that could be matched by this sequence
|
||||||
|
*/
|
||||||
public int getMaxSequenceSize() {
|
public int getMaxSequenceSize() {
|
||||||
int max = 0;
|
int max = 0;
|
||||||
for (DittedBitSequence element : possible) {
|
for (DittedBitSequence element : possible) {
|
||||||
|
@ -51,6 +59,12 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a pattern to this search sequence. The last pattern added is the successful
|
||||||
|
* match pattern.
|
||||||
|
* @param pat pattern to add
|
||||||
|
* @param pos position within the current set of patterns to add this pattern
|
||||||
|
*/
|
||||||
public void addSequence(DittedBitSequence pat, int pos) {
|
public void addSequence(DittedBitSequence pat, int pos) {
|
||||||
possible.add(pat);
|
possible.add(pat);
|
||||||
if (pos == pat.getSize()) {
|
if (pos == pat.getSize()) {
|
||||||
|
@ -61,6 +75,9 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the sequences that have been added
|
||||||
|
*/
|
||||||
public void sortSequences() {
|
public void sortSequences() {
|
||||||
Comparator<DittedBitSequence> comp = new Comparator<DittedBitSequence>() {
|
Comparator<DittedBitSequence> comp = new Comparator<DittedBitSequence>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -150,9 +167,9 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
j += 1;
|
j += 1;
|
||||||
thispat = (i == success.size()) ? PATERN_ENDED : success.get(i).getIndex();
|
thispat = (i == success.size()) ? PATTERN_ENDED : success.get(i).getIndex();
|
||||||
oppat =
|
oppat =
|
||||||
(j == op.success.size()) ? PATERN_ENDED : op.success.get(j).getIndex();
|
(j == op.success.size()) ? PATTERN_ENDED : op.success.get(j).getIndex();
|
||||||
}
|
}
|
||||||
else if (thispat < oppat) {
|
else if (thispat < oppat) {
|
||||||
if (curpat != thispat) {
|
if (curpat != thispat) {
|
||||||
|
@ -160,7 +177,7 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
curpat = thispat;
|
curpat = thispat;
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
thispat = (i == success.size()) ? PATERN_ENDED : success.get(i).getIndex();
|
thispat = (i == success.size()) ? PATTERN_ENDED : success.get(i).getIndex();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (curpat != oppat) {
|
if (curpat != oppat) {
|
||||||
|
@ -169,7 +186,7 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
}
|
}
|
||||||
j += 1;
|
j += 1;
|
||||||
oppat =
|
oppat =
|
||||||
(j == op.success.size()) ? PATERN_ENDED : op.success.get(j).getIndex();
|
(j == op.success.size()) ? PATTERN_ENDED : op.success.get(j).getIndex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
success = tmp;
|
success = tmp;
|
||||||
|
@ -177,6 +194,12 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to match this Sequence to the byteArray, and add any matches to the match list
|
||||||
|
* @param bytearray array of bytes to match
|
||||||
|
* @param numbytes retrict number of bytes to allow to match
|
||||||
|
* @param match list of matches, the result
|
||||||
|
*/
|
||||||
public void sequenceMatch(byte[] bytearray, int numbytes, ArrayList<Match> match) {
|
public void sequenceMatch(byte[] bytearray, int numbytes, ArrayList<Match> match) {
|
||||||
int subindex = 0;
|
int subindex = 0;
|
||||||
SequenceSearchState curstate = this;
|
SequenceSearchState curstate = this;
|
||||||
|
@ -242,6 +265,8 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
*/
|
*/
|
||||||
public void apply(InputStream in, long maxBytes, ArrayList<Match> match, TaskMonitor monitor)
|
public void apply(InputStream in, long maxBytes, ArrayList<Match> match, TaskMonitor monitor)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
long progress = monitor.getProgress();
|
||||||
|
|
||||||
int maxSize = getMaxSequenceSize() + 1;
|
int maxSize = getMaxSequenceSize() + 1;
|
||||||
if (maxSize < 4096) {
|
if (maxSize < 4096) {
|
||||||
maxSize = 4096;
|
maxSize = 4096;
|
||||||
|
@ -317,7 +342,7 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
monitor.setProgress(offset);
|
monitor.setProgress(progress + offset);
|
||||||
}
|
}
|
||||||
if (ra != secondBuf.length) {
|
if (ra != secondBuf.length) {
|
||||||
fullBuffers = 1;
|
fullBuffers = 1;
|
||||||
|
@ -373,8 +398,15 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public ArrayList<SequenceSearchState> buildTransitionLevel(
|
/**
|
||||||
ArrayList<SequenceSearchState> prev, int pos) {
|
* Build a new transition level for the state machine
|
||||||
|
*
|
||||||
|
* @param prev previous search sequences
|
||||||
|
* @param pos position within the search sequence state for this level
|
||||||
|
* @return list of possible new search states to be added to the state machine
|
||||||
|
*/
|
||||||
|
static ArrayList<SequenceSearchState> buildTransitionLevel(ArrayList<SequenceSearchState> prev,
|
||||||
|
int pos) {
|
||||||
ArrayList<SequenceSearchState> res = new ArrayList<SequenceSearchState>();
|
ArrayList<SequenceSearchState> res = new ArrayList<SequenceSearchState>();
|
||||||
Iterator<SequenceSearchState> iterator = prev.iterator();
|
Iterator<SequenceSearchState> iterator = prev.iterator();
|
||||||
while (iterator.hasNext()) { // For each current state
|
while (iterator.hasNext()) { // For each current state
|
||||||
|
@ -407,6 +439,11 @@ public class SequenceSearchState implements Comparable<SequenceSearchState> {
|
||||||
return finalres;
|
return finalres;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a search state machine from a list of DittedBitSequences
|
||||||
|
* @param patterns bit sequence patterns
|
||||||
|
* @return search state the will match the given sequences
|
||||||
|
*/
|
||||||
static public SequenceSearchState buildStateMachine(
|
static public SequenceSearchState buildStateMachine(
|
||||||
ArrayList<? extends DittedBitSequence> patterns) {
|
ArrayList<? extends DittedBitSequence> patterns) {
|
||||||
SequenceSearchState root = new SequenceSearchState(null);
|
SequenceSearchState root = new SequenceSearchState(null);
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.analyzers;
|
package ghidra.app.analyzers;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -51,7 +49,6 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
||||||
private static final String DESCRIPTION =
|
private static final String DESCRIPTION =
|
||||||
"Search for architecture specific byte patterns: typically starts of functions";
|
"Search for architecture specific byte patterns: typically starts of functions";
|
||||||
private static final String PRE_FUNCTION_MATCH_PROPERTY_NAME = "PreFunctionMatch";
|
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 final static String OPTION_NAME_DATABLOCKS = "Search Data Blocks";
|
||||||
private static final String OPTION_DESCRIPTION_DATABLOCKS =
|
private static final String OPTION_DESCRIPTION_DATABLOCKS =
|
||||||
"Search for byte patterns in blocks that are not executable";
|
"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);
|
applyActionToSet(program, addr, funcResult, match);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected boolean checkPreRequisites(Program program, Address addr) {
|
protected boolean checkPreRequisites(Program program, Address addr) {
|
||||||
/**
|
/**
|
||||||
* If the match's mark point occurs in undefined data, schedule disassembly
|
* 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 false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void applyActionToSet(Program program, Address addr, AddressSet resultSet,
|
protected void applyActionToSet(Program program, Address addr, AddressSet resultSet,
|
||||||
Match match) {
|
Match match) {
|
||||||
|
@ -632,25 +627,34 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
||||||
return false;
|
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
|
// clear out any previous potential matches, because we are re-looking at these places
|
||||||
// this will keep cruft from accumulating in the property map.
|
// this will keep cruft from accumulating in the property map.
|
||||||
getOrCreatePotentialMatchPropertyMap(program).remove(restrictedSet);
|
getOrCreatePotentialMatchPropertyMap(program).remove(set);
|
||||||
|
|
||||||
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
MemoryBytePatternSearcher patternSearcher;
|
||||||
for (MemoryBlock block2 : blocks) {
|
patternSearcher = new MemoryBytePatternSearcher("Function Starts", root) {
|
||||||
MemoryBlock block = block2;
|
|
||||||
if (!restrictedSet.intersects(block.getStart(), block.getEnd())) {
|
@Override
|
||||||
continue;
|
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);
|
AutoAnalysisManager analysisManager = AutoAnalysisManager.getAnalysisManager(program);
|
||||||
if (!disassemResult.isEmpty()) {
|
if (!disassemResult.isEmpty()) {
|
||||||
analysisManager.disassemble(disassemResult);
|
analysisManager.disassemble(disassemResult);
|
||||||
|
@ -708,26 +712,39 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get rid of any blocks from the address set that shouldn't be searched.
|
* @return true - if there are any blocks marked executable
|
||||||
*
|
|
||||||
* @param program
|
|
||||||
* @param bset
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private AddressSet removeNotSearchedAddresses(Program program, AddressSetView bset) {
|
private boolean checkForExecuteBlock(Program program) {
|
||||||
AddressSet restrictedSet = new AddressSet(bset);
|
|
||||||
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
||||||
boolean hasExecutable = false;
|
boolean hasExecutable = false;
|
||||||
|
|
||||||
for (MemoryBlock block : blocks) {
|
for (MemoryBlock block : blocks) {
|
||||||
if (block.isExecute()) {
|
if (block.isExecute()) {
|
||||||
hasExecutable = true;
|
hasExecutable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return hasExecutable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get rid of any blocks from the address set that shouldn't be searched.
|
||||||
|
* Non-executable and non-initialized.
|
||||||
|
*
|
||||||
|
* @param program program
|
||||||
|
* @param bset current set of restricted address ranges
|
||||||
|
* @return return new set with blocks not to be searched removed
|
||||||
|
*/
|
||||||
|
private AddressSet removeNotSearchedAddresses(Program program, AddressSetView bset) {
|
||||||
|
AddressSet restrictedSet = new AddressSet(bset);
|
||||||
|
MemoryBlock[] blocks = program.getMemory().getBlocks();
|
||||||
|
boolean hasExecutable = checkForExecuteBlock(program);
|
||||||
|
|
||||||
for (MemoryBlock block : blocks) {
|
for (MemoryBlock block : blocks) {
|
||||||
if (!block.isInitialized()) {
|
if (!block.isInitialized()) {
|
||||||
restrictedSet.deleteRange(block.getStart(), block.getEnd());
|
restrictedSet.deleteRange(block.getStart(), block.getEnd());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// if
|
||||||
if (executableBlocksOnly && hasExecutable) {
|
if (executableBlocksOnly && hasExecutable) {
|
||||||
if (!block.isExecute()) {
|
if (!block.isExecute()) {
|
||||||
restrictedSet.deleteRange(block.getStart(), block.getEnd());
|
restrictedSet.deleteRange(block.getStart(), block.getEnd());
|
||||||
|
@ -816,94 +833,6 @@ public class FunctionStartAnalyzer extends AbstractAnalyzer implements PatternFa
|
||||||
return patlist;
|
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
|
@Override
|
||||||
public MatchAction getMatchActionByName(String nm) {
|
public MatchAction getMatchActionByName(String nm) {
|
||||||
if (nm.equals("funcstart")) {
|
if (nm.equals("funcstart")) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ public abstract class AbstractCreateDataBackgroundCmd<T extends AbstractCreateDa
|
||||||
extends BackgroundCommand {
|
extends BackgroundCommand {
|
||||||
|
|
||||||
protected final String name;
|
protected final String name;
|
||||||
protected final Address address;
|
private Address address;
|
||||||
protected final int count;
|
protected final int count;
|
||||||
protected final DataValidationOptions validationOptions;
|
protected final DataValidationOptions validationOptions;
|
||||||
protected final DataApplyOptions applyOptions;
|
protected final DataApplyOptions applyOptions;
|
||||||
|
@ -178,7 +178,7 @@ public abstract class AbstractCreateDataBackgroundCmd<T extends AbstractCreateDa
|
||||||
catch (InvalidInputException e) {
|
catch (InvalidInputException e) {
|
||||||
// Catch the exception and output the error, but still should create
|
// Catch the exception and output the error, but still should create
|
||||||
// associated data if possible, even though markup failed.
|
// associated data if possible, even though markup failed.
|
||||||
handleErrorMessage(program, name, getDataAddress(), getDataAddress(), e);
|
handleErrorMessage(program, name, address, address, e);
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ public abstract class AbstractCreateDataBackgroundCmd<T extends AbstractCreateDa
|
||||||
}
|
}
|
||||||
catch (AddressOutOfBoundsException | CodeUnitInsertionException | DataTypeConflictException
|
catch (AddressOutOfBoundsException | CodeUnitInsertionException | DataTypeConflictException
|
||||||
| InvalidDataTypeException e) {
|
| InvalidDataTypeException e) {
|
||||||
handleErrorMessage(program, name, getDataAddress(), getDataAddress(), e);
|
handleErrorMessage(program, name, address, address, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,8 +212,8 @@ public abstract class AbstractCreateDataBackgroundCmd<T extends AbstractCreateDa
|
||||||
throw new CodeUnitInsertionException(
|
throw new CodeUnitInsertionException(
|
||||||
"Unable to get data type from model, " + model.getName() + ".");
|
"Unable to get data type from model, " + model.getName() + ".");
|
||||||
}
|
}
|
||||||
if (!memory.getLoadedAndInitializedAddressSet().contains(getDataAddress())) {
|
if (!memory.getLoadedAndInitializedAddressSet().contains(address)) {
|
||||||
String message = "Can't create an " + dt.getName() + " @ " + getDataAddress() +
|
String message = "Can't create an " + dt.getName() + " @ " + address +
|
||||||
" which isn't in loaded and initialized memory for " + program.getName();
|
" which isn't in loaded and initialized memory for " + program.getName();
|
||||||
throw new CodeUnitInsertionException(message);
|
throw new CodeUnitInsertionException(message);
|
||||||
}
|
}
|
||||||
|
@ -229,15 +229,14 @@ public abstract class AbstractCreateDataBackgroundCmd<T extends AbstractCreateDa
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
// Is the data type already applied at the address?
|
// Is the data type already applied at the address?
|
||||||
if (matchingDataExists(dt, program, getDataAddress())) {
|
if (matchingDataExists(dt, program, address)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
// Create data at the address using the datatype.
|
// Create data at the address using the datatype.
|
||||||
DataUtilities.createData(program, getDataAddress(), dt, dt.getLength(), false,
|
DataUtilities.createData(program, address, dt, dt.getLength(), false, getClearDataMode());
|
||||||
getClearDataMode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,7 +361,17 @@ public abstract class AbstractCreateDataBackgroundCmd<T extends AbstractCreateDa
|
||||||
*
|
*
|
||||||
* @return the address of the data item being created.
|
* @return the address of the data item being created.
|
||||||
*/
|
*/
|
||||||
protected Address getDataAddress() {
|
final protected Address getDataAddress() {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the address of the data item to be applied.
|
||||||
|
* Can be used for sub classes that need to apply multiple data items.
|
||||||
|
*
|
||||||
|
* @param addr set the current data address
|
||||||
|
*/
|
||||||
|
final protected void setDataAddress(Address addr) {
|
||||||
|
address = addr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,6 @@ public class CreateRtti4BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rt
|
||||||
private List<MemoryBlock> vfTableBlocks;
|
private List<MemoryBlock> vfTableBlocks;
|
||||||
private List<Address> rtti4Locations;
|
private List<Address> rtti4Locations;
|
||||||
|
|
||||||
private Address currentProcessingAddress;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a command for applying an RTTI4 dataType at an address.
|
* Constructs a command for applying an RTTI4 dataType at an address.
|
||||||
* @param address the address where the data should be created using the data type.
|
* @param address the address where the data should be created using the data type.
|
||||||
|
@ -79,14 +77,14 @@ public class CreateRtti4BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rt
|
||||||
List<Address> goodRtti4Locations = new ArrayList<Address>();
|
List<Address> goodRtti4Locations = new ArrayList<Address>();
|
||||||
boolean succeeded = false;
|
boolean succeeded = false;
|
||||||
for (Address addr : rtti4Locations) {
|
for (Address addr : rtti4Locations) {
|
||||||
currentProcessingAddress = addr;
|
setDataAddress(addr);
|
||||||
succeeded |= super.doApplyTo(program, taskMonitor);
|
succeeded |= super.doApplyTo(program, taskMonitor);
|
||||||
goodRtti4Locations.add(addr);
|
goodRtti4Locations.add(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if any succeeded and should create associated data, make the vftables all at one time
|
// if any succeeded and should create associated data, make the vftables all at one time
|
||||||
if (succeeded && applyOptions.shouldFollowData()) {
|
if (succeeded && applyOptions.shouldFollowData()) {
|
||||||
createaAssociatedVfTables(program, goodRtti4Locations, taskMonitor);
|
createAssociatedVfTables(program, goodRtti4Locations, taskMonitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return succeeded;
|
return succeeded;
|
||||||
|
@ -147,7 +145,7 @@ public class CreateRtti4BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rt
|
||||||
return cmd.applyTo(model.getProgram(), monitor);
|
return cmd.applyTo(model.getProgram(), monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean createaAssociatedVfTables(Program program, List<Address> goodRtti4Locations,
|
private boolean createAssociatedVfTables(Program program, List<Address> goodRtti4Locations,
|
||||||
TaskMonitor taskMonitor) throws CancelledException {
|
TaskMonitor taskMonitor) throws CancelledException {
|
||||||
|
|
||||||
MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("RTTI4 Vftables");
|
MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("RTTI4 Vftables");
|
||||||
|
@ -159,10 +157,6 @@ public class CreateRtti4BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rt
|
||||||
byte[] bytes = ProgramMemoryUtil.getDirectAddressBytes(program, rtti4Address);
|
byte[] bytes = ProgramMemoryUtil.getDirectAddressBytes(program, rtti4Address);
|
||||||
|
|
||||||
addByteSearchPattern(searcher, foundVFtables, rtti4Address, bytes);
|
addByteSearchPattern(searcher, foundVFtables, rtti4Address, bytes);
|
||||||
|
|
||||||
bytes = ProgramMemoryUtil.getShiftedDirectAddressBytes(program, rtti4Address);
|
|
||||||
|
|
||||||
addByteSearchPattern(searcher, foundVFtables, rtti4Address, bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AddressSet searchSet = new AddressSet();
|
AddressSet searchSet = new AddressSet();
|
||||||
|
@ -256,10 +250,9 @@ public class CreateRtti4BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rt
|
||||||
|
|
||||||
// Plate Comment
|
// Plate Comment
|
||||||
// Plate Comment
|
// Plate Comment
|
||||||
EHDataTypeUtilities.createPlateCommentIfNeeded(program,
|
EHDataTypeUtilities.createPlateCommentIfNeeded(program, RttiUtil.CONST_PREFIX +
|
||||||
RttiUtil.CONST_PREFIX + RttiUtil.getDescriptorTypeNamespace(rtti0Model) +
|
RttiUtil.getDescriptorTypeNamespace(rtti0Model) + Namespace.DELIMITER, RTTI_4_NAME,
|
||||||
Namespace.DELIMITER,
|
null, getDataAddress(), applyOptions);
|
||||||
RTTI_4_NAME, null, getDataAddress(), applyOptions);
|
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
|
@ -271,15 +264,4 @@ public class CreateRtti4BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rt
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creating RTTI4 tables handles creating multiple tables at once.
|
|
||||||
* The generic base class that handles getting the address will get the RTTI4 tabl address.
|
|
||||||
*
|
|
||||||
* @return the current address to process
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Address getDataAddress() {
|
|
||||||
return currentProcessingAddress;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,12 +240,6 @@ public class RttiAnalyzer extends AbstractAnalyzer {
|
||||||
// 32-bit could have direct address in memory
|
// 32-bit could have direct address in memory
|
||||||
bytes = ProgramMemoryUtil.getDirectAddressBytes(program, rtti0Address);
|
bytes = ProgramMemoryUtil.getDirectAddressBytes(program, rtti0Address);
|
||||||
|
|
||||||
addByteSearchPattern(searcher, validationOptions, addresses, rtti0PointerOffset,
|
|
||||||
rtti0Address, bytes);
|
|
||||||
|
|
||||||
// or referenced by a shifted pointer based on program data organization shift amount
|
|
||||||
bytes = ProgramMemoryUtil.getShiftedDirectAddressBytes(program, rtti0Address);
|
|
||||||
|
|
||||||
addByteSearchPattern(searcher, validationOptions, addresses, rtti0PointerOffset,
|
addByteSearchPattern(searcher, validationOptions, addresses, rtti0PointerOffset,
|
||||||
rtti0Address, bytes);
|
rtti0Address, bytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.program.database.util;
|
package ghidra.program.database.util;
|
||||||
|
|
||||||
|
import java.util.ConcurrentModificationException;
|
||||||
|
|
||||||
|
import db.*;
|
||||||
|
import db.util.ErrorHandler;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.database.map.AddressMap;
|
import ghidra.program.database.map.AddressMap;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
|
@ -26,11 +29,6 @@ import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
import java.util.ConcurrentModificationException;
|
|
||||||
|
|
||||||
import db.*;
|
|
||||||
import db.util.ErrorHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AddressSetPropertyMap that uses a RangeMapDB to maintain a set of addresses.
|
* AddressSetPropertyMap that uses a RangeMapDB to maintain a set of addresses.
|
||||||
*
|
*
|
||||||
|
@ -74,8 +72,8 @@ public class AddressSetPropertyMapDB implements AddressSetPropertyMap {
|
||||||
DBHandle dbh = program.getDBHandle();
|
DBHandle dbh = program.getDBHandle();
|
||||||
String tableName = AddressSetPropertyMapDB.TABLE_PREFIX + mapName;
|
String tableName = AddressSetPropertyMapDB.TABLE_PREFIX + mapName;
|
||||||
if (dbh.getTable(tableName) != null) {
|
if (dbh.getTable(tableName) != null) {
|
||||||
throw new DuplicateNameException("Address Set Property Map named " + mapName +
|
throw new DuplicateNameException(
|
||||||
" already exists.");
|
"Address Set Property Map named " + mapName + " already exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AddressSetPropertyMapDB(program, mapName, program, addrMap, lock);
|
return new AddressSetPropertyMapDB(program, mapName, program, addrMap, lock);
|
||||||
|
@ -91,9 +89,8 @@ public class AddressSetPropertyMapDB implements AddressSetPropertyMap {
|
||||||
this.mapName = mapName;
|
this.mapName = mapName;
|
||||||
this.lock = lock;
|
this.lock = lock;
|
||||||
|
|
||||||
propertyMap =
|
propertyMap = new AddressRangeMapDB(program.getDBHandle(), program.getAddressMap(),
|
||||||
new AddressRangeMapDB(program.getDBHandle(), program.getAddressMap(),
|
program.getLock(), MY_PREFIX + mapName, errHandler, BooleanField.class, true);
|
||||||
program.getLock(), MY_PREFIX + mapName, errHandler, BooleanField.class, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,7 +111,7 @@ public class AddressSetPropertyMapDB implements AddressSetPropertyMap {
|
||||||
* @see ghidra.program.model.util.AddressSetPropertyMap#add(ghidra.program.model.address.AddressSet)
|
* @see ghidra.program.model.util.AddressSetPropertyMap#add(ghidra.program.model.address.AddressSet)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void add(AddressSet addressSet) {
|
public void add(AddressSetView addressSet) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
|
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
|
@ -135,7 +132,7 @@ public class AddressSetPropertyMapDB implements AddressSetPropertyMap {
|
||||||
* @see ghidra.program.model.util.AddressSetPropertyMap#set(ghidra.program.model.address.AddressSet)
|
* @see ghidra.program.model.util.AddressSetPropertyMap#set(ghidra.program.model.address.AddressSet)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void set(AddressSet addressSet) {
|
public void set(AddressSetView addressSet) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
|
@ -169,7 +166,7 @@ public class AddressSetPropertyMapDB implements AddressSetPropertyMap {
|
||||||
* @see ghidra.program.model.util.AddressSetPropertyMap#remove(ghidra.program.model.address.AddressSet)
|
* @see ghidra.program.model.util.AddressSetPropertyMap#remove(ghidra.program.model.address.AddressSet)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void remove(AddressSet addressSet) {
|
public void remove(AddressSetView addressSet) {
|
||||||
checkDeleted();
|
checkDeleted();
|
||||||
lock.acquire();
|
lock.acquire();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -23,59 +22,60 @@ import ghidra.program.model.address.*;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface AddressSetPropertyMap {
|
public interface AddressSetPropertyMap {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the address range to the property map.
|
* Add the address range to the property map.
|
||||||
* @param start start of the range
|
* @param start start of the range
|
||||||
* @param end end of the range
|
* @param end end of the range
|
||||||
*/
|
*/
|
||||||
void add(Address start, Address end);
|
void add(Address start, Address end);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the address set to the property map.
|
* Add the address set to the property map.
|
||||||
* @param addressSet address set to add
|
* @param addressSet address set to add
|
||||||
*/
|
*/
|
||||||
void add(AddressSet addressSet);
|
void add(AddressSetView addressSet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the property map and set it with the given address set.
|
* Clear the property map and set it with the given address set.
|
||||||
* @param addressSet address set to use
|
* @param addressSet address set to use
|
||||||
*/
|
*/
|
||||||
void set(AddressSet addressSet);
|
void set(AddressSetView addressSet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the address range from the property map.
|
* Remove the address range from the property map.
|
||||||
* @param start start of the range
|
* @param start start of the range
|
||||||
* @param end end of the range
|
* @param end end of the range
|
||||||
*/
|
*/
|
||||||
void remove(Address start, Address end);
|
void remove(Address start, Address end);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the address set from the property map.
|
* Remove the address set from the property map.
|
||||||
* @param addressSet address set to remove
|
* @param addressSet address set to remove
|
||||||
*/
|
*/
|
||||||
void remove(AddressSet addressSet);
|
void remove(AddressSetView addressSet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the address set for the property map.
|
* Return the address set for the property map.
|
||||||
*/
|
*/
|
||||||
AddressSet getAddressSet();
|
AddressSet getAddressSet();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an address iterator over the property map.
|
* Return an address iterator over the property map.
|
||||||
*/
|
*/
|
||||||
AddressIterator getAddresses();
|
AddressIterator getAddresses();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an address range iterator over the property map.
|
* Return an address range iterator over the property map.
|
||||||
*/
|
*/
|
||||||
AddressRangeIterator getAddressRanges();
|
AddressRangeIterator getAddressRanges();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the property map.
|
* Clear the property map.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the property map contains the given address.
|
* Return whether the property map contains the given address.
|
||||||
* @param addr address to check
|
* @param addr address to check
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue