GP-5515: Faster Rust signature search

This commit is contained in:
Ryan Kurtz 2025-03-20 14:22:56 -04:00
parent cdd68cc791
commit cb949f275a
5 changed files with 52 additions and 41 deletions

View file

@ -15,15 +15,20 @@
*/
package ghidra.app.plugin.core.analysis.rust;
import java.util.List;
import ghidra.program.model.data.CategoryPath;
public class RustConstants {
public static final CategoryPath RUST_CATEGORYPATH = new CategoryPath("/rust");
public static final byte[] RUST_SIGNATURE_1 = "RUST_BACKTRACE".getBytes();
public static final byte[] RUST_SIGNATURE_2 = "/rustc/".getBytes();
public static final byte[] RUST_SIGNATURE_3 = "RUST_MIN_STACK".getBytes();
public static final String RUST_EXTENSIONS_PATH = "extensions/rust/";
public static final String RUST_EXTENSIONS_UNIX = "unix";
public static final String RUST_EXTENSIONS_WINDOWS = "windows";
public static final String RUST_COMPILER = "rustc";
public static final List<byte[]> RUST_SIGNATURES = List.of(
"RUST_BACKTRACE".getBytes(),
"RUST_MIN_STACK".getBytes(),
"/rustc/".getBytes()
);
}

View file

@ -17,6 +17,7 @@ package ghidra.app.plugin.core.analysis.rust;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import org.xml.sax.SAXException;
@ -25,10 +26,14 @@ import ghidra.app.plugin.processors.sleigh.SleighException;
import ghidra.framework.Application;
import ghidra.framework.store.LockException;
import ghidra.program.database.SpecExtension;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.lang.Processor;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.util.Msg;
import ghidra.util.bytesearch.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import ghidra.xml.XmlParseException;
@ -36,6 +41,7 @@ import ghidra.xml.XmlParseException;
* Rust utility functions
*/
public class RustUtilities {
/**
* Checks if a given {@link MemoryBlock} contains a Rust signature
* <p>
@ -43,26 +49,38 @@ public class RustUtilities {
* If the program is determined to be rust, then the compiler property is set to
* {@link RustConstants#RUST_COMPILER}.
*
* @param program The {@link Program}
* @param block The {@link MemoryBlock} to scan for Rust signatures
* @param monitor The monitor
* @return True if the given {@link MemoryBlock} is not null and contains a Rust signature;
* otherwise, false
* @throws IOException if there was an IO-related error
* @throws CancelledException if the user cancelled the operation
*/
public static boolean isRust(MemoryBlock block) throws IOException {
public static boolean isRust(Program program, MemoryBlock block, TaskMonitor monitor)
throws IOException, CancelledException {
if (block == null) {
return false;
}
byte[] bytes = block.getData().readAllBytes();
if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_1)) {
return true;
// Use a MemoryBytePatternSearch for more efficient byte searching over a list of potential
// byte signatures. The below action sets our supplied boolean to true on a match, which we
// can later query and use as a return value for this method.
GenericMatchAction<AtomicBoolean> action =
new GenericMatchAction<AtomicBoolean>(new AtomicBoolean()) {
@Override
public void apply(Program prog, Address addr, Match match) {
getMatchValue().set(true);
}
if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_2)) {
return true;
};
MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("Rust signatures");
for (byte[] sig : RustConstants.RUST_SIGNATURES) {
searcher.addPattern(new GenericByteSequencePattern<AtomicBoolean>(sig, action));
}
if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_3)) {
return true;
}
return false;
searcher.search(program, new AddressSet(block.getAddressRange()), monitor);
return action.getMatchValue().get();
}
/**
@ -106,22 +124,4 @@ public class RustUtilities {
return extensionCount;
}
private static boolean containsBytes(byte[] data, byte[] bytes) {
for (int i = 0; i < data.length - bytes.length; i++) {
boolean isMatch = true;
for (int j = 0; j < bytes.length; j++) {
if (Byte.compare(data[i + j], bytes[j]) != 0) {
isMatch = false;
break;
}
}
if (isMatch) {
return true;
}
}
return false;
}
}

View file

@ -2464,10 +2464,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
}
}
private void setCompiler(TaskMonitor monitor) {
private void setCompiler(TaskMonitor monitor) throws CancelledException {
// Check for Rust
try {
if (RustUtilities.isRust(memory.getBlock(ElfSectionHeaderConstants.dot_rodata))) {
if (RustUtilities.isRust(program, memory.getBlock(ElfSectionHeaderConstants.dot_rodata),
monitor)) {
program.setCompiler(RustConstants.RUST_COMPILER);
int extensionCount = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_UNIX);

View file

@ -1805,7 +1805,7 @@ public class MachoProgramBuilder {
}
}
protected void setCompiler() {
protected void setCompiler() throws CancelledException {
// Check for Rust
try {
SegmentCommand segment = machoHeader.getSegment(SegmentNames.SEG_TEXT);
@ -1816,7 +1816,8 @@ public class MachoProgramBuilder {
if (section == null) {
return;
}
if (RustUtilities.isRust(memory.getBlock(space.getAddress(section.getAddress())))) {
if (RustUtilities.isRust(program,
memory.getBlock(space.getAddress(section.getAddress())), monitor)) {
program.setCompiler(RustConstants.RUST_COMPILER);
int extensionCount = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_UNIX);

View file

@ -975,17 +975,21 @@ public class PeLoader extends AbstractPeDebugLoader {
DOSHeader dh = pe.getDOSHeader();
// Check for Rust. Program object is required, which may be null.
if (program != null && RustUtilities.isRust(program.getMemory().getBlock(".rdata"))) {
try {
if (program != null && RustUtilities.isRust(program,
program.getMemory().getBlock(".rdata"), monitor)) {
int extensionCount = RustUtilities.addExtensions(program, monitor,
RustConstants.RUST_EXTENSIONS_WINDOWS);
log.appendMsg("Installed " + extensionCount + " Rust cspec extensions");
}
return CompilerEnum.Rustc;
}
catch (CancelledException e) {
// Move on
}
catch (IOException e) {
log.appendMsg("Rust error: " + e.getMessage());
}
return CompilerEnum.Rustc;
}
// Check for Swift
List<String> sectionNames =