From 3305f6af5def3e4e563e52354304db1b110e3f22 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Mon, 3 Jul 2023 14:38:12 -0400 Subject: [PATCH] GP-1574: Improved support for dyld_shared_cache slide pointer fixups --- .../format/macho/dyld/DyldArchitecture.java | 37 +- .../format/macho/dyld/DyldCacheHeader.java | 31 +- .../macho/dyld/DyldCacheSlideFixup.java | 25 ++ .../macho/dyld/DyldCacheSlideInfo1.java | 253 +++++++------- .../macho/dyld/DyldCacheSlideInfo2.java | 315 +++++++++--------- .../macho/dyld/DyldCacheSlideInfo3.java | 192 +++++------ .../macho/dyld/DyldCacheSlideInfo4.java | 263 +++++++-------- .../macho/dyld/DyldCacheSlideInfoCommon.java | 182 ++++++---- .../util/opinion/DyldCacheProgramBuilder.java | 32 +- .../dyldcache/DyldCacheDylibExtractor.java | 100 ++++-- .../ios/dyldcache/DyldCacheFileSystem.java | 33 +- 11 files changed, 778 insertions(+), 685 deletions(-) create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideFixup.java 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) {