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.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.services.*;
import ghidra.app.util.PseudoDisassembler;
import ghidra.app.util.PseudoInstruction;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.MachException;
import ghidra.app.util.bin.format.macho.MachHeader;
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.opinion.DyldCacheLoader;
import ghidra.app.util.opinion.MachoLoader;
import ghidra.app.util.opinion.*;
import ghidra.app.util.opinion.DyldCacheUtils.DyldCacheImageRecord;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.framework.options.Options;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.disassemble.Disassembler;
@ -191,29 +195,31 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
private void analyzeDyldCacheFunctionStarts(Program program, List<ByteProvider> providers,
AddressSetView set, TaskMonitor monitor, MessageLog log)
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".
for (ByteProvider provider : providers) {
DyldCacheHeader header = new DyldCacheHeader(new BinaryReader(provider, true));
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
for (DyldCacheHeader dyldCacheHeader : providerMap.keySet()) {
List<DyldCacheImage> mappedImages = dyldCacheHeader.getMappedImages();
monitor.initialize(mappedImages.size());
for (DyldCacheImage mappedImage : mappedImages) {
String name = new File(mappedImage.getPath()).getName();
// Process each Mach-O header
try (SplitDyldCache splitDyldCache =
new SplitDyldCache(providers, headers, names, log, monitor)) {
List<DyldCacheImageRecord> imageRecords = DyldCacheUtils.getImageRecords(headers);
monitor.initialize(imageRecords.size());
for (DyldCacheImageRecord imageRecord : imageRecords) {
String name = new File(imageRecord.image().getPath()).getName();
monitor.checkCancelled();
monitor.setMessage("Analyzing function starts for " + name + "...");
monitor.incrementProgress(1);
// Parse Mach-O header
MachHeader machoHeader = new MachHeader(providerMap.get(dyldCacheHeader),
mappedImage.getAddress() - dyldCacheHeader.getBaseAddress(), false);
machoHeader.parse();
MachHeader machoHeader = splitDyldCache.getMacho(imageRecord);
machoHeader.parse(splitDyldCache);
// 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
@ -221,8 +227,8 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
SegmentCommand linkEdit = machoHeader.getSegment(SegmentNames.SEG_LINKEDIT);
if (linkEdit != null) {
boolean foundLinkEdit = false;
for (DyldCacheHeader header : providerMap.keySet()) {
for (DyldCacheMappingInfo mappingInfo : header.getMappingInfos()) {
for (DyldCacheHeader h : headers) {
for (DyldCacheMappingInfo mappingInfo : h.getMappingInfos()) {
if (mappingInfo.contains(linkEdit.getVMaddress(), true)) {
analyzeFunctionStarts(program, machoHeader, set, monitor);
foundLinkEdit = true;
@ -287,7 +293,8 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
else if (usePseudoDisassembler) {
try {
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";
}
else if (!pdis.isValidSubroutine(addr, true, false)) {
@ -295,7 +302,7 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
}
}
catch (Exception e) {
// ignore
skipMessage = e.getMessage();
}
}
if (skipMessage != null) {

View file

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

View file

@ -175,7 +175,13 @@ public abstract class LoadCommand implements StructConverter {
long size) {
if (fileOffset != 0 && size != 0) {
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) {
return space.getAddress(
segment.getVMaddress() + (fileOffset - segment.getFileOffset()));

View file

@ -36,7 +36,7 @@ import ghidra.util.task.TaskMonitor;
/**
* 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")
public class DyldCacheAccelerateInfo implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* 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")
public class DyldCacheAcceleratorDof implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* 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")
public class DyldCacheAcceleratorInitializer implements StructConverter {

View file

@ -34,7 +34,7 @@ import ghidra.util.task.TaskMonitor;
/**
* 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 {
@ -115,6 +115,10 @@ public class DyldCacheHeader implements StructConverter {
private long dynamicDataMaxSize;
private int tproMappingsOffset;
private int tproMappingsCount;
private long functionVariantInfoAddr;
private long functionVariantInfoSize;
private long prewarmingDataOffset;
private long prewarmingDataSize;
private int headerSize;
private BinaryReader reader;
@ -358,6 +362,18 @@ public class DyldCacheHeader implements StructConverter {
if (reader.getPointerIndex() < mappingOffset) {
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);
@ -1025,6 +1041,34 @@ public class DyldCacheHeader implements StructConverter {
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}
*
@ -1052,27 +1096,12 @@ public class DyldCacheHeader implements StructConverter {
}
/**
* Generates a {@link List} of {@link DyldCacheImage}s that are mapped in by this
* {@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.
* Gets the {@link List} of {@link DyldCacheImageInfo}s. Requires header to have been parsed.
*
* @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() {
// NOTE: A subcache will have an entry for every image, but not every image will be mapped
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;
public List<DyldCacheImageInfo> getImageInfos() {
return imageInfoList;
}
/**
@ -1213,6 +1242,10 @@ public class DyldCacheHeader implements StructConverter {
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");
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
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.
*
* @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")
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.
*
* @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")
public class DyldCacheImageInfoExtra implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* 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")
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.
*
* @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 {

View file

@ -38,7 +38,7 @@ import ghidra.util.task.TaskMonitor;
/**
* 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")
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.
*
* @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")
public class DyldCacheMappingAndSlideInfo implements StructConverter {

View file

@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* 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")
public class DyldCacheMappingInfo implements StructConverter {

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* 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")
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
* 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 {

View file

@ -28,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException;
/**
* 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 {

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.dyld.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.DyldCacheUtils.DyldCacheImageRecord;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
@ -109,6 +110,9 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
}
}
// Process DYLIBs
processDylibs(splitDyldCache, localSymbolsPresent);
// Perform additional DYLD processing
for (int i = 0; i < splitDyldCache.size(); i++) {
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
@ -118,7 +122,6 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
markupHeaders(header);
markupBranchIslands(header, bp);
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
* 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
* @throws Exception if there was a problem processing the DYLIB files
*/
private void processDylibs(SplitDyldCache splitDyldCache, DyldCacheHeader dyldCacheHeader,
ByteProvider bp, boolean localSymbolsPresent) throws Exception {
private void processDylibs(SplitDyldCache splitDyldCache, boolean localSymbolsPresent)
throws Exception {
// Create an "info" object for each DyldCache DYLIB, which will make processing them
// easier. Save off the "libobjc" DYLIB for additional processing later.
monitor.setMessage("Parsing DYLIB's...");
DyldCacheMachoInfo libobjcInfo = null;
TreeSet<DyldCacheMachoInfo> infoSet =
new TreeSet<>((a, b) -> a.headerAddr.compareTo(b.headerAddr));
List<DyldCacheImage> mappedImages = dyldCacheHeader.getMappedImages();
monitor.initialize(mappedImages.size());
for (DyldCacheImage mappedImage : mappedImages) {
List<DyldCacheImageRecord> imageRecords = splitDyldCache.getImageRecords();
monitor.initialize(imageRecords.size());
for (DyldCacheImageRecord imageRecord : imageRecords) {
monitor.checkCancelled();
monitor.incrementProgress(1);
DyldCacheMachoInfo info = new DyldCacheMachoInfo(splitDyldCache, bp,
mappedImage.getAddress() - dyldCacheHeader.getBaseAddress(),
space.getAddress(mappedImage.getAddress()), mappedImage.getPath());
DyldCacheImage image = imageRecord.image();
DyldCacheMachoInfo info =
new DyldCacheMachoInfo(splitDyldCache, splitDyldCache.getMacho(imageRecord),
space.getAddress(image.getAddress()), image.getPath());
infoSet.add(info);
if (libobjcInfo == null && info.name.contains("libobjc.")) {
libobjcInfo = info;
@ -427,15 +429,15 @@ public class DyldCacheProgramBuilder extends MachoProgramBuilder {
* Creates a new {@link DyldCacheMachoInfo} object with the given parameters.
*
* @param splitDyldCache The {@link SplitDyldCache}
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes
* @param offset The offset in the provider to the start of the Mach-O
* @param header The {@link MachHeader#parse(SplitDyldCache) unparsed} {@link MachHeader}
* @param headerAddr The Mach-O's header address
* @param path The path of the Mach-O
* @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.header = new MachHeader(provider, offset, false);
this.header = header;
this.header.parse(splitDyldCache);
this.path = path;
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.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.importer.MessageLog;
import ghidra.formats.gfilesystem.*;
@ -80,6 +82,43 @@ public class DyldCacheUtils {
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
* support.
@ -106,7 +145,7 @@ public class DyldCacheUtils {
private List<ByteProvider> providers = new ArrayList<>();
private List<DyldCacheHeader> headers = new ArrayList<>();
private List<String> names = new ArrayList<>();
private FileSystemService fsService;
private FileSystemService fsService = FileSystemService.getInstance();
/**
* Creates a new {@link SplitDyldCache}
@ -135,7 +174,6 @@ public class DyldCacheUtils {
baseHeader.getSymbolFileUUID() == null) {
return;
}
fsService = FileSystemService.getInstance();
Map<String, FSRL> uuidToFileMap = new HashMap<>();
for (FSRL splitFSRL : findSplitDyldCacheFiles(baseProvider.getFSRL(), monitor)) {
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
*
@ -242,6 +296,33 @@ public class DyldCacheUtils {
.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
public void close() throws IOException {
// 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.dyld.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.DyldCacheUtils.DyldCacheImageRecord;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.formats.gfilesystem.*;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
@ -78,26 +79,23 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
RangeSet<Long> allDylibRanges = TreeRangeSet.create();
// Find the DYLIB's and add them as files
monitor.initialize(splitDyldCache.size(), "Find DYLD DYLIBs...");
for (int i = 0; i < splitDyldCache.size(); i++) {
List<DyldCacheImageRecord> imageRecords = splitDyldCache.getImageRecords();
monitor.initialize(imageRecords.size(), "Find DYLD DYLIBs...");
for (DyldCacheImageRecord imageRecord : imageRecords) {
monitor.increment();
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
ByteProvider p = splitDyldCache.getProvider(i);
for (DyldCacheImage mappedImage : header.getMappedImages()) {
MachHeader machHeader =
new MachHeader(p, mappedImage.getAddress() - header.getBaseAddress());
RangeSet<Long> rangeSet = TreeRangeSet.create();
for (SegmentCommand segment : machHeader.parseSegments()) {
Range<Long> range = Range.openClosed(segment.getVMaddress(),
segment.getVMaddress() + segment.getVMsize());
rangeSet.add(range);
}
DyldCacheEntry entry =
new DyldCacheEntry(mappedImage.getPath(), i, rangeSet, null, null, -1);
rangeSet.asRanges().forEach(r -> rangeMap.put(r, entry));
allDylibRanges.addAll(rangeSet);
fsIndex.storeFile(mappedImage.getPath(), fsIndex.getFileCount(), false, -1, entry);
DyldCacheImage image = imageRecord.image();
MachHeader machHeader = splitDyldCache.getMacho(imageRecord);
RangeSet<Long> rangeSet = TreeRangeSet.create();
for (SegmentCommand segment : machHeader.parseSegments()) {
Range<Long> range = Range.openClosed(segment.getVMaddress(),
segment.getVMaddress() + segment.getVMsize());
rangeSet.add(range);
}
DyldCacheEntry entry = new DyldCacheEntry(image.getPath(),
imageRecord.splitCacheIndex(), rangeSet, null, null, -1);
rangeSet.asRanges().forEach(r -> rangeMap.put(r, entry));
allDylibRanges.addAll(rangeSet);
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