Merge remote-tracking branch 'origin/GP-5767_ryanmkurtz_dyld' into patch

This commit is contained in:
ghidra1 2025-06-30 08:24:44 -04:00
commit 0a07e885db
20 changed files with 241 additions and 110 deletions

View file

@ -17,19 +17,23 @@ package ghidra.app.plugin.core.analysis;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.PseudoDisassembler; import ghidra.app.util.PseudoDisassembler;
import ghidra.app.util.PseudoInstruction;
import ghidra.app.util.bin.*; import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.MachException; import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.MachHeader; import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.commands.*; import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.dyld.*; import ghidra.app.util.bin.format.macho.dyld.DyldCacheHeader;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheMappingInfo;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.DyldCacheLoader; import ghidra.app.util.opinion.*;
import ghidra.app.util.opinion.MachoLoader; import ghidra.app.util.opinion.DyldCacheUtils.DyldCacheImageRecord;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.database.mem.FileBytes; import ghidra.program.database.mem.FileBytes;
import ghidra.program.disassemble.Disassembler; import ghidra.program.disassemble.Disassembler;
@ -191,29 +195,31 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
private void analyzeDyldCacheFunctionStarts(Program program, List<ByteProvider> providers, private void analyzeDyldCacheFunctionStarts(Program program, List<ByteProvider> providers,
AddressSetView set, TaskMonitor monitor, MessageLog log) AddressSetView set, TaskMonitor monitor, MessageLog log)
throws MachException, IOException, CancelledException { throws MachException, IOException, CancelledException {
Map<DyldCacheHeader, ByteProvider> providerMap = new HashMap<>(); List<DyldCacheHeader> headers = new ArrayList<>();
List<String> names = new ArrayList<>();
// Parse all DYLD Cache headers. There could be more that one if the DYLD Cache is "split". // Parse all DYLD Cache headers. There could be more that one if the DYLD Cache is "split".
for (ByteProvider provider : providers) { for (ByteProvider provider : providers) {
DyldCacheHeader header = new DyldCacheHeader(new BinaryReader(provider, true)); DyldCacheHeader header = new DyldCacheHeader(new BinaryReader(provider, true));
header.parseFromFile(false, log, monitor); header.parseFromFile(false, log, monitor);
providerMap.put(header, provider); headers.add(header);
names.add(provider.getName());
} }
// Process each Mach-O header found in each DYLD Cache header // Process each Mach-O header
for (DyldCacheHeader dyldCacheHeader : providerMap.keySet()) { try (SplitDyldCache splitDyldCache =
List<DyldCacheImage> mappedImages = dyldCacheHeader.getMappedImages(); new SplitDyldCache(providers, headers, names, log, monitor)) {
monitor.initialize(mappedImages.size()); List<DyldCacheImageRecord> imageRecords = DyldCacheUtils.getImageRecords(headers);
for (DyldCacheImage mappedImage : mappedImages) { monitor.initialize(imageRecords.size());
String name = new File(mappedImage.getPath()).getName(); for (DyldCacheImageRecord imageRecord : imageRecords) {
String name = new File(imageRecord.image().getPath()).getName();
monitor.checkCancelled(); monitor.checkCancelled();
monitor.setMessage("Analyzing function starts for " + name + "..."); monitor.setMessage("Analyzing function starts for " + name + "...");
monitor.incrementProgress(1); monitor.incrementProgress(1);
// Parse Mach-O header // Parse Mach-O header
MachHeader machoHeader = new MachHeader(providerMap.get(dyldCacheHeader), MachHeader machoHeader = splitDyldCache.getMacho(imageRecord);
mappedImage.getAddress() - dyldCacheHeader.getBaseAddress(), false); machoHeader.parse(splitDyldCache);
machoHeader.parse();
// The list of function starts should always be in a __LINKEDIT segment. // The list of function starts should always be in a __LINKEDIT segment.
// If the DYLD Cache is "split", a Mach-O's __LINKEDIT segment may live in a // If the DYLD Cache is "split", a Mach-O's __LINKEDIT segment may live in a
@ -221,8 +227,8 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
SegmentCommand linkEdit = machoHeader.getSegment(SegmentNames.SEG_LINKEDIT); SegmentCommand linkEdit = machoHeader.getSegment(SegmentNames.SEG_LINKEDIT);
if (linkEdit != null) { if (linkEdit != null) {
boolean foundLinkEdit = false; boolean foundLinkEdit = false;
for (DyldCacheHeader header : providerMap.keySet()) { for (DyldCacheHeader h : headers) {
for (DyldCacheMappingInfo mappingInfo : header.getMappingInfos()) { for (DyldCacheMappingInfo mappingInfo : h.getMappingInfos()) {
if (mappingInfo.contains(linkEdit.getVMaddress(), true)) { if (mappingInfo.contains(linkEdit.getVMaddress(), true)) {
analyzeFunctionStarts(program, machoHeader, set, monitor); analyzeFunctionStarts(program, machoHeader, set, monitor);
foundLinkEdit = true; foundLinkEdit = true;
@ -287,7 +293,8 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
else if (usePseudoDisassembler) { else if (usePseudoDisassembler) {
try { try {
final String UDF = "UDF"; final String UDF = "UDF";
if (pdis.disassemble(addr).getMnemonicString().equalsIgnoreCase(UDF)) { PseudoInstruction instr = pdis.disassemble(addr);
if (instr != null && instr.getMnemonicString().equalsIgnoreCase(UDF)) {
skipMessage = "Skipped \"" + UDF + "\" Instruction"; skipMessage = "Skipped \"" + UDF + "\" Instruction";
} }
else if (!pdis.isValidSubroutine(addr, true, false)) { else if (!pdis.isValidSubroutine(addr, true, false)) {
@ -295,7 +302,7 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
} }
} }
catch (Exception e) { catch (Exception e) {
// ignore skipMessage = e.getMessage();
} }
} }
if (skipMessage != null) { if (skipMessage != null) {

View file

@ -363,11 +363,15 @@ public class DynamicSymbolTableCommand extends LoadCommand {
markupPlateComment(program, indirectSymbolTableAddr, source, "indirect"); markupPlateComment(program, indirectSymbolTableAddr, source, "indirect");
Address symbolTableAddr = null;
Address stringTableAddr = null;
SymbolTableCommand symbolTable = header.getFirstLoadCommand(SymbolTableCommand.class); SymbolTableCommand symbolTable = header.getFirstLoadCommand(SymbolTableCommand.class);
Address symbolTableAddr = fileOffsetToAddress(program, header, if (symbolTable != null) {
symbolTable.getSymbolOffset(), symbolTable.getNumberOfSymbols()); symbolTableAddr = fileOffsetToAddress(program, header, symbolTable.getSymbolOffset(),
Address stringTableAddr = fileOffsetToAddress(program, header, symbolTable.getNumberOfSymbols());
stringTableAddr = fileOffsetToAddress(program, header,
symbolTable.getStringTableOffset(), symbolTable.getStringTableSize()); symbolTable.getStringTableOffset(), symbolTable.getStringTableSize());
}
ReferenceManager referenceManager = program.getReferenceManager(); ReferenceManager referenceManager = program.getReferenceManager();
try { try {

View file

@ -175,7 +175,13 @@ public abstract class LoadCommand implements StructConverter {
long size) { long size) {
if (fileOffset != 0 && size != 0) { if (fileOffset != 0 && size != 0) {
AddressSpace space = program.getAddressFactory().getDefaultAddressSpace(); AddressSpace space = program.getAddressFactory().getDefaultAddressSpace();
SegmentCommand segment = getContainingSegment(header, fileOffset); SegmentCommand segment = null;
if (getLinkerDataOffset() != 0) {
segment = header.getSegment(SegmentNames.SEG_LINKEDIT);
}
if (segment == null) {
segment = getContainingSegment(header, fileOffset);
}
if (segment != null) { if (segment != null) {
return space.getAddress( return space.getAddress(
segment.getVMaddress() + (fileOffset - segment.getFileOffset())); segment.getVMaddress() + (fileOffset - segment.getFileOffset()));

View file

@ -36,7 +36,7 @@ import ghidra.util.task.TaskMonitor;
/** /**
* Represents a dyld_cache_accelerator_info structure. * Represents a dyld_cache_accelerator_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> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheAccelerateInfo implements StructConverter { public class DyldCacheAccelerateInfo implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_accelerator_dof structure. * Represents a dyld_cache_accelerator_dof structure.
* *
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheAcceleratorDof implements StructConverter { public class DyldCacheAcceleratorDof implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_accelerator_initializer structure. * Represents a dyld_cache_accelerator_initializer structure.
* *
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheAcceleratorInitializer implements StructConverter { public class DyldCacheAcceleratorInitializer implements StructConverter {

View file

@ -34,7 +34,7 @@ import ghidra.util.task.TaskMonitor;
/** /**
* Represents a dyld_cache_header structure. * Represents a dyld_cache_header structure.
* *
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
public class DyldCacheHeader implements StructConverter { public class DyldCacheHeader implements StructConverter {
@ -115,6 +115,10 @@ public class DyldCacheHeader implements StructConverter {
private long dynamicDataMaxSize; private long dynamicDataMaxSize;
private int tproMappingsOffset; private int tproMappingsOffset;
private int tproMappingsCount; private int tproMappingsCount;
private long functionVariantInfoAddr;
private long functionVariantInfoSize;
private long prewarmingDataOffset;
private long prewarmingDataSize;
private int headerSize; private int headerSize;
private BinaryReader reader; private BinaryReader reader;
@ -358,6 +362,18 @@ public class DyldCacheHeader implements StructConverter {
if (reader.getPointerIndex() < mappingOffset) { if (reader.getPointerIndex() < mappingOffset) {
tproMappingsCount = reader.readNextInt(); tproMappingsCount = reader.readNextInt();
} }
if (reader.getPointerIndex() < mappingOffset) {
functionVariantInfoAddr = reader.readNextLong();
}
if (reader.getPointerIndex() < mappingOffset) {
functionVariantInfoSize = reader.readNextLong();
}
if (reader.getPointerIndex() < mappingOffset) {
prewarmingDataOffset = reader.readNextLong();
}
if (reader.getPointerIndex() < mappingOffset) {
prewarmingDataSize = reader.readNextLong();
}
headerSize = (int) (reader.getPointerIndex() - startIndex); headerSize = (int) (reader.getPointerIndex() - startIndex);
@ -1025,6 +1041,34 @@ public class DyldCacheHeader implements StructConverter {
return tproMappingsCount; return tproMappingsCount;
} }
/**
* {@return the function variant info address}
*/
public long getFunctionVariantInfoAddr() {
return functionVariantInfoAddr;
}
/**
* {@return the function variant info size}
*/
public long getFunctionVariantInfoSize() {
return functionVariantInfoSize;
}
/**
* {@return the pre-warming data offset}
*/
public long getPreWarmingDataOffset() {
return prewarmingDataOffset;
}
/**
* {@return the pre-warming data size}
*/
public long getPreWarmingDataSize() {
return prewarmingDataSize;
}
/** /**
* {@return the reader associated with the header} * {@return the reader associated with the header}
* *
@ -1052,27 +1096,12 @@ public class DyldCacheHeader implements StructConverter {
} }
/** /**
* Generates a {@link List} of {@link DyldCacheImage}s that are mapped in by this * Gets the {@link List} of {@link DyldCacheImageInfo}s. Requires header to have been parsed.
* {@link DyldCacheHeader}. Requires header to have been parsed.
* <p>
* NOTE: A DYLD subcache header may declare an image, but that image may get loaded at an
* address defined by the memory map of a different subcache header. This method will only
* return the images that are mapped by "this" header's memory map.
* *
* @return A {@link List} of {@link DyldCacheImage}s mapped by this {@link DyldCacheHeader} * @return The {@link List} of {@link DyldCacheImageInfo}s
*/ */
public List<DyldCacheImage> getMappedImages() { public List<DyldCacheImageInfo> getImageInfos() {
// NOTE: A subcache will have an entry for every image, but not every image will be mapped return imageInfoList;
List<DyldCacheImage> images = new ArrayList<>();
for (DyldCacheImage imageInfo : imageInfoList) {
for (DyldCacheMappingInfo mappingInfo : mappingInfoList) {
if (mappingInfo.contains(imageInfo.getAddress(), true)) {
images.add(imageInfo);
break;
}
}
}
return images;
} }
/** /**
@ -1213,6 +1242,10 @@ public class DyldCacheHeader implements StructConverter {
addHeaderField(struct, QWORD, "dynamicDataMaxSize", "maximum size of space reserved from dynamic data"); 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, "tproMappingsOffset", "file offset to first dyld_cache_tpro_mapping_info");
addHeaderField(struct, DWORD, "tproMappingsCount", "number of dyld_cache_tpro_mapping_info entries"); addHeaderField(struct, DWORD, "tproMappingsCount", "number of dyld_cache_tpro_mapping_info entries");
addHeaderField(struct, QWORD, "functionVariantInfoAddr", "(unslid) address of dyld_cache_function_variant_info");
addHeaderField(struct, QWORD, "functionVariantInfoSize", "Size of all of the variant information pointed to via the dyld_cache_function_variant_info");
addHeaderField(struct, QWORD, "prewarmingDataOffset", "file offset to dyld_prewarming_header");
addHeaderField(struct, QWORD, "prewarmingDataSize", "byte size of prewarming data");
// @formatter:on // @formatter:on
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY)); struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_image_info structure. * Represents a dyld_cache_image_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> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheImageInfo implements DyldCacheImage, StructConverter { public class DyldCacheImageInfo implements DyldCacheImage, StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_image_info_extra structure. * Represents a dyld_cache_image_info_extra structure.
* *
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheImageInfoExtra implements StructConverter { public class DyldCacheImageInfoExtra implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_image_text_info structure. * Represents a dyld_cache_image_text_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> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheImageTextInfo implements DyldCacheImage, StructConverter { public class DyldCacheImageTextInfo implements DyldCacheImage, StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_local_symbols_entry structure. * Represents a dyld_cache_local_symbols_entry structure.
* *
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
public class DyldCacheLocalSymbolsEntry implements StructConverter { public class DyldCacheLocalSymbolsEntry implements StructConverter {

View file

@ -38,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
/** /**
* Represents a dyld_cache_local_symbols_info structure. * Represents a dyld_cache_local_symbols_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> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheLocalSymbolsInfo implements StructConverter { public class DyldCacheLocalSymbolsInfo implements StructConverter {

View file

@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_mapping_and_slide_info structure. * Represents a dyld_cache_mapping_and_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> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheMappingAndSlideInfo implements StructConverter { public class DyldCacheMappingAndSlideInfo implements StructConverter {

View file

@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_mapping_info structure. * Represents a dyld_cache_mapping_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> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheMappingInfo implements StructConverter { public class DyldCacheMappingInfo implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_cache_range_entry structure. * Represents a dyld_cache_range_entry structure.
* *
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DyldCacheRangeEntry implements StructConverter { public class DyldCacheRangeEntry implements StructConverter {

View file

@ -38,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
* The intent is for the full dyld_cache_slide_info structures to extend this and add their * The intent is for the full dyld_cache_slide_info structures to extend this and add their
* specific parts. * specific parts.
* *
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
public abstract class DyldCacheSlideInfoCommon implements StructConverter { public abstract class DyldCacheSlideInfoCommon implements StructConverter {

View file

@ -28,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException;
/** /**
* Represents a dyld_subcache_entry structure. * Represents a dyld_subcache_entry structure.
* *
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/cache-builder/dyld_cache_format.h">dyld_cache_format.h</a> * @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/include/mach-o/dyld_cache_format.h">dyld_cache_format.h</a>
*/ */
public class DyldSubcacheEntry implements StructConverter { public class DyldSubcacheEntry implements StructConverter {

View file

@ -26,6 +26,7 @@ import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*; import ghidra.app.util.bin.format.macho.commands.*;
import ghidra.app.util.bin.format.macho.dyld.*; import ghidra.app.util.bin.format.macho.dyld.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.DyldCacheUtils.DyldCacheImageRecord;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache; import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.program.database.mem.FileBytes; import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@ -109,6 +110,9 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
} }
} }
// Process DYLIBs
processDylibs(splitDyldCache, localSymbolsPresent);
// Perform additional DYLD processing // Perform additional DYLD processing
for (int i = 0; i < splitDyldCache.size(); i++) { for (int i = 0; i < splitDyldCache.size(); i++) {
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i); DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
@ -118,7 +122,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
markupHeaders(header); markupHeaders(header);
markupBranchIslands(header, bp); markupBranchIslands(header, bp);
createLocalSymbols(header); createLocalSymbols(header);
processDylibs(splitDyldCache, header, bp, localSymbolsPresent);
} }
} }
} }
@ -310,27 +313,26 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* Processes the DYLD Cache's DYLIB files. This will mark up the DYLIB files, added them to the * Processes the DYLD Cache's DYLIB files. This will mark up the DYLIB files, added them to the
* program tree, and make memory blocks for them. * program tree, and make memory blocks for them.
* *
* @param dyldCacheHeader The {@link DyldCacheHeader}
* @param bp The corresponding {@link ByteProvider}
* @param localSymbolsPresent True if DYLD local symbols are present; otherwise, false * @param localSymbolsPresent True if DYLD local symbols are present; otherwise, false
* @throws Exception if there was a problem processing the DYLIB files * @throws Exception if there was a problem processing the DYLIB files
*/ */
private void processDylibs(SplitDyldCache splitDyldCache, DyldCacheHeader dyldCacheHeader, private void processDylibs(SplitDyldCache splitDyldCache, boolean localSymbolsPresent)
ByteProvider bp, boolean localSymbolsPresent) throws Exception { throws Exception {
// Create an "info" object for each DyldCache DYLIB, which will make processing them // Create an "info" object for each DyldCache DYLIB, which will make processing them
// easier. Save off the "libobjc" DYLIB for additional processing later. // easier. Save off the "libobjc" DYLIB for additional processing later.
monitor.setMessage("Parsing DYLIB's..."); monitor.setMessage("Parsing DYLIB's...");
DyldCacheMachoInfo libobjcInfo = null; DyldCacheMachoInfo libobjcInfo = null;
TreeSet<DyldCacheMachoInfo> infoSet = TreeSet<DyldCacheMachoInfo> infoSet =
new TreeSet<>((a, b) -> a.headerAddr.compareTo(b.headerAddr)); new TreeSet<>((a, b) -> a.headerAddr.compareTo(b.headerAddr));
List<DyldCacheImage> mappedImages = dyldCacheHeader.getMappedImages(); List<DyldCacheImageRecord> imageRecords = splitDyldCache.getImageRecords();
monitor.initialize(mappedImages.size()); monitor.initialize(imageRecords.size());
for (DyldCacheImage mappedImage : mappedImages) { for (DyldCacheImageRecord imageRecord : imageRecords) {
monitor.checkCancelled(); monitor.checkCancelled();
monitor.incrementProgress(1); monitor.incrementProgress(1);
DyldCacheMachoInfo info = new DyldCacheMachoInfo(splitDyldCache, bp, DyldCacheImage image = imageRecord.image();
mappedImage.getAddress() - dyldCacheHeader.getBaseAddress(), DyldCacheMachoInfo info =
space.getAddress(mappedImage.getAddress()), mappedImage.getPath()); new DyldCacheMachoInfo(splitDyldCache, splitDyldCache.getMacho(imageRecord),
space.getAddress(image.getAddress()), image.getPath());
infoSet.add(info); infoSet.add(info);
if (libobjcInfo == null && info.name.contains("libobjc.")) { if (libobjcInfo == null && info.name.contains("libobjc.")) {
libobjcInfo = info; libobjcInfo = info;
@ -427,15 +429,15 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* Creates a new {@link DyldCacheMachoInfo} object with the given parameters. * Creates a new {@link DyldCacheMachoInfo} object with the given parameters.
* *
* @param splitDyldCache The {@link SplitDyldCache} * @param splitDyldCache The {@link SplitDyldCache}
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes * @param header The {@link MachHeader#parse(SplitDyldCache) unparsed} {@link MachHeader}
* @param offset The offset in the provider to the start of the Mach-O
* @param headerAddr The Mach-O's header address * @param headerAddr The Mach-O's header address
* @param path The path of the Mach-O * @param path The path of the Mach-O
* @throws Exception If there was a problem handling the Mach-O info * @throws Exception If there was a problem handling the Mach-O info
*/ */
public DyldCacheMachoInfo(SplitDyldCache splitDyldCache, ByteProvider provider, long offset, Address headerAddr, String path) throws Exception { public DyldCacheMachoInfo(SplitDyldCache splitDyldCache, MachHeader header,
Address headerAddr, String path) throws Exception {
this.headerAddr = headerAddr; this.headerAddr = headerAddr;
this.header = new MachHeader(provider, offset, false); this.header = header;
this.header.parse(splitDyldCache); this.header.parse(splitDyldCache);
this.path = path; this.path = path;
this.name = new File(path).getName(); this.name = new File(path).getName();

View file

@ -21,6 +21,8 @@ import java.util.*;
import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.dyld.*; import ghidra.app.util.bin.format.macho.dyld.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.formats.gfilesystem.*; import ghidra.formats.gfilesystem.*;
@ -80,6 +82,43 @@ public class DyldCacheUtils {
return isDyldCache(new String(bytes).trim()); return isDyldCache(new String(bytes).trim());
} }
/**
* A {@link DyldCacheImage} and its corresponding metadata
*
* @param image The {@link DyldCacheImage}
* @param splitCacheIndex The image's index in the {@link SplitDyldCache}
*/
public record DyldCacheImageRecord(DyldCacheImage image, int splitCacheIndex) {}
/**
* Gets all the {@link DyldCacheImageRecord}s for the given {@link List} of
* {@link DyldCacheHeader}s
*
* @param headers The {@link List} of {@link DyldCacheHeader}s
* @return A {@link List} of {@link DyldCacheImageRecord}s
*/
public final static List<DyldCacheImageRecord> getImageRecords(List<DyldCacheHeader> headers) {
Set<Long> addrs = new HashSet<>();
List<DyldCacheImageRecord> imageRecords = new ArrayList<>();
for (DyldCacheHeader header : headers) {
for (DyldCacheImage image : header.getImageInfos()) {
if (addrs.contains(image.getAddress())) {
continue;
}
for (int i = 0; i < headers.size(); i++) {
for (DyldCacheMappingInfo mappingInfo : headers.get(i).getMappingInfos()) {
if (mappingInfo.contains(image.getAddress(), true)) {
imageRecords.add(new DyldCacheImageRecord(image, i));
addrs.add(image.getAddress());
break;
}
}
}
}
}
return imageRecords;
}
/** /**
* Determines if the given signature represents a DYLD cache signature with an architecture we * Determines if the given signature represents a DYLD cache signature with an architecture we
* support. * support.
@ -106,7 +145,7 @@ public class DyldCacheUtils {
private List<ByteProvider> providers = new ArrayList<>(); private List<ByteProvider> providers = new ArrayList<>();
private List<DyldCacheHeader> headers = new ArrayList<>(); private List<DyldCacheHeader> headers = new ArrayList<>();
private List<String> names = new ArrayList<>(); private List<String> names = new ArrayList<>();
private FileSystemService fsService; private FileSystemService fsService = FileSystemService.getInstance();
/** /**
* Creates a new {@link SplitDyldCache} * Creates a new {@link SplitDyldCache}
@ -135,7 +174,6 @@ public class DyldCacheUtils {
baseHeader.getSymbolFileUUID() == null) { baseHeader.getSymbolFileUUID() == null) {
return; return;
} }
fsService = FileSystemService.getInstance();
Map<String, FSRL> uuidToFileMap = new HashMap<>(); Map<String, FSRL> uuidToFileMap = new HashMap<>();
for (FSRL splitFSRL : findSplitDyldCacheFiles(baseProvider.getFSRL(), monitor)) { for (FSRL splitFSRL : findSplitDyldCacheFiles(baseProvider.getFSRL(), monitor)) {
monitor.setMessage("Parsing " + splitFSRL.getName() + " headers..."); monitor.setMessage("Parsing " + splitFSRL.getName() + " headers...");
@ -179,6 +217,22 @@ public class DyldCacheUtils {
} }
} }
/**
* Creates a new {@link SplitDyldCache}
*
* @param providers The cache's ordered {@link ByteProvider}s
* @param headers The cache's ordered {@link DyldCacheHeader}s
* @param names The cache's ordered names
* @param log The log
* @param monitor A cancelable task monitor
*/
public SplitDyldCache(List<ByteProvider> providers, List<DyldCacheHeader> headers,
List<String> names, MessageLog log, TaskMonitor monitor) {
this.providers = new ArrayList<>(providers);
this.headers = new ArrayList<>(headers);
this.names = new ArrayList<>(names);
}
/** /**
* Gets the i'th {@link ByteProvider} in the split DYLD Cache * Gets the i'th {@link ByteProvider} in the split DYLD Cache
* *
@ -242,6 +296,33 @@ public class DyldCacheUtils {
.orElse(null); .orElse(null);
} }
/**
* Gets all the {@link DyldCacheImageRecord}s from the entire cache
*
* @return A {@link List} of {@link DyldCacheImageRecord}s from the entire cache
*/
public List<DyldCacheImageRecord> getImageRecords() {
return DyldCacheUtils.getImageRecords(headers);
}
/**
* Gets the Mach-O of the given {@link DyldCacheImageRecord}.
* <p>
* NOTE: The returned Mach-O is not yet {@link MachHeader#parse(SplitDyldCache) parsed}.
*
* @param imageRecord The desired Mach-O's {@link DyldCacheImageRecord}
* @return The {@link DyldCacheImageRecord}'s Mach-O
* @throws MachException If there was a problem creating the {@link MachHeader}
* @throws IOException If there was an IO-related error
*/
public MachHeader getMacho(DyldCacheImageRecord imageRecord)
throws MachException, IOException {
int i = imageRecord.splitCacheIndex();
DyldCacheImage image = imageRecord.image();
return new MachHeader(providers.get(i),
image.getAddress() - headers.get(i).getBaseAddress(), false);
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
// Assume someone else is responsible for closing the base providers that was passed // Assume someone else is responsible for closing the base providers that was passed

View file

@ -28,6 +28,7 @@ import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.bin.format.macho.commands.SegmentCommand; import ghidra.app.util.bin.format.macho.commands.SegmentCommand;
import ghidra.app.util.bin.format.macho.dyld.*; import ghidra.app.util.bin.format.macho.dyld.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.DyldCacheUtils.DyldCacheImageRecord;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache; import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.formats.gfilesystem.*; import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo; import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
@ -78,26 +79,23 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
RangeSet<Long> allDylibRanges = TreeRangeSet.create(); RangeSet<Long> allDylibRanges = TreeRangeSet.create();
// Find the DYLIB's and add them as files // Find the DYLIB's and add them as files
monitor.initialize(splitDyldCache.size(), "Find DYLD DYLIBs..."); List<DyldCacheImageRecord> imageRecords = splitDyldCache.getImageRecords();
for (int i = 0; i < splitDyldCache.size(); i++) { monitor.initialize(imageRecords.size(), "Find DYLD DYLIBs...");
for (DyldCacheImageRecord imageRecord : imageRecords) {
monitor.increment(); monitor.increment();
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i); DyldCacheImage image = imageRecord.image();
ByteProvider p = splitDyldCache.getProvider(i); MachHeader machHeader = splitDyldCache.getMacho(imageRecord);
for (DyldCacheImage mappedImage : header.getMappedImages()) {
MachHeader machHeader =
new MachHeader(p, mappedImage.getAddress() - header.getBaseAddress());
RangeSet<Long> rangeSet = TreeRangeSet.create(); RangeSet<Long> rangeSet = TreeRangeSet.create();
for (SegmentCommand segment : machHeader.parseSegments()) { for (SegmentCommand segment : machHeader.parseSegments()) {
Range<Long> range = Range.openClosed(segment.getVMaddress(), Range<Long> range = Range.openClosed(segment.getVMaddress(),
segment.getVMaddress() + segment.getVMsize()); segment.getVMaddress() + segment.getVMsize());
rangeSet.add(range); rangeSet.add(range);
} }
DyldCacheEntry entry = DyldCacheEntry entry = new DyldCacheEntry(image.getPath(),
new DyldCacheEntry(mappedImage.getPath(), i, rangeSet, null, null, -1); imageRecord.splitCacheIndex(), rangeSet, null, null, -1);
rangeSet.asRanges().forEach(r -> rangeMap.put(r, entry)); rangeSet.asRanges().forEach(r -> rangeMap.put(r, entry));
allDylibRanges.addAll(rangeSet); allDylibRanges.addAll(rangeSet);
fsIndex.storeFile(mappedImage.getPath(), fsIndex.getFileCount(), false, -1, entry); fsIndex.storeFile(image.getPath(), fsIndex.getFileCount(), false, -1, entry);
}
} }
// Find and store all the mappings for all of the subcaches. We need to remove the DYLIB's // Find and store all the mappings for all of the subcaches. We need to remove the DYLIB's