From cb949f275a5dbdc17bde7e383bd4ecaf22c7bcf4 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Thu, 20 Mar 2025 14:22:56 -0400 Subject: [PATCH] GP-5515: Faster Rust signature search --- .../core/analysis/rust/RustConstants.java | 11 +++- .../core/analysis/rust/RustUtilities.java | 58 +++++++++---------- .../app/util/opinion/ElfProgramBuilder.java | 5 +- .../app/util/opinion/MachoProgramBuilder.java | 5 +- .../ghidra/app/util/opinion/PeLoader.java | 14 +++-- 5 files changed, 52 insertions(+), 41 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustConstants.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustConstants.java index 968f6f0c62..0f0cff37fc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustConstants.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustConstants.java @@ -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 RUST_SIGNATURES = List.of( + "RUST_BACKTRACE".getBytes(), + "RUST_MIN_STACK".getBytes(), + "/rustc/".getBytes() + ); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustUtilities.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustUtilities.java index 0c44da5b48..8b6234c1d3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustUtilities.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustUtilities.java @@ -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 *

@@ -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 action = + new GenericMatchAction(new AtomicBoolean()) { + @Override + public void apply(Program prog, Address addr, Match match) { + getMatchValue().set(true); + } + }; + MemoryBytePatternSearcher searcher = new MemoryBytePatternSearcher("Rust signatures"); + for (byte[] sig : RustConstants.RUST_SIGNATURES) { + searcher.addPattern(new GenericByteSequencePattern(sig, action)); } - if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_2)) { - return true; - } - 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; - } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java index b8bc49470f..f1f5bec30c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java @@ -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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java index 5a149bf571..262da89e8d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java @@ -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); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java index 7d9a013c08..db20ad09ec 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/PeLoader.java @@ -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 { + 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"); } - catch (IOException e) { - log.appendMsg("Rust error: " + e.getMessage()); - } return CompilerEnum.Rustc; } + catch (CancelledException e) { + // Move on + } + catch (IOException e) { + log.appendMsg("Rust error: " + e.getMessage()); + } // Check for Swift List sectionNames =