mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 13:42:06 +00:00
GP-1574: Improved support for dyld_shared_cache slide pointer fixups
This commit is contained in:
parent
2bc6e8b932
commit
3305f6af5d
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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) {}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user