diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldArchitecture.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldArchitecture.java
index bed146ff22..161b9f4901 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldArchitecture.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldArchitecture.java
@@ -31,21 +31,21 @@ public final class DyldArchitecture {
public final static int DYLD_V1_SIGNATURE_LEN = 0x10;
// @formatter:off
- public final static DyldArchitecture X86 = new DyldArchitecture( CpuTypes.CPU_TYPE_X86, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 i386", "i386", Endian.LITTLE );
- public final static DyldArchitecture X86_64 = new DyldArchitecture( CpuTypes.CPU_TYPE_X86_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 x86_64", "x86_64", Endian.LITTLE );
- public final static DyldArchitecture X86_64h = new DyldArchitecture( CpuTypes.CPU_TYPE_X86_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 x86_64h", "x86_64", Endian.LITTLE );
- public final static DyldArchitecture POWERPC = new DyldArchitecture( CpuTypes.CPU_TYPE_POWERPC, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 ppc", "rosetta", Endian.BIG );
- public final static DyldArchitecture ARMV6 = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V6, "dyld_v1 armv6", "armv6", Endian.LITTLE );
- public final static DyldArchitecture ARMV7 = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7, "dyld_v1 armv7", "arm7", Endian.LITTLE );
- public final static DyldArchitecture ARMV7F = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7F, "dyld_v1 armv7f", "arm7", Endian.LITTLE );
- public final static DyldArchitecture ARMV7S = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7S, "dyld_v1 armv7s", "arm7", Endian.LITTLE );
- public final static DyldArchitecture ARMV7K = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7K, "dyld_v1 armv7k", "arm7", Endian.LITTLE );
- public final static DyldArchitecture ARMV8A = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 arm64", "AARCH64", Endian.LITTLE );
- public final static DyldArchitecture ARMV8Ae = new DyldArchitecture(CpuTypes.CPU_TYPE_ARM_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 arm64e", "AARCH64", Endian.LITTLE);
-
-
- public final static DyldArchitecture [] ARCHITECTURES = new DyldArchitecture [] { X86, X86_64, X86_64h, POWERPC, ARMV6, ARMV7, ARMV7F, ARMV7S, ARMV7K, ARMV8A, ARMV8Ae };
+ public final static DyldArchitecture X86 = new DyldArchitecture( CpuTypes.CPU_TYPE_X86, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 i386", "i386", Endian.LITTLE, false );
+ public final static DyldArchitecture X86_64 = new DyldArchitecture( CpuTypes.CPU_TYPE_X86_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 x86_64", "x86_64", Endian.LITTLE, true );
+ public final static DyldArchitecture X86_64h = new DyldArchitecture( CpuTypes.CPU_TYPE_X86_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 x86_64h", "x86_64", Endian.LITTLE, true );
+ public final static DyldArchitecture POWERPC = new DyldArchitecture( CpuTypes.CPU_TYPE_POWERPC, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 ppc", "rosetta", Endian.BIG, false );
+ public final static DyldArchitecture ARMV6 = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V6, "dyld_v1 armv6", "armv6", Endian.LITTLE, false );
+ public final static DyldArchitecture ARMV7 = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7, "dyld_v1 armv7", "arm7", Endian.LITTLE, false );
+ public final static DyldArchitecture ARMV7F = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7F, "dyld_v1 armv7f", "arm7", Endian.LITTLE, false );
+ public final static DyldArchitecture ARMV7S = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7S, "dyld_v1 armv7s", "arm7", Endian.LITTLE, false );
+ public final static DyldArchitecture ARMV7K = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM, CpuSubTypes.CPU_SUBTYPE_ARM_V7K, "dyld_v1 armv7k", "arm7", Endian.LITTLE, false );
+ public final static DyldArchitecture ARMV8A = new DyldArchitecture( CpuTypes.CPU_TYPE_ARM_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 arm64", "AARCH64", Endian.LITTLE, true );
+ public final static DyldArchitecture ARMV8Ae = new DyldArchitecture(CpuTypes.CPU_TYPE_ARM_64, CpuSubTypes.CPU_SUBTYPE_MULTIPLE, "dyld_v1 arm64e", "AARCH64", Endian.LITTLE, true );
// @formatter:on
+
+ public final static DyldArchitecture[] ARCHITECTURES = new DyldArchitecture[] { X86, X86_64,
+ X86_64h, POWERPC, ARMV6, ARMV7, ARMV7F, ARMV7S, ARMV7K, ARMV8A, ARMV8Ae };
/**
* Returns the architecture object with the given signature.
@@ -73,13 +73,16 @@ public final class DyldArchitecture {
private String signature;
private String processor;
private Endian endianness;
+ private boolean is64bit;
- private DyldArchitecture(int cpuType, int cpuSubType, String signature, String processor, Endian endianness) {
+ private DyldArchitecture(int cpuType, int cpuSubType, String signature, String processor,
+ Endian endianness, boolean is64bit) {
this.cpuType = cpuType;
this.cpuSubType = cpuSubType;
this.signature = signature;
this.processor = processor;
this.endianness = endianness;
+ this.is64bit = is64bit;
if (signature.length() + 1 != DYLD_V1_SIGNATURE_LEN) {
throw new IllegalArgumentException("invalid signature string length: "+signature);
@@ -106,6 +109,10 @@ public final class DyldArchitecture {
return endianness;
}
+ public boolean is64bit() {
+ return is64bit;
+ }
+
@Override
public String toString() {
return signature;
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 f7da8ed133..9d99130dcd 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
@@ -389,25 +389,25 @@ public class DyldCacheHeader implements StructConverter {
if (!hasSlideInfo()) {
return;
}
- if (slideInfoOffset != 0) {
- DyldCacheSlideInfoCommon slideInfo = parseSlideInfo(slideInfoOffset, log, monitor);
- if (slideInfo != null) {
- slideInfoList.add(slideInfo);
+ if (slideInfoOffset != 0 &&
+ mappingInfoList.size() > DyldCacheSlideInfoCommon.DATA_PAGE_MAP_ENTRY) {
+ DyldCacheMappingInfo mappingInfo =
+ mappingInfoList.get(DyldCacheSlideInfoCommon.DATA_PAGE_MAP_ENTRY);
+ DyldCacheSlideInfoCommon info = DyldCacheSlideInfoCommon.parseSlideInfo(reader,
+ slideInfoOffset, mappingInfo.getAddress(), mappingInfo.getSize(),
+ mappingInfo.getFileOffset(), log, monitor);
+ if (info != null) {
+ slideInfoList.add(info);
}
}
else if (cacheMappingAndSlideInfoList.size() > 0) {
- // last section contains the real slide infos
- int listLen = cacheMappingAndSlideInfoList.size();
- DyldCacheMappingAndSlideInfo linkEditInfo =
- cacheMappingAndSlideInfoList.get(listLen - 1);
for (DyldCacheMappingAndSlideInfo info : cacheMappingAndSlideInfoList) {
if (info.getSlideInfoFileOffset() == 0) {
continue;
}
- long offsetInEditRegion =
- info.getSlideInfoFileOffset() - linkEditInfo.getSlideInfoFileOffset();
- DyldCacheSlideInfoCommon slideInfo =
- parseSlideInfo(info.getSlideInfoFileOffset(), log, monitor);
+ DyldCacheSlideInfoCommon slideInfo = DyldCacheSlideInfoCommon.parseSlideInfo(reader,
+ info.getSlideInfoFileOffset(), info.getAddress(), info.getSize(),
+ info.getFileOffset(), log, monitor);
slideInfoList.add(slideInfo);
}
}
@@ -756,13 +756,6 @@ public class DyldCacheHeader implements StructConverter {
}
}
- private DyldCacheSlideInfoCommon parseSlideInfo(long offset, MessageLog log,
- TaskMonitor monitor) throws CancelledException {
- DyldCacheSlideInfoCommon slideInfo =
- DyldCacheSlideInfoCommon.parseSlideInfo(reader, offset, log, monitor);
- return slideInfo;
- }
-
private void parseLocalSymbolsInfo(boolean shouldParse, MessageLog log, TaskMonitor monitor)
throws CancelledException {
if (!shouldParse || localSymbolsOffset == 0) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideFixup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideFixup.java
new file mode 100644
index 0000000000..b474706966
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideFixup.java
@@ -0,0 +1,25 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.app.util.bin.format.macho.dyld;
+
+/**
+ * Stores information needed to perform a slide pointer fixup
+ *
+ * @param offset The offset of where to perform the fixup (from some base address/index)
+ * @param value The fixed up value
+ * @param size The size of the fixup in bytes
+ */
+public record DyldCacheSlideFixup(long offset, long value, int size) {}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java
index 0c5dbcc2d3..46aa4b6770 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java
@@ -22,85 +22,149 @@ import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
-import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
-import ghidra.program.model.listing.Program;
-import ghidra.program.model.mem.Memory;
-import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_slide_info structure.
- *
- * @see dyld_cache_format.h
+ *
+ * Seen in iOS 8 and earlier.
*/
public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
- private int toc_offset;
- private int toc_count;
- private int entries_offset;
- private int entries_count;
- private int entries_size;
+ private int tocOffset;
+ private int tocCount;
+ private int entriesOffset;
+ private int entriesCount;
+ private int entriesSize;
- private short toc[];
- private byte bits[][];
-
- public int getTocOffset() {
- return toc_offset;
- }
-
- public int getTocCount() {
- return toc_count;
- }
-
- public int getEntriesOffset() {
- return entries_offset;
- }
-
- public int getEntriesCount() {
- return entries_count;
- }
-
- public int getEntriesSize() {
- return entries_size;
- }
-
- public short[] getToc() {
- return toc;
- }
-
- public byte[][] getEntries() {
- return bits;
- }
+ private short[] toc;
+ private byte[][] bits;
/**
* Create a new {@link DyldCacheSlideInfo1}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 1
+ * @param mappingAddress The base address of where the slide fixups will take place
+ * @param mappingSize The size of the slide fixups block
+ * @param mappingFileOffset The base file offset of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 1
*/
- public DyldCacheSlideInfo1(BinaryReader reader) throws IOException {
- super(reader);
+ public DyldCacheSlideInfo1(BinaryReader reader, long mappingAddress, long mappingSize,
+ long mappingFileOffset) throws IOException {
+ super(reader, mappingAddress, mappingSize, mappingFileOffset);
long startIndex = reader.getPointerIndex() - 4; // version # already read
- toc_offset = reader.readNextInt();
- toc_count = reader.readNextInt();
- entries_offset = reader.readNextInt();
- entries_count = reader.readNextInt();
- entries_size = reader.readNextInt();
+ tocOffset = reader.readNextInt();
+ tocCount = reader.readNextInt();
+ entriesOffset = reader.readNextInt();
+ entriesCount = reader.readNextInt();
+ entriesSize = reader.readNextInt();
- reader.setPointerIndex(startIndex + toc_offset);
- toc = reader.readNextShortArray(toc_count);
+ reader.setPointerIndex(startIndex + tocOffset);
+ toc = reader.readNextShortArray(tocCount);
- reader.setPointerIndex(startIndex + entries_offset);
- bits = new byte[entries_count][];
- for (int i = 0; i < entries_count; i++) {
- bits[i] = reader.readNextByteArray(entries_size);
+ reader.setPointerIndex(startIndex + entriesOffset);
+ bits = new byte[entriesCount][];
+ for (int i = 0; i < entriesCount; i++) {
+ bits[i] = reader.readNextByteArray(entriesSize);
}
}
+ /**
+ * {@return The TOC offset}
+ */
+ public int getTocOffset() {
+ return tocOffset;
+ }
+
+ /**
+ * {@return The TOC count}
+ */
+ public int getTocCount() {
+ return tocCount;
+ }
+
+ /**
+ * {@return The entries offset}
+ */
+ public int getEntriesOffset() {
+ return entriesOffset;
+ }
+
+ /**
+ * {@return The entries count}
+ */
+ public int getEntriesCount() {
+ return entriesCount;
+ }
+
+ /**
+ * {@return The entries size}
+ */
+ public int getEntriesSize() {
+ return entriesSize;
+ }
+
+ /**
+ * {@return The TOC}
+ */
+ public short[] getToc() {
+ return toc;
+ }
+
+ /**
+ * {@return The entries}
+ */
+ public byte[][] getEntries() {
+ return bits;
+ }
+
+ @Override
+ public List getSlideFixups(BinaryReader reader, int pointerSize,
+ MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
+
+ List fixups = new ArrayList<>(1024);
+
+ // V1 pointers currently don't need to be fixed, unless the cache is slid from its preferred
+ // location.
+ // Each bit represents whether or not its corresponding 4-byte address needs to get slid.
+ monitor.initialize(tocCount, "Getting DYLD Cache V1 slide fixups...");
+ for (int tocIndex = 0; tocIndex < tocCount; tocIndex++) {
+ monitor.increment();
+
+ int entryIndex = Short.toUnsignedInt(toc[tocIndex]);
+ if (entryIndex >= entriesCount) {
+ log.appendMsg("Entry too big! [" + tocIndex + "] " + entryIndex + " " +
+ entriesCount + " " + bits.length);
+ continue;
+ }
+
+ byte entry[] = bits[entryIndex];
+ long segmentOffset = 4096L * tocIndex;
+ for (int pageEntriesIndex = 0; pageEntriesIndex < 128; ++pageEntriesIndex) {
+ monitor.checkCancelled();
+
+ long prtEntryBitmap = Byte.toUnsignedLong(entry[pageEntriesIndex]);
+
+ if (prtEntryBitmap != 0) {
+ for (int bitMapIndex = 0; bitMapIndex < 8; ++bitMapIndex) {
+ if ((prtEntryBitmap & (1L << bitMapIndex)) != 0) {
+ long pageOffset = pageEntriesIndex * 8 * 4 + bitMapIndex * 4;
+ long value = reader.readLong(segmentOffset + pageOffset) /* + slide */;
+ fixups.add(
+ new DyldCacheSlideFixup(segmentOffset + pageOffset, value, 8));
+ }
+ }
+ }
+ }
+ }
+
+ return fixups;
+ }
+
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType("dyld_cache_slide_info", 0);
@@ -110,82 +174,17 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
struct.add(DWORD, "entries_offset", "");
struct.add(DWORD, "entries_count", "");
struct.add(DWORD, "entries_size", "");
- if (toc_offset > 0x18) {
- struct.add(new ArrayDataType(ByteDataType.dataType, toc_offset - 0x18, -1),
- "tocAlignment", "");
+ if (tocOffset > 0x18) {
+ struct.add(new ArrayDataType(BYTE, tocOffset - 0x18, -1), "align", "");
}
- struct.add(new ArrayDataType(WordDataType.dataType, toc_count, -1), "toc", "");
- if (entries_offset > (toc_offset + (toc_count * 2))) {
- struct.add(new ArrayDataType(ByteDataType.dataType,
- entries_offset - (toc_offset + (toc_count * 2)), -1), "entriesAlignment", "");
+ struct.add(new ArrayDataType(WORD, tocCount, -1), "toc", "");
+ if (entriesOffset > (tocOffset + (tocCount * 2))) {
+ struct.add(new ArrayDataType(BYTE, entriesOffset - (tocOffset + (tocCount * 2)), -1),
+ "align", "");
}
- struct.add(new ArrayDataType(new ArrayDataType(ByteDataType.dataType, entries_size, -1),
- entries_count, -1), "entries", "");
+ struct.add(new ArrayDataType(new ArrayDataType(BYTE, entriesSize, -1), entriesCount, -1),
+ "entries", "");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
-
- @Override
- public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
- boolean addRelocations, MessageLog log, TaskMonitor monitor)
- throws MemoryAccessException, CancelledException {
-
- Memory memory = program.getMemory();
-
- List mappingInfos = dyldCacheHeader.getMappingInfos();
- DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
- long dataPageStart = dyldCacheMappingInfo.getAddress();
-
- List unchainedLocList = new ArrayList<>(1024);
-
- monitor.setMessage("Fixing V1 chained data page pointers...");
-
- monitor.setMaximum(entries_count);
-
- // V1 pointers currently don't need to be fixed, unless the pointers the
- // dyld is slid from its preferred location.
- for (int tocIndex = 0; tocIndex < toc_count; tocIndex++) {
- monitor.checkCancelled();
-
- int entryIndex = (toc[tocIndex]) & 0xFFFF;
- if (entryIndex > entries_count || entryIndex > bits.length) {
- log.appendMsg("Entry too big! [" + tocIndex + "] " + entryIndex + " " +
- entries_count + " " + bits.length);
- continue;
- }
-
- byte entry[] = bits[entryIndex];
-
- long page = dataPageStart + (4096L * tocIndex);
- for (int pageEntriesIndex = 0; pageEntriesIndex < 128; ++pageEntriesIndex) {
- long prtEntryBitmap = entry[pageEntriesIndex] & 0xffL;
-
- if (prtEntryBitmap != 0) {
- for (int bitMapIndex = 0; bitMapIndex < 8; ++bitMapIndex) {
- if ((prtEntryBitmap & (1L << bitMapIndex)) != 0) {
- long loc = (page + pageEntriesIndex * 8 * 4 + bitMapIndex * 4);
- Address addr =
- memory.getProgram().getLanguage().getDefaultSpace().getAddress(loc);
- long origValue = memory.getLong(addr);
-
- long value = origValue /* + slide */ ;
-
- // not actually changing bytes, so not really a relocation, but a relocate-able place
- if (addRelocations) {
- addRelocationTableEntry(program, addr, 0x1000, value, 8, null);
- }
- //memory.setLong(addr, value);
-
- unchainedLocList.add(addr);
- }
- }
- }
- }
-
- monitor.setProgress(tocIndex);
- }
-
- createChainPointers(program, unchainedLocList, monitor);
- }
-
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java
index 70e18f4077..d3f80f9d13 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java
@@ -22,223 +22,222 @@ import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
-import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
-import ghidra.program.model.listing.Program;
-import ghidra.program.model.mem.Memory;
-import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_slide_info2 structure.
- *
- * @see dyld_cache_format.h
+ *
+ * Seen in iOS 10 and 11.
*/
public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE = 0x4000;
private static final int DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA = 0x8000;
- private int page_size;
- private int page_starts_offset;
- private int page_starts_count;
- private int page_extras_offset;
- private int page_extras_count;
- private long delta_mask;
- private long value_add;
- private short page_starts_entries[];
- private short page_extras_entries[];
-
- public long getPageSize() {
- return Integer.toUnsignedLong(page_size);
- }
-
- public long getPageStartsOffset() {
- return Integer.toUnsignedLong(page_starts_offset);
- }
-
- public long getPageStartsCount() {
- return Integer.toUnsignedLong(page_starts_count);
- }
-
- public long getPageExtrasOffset() {
- return Integer.toUnsignedLong(page_extras_offset);
- }
-
- public long getPageExtrasCount() {
- return Integer.toUnsignedLong(page_extras_count);
- }
-
- public long getDeltaMask() {
- return delta_mask;
- }
-
- public long getValueAdd() {
- return value_add;
- }
-
- public short[] getPageStarts() {
- return page_starts_entries;
- }
-
- public short[] getPageExtras() {
- return page_extras_entries;
- }
+ private int pageSize;
+ private int pageStartsOffset;
+ private int pageStartsCount;
+ private int pageExtrasOffset;
+ private int pageExtrasCount;
+ private long deltaMask;
+ private long valueAdd;
+ private short[] pageStartsEntries;
+ private short[] pageExtrasEntries;
/**
* Create a new {@link DyldCacheSlideInfo2}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 2
+ * @param mappingAddress The base address of where the slide fixups will take place
+ * @param mappingSize The size of the slide fixups block
+ * @param mappingFileOffset The base file offset of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 2
*/
- public DyldCacheSlideInfo2(BinaryReader reader) throws IOException {
- super(reader);
- page_size = reader.readNextInt();
- page_starts_offset = reader.readNextInt();
- page_starts_count = reader.readNextInt();
- page_extras_offset = reader.readNextInt();
- page_extras_count = reader.readNextInt();
- delta_mask = reader.readNextLong();
- value_add = reader.readNextLong();
- page_starts_entries = reader.readNextShortArray(page_starts_count);
- page_extras_entries = reader.readNextShortArray(page_extras_count);
+ public DyldCacheSlideInfo2(BinaryReader reader, long mappingAddress, long mappingSize,
+ long mappingFileOffset) throws IOException {
+ super(reader, mappingAddress, mappingSize, mappingFileOffset);
+ pageSize = reader.readNextInt();
+ pageStartsOffset = reader.readNextInt();
+ pageStartsCount = reader.readNextInt();
+ pageExtrasOffset = reader.readNextInt();
+ pageExtrasCount = reader.readNextInt();
+ deltaMask = reader.readNextLong();
+ valueAdd = reader.readNextLong();
+ pageStartsEntries = reader.readNextShortArray(pageStartsCount);
+ pageExtrasEntries = reader.readNextShortArray(pageExtrasCount);
+ }
+
+ /**
+ * {@return The page size}
+ */
+ public long getPageSize() {
+ return Integer.toUnsignedLong(pageSize);
+ }
+
+ /**
+ * {@return The page starts offset}
+ */
+ public long getPageStartsOffset() {
+ return Integer.toUnsignedLong(pageStartsOffset);
+ }
+
+ /**
+ * {@return The page starts count}
+ */
+ public long getPageStartsCount() {
+ return Integer.toUnsignedLong(pageStartsCount);
+ }
+
+ /**
+ * {@return The page extras offset}
+ */
+ public long getPageExtrasOffset() {
+ return Integer.toUnsignedLong(pageExtrasOffset);
+ }
+
+ /**
+ * {@return The page extras count}
+ */
+ public long getPageExtrasCount() {
+ return Integer.toUnsignedLong(pageExtrasCount);
+ }
+
+ /**
+ * {@return The delta mask}
+ */
+ public long getDeltaMask() {
+ return deltaMask;
+ }
+
+ /**
+ * {@return The "value add"}
+ */
+ public long getValueAdd() {
+ return valueAdd;
+ }
+
+ /**
+ * {@return The page starts array}
+ */
+ public short[] getPageStarts() {
+ return pageStartsEntries;
+ }
+
+ /**
+ * {@return The page extras array}
+ */
+ public short[] getPageExtras() {
+ return pageExtrasEntries;
}
@Override
- public DataType toDataType() throws DuplicateNameException, IOException {
- StructureDataType struct = new StructureDataType("dyld_cache_slide_info2", 0);
- struct.add(DWORD, "version", "");
- struct.add(DWORD, "page_size", "");
- struct.add(DWORD, "page_starts_offset", "");
- struct.add(DWORD, "page_starts_count", "");
- struct.add(DWORD, "page_extras_offset", "");
- struct.add(DWORD, "page_extras_count", "");
- struct.add(QWORD, "delta_mask", "");
- struct.add(QWORD, "value_add", "");
- struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
- return struct;
- }
+ public List getSlideFixups(BinaryReader reader, int pointerSize,
+ MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
- @Override
- public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
- boolean addRelocations, MessageLog log, TaskMonitor monitor)
- throws MemoryAccessException, CancelledException {
+ List fixups = new ArrayList<>();
- long fixedAddressCount = 0;
-
- List mappingInfos = dyldCacheHeader.getMappingInfos();
- DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
- long dataPageStart = dyldCacheMappingInfo.getAddress();
- long pageSize = getPageSize();
- long pageStartsCount = getPageStartsCount();
-
- long deltaMask = getDeltaMask();
- long deltaShift = Long.numberOfTrailingZeros(deltaMask);
- long valueAdd = getValueAdd();
-
- short[] pageEntries = getPageStarts();
- short[] extraEntries = getPageExtras();
-
- monitor.setMessage("Fixing V2 chained data page pointers...");
-
- monitor.setMaximum(pageStartsCount);
+ monitor.initialize(pageStartsCount, "Getting DYLD Cache V2 slide fixups...");
for (int index = 0; index < pageStartsCount; index++) {
- monitor.checkCancelled();
+ monitor.increment();
- long page = dataPageStart + (pageSize * index);
-
- monitor.setProgress(index);
-
- int pageEntry = pageEntries[index] & 0xffff;
+ long segmentOffset = pageSize * index;
+ int pageEntry = Short.toUnsignedInt(pageStartsEntries[index]);
if (pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) {
continue;
}
- List unchainedLocList;
-
if ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) != 0) {
// go into extras and process list of chain entries for the same page
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
- unchainedLocList = new ArrayList(1024);
do {
- pageEntry = extraEntries[extraIndex] & 0xffff;
+ pageEntry = Short.toUnsignedInt(pageExtrasEntries[extraIndex]);
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
-
- List subLocList;
- subLocList = processPointerChain2(program, page, pageOffset, deltaMask,
- deltaShift, valueAdd, addRelocations, monitor);
- unchainedLocList.addAll(subLocList);
+ fixups.addAll(processPointerChain(segmentOffset, pageOffset, reader,
+ pointerSize, monitor));
extraIndex++;
}
while ((pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0);
}
else {
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
-
- unchainedLocList = processPointerChain2(program, page, pageOffset, deltaMask,
- deltaShift, valueAdd, addRelocations, monitor);
+ fixups.addAll(
+ processPointerChain(segmentOffset, pageOffset, reader, pointerSize, monitor));
}
-
- fixedAddressCount += unchainedLocList.size();
-
- createChainPointers(program, unchainedLocList, monitor);
}
- log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
+ return fixups;
}
/**
- * Fixes up any chained pointers, starting at the given address.
+ * Walks the pointer chain at the given reader offset to find necessary
+ * {@link DyldCacheSlideFixup}s
*
- * @param program the program
- * @param page within data pages that has pointers to be unchained
- * @param nextOff offset within the page that is the chain start
- * @param deltaMask delta offset mask for each value
- * @param deltaShift shift needed for the deltaMask to extract the next offset
- * @param valueAdd value to be added to each chain pointer
- *
- * @throws MemoryAccessException IO problem reading file
- * @throws CancelledException user cancels
+ * @param segmentOffset The segment offset
+ * @param pageOffset The page offset
+ * @param reader A reader positioned at the start of the segment to fix
+ * @param pointerSize The size of a pointer in bytes
+ * @param monitor A cancellable monitor
+ * @return A {@link List} of {@link DyldCacheSlideFixup}s
+ * @throws IOException If an IO-related error occurred
+ * @throws CancelledException If the user cancelled the operation
*/
- private List processPointerChain2(Program program, long page, long nextOff,
- long deltaMask, long deltaShift, long valueAdd, boolean addRelocations,
- TaskMonitor monitor) throws MemoryAccessException, CancelledException {
+ private List processPointerChain(long segmentOffset, long pageOffset,
+ BinaryReader reader, int pointerSize, TaskMonitor monitor)
+ throws IOException, CancelledException {
- // TODO: should the image base be used to perform the ASLR slide on the pointers.
- // currently image is kept at it's initial location with no ASLR.
- Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
- Memory memory = program.getMemory();
- List unchainedLocList = new ArrayList<>(1024);
+ List fixups = new ArrayList<>(1024);
+ long valueMask = ~deltaMask;
+ long deltaShift = Long.numberOfTrailingZeros(deltaMask);
- long valueMask = 0xffffffffffffffffL >>> (64 - deltaShift);
-
- long delta = -1;
- while (delta != 0) {
+ for (long delta = -1; delta != 0; pageOffset += delta * 4) {
monitor.checkCancelled();
- Address chainLoc = chainStart.add(nextOff);
- long chainValue = memory.getLong(chainLoc);
+ long dataOffset = segmentOffset + pageOffset;
+ long chainValue =
+ pointerSize == 8 ? reader.readLong(dataOffset) : reader.readUnsignedInt(dataOffset);
delta = (chainValue & deltaMask) >> deltaShift;
- chainValue = chainValue & valueMask;
+ chainValue &= valueMask;
if (chainValue != 0) {
- chainValue += valueAdd;
- // chainValue += slideAmount - if we were sliding
+ chainValue += valueAdd /* + slide */;
+ fixups.add(new DyldCacheSlideFixup(dataOffset, chainValue, pointerSize));
}
- if (addRelocations) {
- addRelocationTableEntry(program, chainLoc, 2, chainValue, 8, null);
- }
-
- memory.setLong(chainLoc, chainValue);
-
- // delay creating data until after memory has been changed
- unchainedLocList.add(chainLoc);
-
- nextOff += (delta * 4);
}
- return unchainedLocList;
+ return fixups;
+ }
+
+ @Override
+ public DataType toDataType() throws DuplicateNameException, IOException {
+ StructureDataType struct = new StructureDataType("dyld_cache_slide_info2", 0);
+ struct.add(DWORD, "version", "currently 2");
+ struct.add(DWORD, "page_size", "currently 4096 (may also be 16384)");
+ struct.add(DWORD, "page_starts_offset", "");
+ struct.add(DWORD, "page_starts_count", "");
+ struct.add(DWORD, "page_extras_offset", "");
+ struct.add(DWORD, "page_extras_count", "");
+ struct.add(QWORD, "delta_mask",
+ "which (contiguous) set of bits contains the delta to the next rebase location");
+ struct.add(QWORD, "value_add", "");
+ if (pageStartsCount > 0) {
+ if (pageStartsOffset > 0x28) {
+ struct.add(new ArrayDataType(BYTE, pageStartsOffset - 0x28, -1), "align", "");
+ }
+ struct.add(new ArrayDataType(WORD, pageStartsCount, -1), "page_starts", "");
+ }
+ if (pageExtrasCount > 0) {
+ if (pageExtrasOffset > (pageStartsOffset + (pageStartsCount * 2))) {
+ struct.add(
+ new ArrayDataType(BYTE,
+ pageExtrasOffset - (pageStartsOffset + (pageStartsCount * 2)), -1),
+ "align", "");
+ }
+ struct.add(new ArrayDataType(WORD, pageExtrasCount, -1), "page_extras", "");
+ }
+ struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
+ return struct;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java
index 7b7b5ec8cc..a529e3929e 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java
@@ -22,162 +22,117 @@ import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
-import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
-import ghidra.program.model.listing.Program;
-import ghidra.program.model.mem.Memory;
-import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
* Represents a dyld_cache_slide_info3 structure.
- *
- * @see dyld_cache_format.h
+ *
+ * Seen in iOS 12 and later.
*/
public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
private static final int DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE = 0xFFFF;
- private int page_size;
- private int page_starts_count;
- private long auth_value_add;
- private short page_starts[];
+ private int pageSize;
+ private int pageStartsCount;
+ private long authValueAdd;
+ private short[] pageStarts;
+ /**
+ * {@return The page size}
+ */
public int getPageSize() {
- return page_size;
+ return pageSize;
}
+ /**
+ * {@return The page starts count}
+ */
public int getPageStartsCount() {
- return page_starts_count;
+ return pageStartsCount;
}
+ /**
+ * {@return The "auth value add"}
+ */
public long getAuthValueAdd() {
- return auth_value_add;
+ return authValueAdd;
}
+ /**
+ * {@return The page starts array}
+ */
public short[] getPageStarts() {
- return page_starts;
+ return pageStarts;
}
/**
* Create a new {@link DyldCacheSlideInfo3}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 3
+ * @param mappingAddress The base address of where the slide fixups will take place
+ * @param mappingSize The size of the slide fixups block
+ * @param mappingFileOffset The base file offset of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 3
*/
- public DyldCacheSlideInfo3(BinaryReader reader) throws IOException {
- super(reader);
- page_size = reader.readNextInt();
- page_starts_count = reader.readNextInt();
- int pad = reader.readNextInt();
- auth_value_add = reader.readNextLong();
- page_starts = reader.readNextShortArray(page_starts_count);
+ public DyldCacheSlideInfo3(BinaryReader reader, long mappingAddress, long mappingSize,
+ long mappingFileOffset) throws IOException {
+ super(reader, mappingAddress, mappingSize, mappingFileOffset);
+ pageSize = reader.readNextInt();
+ pageStartsCount = reader.readNextInt();
+ reader.readNextInt(); // padding
+ authValueAdd = reader.readNextLong();
+ pageStarts = reader.readNextShortArray(pageStartsCount);
}
@Override
- public DataType toDataType() throws DuplicateNameException, IOException {
- StructureDataType struct = new StructureDataType("dyld_cache_slide_info3", 0);
- struct.add(DWORD, "version", "");
- struct.add(DWORD, "page_size", "");
- struct.add(DWORD, "page_starts_count", "");
- struct.add(DWORD, "pad", "");
- struct.add(QWORD, "auth_value_add", "");
- struct.add(new ArrayDataType(WORD, page_starts_count, 1), "page_starts", "");
- struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
- return struct;
- }
+ public List getSlideFixups(BinaryReader reader, int pointerSize,
+ MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
+ List fixups = new ArrayList<>();
- @Override
- public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
- boolean addRelocations, MessageLog log, TaskMonitor monitor)
- throws MemoryAccessException, CancelledException {
- long fixedAddressCount = 0;
-
- List mappingInfos =
- dyldCacheHeader.getCacheMappingAndSlideInfos();
-
- if (mappingInfos.size() <= DATA_PAGE_MAP_ENTRY) {
- return;
- }
-
- DyldCacheMappingAndSlideInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY); // default
- for (DyldCacheMappingAndSlideInfo cacheSlideInfo : mappingInfos) {
- if (cacheSlideInfo.getSlideInfoFileOffset() == getSlideInfoOffset()) {
- dyldCacheMappingInfo = cacheSlideInfo;
- break;
- }
- }
-
- long dataPageStart = dyldCacheMappingInfo.getAddress();
- long pageSize = getPageSize();
- long pageStartsCount = getPageStartsCount();
-
- long authValueAdd = getAuthValueAdd();
-
- short[] pageStarts = getPageStarts();
-
- monitor.setMessage("Fixing V3 chained data page pointers...");
-
- monitor.setMaximum(pageStartsCount);
+ monitor.initialize(pageStartsCount, "Getting DYLD Cache V3 slide fixups...");
for (int index = 0; index < pageStartsCount; index++) {
- monitor.checkCancelled();
+ monitor.increment();
- long page = dataPageStart + (pageSize * index);
+ long segmentOffset = pageSize * index;
- monitor.setProgress(index);
-
- int pageEntry = pageStarts[index] & 0xffff;
+ int pageEntry = Short.toUnsignedInt(pageStarts[index]);
if (pageEntry == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE) {
continue;
}
- long pageOffset = (pageEntry / 8) * 8; // first entry byte based
-
- List unchainedLocList;
- unchainedLocList = processPointerChain3(program, page, pageOffset, authValueAdd,
- addRelocations, monitor);
-
- fixedAddressCount += unchainedLocList.size();
-
- createChainPointers(program, unchainedLocList, monitor);
+ long pageOffset = (pageEntry / 8) * 8; // first entry byte based;
+ fixups.addAll(processPointerChain(segmentOffset, pageOffset, reader, monitor));
}
- log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
-
- monitor.setMessage("Created " + fixedAddressCount + " chained pointers");
+ return fixups;
}
/**
- * Fixes up any chained pointers, starting at the given address.
+ * Walks the pointer chain at the given reader offset to find necessary
+ * {@link DyldCacheSlideFixup}s
*
- * @param program the program
- * @param page within data pages that has pointers to be unchained
- * @param nextOff offset within the page that is the chain start
- * @param auth_value_add value to be added to each chain pointer
- *
- * @return list of locations that were unchained
- *
- * @throws MemoryAccessException IO problem reading file
- * @throws CancelledException user cancels
+ * @param segmentOffset The segment offset
+ * @param pageOffset The page offset
+ * @param reader A reader positioned at the start of the segment to fix
+ * @param monitor A cancellable monitor
+ * @return A {@link List} of {@link DyldCacheSlideFixup}s
+ * @throws IOException If an IO-related error occurred
+ * @throws CancelledException If the user cancelled the operation
*/
- private List processPointerChain3(Program program, long page, long nextOff,
- long auth_value_add, boolean addRelocation, TaskMonitor monitor)
- throws MemoryAccessException, CancelledException {
- // TODO: should the image base be used to perform the ASLR slide on the pointers.
- // currently image is kept at it's initial location with no ASLR.
- Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
- Memory memory = program.getMemory();
+ private List processPointerChain(long segmentOffset, long pageOffset,
+ BinaryReader reader, TaskMonitor monitor) throws IOException, CancelledException {
- List unchainedLocList = new ArrayList<>(1024);
+ List fixups = new ArrayList<>(1024);
- long delta = -1;
- while (delta != 0) {
+ for (long delta = -1; delta != 0; pageOffset += delta * 8) {
monitor.checkCancelled();
- Address chainLoc = chainStart.add(nextOff);
- long chainValue = memory.getLong(chainLoc);
+ long dataOffset = segmentOffset + pageOffset;
+ long chainValue = reader.readLong(dataOffset);
// if authenticated pointer
boolean isAuthenticated = chainValue >>> 63 != 0;
@@ -188,27 +143,30 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
//long diversityData = (chainValue >> 32L) & 0xFFFFL;
//long hasAddressDiversity = (chainValue >> 48L) & 0x1L;
//long key = (chainValue >> 49L) & 0x3L;
- chainValue = offsetFromSharedCacheBase + auth_value_add;
+ chainValue = offsetFromSharedCacheBase + authValueAdd /* + slide */;
}
else {
long top8Bits = chainValue & 0x0007F80000000000L;
long bottom43Bits = chainValue & 0x000007FFFFFFFFFFL;
- chainValue = (top8Bits << 13) | bottom43Bits;
- // chainValue += slideAmount - if we were sliding
+ chainValue = (top8Bits << 13) | bottom43Bits /* + slide */;
}
- if (addRelocation) {
- addRelocationTableEntry(program, chainLoc, 3 * (isAuthenticated ? -1 : 1),
- chainValue, 8, null);
- }
- memory.setLong(chainLoc, chainValue);
-
- // delay creating data until after memory has been changed
- unchainedLocList.add(chainLoc);
-
- nextOff += delta * 8;
+ fixups.add(new DyldCacheSlideFixup(dataOffset, chainValue, 8));
}
- return unchainedLocList;
+ return fixups;
+ }
+
+ @Override
+ public DataType toDataType() throws DuplicateNameException, IOException {
+ StructureDataType struct = new StructureDataType("dyld_cache_slide_info3", 0);
+ struct.add(DWORD, "version", "currently 3");
+ struct.add(DWORD, "page_size", "currently 4096 (may also be 16384)");
+ struct.add(DWORD, "page_starts_count", "");
+ struct.add(DWORD, "pad", "");
+ struct.add(QWORD, "auth_value_add", "");
+ struct.add(new ArrayDataType(WORD, pageStartsCount, 1), "page_starts", "");
+ struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
+ return struct;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java
index ee802c4ba5..675ca117f7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java
@@ -22,220 +22,184 @@ import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
-import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
-import ghidra.program.model.listing.Program;
-import ghidra.program.model.mem.Memory;
-import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
/**
- * Represents a dyld_cache_slide_info3 structure.
- *
- * @see dyld_cache_format.h
+ * Represents a dyld_cache_slide_info4 structure.
+ *
+ * Not seen yet.
*/
public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
private static final int DYLD_CACHE_SLIDE4_PAGE_NO_REBASE = 0xFFFF;
- private static final int DYLD_CACHE_SLIDE4_PAGE_INDEX = 0x7FFF;
private static final int DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA = 0x8000;
- private static final int DYLD_CACHE_SLIDE4_PAGE_EXTRA_END = 0x8000;
private static final int HEADERSIZE4 = 40;
- private int page_size; // currently 4096 (may also be 16384)
- private int page_starts_offset;
- private int page_starts_count;
- private int page_extras_offset;
- private int page_extras_count;
- private long delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location (0xC0000000)
- private long value_add; // base address of cache
+ private int pageSize;
+ private int pageStartsOffset;
+ private int pageStartsCount;
+ private int pageExtrasOffset;
+ private int pageExtrasCount;
+ private long deltaMask;
+ private long valueAdd;
- private short page_starts[];
- private short page_extras[];
+ private short[] pageStarts;
+ private short[] pageExtras;
+ /**
+ * {@return The page size}
+ */
public int getPageSize() {
- return page_size;
+ return pageSize;
}
+ /**
+ * {@return The page starts offset}
+ */
public int getPageStartsOffset() {
- return page_starts_offset;
+ return pageStartsOffset;
}
+ /**
+ * {@return The page starts count}
+ */
public int getPageStartsCount() {
- return page_starts_count;
+ return pageStartsCount;
}
+ /**
+ * {@return The page extras offset}
+ */
public int getPageExtrasOffset() {
- return page_extras_offset;
+ return pageExtrasOffset;
}
+ /**
+ * {@return The page extras count}
+ */
public int getPageExtrasCount() {
- return page_extras_count;
+ return pageExtrasCount;
}
+ /**
+ * {@return The delta mask}
+ */
public long getDeltaMask() {
- return delta_mask;
+ return deltaMask;
}
+ /**
+ * {@return The "value add"}
+ */
public long getValueAdd() {
- return value_add;
+ return valueAdd;
}
+ /**
+ * {@return The page starts array}
+ */
public short[] getPageStarts() {
- return page_starts;
+ return pageStarts;
}
+ /**
+ * {@return The page extras array}
+ */
public short[] getPageExtras() {
- return page_extras;
+ return pageExtras;
}
/**
* Create a new {@link DyldCacheSlideInfo3}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 3
+ * @param mappingAddress The base address of where the slide fixups will take place
+ * @param mappingSize The size of the slide fixups block
+ * @param mappingFileOffset The base file offset of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 3
*/
- public DyldCacheSlideInfo4(BinaryReader reader) throws IOException {
- super(reader);
- page_size = reader.readNextInt();
- page_starts_offset = reader.readNextInt();
- page_starts_count = reader.readNextInt();
- page_extras_offset = reader.readNextInt();
- page_extras_count = reader.readNextInt();
- delta_mask = reader.readNextLong();
- value_add = reader.readNextLong();
- reader.setPointerIndex(page_starts_offset);
- page_starts = reader.readNextShortArray(page_starts_count);
- reader.setPointerIndex(page_extras_offset);
- page_extras = reader.readNextShortArray(page_extras_count);
+ public DyldCacheSlideInfo4(BinaryReader reader, long mappingAddress, long mappingSize,
+ long mappingFileOffset) throws IOException {
+ super(reader, mappingAddress, mappingSize, mappingFileOffset);
+ pageSize = reader.readNextInt();
+ pageStartsOffset = reader.readNextInt();
+ pageStartsCount = reader.readNextInt();
+ pageExtrasOffset = reader.readNextInt();
+ pageExtrasCount = reader.readNextInt();
+ deltaMask = reader.readNextLong();
+ valueAdd = reader.readNextLong();
+ reader.setPointerIndex(pageStartsOffset);
+ pageStarts = reader.readNextShortArray(pageStartsCount);
+ reader.setPointerIndex(pageExtrasOffset);
+ pageExtras = reader.readNextShortArray(pageExtrasCount);
}
@Override
- public DataType toDataType() throws DuplicateNameException, IOException {
- StructureDataType struct = new StructureDataType("dyld_cache_slide_info4", 0);
- struct.add(DWORD, "version", "");
- struct.add(DWORD, "page_size", "");
- struct.add(DWORD, "page_starts_offset", "");
- struct.add(DWORD, "page_starts_count", "");
- struct.add(DWORD, "page_extras_offset", "");
- struct.add(DWORD, "page_extras_count", "");
- struct.add(QWORD, "delta_mask", "");
- struct.add(QWORD, "value_add", "");
+ public List getSlideFixups(BinaryReader reader, int pointerSize,
+ MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
+ List fixups = new ArrayList<>();
- if (page_starts_offset == HEADERSIZE4) {
- struct.add(new ArrayDataType(WORD, page_starts_count, 1), "page_starts", "");
- }
- if (page_extras_offset == (HEADERSIZE4 + page_starts_count * 2)) {
- struct.add(new ArrayDataType(WORD, page_extras_count, 1), "page_extras", "");
- }
- struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
- return struct;
- }
-
- @Override
- public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
- boolean addRelocations, MessageLog log, TaskMonitor monitor)
- throws MemoryAccessException, CancelledException {
- long fixedAddressCount = 0;
-
- List mappingInfos = dyldCacheHeader.getMappingInfos();
- DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
- long dataPageStart = dyldCacheMappingInfo.getAddress();
- long pageSize = getPageSize();
- long pageStartsCount = getPageStartsCount();
-
- long deltaMask = getDeltaMask();
- long deltaShift = Long.numberOfTrailingZeros(deltaMask);
- long valueAdd = getValueAdd();
-
- short[] pageEntries = getPageStarts();
- short[] extraEntries = getPageExtras();
-
- monitor.setMessage("Fixing V4 chained data page pointers...");
-
- monitor.setMaximum(pageStartsCount);
+ monitor.initialize(pageStartsCount, "Getting DYLD Cache V4 slide fixups...");
for (int index = 0; index < pageStartsCount; index++) {
- monitor.checkCancelled();
+ monitor.increment();
- long page = dataPageStart + (pageSize * index);
+ long segmentOffset = pageSize * index;
- monitor.setProgress(index);
-
- int pageEntry = pageEntries[index] & 0xffff;
+ int pageEntry = Short.toUnsignedInt(pageStarts[index]);
if (pageEntry == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE) {
continue;
}
- List unchainedLocList;
-
if ((pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) != 0) {
// go into extras and process list of chain entries for the same page
int extraIndex = (pageEntry & CHAIN_OFFSET_MASK);
- unchainedLocList = new ArrayList<>(1024);
do {
- pageEntry = extraEntries[extraIndex] & 0xffff;
+ pageEntry = Short.toUnsignedInt(pageExtras[extraIndex]);
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
-
- List subLocList;
- subLocList = processPointerChain4(program, page, pageOffset, deltaMask,
- deltaShift, valueAdd, addRelocations, monitor);
- unchainedLocList.addAll(subLocList);
+ fixups.addAll(processPointerChain(segmentOffset, pageOffset, reader, monitor));
extraIndex++;
}
while ((pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA) == 0);
}
else {
long pageOffset = pageEntry * BYTES_PER_CHAIN_OFFSET;
+ fixups.addAll(processPointerChain(segmentOffset, pageOffset, reader, monitor));
- unchainedLocList = processPointerChain4(program, page, pageOffset, deltaMask,
- deltaShift, valueAdd, addRelocations, monitor);
}
-
- fixedAddressCount += unchainedLocList.size();
-
- createChainPointers(program, unchainedLocList, monitor);
}
- log.appendMsg("Fixed " + fixedAddressCount + " chained pointers.");
+ return fixups;
}
/**
- * Fixes up any chained pointers, starting at the given address.
+ * Walks the pointer chain at the given reader offset to find necessary
+ * {@link DyldCacheSlideFixup}s
*
- * @param unchainedLocList list of locations that were unchained
- * @param page within data pages that has pointers to be unchained
- * @param nextOff offset within the page that is the chain start
- * @param deltaMask delta offset mask for each value
- * @param deltaShift shift needed for the deltaMask to extract the next offset
- * @param valueAdd value to be added to each chain pointer
- *
- * @throws MemoryAccessException IO problem reading file
- * @throws CancelledException user cancels
+ * @param segmentOffset The segment offset
+ * @param pageOffset The page offset
+ * @param reader A reader positioned at the start of the segment to fix
+ * @param monitor A cancellable monitor
+ * @return A {@link List} of {@link DyldCacheSlideFixup}s
+ * @throws IOException If an IO-related error occurred
+ * @throws CancelledException If the user cancelled the operation
*/
- private List processPointerChain4(Program program, long page, long nextOff,
- long deltaMask, long deltaShift, long valueAdd, boolean addRelocations,
- TaskMonitor monitor) throws MemoryAccessException, CancelledException {
+ private List processPointerChain(long segmentOffset, long pageOffset,
+ BinaryReader reader, TaskMonitor monitor) throws IOException, CancelledException {
+ List fixups = new ArrayList<>(1024);
+ long valueMask = ~deltaMask;
+ long deltaShift = Long.numberOfTrailingZeros(deltaMask);
- // TODO: should the image base be used to perform the ASLR slide on the pointers.
- // currently image is kept at it's initial location with no ASLR.
- Address chainStart = program.getLanguage().getDefaultSpace().getAddress(page);
- Memory memory = program.getMemory();
-
- List unchainedLocList = new ArrayList(1024);
-
- int valueMask = 0xffffffff >>> (32 - deltaShift);
-
- long delta = -1;
- while (delta != 0) {
+ for (long delta = -1; delta != 0; pageOffset += delta * 4) {
monitor.checkCancelled();
- Address chainLoc = chainStart.add(nextOff);
- int chainValue = memory.getInt(chainLoc);
+ long dataOffset = segmentOffset + pageOffset;
+ int chainValue = reader.readInt(dataOffset);
delta = (chainValue & deltaMask) >> deltaShift;
- chainValue = chainValue & valueMask;
+ chainValue &= valueMask;
if ((chainValue & 0xFFFF8000) == 0) {
// small positive non-pointer, use as-is
}
@@ -243,22 +207,35 @@ public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
chainValue |= 0xC0000000;
}
else {
- chainValue += valueAdd;
- // chainValue += slideAmount - if we were sliding
+ chainValue += valueAdd /* + slide */;
}
- if (addRelocations) {
- addRelocationTableEntry(program, chainLoc, 4, chainValue, 4, null);
- }
-
- memory.setInt(chainLoc, chainValue);
-
- // delay creating data until after memory has been changed
- unchainedLocList.add(chainLoc);
-
- nextOff += (delta * 4);
+ fixups.add(new DyldCacheSlideFixup(dataOffset, chainValue, 4));
}
- return unchainedLocList;
+ return fixups;
+ }
+
+ @Override
+ public DataType toDataType() throws DuplicateNameException, IOException {
+ StructureDataType struct = new StructureDataType("dyld_cache_slide_info4", 0);
+ struct.add(DWORD, "version", "currently 4");
+ struct.add(DWORD, "page_size", "currently 4096 (may also be 16384)");
+ struct.add(DWORD, "page_starts_offset", "");
+ struct.add(DWORD, "page_starts_count", "");
+ struct.add(DWORD, "page_extras_offset", "");
+ struct.add(DWORD, "page_extras_count", "");
+ struct.add(QWORD, "delta_mask",
+ "which (contiguous) set of bits contains the delta to the next rebase location (0xC0000000)");
+ struct.add(QWORD, "value_add", "base address of cache");
+
+ if (pageStartsOffset == HEADERSIZE4) {
+ struct.add(new ArrayDataType(WORD, pageStartsCount, 1), "page_starts", "");
+ }
+ if (pageExtrasOffset == (HEADERSIZE4 + pageStartsCount * 2)) {
+ struct.add(new ArrayDataType(WORD, pageExtrasCount, 1), "page_extras", "");
+ }
+ struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
+ return struct;
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java
index fa10c28242..0704855e06 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java
@@ -18,13 +18,14 @@ package ghidra.app.util.bin.format.macho.dyld;
import java.io.IOException;
import java.util.List;
-import ghidra.app.util.bin.BinaryReader;
-import ghidra.app.util.bin.StructConverter;
+import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
+import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.util.CodeUnitInsertionException;
@@ -50,118 +51,177 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info
* @param slideInfoOffset The offset of the slide info to parse
+ * @param mappingAddress The base address of where the slide fixups will take place
+ * @param mappingSize The size of the slide fixups block
+ * @param mappingFileOffset The base file offset of where the slide fixups will take place
* @param log The log
* @param monitor A cancelable task monitor
* @return The slide info object
*/
public static DyldCacheSlideInfoCommon parseSlideInfo(BinaryReader reader, long slideInfoOffset,
- MessageLog log, TaskMonitor monitor) {
+ long mappingAddress, long mappingSize, long mappingFileOffset, MessageLog log,
+ TaskMonitor monitor) {
if (slideInfoOffset == 0) {
return null;
}
- DyldCacheSlideInfoCommon returnedSlideInfo = null;
monitor.setMessage("Parsing DYLD slide info...");
monitor.initialize(1);
try {
reader.setPointerIndex(slideInfoOffset);
- int version = reader.readNextInt();
- reader.setPointerIndex(slideInfoOffset);
- switch (version) {
- case 1:
- returnedSlideInfo = new DyldCacheSlideInfo1(reader);
- break;
- case 2:
- returnedSlideInfo = new DyldCacheSlideInfo2(reader);
- break;
- case 3:
- returnedSlideInfo = new DyldCacheSlideInfo3(reader);
- break;
- case 4:
- returnedSlideInfo = new DyldCacheSlideInfo4(reader);
- break;
- default:
- throw new IOException();
- }
+ int version = reader.readInt(reader.getPointerIndex());
+ DyldCacheSlideInfoCommon returnedSlideInfo = switch (version) {
+ case 1 -> new DyldCacheSlideInfo1(reader, mappingAddress, mappingSize,
+ mappingFileOffset);
+ case 2 -> new DyldCacheSlideInfo2(reader, mappingAddress, mappingSize,
+ mappingFileOffset);
+ case 3 -> new DyldCacheSlideInfo3(reader, mappingAddress, mappingSize,
+ mappingFileOffset);
+ case 4 -> new DyldCacheSlideInfo4(reader, mappingAddress, mappingSize,
+ mappingFileOffset);
+ default -> throw new IOException("Unrecognized slide info version: " + version);
+ };
monitor.incrementProgress(1);
+ returnedSlideInfo.slideInfoOffset = slideInfoOffset;
+ return returnedSlideInfo;
}
catch (IOException e) {
- log.appendMsg(DyldCacheHeader.class.getSimpleName(),
+ log.appendMsg(DyldCacheSlideInfoCommon.class.getSimpleName(),
"Failed to parse dyld_cache_slide_info.");
return null;
}
- returnedSlideInfo.slideInfoOffset = slideInfoOffset;
- return returnedSlideInfo;
}
protected int version;
protected long slideInfoOffset;
+ protected long mappingAddress;
+ protected long mappingSize;
+ protected long mappingFileOffset;
/**
* Create a new {@link DyldCacheSlideInfoCommon}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info
+ * @param mappingAddress The base address of where the slide fixups will take place
+ * @param mappingSize The size of the slide fixups block
+ * @param mappingFileOffset The base file offset of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info
*/
- public DyldCacheSlideInfoCommon(BinaryReader reader) throws IOException {
- version = reader.readNextInt();
+ public DyldCacheSlideInfoCommon(BinaryReader reader, long mappingAddress, long mappingSize,
+ long mappingFileOffset)
+ throws IOException {
+ this.mappingAddress = mappingAddress;
+ this.mappingSize = mappingSize;
+ this.mappingFileOffset = mappingFileOffset;
+ this.version = reader.readNextInt();
}
/**
- * Gets the version of the DYLD slide info.
- *
- * @return The version of the DYLD slide info.
+ * {@return The version of the DYLD slide info}
*/
public int getVersion() {
return version;
}
/**
- * Return the original slide info offset
- *
- * @return the original slide info offset
+ * {@return The original slide info offset}
*/
public long getSlideInfoOffset() {
return slideInfoOffset;
}
- public abstract void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
- boolean addRelocations, MessageLog log, TaskMonitor monitor)
- throws MemoryAccessException, CancelledException;
-
- protected void addRelocationTableEntry(Program program, Address chainLoc, int type,
- long chainValue, int appliedByteLength, String name) {
- // Add entry to relocation table for the pointer fixup
- program.getRelocationTable()
- .add(chainLoc, Status.APPLIED, type, new long[] { chainValue }, appliedByteLength,
- name);
+ /**
+ * {@return The base address of where the slide fixups will take place}
+ */
+ public long getMappingAddress() {
+ return mappingAddress;
}
/**
- * Create pointers at each fixed chain location.
- *
- * @param program The program
- * @param unchainedLocList Address list of fixed pointer locations
- * @param monitor A cancelable task monitor
- *
- * @throws CancelledException if the user cancels
+ * {@return The size of the slide fixups block}
*/
- protected void createChainPointers(Program program, List unchainedLocList,
- TaskMonitor monitor) throws CancelledException {
- int numFixedLocations = unchainedLocList.size();
+ public long getMappingSize() {
+ return mappingSize;
+ }
- monitor.setMessage("Fixed " + numFixedLocations + " chained pointers. Creating Pointers");
+ /**
+ * {@return The base file offset of where the slide fixups will take place}
+ */
+ public long getMappingFileOffset() {
+ return mappingFileOffset;
+ }
- // Create pointers at any fixed-up addresses
- for (Address addr : unchainedLocList) {
- monitor.checkCancelled();
- try {
- program.getListing().createData(addr, Pointer64DataType.dataType);
+ /**
+ * Walks the slide fixup information and collects a {@link List} of {@link DyldCacheSlideFixup}s
+ * that will need to be applied to the image
+ *
+ * @param reader A {@link BinaryReader} positioned at the start of the segment to fix up
+ * @param pointerSize The size of a pointer in bytes
+ * @param log The log
+ * @param monitor A cancellable monitor
+ * @return A {@link List} of {@link DyldCacheSlideFixup}s
+ * @throws IOException If there was an IO-related issue
+ * @throws CancelledException If the user cancelled the operation
+ */
+ public abstract List getSlideFixups(BinaryReader reader, int pointerSize,
+ MessageLog log, TaskMonitor monitor) throws IOException, CancelledException;
+
+ /**
+ * Fixes up the programs slide pointers
+ *
+ * @param program The {@link Program}
+ * @param addRelocations True if slide pointer locations should be added to the relocation
+ * table; otherwise, false
+ * @param log The log
+ * @param monitor A cancellable monitor
+ * @throws MemoryAccessException If there was a problem accessing memory
+ * @throws CancelledException If the user cancelled the operation
+ */
+ public void fixSlidePointers(Program program, boolean addRelocations, MessageLog log,
+ TaskMonitor monitor) throws MemoryAccessException, CancelledException {
+
+ Memory memory = program.getMemory();
+ AddressSpace space = program.getAddressFactory().getDefaultAddressSpace();
+ Address dataPageAddr = space.getAddress(mappingAddress);
+
+ try (ByteProvider provider = new MemoryByteProvider(memory, dataPageAddr)) {
+ BinaryReader reader = new BinaryReader(provider, !memory.isBigEndian());
+
+ List fixups =
+ getSlideFixups(reader, program.getDefaultPointerSize(), log, monitor);
+
+ monitor.initialize(fixups.size(), "Fixing DYLD Cache slide pointers...");
+ for (DyldCacheSlideFixup fixup : fixups) {
+ monitor.increment();
+ Address addr = dataPageAddr.add(fixup.offset());
+ if (fixup.size() == 8) {
+ memory.setLong(addr, fixup.value());
+ }
+ else {
+ memory.setInt(addr, (int) fixup.value());
+ }
}
- catch (CodeUnitInsertionException e) {
- // No worries, something presumably more important was there already
+
+ monitor.initialize(fixups.size(), "Marking up DYLD Cache slide pointers...");
+ for (DyldCacheSlideFixup fixup : fixups) {
+ monitor.increment();
+ Address addr = dataPageAddr.add(fixup.offset());
+ if (addRelocations) {
+ program.getRelocationTable()
+ .add(addr, Status.APPLIED, version, new long[] { fixup.value() },
+ fixup.size(), null);
+ }
+ try {
+ program.getListing().createData(addr, POINTER);
+ }
+ catch (CodeUnitInsertionException e) {
+ // No worries, something presumably more important was there already
+ }
}
}
+ catch (IOException e) {
+ throw new MemoryAccessException(e.getMessage(), e);
+ }
}
@Override
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java
index dc97f10e64..296ee1da4f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java
@@ -110,7 +110,7 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
ByteProvider bp = splitDyldCache.getProvider(i);
- fixPageChains(header);
+ fixSlidePointers(header);
markupHeaders(header);
markupBranchIslands(header, bp);
createLocalSymbols(header);
@@ -254,13 +254,13 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
}
/**
- * Fixes any chained pointers within each of the data pages.
+ * Fixes any slide pointers within each of the data pages.
*
* @param dyldCacheHeader The {@link DyldCacheHeader}
* @throws MemoryAccessException if there was a problem reading/writing memory.
* @throws CancelledException if user cancels
*/
- private void fixPageChains(DyldCacheHeader dyldCacheHeader)
+ private void fixSlidePointers(DyldCacheHeader dyldCacheHeader)
throws MemoryAccessException, CancelledException {
if (!options.processChainedFixups()) {
return;
@@ -271,9 +271,8 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
for (DyldCacheSlideInfoCommon info : slideInfos) {
int version = info.getVersion();
- log.appendMsg("Fixing page chains version: " + version);
- info.fixPageChains(program, dyldCacheHeader, options.addChainedFixupsRelocations(), log,
- monitor);
+ log.appendMsg("Fixing slide pointers version: " + version);
+ info.fixSlidePointers(program, options.addChainedFixupsRelocations(), log, monitor);
}
}
@@ -317,9 +316,17 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
info.markupHeaders();
}
- if (options.processDylibMemory()) {
+ // Add DyldCache Mach-O's to program tree
+ monitor.setMessage("Adding DYLIB's to program tree...");
+ monitor.initialize(infoSet.size());
+ for (DyldCacheMachoInfo info : infoSet) {
+ monitor.checkCancelled();
+ monitor.incrementProgress(1);
+ info.addToProgramTree();
+ }
- // Process DyldCache DYLIB memory blocks
+ // Process DyldCache DYLIB memory blocks
+ if (options.processDylibMemory()) {
monitor.setMessage("Processing DYLIB memory blocks...");
monitor.initialize(infoSet.size());
for (DyldCacheMachoInfo info : infoSet) {
@@ -328,15 +335,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
info.processMemoryBlocks();
}
- // Add DyldCache Mach-O's to program tree
- monitor.setMessage("Adding DYLIB's to program tree...");
- monitor.initialize(infoSet.size());
- for (DyldCacheMachoInfo info : infoSet) {
- monitor.checkCancelled();
- monitor.incrementProgress(1);
- info.addToProgramTree();
- }
-
}
// Markup DyldCache DYLIB load command data
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 816cbb5dc7..9e840fb205 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
@@ -22,11 +22,11 @@ import java.util.*;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
-import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
-import ghidra.app.util.bin.format.macho.dyld.DyldCacheMappingInfo;
+import ghidra.app.util.bin.format.macho.dyld.*;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.formats.gfilesystem.FSRL;
import ghidra.util.*;
+import ghidra.util.exception.CancelledException;
import ghidra.util.exception.NotFoundException;
import ghidra.util.task.TaskMonitor;
@@ -42,17 +42,20 @@ public class DyldCacheDylibExtractor {
* @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 slideFixupMap A {@link Map} of {@link DyldCacheSlideFixup}s to perform
* @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 MachException If there was an error parsing the DYLIB headers
* @throws IOException If there was an IO-related issue with extracting the DYLIB
+ * @throws CancelledException If the user cancelled the operation
*/
public static ByteProvider extractDylib(long dylibOffset, SplitDyldCache splitDyldCache,
- int index, FSRL fsrl, TaskMonitor monitor) throws IOException, MachException {
+ int index, Map> slideFixupMap,
+ FSRL fsrl, TaskMonitor monitor) throws IOException, MachException, CancelledException {
PackedSegments packedSegments =
- new PackedSegments(dylibOffset, splitDyldCache, index, monitor);
+ new PackedSegments(dylibOffset, splitDyldCache, index, slideFixupMap, monitor);
return packedSegments.getByteProvider(fsrl);
}
@@ -64,8 +67,10 @@ public class DyldCacheDylibExtractor {
*/
private static class PackedSegments {
+ private ByteProvider provider;
private BinaryReader reader;
- private MachHeader header;
+ private Map> slideFixupMap;
+ private MachHeader machoHeader;
private SegmentCommand textSegment;
private SegmentCommand linkEditSegment;
private Map packedSegmentStarts = new HashMap<>();
@@ -80,17 +85,21 @@ public class DyldCacheDylibExtractor {
* @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 slideFixupMap A {@link Map} of {@link DyldCacheSlideFixup}s to perform
* @param monitor {@link TaskMonitor}
* @throws MachException If there was an error parsing the DYLIB headers
* @throws IOException If there was an IO-related error
+ * @throws CancelledException If the user cancelled the operation
*/
public PackedSegments(long dylibOffset, SplitDyldCache splitDyldCache, int index,
- TaskMonitor monitor) throws MachException, IOException {
- ByteProvider provider = splitDyldCache.getProvider(index);
- this.header = new MachHeader(provider, dylibOffset, false).parse(splitDyldCache);
- this.textSegment = header.getSegment(SegmentNames.SEG_TEXT);
- this.linkEditSegment = header.getSegment(SegmentNames.SEG_LINKEDIT);
- this.reader = new BinaryReader(provider, header.isLittleEndian());
+ Map> slideFixupMap,
+ TaskMonitor monitor) throws MachException, IOException, CancelledException {
+ this.provider = splitDyldCache.getProvider(index);
+ this.slideFixupMap = slideFixupMap;
+ this.machoHeader = new MachHeader(provider, dylibOffset, false).parse(splitDyldCache);
+ this.textSegment = machoHeader.getSegment(SegmentNames.SEG_TEXT);
+ this.linkEditSegment = machoHeader.getSegment(SegmentNames.SEG_LINKEDIT);
+ this.reader = new BinaryReader(provider, machoHeader.isLittleEndian());
this.monitor = monitor;
// Keep track of each segment's file offset in the DYLD cache.
@@ -98,14 +107,14 @@ public class DyldCacheDylibExtractor {
// packed array.
int packedSize = 0;
int packedLinkEditSize = 0;
- for (SegmentCommand segment : header.getAllSegments()) {
+ for (SegmentCommand segment : machoHeader.getAllSegments()) {
packedSegmentStarts.put(segment, packedSize);
// The __LINKEDIT segment is shared across all DYLIB's, so it is very large. We
// Want to create a new packed __LINKEDIT segment with only the relevant info for
// the DYLIB we are extracting, resulting in a significantly smaller file.
if (segment == linkEditSegment) {
- for (LoadCommand cmd : header.getLoadCommands()) {
+ for (LoadCommand cmd : machoHeader.getLoadCommands()) {
int offset = cmd.getLinkerDataOffset();
int size = cmd.getLinkerDataSize();
if (offset == 0 || size == 0) {
@@ -136,7 +145,7 @@ public class DyldCacheDylibExtractor {
packed = new byte[packedSize];
// Copy each segment into the packed array (leaving no gaps)
- for (SegmentCommand segment : header.getAllSegments()) {
+ for (SegmentCommand segment : machoHeader.getAllSegments()) {
long segmentSize = segment.getFileSize();
ByteProvider segmentProvider = getSegmentProvider(segment, splitDyldCache);
if (segment.getFileOffset() + segmentSize > segmentProvider.length()) {
@@ -158,8 +167,7 @@ public class DyldCacheDylibExtractor {
// Fixup various fields in the packed array
fixupMachHeader();
fixupLoadCommands();
-
- // TODO: Fixup pointer chains
+ fixupSlidePointers();
}
/**
@@ -210,8 +218,8 @@ public class DyldCacheDylibExtractor {
packedSymbolStringTable.length);
}
else {
- byte[] bytes =
- linkEditSegmentProvider.readBytes(cmd.getLinkerDataOffset(), cmd.getLinkerDataSize());
+ byte[] bytes = linkEditSegmentProvider.readBytes(cmd.getLinkerDataOffset(),
+ cmd.getLinkerDataSize());
System.arraycopy(bytes, 0, packedLinkEdit, packedLinkEditDataStarts.get(cmd),
bytes.length);
}
@@ -230,7 +238,7 @@ public class DyldCacheDylibExtractor {
*/
private byte[] nlistToArray(NList nlist, int stringIndex) {
byte[] ret = new byte[nlist.getSize()];
- DataConverter conv = DataConverter.getInstance(!header.isLittleEndian());
+ DataConverter conv = DataConverter.getInstance(!machoHeader.isLittleEndian());
conv.putInt(ret, 0, stringIndex);
ret[4] = nlist.getType();
ret[5] = nlist.getSection();
@@ -251,8 +259,8 @@ public class DyldCacheDylibExtractor {
*/
private void fixupMachHeader() throws IOException {
// Indicate that the new packed DYLIB is no longer in the cache
- set(header.getStartIndexInProvider() + 0x18,
- header.getFlags() & ~MachHeaderFlags.MH_DYLIB_IN_CACHE, 4);
+ set(machoHeader.getStartIndexInProvider() + 0x18,
+ machoHeader.getFlags() & ~MachHeaderFlags.MH_DYLIB_IN_CACHE, 4);
}
/**
@@ -262,7 +270,7 @@ public class DyldCacheDylibExtractor {
*/
private void fixupLoadCommands() throws IOException {
// Fixup indices, offsets, etc in the packed DYLIB's load commands
- for (LoadCommand cmd : header.getLoadCommands()) {
+ for (LoadCommand cmd : machoHeader.getLoadCommands()) {
if (monitor.isCancelled()) {
break;
}
@@ -564,5 +572,53 @@ public class DyldCacheDylibExtractor {
DataConverter converter = LittleEndianDataConverter.INSTANCE;
return size == 8 ? converter.getBytes(value) : converter.getBytes((int) value);
}
+
+ /**
+ * Fixes-up the slide pointers
+ *
+ * @throws IOException If there was an IO-related issue performing the fix-up
+ * @throws CancelledException If the user cancelled the operation
+ */
+ private void fixupSlidePointers() throws IOException, CancelledException {
+ // TODO; Optimize this fixup algorithm
+ long total = slideFixupMap.values().stream().flatMap(List::stream).count();
+ monitor.initialize(total, "Fixing slide pointers...");
+ for (DyldCacheSlideInfoCommon slideInfo : slideFixupMap.keySet()) {
+ for (DyldCacheSlideFixup fixup : slideFixupMap.get(slideInfo)) {
+ monitor.increment();
+ long addr = slideInfo.getMappingAddress() + fixup.offset();
+ long fileOffset = slideInfo.getMappingFileOffset() + fixup.offset();
+ SegmentCommand segment = getSegmentContaining(addr);
+ if (segment == null) {
+ // Fixup is not in this Mach-O
+ continue;
+ }
+ byte[] newBytes = toBytes(fixup.value(), fixup.size());
+ try {
+ System.arraycopy(newBytes, 0, packed,
+ (int) getPackedOffset(fileOffset, segment), newBytes.length);
+ }
+ catch (NotFoundException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the {@link SegmentCommand segment} that contains the given virtual address
+ *
+ * @param addr The address
+ * @return The {@link SegmentCommand segment} that contains the given virtual address
+ */
+ private SegmentCommand getSegmentContaining(long addr) {
+ for (SegmentCommand segment : machoHeader.getAllSegments()) {
+ if (addr >= segment.getVMaddress() &&
+ addr < segment.getVMaddress() + segment.getVMsize()) {
+ return segment;
+ }
+ }
+ return null;
+ }
}
}
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 d51d5791f7..d1c016edc9 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
@@ -18,11 +18,9 @@ package ghidra.file.formats.ios.dyldcache;
import java.io.IOException;
import java.util.*;
-import ghidra.app.util.bin.BinaryReader;
-import ghidra.app.util.bin.ByteProvider;
+import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.MachException;
-import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
-import ghidra.app.util.bin.format.macho.dyld.DyldCacheImage;
+import ghidra.app.util.bin.format.macho.dyld.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.DyldCacheUtils;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
@@ -37,6 +35,7 @@ import ghidra.util.task.TaskMonitor;
public class DyldCacheFileSystem extends GFileSystemBase {
private SplitDyldCache splitDyldCache;
+ private Map> slideFixupMap;
private Map addrMap = new HashMap<>();
private Map indexMap = new HashMap<>();
@@ -46,6 +45,7 @@ public class DyldCacheFileSystem extends GFileSystemBase {
@Override
public void close() throws IOException {
+ slideFixupMap.clear();
addrMap.clear();
indexMap.clear();
splitDyldCache.close();
@@ -53,7 +53,8 @@ public class DyldCacheFileSystem extends GFileSystemBase {
}
@Override
- public ByteProvider getByteProvider(GFile file, TaskMonitor monitor) throws IOException {
+ public ByteProvider getByteProvider(GFile file, TaskMonitor monitor)
+ throws CancelledException, IOException {
Long addr = addrMap.get(file);
if (addr == null) {
return null;
@@ -63,7 +64,7 @@ public class DyldCacheFileSystem extends GFileSystemBase {
addr - splitDyldCache.getDyldCacheHeader(index).getBaseAddress();
try {
return DyldCacheDylibExtractor.extractDylib(machHeaderStartIndexInProvider,
- splitDyldCache, index, file.getFSRL(), monitor);
+ splitDyldCache, index, slideFixupMap, file.getFSRL(), monitor);
}
catch (MachException e) {
throw new IOException("Invalid Mach-O header detected at 0x" +
@@ -127,6 +128,26 @@ public class DyldCacheFileSystem extends GFileSystemBase {
monitor.incrementProgress(1);
}
}
+
+ // Get slide fixups
+ slideFixupMap = new HashMap<>();
+ for (int i = 0; i < splitDyldCache.size(); i++) {
+ DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
+ ByteProvider bp = splitDyldCache.getProvider(i);
+ DyldArchitecture arch = header.getArchitecture();
+ for (DyldCacheSlideInfoCommon slideInfo : header.getSlideInfos()) {
+ try (ByteProvider wrapper = new ByteProviderWrapper(bp,
+ slideInfo.getMappingFileOffset(), slideInfo.getMappingSize())) {
+ BinaryReader wrapperReader =new BinaryReader(wrapper, !arch.getEndianness().isBigEndian());
+ List fixups = slideInfo.getSlideFixups(wrapperReader,
+ arch.is64bit() ? 8 : 4, log, monitor);
+ slideFixupMap.put(slideInfo, fixups);
+ }
+ catch (IOException e) {
+ throw new IOException(e);
+ }
+ }
+ }
}
private void storeFile(GFile file, Long addr, Integer index) {