From 9dd1a3fb109578b059ec2c2d5e5ee62d1ec1e67a Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Wed, 13 Dec 2023 07:24:43 -0500 Subject: [PATCH] GP-4103: Fixing issue with loading Mach-O Rust binaries --- .../core/analysis/rust/RustConstants.java | 3 +- .../analysis/rust/RustStringAnalyzer.java | 8 +-- .../core/analysis/rust/RustUtilities.java | 58 ++++++++++--------- .../rust/demangler/RustDemangler.java | 3 +- .../datamgr/util/DataTypeArchiveUtility.java | 3 +- .../app/util/opinion/ElfProgramBuilder.java | 6 +- .../app/util/opinion/MachoProgramBuilder.java | 15 +++-- .../ghidra/app/util/opinion/PeLoader.java | 14 ++--- 8 files changed, 61 insertions(+), 49 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 032cb0565a..9743b67f4a 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 @@ -21,7 +21,8 @@ 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 String RUST_EXTENSIONS_PATH = "/extensions/rust/"; + 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"; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustStringAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustStringAnalyzer.java index e8c4877a2b..3fa8f5c566 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustStringAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/RustStringAnalyzer.java @@ -44,7 +44,7 @@ public class RustStringAnalyzer extends AbstractAnalyzer { @Override public boolean canAnalyze(Program program) { String name = program.getCompiler(); - return name.contains("rustc"); + return name.contains(RustConstants.RUST_COMPILER); } @Override @@ -98,9 +98,9 @@ public class RustStringAnalyzer extends AbstractAnalyzer { /** * Get the number of bytes to the next reference, or the max length - * @param program - * @param address - * @param maxLen + * @param program The {@link Program} + * @param address The {@link Address} + * @param maxLen The maximum length * @return maximum length to create the string */ private static int getMaxStringLength(Program program, Address address, int maxLen) { 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 bbb83b573b..add3b4ba8e 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 @@ -25,6 +25,7 @@ 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.lang.Processor; import ghidra.program.model.listing.Program; import ghidra.program.model.mem.MemoryBlock; import ghidra.util.Msg; @@ -36,50 +37,51 @@ import ghidra.xml.XmlParseException; */ public class RustUtilities { /** - * Checks if a given {@link Program} was written in Rust + * Checks if a given {@link MemoryBlock} contains a Rust signature * - * @param program The {@link Program} to check - * @param blockName The name of the {@link MemoryBlock} to scan for Rust signatures - * @return True if the given {@link Program} was written in Rust; otherwise, false + * @param block The {@link MemoryBlock} to scan for Rust signatures + * @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 */ - public static boolean isRust(Program program, String blockName) throws IOException { - MemoryBlock[] blocks = program.getMemory().getBlocks(); - for (MemoryBlock block : blocks) { - if (block.getName().equals(blockName)) { - byte[] bytes = block.getData().readAllBytes(); - if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_1)) { - return true; - } - if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_2)) { - return true; - } - } + public static boolean isRust(MemoryBlock block) throws IOException { + if (block == null) { + return false; + } + byte[] bytes = block.getData().readAllBytes(); + if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_1)) { + return true; + } + if (containsBytes(bytes, RustConstants.RUST_SIGNATURE_2)) { + return true; } return false; } public static int addExtensions(Program program, TaskMonitor monitor, String subPath) throws IOException { - var processor = program.getLanguageCompilerSpecPair().getLanguage().getProcessor(); + Processor processor = program.getLanguageCompilerSpecPair().getLanguage().getProcessor(); ResourceFile module = Application.getModuleDataSubDirectory(processor.toString(), RustConstants.RUST_EXTENSIONS_PATH + subPath); int extensionCount = 0; ResourceFile[] files = module.listFiles(); - for (ResourceFile file : files) { - InputStream stream = file.getInputStream(); - byte[] bytes = stream.readAllBytes(); - String xml = new String(bytes); + if (files != null) { + for (ResourceFile file : files) { + InputStream stream = file.getInputStream(); + byte[] bytes = stream.readAllBytes(); + String xml = new String(bytes); - try { - SpecExtension extension = new SpecExtension(program); - extension.addReplaceCompilerSpecExtension(xml, monitor); - extensionCount += 1; - } - catch (SleighException | SAXException | XmlParseException | LockException e) { - Msg.error(program, "Failed to load load cspec extensions"); + try { + SpecExtension extension = new SpecExtension(program); + extension.addReplaceCompilerSpecExtension(xml, monitor); + extensionCount++; + } + catch (SleighException | SAXException | XmlParseException | LockException e) { + Msg.error(RustUtilities.class, + "Failed to load Rust cspec extension: " + file.getAbsolutePath(), e); + } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/demangler/RustDemangler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/demangler/RustDemangler.java index 8495ce7f8d..c753427d30 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/demangler/RustDemangler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/rust/demangler/RustDemangler.java @@ -15,6 +15,7 @@ */ package ghidra.app.plugin.core.analysis.rust.demangler; +import ghidra.app.plugin.core.analysis.rust.RustConstants; import ghidra.app.util.demangler.*; import ghidra.program.model.listing.Program; @@ -35,7 +36,7 @@ public class RustDemangler implements Demangler { @Override public boolean canDemangle(Program program) { String name = program.getCompiler(); - return name != null && name.contains("rustc"); + return name != null && name.contains(RustConstants.RUST_COMPILER); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeArchiveUtility.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeArchiveUtility.java index e0f7bc49fe..f339468d68 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeArchiveUtility.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeArchiveUtility.java @@ -18,6 +18,7 @@ package ghidra.app.plugin.core.datamgr.util; import java.util.*; import generic.jar.ResourceFile; +import ghidra.app.plugin.core.analysis.rust.RustConstants; import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.util.opinion.*; import ghidra.framework.Application; @@ -144,7 +145,7 @@ public class DataTypeArchiveUtility { list.add("generic_clib"); } - if (program.getCompiler().contains("rustc")) { + if (program.getCompiler().contains(RustConstants.RUST_COMPILER)) { list.add("rust-common"); } 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 15f4172802..bec7d0d76e 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 @@ -2443,15 +2443,15 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { private void setCompiler(TaskMonitor monitor) { // Check for Rust try { - if (RustUtilities.isRust(program, ElfSectionHeaderConstants.dot_rodata)) { + if (RustUtilities.isRust(memory.getBlock(ElfSectionHeaderConstants.dot_rodata))) { + program.setCompiler(RustConstants.RUST_COMPILER); int extensionCount = RustUtilities.addExtensions(program, monitor, RustConstants.RUST_EXTENSIONS_UNIX); log.appendMsg("Installed " + extensionCount + " Rust cspec extensions"); - program.setCompiler("rustc"); } } catch (IOException e) { - log.appendException(e); + log.appendMsg("Rust error: " + e.getMessage()); } } 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 a34dc04ab4..5561843494 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 @@ -1694,16 +1694,23 @@ public class MachoProgramBuilder { protected void setCompiler() { // Check for Rust try { - if (RustUtilities.isRust(program, - SegmentNames.SEG_TEXT + "." + SectionNames.TEXT_CONST)) { + SegmentCommand segment = machoHeader.getSegment(SegmentNames.SEG_TEXT); + if (segment == null) { + return; + } + Section section = segment.getSectionByName(SectionNames.TEXT_CONST); + if (section == null) { + return; + } + if (RustUtilities.isRust(memory.getBlock(space.getAddress(section.getAddress())))) { + program.setCompiler(RustConstants.RUST_COMPILER); int extensionCount = RustUtilities.addExtensions(program, monitor, RustConstants.RUST_EXTENSIONS_UNIX); log.appendMsg("Installed " + extensionCount + " Rust cspec extensions"); - program.setCompiler("rustc"); } } catch (IOException e) { - log.appendException(e); + log.appendMsg("Rust error: " + e.getMessage()); } } 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 b23f1c64ca..5643e80aa8 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 @@ -902,7 +902,7 @@ public class PeLoader extends AbstractPeDebugLoader { BorlandCpp("borland:c++", "borlandcpp"), BorlandUnk("borland:unknown", "borlandcpp"), CLI("cli", "cli"), - Rustc("rustc", "rustc"), + Rustc(RustConstants.RUST_COMPILER, RustConstants.RUST_COMPILER), GOLANG("golang", "golang"), Unknown("unknown", "unknown"), @@ -958,16 +958,16 @@ public class PeLoader extends AbstractPeDebugLoader { DOSHeader dh = pe.getDOSHeader(); // Check for Rust. Program object is required, which may be null. - try { - if (program != null && RustUtilities.isRust(program, ".rdata")) { + if (program != null && RustUtilities.isRust(program.getMemory().getBlock(".rdata"))) { + try { int extensionCount = RustUtilities.addExtensions(program, monitor, RustConstants.RUST_EXTENSIONS_WINDOWS); log.appendMsg("Installed " + extensionCount + " Rust cspec extensions"); - return CompilerEnum.Rustc; } - } - catch (IOException e) { - log.appendException(e); + catch (IOException e) { + log.appendMsg("Rust error: " + e.getMessage()); + } + return CompilerEnum.Rustc; } // Check for managed code (.NET)