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);
+ }
+ }
}
}