Merge branch 'GP-5291_ryanmkurtz_dyld' (Closes #7396)

This commit is contained in:
Ryan Kurtz 2025-01-21 13:11:21 -05:00
commit f94a76e875
4 changed files with 595 additions and 87 deletions

View file

@ -27,7 +27,6 @@ import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
@ -37,7 +36,6 @@ import ghidra.util.task.TaskMonitor;
*
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a>
*/
@SuppressWarnings("unused")
public class DyldCacheHeader implements StructConverter {
private byte[] magic;
@ -69,13 +67,12 @@ public class DyldCacheHeader implements StructConverter {
private long progClosuresTrieAddr;
private long progClosuresTrieSize;
private int platform;
private int dyld_info;
private int dyldInfo;
private int formatVersion;
private boolean dylibsExpectedOnDisk;
private boolean simulator;
private boolean locallyBuiltCache;
private boolean builtFromChainedFixups;
private int padding;
private long sharedRegionStart;
private long sharedRegionSize;
private long maxSlide;
@ -116,6 +113,8 @@ public class DyldCacheHeader implements StructConverter {
private long cacheAtlasSize;
private long dynamicDataOffset;
private long dynamicDataMaxSize;
private int tproMappingsOffset;
private int tproMappingsCount;
private int headerSize;
private BinaryReader reader;
@ -132,6 +131,9 @@ public class DyldCacheHeader implements StructConverter {
private List<DyldCacheMappingAndSlideInfo> cacheMappingAndSlideInfoList;
private MemoryBlock fileBlock;
@SuppressWarnings("unused")
private int padding;
/**
* Create a new {@link DyldCacheHeader}.
*
@ -212,13 +214,13 @@ public class DyldCacheHeader implements StructConverter {
platform = reader.readNextInt();
}
if (reader.getPointerIndex() < mappingOffset) {
dyld_info = reader.readNextInt();
formatVersion = dyld_info & 0xff;
dylibsExpectedOnDisk = (dyld_info >>> 8 & 1) == 1;
simulator = (dyld_info >>> 9 & 1) == 1;
locallyBuiltCache = (dyld_info >> 10 & 1) == 1;
builtFromChainedFixups = (dyld_info >> 11 & 1) == 1;
padding = (dyld_info >> 12) & 0xfffff;
dyldInfo = reader.readNextInt();
formatVersion = dyldInfo & 0xff;
dylibsExpectedOnDisk = (dyldInfo >>> 8 & 1) == 1;
simulator = (dyldInfo >>> 9 & 1) == 1;
locallyBuiltCache = (dyldInfo >> 10 & 1) == 1;
builtFromChainedFixups = (dyldInfo >> 11 & 1) == 1;
padding = (dyldInfo >> 12) & 0xfffff;
}
if (reader.getPointerIndex() < mappingOffset) {
sharedRegionStart = reader.readNextLong();
@ -350,6 +352,12 @@ public class DyldCacheHeader implements StructConverter {
if (reader.getPointerIndex() < mappingOffset) {
dynamicDataMaxSize = reader.readNextLong();
}
if (reader.getPointerIndex() < mappingOffset) {
tproMappingsOffset = reader.readNextInt();
}
if (reader.getPointerIndex() < mappingOffset) {
tproMappingsCount = reader.readNextInt();
}
headerSize = (int) (reader.getPointerIndex() - startIndex);
@ -385,7 +393,7 @@ public class DyldCacheHeader implements StructConverter {
parseSlideInfos(log, monitor);
}
private void parseSlideInfos(MessageLog log, TaskMonitor monitor) throws CancelledException {
private void parseSlideInfos(MessageLog log, TaskMonitor monitor) {
if (!hasSlideInfo()) {
return;
}
@ -476,43 +484,563 @@ public class DyldCacheHeader implements StructConverter {
}
/**
* Gets the base address of the DYLD cache. This is where the cache should be loaded in
* memory.
*
* @return The base address of the DYLD cache
*/
public long getBaseAddress() {
return baseAddress;
}
/**
* Gets the magic bytes, which contain version information.
*
* @return The magic bytes
* {@return the magic bytes, which contain version information}
*/
public byte[] getMagic() {
return magic;
}
/**
* Gets the UUID in {@link String} form
*
* @return The UUID in {@link String} form, or null if a UUID is not defined
* {@return the mapping offset}
*/
public String getUUID() {
return NumericUtilities.convertBytesToString(uuid);
public int getMappingOffset() {
return mappingOffset;
}
/**
* Gets the DYLD entry point address (if known)
*
* @return The DYLD entry point address, or null if it is not known
* {@return the mapping count}
*/
public Long getEntryPoint() {
if (!hasAccelerateInfo()) {
return accelerateInfoSize_dyldInCacheEntry;
}
return null;
public int getMappingCount() {
return mappingCount;
}
/**
* {@return the old images offset}
*/
public int getImagesOffsetOld() {
return imagesOffsetOld;
}
/**
* {@return the old images count}
*/
public int getImagesCountOld() {
return imagesCountOld;
}
/**
* {@return the dyld base address}
*/
public long getDyldBaseAddress() {
return dyldBaseAddress;
}
/**
* {@return the code signature offset}
*/
public long getCodeSignatureOffset() {
return codeSignatureOffset;
}
/**
* {@return the code signature size}
*/
public long getCodeSignatureSize() {
return codeSignatureSize;
}
/**
* {@return the slide info offset}
*/
public long getSlideInfoOffset() {
return slideInfoOffset;
}
/**
* {@return the slide info size}
*/
public long getSlideInfoSize() {
return slideInfoSize;
}
/**
* {@return the local symbols offset}
*/
public long getLocalSymbolsOffset() {
return localSymbolsOffset;
}
/**
* {@return the local symbols size}
*/
public long getLocalSymbolsSize() {
return localSymbolsSize;
}
/**
* {@return the UUID, or {@code null} if it is not defined}
*/
public byte[] getUUID() {
return uuid;
}
/**
* {@return the cache type}
*/
public long getCacheType() {
return cacheType;
}
/**
* {@return the branch pools offset}
*/
public int getBranchPoolsOffset() {
return branchPoolsOffset;
}
/**
* {@return the branch pools count}
*/
public int getBranchPoolsCount() {
return branchPoolsCount;
}
/**
* {@return the old accelerate info address or new address of mach header in dyld cache, or
* {@code null} if it is not defined}
*/
public long getAccelerateInfoAddrOrDyldInCacheMH() {
return accelerateInfoAddr_dyldInCacheMH;
}
/**
* {@return the old accelerate info size or new address of entry point in dyld cache, or
* {@code null} if it is not defined}
*/
public long getAccelerateInfoSizeOrDyldInCacheEntry() {
return accelerateInfoSize_dyldInCacheEntry;
}
/**
* {@return the images text offset}
*/
public long getImagesTextOffset() {
return imagesTextOffset;
}
/**
* {@return the images text count}
*/
public long getImagesTextCount() {
return imagesTextCount;
}
/**
* {@return the patch info address}
*/
public long getPatchInfoAddr() {
return patchInfoAddr;
}
/**
* {@return the patch info size}
*/
public long getPatchInfoSize() {
return patchInfoSize;
}
/**
* {@return the other image group address (unused)}
*/
public long getOtherImageGroupAddrUnused() {
return otherImageGroupAddrUnused;
}
/**
* {@return the other image group size (unused)}
*/
public long getOtherImageGroupSizeUnused() {
return otherImageGroupSizeUnused;
}
/**
* {@return the program launch closures address}
*/
public long getProgClosuresAddr() {
return progClosuresAddr;
}
/**
* {@return the program launch closures size}
*/
public long getProgClosuresSize() {
return progClosuresSize;
}
/**
* {@return the program launch closures trie address}
*/
public long getProgClosuresTrieAddr() {
return progClosuresTrieAddr;
}
/**
* {@return the program launch closures trie size}
*/
public long getProgClosuresTrieSize() {
return progClosuresTrieSize;
}
/**
* {@return the platform}
*/
public int getPlatform() {
return platform;
}
/**
* {@return the dyld info}
*/
public int getDyldInfo() {
return dyldInfo;
}
/**
* {@return the format version}
*/
public int getFormatVersion() {
return formatVersion;
}
/**
* {@return the dylibs expected on disk value}
*/
public boolean getDylibsExpectedOnDisk() {
return dylibsExpectedOnDisk;
}
/**
* {@return the simulator value}
*/
public boolean getSimulator() {
return simulator;
}
/**
* {@return the locally built cache value}
*/
public boolean getLocallyBuildCache() {
return locallyBuiltCache;
}
/**
* {@return the built from chained fixups value}
*/
public boolean getBuiltFromChainedFixups() {
return builtFromChainedFixups;
}
/**
* {@return the shared region start}
*/
public long getSharedRegionStart() {
return sharedRegionStart;
}
/**
* {@return the shared region size}
*/
public long getSharedRegionSize() {
return sharedRegionSize;
}
/**
* {@return the max slide}
*/
public long getMaxSlide() {
return maxSlide;
}
/**
* {@return the dylibs image array address}
*/
public long getDylibsImageArrayAddr() {
return dylibsImageArrayAddr;
}
/**
* {@return the dylibs image array size}
*/
public long getDylibsImageArraySize() {
return dylibsImageArraySize;
}
/**
* {@return the dylibs trie address}
*/
public long getDylibsTriAddr() {
return dylibsTrieAddr;
}
/**
* {@return the dylibs trie size}
*/
public long getDylibsTrieSize() {
return dylibsTrieSize;
}
/**
* {@return the other image array address}
*/
public long getOtherImageArrayAddr() {
return otherImageArrayAddr;
}
/**
* {@return the other image array size}
*/
public long getOtherImageArraySize() {
return otherImageArraySize;
}
/**
* {@return the other trie address}
*/
public long getOtherTriAddr() {
return otherTrieAddr;
}
/**
* {@return the other trie size}
*/
public long getOtherTrieSize() {
return otherTrieSize;
}
/**
* {@return the mapping with slide offset}
*/
public int getMappingWithSlideOffset() {
return mappingWithSlideOffset;
}
/**
* {@return the mapping with slide count}
*/
public int getMappingWithSlideCount() {
return mappingWithSlideCount;
}
/**
* {@return the dylibs PrebuildLoaderSet state array address (unused), or {@code null} if it is
* not defined}
*/
public long getDylibsPBLStateArrayAddrUnused() {
return dylibsPBLStateArrayAddrUnused;
}
/**
* {@return the dylibs PrebuildLoaderSet set address}
*/
public long getDylibsPBLSetAddr() {
return dylibsPBLSetAddr;
}
/**
* {@return the programs PrebuildLoaderSet set pool address, or {@code null} if it is not
* defined}
*/
public long getProgramsPBLSetPoolAddr() {
return programsPBLSetPoolAddr;
}
/**
* {@return the programs PrebuildLoaderSet set pool size}
*/
public long getProgramsPBLSetPoolSize() {
return programsPBLSetPoolSize;
}
/**
* {@return the program trie address}
*/
public long getProgramTrieAddr() {
return programTrieAddr;
}
/**
* {@return the program trie size}
*/
public int getProgramTrieSize() {
return programTrieSize;
}
/**
* {@return the OS version}
*/
public int getOsVersion() {
return osVersion;
}
/**
* {@return the alt platform}
*/
public int getAltPlatform() {
return altPlatform;
}
/**
* {@return the alt OS version}
*/
public int getAltOsVersion() {
return altOsVersion;
}
/**
* {@return the swift opts offset}
*/
public long getSwiftOptsOffset() {
return swiftOptsOffset;
}
/**
* {@return the swift opts size}
*/
public long getSwiftOptsSize() {
return swiftOptsSize;
}
/**
* {@return the subcache array offset}
*/
public int getSubCacheArrayOffset() {
return subCacheArrayOffset;
}
/**
* {@return the subcache array count, or {@code null} if it is not defined}
*/
public Integer getSubCacheArrayCount() {
return subCacheArrayCount;
}
/**
* {@return the symbol file UUID, or {@code null} if it is not defined}
*/
public byte[] getSymbolFileUUID() {
return symbolFileUUID;
}
/**
* {@return the rosetta read-only address}
*/
public long getRosettaReadOnlyAddr() {
return rosettaReadOnlyAddr;
}
/**
* {@return the rosetta read-only size}
*/
public long getRosettaReadOnlySize() {
return rosettaReadOnlySize;
}
/**
* {@return the rosetta read-write address}
*/
public long getRosettaReadWriteAddr() {
return rosettaReadWriteAddr;
}
/**
* {@return the rosetta read-write size}
*/
public long getRosettaReadWriteSize() {
return rosettaReadWriteSize;
}
/**
* {@return the images offset}
*/
public int getImagesOffset() {
return imagesOffset;
}
/**
* {@return the images count}
*/
public int getImagesCount() {
return imagesCount;
}
/**
* {@return the cache subtype, or {@code null} if it is not defined}
*/
public Integer getCacheSubType() {
return cacheSubType;
}
/**
* {@return the ObjC opts offset}
*/
public long getObjcOptsOffset() {
return objcOptsOffset;
}
/**
* {@return the ObjC opts size}
*/
public long getObjcOptsSize() {
return objcOptsSize;
}
/**
* {@return the cache atlas offset}
*/
public long getCacheAtlasOffset() {
return cacheAtlasOffset;
}
/**
* {@return the cache atlas size}
*/
public long getCacheAtlasSize() {
return cacheAtlasSize;
}
/**
* {@return the dynamic data offset}
*/
public long getDynamicDataOffset() {
return dynamicDataOffset;
}
/**
* {@return the dynamic data max size}
*/
public long getDynamicDataMaxSize() {
return dynamicDataMaxSize;
}
/**
* {@return the tpro mappings offset}
*/
public int getTproMappingsOffset() {
return tproMappingsOffset;
}
/**
* {@return the tpro mappings count}
*/
public int getTproMappingsCount() {
return tproMappingsCount;
}
/**
* {@return the reader associated with the header}
*
*/
public BinaryReader getReader() {
return reader;
}
/**
* {@return the base address of the DYLD cache}
* <p>
* This is where the cache should be loaded in memory.
*/
public long getBaseAddress() {
return baseAddress;
}
/**
@ -524,30 +1052,6 @@ public class DyldCacheHeader implements StructConverter {
return mappingInfoList;
}
/**
* Gets the file offset to first {@link DyldCacheImageInfo}.
*
* @return The file offset to first {@link DyldCacheImageInfo}
*/
public int getImagesOffset() {
if (imagesOffset != 0) {
return imagesOffset;
}
return imagesOffsetOld;
}
/**
* Gets the number of {@link DyldCacheImageInfo}s.
*
* @return The number of {@link DyldCacheImageInfo}s
*/
public int getImagesCount() {
if (imagesOffset != 0) {
return imagesCount;
}
return imagesCountOld;
}
/**
* Generates a {@link List} of {@link DyldCacheImage}s that are mapped in by this
* {@link DyldCacheHeader}. Requires header to have been parsed.
@ -581,16 +1085,6 @@ public class DyldCacheHeader implements StructConverter {
return subcacheEntryList;
}
/**
* Gets the symbol file UUID in {@link String} form
*
* @return The symbol file UUID in {@link String} form, or null if a symbol file UUID is not
* defined or is all zeros
*/
public String getSymbolFileUUID() {
return NumericUtilities.convertBytesToString(symbolFileUUID);
}
/**
* Gets the {@link List} of {@link DyldCacheMappingAndSlideInfo}s. Requires header to have been parsed.
*
@ -718,6 +1212,8 @@ public class DyldCacheHeader implements StructConverter {
addHeaderField(struct, QWORD, "cacheAtlasSize", "size of embedded cache atlas");
addHeaderField(struct, QWORD, "dynamicDataOffset", "VM offset from cache_header* to the location of dyld_cache_dynamic_data_header");
addHeaderField(struct, QWORD, "dynamicDataMaxSize", "maximum size of space reserved from dynamic data");
addHeaderField(struct, DWORD, "tproMappingsOffset", "file offset to first dyld_cache_tpro_mapping_info");
addHeaderField(struct, DWORD, "tproMappingsCount", "number of dyld_cache_tpro_mapping_info entries");
// @formatter:on
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
@ -871,7 +1367,7 @@ public class DyldCacheHeader implements StructConverter {
}
private void markupHeader(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
MessageLog log) {
monitor.setMessage("Marking up DYLD header...");
monitor.initialize(1);
try {
@ -948,7 +1444,7 @@ public class DyldCacheHeader implements StructConverter {
}
private void markupCodeSignature(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
MessageLog log) {
monitor.setMessage("Marking up DYLD code signature...");
monitor.initialize(1);
try {
@ -965,7 +1461,7 @@ public class DyldCacheHeader implements StructConverter {
}
private void markupSlideInfo(Program program, AddressSpace space, TaskMonitor monitor,
MessageLog log) throws CancelledException {
MessageLog log) {
monitor.setMessage("Marking up DYLD slide info...");
monitor.initialize(1);
try {
@ -1012,7 +1508,7 @@ public class DyldCacheHeader implements StructConverter {
monitor.initialize(branchPoolList.size());
try {
Address addr = fileOffsetToAddr(branchPoolsOffset, program, space);
for (Long element : branchPoolList) {
for (int i = 0; i < branchPoolList.size(); i++) {
Data d = DataUtilities.createData(program, addr, Pointer64DataType.dataType,
Pointer64DataType.dataType.getLength(),
DataUtilities.ClearDataMode.CHECK_FOR_SPACE);

View file

@ -35,6 +35,7 @@ import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
@ -143,7 +144,9 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
*/
private void setDyldCacheEntryPoint(SplitDyldCache splitDyldCache) throws Exception {
monitor.initialize(1, "Setting entry pointer base...");
Long entryPoint = splitDyldCache.getDyldCacheHeader(0).getEntryPoint();
Long entryPoint = !splitDyldCache.getDyldCacheHeader(0).hasAccelerateInfo()
? splitDyldCache.getDyldCacheHeader(0).getAccelerateInfoSizeOrDyldInCacheEntry()
: null;
if (entryPoint != null) {
Address entryPointAddr = space.getAddress(entryPoint);
program.getSymbolTable().addExternalEntryPoint(entryPointAddr);
@ -186,7 +189,8 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
if (!bookmarkSet) {
program.getBookmarkManager()
.setBookmark(block.getStart(), BookmarkType.INFO, "Dyld Cache Header",
name + " - " + dyldCacheHeader.getUUID());
name + " - " +
NumericUtilities.convertBytesToString(dyldCacheHeader.getUUID()));
bookmarkSet = true;
}

View file

@ -27,6 +27,7 @@ import ghidra.formats.gfilesystem.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -149,7 +150,8 @@ public class DyldCacheUtils {
splitHeader.parseFromFile(shouldProcessLocalSymbols, log, monitor);
headers.add(splitHeader);
names.add(splitFSRL.getName());
uuidToFileMap.put(splitHeader.getUUID(), splitFSRL);
uuidToFileMap.put(NumericUtilities.convertBytesToString(splitHeader.getUUID()),
splitFSRL);
}
// Validate the subcaches
@ -164,7 +166,8 @@ public class DyldCacheUtils {
}
log.appendMsg("Including subcache: " + fsrl.getName() + " - " + uuid);
}
String symbolUUID = baseHeader.getSymbolFileUUID();
String symbolUUID =
NumericUtilities.convertBytesToString(baseHeader.getSymbolFileUUID());
if (symbolUUID != null) {
FSRL symbolFSRL = uuidToFileMap.get(symbolUUID);
if (symbolFSRL == null) {

View file

@ -55,10 +55,15 @@ public class DyldCacheAnalyzer extends FileFormatAnalyzer {
createFragment(program, headerDataType.getName(), headerData.getMinAddress(),
headerData.getMaxAddress().add(1));
reader.setPointerIndex(header.getImagesOffset());
Address address = toAddr(program, header.getImagesOffset());
int imagesOffset = header.getImagesOffset() != 0 ? header.getImagesOffset()
: header.getImagesOffsetOld();
int imagesCount = header.getImagesOffset() != 0 ? header.getImagesCount()
: header.getImagesCountOld();
for (int i = 0; i < header.getImagesCount(); ++i) {
reader.setPointerIndex(imagesOffset);
Address address = toAddr(program, imagesOffset);
for (int i = 0; i < imagesCount; ++i) {
if (monitor.isCancelled()) {
break;