GP-1574: Improved support for dyld_shared_cache slide pointer fixups

This commit is contained in:
Ryan Kurtz 2023-07-03 14:38:12 -04:00
parent 2bc6e8b932
commit 3305f6af5d
11 changed files with 778 additions and 685 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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) {}

View File

@ -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 <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a>
* <p>
* 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<DyldCacheSlideFixup> getSlideFixups(BinaryReader reader, int pointerSize,
MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
List<DyldCacheSlideFixup> 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<DyldCacheMappingInfo> mappingInfos = dyldCacheHeader.getMappingInfos();
DyldCacheMappingInfo dyldCacheMappingInfo = mappingInfos.get(DATA_PAGE_MAP_ENTRY);
long dataPageStart = dyldCacheMappingInfo.getAddress();
List<Address> 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);
}
}

View File

@ -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 <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a>
* <p>
* 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<DyldCacheSlideFixup> 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<DyldCacheSlideFixup> fixups = new ArrayList<>();
long fixedAddressCount = 0;
List<DyldCacheMappingInfo> 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<Address> 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<Address>(1024);
do {
pageEntry = extraEntries[extraIndex] & 0xffff;
pageEntry = Short.toUnsignedInt(pageExtrasEntries[extraIndex]);
long pageOffset = (pageEntry & CHAIN_OFFSET_MASK) * BYTES_PER_CHAIN_OFFSET;
List<Address> 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<Address> processPointerChain2(Program program, long page, long nextOff,
long deltaMask, long deltaShift, long valueAdd, boolean addRelocations,
TaskMonitor monitor) throws MemoryAccessException, CancelledException {
private List<DyldCacheSlideFixup> 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<Address> unchainedLocList = new ArrayList<>(1024);
List<DyldCacheSlideFixup> 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;
}
}

View File

@ -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 <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a>
* <p>
* 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<DyldCacheSlideFixup> getSlideFixups(BinaryReader reader, int pointerSize,
MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
List<DyldCacheSlideFixup> fixups = new ArrayList<>();
@Override
public void fixPageChains(Program program, DyldCacheHeader dyldCacheHeader,
boolean addRelocations, MessageLog log, TaskMonitor monitor)
throws MemoryAccessException, CancelledException {
long fixedAddressCount = 0;
List<DyldCacheMappingAndSlideInfo> 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<Address> 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<Address> 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<DyldCacheSlideFixup> processPointerChain(long segmentOffset, long pageOffset,
BinaryReader reader, TaskMonitor monitor) throws IOException, CancelledException {
List<Address> unchainedLocList = new ArrayList<>(1024);
List<DyldCacheSlideFixup> 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;
}
}

View File

@ -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 <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a>
* Represents a dyld_cache_slide_info4 structure.
* <p>
* 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<DyldCacheSlideFixup> getSlideFixups(BinaryReader reader, int pointerSize,
MessageLog log, TaskMonitor monitor) throws IOException, CancelledException {
List<DyldCacheSlideFixup> 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<DyldCacheMappingInfo> 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<Address> 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<Address> 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<Address> processPointerChain4(Program program, long page, long nextOff,
long deltaMask, long deltaShift, long valueAdd, boolean addRelocations,
TaskMonitor monitor) throws MemoryAccessException, CancelledException {
private List<DyldCacheSlideFixup> processPointerChain(long segmentOffset, long pageOffset,
BinaryReader reader, TaskMonitor monitor) throws IOException, CancelledException {
List<DyldCacheSlideFixup> 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<Address> unchainedLocList = new ArrayList<Address>(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;
}
}

View File

@ -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<Address> 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<DyldCacheSlideFixup> 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<DyldCacheSlideFixup> 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

View File

@ -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

View File

@ -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<DyldCacheSlideInfoCommon, List<DyldCacheSlideFixup>> 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<DyldCacheSlideInfoCommon, List<DyldCacheSlideFixup>> slideFixupMap;
private MachHeader machoHeader;
private SegmentCommand textSegment;
private SegmentCommand linkEditSegment;
private Map<SegmentCommand, Integer> 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<DyldCacheSlideInfoCommon, List<DyldCacheSlideFixup>> 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;
}
}
}

View File

@ -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<DyldCacheSlideInfoCommon, List<DyldCacheSlideFixup>> slideFixupMap;
private Map<GFile, Long> addrMap = new HashMap<>();
private Map<GFile, Integer> 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<DyldCacheSlideFixup> 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) {