mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-10-23 05:31:02 +00:00
Add a WildcardAssembler Plugin.
This commit is contained in:
parent
65dbaeaf57
commit
43131199cf
0
Ghidra/Features/WildcardAssembler/Module.manifest
Normal file
0
Ghidra/Features/WildcardAssembler/Module.manifest
Normal file
29
Ghidra/Features/WildcardAssembler/build.gradle
Normal file
29
Ghidra/Features/WildcardAssembler/build.gradle
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/helpProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/jacocoProject.gradle"
|
||||
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
|
||||
apply plugin: 'eclipse'
|
||||
eclipse.project.name = 'Features WildcardAssembler'
|
||||
|
||||
dependencies {
|
||||
|
||||
api project(':Base')
|
||||
|
||||
testImplementation project(path: ':SoftwareModeling', configuration: 'testArtifacts')
|
||||
}
|
6
Ghidra/Features/WildcardAssembler/certification.manifest
Normal file
6
Ghidra/Features/WildcardAssembler/certification.manifest
Normal file
|
@ -0,0 +1,6 @@
|
|||
##VERSION: 2.0
|
||||
##MODULE IP: FAMFAMFAM Icons - CC 2.5
|
||||
##MODULE IP: Oxygen Icons - LGPL 3.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
src/main/help/help/TOC_Source.xml||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/WildcardAssemblerModule/Wildcard_Assembler.html||GHIDRA||||END|
|
|
@ -0,0 +1,338 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
// This script illustrates one way to work with the WildSleighAssembler by searching for the first
|
||||
// 10 instances of each encoding of a single predefined x86_64 instruction with a wildcard.
|
||||
//
|
||||
// This script assembles the instruction "XOR R13D,`Q1/R1(2|3)D`" where the second operand is a
|
||||
// wildcard which we have constrained to be either R12D or R13D. Using the metadata from assembly
|
||||
// we find all the unique encodings after discounting wildcard specific bits and search for each of
|
||||
// these unique encodings in the binary. For performance / example reasons we only find the first
|
||||
// 10 search results for each starting from currentAddress. For each result, we print the address
|
||||
// of the hit and the value of the wildcard at that location.
|
||||
//
|
||||
// See documentation within the script for more detail on APIs. See "Help > Contents > Ghidra
|
||||
// Functionality > Wildcard Assembler" for assembly wildcard syntax.
|
||||
//
|
||||
// See the "WildSleighAssemblerInfo" script for a simpler use of the WildSleighAssembler.
|
||||
// @category Examples
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.AssemblySelector;
|
||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolutionResults;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.asm.wild.WildOperandInfo;
|
||||
import ghidra.asm.wild.WildSleighAssembler;
|
||||
import ghidra.asm.wild.WildSleighAssemblerBuilder;
|
||||
import ghidra.asm.wild.sem.WildAssemblyResolvedPatterns;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class FindInstructionWithWildcard extends GhidraScript {
|
||||
|
||||
public void run() throws Exception {
|
||||
|
||||
var instruction = askString("Instruction to search",
|
||||
"Instruction to search for with wildcard (example is for x86_64, adjust if you are using a different architecture):",
|
||||
"XOR R13D,`Q1/R1(2|3)D`");
|
||||
var allValidResults = getAllResolvedPatterns(instruction);
|
||||
|
||||
var encodings = getMapOfUniqueInstructionEncodings(allValidResults);
|
||||
|
||||
searchMemoryForEncodings(encodings, allValidResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a {@link WildSleighAssembler} to assemble the given {@code wildcardedInstruction}
|
||||
*
|
||||
* @param wildcardedInstruction
|
||||
* @return All {@link WildAssemblyResolvedPatterns} produced from the given input (e.g. All
|
||||
* VALID results of assembling the given input)
|
||||
*/
|
||||
private List<WildAssemblyResolvedPatterns> getAllResolvedPatterns(
|
||||
String wildcardedInstruction) {
|
||||
var allValidResults = new ArrayList<WildAssemblyResolvedPatterns>();
|
||||
|
||||
SleighLanguage currentLanguage = (SleighLanguage) currentProgram.getLanguage();
|
||||
|
||||
// Create a WildSleighAssembler that we'll use to assemble our wildcard-included
|
||||
// instruction
|
||||
WildSleighAssemblerBuilder assemblerBuilder =
|
||||
new WildSleighAssemblerBuilder(currentLanguage);
|
||||
WildSleighAssembler assembler =
|
||||
assemblerBuilder.getAssembler(new AssemblySelector(), currentProgram);
|
||||
|
||||
// Parse a single line of assembly which includes a wildcard.
|
||||
Collection<AssemblyParseResult> parses = assembler.parseLine(wildcardedInstruction);
|
||||
|
||||
// Remove all the AssemblyParseResults that represent parse errors
|
||||
List<AssemblyParseResult> allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toList();
|
||||
|
||||
// Try to resolve each AssemblyParseResult at address 0 and collect all the results which
|
||||
// are valid
|
||||
Address addr0 = currentLanguage.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = assembler.resolveTree(r, addr0);
|
||||
|
||||
allValidResults.addAll(getValidResults(results));
|
||||
}
|
||||
return allValidResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce the given {@code WildAssemblyResolvedPatterns} to a map keyed by unique instruction
|
||||
* encodings WITHOUT specific wildcard values. Each key in this map corresponds to a set of
|
||||
* {@code WildOperandInfo} options for the corresponding encoding.
|
||||
*
|
||||
* @param allValidResolvedPatterns
|
||||
* @return
|
||||
*/
|
||||
private Map<AssemblyPatternBlock, Set<WildOperandInfo>> getMapOfUniqueInstructionEncodings(
|
||||
List<WildAssemblyResolvedPatterns> allValidResolvedPatterns) {
|
||||
|
||||
// Bail out early if we were not able to find any results (should only happen if the hard
|
||||
// coded instruction in this example script is changed)
|
||||
if (allValidResolvedPatterns.isEmpty()) {
|
||||
println("No assembly results for given assembly with wildcard!");
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
// 'allValidResolvedPatterns' has one entry for each encoding/wildcard value pair. We're
|
||||
// going to reduce that down to a map where each:
|
||||
// * Key is a single encoding of an instruction WITHOUT the wildcard operand bits specified
|
||||
// * Value is a set of WildOperandInfo instances containing each valid wildcard completion
|
||||
Map<AssemblyPatternBlock, Set<WildOperandInfo>> encodings =
|
||||
new HashMap<AssemblyPatternBlock, Set<WildOperandInfo>>();
|
||||
for (WildAssemblyResolvedPatterns x : allValidResolvedPatterns) {
|
||||
var y = new ReducedWildcardAssemblyResolvedPattern(x);
|
||||
var existing = encodings.get(y.maskedInstruction);
|
||||
if (existing == null) {
|
||||
existing = new HashSet<WildOperandInfo>();
|
||||
}
|
||||
existing.addAll(y.parent.getOperandInfo());
|
||||
encodings.put(y.maskedInstruction, existing);
|
||||
}
|
||||
return encodings;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper class which creates and holds an {@code AssemblyPatternBlock} for a given
|
||||
* {@code WildAssemblyResolvedPatterns}. This created {@code AssemblyPatternBlock} does NOT have
|
||||
* any bits specified that are part of a wildcarded operand. This is in contrast to the original
|
||||
* {@code WildAssemblyResolvedPatterns} where those bits are specified to have the values that
|
||||
* correspond to the {@code WildOperandInfo} values found in the
|
||||
* {@code WildAssemblyResolvedPatterns}.
|
||||
*/
|
||||
static class ReducedWildcardAssemblyResolvedPattern {
|
||||
/**
|
||||
* The original WildAssemblyResolvedPatterns that this is based on
|
||||
*/
|
||||
WildAssemblyResolvedPatterns parent;
|
||||
/**
|
||||
* The portion of the instruction that is NOT any wildcarded operand
|
||||
*/
|
||||
AssemblyPatternBlock maskedInstruction;
|
||||
|
||||
ReducedWildcardAssemblyResolvedPattern(WildAssemblyResolvedPatterns input) {
|
||||
parent = input;
|
||||
|
||||
// Remove all the bits which correspond to wildcarded opcodes from the instruction save
|
||||
// the result as maskedInstruction
|
||||
var reducedInstruction = input.getInstruction();
|
||||
for (WildOperandInfo info : input.getOperandInfo()) {
|
||||
reducedInstruction = reducedInstruction.maskOut(info.location());
|
||||
}
|
||||
maskedInstruction = reducedInstruction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true of the given value shares the same {@code maskedInstruction} and wildcard(s)
|
||||
* as this instance.
|
||||
*
|
||||
* @param other
|
||||
* Value to compare against
|
||||
* @return True if both values share the same maskedInstruction and wildcard(s)
|
||||
*/
|
||||
boolean sameBaseEncoding(ReducedWildcardAssemblyResolvedPattern other) {
|
||||
if (!this.maskedInstruction.equals(other.maskedInstruction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loop over each WildOperandInfo in this to ensure that there is a matching one in
|
||||
// other which shares the same wildcard (name) and location. Remember that there might
|
||||
// be more than one wildcard in an instruction with the same name so we can't assume
|
||||
// there's not a match if a matching name doesn't have the same location.
|
||||
for (WildOperandInfo info : this.parent.getOperandInfo()) {
|
||||
var foundMatch = false;
|
||||
|
||||
// Check all of other's WildOperandInfo
|
||||
for (WildOperandInfo otherInfo : other.parent.getOperandInfo()) {
|
||||
// Check if we have matching wildcards (names), expressions, and locations.
|
||||
// Notice that we're *NOT* checking choice here, as we expect those to be different.
|
||||
if (info.wildcard().equals(otherInfo.wildcard()) &&
|
||||
info.expression().equals(otherInfo.expression()) &&
|
||||
info.location().equals(otherInfo.location())) {
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundMatch) {
|
||||
// We were unable to find a wildcard that matched so we declare that these
|
||||
// encodings don't have the same base encoding
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for at most 10 matches for each given encoding, starting at {@code currentAddress}
|
||||
* and prints results to the console.
|
||||
* <p>
|
||||
* This searches encoding by encoding, restarting back at the start of memory for each.
|
||||
* <p>
|
||||
* Does not currently print wildcard information about the search results, but this could be
|
||||
* added.
|
||||
*
|
||||
* @param encodings
|
||||
* HashMap of encodings to that encoding's possible WildOperandInfo values.
|
||||
* @throws MemoryAccessException
|
||||
* If we find bytes but can't read them
|
||||
*/
|
||||
private void searchMemoryForEncodings(
|
||||
Map<AssemblyPatternBlock, Set<WildOperandInfo>> encodings,
|
||||
List<WildAssemblyResolvedPatterns> allValidResolvedPatterns)
|
||||
throws MemoryAccessException {
|
||||
|
||||
Memory memory = currentProgram.getMemory();
|
||||
|
||||
for (var encoding : encodings.keySet()) {
|
||||
println("Searching for encoding: " + encoding.toString());
|
||||
|
||||
// Start/restart back at currentAddress for each new encoding search
|
||||
var searchFromAddress = currentAddress;
|
||||
var matchCount = 0;
|
||||
|
||||
// Stop if we run out of addresses or don't have a currentAddress
|
||||
while (searchFromAddress != null) {
|
||||
|
||||
var matchAddress =
|
||||
memory.findBytes(searchFromAddress, encoding.getVals(), encoding.getMask(),
|
||||
getReusePreviousChoices(), monitor);
|
||||
if (matchAddress == null) {
|
||||
// No match found, go to next encoding
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the specific bytes found at this address and print match info
|
||||
var foundBytes = new byte[encoding.length()];
|
||||
memory.getBytes(matchAddress, foundBytes);
|
||||
printSearchHitInfo(matchAddress, foundBytes, allValidResolvedPatterns);
|
||||
|
||||
// Continue to the next result (unless we've had 10 matches already)
|
||||
searchFromAddress = matchAddress.next();
|
||||
matchCount += 1;
|
||||
if (matchCount > 10) {
|
||||
println("Stopping after 10 matches!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information about a specific search hit to the console
|
||||
* <p>
|
||||
* NOTE: This is certainly not the highest performance way to do this, but it is reasonably
|
||||
* simple and shows what is possible.
|
||||
*
|
||||
* @param matchAddress
|
||||
* The address where our search hit occurred
|
||||
* @param matchData
|
||||
* The bytes found at matchAddress. Must include the entire matching instruction!
|
||||
* @param allValidResolvedPatterns
|
||||
* All resolved patterns which were searched from (used to find wildcard information)
|
||||
*/
|
||||
private void printSearchHitInfo(Address matchAddress, byte[] matchData,
|
||||
List<WildAssemblyResolvedPatterns> allValidResolvedPatterns) {
|
||||
|
||||
println("Hit at address: " + matchAddress.toString());
|
||||
|
||||
// Check all the resolutions we were searching for and find the one which matches the found
|
||||
// bytes and use that resolution to determine what the wildcard values are for the given
|
||||
// hit.
|
||||
//
|
||||
// It'd likely be much faster to deduplicate similar WildAssemblyResolvedPatterns based on
|
||||
// their instruction with wildcards masked out (similar to what is done in
|
||||
// ReducedWildcardAssemblyResolvedPattern) and create a lookup table for wildcard values but
|
||||
// that's beyond this basic example script.
|
||||
for (WildAssemblyResolvedPatterns resolved : allValidResolvedPatterns) {
|
||||
var resolvedInstruction = resolved.getInstruction();
|
||||
if (resolvedInstruction.length() > matchData.length) {
|
||||
// It can't be this resolution because we were not given enough bytes of
|
||||
// matchData
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mask out the matchData with the mask of our candidate resolvedInstruction and
|
||||
// see if
|
||||
// the results match. If they do, then this is the WildAssemblyResolvedPatterns
|
||||
var matchMasked = resolvedInstruction.getMaskedValue(matchData);
|
||||
if (matchMasked.equals(resolvedInstruction)) {
|
||||
for (WildOperandInfo info : resolved.getOperandInfo()) {
|
||||
println("Wildcard `" + info.wildcard() + "` = " + info.choice().toString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
println("Failed to find search hit info");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all items from {@code results} which are instances of
|
||||
* {@link WildAssemblyResolvedPatterns}
|
||||
*
|
||||
* @param results
|
||||
* The results to return {@link WildAssemblyResolvePatterns} from
|
||||
* @return All {@link WildAssemblyResolvedPatterns} which were found in the input
|
||||
*/
|
||||
private List<WildAssemblyResolvedPatterns> getValidResults(AssemblyResolutionResults results) {
|
||||
var out = new ArrayList<WildAssemblyResolvedPatterns>();
|
||||
for (AssemblyResolution result : results) {
|
||||
if (result instanceof WildAssemblyResolvedPatterns resolvedPatterns) {
|
||||
out.add(resolvedPatterns);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
// Prints information about the results of assembling an instruction using the WildSleighAssembler
|
||||
// when that instruction has one or more wildcards in it.
|
||||
//
|
||||
// This script uses currentProgram and currentAddress to determine architecture and location.
|
||||
//
|
||||
// Notice that this script doesn't only output the assembled bytes of an instruction, but also more
|
||||
// specific information about each wildcard in the input instruction.
|
||||
//
|
||||
// See the "FindInstructionWithWildcard" script for another example of using the WildSleighAssembler
|
||||
// @category Examples
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.plugin.assembler.AssemblySelector;
|
||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolution;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.asm.wild.WildOperandInfo;
|
||||
import ghidra.asm.wild.WildSleighAssembler;
|
||||
import ghidra.asm.wild.WildSleighAssemblerBuilder;
|
||||
import ghidra.asm.wild.sem.WildAssemblyResolvedPatterns;
|
||||
|
||||
public class WildSleighAssemblerInfo extends GhidraScript {
|
||||
|
||||
List<String> sampleInstructions =
|
||||
Arrays.asList("MOV EAX,`Q1`", "MOV RDI,qword ptr [`Q1` + -0x30]", "Custom");
|
||||
|
||||
public void run() throws Exception {
|
||||
|
||||
String instruction = askChoice("Instruction to assemble", "Assemble this instruction:",
|
||||
sampleInstructions, "Custom");
|
||||
|
||||
if (instruction.equals("Custom")) {
|
||||
instruction = askString("Instruction",
|
||||
"Instruction to assemble and print information about.",
|
||||
"MOV RDI,qword ptr [`Q1` + -0x30]");
|
||||
}
|
||||
var assemblyResolutions = getAllAssemblyResolutions(instruction);
|
||||
printAssemblyParseResults(assemblyResolutions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a {@link WildSleighAssembler} to assemble the given {@code wildcardedInstruction}
|
||||
*
|
||||
* @param wildcardedInstruction
|
||||
* String of the instruction to assemble, possibly including a wildcard
|
||||
* @return All AssemblyParseResult produced from the given input
|
||||
*/
|
||||
private List<AssemblyResolution> getAllAssemblyResolutions(
|
||||
String wildcardedInstruction) {
|
||||
|
||||
SleighLanguage language = (SleighLanguage) currentProgram.getLanguage();
|
||||
|
||||
// Make sure that if one of the example instructions was chosen the current binary has the
|
||||
// correct architecture.
|
||||
if (sampleInstructions.contains(wildcardedInstruction) &&
|
||||
!language.getLanguageID().toString().equals("x86:LE:64:default")) {
|
||||
popup(
|
||||
"The current program is not a \"x86:LE:64:default\" binary that the example was " +
|
||||
"designed for. This script will continue and try anyway, but the results might " +
|
||||
"not be as expected. Retry with a custom instruction in your architecture!");
|
||||
}
|
||||
|
||||
// Create a WildSleighAssembler that we'll use to assemble our wildcard-included instruction
|
||||
WildSleighAssemblerBuilder assemblerBuilder = new WildSleighAssemblerBuilder(language);
|
||||
WildSleighAssembler assembler =
|
||||
assemblerBuilder.getAssembler(new AssemblySelector(), currentProgram);
|
||||
|
||||
// Parse a single line of assembly which includes a wildcard.
|
||||
Collection<AssemblyParseResult> parses = assembler.parseLine(wildcardedInstruction);
|
||||
|
||||
long errorCount = parses.stream().filter(p -> p.isError()).count();
|
||||
println("Removing " + errorCount + " of " + parses.size() +
|
||||
" AssemblyParseResults which are errored parses");
|
||||
|
||||
return parses
|
||||
.stream()
|
||||
// Remove all the AssemblyParseResults that represent parse errors
|
||||
.filter(p -> !p.isError())
|
||||
// Resolve each parseTree at the current address and collect all AssemblyResolutions
|
||||
// into a single flat collection using flatMap
|
||||
.flatMap(p -> assembler.resolveTree(p, currentAddress).stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information about the {@link WildAssemblyResolvedPatterns} in the given list.
|
||||
*
|
||||
* @param resolutionResults
|
||||
*/
|
||||
private void printAssemblyParseResults(List<AssemblyResolution> resolutionResults) {
|
||||
var errorCount = 0;
|
||||
|
||||
for (AssemblyResolution r : resolutionResults) {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
if (r instanceof WildAssemblyResolvedPatterns resolution) {
|
||||
printWildAssemblyResolvedPatterns(resolution);
|
||||
}
|
||||
else {
|
||||
errorCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCount > 0) {
|
||||
println(
|
||||
"Additionally " + errorCount +
|
||||
" non-WildAssemblyResolvedPatterns were not printed");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information about a single {@link WildAssemblyResolvedPatterns}, including information
|
||||
* about each of its wildcards.
|
||||
*
|
||||
* @param x
|
||||
* The value to print information about.
|
||||
*/
|
||||
private void printWildAssemblyResolvedPatterns(WildAssemblyResolvedPatterns x) {
|
||||
println("Instruction bits (including wildcard values): " + x.getInstruction());
|
||||
for (WildOperandInfo info : x.getOperandInfo()) {
|
||||
String out =
|
||||
"\tThe wildcard " + info.wildcard() + " is found in bits " + info.location();
|
||||
if (info.choice() == null) {
|
||||
out += " with a value which can be computed with the expression: " +
|
||||
info.expression();
|
||||
}
|
||||
else {
|
||||
out += " with the value: " + info.choice() +
|
||||
" which can be computed with the expression: " + info.expression();
|
||||
}
|
||||
println(out);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version='1.0' encoding='ISO-8859-1' ?>
|
||||
<!--
|
||||
|
||||
This is an XML file intended to be parsed by the Ghidra help system. It is loosely based
|
||||
upon the JavaHelp table of contents document format. The Ghidra help system uses a
|
||||
TOC_Source.xml file to allow a module with help to define how its contents appear in the
|
||||
Ghidra help viewer's table of contents. The main document (in the Base module)
|
||||
defines a basic structure for the
|
||||
Ghidra table of contents system. Other TOC_Source.xml files may use this structure to insert
|
||||
their files directly into this structure (and optionally define a substructure).
|
||||
|
||||
|
||||
In this document, a tag can be either a <tocdef> or a <tocref>. The former is a definition
|
||||
of an XML item that may have a link and may contain other <tocdef> and <tocref> children.
|
||||
<tocdef> items may be referred to in other documents by using a <tocref> tag with the
|
||||
appropriate id attribute value. Using these two tags allows any module to define a place
|
||||
in the table of contents system (<tocdef>), which also provides a place for
|
||||
other TOC_Source.xml files to insert content (<tocref>).
|
||||
|
||||
During the help build time, all TOC_Source.xml files will be parsed and validated to ensure
|
||||
that all <tocref> tags point to valid <tocdef> tags. From these files will be generated
|
||||
<module name>_TOC.xml files, which are table of contents files written in the format
|
||||
desired by the JavaHelp system. Additionally, the genated files will be merged together
|
||||
as they are loaded by the JavaHelp system. In the end, when displaying help in the Ghidra
|
||||
help GUI, there will be on table of contents that has been created from the definitions in
|
||||
all of the modules' TOC_Source.xml files.
|
||||
|
||||
|
||||
Tags and Attributes
|
||||
|
||||
<tocdef>
|
||||
-id - the name of the definition (this must be unique across all TOC_Source.xml files)
|
||||
-text - the display text of the node, as seen in the help GUI
|
||||
-target** - the file to display when the node is clicked in the GUI
|
||||
-sortgroup - this is a string that defines where a given node should appear under a given
|
||||
parent. The string values will be sorted by the JavaHelp system using
|
||||
a javax.text.RulesBasedCollator. If this attribute is not specified, then
|
||||
the text of attribute will be used.
|
||||
|
||||
<tocref>
|
||||
-id - The id of the <tocdef> that this reference points to
|
||||
|
||||
**The URL for the target is relative and should start with 'help/topics'. This text is
|
||||
used by the Ghidra help system to provide a universal starting point for all links so that
|
||||
they can be resolved at runtime, across modules.
|
||||
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<tocroot>
|
||||
<tocref id="Ghidra Functionality">
|
||||
|
||||
<tocdef id="Wildcard Assembler"
|
||||
text="Wildcard Assembler"
|
||||
target="help/topics/WildcardAssemblerModule/Wildcard_Assembler.html" >
|
||||
</tocdef>
|
||||
</tocref>
|
||||
</tocroot>
|
|
@ -0,0 +1,96 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META http-equiv="Content-Language" content="en-us">
|
||||
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
|
||||
<TITLE>Wildcard Assembler Module</TITLE>
|
||||
<LINK rel="stylesheet" type="text/css" href="help/shared/DefaultStyle.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1><A name="Wildcard_Assembler_Module"></A>Wildcard Assembler Module</H1>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<P><B>This feature is currently only available as an API for Ghidra
|
||||
scripts and plugins. For an example of how to use the API, see the
|
||||
FindInstructionWithWildcard and WildSleighAssemblerInfo scripts in the
|
||||
Script Manager.</B></P>
|
||||
|
||||
<P>The <I>Wildcard Assembler</I> extends Ghidra's assembler to enable
|
||||
assembling instructions with specific tokens replaced with wildcards.</P>
|
||||
|
||||
<p>This assembler will return metadata for each wildcard in an assembled
|
||||
instruction. This metadata includes details of which specific bits of an
|
||||
assembled instruction are used to derive the value of the wildcarded token
|
||||
and the expression used to derive the value.</p>
|
||||
|
||||
<H2>Wildcard Syntax</H2>
|
||||
|
||||
<P>Wildcards in instructions are specified by replacing the
|
||||
to-be-wildcarded token with a wildcard name surrounded by backticks (e.g.
|
||||
<CODE>`Q1`</CODE> where Q1 is an arbitrary wildcard name) and passing the
|
||||
entire instruction to the Wildcard Assembler.</P>
|
||||
|
||||
<P>By default, the Wildcard Assembler will return metadata about all
|
||||
possible values that a wildcarded token could take and all the encodings
|
||||
of all these values. This behavior can be limited by filtering the
|
||||
wildcard by appending specific syntax after the wildcard name:</P>
|
||||
|
||||
<UL>
|
||||
<LI><B>Numeric Filter:</B>
|
||||
<UL>
|
||||
<LI>Appending <CODE>[..]</CODE> will constrain the wildcarded token
|
||||
to only numeric values (and not registers or other strings).</LI>
|
||||
<LI>Appending <CODE>[0x0..0x100]</CODE> (where 0x0 and 0x100 are
|
||||
arbitrary hexadecimal values with the smaller number first) will
|
||||
constrain the wildcarded token to only numeric values between the
|
||||
two given values. This can be used to ensure that the returned
|
||||
encodings can hold values of a desired size. Multiple non-contiguous
|
||||
ranges can be specified by separating them with commas (e.g.
|
||||
<CODE>[0x0..0x5,0x1000-0x4000]</CODE>)</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI><B>Regex Filter:</B>
|
||||
<UL>
|
||||
<LI>Appending <CODE>/ABCD</CODE> where ABCD is an arbitrary
|
||||
regular expression will constrain the wildcarded token to only be
|
||||
string tokens matching the given regular expression. This is most
|
||||
likely used for filtering register names; for example appending
|
||||
<CODE>/(sp)|(lr)</CODE> to a wildcard in a register position in
|
||||
ARM assembly will limit the wildcard results to only encodings
|
||||
using the <CODE>sp</CODE> or <CODE>lr</CODE> registers in that
|
||||
position.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
<P>Normally a wildcard will only match a single token. To allow a single
|
||||
wildcard to match multiple related tokens: precede the wildcard name with a
|
||||
<CODE>!</CODE> character. For example, in a x86:LE:32:default binary:</P>
|
||||
<BLOCKQUOTE>
|
||||
<DL>
|
||||
<DT>No wildcard:</DT>
|
||||
<DD><CODE>MOVSD.REP ES:EDI,ESI</CODE></DD>
|
||||
|
||||
<DT>Single token:</DT>
|
||||
<DD><CODE>MOVSD.REP `Q1`:EDI,ESI</CODE></DD>
|
||||
|
||||
<DT>Single token:</DT>
|
||||
<DD><CODE>MOVSD.REP ES:`Q2`,ESI</CODE></DD>
|
||||
|
||||
<DT>Single token (Does <I>NOT</I> assemble):</DT>
|
||||
<DD><CODE>MOVSD.REP `Q3`,ESI</CODE></DD>
|
||||
|
||||
<DT>Multi-token:</DT>
|
||||
<DD><CODE>MOVSD.REP `!Q4`,ESI</CODE></DD>
|
||||
</DL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P class="providedbyplugin">Provided by: <I>Wildcard Assembler Module</I></P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -0,0 +1,30 @@
|
|||
/* ###
|
||||
* 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.asm.wild;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyConstructorSemantic;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyPatternBlock;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
|
||||
public record WildOperandInfo(String wildcard, List<AssemblyConstructorSemantic> path,
|
||||
AssemblyPatternBlock location, PatternExpression expression, Object choice) {
|
||||
|
||||
public WildOperandInfo shift(int amt) {
|
||||
return new WildOperandInfo(wildcard, path, location.shift(amt), expression, choice);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* ###
|
||||
* 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.asm.wild;
|
||||
|
||||
import ghidra.app.plugin.assembler.AssemblySelector;
|
||||
import ghidra.app.plugin.assembler.sleigh.AbstractSleighAssembler;
|
||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParser;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseBranch;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.asm.wild.sem.WildAssemblyResolvedPatterns;
|
||||
import ghidra.asm.wild.sem.WildAssemblyTreeResolver;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class WildSleighAssembler extends AbstractSleighAssembler<WildAssemblyResolvedPatterns> {
|
||||
|
||||
protected WildSleighAssembler(
|
||||
AbstractAssemblyResolutionFactory<WildAssemblyResolvedPatterns, ?> factory,
|
||||
AssemblySelector selector, SleighLanguage lang, AssemblyParser parser,
|
||||
AssemblyDefaultContext defaultContext, AssemblyContextGraph ctxGraph) {
|
||||
super(factory, selector, lang, parser, defaultContext, ctxGraph);
|
||||
}
|
||||
|
||||
protected WildSleighAssembler(
|
||||
AbstractAssemblyResolutionFactory<WildAssemblyResolvedPatterns, ?> factory,
|
||||
AssemblySelector selector, Program program, AssemblyParser parser,
|
||||
AssemblyDefaultContext defaultContext, AssemblyContextGraph ctxGraph) {
|
||||
super(factory, selector, program, parser, defaultContext, ctxGraph);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyTreeResolver newResolver(Address at, AssemblyParseBranch tree,
|
||||
AssemblyPatternBlock ctx) {
|
||||
return new WildAssemblyTreeResolver(factory, lang, at, tree, ctx, ctxGraph);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/* ###
|
||||
* 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.asm.wild;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.assembler.AssemblySelector;
|
||||
import ghidra.app.plugin.assembler.sleigh.AbstractSleighAssemblerBuilder;
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyGrammar;
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblySentential;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AbstractAssemblyResolutionFactory;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedBackfill;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.*;
|
||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.plugin.processors.sleigh.pattern.DisjointPattern;
|
||||
import ghidra.asm.wild.grammars.WildAssemblyProduction;
|
||||
import ghidra.asm.wild.sem.WildAssemblyResolutionFactory;
|
||||
import ghidra.asm.wild.sem.WildAssemblyResolvedPatterns;
|
||||
import ghidra.asm.wild.symbol.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class WildSleighAssemblerBuilder
|
||||
extends AbstractSleighAssemblerBuilder<WildAssemblyResolvedPatterns, WildSleighAssembler> {
|
||||
|
||||
protected final Map<AssemblySymbol, AssemblyNonTerminal> wildNTs = new HashMap<>();
|
||||
|
||||
public WildSleighAssemblerBuilder(SleighLanguage lang) {
|
||||
super(lang);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractAssemblyResolutionFactory< //
|
||||
WildAssemblyResolvedPatterns, AssemblyResolvedBackfill> newResolutionFactory() {
|
||||
return new WildAssemblyResolutionFactory();
|
||||
}
|
||||
|
||||
protected WildAssemblyTerminal generateWildTerminal(AssemblySymbol t) {
|
||||
if (t instanceof AssemblyNonTerminal nt) {
|
||||
if ("instruction".equals(nt.getName())) {
|
||||
// Never allow full instruction to be wildcarded
|
||||
return null;
|
||||
}
|
||||
return new WildAssemblySubtableTerminal(nt.getName());
|
||||
}
|
||||
if (t instanceof AssemblyFixedNumericTerminal term) {
|
||||
return new WildAssemblyFixedNumericTerminal(term.getVal());
|
||||
}
|
||||
if (t instanceof AssemblyNumericMapTerminal term) {
|
||||
return new WildAssemblyNumericMapTerminal(term.getName(), term.getMap());
|
||||
}
|
||||
if (t instanceof AssemblyNumericTerminal term) {
|
||||
return new WildAssemblyNumericTerminal(term.getName(), term.getBitSize(),
|
||||
term.getSpace());
|
||||
}
|
||||
if (t instanceof AssemblyStringMapTerminal term) {
|
||||
return new WildAssemblyStringMapTerminal(term.getName(), term.getMap());
|
||||
}
|
||||
if (t instanceof AssemblyStringTerminal term && term.getDefiningSymbol() != null) {
|
||||
return new WildAssemblyStringTerminal(term.getString());
|
||||
}
|
||||
/**
|
||||
* Exclude string terminals. These should be purely syntactic elements. Use of them as fixed
|
||||
* literals, e.g., 1 or RAX, is an error on the spec's part.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
protected AssemblyNonTerminal createWildNonTerminal(AssemblySymbol s) {
|
||||
WildAssemblyTerminal wt = generateWildTerminal(s);
|
||||
if (wt == null) {
|
||||
return null;
|
||||
}
|
||||
WildAssemblyNonTerminal nt =
|
||||
new WildAssemblyNonTerminal("w`" + s.getName(), s.takesOperandIndex());
|
||||
grammar.addProduction(new WildAssemblyProduction(nt, new AssemblySentential<>(s)));
|
||||
grammar.addProduction(new WildAssemblyProduction(nt, new AssemblySentential<>(wt)));
|
||||
return nt;
|
||||
}
|
||||
|
||||
protected AssemblyNonTerminal getOrCreateWildNonTerminal(AssemblySymbol s) {
|
||||
return wildNTs.computeIfAbsent(s, this::createWildNonTerminal);
|
||||
}
|
||||
|
||||
protected AssemblySymbol maybeReplaceSymbol(AssemblySymbol s) {
|
||||
AssemblyNonTerminal nt = getOrCreateWildNonTerminal(s);
|
||||
if (nt == null) {
|
||||
return s;
|
||||
}
|
||||
return nt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addProduction(AssemblyGrammar subgrammar, AssemblyNonTerminal lhs,
|
||||
AssemblySentential<AssemblyNonTerminal> rhs, DisjointPattern pattern, Constructor cons,
|
||||
List<Integer> indices) {
|
||||
// Don't call super. We want to replace the original production
|
||||
AssemblySentential<AssemblyNonTerminal> wildRhs = new AssemblySentential<>();
|
||||
for (AssemblySymbol sym : rhs.getSymbols()) {
|
||||
wildRhs.addSymbol(maybeReplaceSymbol(sym));
|
||||
}
|
||||
subgrammar.addProduction(lhs, wildRhs, pattern, cons, indices);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildSleighAssembler newAssembler(AssemblySelector selector) {
|
||||
return new WildSleighAssembler(factory, selector, lang, parser, defaultContext, ctxGraph);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildSleighAssembler newAssembler(AssemblySelector selector, Program program) {
|
||||
return new WildSleighAssembler(factory, selector, program, parser, defaultContext,
|
||||
ctxGraph);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/* ###
|
||||
* 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.asm.wild.grammars;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyProduction;
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblySentential;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNonTerminal;
|
||||
|
||||
public class WildAssemblyProduction extends AssemblyProduction {
|
||||
|
||||
public WildAssemblyProduction(AssemblyNonTerminal lhs,
|
||||
AssemblySentential<AssemblyNonTerminal> rhs) {
|
||||
super(lhs, rhs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConstructor() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.MaskedLong;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AbstractAssemblyResolutionFactory.AbstractAssemblyResolutionBuilder;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AbstractAssemblyResolutionFactory.AbstractAssemblyResolvedPatternsBuilder;
|
||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||
import ghidra.app.plugin.processors.sleigh.ContextOp;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
import ghidra.asm.wild.WildOperandInfo;
|
||||
import ghidra.asm.wild.sem.WildAssemblyResolutionFactory.WildAssemblyResolvedPatternsBuilder;
|
||||
|
||||
public class DefaultWildAssemblyResolvedPatterns extends DefaultAssemblyResolvedPatterns
|
||||
implements WildAssemblyResolvedPatterns {
|
||||
|
||||
protected final WildAssemblyResolutionFactory factory;
|
||||
protected final Set<WildOperandInfo> opInfo;
|
||||
|
||||
protected DefaultWildAssemblyResolvedPatterns(WildAssemblyResolutionFactory factory,
|
||||
String description, Constructor cons, List<? extends AssemblyResolution> children,
|
||||
AssemblyResolution right, AssemblyPatternBlock ins, AssemblyPatternBlock ctx,
|
||||
Set<AssemblyResolvedBackfill> backfills, Set<AssemblyResolvedPatterns> forbids,
|
||||
Set<WildOperandInfo> opInfo) {
|
||||
super(factory, description, cons, children, right, ins, ctx, backfills, forbids);
|
||||
this.factory = factory;
|
||||
this.opInfo = opInfo == null ? Set.of() : Collections.unmodifiableSet(opInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<WildOperandInfo> getOperandInfo() {
|
||||
return opInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int computeHash() {
|
||||
int result = super.computeHash();
|
||||
result *= 31;
|
||||
result += Objects.hashCode(opInfo);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean wildPartsEqual(DefaultWildAssemblyResolvedPatterns that) {
|
||||
if (!partsEqual(that)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.opInfo, that.opInfo)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
DefaultWildAssemblyResolvedPatterns that = (DefaultWildAssemblyResolvedPatterns) obj;
|
||||
return wildPartsEqual(that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String lineToString() {
|
||||
return "WILD:" + super.lineToString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String childrenToString(String indent) {
|
||||
if (opInfo.isEmpty()) {
|
||||
return super.childrenToString(indent);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(indent + "opInfo\n:");
|
||||
for (WildOperandInfo i : opInfo) {
|
||||
sb.append(indent + " " + i + "\n");
|
||||
}
|
||||
sb.append(super.childrenToString(indent));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected WildAssemblyResolvedPatternsBuilder withWildInfoBuilder(String wildcard,
|
||||
List<AssemblyConstructorSemantic> path, AssemblyPatternBlock location,
|
||||
PatternExpression expression, Object choice) {
|
||||
var builder = factory.newPatternsBuilder();
|
||||
builder.copyFromDefault(this);
|
||||
var newOpInfo = new WildOperandInfo(wildcard, path, location, expression, choice);
|
||||
if (opInfo.isEmpty()) {
|
||||
builder.opInfo = Set.of(newOpInfo);
|
||||
} else {
|
||||
builder.opInfo = new HashSet<>(opInfo);
|
||||
builder.opInfo.add(newOpInfo);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WildAssemblyResolvedPatterns withWildInfo(String wildcard,
|
||||
List<AssemblyConstructorSemantic> path, AssemblyPatternBlock location,
|
||||
PatternExpression expression, Object choice) {
|
||||
return withWildInfoBuilder(wildcard, path, location, expression, choice).build();
|
||||
}
|
||||
|
||||
protected WildAssemblyResolvedPatterns cast(AssemblyResolvedPatterns pat) {
|
||||
return (WildAssemblyResolvedPatterns) pat;
|
||||
}
|
||||
|
||||
protected WildAssemblyResolvedPatternsBuilder cast(
|
||||
AbstractAssemblyResolvedPatternsBuilder<?> builder) {
|
||||
return (WildAssemblyResolvedPatternsBuilder) builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder shiftBuilder(int amt) {
|
||||
var builder = cast(super.shiftBuilder(amt));
|
||||
builder.opInfo = new HashSet<>();
|
||||
for (WildOperandInfo info : opInfo) {
|
||||
builder.opInfo.add(info.shift(amt));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
// NOTE: Do not override truncateBuilder. The docs say only used for reading a context op.
|
||||
|
||||
@Override
|
||||
protected AbstractAssemblyResolutionBuilder<?, ?> checkNotForbiddenBuilder() {
|
||||
var builder = super.checkNotForbiddenBuilder();
|
||||
if (builder instanceof WildAssemblyResolvedPatternsBuilder pb) {
|
||||
pb.opInfo = opInfo;
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder combineBuilder(AssemblyResolvedPatterns pat) {
|
||||
var builder = cast(super.combineBuilder(pat));
|
||||
if (builder != null) {
|
||||
builder.opInfo = new HashSet<>(this.opInfo);
|
||||
builder.opInfo.addAll(cast(pat).getOperandInfo());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
// NOTE: Do not override combineLessBackfill. Taken care of by combineBuilder.
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder combineBuilder(AssemblyResolvedBackfill bf) {
|
||||
var builder = cast(super.combineBuilder(bf));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder withForbidsBuilder(
|
||||
Set<AssemblyResolvedPatterns> more) {
|
||||
var builder = cast(super.withForbidsBuilder(more));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder withDescriptionBuilder(String description) {
|
||||
var builder = cast(super.withDescriptionBuilder(description));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder withConstructorBuilder(Constructor cons) {
|
||||
var builder = cast(super.withConstructorBuilder(cons));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder writeContextOpBuilder(ContextOp cop,
|
||||
MaskedLong val) {
|
||||
var builder = cast(super.writeContextOpBuilder(cop, val));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder copyAppendDescriptionBuilder(String append) {
|
||||
var builder = cast(super.copyAppendDescriptionBuilder(append));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder withRightBuilder(AssemblyResolution right) {
|
||||
var builder = cast(super.withRightBuilder(right));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder nopLeftSiblingBuilder() {
|
||||
var builder = cast(super.nopLeftSiblingBuilder());
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder parentBuilder(String description, int opCount) {
|
||||
var builder = cast(super.parentBuilder(description, opCount));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder maskOutBuilder(ContextOp cop) {
|
||||
var builder = cast(super.maskOutBuilder(cop));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatternsBuilder solveContextChangesForForbidsBuilder(
|
||||
AssemblyConstructorSemantic sem, Map<String, Long> vals) {
|
||||
var builder = cast(super.solveContextChangesForForbidsBuilder(sem, vals));
|
||||
builder.opInfo = opInfo;
|
||||
return builder;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.MaskedLong;
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.OperandValueSolver;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.Constructor;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.*;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
|
||||
public class PatternUtils {
|
||||
private PatternUtils() {
|
||||
}
|
||||
|
||||
public static WildAssemblyResolvedPatterns castWild(AssemblyResolvedPatterns rp) {
|
||||
return (WildAssemblyResolvedPatterns) rp;
|
||||
}
|
||||
|
||||
public static AssemblyPatternBlock collectLocation(PatternExpression exp) {
|
||||
if (exp instanceof BinaryExpression bin) {
|
||||
return collectLocation(bin.getLeft()).combine(collectLocation(bin.getRight()));
|
||||
}
|
||||
if (exp instanceof UnaryExpression un) {
|
||||
return collectLocation(un.getUnary());
|
||||
}
|
||||
if (exp instanceof ContextField cf) {
|
||||
// TODO: I'm not sure how to capture info for operands that go temporarily into context
|
||||
return AssemblyPatternBlock.nop();
|
||||
}
|
||||
if (exp instanceof TokenField tf) {
|
||||
return AssemblyPatternBlock.fromTokenField(tf, MaskedLong.ONES);
|
||||
}
|
||||
if (exp instanceof OperandValue ov) {
|
||||
// I still have a lot of uncertainty as to what an OperandValue is
|
||||
Constructor cons = ov.getConstructor();
|
||||
OperandSymbol sym = cons.getOperand(ov.getIndex());
|
||||
PatternExpression patexp = OperandValueSolver.getDefiningExpression(sym);
|
||||
return collectLocation(patexp).shift(AssemblyTreeResolver.computeOffset(sym, cons));
|
||||
}
|
||||
// constant, start, end, next2
|
||||
return AssemblyPatternBlock.nop();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseTreeNode;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.SubtableSymbol;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseHiddenNode;
|
||||
|
||||
public class WildAssemblyConstructStateGenerator extends AssemblyHiddenConstructStateGenerator {
|
||||
protected final String wildcard;
|
||||
|
||||
public WildAssemblyConstructStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||
SubtableSymbol subtableSym, String wildcard, AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, subtableSym, fromLeft);
|
||||
this.wildcard = wildcard;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AssemblyParseTreeNode getFiller() {
|
||||
return new WildAssemblyParseHiddenNode(resolver.getGrammar(), wildcard);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken.RegexWildcard;
|
||||
|
||||
public class WildAssemblyFixedNumericStateGenerator
|
||||
extends AbstractAssemblyStateGenerator<WildAssemblyParseToken> {
|
||||
|
||||
protected final OperandSymbol opSym;
|
||||
protected final long val;
|
||||
|
||||
public WildAssemblyFixedNumericStateGenerator(
|
||||
AbstractAssemblyTreeResolver<?> resolver, WildAssemblyParseToken node,
|
||||
OperandSymbol opSym, long val, AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, node, fromLeft);
|
||||
this.opSym = opSym;
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||
if (!node.wild.test(val)) {
|
||||
return Stream.of();
|
||||
}
|
||||
return Stream.of(
|
||||
new AssemblyGeneratedPrototype(new WildAssemblyOperandState(resolver, gc.path, gc.shift,
|
||||
node.getSym(), val, opSym, node.wildcardName(), val), fromLeft));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.OperandValueSolver;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
|
||||
public class WildAssemblyNopState extends AssemblyNopState {
|
||||
protected final OperandSymbol opSym; // super just uses it for length
|
||||
protected final String wildcard;
|
||||
|
||||
public WildAssemblyNopState(AbstractAssemblyTreeResolver<?> resolver,
|
||||
List<AssemblyConstructorSemantic> path, int shift, OperandSymbol opSym,
|
||||
String wildcard) {
|
||||
super(resolver, path, shift, opSym);
|
||||
this.opSym = opSym;
|
||||
this.wildcard = Objects.requireNonNull(wildcard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHash() {
|
||||
int result = super.computeHash();
|
||||
result *= 31;
|
||||
result += wildcard.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean wildPartsEqual(WildAssemblyNopState that) {
|
||||
if (!partsEqual(that)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.wildcard.equals(that.wildcard)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
WildAssemblyNopState that = (WildAssemblyNopState) obj;
|
||||
return wildPartsEqual(that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WILD:" + super.toString();
|
||||
}
|
||||
|
||||
protected WildAssemblyResolvedPatterns castWildPatterns(AssemblyResolvedPatterns rp) {
|
||||
return (WildAssemblyResolvedPatterns) rp;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<AssemblyResolvedPatterns> resolve(AssemblyResolvedPatterns fromRight,
|
||||
Collection<AssemblyResolvedError> errors) {
|
||||
PatternExpression symExp = OperandValueSolver.getDefiningExpression(opSym);
|
||||
AssemblyPatternBlock location = PatternUtils.collectLocation(symExp).shift(shift);
|
||||
return super.resolve(fromRight, errors)
|
||||
.map(PatternUtils::castWild)
|
||||
.map(r -> r.withWildInfo(wildcard, path, location, symExp, null));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken;
|
||||
|
||||
public class WildAssemblyNopStateGenerator
|
||||
extends AbstractAssemblyStateGenerator<WildAssemblyParseToken> {
|
||||
|
||||
protected final OperandSymbol opSym;
|
||||
protected final String wildcard;
|
||||
|
||||
public WildAssemblyNopStateGenerator(WildAssemblyTreeResolver resolver,
|
||||
WildAssemblyParseToken node, OperandSymbol opSym, String wildcard,
|
||||
AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, node, fromLeft);
|
||||
this.opSym = opSym;
|
||||
this.wildcard = wildcard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||
// TODO: Do we want to restrict the values?
|
||||
// TODO: Is this the right place to generate "interesting values"?
|
||||
gc.dbg("Generating WILD NOP for " + opSym);
|
||||
return Stream.of(new AssemblyGeneratedPrototype(
|
||||
new WildAssemblyNopState(resolver, gc.path, gc.shift, opSym, wildcard), fromLeft));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken.RegexWildcard;
|
||||
|
||||
public class WildAssemblyNumericMapStateGenerator
|
||||
extends AbstractAssemblyStateGenerator<WildAssemblyParseToken> {
|
||||
|
||||
protected final OperandSymbol opSym;
|
||||
protected final Map<Long, Integer> map;
|
||||
|
||||
public WildAssemblyNumericMapStateGenerator(WildAssemblyTreeResolver resolver,
|
||||
WildAssemblyParseToken node, OperandSymbol opSym, Map<Long, Integer> map,
|
||||
AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, node, fromLeft);
|
||||
this.opSym = opSym;
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||
// TODO: If all values are represented, perhaps just leave the bits unspecified.
|
||||
// I'll lose the choice information, though....
|
||||
if (node.wild instanceof RegexWildcard) {
|
||||
// Faster to recognize this up front, instead of streaming and filtering all out
|
||||
return Stream.of();
|
||||
}
|
||||
return map.entrySet()
|
||||
.stream()
|
||||
.filter(e -> node.wild.test(e.getKey()))
|
||||
.map(e -> new AssemblyGeneratedPrototype(
|
||||
new WildAssemblyOperandState(resolver, gc.path, gc.shift, node.getSym(),
|
||||
e.getValue(), opSym, node.wildcardName(), e.getKey()),
|
||||
fromLeft));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyGeneratedPrototype;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedPatterns;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken.*;
|
||||
|
||||
public class WildAssemblyNumericStateGenerator extends WildAssemblyNopStateGenerator {
|
||||
|
||||
public WildAssemblyNumericStateGenerator(WildAssemblyTreeResolver resolver,
|
||||
WildAssemblyParseToken node, OperandSymbol opSym, String wildcard,
|
||||
AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, node, opSym, wildcard, fromLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||
if (node.wild instanceof RegexWildcard) {
|
||||
return Stream.of();
|
||||
}
|
||||
if (node.wild instanceof FreeWildcard || node.wild instanceof NumericWildcard) {
|
||||
return super.generate(gc);
|
||||
}
|
||||
if (node.wild instanceof RangesWildcard wild) {
|
||||
return wild.stream()
|
||||
.mapToObj(v -> new AssemblyGeneratedPrototype(new WildAssemblyOperandState(
|
||||
resolver, gc.path, gc.shift, node.getSym(), v, opSym, node.wildcardName(),
|
||||
v), fromLeft));
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.expr.OperandValueSolver;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyTerminal;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
|
||||
public class WildAssemblyOperandState extends AssemblyOperandState {
|
||||
protected final String wildcard;
|
||||
protected final Object choice;
|
||||
|
||||
protected WildAssemblyOperandState(AbstractAssemblyTreeResolver<?> resolver,
|
||||
List<AssemblyConstructorSemantic> path, int shift, AssemblyTerminal terminal,
|
||||
long value, OperandSymbol opSym, String wildcard, Object choice) {
|
||||
super(resolver, path, shift, terminal, value, opSym);
|
||||
this.wildcard = Objects.requireNonNull(wildcard);
|
||||
this.choice = choice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int computeHash() {
|
||||
int result = super.computeHash();
|
||||
result *= 31;
|
||||
result += wildcard.hashCode();
|
||||
result *= 31;
|
||||
result += choice.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean wildPartsEqual(WildAssemblyOperandState that) {
|
||||
if (!partsEqual(that)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.wildcard.equals(that.wildcard)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.choice.equals(that.choice)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
WildAssemblyOperandState that = (WildAssemblyOperandState) obj;
|
||||
return wildPartsEqual(that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WILD:" + super.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<AssemblyResolvedPatterns> resolve(AssemblyResolvedPatterns fromRight,
|
||||
Collection<AssemblyResolvedError> errors) {
|
||||
PatternExpression symExp = OperandValueSolver.getDefiningExpression(opSym);
|
||||
AssemblyPatternBlock location = PatternUtils.collectLocation(symExp).shift(shift);
|
||||
return super.resolve(fromRight, errors)
|
||||
.map(PatternUtils::castWild)
|
||||
.map(r -> r.withWildInfo(wildcard, path, location, symExp, choice));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AbstractAssemblyResolutionFactory;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.AssemblyResolvedBackfill;
|
||||
import ghidra.asm.wild.WildOperandInfo;
|
||||
|
||||
public class WildAssemblyResolutionFactory extends
|
||||
AbstractAssemblyResolutionFactory<WildAssemblyResolvedPatterns, AssemblyResolvedBackfill> {
|
||||
|
||||
protected class WildAssemblyResolvedPatternsBuilder
|
||||
extends AbstractAssemblyResolvedPatternsBuilder<WildAssemblyResolvedPatterns> {
|
||||
protected Set<WildOperandInfo> opInfo;
|
||||
|
||||
@Override
|
||||
protected WildAssemblyResolvedPatterns build() {
|
||||
return new DefaultWildAssemblyResolvedPatterns(WildAssemblyResolutionFactory.this,
|
||||
description, cons, children, right, ins, ctx, backfills, forbids, opInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WildAssemblyResolvedPatternsBuilder newPatternsBuilder() {
|
||||
return new WildAssemblyResolvedPatternsBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultAssemblyResolvedBackfillBuilder newBackfillBuilder() {
|
||||
return new DefaultAssemblyResolvedBackfillBuilder();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.expression.PatternExpression;
|
||||
import ghidra.asm.wild.WildOperandInfo;
|
||||
|
||||
public interface WildAssemblyResolvedPatterns extends AssemblyResolvedPatterns {
|
||||
|
||||
Set<WildOperandInfo> getOperandInfo();
|
||||
|
||||
WildAssemblyResolvedPatterns withWildInfo(String wildcard,
|
||||
List<AssemblyConstructorSemantic> path, AssemblyPatternBlock location,
|
||||
PatternExpression expression, Object choice);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.collections4.MultiValuedMap;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken.NumericWildcard;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken.RangesWildcard;
|
||||
|
||||
public class WildAssemblyStringMapStateGenerator
|
||||
extends AbstractAssemblyStateGenerator<WildAssemblyParseToken> {
|
||||
|
||||
protected final OperandSymbol opSym;
|
||||
protected final MultiValuedMap<String, Integer> map;
|
||||
|
||||
public WildAssemblyStringMapStateGenerator(WildAssemblyTreeResolver resolver,
|
||||
WildAssemblyParseToken node, OperandSymbol opSym, MultiValuedMap<String, Integer> map,
|
||||
AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, node, fromLeft);
|
||||
this.opSym = opSym;
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||
// TODO: If all values are represented, perhaps just leave the bits unspecified.
|
||||
// I'll lose the choice information, though....
|
||||
if (node.wild instanceof RangesWildcard || node.wild instanceof NumericWildcard) {
|
||||
return Stream.of();
|
||||
}
|
||||
return map.entries()
|
||||
.stream()
|
||||
.filter(e -> node.wild.test(e.getKey()))
|
||||
.map(e -> new AssemblyGeneratedPrototype(
|
||||
new WildAssemblyOperandState(resolver, gc.path, gc.shift, node.getSym(),
|
||||
e.getValue(), opSym, node.wildcardName(), e.getKey()),
|
||||
fromLeft));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.OperandSymbol;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken;
|
||||
|
||||
public class WildAssemblyStringStateGenerator
|
||||
extends AbstractAssemblyStateGenerator<WildAssemblyParseToken> {
|
||||
|
||||
protected final OperandSymbol opSym;
|
||||
protected final String val;
|
||||
|
||||
public WildAssemblyStringStateGenerator(AbstractAssemblyTreeResolver<?> resolver,
|
||||
WildAssemblyParseToken node, OperandSymbol opSym, String val,
|
||||
AssemblyResolvedPatterns fromLeft) {
|
||||
super(resolver, node, fromLeft);
|
||||
this.opSym = opSym;
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<AssemblyGeneratedPrototype> generate(GeneratorContext gc) {
|
||||
if (!node.wild.test(val)) {
|
||||
return Stream.of();
|
||||
}
|
||||
return Stream.of(new AssemblyGeneratedPrototype(new WildAssemblyOperandState(resolver,
|
||||
gc.path, gc.shift, node.getSym(), 0, opSym, node.wildcardName(), val), fromLeft));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/* ###
|
||||
* 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.asm.wild.sem;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseBranch;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseTreeNode;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.app.plugin.processors.sleigh.symbol.*;
|
||||
import ghidra.asm.wild.grammars.WildAssemblyProduction;
|
||||
import ghidra.asm.wild.symbol.*;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseHiddenNode;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public class WildAssemblyTreeResolver
|
||||
extends AbstractAssemblyTreeResolver<WildAssemblyResolvedPatterns> {
|
||||
|
||||
public WildAssemblyTreeResolver(
|
||||
AbstractAssemblyResolutionFactory<WildAssemblyResolvedPatterns, ?> factory,
|
||||
SleighLanguage lang, Address at, AssemblyParseBranch tree, AssemblyPatternBlock context,
|
||||
AssemblyContextGraph ctxGraph) {
|
||||
super(factory, lang, at, tree, context, ctxGraph);
|
||||
}
|
||||
|
||||
protected AbstractAssemblyStateGenerator<?> getWildHiddenStateGenerator(OperandSymbol opSym,
|
||||
String wildcard, AssemblyResolvedPatterns fromLeft) {
|
||||
TripleSymbol defSym = opSym.getDefiningSymbol();
|
||||
if (defSym instanceof SubtableSymbol subtable) {
|
||||
return new WildAssemblyConstructStateGenerator(this, subtable, wildcard, fromLeft);
|
||||
}
|
||||
return new WildAssemblyNopStateGenerator(this, null, opSym, wildcard, fromLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractAssemblyStateGenerator<?> getStateGenerator(OperandSymbol opSym,
|
||||
AssemblyParseTreeNode node, AssemblyResolvedPatterns fromLeft) {
|
||||
if (node instanceof WildAssemblyParseHiddenNode hidden) {
|
||||
return getWildHiddenStateGenerator(opSym, hidden.wildcard, fromLeft);
|
||||
}
|
||||
if (node instanceof AssemblyParseBranch branch && !branch.isConstructor()) {
|
||||
if (branch.getProduction() instanceof WildAssemblyProduction) {
|
||||
assert branch.getSubstitutions().size() == 1;
|
||||
return getStateGenerator(opSym, branch.getSubstitution(0), fromLeft);
|
||||
}
|
||||
}
|
||||
if (!(node instanceof WildAssemblyParseToken token)) {
|
||||
return super.getStateGenerator(opSym, node, fromLeft);
|
||||
}
|
||||
if (node.getSym() instanceof WildAssemblySubtableTerminal term) {
|
||||
return getWildHiddenStateGenerator(opSym, token.wildcardName(), fromLeft);
|
||||
}
|
||||
if (node.getSym() instanceof WildAssemblyNumericMapTerminal term) {
|
||||
return new WildAssemblyNumericMapStateGenerator(this, token, opSym, term.map, fromLeft);
|
||||
}
|
||||
if (node.getSym() instanceof WildAssemblyStringMapTerminal term) {
|
||||
return new WildAssemblyStringMapStateGenerator(this, token, opSym, term.map, fromLeft);
|
||||
}
|
||||
if (node.getSym() instanceof WildAssemblyStringTerminal term) {
|
||||
return new WildAssemblyStringStateGenerator(this, token, opSym, term.str, fromLeft);
|
||||
}
|
||||
if (node.getSym() instanceof WildAssemblyFixedNumericTerminal term) {
|
||||
return new WildAssemblyFixedNumericStateGenerator(this, token, opSym, term.val,
|
||||
fromLeft);
|
||||
}
|
||||
if (node.getSym() instanceof WildAssemblyNumericTerminal term) {
|
||||
return new WildAssemblyNumericStateGenerator(this, token, opSym, token.wildcardName(),
|
||||
fromLeft);
|
||||
}
|
||||
return super.getStateGenerator(opSym, node, fromLeft);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/* ###
|
||||
* 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.asm.wild.symbol;
|
||||
|
||||
public class WildAssemblyFixedNumericTerminal extends WildAssemblyTerminal {
|
||||
public final long val;
|
||||
|
||||
public WildAssemblyFixedNumericTerminal(long val) {
|
||||
super("WILD:" + val);
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[wild:" + val + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/* ###
|
||||
* 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.asm.wild.symbol;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNonTerminal;
|
||||
|
||||
public class WildAssemblyNonTerminal extends AssemblyNonTerminal {
|
||||
|
||||
protected final boolean takesOperandIndex;
|
||||
|
||||
public WildAssemblyNonTerminal(String name, boolean takesOperandIndex) {
|
||||
super(name);
|
||||
this.takesOperandIndex = takesOperandIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takesOperandIndex() {
|
||||
return takesOperandIndex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* ###
|
||||
* 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.asm.wild.symbol;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class WildAssemblyNumericMapTerminal extends WildAssemblyTerminal {
|
||||
public final Map<Long, Integer> map;
|
||||
|
||||
public WildAssemblyNumericMapTerminal(String name, Map<Long, Integer> map) {
|
||||
super(name);
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[wildnum:" + name + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* ###
|
||||
* 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.asm.wild.symbol;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
|
||||
public class WildAssemblyNumericTerminal extends WildAssemblyTerminal {
|
||||
protected final int bitsize;
|
||||
protected final AddressSpace space;
|
||||
|
||||
public WildAssemblyNumericTerminal(String name, int bitsize, AddressSpace space) {
|
||||
super(name);
|
||||
this.bitsize = bitsize;
|
||||
this.space = space;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (bitsize == 0) {
|
||||
return "[wildnum:" + name + "]";
|
||||
}
|
||||
return "[wildnum" + bitsize + ":" + name + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* ###
|
||||
* 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.asm.wild.symbol;
|
||||
|
||||
import org.apache.commons.collections4.MultiValuedMap;
|
||||
|
||||
public class WildAssemblyStringMapTerminal extends WildAssemblyTerminal {
|
||||
public final MultiValuedMap<String, Integer> map;
|
||||
|
||||
public WildAssemblyStringMapTerminal(String name, MultiValuedMap<String, Integer> map) {
|
||||
super(name);
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[wildlist:" + name + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/* ###
|
||||
* 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.asm.wild.symbol;
|
||||
|
||||
public class WildAssemblyStringTerminal extends WildAssemblyTerminal {
|
||||
public final String str;
|
||||
|
||||
public WildAssemblyStringTerminal(String str) {
|
||||
super("\"" + str + "\"");
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[wildstr:" + name + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takesOperandIndex() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.asm.wild.symbol;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNumericSymbols;
|
||||
|
||||
public class WildAssemblySubtableTerminal extends WildAssemblyTerminal {
|
||||
// Yes, leave the ! part of the spec/name
|
||||
public static final Pattern PAT_WILD_TREE = Pattern.compile("`(?<spec>![^`]*)`");
|
||||
|
||||
public WildAssemblySubtableTerminal(String name) {
|
||||
super("`WILD`-" + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + name + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pattern getPattern() {
|
||||
return PAT_WILD_TREE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getSuggestions(String got, AssemblyNumericSymbols symbols) {
|
||||
return List.of("`!Q1`");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* ###
|
||||
* 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.asm.wild.symbol;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyGrammar;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyNumericSymbols;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyTerminal;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseToken;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken;
|
||||
|
||||
public abstract class WildAssemblyTerminal extends AssemblyTerminal {
|
||||
public static final Pattern PAT_WILD = Pattern.compile("`(?<spec>[^`]*)`");
|
||||
|
||||
public WildAssemblyTerminal(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
protected Pattern getPattern() {
|
||||
return PAT_WILD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends AssemblyParseToken> match(String buffer, int pos,
|
||||
AssemblyGrammar grammar, AssemblyNumericSymbols symbols) {
|
||||
Matcher matcher = getPattern().matcher(buffer).region(pos, buffer.length());
|
||||
if (!matcher.lookingAt()) {
|
||||
return List.of();
|
||||
}
|
||||
return List.of(
|
||||
new WildAssemblyParseToken(grammar, this, matcher.group(), matcher.group("spec")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getSuggestions(String got, AssemblyNumericSymbols symbols) {
|
||||
return List.of("`Q1`");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* ###
|
||||
* 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.asm.wild.tree;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyGrammar;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblySymbol;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseTreeNode;
|
||||
|
||||
public class WildAssemblyParseHiddenNode extends AssemblyParseTreeNode {
|
||||
public final String wildcard;
|
||||
|
||||
public WildAssemblyParseHiddenNode(AssemblyGrammar grammar, String wildcard) {
|
||||
super(grammar);
|
||||
this.wildcard = wildcard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssemblySymbol getSym() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void print(PrintStream out, String indent) {
|
||||
out.print("<wild-hidden>");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateString() {
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/* ###
|
||||
* 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.asm.wild.tree;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.LongStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import ghidra.app.plugin.assembler.sleigh.grammars.AssemblyGrammar;
|
||||
import ghidra.app.plugin.assembler.sleigh.symbol.AssemblyTerminal;
|
||||
import ghidra.app.plugin.assembler.sleigh.tree.AssemblyParseToken;
|
||||
|
||||
public class WildAssemblyParseToken extends AssemblyParseToken {
|
||||
|
||||
public interface Wildcard {
|
||||
public static Wildcard parse(String spec) {
|
||||
Matcher matRegex = RegexWildcard.PATTERN.matcher(spec);
|
||||
if (matRegex.matches()) {
|
||||
return RegexWildcard.get(matRegex);
|
||||
}
|
||||
Matcher matNumeric = NumericWildcard.PATTERN.matcher(spec);
|
||||
if (matNumeric.matches()) {
|
||||
return NumericWildcard.get(matNumeric);
|
||||
}
|
||||
Matcher matRanges = RangesWildcard.PATTERN.matcher(spec);
|
||||
if (matRanges.matches()) {
|
||||
return RangesWildcard.get(matRanges);
|
||||
}
|
||||
return new FreeWildcard(spec);
|
||||
}
|
||||
|
||||
public String name();
|
||||
|
||||
public boolean test(Object object);
|
||||
}
|
||||
|
||||
public record FreeWildcard(String name) implements Wildcard {
|
||||
@Override
|
||||
public boolean test(Object object) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public record RegexWildcard(String name, Pattern pat) implements Wildcard {
|
||||
static final Pattern PATTERN = Pattern.compile("(?<name>[^/]*)/(?<regex>.*)");
|
||||
|
||||
public static RegexWildcard get(Matcher matcher) {
|
||||
return new RegexWildcard(matcher.group("name"),
|
||||
Pattern.compile(matcher.group("regex")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Object object) {
|
||||
if (!(object instanceof CharSequence cs)) {
|
||||
return false;
|
||||
}
|
||||
return pat.matcher(cs).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
// Because Pattern does not override equals
|
||||
return o instanceof RegexWildcard that &&
|
||||
Objects.equals(this.name, that.name) &&
|
||||
Objects.equals(this.pat.toString(), that.pat.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public record NumericWildcard(String name) implements Wildcard {
|
||||
static final Pattern PATTERN = Pattern.compile("(?<name>.*)\\[\\.\\.\\]");
|
||||
|
||||
public static NumericWildcard get(Matcher matcher) {
|
||||
return new NumericWildcard(matcher.group("name"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Object object) {
|
||||
return object instanceof Number;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: It's possible we'll eventually want BigInteger's here.
|
||||
public record WildRange(long min, long max) implements Comparable<WildRange> {
|
||||
public WildRange(long min, long max) {
|
||||
if (min > max) {
|
||||
throw new AssertionError("max > max");
|
||||
}
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public static WildRange parse(String str) {
|
||||
String[] parts = str.split("\\.\\.");
|
||||
if (parts.length == 1) {
|
||||
long val = Long.decode(parts[0]);
|
||||
return new WildRange(val, val);
|
||||
}
|
||||
if (parts.length == 2) {
|
||||
long min = Long.decode(parts[0]);
|
||||
long max = Long.decode(parts[1]);
|
||||
return new WildRange(min, max);
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid range specification in wildcard: " + str);
|
||||
}
|
||||
|
||||
public LongStream stream() {
|
||||
return LongStream.rangeClosed(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(WildRange that) {
|
||||
return Long.compare(this.min, that.min);
|
||||
}
|
||||
}
|
||||
|
||||
public record RangesWildcard(String name, List<WildRange> ranges) implements Wildcard {
|
||||
public static final Pattern PATTERN =
|
||||
Pattern.compile("(?<name>[^\\[]*)\\[(?<ranges>[^\\]]*)\\]");
|
||||
|
||||
public static RangesWildcard get(Matcher matcher) {
|
||||
return new RangesWildcard(matcher.group("name"), parseRanges(matcher.group("ranges")));
|
||||
}
|
||||
|
||||
public static List<WildRange> parseRanges(String str) {
|
||||
return Stream.of(str.split(",")).map(WildRange::parse).sorted().toList();
|
||||
}
|
||||
|
||||
static long getLong(Object a) {
|
||||
if (a instanceof Number n) {
|
||||
return n.longValue();
|
||||
}
|
||||
if (a instanceof WildRange range) {
|
||||
return range.min;
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
static int searchComp(Object a, Object b) {
|
||||
return Long.compare(getLong(a), getLong(b));
|
||||
}
|
||||
|
||||
public LongStream stream() {
|
||||
return ranges.stream().flatMapToLong(i -> i.stream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Object object) {
|
||||
if (!(object instanceof Number n)) {
|
||||
return false;
|
||||
}
|
||||
long lv = n.longValue();
|
||||
int i = Collections.binarySearch(ranges, lv, RangesWildcard::searchComp);
|
||||
if (i >= 0) {
|
||||
return true; // We're exactly at one of the mins
|
||||
}
|
||||
// -i-1 is first index greater (ceiling). I want last index lesser (floor).
|
||||
i = -i - 2;
|
||||
if (i < 0) {
|
||||
return false;
|
||||
}
|
||||
return lv <= ranges.get(i).max; // I already know lv >= min
|
||||
}
|
||||
}
|
||||
|
||||
public final Wildcard wild;
|
||||
|
||||
public WildAssemblyParseToken(AssemblyGrammar grammar, AssemblyTerminal term, String str,
|
||||
String spec) {
|
||||
super(grammar, term, str);
|
||||
this.wild = Wildcard.parse(spec);
|
||||
}
|
||||
|
||||
public String wildcardName() {
|
||||
return wild.name();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,788 @@
|
|||
/* ###
|
||||
* 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.asm.wild;
|
||||
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.Unique;
|
||||
import ghidra.app.plugin.assembler.AssemblySelector;
|
||||
import ghidra.app.plugin.assembler.sleigh.parse.AssemblyParseResult;
|
||||
import ghidra.app.plugin.assembler.sleigh.sem.*;
|
||||
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
|
||||
import ghidra.asm.wild.sem.WildAssemblyResolvedPatterns;
|
||||
import ghidra.asm.wild.symbol.WildAssemblyTerminal;
|
||||
import ghidra.asm.wild.tree.WildAssemblyParseToken.*;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.LanguageID;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
||||
public class WildSleighAssemblerTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
static SleighLanguage toy;
|
||||
static WildSleighAssembler asmToy;
|
||||
static SleighLanguage arm;
|
||||
static WildSleighAssembler asmArm;
|
||||
static SleighLanguage mips;
|
||||
static WildSleighAssembler asmMips;
|
||||
static SleighLanguage x86;
|
||||
static WildSleighAssembler asmX86;
|
||||
static Program x86Program;
|
||||
static SleighLanguage x8664;
|
||||
static WildSleighAssembler asmX8664;
|
||||
static Program x8664Program;
|
||||
|
||||
protected static void toy() throws Exception {
|
||||
if (toy != null) {
|
||||
return;
|
||||
}
|
||||
toy =
|
||||
(SleighLanguage) getLanguageService().getLanguage(new LanguageID("Toy:BE:64:default"));
|
||||
WildSleighAssemblerBuilder builder = new WildSleighAssemblerBuilder(toy);
|
||||
asmToy = builder.getAssembler(new AssemblySelector());
|
||||
}
|
||||
|
||||
protected static void arm() throws Exception {
|
||||
if (arm != null) {
|
||||
return;
|
||||
}
|
||||
ProgramBuilder armProgramBuilder = new ProgramBuilder("arm_le_test", "ARM:LE:32:v8");
|
||||
armProgramBuilder.setBytes(String.format("0x%08X", 0x0),
|
||||
"00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 fb ff ff eb 00 00 a0 e1");
|
||||
Program armProgram = armProgramBuilder.getProgram();
|
||||
arm = (SleighLanguage) armProgram.getLanguage();
|
||||
WildSleighAssemblerBuilder builderArm = new WildSleighAssemblerBuilder(arm);
|
||||
asmArm = builderArm.getAssembler(new AssemblySelector(), armProgram);
|
||||
}
|
||||
|
||||
protected static void mips() throws Exception {
|
||||
if (mips != null) {
|
||||
return;
|
||||
}
|
||||
ProgramBuilder mipsProgramBuilder = new ProgramBuilder("mips_test", "MIPS:BE:32:default");
|
||||
// The following is:
|
||||
// 0x00000000: jalx 0x8
|
||||
// 0x00000004: nop
|
||||
// 0x00000008: restore 0x1b8,ra,s0-s1
|
||||
// 0x0000000c: nop
|
||||
mipsProgramBuilder.setBytes("0x00000000",
|
||||
"0c 00 00 08 00 00 00 00 f0 30 64 77 00 00 00 00");
|
||||
// This line sets the binary at addresses 0x8-0xc to be MIPS 16 (e.g. the
|
||||
// restore instruction above)
|
||||
mipsProgramBuilder.setRegisterValue("ISA_MODE", "0x8", "0xc", 1);
|
||||
mipsProgramBuilder.disassemble("0x00000000", 0x10);
|
||||
var mipsProgram = mipsProgramBuilder.getProgram();
|
||||
mips = (SleighLanguage) mipsProgram.getLanguage();
|
||||
WildSleighAssemblerBuilder mipsBuilder = new WildSleighAssemblerBuilder(mips);
|
||||
asmMips = mipsBuilder.getAssembler(new AssemblySelector(), mipsProgram);
|
||||
}
|
||||
|
||||
protected static void x86() throws Exception {
|
||||
if (x86 != null) {
|
||||
return;
|
||||
}
|
||||
ProgramBuilder x86ProgramBuilder = new ClassicSampleX86ProgramBuilder();
|
||||
x86Program = x86ProgramBuilder.getProgram();
|
||||
x86 = (SleighLanguage) x86Program.getLanguage();
|
||||
WildSleighAssemblerBuilder builderX86 = new WildSleighAssemblerBuilder(x86);
|
||||
asmX86 = builderX86.getAssembler(new AssemblySelector(), x86Program);
|
||||
}
|
||||
|
||||
protected static void x8664() throws Exception {
|
||||
if (x8664 != null) {
|
||||
return;
|
||||
}
|
||||
ProgramBuilder x8664ProgramBuilder = new ProgramBuilder("x86_64_test", "x86:LE:64:default");
|
||||
x8664Program = x8664ProgramBuilder.getProgram();
|
||||
x8664 = (SleighLanguage) x8664Program.getLanguage();
|
||||
WildSleighAssemblerBuilder builderX8664 = new WildSleighAssemblerBuilder(x8664);
|
||||
asmX8664 = builderX8664.getAssembler(new AssemblySelector(), x8664Program);
|
||||
}
|
||||
|
||||
protected void dumpResults(AssemblyResolutionResults results) {
|
||||
System.err.println("results:" + results);
|
||||
for (AssemblyResolution res : results) {
|
||||
if (res instanceof WildAssemblyResolvedPatterns pats) {
|
||||
System.err.println(pats.getInstruction());
|
||||
for (WildOperandInfo info : pats.getOperandInfo()) {
|
||||
var choice_str = "?";
|
||||
var choice = info.choice();
|
||||
if (choice != null) {
|
||||
choice_str = choice.toString();
|
||||
}
|
||||
|
||||
System.err.println(info.location() + ": " + info.wildcard() + " = " +
|
||||
info.expression() + "(" + info.path() + ") == " + choice_str.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all items from {@code results} which are instances of
|
||||
* {@code WildAssemblyResolvedPatterns}
|
||||
*
|
||||
* @param results
|
||||
* @return
|
||||
*/
|
||||
protected List<WildAssemblyResolvedPatterns> getValidResults(
|
||||
AssemblyResolutionResults results) {
|
||||
var out = new ArrayList<WildAssemblyResolvedPatterns>();
|
||||
for (AssemblyResolution res : results) {
|
||||
if (res instanceof WildAssemblyResolvedPatterns pats) {
|
||||
out.add(pats);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all Choice values for the given {@code wildcardIdentifier} found in the given
|
||||
* {@code results}
|
||||
*
|
||||
* @param wildcardIdentifier
|
||||
* @param results
|
||||
* @return
|
||||
*/
|
||||
protected List<String> getChoiceValues(String wildcardIdentifier,
|
||||
AssemblyResolutionResults results) {
|
||||
return getValidResults(results).stream()
|
||||
.flatMap(x -> x.getOperandInfo()
|
||||
.stream()
|
||||
.filter(oi -> oi.wildcard().equals(wildcardIdentifier))
|
||||
.filter(oi -> oi.choice() != null)
|
||||
.map(oi -> oi.choice().toString()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
protected Set<AssemblyPatternBlock> getInstructionPatterns(AssemblyResolutionResults results) {
|
||||
return getValidResults(results).stream()
|
||||
.map(res -> res.getInstruction())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
protected Set<AssemblyPatternBlock> makeInstructionPatterns(String... patterns) {
|
||||
return Stream.of(patterns)
|
||||
.map(AssemblyPatternBlock::fromString)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all possible instruction encodings from the given {@code results}
|
||||
*
|
||||
* @param results the assembly results whose instruction bytes to collect
|
||||
* @return the full list of results
|
||||
*/
|
||||
protected List<byte[]> getInstructionValues(AssemblyResolutionResults results) {
|
||||
var out = new ArrayList<byte[]>();
|
||||
for (WildAssemblyResolvedPatterns res : getValidResults(results)) {
|
||||
for (byte[] instructionVal : res.getInstruction().possibleVals()) {
|
||||
// Read the docs on possibleVals(). You must create a copy.
|
||||
out.add(Arrays.copyOf(instructionVal, instructionVal.length));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all possible instruction encodings as hex strings from the given {@code results}
|
||||
*
|
||||
* @param results the results to encode and collect
|
||||
* @return the list
|
||||
*/
|
||||
protected List<String> getInstructionValuesHex(AssemblyResolutionResults results) {
|
||||
return getInstructionValues(results).stream()
|
||||
.map(x -> NumericUtilities.convertBytesToString(x, ":"))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWildRegex() {
|
||||
Pattern patWild = WildAssemblyTerminal.PAT_WILD;
|
||||
|
||||
Matcher mat = patWild.matcher("`test` more` and `more`");
|
||||
|
||||
assertTrue(mat.find(0));
|
||||
assertEquals("`test`", mat.group());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpecRegexWildcard() {
|
||||
assertEquals(new RegexWildcard("Q1", Pattern.compile("r.")), Wildcard.parse("Q1/r."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpecNumericWildcard() {
|
||||
assertEquals(new NumericWildcard("Q1"), Wildcard.parse("Q1[..]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpecRangesWildcard() {
|
||||
assertEquals(new RangesWildcard("Q1", List.of(new WildRange(1, 1))),
|
||||
Wildcard.parse("Q1[1]"));
|
||||
assertEquals(new RangesWildcard("Q1", List.of(new WildRange(1, 2))),
|
||||
Wildcard.parse("Q1[1..2]"));
|
||||
assertEquals(new RangesWildcard("Q1", List.of(
|
||||
new WildRange(-16, -4),
|
||||
new WildRange(1, 2))),
|
||||
Wildcard.parse("Q1[1..2,-0x10..-4]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangesTest() {
|
||||
Wildcard wild = Wildcard.parse("Q1[1..2,-0x10..-4]");
|
||||
|
||||
assertFalse(wild.test("r1"));
|
||||
|
||||
assertTrue(wild.test(1));
|
||||
assertTrue(wild.test(2));
|
||||
assertFalse(wild.test(0));
|
||||
assertFalse(wild.test(3));
|
||||
|
||||
assertTrue(wild.test(-16));
|
||||
assertTrue(wild.test(-10));
|
||||
assertTrue(wild.test(-4));
|
||||
assertFalse(wild.test(-17));
|
||||
assertFalse(wild.test(-3));
|
||||
|
||||
assertTrue(wild.test(-10L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWildRegOp_Toy() throws Exception {
|
||||
toy();
|
||||
Collection<AssemblyParseResult> parses = asmToy.parseLine("add `Q1`, #6");
|
||||
assertTrue(parses.stream().anyMatch(p -> !p.isError()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWildImm_Toy() throws Exception {
|
||||
toy();
|
||||
Collection<AssemblyParseResult> parses = asmToy.parseLine("add r0, #`Q2`");
|
||||
assertTrue(parses.stream().anyMatch(p -> !p.isError()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWildStr_Err_Toy() throws Exception {
|
||||
toy();
|
||||
Collection<AssemblyParseResult> parses = asmToy.parseLine("add r0, `Q3`6");
|
||||
assertFalse(parses.stream().anyMatch(p -> !p.isError()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWildRegAndImmOp_Toy() throws Exception {
|
||||
toy();
|
||||
Collection<AssemblyParseResult> parses = asmToy.parseLine("add `Q1`, #`Q2`");
|
||||
assertTrue(parses.stream().anyMatch(p -> !p.isError()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAndResolveWildRegOp_Toy() throws Exception {
|
||||
toy();
|
||||
Collection<AssemblyParseResult> parses = asmToy.parseLine("add `Q1/r.`, #6");
|
||||
AssemblyParseResult one = Unique.assertOne(parses.stream().filter(p -> !p.isError()));
|
||||
System.err.println("parse: " + one);
|
||||
|
||||
Address addr0 = toy.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
AssemblyResolutionResults results = asmToy.resolveTree(one, addr0);
|
||||
dumpResults(results);
|
||||
|
||||
assertEquals(
|
||||
makeInstructionPatterns("C8:06", "C8:16", "C8:26", "C8:36", "C8:46", "C8:56", "C8:66",
|
||||
"C8:76", "C8:86", "C8:96"),
|
||||
getInstructionPatterns(results));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAndResolveWildImmOp_Toy() throws Exception {
|
||||
toy();
|
||||
Collection<AssemblyParseResult> parses = asmToy.parseLine("add r0, #`Q2[0,2..4]`");
|
||||
AssemblyParseResult one = Unique.assertOne(parses.stream().filter(p -> !p.isError()));
|
||||
System.err.println("parse: " + one);
|
||||
|
||||
Address addr0 = toy.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
AssemblyResolutionResults results = asmToy.resolveTree(one, addr0);
|
||||
dumpResults(results);
|
||||
|
||||
assertEquals(makeInstructionPatterns("C8:00", "C8:02", "C8:03", "C8:04"),
|
||||
getInstructionPatterns(results));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAndResolveWildSubTree_Toy() throws Exception {
|
||||
toy();
|
||||
Collection<AssemblyParseResult> parses =
|
||||
asmToy.parseLine("add r0, `!Q2`").stream().filter(p -> !p.isError()).toList();
|
||||
|
||||
Address addr0 = toy.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
var allValidPatterns = new HashSet<AssemblyPatternBlock>();
|
||||
for (AssemblyParseResult p : parses) {
|
||||
System.err.println("parse: " + p);
|
||||
AssemblyResolutionResults results = asmToy.resolveTree(p, addr0);
|
||||
dumpResults(results);
|
||||
allValidPatterns.addAll(getInstructionPatterns(results));
|
||||
}
|
||||
assertEquals(makeInstructionPatterns(
|
||||
"C8:0X", // Unspecified immediate op
|
||||
// enumerated register ops
|
||||
"C0:00", "C0:01", "C0:02", "C0:03", "C0:04", "C0:05", "C0:06", "C0:07",
|
||||
"C0:08", "C0:09", "C0:0A", "C0:0B", "C0:0C", "C0:0D", "C0:0E", "C0:0F"),
|
||||
allValidPatterns);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAndResolveWildNotSubTree_Toy() throws Exception {
|
||||
toy();
|
||||
Collection<AssemblyParseResult> parses =
|
||||
asmToy.parseLine("add r0, `Q2`").stream().filter(p -> !p.isError()).toList();
|
||||
|
||||
Address addr0 = toy.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
var allValidPatterns = new HashSet<AssemblyPatternBlock>();
|
||||
for (AssemblyParseResult p : parses) {
|
||||
System.err.println("parse: " + p);
|
||||
AssemblyResolutionResults results = asmToy.resolveTree(p, addr0);
|
||||
dumpResults(results);
|
||||
allValidPatterns.addAll(getInstructionPatterns(results));
|
||||
}
|
||||
assertEquals(makeInstructionPatterns(
|
||||
// Immediate op is excluded, because Toy's Sleigh spec wants a # on the literal
|
||||
// enumerated register ops
|
||||
"C0:00", "C0:01", "C0:02", "C0:03", "C0:04", "C0:05", "C0:06", "C0:07",
|
||||
"C0:08", "C0:09", "C0:0A", "C0:0B", "C0:0C", "C0:0D", "C0:0E", "C0:0F"),
|
||||
allValidPatterns);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMov_arm() throws Exception {
|
||||
arm();
|
||||
Collection<AssemblyParseResult> parses = asmArm.parseLine("mov `Q1`, #0x0");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidChoices = new ArrayList<String>();
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = arm.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmArm.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidChoices.addAll(getChoiceValues("Q1", results));
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
// This is the encoding of "mov pc,#0x0"
|
||||
assertThat("Expected to have a Q1=='pc' encoding",
|
||||
allValidEncodings, hasItem("00:f0:a0:e3"));
|
||||
// This is the encoding of "mov r5,#0x0"
|
||||
assertThat("Expected to have a Q1=='r5' encoding",
|
||||
allValidEncodings, hasItem("00:50:a0:e3"));
|
||||
|
||||
assertEquals(Set.of(
|
||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "lr", "sp", "pc"),
|
||||
Set.copyOf(allValidChoices));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSub_arm() throws Exception {
|
||||
arm();
|
||||
Collection<AssemblyParseResult> parses = asmArm.parseLine("sub r1,r1,#0x200");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = arm.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmArm.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
assertTrue("Expect to have one valid encoding", allValidEncodings.size() == 1);
|
||||
assertTrue("Expect to have 02:1c:41:e2 as an encoding",
|
||||
allValidEncodings.contains("02:1c:41:e2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubWildcard_arm() throws Exception {
|
||||
arm();
|
||||
Collection<AssemblyParseResult> parses = asmArm.parseLine("sub `Q3/r.`,`Q4`,#0x200");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = arm.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmArm.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
assertTrue("Expect to have multiple valid encodings", allValidEncodings.size() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestore_mips() throws Exception {
|
||||
mips();
|
||||
Collection<AssemblyParseResult> parses = asmMips.parseLine("restore 0x1b8,ra,s0-s1");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr8 = mips.getAddressFactory().getDefaultAddressSpace().getAddress(8);
|
||||
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmMips.resolveTree(r, addr8);
|
||||
dumpResults(results);
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
assertThat(allValidEncodings, hasItem("f0:30:64:77"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestoreWild_mips() throws Exception {
|
||||
mips();
|
||||
Collection<AssemblyParseResult> parses = asmMips.parseLine("restore `Q1`,ra,s0-s1");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidResults = new ArrayList<WildAssemblyResolvedPatterns>();
|
||||
Address addr8 = mips.getAddressFactory().getDefaultAddressSpace().getAddress(8);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmMips.resolveTree(r, addr8);
|
||||
dumpResults(results);
|
||||
allValidResults.addAll(getValidResults(results));
|
||||
}
|
||||
|
||||
// I expect at least an encoding like 0xf0306477 (see "testRestore_mips" test)
|
||||
assertFalse(allValidResults.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLw_mips() throws Exception {
|
||||
mips();
|
||||
Collection<AssemblyParseResult> parses = asmMips.parseLine("lw `Q1`,0x0(a0)");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
// Note here, be sure to go past the mips16 code at the start of our fake
|
||||
// program
|
||||
Address addr0 = mips.getAddressFactory().getDefaultAddressSpace().getAddress(0x100);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmMips.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
// Build all 32 encodings (one per target register, Q1) and verify they're in
|
||||
// the results
|
||||
byte[] expected = NumericUtilities.convertStringToBytes("8c800000");
|
||||
for (var i = 1; i < 32; i++) {
|
||||
expected[1] = (byte) (0x80 + i);
|
||||
String expectedHex = NumericUtilities.convertBytesToString(expected, ":");
|
||||
assertTrue("Expected to have " + expectedHex + " as an encoding",
|
||||
allValidEncodings.contains(expectedHex));
|
||||
}
|
||||
|
||||
assertEquals(32, allValidEncodings.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCall_x86() throws Exception {
|
||||
x86();
|
||||
Collection<AssemblyParseResult> parses = asmX86.parseLine("CALL 0x004058f3");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = x86.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX86.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
// These are the two encodings Ghidra suggests when using the "Patch
|
||||
// Instruction" right-click menu option
|
||||
assertEquals(Set.of(
|
||||
"e8:ee:58:40:00",
|
||||
"67:e8:ed:58:40:00"),
|
||||
Set.copyOf(allValidEncodings));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallWildcard_x86() throws Exception {
|
||||
x86();
|
||||
Collection<AssemblyParseResult> parses =
|
||||
asmX86.parseLine("CALL `Q1[0x00400000..0x00400003]`");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidResults = new ArrayList<WildAssemblyResolvedPatterns>();
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = x86.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX86.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidResults.addAll(getValidResults(results));
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
assertEquals(Set.of(
|
||||
"e8:fb:ff:3f:00", "e8:fc:ff:3f:00", "e8:fd:ff:3f:00", "e8:fe:ff:3f:00",
|
||||
"67:e8:fa:ff:3f:00", "67:e8:fb:ff:3f:00", "67:e8:fc:ff:3f:00", "67:e8:fd:ff:3f:00"),
|
||||
Set.copyOf(allValidEncodings));
|
||||
|
||||
WildAssemblyResolvedPatterns call0x00400000 = Unique.assertOne(allValidResults.stream()
|
||||
.filter(r -> r.getInstruction()
|
||||
.equals(AssemblyPatternBlock.fromString("e8:fb:ff:3f:00"))));
|
||||
WildOperandInfo targetInfo = Unique.assertOne(call0x00400000.getOperandInfo());
|
||||
assertEquals("Q1", targetInfo.wildcard());
|
||||
assertEquals(AssemblyPatternBlock.fromString("SS:FF:FF:FF:FF"), targetInfo.location());
|
||||
assertEquals(0x00400000L, targetInfo.choice());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMov_x86() throws Exception {
|
||||
x86();
|
||||
Collection<AssemblyParseResult> parses = asmX86.parseLine("MOV EBP,`Q1/E.P`");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = x86Program.getMinAddress();
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX86.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
// This will blow up if the numeric values for Q1 are still in the results
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
// These are the four encodings Ghidra suggests when using the "Patch
|
||||
// Instruction" right-click menu option
|
||||
assertEquals(Set.of(
|
||||
// These two are when Q1 == "EBP"
|
||||
"89:ed", "8b:ed",
|
||||
// These two are when Q1 == "ESP"
|
||||
"89:e5", "8b:ec"),
|
||||
Set.copyOf(allValidEncodings));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShrd_x86() throws Exception {
|
||||
x86();
|
||||
Collection<AssemblyParseResult> parses = asmX86.parseLine("SHRD EAX,EBX,0x7");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = x86Program.getMinAddress();
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX86.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
// This will blow up if the numeric values for Q1 are still in the results
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
assertEquals(1, allValidEncodings.size());
|
||||
|
||||
var expectedEncodings = List.of("0f:ac:d8:07");
|
||||
for (String expected : expectedEncodings) {
|
||||
assertTrue("Expected to have " + expected + " as an encoding",
|
||||
allValidEncodings.contains(expected));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShrdWildcard_x86() throws Exception {
|
||||
x86();
|
||||
Collection<AssemblyParseResult> parses = asmX86.parseLine("SHRD EAX,EBX,`Q1[..]`");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidResults = new ArrayList<WildAssemblyResolvedPatterns>();
|
||||
Address addr0 = x86Program.getMinAddress();
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX86.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidResults.addAll(getValidResults(results));
|
||||
}
|
||||
|
||||
WildAssemblyResolvedPatterns res = Unique.assertOne(allValidResults);
|
||||
|
||||
assertEquals(AssemblyPatternBlock.fromString("0F:AC:D8"), res.getInstruction());
|
||||
assertEquals(1, res.getOperandInfo().size());
|
||||
WildOperandInfo wild = res.getOperandInfo().iterator().next();
|
||||
assertEquals("Q1", wild.wildcard());
|
||||
assertEquals(
|
||||
"The Q1 operand should be the final byte of this instruction... (e.g. after the 0xD8)",
|
||||
AssemblyPatternBlock.fromString("SS:SS:SS:FF"), wild.location());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallDx_x86() throws Exception {
|
||||
x86();
|
||||
Collection<AssemblyParseResult> parses = asmX86.parseLine("CALL DX");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = x86.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX86.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
// These are the three encodings Ghidra suggests when using the "Patch
|
||||
// Instruction" right-click menu option
|
||||
assertEquals(Set.of("67:66:ff:d2", "66:67:ff:d2", "66:ff:d2"),
|
||||
Set.copyOf(allValidEncodings));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallDx_x8664() throws Exception {
|
||||
x8664();
|
||||
Collection<AssemblyParseResult> parses = asmX8664.parseLine("CALL DX");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = x8664.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX8664.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
// This is the encoding Ghidra suggests when using the "Patch
|
||||
// Instruction" right-click menu option
|
||||
assertEquals(List.of("66:ff:d2"), allValidEncodings);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallDxWild_x8664() throws Exception {
|
||||
x8664();
|
||||
Collection<AssemblyParseResult> parses = asmX8664.parseLine("CALL `Q1/(C|D)X`");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidChoices = new ArrayList<String>();
|
||||
var allValidResults = new ArrayList<AssemblyPatternBlock>();
|
||||
Address addr0 = x8664.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX8664.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidChoices.addAll(getChoiceValues("Q1", results));
|
||||
allValidResults.addAll(
|
||||
getValidResults(results).stream().map(x -> x.getInstruction()).toList());
|
||||
}
|
||||
|
||||
assertEquals(Set.of("CX", "DX"), Set.copyOf(allValidChoices));
|
||||
assertEquals(
|
||||
makeInstructionPatterns("66:ff:d2", "66:ff:d1"),
|
||||
Set.copyOf(allValidResults));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLea_x8664() throws Exception {
|
||||
x8664();
|
||||
Collection<AssemblyParseResult> parses = asmX8664.parseLine("LEA EAX, [ EDX + -0x6c ]");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidResults = new ArrayList<AssemblyPatternBlock>();
|
||||
var allValidEncodings = new ArrayList<String>();
|
||||
Address addr0 = x8664.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX8664.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidResults.addAll(
|
||||
getValidResults(results).stream().map(x -> x.getInstruction()).toList());
|
||||
allValidEncodings.addAll(getInstructionValuesHex(results));
|
||||
}
|
||||
|
||||
assertTrue(allValidResults.size() > 0);
|
||||
assertEquals(Set.of(
|
||||
"67:8d:42:94",
|
||||
"67:8d:44:22:94",
|
||||
"67:8d:44:62:94",
|
||||
"67:8d:44:a2:94",
|
||||
"67:8d:44:e2:94",
|
||||
"67:8d:82:94:ff:ff:ff",
|
||||
"67:8d:84:22:94:ff:ff:ff",
|
||||
"67:8d:84:62:94:ff:ff:ff",
|
||||
"67:8d:84:a2:94:ff:ff:ff",
|
||||
"67:8d:84:e2:94:ff:ff:ff"),
|
||||
Set.copyOf(allValidEncodings));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeaWild_x8664() throws Exception {
|
||||
x8664();
|
||||
Collection<AssemblyParseResult> parses =
|
||||
asmX8664.parseLine("LEA EAX, [ `Q1/EDX` + -0x6c ]");
|
||||
AssemblyParseResult[] allResults = parses.stream()
|
||||
.filter(p -> !p.isError())
|
||||
.toArray(AssemblyParseResult[]::new);
|
||||
|
||||
var allValidChoices = new ArrayList<String>();
|
||||
var allValidResults = new ArrayList<WildAssemblyResolvedPatterns>();
|
||||
Address addr0 = x8664.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
for (AssemblyParseResult r : allResults) {
|
||||
AssemblyResolutionResults results = asmX8664.resolveTree(r, addr0);
|
||||
dumpResults(results);
|
||||
allValidChoices.addAll(getChoiceValues("Q1", results));
|
||||
allValidResults.addAll(getValidResults(results));
|
||||
}
|
||||
|
||||
WildAssemblyResolvedPatterns shortest =
|
||||
Unique.assertOne(allValidResults.stream().filter(r -> r.getInstructionLength() == 4));
|
||||
assertEquals(AssemblyPatternBlock.fromString("67:8d:42:94"), shortest.getInstruction());
|
||||
WildOperandInfo opInfoEDX = Unique.assertOne(
|
||||
shortest.getOperandInfo().stream().filter(x -> x.choice().toString().equals("EDX")));
|
||||
assertEquals(AssemblyPatternBlock.fromString("SS:SS:X[x111]"), opInfoEDX.location());
|
||||
assertEquals(Set.of("EDX"), Set.copyOf(allValidChoices));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user