diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/MachoFunctionStartsAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/MachoFunctionStartsAnalyzer.java index b7958b3086..437ec26f7a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/MachoFunctionStartsAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/MachoFunctionStartsAnalyzer.java @@ -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 providers, AddressSetView set, TaskMonitor monitor, MessageLog log) throws MachException, IOException, CancelledException { - Map providerMap = new HashMap<>(); + List headers = new ArrayList<>(); + List 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 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 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) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java index 7bd35c08b9..3f660279ff 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java @@ -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 { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommand.java index fbc846238e..bb2d724452 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommand.java @@ -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())); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAccelerateInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAccelerateInfo.java index eff7e25ce6..baa0206c6e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAccelerateInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAccelerateInfo.java @@ -36,7 +36,7 @@ import ghidra.util.task.TaskMonitor; /** * Represents a dyld_cache_accelerator_info structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheAccelerateInfo implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorDof.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorDof.java index 79620a17cc..b2e82ac1d9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorDof.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorDof.java @@ -4,9 +4,9 @@ * 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. @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_accelerator_dof structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheAcceleratorDof implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorInitializer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorInitializer.java index 2d771e143b..abe7199c2e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorInitializer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheAcceleratorInitializer.java @@ -4,9 +4,9 @@ * 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. @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_accelerator_initializer structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheAcceleratorInitializer implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java index d077242719..075aa38174 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheHeader.java @@ -34,7 +34,7 @@ import ghidra.util.task.TaskMonitor; /** * Represents a dyld_cache_header structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ 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. - *

- * 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 getMappedImages() { - // NOTE: A subcache will have an entry for every image, but not every image will be mapped - List 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 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)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfo.java index 3c111b25ed..c269274171 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfo.java @@ -4,9 +4,9 @@ * 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. @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_image_info structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheImageInfo implements DyldCacheImage, StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfoExtra.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfoExtra.java index ceec876153..bf8998c8d7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfoExtra.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageInfoExtra.java @@ -4,9 +4,9 @@ * 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. @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_image_info_extra structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheImageInfoExtra implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageTextInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageTextInfo.java index bbb94b9b2b..fe58bcf9e3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageTextInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImageTextInfo.java @@ -4,9 +4,9 @@ * 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. @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_image_text_info structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheImageTextInfo implements DyldCacheImage, StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsEntry.java index 24f584b59a..60c5794116 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsEntry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsEntry.java @@ -4,9 +4,9 @@ * 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. @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_local_symbols_entry structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ public class DyldCacheLocalSymbolsEntry implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsInfo.java index 69b8122c6b..8fda44c08d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheLocalSymbolsInfo.java @@ -4,9 +4,9 @@ * 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. @@ -38,7 +38,7 @@ import ghidra.util.task.TaskMonitor; /** * Represents a dyld_cache_local_symbols_info structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheLocalSymbolsInfo implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java index 62ca15c475..279bbe39ed 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingAndSlideInfo.java @@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_mapping_and_slide_info structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheMappingAndSlideInfo implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingInfo.java index 4fe469fdb8..c086a72ed8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheMappingInfo.java @@ -27,7 +27,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_mapping_info structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheMappingInfo implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheRangeEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheRangeEntry.java index 92f89f958a..afa755fc84 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheRangeEntry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheRangeEntry.java @@ -4,9 +4,9 @@ * 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. @@ -26,7 +26,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_cache_range_entry structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ @SuppressWarnings("unused") public class DyldCacheRangeEntry implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java index 85dbee0e00..6f044a660a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfoCommon.java @@ -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 dyld_cache_format.h + * @see dyld_cache_format.h */ public abstract class DyldCacheSlideInfoCommon implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldSubcacheEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldSubcacheEntry.java index 3ab983d3f3..0fff8f530f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldSubcacheEntry.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldSubcacheEntry.java @@ -4,9 +4,9 @@ * 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. @@ -28,7 +28,7 @@ import ghidra.util.exception.DuplicateNameException; /** * Represents a dyld_subcache_entry structure. * - * @see dyld_cache_format.h + * @see dyld_cache_format.h */ public class DyldSubcacheEntry implements StructConverter { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java index bcb6e6cd03..56334b709e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheProgramBuilder.java @@ -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 infoSet = new TreeSet<>((a, b) -> a.headerAddr.compareTo(b.headerAddr)); - List mappedImages = dyldCacheHeader.getMappedImages(); - monitor.initialize(mappedImages.size()); - for (DyldCacheImage mappedImage : mappedImages) { + List 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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheUtils.java index c6909cd968..5cdbfbff04 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheUtils.java @@ -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 getImageRecords(List headers) { + Set addrs = new HashSet<>(); + List 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 providers = new ArrayList<>(); private List headers = new ArrayList<>(); private List 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 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 providers, List headers, + List 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 getImageRecords() { + return DyldCacheUtils.getImageRecords(headers); + } + + /** + * Gets the Mach-O of the given {@link DyldCacheImageRecord}. + *

+ * 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 diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java index 4459124c8b..b1154740df 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheFileSystem.java @@ -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 { RangeSet 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 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 rangeSet = TreeRangeSet.create(); - for (SegmentCommand segment : machHeader.parseSegments()) { - Range 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 rangeSet = TreeRangeSet.create(); + for (SegmentCommand segment : machHeader.parseSegments()) { + Range 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