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 dc26406469..f751995705 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 @@ -426,12 +426,35 @@ public class DyldCacheHeader implements StructConverter { } /** - * Gets the {@link List} of {@link DyldCacheImageInfo}s. Requires header to have been parsed. + * Generates a {@link List} of {@link DyldCacheImage}s that are mapped in by this + * {@link DyldCacheHeader}. Requires header to have been parsed. + *

+ * NOTE: A "split" DYLD Cache header may declare an image, but that image may get loaded at an + * address defined by the memory map of a different split header. This method will only return + * the images that are mapped by "this" header's memory map. * - * @return The {@link List} of {@link DyldCacheImageInfo}s + * + * @return A {@link List} of {@link DyldCacheImage}s mapped by this {@link DyldCacheHeader} */ - public List getImageInfos() { - return imageInfoList; + public List getMappedImages() { + List images = new ArrayList<>(); + if (imageInfoList.size() > 0) { + // The old, simple way + images.addAll(imageInfoList); + } + else { + // The new, split file way. A split file will have an entry for every image, but + // not every image will be mapped. + for (DyldCacheImageTextInfo imageTextInfo : imageTextInfoList) { + for (DyldCacheMappingInfo mappingInfo : mappingInfoList) { + if (mappingInfo.contains(imageTextInfo.getAddress())) { + images.add(imageTextInfo); + break; + } + } + } + } + return images; } /** @@ -691,8 +714,8 @@ public class DyldCacheHeader implements StructConverter { monitor.setMessage("Marking up DYLD header..."); monitor.initialize(1); try { - DataUtilities.createData(program, program.getImageBase(), toDataType(), -1, false, - DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + DataUtilities.createData(program, space.getAddress(getBaseAddress()), toDataType(), -1, + false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE); monitor.incrementProgress(1); } catch (CodeUnitInsertionException | DuplicateNameException | IOException e) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImage.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImage.java new file mode 100644 index 0000000000..e3077fff01 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheImage.java @@ -0,0 +1,36 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.macho.dyld; + +/** + * A convenience interface for getting the address and path of a DYLD Cache image + */ +public interface DyldCacheImage { + + /** + * Gets the address the start of the image + * + * @return The address of the start of the image + */ + public long getAddress(); + + /** + * Gets the path of the image + * + * @return The path of the image + */ + public String getPath(); +} 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 084ebc952f..2b0dc702e3 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 @@ -29,7 +29,7 @@ import ghidra.util.exception.DuplicateNameException; * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") -public class DyldCacheImageInfo implements StructConverter { +public class DyldCacheImageInfo implements DyldCacheImage, StructConverter { private long address; private long modTime; @@ -55,20 +55,12 @@ public class DyldCacheImageInfo implements StructConverter { path = reader.readAsciiString(pathFileOffset); } - /** - * Gets the address the start of the image. - * - * @return The address of the start of the image - */ + @Override public long getAddress() { return address; } - /** - * Gets the path of the image. - * - * @return The path of the image - */ + @Override public String getPath() { return path; } 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 a6d08dfec5..4203e07b51 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 @@ -29,7 +29,7 @@ import ghidra.util.exception.DuplicateNameException; * @see dyld3/shared-cache/dyld_cache_format.h */ @SuppressWarnings("unused") -public class DyldCacheImageTextInfo implements StructConverter { +public class DyldCacheImageTextInfo implements DyldCacheImage, StructConverter { private byte[] uuid; private long loadAddress; @@ -52,12 +52,13 @@ public class DyldCacheImageTextInfo implements StructConverter { path = reader.readAsciiString(pathOffset); } + + @Override + public long getAddress() { + return loadAddress; + } - /** - * Gets the path of the image text. - * - * @return The path of the image text. - */ + @Override public String getPath() { return path; } 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 96c62a7731..c9a4184ba4 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 @@ -106,6 +106,17 @@ public class DyldCacheMappingInfo implements StructConverter { return (initProt & SegmentConstants.PROTECTION_X) != 0; } + /** + * Returns true if the mapping contains the given address + * + * @param addr The address to check + * @return True if the mapping contains the given address; otherwise, false + */ + public boolean contains(long addr) { + return Long.compareUnsigned(addr, address) >= 0 && + Long.compareUnsigned(addr, address + size) < 0; + } + @Override public DataType toDataType() throws DuplicateNameException, IOException { StructureDataType struct = new StructureDataType("dyld_cache_mapping_info", 0); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheLoader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheLoader.java index c773b1cc3a..d382fbf293 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheLoader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/DyldCacheLoader.java @@ -18,9 +18,7 @@ package ghidra.app.util.opinion; import java.io.IOException; import java.util.*; -import ghidra.app.util.MemoryBlockUtils; -import ghidra.app.util.Option; -import ghidra.app.util.OptionUtils; +import ghidra.app.util.*; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.format.macho.dyld.DyldArchitecture; @@ -51,11 +49,19 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader { static final boolean CREATE_DYLIB_SECTIONS_OPTION_DEFAULT = false; /** Loader option to add relocation entries for each fixed chain pointer */ - static final String ADD_RELOCATION_ENTRIES_OPTION_NAME = "Add relocation entries for fixed chain pointers"; + static final String ADD_RELOCATION_ENTRIES_OPTION_NAME = + "Add relocation entries for fixed chain pointers"; /** Default value for loader option add relocation entries */ static final boolean ADD_RELOCATION_ENTRIES_OPTION_DEFAULT = false; + /** Loader option to combine split DYLD Cache files (.1, .2, .symbol, etc) into one program */ + static final String COMBINE_SPLIT_FILES_OPTION_NAME = + "Auto import and combine split DYLD Cache files"; + + /** Default value for loader option add relocation entries */ + static final boolean COMBINE_SPLIT_FILES_OPTION_DEFAULT = true; + @Override public Collection findSupportedLoadSpecs(ByteProvider provider) throws IOException { List loadSpecs = new ArrayList<>(); @@ -92,7 +98,8 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader { DyldCacheProgramBuilder.buildProgram(program, provider, MemoryBlockUtils.createFileBytes(program, provider, monitor), shouldProcessSymbols(options), shouldCreateDylibSections(options), - shouldAddRelocationEntries(options), log, monitor); + shouldAddRelocationEntries(options), shouldCombineSplitFiles(options), log, + monitor); } catch (CancelledException e) { return; @@ -113,25 +120,35 @@ public class DyldCacheLoader extends AbstractLibrarySupportLoader { list.add( new Option(CREATE_DYLIB_SECTIONS_OPTION_NAME, CREATE_DYLIB_SECTIONS_OPTION_DEFAULT, Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-createDylibSections")); - list.add( - new Option(ADD_RELOCATION_ENTRIES_OPTION_NAME, ADD_RELOCATION_ENTRIES_OPTION_DEFAULT, - Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-addRelocationEntries")); + list.add(new Option(ADD_RELOCATION_ENTRIES_OPTION_NAME, + ADD_RELOCATION_ENTRIES_OPTION_DEFAULT, Boolean.class, + Loader.COMMAND_LINE_ARG_PREFIX + "-addRelocationEntries")); + list.add(new Option(COMBINE_SPLIT_FILES_OPTION_NAME, COMBINE_SPLIT_FILES_OPTION_DEFAULT, + Boolean.class, Loader.COMMAND_LINE_ARG_PREFIX + "-combineSplitFiles")); } return list; } private boolean shouldProcessSymbols(List