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 a8d41a0317..4939373bc6 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 @@ -132,6 +132,10 @@ public class SegmentCommand extends LoadCommand { public long getFileOffset() { return fileoff; } + + public void setFileOffset(long fileOffset) { + fileoff = fileOffset; + } public long getFileSize() { return filesize; 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 21f539dff7..d2c73caf71 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 @@ -24,7 +24,6 @@ import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.macho.*; import ghidra.app.util.bin.format.macho.commands.*; -import ghidra.formats.gfilesystem.GFile; import ghidra.util.*; import ghidra.util.exception.NotFoundException; import ghidra.util.task.TaskMonitor; @@ -38,7 +37,6 @@ public class DyldCacheDylibExtractor { * Gets an {@link InputStream} that reads a DYLIB from a {@link DyldCacheFileSystem}. The * DYLIB's header will be altered to account for its segment bytes being packed down. * - * @param file The {@link GFile DYLIB} * @param dylibOffset The offset of the DYLIB in the given provider * @param provider The DYLD * @param monitor A cancellable {@link TaskMonitor} @@ -47,7 +45,7 @@ public class DyldCacheDylibExtractor { * @throws IOException If there was an IO-related issue with extracting the DYLIB * @throws MachException If there was an error parsing the DYLIB headers */ - public static InputStream extractDylib(GFile file, long dylibOffset, ByteProvider provider, + public static InputStream extractDylib(long dylibOffset, ByteProvider provider, TaskMonitor monitor) throws IOException, MachException { // Make sure Mach-O header is valid @@ -56,7 +54,7 @@ public class DyldCacheDylibExtractor { header.parse(); // Pack the DYLIB - PackedDylib packedDylib = new PackedDylib(header, provider); + PackedDylib packedDylib = new PackedDylib(header, dylibOffset, provider); // Fixup indices, offsets, etc in the packed DYLIB's header for (LoadCommand cmd : header.getLoadCommands()) { @@ -206,20 +204,34 @@ public class DyldCacheDylibExtractor { * Creates a new {@link PackedDylib} object * * @param header The DYLD's DYLIB's Mach-O header + * @param dylibOffset The offset of the DYLIB in the given provider * @param provider The DYLD's bytes * @throws IOException If there was an IO-related error */ - public PackedDylib(MachHeader header, ByteProvider provider) throws IOException { + public PackedDylib(MachHeader header, long dylibOffset, ByteProvider provider) + throws IOException { reader = new BinaryReader(provider, true); packedStarts = new HashMap<>(); int size = 0; for (SegmentCommand segment : header.getAllSegments()) { packedStarts.put(segment, size); size += segment.getFileSize(); + + // 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) { + segment.setFileOffset(dylibOffset); + } } packed = new byte[size]; for (SegmentCommand segment : header.getAllSegments()) { - byte[] bytes = provider.readBytes(segment.getFileOffset(), segment.getFileSize()); + long segmentSize = segment.getFileSize(); + if (segment.getFileOffset() + segmentSize > provider.length()) { + segmentSize = provider.length() - segment.getFileOffset(); + Msg.warn(this, segment.getSegmentName() + + " segment extends beyond end of file. Truncating..."); + } + byte[] bytes = provider.readBytes(segment.getFileOffset(), segmentSize); System.arraycopy(bytes, 0, packed, packedStarts.get(segment), bytes.length); } } @@ -246,8 +258,7 @@ public class DyldCacheDylibExtractor { if (size != 4 && size != 8) { throw new IllegalArgumentException("Size must be 4 or 8 (got " + size + ")"); } - long orig = size == 8 ? reader.readLong(fileOffset) - : Conv.intToLong(reader.readInt(fileOffset)); + long orig = reader.readUnsignedValue(fileOffset, size); try { byte[] newBytes = toBytes(getPackedOffset(orig), size); System.arraycopy(newBytes, 0, packed, (int) getPackedOffset(fileOffset), 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 5a55099cbe..d67eb9ae02 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 @@ -57,8 +57,8 @@ public class DyldCacheFileSystem extends GFileSystemBase { } long machHeaderStartIndexInProvider = data.getAddress() - header.getBaseAddress(); try { - return DyldCacheDylibExtractor.extractDylib(file, machHeaderStartIndexInProvider, - provider, monitor); + return DyldCacheDylibExtractor.extractDylib(machHeaderStartIndexInProvider, provider, + monitor); } catch (MachException e) { throw new IOException("Invalid Mach-O header detected at 0x" + @@ -153,11 +153,8 @@ public class DyldCacheFileSystem extends GFileSystemBase { monitor.incrementProgress(1); - GFileImpl file = GFileImpl.fromPathString(this, root, data.getPath(), null, false, - 0/*TODO compute length?*/ ); + GFileImpl file = GFileImpl.fromPathString(this, root, data.getPath(), null, false, -1); storeFile(file, data); - - file.setLength(provider.length() - (data.getAddress() - header.getBaseAddress())); } }