mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-5767_ryanmkurtz_dyld' into patch
This commit is contained in:
commit
0a07e885db
20 changed files with 241 additions and 110 deletions
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 <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 {
|
||||
|
|
|
@ -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 <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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 <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 {
|
||||
|
|
|
@ -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 <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 {
|
||||
|
|
|
@ -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 <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 {
|
||||
|
|
|
@ -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 <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 {
|
||||
|
||||
|
|
|
@ -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 <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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 <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 {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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 <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 {
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue