From 3f130fc4e111108d2511e8a5349a107939a71534 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Wed, 16 Aug 2023 12:34:15 -0400 Subject: [PATCH] GP-0: Small Mach-O refactorings --- .../app/util/bin/format/macho/Section.java | 13 +++++++ .../format/macho/commands/SegmentCommand.java | 11 ++++++ .../format/macho/dyld/DyldCacheHeader.java | 2 +- .../dyld/DyldCacheMappingAndSlideInfo.java | 27 ++++++++++++-- .../app/util/opinion/MachoProgramBuilder.java | 16 ++++----- ...Extractor.java => DyldCacheExtractor.java} | 36 +++++++++---------- .../ios/dyldcache/DyldCacheFileSystem.java | 4 +-- 7 files changed, 76 insertions(+), 33 deletions(-) rename Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/{DyldCacheDylibExtractor.java => DyldCacheExtractor.java} (97%) 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 02d9e6a41d..9ddeb07edc 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 @@ -146,6 +146,8 @@ public class Section implements StructConverter { /** * Returns an input stream to underlying bytes of this section. + * + * @param header The Mach-O header * @return an input stream to underlying bytes of this section * @throws IOException if an i/o error occurs. */ @@ -220,6 +222,17 @@ public class Section implements StructConverter { return reserved3; } + /** + * Returns true if the section contains the given address + * + * @param address The address to check + * @return True if the section contains the given address; otherwise, false + */ + public boolean contains(long address) { + return Long.compareUnsigned(address, addr) >= 0 && + Long.compareUnsigned(address, addr + size) < 0; + } + @Override public DataType toDataType() throws DuplicateNameException, IOException { StructureDataType struct = new StructureDataType("section", 0); 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 cd3149b356..85e64eee56 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 @@ -200,6 +200,17 @@ public class SegmentCommand extends LoadCommand { return (flags & SegmentConstants.FLAG_APPLE_PROTECTED) != 0; } + /** + * Returns true if the segment contains the given address + * + * @param addr The address to check + * @return True if the segment contains the given address; otherwise, false + */ + public boolean contains(long addr) { + return Long.compareUnsigned(addr, vmaddr) >= 0 && + Long.compareUnsigned(addr, vmaddr + vmsize) < 0; + } + @Override public DataType toDataType() throws DuplicateNameException, IOException { StructureDataType struct = new StructureDataType(getCommandName(), 0); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java index 1560ddb9d8..561cba617c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java @@ -652,7 +652,7 @@ public class DyldCacheHeader implements StructConverter { addHeaderField(struct, QWORD, "localSymbolsOffset","file offset of where local symbols are stored"); addHeaderField(struct, QWORD, "localSymbolsSize", "size of local symbols information"); addHeaderField(struct, new ArrayDataType(BYTE, 16, 1), "uuid","unique value for each shared cache file"); - addHeaderField(struct, QWORD, "cacheType", "0 for development, 1 for production"); + addHeaderField(struct, QWORD, "cacheType", "0 for development, 1 for production, 2 for multi-cache"); addHeaderField(struct, DWORD, "branchPoolsOffset","file offset to table of uint64_t pool addresses"); addHeaderField(struct, DWORD, "branchPoolsCount", "number of uint64_t entries"); if (hasAccelerateInfo()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java index 9ff51c8186..5da7046c96 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java @@ -32,9 +32,11 @@ import ghidra.util.exception.DuplicateNameException; @SuppressWarnings("unused") public class DyldCacheMappingAndSlideInfo implements StructConverter { - public static long DYLD_CACHE_MAPPING_AUTH_DATA = 1 << 3L; - public static long DYLD_CACHE_MAPPING_DIRTY_DATA = 1 << 1L; - public static long DYLD_CACHE_MAPPING_CONST_DATA = 1 << 2L; + public static long DYLD_CACHE_MAPPING_AUTH_DATA = 0x1; + public static long DYLD_CACHE_MAPPING_DIRTY_DATA = 0x2; + public static long DYLD_CACHE_MAPPING_CONST_DATA = 0x4; + public static long DYLD_CACHE_MAPPING_TEXT_STUBS = 0x8; + public static long DYLD_CACHE_DYNAMIC_CONFIG_DATA = 0x10; private long address; private long size; @@ -128,6 +130,14 @@ public class DyldCacheMappingAndSlideInfo implements StructConverter { return (flags & DYLD_CACHE_MAPPING_CONST_DATA) != 0; } + public boolean isTextStubs() { + return (flags & DYLD_CACHE_MAPPING_TEXT_STUBS) != 0; + } + + public boolean isConfigData() { + return (flags & DYLD_CACHE_DYNAMIC_CONFIG_DATA) != 0; + } + /** * Returns true if the initial protections include READ. * @@ -155,6 +165,17 @@ public class DyldCacheMappingAndSlideInfo implements StructConverter { return (initProt & SegmentConstants.PROTECTION_X) != 0; } + /** + * Returns true if the mapping contains the given address + * + * @param addr The address to check + * @return True if the mapping contains the given address; otherwise, false + */ + public boolean contains(long addr) { + return Long.compareUnsigned(addr, address) >= 0 && + Long.compareUnsigned(addr, address + size) < 0; + } + @Override public DataType toDataType() throws DuplicateNameException, IOException { StructureDataType struct = new StructureDataType("dyld_cache_mapping_and_slide_info", 0); 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 7408285259..9192cac750 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 @@ -633,7 +633,7 @@ public class MachoProgramBuilder { int symbolIndex = indirectSymbols[i]; NList symbol = symbolTableCommand.getSymbolAt(symbolIndex); if (symbol != null) { - String name = generateValidName(symbol.getString()); + String name = SymbolUtilities.replaceInvalidChars(symbol.getString(), true); if (name != null && name.length() > 0) { try { program.getSymbolTable() @@ -701,7 +701,7 @@ public class MachoProgramBuilder { return; } try { - String name = generateValidName(symbol.getString()); + String name = SymbolUtilities.replaceInvalidChars(symbol.getString(), true); if (name != null && name.length() > 0) { program.getSymbolTable().createLabel(start, name, SourceType.IMPORTED); } @@ -751,7 +751,7 @@ public class MachoProgramBuilder { } for (NList symbol : absoluteSymbols) { try { - String name = generateValidName(symbol.getString()); + String name = SymbolUtilities.replaceInvalidChars(symbol.getString(), true); if (name != null && name.length() > 0) { program.getSymbolTable().createLabel(start, name, SourceType.IMPORTED); } @@ -1200,7 +1200,9 @@ public class MachoProgramBuilder { } } - program.getSymbolTable().createExternalLibrary(Library.UNKNOWN, SourceType.IMPORTED); + if (program.getSymbolTable().getLibrarySymbol(Library.UNKNOWN) == null) { + program.getSymbolTable().createExternalLibrary(Library.UNKNOWN, SourceType.IMPORTED); + } } /** @@ -1485,7 +1487,7 @@ public class MachoProgramBuilder { } } - private Namespace createNamespace(String namespaceName) { + protected Namespace createNamespace(String namespaceName) { try { return program.getSymbolTable() .createNameSpace(program.getGlobalNamespace(), namespaceName, @@ -1503,10 +1505,6 @@ public class MachoProgramBuilder { return program.getGlobalNamespace(); } - private String generateValidName(String name) { - return SymbolUtilities.replaceInvalidChars(name, true); - } - /** * create a one-byte function, so that when the code is analyzed, * it will be disassembled, and the function created with the correct body. diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheDylibExtractor.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheExtractor.java similarity index 97% rename from Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheDylibExtractor.java rename to Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheExtractor.java index aa1562bdf7..7e4e08b457 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheDylibExtractor.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheExtractor.java @@ -32,9 +32,9 @@ import ghidra.util.exception.NotFoundException; import ghidra.util.task.TaskMonitor; /** - * A class for extracting DYLIB files from a {@link DyldCacheFileSystem} + * A class for extracting components from a {@link DyldCacheFileSystem} */ -public class DyldCacheDylibExtractor { +public class DyldCacheExtractor { /** * Gets an {@link ByteProvider} that reads a DYLIB from a {@link DyldCacheFileSystem}. The @@ -61,6 +61,22 @@ public class DyldCacheDylibExtractor { return packedSegments.getByteProvider(fsrl); } + /** + * Converts the given value to a byte array + * + * @param value The value to convert to a byte array + * @param size The number of bytes to convert (must be 4 or 8) + * @return The value as a byte array of the given size + * @throws IllegalArgumentException if size is an unsupported value + */ + private static byte[] toBytes(long value, int size) throws IllegalArgumentException { + if (size != 4 && size != 8) { + throw new IllegalArgumentException("Size must be 4 or 8 (got " + size + ")"); + } + DataConverter converter = LittleEndianDataConverter.INSTANCE; + return size == 8 ? converter.getBytes(value) : converter.getBytes((int) value); + } + /** * Gets a {@link Map} of {DyldCacheSlideInfoCommon}s to their corresponding * {@link DyldCacheSlideFixup}s @@ -619,22 +635,6 @@ public class DyldCacheDylibExtractor { "Failed to find provider for segment: " + segment.getSegmentName()); } - /** - * Converts the given value to a byte array - * - * @param value The value to convert to a byte array - * @param size The number of bytes to convert (must be 4 or 8) - * @return The value as a byte array of the given size - * @throws IllegalArgumentException if size is an unsupported value - */ - private byte[] toBytes(long value, int size) throws IllegalArgumentException { - if (size != 4 && size != 8) { - throw new IllegalArgumentException("Size must be 4 or 8 (got " + size + ")"); - } - DataConverter converter = LittleEndianDataConverter.INSTANCE; - return size == 8 ? converter.getBytes(value) : converter.getBytes((int) value); - } - /** * Fixes-up the slide pointers * diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java index a28cb26928..a25548e204 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java @@ -70,7 +70,7 @@ public class DyldCacheFileSystem extends GFileSystemBase { addr - splitDyldCache.getDyldCacheHeader(index).getBaseAddress(); if (slideFixupMap == null) { - slideFixupMap = DyldCacheDylibExtractor.getSlideFixups(splitDyldCache, monitor); + slideFixupMap = DyldCacheExtractor.getSlideFixups(splitDyldCache, monitor); } if (!parsedLocalSymbols) { @@ -82,7 +82,7 @@ public class DyldCacheFileSystem extends GFileSystemBase { } try { - return DyldCacheDylibExtractor.extractDylib(machHeaderStartIndexInProvider, + return DyldCacheExtractor.extractDylib(machHeaderStartIndexInProvider, splitDyldCache, index, slideFixupMap, file.getFSRL(), monitor); } catch (MachException e) {