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 b17ca07648..6aaf2a2df3 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 @@ -135,9 +135,9 @@ public class MachHeader implements StructConverter { _commandIndex = _reader.getPointerIndex(); } - public void parse() throws IOException, MachException { + public MachHeader parse() throws IOException, MachException { if (_parsed) { - return; + return this; } for (int i = 0; i < nCmds; ++i) { _reader.setPointerIndex(_commandIndex); @@ -146,6 +146,7 @@ public class MachHeader implements StructConverter { _commandIndex += lc.getCommandSize(); } _parsed = true; + return this; } public int getMagic() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeaderFlags.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeaderFlags.java index 3101baa585..048c1fa5d6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeaderFlags.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/MachHeaderFlags.java @@ -21,161 +21,192 @@ import java.util.List; /** * Constants for the flags field of the mach_header + * + * @see mach-o/loader.h */ public final class MachHeaderFlags { /** - * the object file has no undefined references. + * the object file has no undefined references */ - public final static int MH_NOUNDEFS = 0x1; + public final static int MH_NOUNDEFS = 0x1; + /** - * the object file is the output of an incremental - * link against a base file and can't be link - * edited again. + * the object file is the output of an incremental link against a base file and + * can't be link edited again. */ - public final static int MH_INCRLINK = 0x2; + public final static int MH_INCRLINK = 0x2; + /** - * the object file is input for the dynamic - * linker and can't be staticly link edited again. + * the object file is input for the dynamic linker and can't be staticly link + * edited again */ - public final static int MH_DYLDLINK = 0x4; + public final static int MH_DYLDLINK = 0x4; + /** - * the object file's undefined references - * are bound by the dynamic linker when loaded. + * the object file's undefined references are bound by the dynamic linker when + * loaded */ - public final static int MH_BINDATLOAD = 0x8; + public final static int MH_BINDATLOAD = 0x8; + /** - * the file has its dynamic undefined references - * prebound. + * the file has its dynamic undefined references prebound */ - public final static int MH_PREBOUND = 0x10; + public final static int MH_PREBOUND = 0x10; + /** - * the file has its read-only and read-write - * segments split. + * the file has its read-only and read-write segments split */ - public final static int MH_SPLIT_SEGS = 0x20; + public final static int MH_SPLIT_SEGS = 0x20; + /** - * the shared library init routine is to be - * run lazily via catching memory faults to its - * writeable segments (obsolete). + * the shared library init routine is to be run lazily via catching memory faults to its + * writeable segments (obsolete) */ - public final static int MH_LAZY_INIT = 0x40; + public final static int MH_LAZY_INIT = 0x40; + /** - * the image is using two-level name space bindings. + * the image is using two-level name space bindings */ - public final static int MH_TWOLEVEL = 0x80; + public final static int MH_TWOLEVEL = 0x80; + /** - * the executable is forcing all images to use - * flat name space bindings. + * the executable is forcing all images to use flat name space bindings */ - public final static int MH_FORCE_FLAT = 0x100; + public final static int MH_FORCE_FLAT = 0x100; + /** - * this umbrella guarantees no multiple defintions - * of symbols in its sub-images so the two-level - * namespace hints can always be used. - * */ - public final static int MH_NOMULTIDEFS = 0x200; - /** - * do not have dyld notify the prebinding - * agent about this executable. + * this umbrella guarantees no multiple definitions of symbols in its sub-images so the + * two-level namespace hints can always be used */ - public final static int MH_NOFIXPREBINDING = 0x400; + public final static int MH_NOMULTIDEFS = 0x200; + /** - * the binary is not prebound but can have - * its prebinding redone. only used when - * MH_PREBOUND is not set. + * do not have dyld notify the prebinding agent about this executable */ - public final static int MH_PREBINDABLE = 0x800; + public final static int MH_NOFIXPREBINDING = 0x400; + /** - * indicates that this binary binds to all - * two-level namespace modules of its dependent - * libraries. only used when MH_PREBINDABLE and - * MH_TWOLEVEL are both set. + * the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is + * not set */ - public final static int MH_ALLMODSBOUND = 0x1000; + public final static int MH_PREBINDABLE = 0x800; + /** - * safe to divide up the sections into - * sub-sections via symbols for dead code - * stripping. + * indicates that this binary binds to all two-level namespace modules of its dependent + * libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. */ - public final static int MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000; + public final static int MH_ALLMODSBOUND = 0x1000; + + /** + * safe to divide up the sections into sub-sections via symbols for dead code stripping + */ + public final static int MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000; + /** * the binary has been canonicalized via the unprebind operation. */ - public final static int MH_CANONICAL = 0x4000; + public final static int MH_CANONICAL = 0x4000; + /** * the final linked image contains external weak symbols. */ - public final static int MH_WEAK_DEFINES = 0x8000; + public final static int MH_WEAK_DEFINES = 0x8000; + /** * the final linked image uses weak symbols. */ - public final static int MH_BINDS_TO_WEAK = 0x10000; + public final static int MH_BINDS_TO_WEAK = 0x10000; + /** - * when this bit is set, all stacks in the task - * will be given stack execution privilege. - * only used in MH_EXECUTE filetypes. + * When this bit is set, all stacks in the task will be given stack execution privilege. only + * used in MH_EXECUTE filetypes. */ - public final static int MH_ALLOW_STACK_EXECUTION = 0x20000; + public final static int MH_ALLOW_STACK_EXECUTION = 0x20000; + /** - * When this bit is set, the binary declares it is safe for use in - * processes with uid zero + * When this bit is set, the binary declares it is safe for use in processes with uid zero */ - public final static int MH_ROOT_SAFE = 0x40000; + public final static int MH_ROOT_SAFE = 0x40000; + /** - * When this bit is set, the binary declares it is safe for use in - * processes when issetugid() is true + * When this bit is set, the binary declares it is safe for use in processes when issetugid() + * is true */ - public final static int MH_SETUID_SAFE = 0x80000; + public final static int MH_SETUID_SAFE = 0x80000; + /** - * When this bit is set on a dylib, the static linker does not need to - * examine dependent dylibs to see if any are re-exported + * When this bit is set on a dylib, the static linker does not need to examine dependent dylibs + * to see if any are re-exported */ - public final static int MH_NO_REEXPORTED_DYLIBS = 0x100000; + public final static int MH_NO_REEXPORTED_DYLIBS = 0x100000; + /** - * When this bit is set, the OS will load the main executable at a - * random address. Only used in MH_EXECUTE filetypes. + * When this bit is set, the OS will load the main executable at a random address. Only used in + * MH_EXECUTE filetypes. */ - public final static int MH_PIE = 0x200000; + public final static int MH_PIE = 0x200000; + /** - * Only for use on dylibs. - * When linking against a dylib that - * has this bit set, the static linker will automatically not create a - * LC_LOAD_DYLIB load command to the - * dylib if no symbols are being referenced from the dylib. + * Only for use on dylibs. When linking against a dylib that has this bit set, the static linker + * will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are + * being referenced from the dylib. */ - public final static int MH_DEAD_STRIPPABLE_DYLIB = 0x400000; + public final static int MH_DEAD_STRIPPABLE_DYLIB = 0x400000; + /** * Contains a section of type S_THREAD_LOCAL_VARIABLES. */ - public final static int MH_HAS_TLV_DESCRIPTORS = 0x800000; + public final static int MH_HAS_TLV_DESCRIPTORS = 0x800000; + /** - * When this bit is set, the OS will run the main executable - * with a non-executable heap even on platforms ( e.g., i386 ) - * that don't require it. - * Only used in MH_EXECUTE file types. + * When this bit is set, the OS will run the main executable with a non-executable heap even on + * platforms ( e.g., i386 ) that don't require it. Only used in MH_EXECUTE file types. */ - public final static int MH_NO_HEAP_EXECUTION = 0x1000000; + public final static int MH_NO_HEAP_EXECUTION = 0x1000000; + /** - * + * The code was linked for use in an application extension. */ - public final static int MH_APP_EXTENSION_SAFE = 0x2000000; + public final static int MH_APP_EXTENSION_SAFE = 0x2000000; + + /** + * The external symbols listed in the nlist symbol table do not include all the symbols listed + * in the dyld info. + */ + public final static int MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; + + /** + * Allow LC_MIN_VERSION_MACOS and LC_BUILD_VERSION load commands with the platforms macOS, + * iOSMac, iOSSimulator, tvOSSimulator and watchOSSimulator. + */ + public final static int MH_SIM_SUPPORT = 0x08000000; + + /** + * Only for use on dylibs. When this bit is set, the dylib is part of the dyld shared cache, + * rather than loose in the filesystem. + */ + public final static int MH_DYLIB_IN_CACHE = 0x80000000; /** * Returns string representation of the flag values. + * + * @param flags the flags value to get the string representation of. + * @return a string representation of the flag values. */ public final static List getFlags(int flags) { - List list = new ArrayList(); - Field [] fields = MachHeaderFlags.class.getDeclaredFields(); + List list = new ArrayList<>(); + Field[] fields = MachHeaderFlags.class.getDeclaredFields(); for (Field field : fields) { if (field.getName().startsWith("MH_")) { try { - Integer value = (Integer)field.get(null); + Integer value = (Integer) field.get(null); if ((flags & value) != 0) { list.add(field.getName().substring("MH_".length())); } } catch (Exception e) { + // do nothing } } } 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/DyldCacheDylibExtractor.java index e71c07c42e..0be1c110b8 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/DyldCacheDylibExtractor.java @@ -45,192 +45,73 @@ public class DyldCacheDylibExtractor { * @param fsrl {@link FSRL} to assign to the resulting {@link ByteProvider} * @param monitor {@link TaskMonitor} * @return {@link ByteProvider} containing the bytes of the DYLIB - * @throws IOException If there was an IO-related issue with extracting the DYLIB * @throws MachException If there was an error parsing the DYLIB headers + * @throws IOException If there was an IO-related issue with extracting the DYLIB */ public static ByteProvider extractDylib(long dylibOffset, SplitDyldCache splitDyldCache, int index, FSRL fsrl, TaskMonitor monitor) throws IOException, MachException { - // Make sure Mach-O header is valid - MachHeader dylibHeader = - new MachHeader(splitDyldCache.getProvider(index), dylibOffset, false); - dylibHeader.parse(); + PackedSegments packedSegments = + new PackedSegments(dylibOffset, splitDyldCache, index, monitor); - // Pack the DYLIB - PackedDylib packedDylib = new PackedDylib(dylibHeader, dylibOffset, splitDyldCache, index); - - // TODO: Fixup pointer chains - - // Fixup indices, offsets, etc in the packed DYLIB's header - for (LoadCommand cmd : dylibHeader.getLoadCommands()) { - if (monitor.isCancelled()) { - break; - } - switch (cmd.getCommandType()) { - case LoadCommandTypes.LC_SEGMENT: - fixupSegment((SegmentCommand) cmd, packedDylib, false, monitor); - break; - case LoadCommandTypes.LC_SEGMENT_64: - fixupSegment((SegmentCommand) cmd, packedDylib, true, monitor); - break; - case LoadCommandTypes.LC_SYMTAB: - fixupSymbolTable((SymbolTableCommand) cmd, packedDylib); - break; - case LoadCommandTypes.LC_DYSYMTAB: - fixupDynamicSymbolTable((DynamicSymbolTableCommand) cmd, packedDylib); - break; - case LoadCommandTypes.LC_DYLD_INFO: - case LoadCommandTypes.LC_DYLD_INFO_ONLY: - fixupDyldInfo((DyldInfoCommand) cmd, packedDylib); - break; - } - } - - return packedDylib.getByteProvider(fsrl); + return packedSegments.getByteProvider(fsrl); } /** - * Fixes-up the old DYLD file offsets in the given segment so they are correct for the newly - * packed DYLIB - * - * @param cmd The segment to fix-up - * @param packedDylib The packed DYLIB - * @param is64bit True if the segment is 64-bit; false if 32-bit - * @param monitor A cancellable {@link TaskMonitor} - * @throws IOException If there was an IO-related issue performing the fix-up + * A packed DYLIB that was once living inside of a DYLD shared cache. The DYLIB is said to be + * packed because its segment file bytes, which were not adjacent in its containing DYLD, are + * now adjacent in its new array. */ - private static void fixupSegment(SegmentCommand cmd, PackedDylib packedDylib, boolean is64bit, - TaskMonitor monitor) throws IOException { - if (cmd.getFileOffset() > 0 && cmd.getFileSize() > 0) { - packedDylib.fixup(cmd.getStartIndex() + (is64bit ? 0x28 : 0x20), is64bit ? 8 : 4); - } - long sectionStartIndex = cmd.getStartIndex() + (is64bit ? 0x48 : 0x38); - for (Section section : cmd.getSections()) { - if (monitor.isCancelled()) { - break; - } - if (section.getOffset() > 0 && section.getSize() > 0) { - packedDylib.fixup(sectionStartIndex + (is64bit ? 0x30 : 0x28), 4); - } - if (section.getRelocationOffset() > 0) { - packedDylib.fixup(sectionStartIndex + (is64bit ? 0x38 : 0x30), 4); - } - sectionStartIndex += is64bit ? 0x50 : 0x44; - } - } - - /** - * Fixes-up the old DYLD file offsets in the given symbol table so they are correct for the - * newly packed DYLIB - * - * @param cmd The symbol table to fix-up - * @param packedDylib The packed DYLIB - * @throws IOException If there was an IO-related issue performing the fix-up - */ - private static void fixupSymbolTable(SymbolTableCommand cmd, PackedDylib packedDylib) - throws IOException { - if (cmd.getSymbolOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x8, 4); - } - if (cmd.getStringTableOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x10, 4); - } - } - - /** - * Fixes-up the old DYLD file offsets in the given dynamic symbol table so they are correct for - * the newly packed DYLIB - * - * @param cmd The dynamic symbol table to fix-up - * @param packedDylib The packed DYLIB - * @throws IOException If there was an IO-related issue performing the fix-up - */ - private static void fixupDynamicSymbolTable(DynamicSymbolTableCommand cmd, - PackedDylib packedDylib) throws IOException { - if (cmd.getTableOfContentsOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x20, 4); - } - if (cmd.getModuleTableOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x28, 4); - } - if (cmd.getReferencedSymbolTableOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x30, 4); - } - if (cmd.getIndirectSymbolTableOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x38, 4); - } - if (cmd.getExternalRelocationOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x40, 4); - } - if (cmd.getLocalRelocationOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x48, 4); - } - } - - /** - * Fixes-up the old DYLD file offsets in the given DYLD Info command so they are correct for the - * newly packed DYLIB - * - * @param cmd The DYLD Info command to fix-up - * @param packedDylib The packed DYLIB - * @throws IOException If there was an IO-related issue performing the fix-up - */ - private static void fixupDyldInfo(DyldInfoCommand cmd, PackedDylib packedDylib) - throws IOException { - if (cmd.getRebaseOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x8, 4); - } - if (cmd.getBindOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x10, 4); - } - if (cmd.getWeakBindOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x18, 4); - } - if (cmd.getLazyBindOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x20, 4); - } - if (cmd.getExportOffset() > 0) { - packedDylib.fixup(cmd.getStartIndex() + 0x28, 4); - } - } - - /** - * A packed DYLIB that was once living inside of a DYLD. The DYLIB is said to be packed - * because its segment file bytes, which were not adjacent in its containing DYLD, are now - * adjacent in its new array. - */ - private static class PackedDylib { + private static class PackedSegments { private BinaryReader reader; - private Map packedStarts; + private MachHeader header; + private Map packedSegmentStarts = new HashMap<>(); + private Map packedSegmentAdjustments = new HashMap<>(); private byte[] packed; + private TaskMonitor monitor; /** - * Creates a new {@link PackedDylib} object + * Creates a new {@link PackedSegments} object * - * @param dylibHeader The DYLD's DYLIB's Mach-O header * @param dylibOffset The offset of the DYLIB in the given provider * @param splitDyldCache The {@link SplitDyldCache} * @param index The DYLIB's {@link SplitDyldCache} index + * @param monitor {@link TaskMonitor} + * @throws MachException If there was an error parsing the DYLIB headers * @throws IOException If there was an IO-related error */ - public PackedDylib(MachHeader dylibHeader, long dylibOffset, SplitDyldCache splitDyldCache, - int index) throws IOException { - reader = new BinaryReader(splitDyldCache.getProvider(index), true); - packedStarts = new HashMap<>(); - int size = 0; - for (SegmentCommand segment : dylibHeader.getAllSegments()) { - packedStarts.put(segment, size); - size += segment.getFileSize(); + public PackedSegments(long dylibOffset, SplitDyldCache splitDyldCache, int index, + TaskMonitor monitor) throws MachException, IOException { + ByteProvider provider = splitDyldCache.getProvider(index); + this.reader = new BinaryReader(provider, true); + this.header = new MachHeader(provider, dylibOffset, false).parse(); + this.monitor = monitor; - // Some older DYLDs use relative file offsets for only their __TEXT segment. - // Adjust these segments to be consistent with all the other segments. - if (segment.getFileOffset() == 0) { + // Keep track of each segment's file offset in the DYLD cache. + // Also keep a running total of each segment's size so we know how big to make our + // packed array. + int packedSize = 0; + for (SegmentCommand segment : header.getAllSegments()) { + packedSegmentStarts.put(segment, packedSize); + packedSize += segment.getFileSize(); + + // Some older DYLDs use a file offset of 0 for their __TEXT segment, despite being + // in the middle of the cache and despite the other segments using absolute cache + // file offsets. Adjust these segments to be consistent with all the other segments, + // and store their adjustment values so we can later work with them as absolute + // cache file offsets. + if (segment.getSegmentName().equals(SegmentNames.SEG_TEXT) && + segment.getFileOffset() == 0) { segment.setFileOffset(dylibOffset); + packedSegmentAdjustments.put(segment, (int)dylibOffset); } } - packed = new byte[size]; - for (SegmentCommand segment : dylibHeader.getAllSegments()) { + + packed = new byte[packedSize]; + + // Copy each segment into the packed array (leaving no gaps) + for (SegmentCommand segment : header.getAllSegments()) { long segmentSize = segment.getFileSize(); ByteProvider segmentProvider = getSegmentProvider(segment, splitDyldCache); if (segment.getFileOffset() + segmentSize > segmentProvider.length()) { @@ -239,14 +120,71 @@ public class DyldCacheDylibExtractor { " segment extends beyond end of file. Truncating..."); } byte[] bytes = segmentProvider.readBytes(segment.getFileOffset(), segmentSize); - System.arraycopy(bytes, 0, packed, packedStarts.get(segment), bytes.length); + System.arraycopy(bytes, 0, packed, packedSegmentStarts.get(segment), bytes.length); } + + // Fixup various fields in the packed array + fixupMachHeader(); + fixupLoadCommands(); + + // TODO: Fixup pointer chains } - ByteProvider getByteProvider(FSRL fsrl) { + /** + * Gets a {@link ByteProvider} for this {@link PackedSegments} object + * + * @param fsrl FSRL identity of the file + * @return A {@link ByteProvider} for this {@link PackedSegments} object + */ + public ByteProvider getByteProvider(FSRL fsrl) { return new ByteArrayProvider(packed, fsrl); } + /** + * Sets the bytes at the given packed DYLIB offset to the given value + * + * @param packedOffset The packed DYLIB offset to fix-up + * @param value The new value + * @param size The number of bytes to set (must be 4 or 8) + * @throws IllegalArgumentException if size is an unsupported value + */ + public void set(int packedOffset, long value, int size) throws IllegalArgumentException { + if (size != 4 && size != 8) { + throw new IllegalArgumentException("Size must be 4 or 8 (got " + size + ")"); + } + byte[] newBytes = toBytes(value, size); + System.arraycopy(newBytes, 0, packed, packedOffset, newBytes.length); + } + + /** + * Fixes up the bytes at the given DYLD file offset to map to the correct offset in the + * packed DYLIB + * + * @param fileOffset The DYLD file offset to fix-up + * @param adjustment An value to add to the bytes at the given DYLD file offset prior to + * looking them up in the packed DYLIB + * @param size The number of bytes to fix-up (must be 4 or 8) + * @throws IOException If there was an IO-related error + * @throws IllegalArgumentException if size is an unsupported value + */ + public void fixup(long fileOffset, long adjustment, int size) + throws IOException, IllegalArgumentException { + if (size != 4 && size != 8) { + throw new IllegalArgumentException("Size must be 4 or 8 (got " + size + ")"); + } + long value = reader.readUnsignedValue(fileOffset, size); + value += adjustment; + + try { + byte[] newBytes = toBytes(getPackedOffset(value), size); + System.arraycopy(newBytes, 0, packed, (int) getPackedOffset(fileOffset), + newBytes.length); + } + catch (NotFoundException e) { + Msg.warn(this, e.getMessage()); + } + } + /** * Fixes up the bytes at the given DYLD file offset to map to the correct offset in the * packed DYLIB @@ -256,19 +194,8 @@ public class DyldCacheDylibExtractor { * @throws IOException If there was an IO-related error * @throws IllegalArgumentException if size is an unsupported value */ - public void fixup(long fileOffset, int size) throws IOException { - if (size != 4 && size != 8) { - throw new IllegalArgumentException("Size must be 4 or 8 (got " + size + ")"); - } - long orig = reader.readUnsignedValue(fileOffset, size); - try { - byte[] newBytes = toBytes(getPackedOffset(orig), size); - System.arraycopy(newBytes, 0, packed, (int) getPackedOffset(fileOffset), - newBytes.length); - } - catch (NotFoundException e) { - Msg.warn(this, e.getMessage()); - } + public void fixup(long fileOffset, int size) throws IOException, IllegalArgumentException { + fixup(fileOffset, 0, size); } /** @@ -279,10 +206,14 @@ public class DyldCacheDylibExtractor { * @throws NotFoundException If there was no corresponding DYLIB offset */ private long getPackedOffset(long fileOffset) throws NotFoundException { - for (SegmentCommand segment : packedStarts.keySet()) { + for (SegmentCommand segment : packedSegmentStarts.keySet()) { + long size = segment.getFileSize(); + if (size == 0) { + size = segment.getVMsize(); + } if (fileOffset >= segment.getFileOffset() && - fileOffset < segment.getFileOffset() + segment.getFileSize()) { - return fileOffset - segment.getFileOffset() + packedStarts.get(segment); + fileOffset < segment.getFileOffset() + size) { + return fileOffset - segment.getFileOffset() + packedSegmentStarts.get(segment); } } throw new NotFoundException( @@ -301,8 +232,8 @@ public class DyldCacheDylibExtractor { private ByteProvider getSegmentProvider(SegmentCommand segment, SplitDyldCache splitDyldCache) throws IOException { for (int i = 0; i < splitDyldCache.size(); i++) { - DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i); - for (DyldCacheMappingInfo mappingInfo : header.getMappingInfos()) { + DyldCacheHeader dyldCacheheader = splitDyldCache.getDyldCacheHeader(i); + for (DyldCacheMappingInfo mappingInfo : dyldCacheheader.getMappingInfos()) { if (mappingInfo.contains(segment.getVMaddress())) { return splitDyldCache.getProvider(i); } @@ -327,5 +258,170 @@ public class DyldCacheDylibExtractor { DataConverter converter = LittleEndianDataConverter.INSTANCE; return size == 8 ? converter.getBytes(value) : converter.getBytes((int) value); } + + /** + * Fixes-up the {@link MachHeader} in the newly packed DYLIB + */ + private void fixupMachHeader() { + // Indicate that the new packed DYLIB is no longer in the cache + set(0x18, header.getFlags() & ~MachHeaderFlags.MH_DYLIB_IN_CACHE, 4); + } + + /** + * Fixes-up various fields in the new packed DYLIB's load commands + * + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private void fixupLoadCommands() throws IOException { + // Fixup indices, offsets, etc in the packed DYLIB's load commands + for (LoadCommand cmd : header.getLoadCommands()) { + if (monitor.isCancelled()) { + break; + } + switch (cmd.getCommandType()) { + case LoadCommandTypes.LC_SEGMENT: + fixupSegment((SegmentCommand) cmd, false); + break; + case LoadCommandTypes.LC_SEGMENT_64: + fixupSegment((SegmentCommand) cmd, true); + break; + case LoadCommandTypes.LC_SYMTAB: + fixupSymbolTable((SymbolTableCommand) cmd); + break; + case LoadCommandTypes.LC_DYSYMTAB: + fixupDynamicSymbolTable((DynamicSymbolTableCommand) cmd); + break; + case LoadCommandTypes.LC_DYLD_INFO: + case LoadCommandTypes.LC_DYLD_INFO_ONLY: + fixupDyldInfo((DyldInfoCommand) cmd); + break; + case LoadCommandTypes.LC_CODE_SIGNATURE: + case LoadCommandTypes.LC_SEGMENT_SPLIT_INFO: + case LoadCommandTypes.LC_FUNCTION_STARTS: + case LoadCommandTypes.LC_DATA_IN_CODE: + case LoadCommandTypes.LC_DYLIB_CODE_SIGN_DRS: + case LoadCommandTypes.LC_OPTIMIZATION_HINT: + case LoadCommandTypes.LC_DYLD_EXPORTS_TRIE: + case LoadCommandTypes.LC_DYLD_CHAINED_FIXUPS: + fixupLinkEditData((LinkEditDataCommand) cmd); + break; + } + } + } + + /** + * Fixes-up the old DYLD file offsets in the given segment so they are correct for the newly + * packed DYLIB + * + * @param segment The segment to fix-up + * @param is64bit True if the segment is 64-bit; false if 32-bit + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private void fixupSegment(SegmentCommand segment, boolean is64bit) throws IOException { + long adjustment = packedSegmentAdjustments.getOrDefault(segment, 0); + if (segment.getFileOffset() > 0) { + fixup(segment.getStartIndex() + (is64bit ? 0x28 : 0x20), adjustment, + is64bit ? 8 : 4); + } + long sectionStartIndex = segment.getStartIndex() + (is64bit ? 0x48 : 0x38); + for (Section section : segment.getSections()) { + if (monitor.isCancelled()) { + break; + } + + // For some reason the section file offsets in the iOS 10 DYLD cache do not want + // the adjustment despite the segment needed it. We can expect to see warnings + // in that particular version. + if (section.getOffset() > 0 && section.getSize() > 0) { + fixup(sectionStartIndex + (is64bit ? 0x30 : 0x28), adjustment, 4); + } + if (section.getRelocationOffset() > 0) { + fixup(sectionStartIndex + (is64bit ? 0x38 : 0x30), adjustment, 4); + } + sectionStartIndex += is64bit ? 0x50 : 0x44; + } + } + + /** + * Fixes-up the old DYLD file offsets in the given symbol table so they are correct for the + * newly packed DYLIB + * + * @param cmd The symbol table to fix-up + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private void fixupSymbolTable(SymbolTableCommand cmd) throws IOException { + if (cmd.getSymbolOffset() > 0) { + fixup(cmd.getStartIndex() + 0x8, 4); + } + if (cmd.getStringTableOffset() > 0) { + fixup(cmd.getStartIndex() + 0x10, 4); + } + } + + /** + * Fixes-up the old DYLD file offsets in the given dynamic symbol table so they are correct for + * the newly packed DYLIB + * + * @param cmd The dynamic symbol table to fix-up + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private void fixupDynamicSymbolTable(DynamicSymbolTableCommand cmd) throws IOException { + if (cmd.getTableOfContentsOffset() > 0) { + fixup(cmd.getStartIndex() + 0x20, 4); + } + if (cmd.getModuleTableOffset() > 0) { + fixup(cmd.getStartIndex() + 0x28, 4); + } + if (cmd.getReferencedSymbolTableOffset() > 0) { + fixup(cmd.getStartIndex() + 0x30, 4); + } + if (cmd.getIndirectSymbolTableOffset() > 0) { + fixup(cmd.getStartIndex() + 0x38, 4); + } + if (cmd.getExternalRelocationOffset() > 0) { + fixup(cmd.getStartIndex() + 0x40, 4); + } + if (cmd.getLocalRelocationOffset() > 0) { + fixup(cmd.getStartIndex() + 0x48, 4); + } + } + + /** + * Fixes-up the old DYLD file offsets in the given DYLD Info command so they are correct for the + * newly packed DYLIB + * + * @param cmd The DYLD Info command to fix-up + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private void fixupDyldInfo(DyldInfoCommand cmd) throws IOException { + if (cmd.getRebaseOffset() > 0) { + fixup(cmd.getStartIndex() + 0x8, 4); + } + if (cmd.getBindOffset() > 0) { + fixup(cmd.getStartIndex() + 0x10, 4); + } + if (cmd.getWeakBindOffset() > 0) { + fixup(cmd.getStartIndex() + 0x18, 4); + } + if (cmd.getLazyBindOffset() > 0) { + fixup(cmd.getStartIndex() + 0x20, 4); + } + if (cmd.getExportOffset() > 0) { + fixup(cmd.getStartIndex() + 0x28, 4); + } + } + + /** + * Fixes-up the old DYLD file offsets in the given link edit data command so they are correct + * for the newly packed DYLIB + * + * @param cmd The link edit data command to fix-up + * @throws IOException If there was an IO-related issue performing the fix-up + */ + private void fixupLinkEditData(LinkEditDataCommand cmd) throws IOException { + if (cmd.getDataOffset() > 0) { + fixup(cmd.getStartIndex() + 0x8, 4); + } + } } }