From b26a6f7c839125040a6ba40eeb21da794a595fc6 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Thu, 12 Oct 2023 07:53:30 -0400 Subject: [PATCH] GP-3926: The MachoLoader can now load binaries with obfuscated segment and section names --- .../app/util/bin/format/macho/MachHeader.java | 28 ++++++++++++++++ .../app/util/bin/format/macho/Section.java | 8 +++++ .../format/macho/commands/SegmentCommand.java | 4 +++ .../app/util/opinion/MachoProgramBuilder.java | 33 ++++++------------- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeader.java index cac31183d5..01b97f98ae 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeader.java @@ -18,11 +18,13 @@ package ghidra.app.util.bin.format.macho; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.function.Function; import ghidra.app.util.bin.*; import ghidra.app.util.bin.format.macho.commands.*; import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache; import ghidra.program.model.data.*; +import ghidra.program.model.mem.Memory; import ghidra.util.DataConverter; import ghidra.util.exception.DuplicateNameException; @@ -186,6 +188,7 @@ public class MachHeader implements StructConverter { LoadCommand lc = LoadCommandFactory.getLoadCommand(_reader, this, splitDyldCache); _commands.add(lc); } + sanitizeSegmentSectionNames(getAllSegments()); _parsed = true; return this; } @@ -211,6 +214,7 @@ public class MachHeader implements StructConverter { _reader.setPointerIndex(_reader.getPointerIndex() + size - 8); } } + sanitizeSegmentSectionNames(segments); return segments; } @@ -383,6 +387,30 @@ public class MachHeader implements StructConverter { return getDescription(); } + /** + * Sanitizes invalid segment/section names so they can be used as memory blocks and program tree + * modules + * + * @param segments A {@link List} of {@link SegmentCommand segments} to sanitize + */ + private void sanitizeSegmentSectionNames(List segments) { + Function invalid = s -> s.isBlank() || !Memory.isValidMemoryBlockName(s); + for (int i = 0; i < segments.size(); i++) { + SegmentCommand segment = segments.get(i); + if (invalid.apply(segment.getSegmentName())) { + segment.setSegmentName("__INVALID.%d".formatted(i)); + } + List
sections = segment.getSections(); + for (int j = 0; j < sections.size(); j++) { + Section section = sections.get(j); + section.setSegmentName(segment.getSegmentName()); + if (invalid.apply(section.getSectionName())) { + section.setSectionName("__invalid.%d".formatted(j)); + } + } + } + } + /** * Creates a new Mach Header byte array * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/Section.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/Section.java index 9ddeb07edc..c4b1c935e0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/Section.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/Section.java @@ -166,10 +166,18 @@ public class Section implements StructConverter { return sectname; } + public void setSectionName(String name) { + this.sectname = name; + } + public String getSegmentName() { return segname; } + public void setSegmentName(String name) { + this.segname = name; + } + public long getAddress() { // Mask off possible chained fixup found in kernelcache section addresses if ((addr & 0xfff000000000L) == 0xfff000000000L) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java index 1493f89cfd..c8efe570af 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java @@ -106,6 +106,10 @@ public class SegmentCommand extends LoadCommand { return segname; } + public void setSegmentName(String name) { + this.segname = name; + } + public long getVMaddress() { // Mask off possible chained fixup found in kernelcache segment addresses if ((vmaddr & 0xfff000000000L) == 0xfff000000000L) { 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 94a6278820..91c7b9bce5 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 @@ -196,32 +196,26 @@ public class MachoProgramBuilder { } // Create memory blocks for segments. - ListIterator it = header.getAllSegments().listIterator(); - while (it.hasNext()) { - int i = it.nextIndex(); - final SegmentCommand segment = it.next(); - + for (SegmentCommand segment : header.getAllSegments()) { if (monitor.isCancelled()) { break; } if (segment.getFileSize() > 0 && (allowZeroAddr || segment.getVMaddress() != 0)) { - String segmentName = segment.getSegmentName(); - if (segmentName.isBlank()) { - segmentName = "SEGMENT." + i; - } - if (createMemoryBlock(segmentName, space.getAddress(segment.getVMaddress()), - segment.getFileOffset(), segment.getFileSize(), segmentName, source, - segment.isRead(), segment.isWrite(), segment.isExecute(), false) == null) { + if (createMemoryBlock(segment.getSegmentName(), + space.getAddress(segment.getVMaddress()), segment.getFileOffset(), + segment.getFileSize(), segment.getSegmentName(), source, segment.isRead(), + segment.isWrite(), segment.isExecute(), false) == null) { log.appendMsg(String.format("Failed to create block: %s 0x%x 0x%x", segment.getSegmentName(), segment.getVMaddress(), segment.getVMsize())); } if (segment.getVMsize() > segment.getFileSize()) { // Pad the remaining address range with uninitialized data - if (createMemoryBlock(segmentName, + if (createMemoryBlock(segment.getSegmentName(), space.getAddress(segment.getVMaddress()).add(segment.getFileSize()), 0, - segment.getVMsize() - segment.getFileSize(), segmentName, source, - segment.isRead(), segment.isWrite(), segment.isExecute(), true) == null) { + segment.getVMsize() - segment.getFileSize(), segment.getSegmentName(), + source, segment.isRead(), segment.isWrite(), segment.isExecute(), + true) == null) { log.appendMsg(String.format("Failed to create block: %s 0x%x 0x%x", segment.getSegmentName(), segment.getVMaddress(), segment.getVMsize())); } @@ -341,11 +335,7 @@ public class MachoProgramBuilder { */ protected void fixupProgramTree() throws Exception { ProgramModule rootModule = listing.getDefaultRootModule(); - ListIterator it = machoHeader.getAllSegments().listIterator(); - while (it.hasNext()) { - int i = it.nextIndex(); - SegmentCommand segment = it.next(); - + for (SegmentCommand segment : machoHeader.getAllSegments()) { if (segment.getVMsize() == 0) { continue; } @@ -361,9 +351,6 @@ public class MachoProgramBuilder { // section fragments, it will represent the parts of the segment that weren't in any // section. String segmentName = segment.getSegmentName(); - if (segmentName.isBlank()) { - segmentName = "SEGMENT." + i; - } String noSectionsName = segmentName + " "; ProgramFragment segmentFragment = null; for (Group group : rootModule.getChildren()) {