Improving the efficiency of fixing up dyld_shared_cache slide pointers

This commit is contained in:
Ryan Kurtz 2025-02-11 13:34:27 -05:00
parent eaa8aeb0c8
commit c474967356
17 changed files with 299 additions and 259 deletions

View file

@ -223,7 +223,7 @@ public class MachoFunctionStartsAnalyzer extends AbstractAnalyzer {
boolean foundLinkEdit = false;
for (DyldCacheHeader header : providerMap.keySet()) {
for (DyldCacheMappingInfo mappingInfo : header.getMappingInfos()) {
if (mappingInfo.contains(linkEdit.getVMaddress())) {
if (mappingInfo.contains(linkEdit.getVMaddress(), true)) {
analyzeFunctionStarts(program, machoHeader, set, monitor);
foundLinkEdit = true;
break;

View file

@ -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.
@ -191,7 +191,7 @@ public class LoadCommandFactory {
for (int i = 0; i < splitDyldCache.size(); i++) {
DyldCacheHeader dyldCacheHeader = splitDyldCache.getDyldCacheHeader(i);
for (DyldCacheMappingInfo mappingInfo : dyldCacheHeader.getMappingInfos()) {
if (mappingInfo.contains(linkEdit.getVMaddress())) {
if (mappingInfo.contains(linkEdit.getVMaddress(), true)) {
return new BinaryReader(splitDyldCache.getProvider(i), true);
}
}

View file

@ -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.
@ -206,6 +206,10 @@ public class SegmentCommand extends LoadCommand {
return (flags & SegmentConstants.FLAG_APPLE_PROTECTED) != 0;
}
public boolean is32bit() {
return is32bit;
}
/**
* Returns true if the segment contains the given address
*

View file

@ -402,20 +402,19 @@ public class DyldCacheHeader implements StructConverter {
DyldCacheMappingInfo mappingInfo =
mappingInfoList.get(DyldCacheSlideInfoCommon.DATA_PAGE_MAP_ENTRY);
DyldCacheSlideInfoCommon info = DyldCacheSlideInfoCommon.parseSlideInfo(reader,
slideInfoOffset, mappingInfo.getAddress(), mappingInfo.getSize(),
mappingInfo.getFileOffset(), log, monitor);
slideInfoOffset, mappingInfo, log, monitor);
if (info != null) {
slideInfoList.add(info);
}
}
else if (cacheMappingAndSlideInfoList.size() > 0) {
for (DyldCacheMappingAndSlideInfo info : cacheMappingAndSlideInfoList) {
for (int i = 0; i < cacheMappingAndSlideInfoList.size(); i++) {
DyldCacheMappingAndSlideInfo info = cacheMappingAndSlideInfoList.get(i);
if (info.getSlideInfoFileOffset() == 0) {
continue;
}
DyldCacheSlideInfoCommon slideInfo = DyldCacheSlideInfoCommon.parseSlideInfo(reader,
info.getSlideInfoFileOffset(), info.getAddress(), info.getSize(),
info.getFileOffset(), log, monitor);
info.getSlideInfoFileOffset(), mappingInfoList.get(i), log, monitor);
if (slideInfo != null) {
slideInfoList.add(slideInfo);
}
@ -1067,7 +1066,7 @@ public class DyldCacheHeader implements StructConverter {
List<DyldCacheImage> images = new ArrayList<>();
for (DyldCacheImage imageInfo : imageInfoList) {
for (DyldCacheMappingInfo mappingInfo : mappingInfoList) {
if (mappingInfo.contains(imageInfo.getAddress())) {
if (mappingInfo.contains(imageInfo.getAddress(), true)) {
images.add(imageInfo);
break;
}

View file

@ -187,11 +187,15 @@ public class DyldCacheMappingAndSlideInfo implements StructConverter {
* Returns true if the mapping contains the given address
*
* @param addr The address to check
* @param isAddr True if the {@code addr} parameter is an address; false if it's a file offset
* @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;
public boolean contains(long addr, boolean isAddr) {
return isAddr
? Long.compareUnsigned(addr, address) >= 0 &&
Long.compareUnsigned(addr, address + size) < 0
: Long.compareUnsigned(addr, fileOffset) >= 0 &&
Long.compareUnsigned(addr, fileOffset + size) < 0;
}
@Override

View file

@ -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.
@ -106,15 +106,27 @@ public class DyldCacheMappingInfo implements StructConverter {
return (initProt & SegmentConstants.PROTECTION_X) != 0;
}
public int getMaxProtection() {
return maxProt;
}
public int getInitialProtection() {
return initProt;
}
/**
* Returns true if the mapping contains the given address
*
* @param addr The address to check
* @param isAddr True if the {@code addr} parameter is an address; false if it's a file offset
* @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;
public boolean contains(long addr, boolean isAddr) {
return isAddr
? Long.compareUnsigned(addr, address) >= 0 &&
Long.compareUnsigned(addr, address + size) < 0
: Long.compareUnsigned(addr, fileOffset) >= 0 &&
Long.compareUnsigned(addr, fileOffset + size) < 0;
}
@Override

View file

@ -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.
@ -47,14 +47,12 @@ public class DyldCacheSlideInfo1 extends DyldCacheSlideInfoCommon {
* Create a new {@link DyldCacheSlideInfo1}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 1
* @param mappingAddress The base address of where the slide fixups will take place
* @param mappingSize The size of the slide fixups block
* @param mappingFileOffset The base file offset of where the slide fixups will take place
* @param mappingInfo The {@link DyldCacheMappingInfo} of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 1
*/
public DyldCacheSlideInfo1(BinaryReader reader, long mappingAddress, long mappingSize,
long mappingFileOffset) throws IOException {
super(reader, mappingAddress, mappingSize, mappingFileOffset);
public DyldCacheSlideInfo1(BinaryReader reader, DyldCacheMappingInfo mappingInfo)
throws IOException {
super(reader, mappingInfo);
long startIndex = reader.getPointerIndex() - 4; // version # already read
tocOffset = reader.readNextInt();

View file

@ -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.
@ -51,14 +51,12 @@ public class DyldCacheSlideInfo2 extends DyldCacheSlideInfoCommon {
* Create a new {@link DyldCacheSlideInfo2}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 2
* @param mappingAddress The base address of where the slide fixups will take place
* @param mappingSize The size of the slide fixups block
* @param mappingFileOffset The base file offset of where the slide fixups will take place
* @param mappingInfo The {@link DyldCacheMappingInfo} of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 2
*/
public DyldCacheSlideInfo2(BinaryReader reader, long mappingAddress, long mappingSize,
long mappingFileOffset) throws IOException {
super(reader, mappingAddress, mappingSize, mappingFileOffset);
public DyldCacheSlideInfo2(BinaryReader reader, DyldCacheMappingInfo mappingInfo)
throws IOException {
super(reader, mappingInfo);
pageSize = reader.readNextInt();
pageStartsOffset = reader.readNextInt();
pageStartsCount = reader.readNextInt();

View file

@ -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.
@ -73,14 +73,12 @@ public class DyldCacheSlideInfo3 extends DyldCacheSlideInfoCommon {
* Create a new {@link DyldCacheSlideInfo3}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 3
* @param mappingAddress The base address of where the slide fixups will take place
* @param mappingSize The size of the slide fixups block
* @param mappingFileOffset The base file offset of where the slide fixups will take place
* @param mappingInfo The {@link DyldCacheMappingInfo} of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 3
*/
public DyldCacheSlideInfo3(BinaryReader reader, long mappingAddress, long mappingSize,
long mappingFileOffset) throws IOException {
super(reader, mappingAddress, mappingSize, mappingFileOffset);
public DyldCacheSlideInfo3(BinaryReader reader, DyldCacheMappingInfo mappingInfo)
throws IOException {
super(reader, mappingInfo);
pageSize = reader.readNextInt();
pageStartsCount = reader.readNextInt();
reader.readNextInt(); // padding

View file

@ -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.
@ -116,14 +116,12 @@ public class DyldCacheSlideInfo4 extends DyldCacheSlideInfoCommon {
* Create a new {@link DyldCacheSlideInfo4}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 3
* @param mappingAddress The base address of where the slide fixups will take place
* @param mappingSize The size of the slide fixups block
* @param mappingFileOffset The base file offset of where the slide fixups will take place
* @param mappingInfo The {@link DyldCacheMappingInfo} of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 3
*/
public DyldCacheSlideInfo4(BinaryReader reader, long mappingAddress, long mappingSize,
long mappingFileOffset) throws IOException {
super(reader, mappingAddress, mappingSize, mappingFileOffset);
public DyldCacheSlideInfo4(BinaryReader reader, DyldCacheMappingInfo mappingInfo)
throws IOException {
super(reader, mappingInfo);
pageSize = reader.readNextInt();
pageStartsOffset = reader.readNextInt();
pageStartsCount = reader.readNextInt();

View file

@ -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.
@ -75,14 +75,12 @@ public class DyldCacheSlideInfo5 extends DyldCacheSlideInfoCommon {
* Create a new {@link DyldCacheSlideInfo5}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info 5
* @param mappingAddress The base address of where the slide fixups will take place
* @param mappingSize The size of the slide fixups block
* @param mappingFileOffset The base file offset of where the slide fixups will take place
* @param mappingInfo The {@link DyldCacheMappingInfo} of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info 5
*/
public DyldCacheSlideInfo5(BinaryReader reader, long mappingAddress, long mappingSize,
long mappingFileOffset) throws IOException {
super(reader, mappingAddress, mappingSize, mappingFileOffset);
public DyldCacheSlideInfo5(BinaryReader reader, DyldCacheMappingInfo mappingInfo)
throws IOException {
super(reader, mappingInfo);
pageSize = reader.readNextInt();
pageStartsCount = reader.readNextInt();
reader.readNextInt(); // padding

View file

@ -51,16 +51,13 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info
* @param slideInfoOffset The offset of the slide info to parse
* @param mappingAddress The base address of where the slide fixups will take place
* @param mappingSize The size of the slide fixups block
* @param mappingFileOffset The base file offset of where the slide fixups will take place
* @param mappingInfo The {@link DyldCacheMappingInfo} of where the slide fixups will take place
* @param log The log
* @param monitor A cancelable task monitor
* @return The slide info object
*/
public static DyldCacheSlideInfoCommon parseSlideInfo(BinaryReader reader, long slideInfoOffset,
long mappingAddress, long mappingSize, long mappingFileOffset, MessageLog log,
TaskMonitor monitor) {
DyldCacheMappingInfo mappingInfo, MessageLog log,TaskMonitor monitor) {
if (slideInfoOffset == 0) {
return null;
}
@ -73,16 +70,11 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
int version = reader.readInt(reader.getPointerIndex());
errorMessage += version;
DyldCacheSlideInfoCommon returnedSlideInfo = switch (version) {
case 1 -> new DyldCacheSlideInfo1(reader, mappingAddress, mappingSize,
mappingFileOffset);
case 2 -> new DyldCacheSlideInfo2(reader, mappingAddress, mappingSize,
mappingFileOffset);
case 3 -> new DyldCacheSlideInfo3(reader, mappingAddress, mappingSize,
mappingFileOffset);
case 4 -> new DyldCacheSlideInfo4(reader, mappingAddress, mappingSize,
mappingFileOffset);
case 5 -> new DyldCacheSlideInfo5(reader, mappingAddress, mappingSize,
mappingFileOffset);
case 1 -> new DyldCacheSlideInfo1(reader, mappingInfo);
case 2 -> new DyldCacheSlideInfo2(reader, mappingInfo);
case 3 -> new DyldCacheSlideInfo3(reader, mappingInfo);
case 4 -> new DyldCacheSlideInfo4(reader, mappingInfo);
case 5 -> new DyldCacheSlideInfo5(reader, mappingInfo);
default -> throw new IOException(); // will be caught and version will be added to message
};
monitor.incrementProgress(1);
@ -97,24 +89,18 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
protected int version;
protected long slideInfoOffset;
protected long mappingAddress;
protected long mappingSize;
protected long mappingFileOffset;
protected DyldCacheMappingInfo mappingInfo;
/**
* Create a new {@link DyldCacheSlideInfoCommon}.
*
* @param reader A {@link BinaryReader} positioned at the start of a DYLD slide info
* @param mappingAddress The base address of where the slide fixups will take place
* @param mappingSize The size of the slide fixups block
* @param mappingFileOffset The base file offset of where the slide fixups will take place
* @param mappingInfo The {@link DyldCacheMappingInfo} of where the slide fixups will take place
* @throws IOException if there was an IO-related problem creating the DYLD slide info
*/
public DyldCacheSlideInfoCommon(BinaryReader reader, long mappingAddress, long mappingSize,
long mappingFileOffset) throws IOException {
this.mappingAddress = mappingAddress;
this.mappingSize = mappingSize;
this.mappingFileOffset = mappingFileOffset;
public DyldCacheSlideInfoCommon(BinaryReader reader, DyldCacheMappingInfo mappingInfo)
throws IOException {
this.mappingInfo = mappingInfo;
this.version = reader.readNextInt();
}
@ -135,22 +121,8 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
/**
* {@return The base address of where the slide fixups will take place}
*/
public long getMappingAddress() {
return mappingAddress;
}
/**
* {@return The size of the slide fixups block}
*/
public long getMappingSize() {
return mappingSize;
}
/**
* {@return The base file offset of where the slide fixups will take place}
*/
public long getMappingFileOffset() {
return mappingFileOffset;
public DyldCacheMappingInfo getMappingInfo() {
return mappingInfo;
}
/**
@ -185,7 +157,7 @@ public abstract class DyldCacheSlideInfoCommon implements StructConverter {
Memory memory = program.getMemory();
AddressSpace space = program.getAddressFactory().getDefaultAddressSpace();
Address dataPageAddr = space.getAddress(mappingAddress);
Address dataPageAddr = space.getAddress(mappingInfo.getAddress());
try (ByteProvider provider = new MemoryByteProvider(memory, dataPageAddr)) {
BinaryReader reader = new BinaryReader(provider, !memory.isBigEndian());

View file

@ -281,37 +281,23 @@ public class ExtractedMacho {
* @throws IOException If there was an IO-related issue performing the fix-up
*/
private void fixupLoadCommands() throws IOException {
for (LoadCommand cmd : machoHeader.getLoadCommands()) {
for (LoadCommand lc : machoHeader.getLoadCommands()) {
if (monitor.isCancelled()) {
break;
}
switch (cmd.getCommandType()) {
case LoadCommandTypes.LC_SEGMENT:
fixupSegment((SegmentCommand) cmd, false);
break;
case LoadCommandTypes.LC_SEGMENT_64:
fixupSegment((SegmentCommand) cmd, true);
break;
case LoadCommandTypes.LC_SYMTAB:
fixupSymbolTable((SymbolTableCommand) cmd);
break;
case LoadCommandTypes.LC_DYSYMTAB:
fixupDynamicSymbolTable((DynamicSymbolTableCommand) cmd);
break;
case LoadCommandTypes.LC_DYLD_INFO:
case LoadCommandTypes.LC_DYLD_INFO_ONLY:
fixupDyldInfo((DyldInfoCommand) cmd);
break;
case LoadCommandTypes.LC_CODE_SIGNATURE:
case LoadCommandTypes.LC_SEGMENT_SPLIT_INFO:
case LoadCommandTypes.LC_FUNCTION_STARTS:
case LoadCommandTypes.LC_DATA_IN_CODE:
case LoadCommandTypes.LC_DYLIB_CODE_SIGN_DRS:
case LoadCommandTypes.LC_OPTIMIZATION_HINT:
case LoadCommandTypes.LC_DYLD_EXPORTS_TRIE:
case LoadCommandTypes.LC_DYLD_CHAINED_FIXUPS:
fixupLinkEditData((LinkEditDataCommand) cmd);
break;
switch (lc) {
case SegmentCommand cmd -> fixupSegment(cmd);
case SymbolTableCommand cmd -> fixupSymbolTable(cmd);
case DynamicSymbolTableCommand cmd -> fixupDynamicSymbolTable(cmd);
case DyldInfoCommand cmd -> fixupDyldInfo(cmd);
case LinkEditDataCommand cmd -> fixupLinkEditData(cmd);
case CorruptLoadCommand cmd -> throw new IOException(
"Error fixing corrupt %s at 0x%x".formatted(
LoadCommandTypes.getLoadCommandName(cmd.getCommandType()),
cmd.getStartIndex()));
default -> {
// Do nothing
}
}
}
}
@ -321,11 +307,11 @@ public class ExtractedMacho {
* for the newly packed Mach-O
*
* @param segment The segment to fix-up
* @param is64bit True if the segment is 64-bit; false if 32-bit
* @throws IOException If there was an IO-related issue performing the fix-up
*/
private void fixupSegment(SegmentCommand segment, boolean is64bit) throws IOException {
private void fixupSegment(SegmentCommand segment) throws IOException {
long adjustment = packedSegmentAdjustments.getOrDefault(segment, 0);
boolean is64bit = !segment.is32bit();
set(segment.getStartIndex() + (is64bit ? 0x18 : 0x18), segment.getVMaddress(),
is64bit ? 8 : 4);

View file

@ -18,6 +18,7 @@ package ghidra.file.formats.ios.dyldcache;
import com.google.common.collect.RangeSet;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheMappingAndSlideInfo;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheMappingInfo;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
/**
@ -26,10 +27,13 @@ import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
* @param path The path of the entry
* @param splitCacheIndex The entry's {@link SplitDyldCache} index
* @param rangeSet The entry's address ranges
* @param mappingInfo The entry's {@link DyldCacheMappingAndSlideInfo}; could be null if this entry
* @param mappingInfo The entry's {@link DyldCacheMappingInfo}; could be null if this entry
* represents a DYLIB
* @param mappingIndex The entry's {@link DyldCacheMappingAndSlideInfo} index; ignored if the
* @param mappingAndSlideInfo The entry's {@link DyldCacheMappingAndSlideInfo}; could be null if
* this entry represents a DYLIB, or if the cache is old and doesn't support this structure
* @param mappingIndex The entry's {@link DyldCacheMappingInfo} index; ignored if the
* {@code mappingInfo} is null.
*/
public record DyldCacheEntry(String path, int splitCacheIndex, RangeSet<Long> rangeSet,
DyldCacheMappingAndSlideInfo mappingInfo, int mappingIndex) {}
DyldCacheMappingInfo mappingInfo, DyldCacheMappingAndSlideInfo mappingAndSlideInfo,
int mappingIndex) {}

View file

@ -31,7 +31,6 @@ import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.file.formats.ios.ExtractedMacho;
import ghidra.formats.gfilesystem.FSRL;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.NotFoundException;
import ghidra.util.task.TaskMonitor;
/**
@ -52,7 +51,6 @@ public class DyldCacheExtractor {
*
* @param entry The mapping's {@link DyldCacheEntry}
* @param splitDyldCache The {@link SplitDyldCache}
* @param index The DYLIB's {@link SplitDyldCache} index
* @param slideFixupMap A {@link Map} of {@link DyldFixup}s to perform
* @param fsrl {@link FSRL} to assign to the resulting {@link ByteProvider}
* @param monitor {@link TaskMonitor}
@ -62,12 +60,12 @@ public class DyldCacheExtractor {
* @throws CancelledException If the user cancelled the operation
*/
public static ByteProvider extractDylib(DyldCacheEntry entry, SplitDyldCache splitDyldCache,
int index, Map<DyldCacheSlideInfoCommon, List<DyldFixup>> slideFixupMap,
FSRL fsrl, TaskMonitor monitor) throws IOException, MachException, CancelledException {
Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> slideFixupMap, FSRL fsrl,
TaskMonitor monitor) throws IOException, MachException, CancelledException {
long dylibOffset = entry.rangeSet().asRanges().iterator().next().lowerEndpoint() -
splitDyldCache.getDyldCacheHeader(entry.splitCacheIndex()).getBaseAddress();
ExtractedMacho extractedMacho = new DyldPackedSegments(dylibOffset, splitDyldCache, index,
FOOTER_V1, slideFixupMap, monitor);
ExtractedMacho extractedMacho = new DyldPackedSegments(dylibOffset, splitDyldCache,
entry.splitCacheIndex(), FOOTER_V1, slideFixupMap, monitor);
extractedMacho.pack();
return extractedMacho.getByteProvider(fsrl);
}
@ -78,7 +76,6 @@ public class DyldCacheExtractor {
* @param entry The mapping's {@link DyldCacheEntry}
* @param segmentName The name of the segment in the resulting Mach-O
* @param splitDyldCache The {@link SplitDyldCache}
* @param index The mapping's {@link SplitDyldCache} index
* @param slideFixupMap A {@link Map} of {@link DyldFixup}s to perform
* @param fsrl {@link FSRL} to assign to the resulting {@link ByteProvider}
* @param monitor {@link TaskMonitor}
@ -88,35 +85,15 @@ public class DyldCacheExtractor {
* @throws CancelledException If the user cancelled the operation
*/
public static ByteProvider extractMapping(DyldCacheEntry entry, String segmentName,
SplitDyldCache splitDyldCache, int index,
Map<DyldCacheSlideInfoCommon, List<DyldFixup>> slideFixupMap, FSRL fsrl,
SplitDyldCache splitDyldCache,
Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> slideFixupMap, FSRL fsrl,
TaskMonitor monitor) throws IOException, MachException, CancelledException {
int magic = MachConstants.MH_MAGIC_64;
List<Range<Long>> ranges = new ArrayList<>(entry.rangeSet().asRanges());
DyldCacheMappingAndSlideInfo mappingInfo = entry.mappingInfo();
DyldCacheMappingInfo mappingInfo = entry.mappingInfo();
int allSegmentsSize = SegmentCommand.size(magic) * ranges.size();
// Fix slide pointers
ByteProvider origProvider = splitDyldCache.getProvider(index);
byte[] fixedProviderBytes = origProvider.readBytes(0, origProvider.length());
DyldCacheSlideInfoCommon slideInfo = slideFixupMap.keySet()
.stream()
.filter(e -> e.getMappingAddress() == mappingInfo.getAddress())
.findFirst()
.orElse(null);
if (slideInfo != null) {
List<DyldFixup> slideFixups = slideFixupMap.get(slideInfo);
monitor.initialize(slideFixups.size(), "Fixing slide pointers...");
for (DyldFixup fixup : slideFixups) {
monitor.increment();
long fileOffset = slideInfo.getMappingFileOffset() + fixup.offset();
byte[] newBytes = ExtractedMacho.toBytes(fixup.value(), fixup.size());
System.arraycopy(newBytes, 0, fixedProviderBytes, (int) fileOffset,
newBytes.length);
}
}
// Mach-O Header
byte[] header = MachHeader.create(magic, 0x100000c, 0x80000002, 6, ranges.size(),
allSegmentsSize, 0x42100085, 0);
@ -125,19 +102,22 @@ public class DyldCacheExtractor {
List<byte[]> segments = new ArrayList<>();
List<byte[]> data = new ArrayList<>();
int current = header.length + allSegmentsSize;
try (ByteProvider fixedProvider = new ByteArrayProvider(fixedProviderBytes)) {
try (ByteProvider slidProvider =
new DyldCacheSlidProvider(entry.mappingInfo(), splitDyldCache, entry.splitCacheIndex(),
slideFixupMap, monitor)) {
for (int i = 0; i < ranges.size(); i++) {
Range<Long> range = ranges.get(i);
// Segment Command
long dataSize = range.upperEndpoint() - range.lowerEndpoint();
segments.add(
SegmentCommand.create(magic, "%s.%d.%d".formatted(segmentName, index, i),
SegmentCommand.create(magic,
"%s.%d.%d".formatted(segmentName, entry.splitCacheIndex(), i),
range.lowerEndpoint(), dataSize, current, dataSize,
mappingInfo.getMaxProtection(), mappingInfo.getMaxProtection(), 0, 0));
// Data
data.add(fixedProvider.readBytes(
data.add(slidProvider.readBytes(
range.lowerEndpoint() - mappingInfo.getAddress() + mappingInfo.getFileOffset(),
dataSize));
@ -176,10 +156,10 @@ public class DyldCacheExtractor {
* @throws CancelledException If the user cancelled the operation
* @throws IOException If there was an IO-related issue with getting the slide fixups
*/
public static Map<DyldCacheSlideInfoCommon, List<DyldFixup>> getSlideFixups(
public static Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> getSlideFixups(
SplitDyldCache splitDyldCache, TaskMonitor monitor)
throws CancelledException, IOException {
Map<DyldCacheSlideInfoCommon, List<DyldFixup>> slideFixupMap = new HashMap<>();
Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> slideFixupMap = new HashMap<>();
MessageLog log = new MessageLog();
for (int i = 0; i < splitDyldCache.size(); i++) {
@ -187,13 +167,18 @@ public class DyldCacheExtractor {
ByteProvider bp = splitDyldCache.getProvider(i);
DyldArchitecture arch = header.getArchitecture();
for (DyldCacheSlideInfoCommon slideInfo : header.getSlideInfos()) {
try (ByteProvider wrapper = new ByteProviderWrapper(bp,
slideInfo.getMappingFileOffset(), slideInfo.getMappingSize())) {
DyldCacheMappingInfo mappingInfo = slideInfo.getMappingInfo();
try (ByteProvider wrapper = new ByteProviderWrapper(bp, mappingInfo.getFileOffset(),
mappingInfo.getSize())) {
BinaryReader wrapperReader =
new BinaryReader(wrapper, !arch.getEndianness().isBigEndian());
List<DyldFixup> fixups = slideInfo.getSlideFixups(wrapperReader,
arch.is64bit() ? 8 : 4, log, monitor);
slideFixupMap.put(slideInfo, fixups);
HashMap<Long, DyldFixup> subMap = new HashMap<>();
for (DyldFixup fixup : fixups) {
subMap.put(mappingInfo.getFileOffset() + fixup.offset(), fixup);
}
slideFixupMap.put(mappingInfo, subMap);
}
}
}
@ -209,7 +194,7 @@ public class DyldCacheExtractor {
private static class DyldPackedSegments extends ExtractedMacho {
private SplitDyldCache splitDyldCache;
private Map<DyldCacheSlideInfoCommon, List<DyldFixup>> slideFixupMap;
private Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> slideFixupMap;
/**
* Creates a new {@link DyldPackedSegments} object
@ -226,7 +211,7 @@ public class DyldCacheExtractor {
* @throws CancelledException If the user cancelled the operation
*/
public DyldPackedSegments(long dylibOffset, SplitDyldCache splitDyldCache, int index,
byte[] footer, Map<DyldCacheSlideInfoCommon, List<DyldFixup>> slideFixupMap,
byte[] footer, Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> slideFixupMap,
TaskMonitor monitor) throws MachException, IOException, CancelledException {
super(splitDyldCache.getProvider(index), dylibOffset,
new MachHeader(splitDyldCache.getProvider(index), dylibOffset, false)
@ -236,19 +221,14 @@ public class DyldCacheExtractor {
this.slideFixupMap = slideFixupMap;
}
@Override
public void pack() throws IOException, CancelledException {
super.pack();
fixupSlidePointers();
}
@Override
protected ByteProvider getSegmentProvider(SegmentCommand segment) throws IOException {
for (int i = 0; i < splitDyldCache.size(); i++) {
DyldCacheHeader dyldCacheheader = splitDyldCache.getDyldCacheHeader(i);
for (DyldCacheMappingInfo mappingInfo : dyldCacheheader.getMappingInfos()) {
if (mappingInfo.contains(segment.getVMaddress())) {
return splitDyldCache.getProvider(i);
if (mappingInfo.contains(segment.getVMaddress(), true)) {
return new DyldCacheSlidProvider(mappingInfo, splitDyldCache, i,
slideFixupMap, monitor);
}
}
}
@ -262,54 +242,6 @@ public class DyldCacheExtractor {
DyldCacheLocalSymbolsInfo info = splitDyldCache.getLocalSymbolInfo();
return info != null ? info.getNList(textSegment.getVMaddress() - base) : List.of();
}
/**
* Fixes-up the slide pointers
*
* @throws IOException If there was an IO-related issue performing the fix-up
* @throws CancelledException If the user cancelled the operation
*/
private void fixupSlidePointers() throws IOException, CancelledException {
// TODO; Optimize this fixup algorithm
long total = slideFixupMap.values().stream().flatMap(List::stream).count();
monitor.initialize(total, "Fixing slide pointers...");
for (DyldCacheSlideInfoCommon slideInfo : slideFixupMap.keySet()) {
for (DyldFixup fixup : slideFixupMap.get(slideInfo)) {
monitor.increment();
long addr = slideInfo.getMappingAddress() + fixup.offset();
long fileOffset = slideInfo.getMappingFileOffset() + fixup.offset();
SegmentCommand segment = getSegmentContaining(addr);
if (segment == null) {
// Fixup is not in this Mach-O
continue;
}
byte[] newBytes = ExtractedMacho.toBytes(fixup.value(), fixup.size());
try {
System.arraycopy(newBytes, 0, packed,
(int) getPackedOffset(fileOffset, segment), newBytes.length);
}
catch (NotFoundException e) {
throw new IOException(e);
}
}
}
}
/**
* Gets the {@link SegmentCommand segment} that contains the given virtual address
*
* @param addr The address
* @return The {@link SegmentCommand segment} that contains the given virtual address
*/
private SegmentCommand getSegmentContaining(long addr) {
for (SegmentCommand segment : machoHeader.getAllSegments()) {
if (addr >= segment.getVMaddress() &&
addr < segment.getVMaddress() + segment.getVMsize()) {
return segment;
}
}
return null;
}
}
}

View file

@ -52,7 +52,7 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
private ByteProvider provider;
private SplitDyldCache splitDyldCache;
private boolean parsedLocalSymbols = false;
private Map<DyldCacheSlideInfoCommon, List<DyldFixup>> slideFixupMap;
private Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> slideFixupMap;
private RangeMap<Long, DyldCacheEntry> rangeMap = TreeRangeMap.create();
/**
@ -94,7 +94,7 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
rangeSet.add(range);
}
DyldCacheEntry entry =
new DyldCacheEntry(mappedImage.getPath(), i, rangeSet, null, -1);
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,
@ -110,18 +110,24 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
monitor.increment();
DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i);
String name = splitDyldCache.getName(i);
List<DyldCacheMappingAndSlideInfo> mappingInfos = header.getCacheMappingAndSlideInfos();
List<DyldCacheMappingInfo> mappingInfos = header.getMappingInfos();
List<DyldCacheMappingAndSlideInfo> mappingAndSlideInfos =
header.getCacheMappingAndSlideInfos();
for (int j = 0; j < mappingInfos.size(); j++) {
DyldCacheMappingAndSlideInfo mappingInfo = mappingInfos.get(j);
DyldCacheMappingInfo mappingInfo = mappingInfos.get(j);
DyldCacheMappingAndSlideInfo mappingAndSlideInfo =
!mappingAndSlideInfos.isEmpty() ? mappingAndSlideInfos.get(j) : null;
Range<Long> mappingRange = Range.openClosed(mappingInfo.getAddress(),
mappingInfo.getAddress() + mappingInfo.getSize());
RangeSet<Long> reducedRangeSet = TreeRangeSet.create();
reducedRangeSet.add(mappingRange);
reducedRangeSet.removeAll(allDylibRanges);
for (Range<Long> range : reducedRangeSet.asRanges()) {
String path = getComponentPath(name, mappingInfo, j, range);
String path =
getComponentPath(name, mappingInfo, mappingAndSlideInfo, j, range);
DyldCacheEntry entry = new DyldCacheEntry(path, i,
TreeRangeSet.create(CollectionUtils.asIterable(range)), mappingInfo, j);
TreeRangeSet.create(CollectionUtils.asIterable(range)), mappingInfo,
mappingAndSlideInfo, j);
rangeMap.put(range, entry);
fsIndex.storeFile(path, fsIndex.getFileCount(), false, -1, entry);
}
@ -152,11 +158,11 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
try {
if (entry.mappingInfo() != null) {
return DyldCacheExtractor.extractMapping(entry,
getComponentName(entry.mappingInfo()), splitDyldCache,
entry.splitCacheIndex(), slideFixupMap, file.getFSRL(), monitor);
getComponentName(entry.mappingAndSlideInfo()), splitDyldCache, slideFixupMap,
file.getFSRL(), monitor);
}
return DyldCacheExtractor.extractDylib(entry, splitDyldCache, entry.splitCacheIndex(),
slideFixupMap, file.getFSRL(), monitor);
return DyldCacheExtractor.extractDylib(entry, splitDyldCache, slideFixupMap,
file.getFSRL(), monitor);
}
catch (MachException e) {
throw new IOException("Invalid Mach-O header detected at: " + entry);
@ -213,32 +219,32 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
rangeMap.clear();
}
private String getComponentName(DyldCacheMappingAndSlideInfo mappingInfo) {
String name;
if (mappingInfo.isDirtyData()) {
private String getComponentName(DyldCacheMappingAndSlideInfo mappingAndSlideInfo) {
String name = "DYLD";
if (mappingAndSlideInfo == null) {
return name;
}
if (mappingAndSlideInfo.isDirtyData()) {
name = "DATA_DIRTY";
}
else if (mappingInfo.isConstData()) {
name = mappingInfo.isAuthData() ? "AUTH_CONST" : "DATA_CONST";
else if (mappingAndSlideInfo.isConstData()) {
name = mappingAndSlideInfo.isAuthData() ? "AUTH_CONST" : "DATA_CONST";
}
else if (mappingInfo.isTextStubs()) {
else if (mappingAndSlideInfo.isTextStubs()) {
name = "TEXT_STUBS";
}
else if (mappingInfo.isConfigData()) {
else if (mappingAndSlideInfo.isConfigData()) {
name = "DATA_CONFIG";
}
else if (mappingInfo.isAuthData()) {
else if (mappingAndSlideInfo.isAuthData()) {
name = "AUTH";
}
else if (mappingInfo.isReadOnlyData()) {
else if (mappingAndSlideInfo.isReadOnlyData()) {
name = "DATA_RO";
}
else if (mappingInfo.isConstTproData()) {
else if (mappingAndSlideInfo.isConstTproData()) {
name = "DATA_CONST_TPRO";
}
else {
name = "DYLD";
}
return name;
}
@ -250,9 +256,10 @@ public class DyldCacheFileSystem extends AbstractFileSystem<DyldCacheEntry> {
* @param mappingIndex The mapping index
* @return The DYLD component path of the given DYLD component
*/
private String getComponentPath(String dyldCacheName, DyldCacheMappingAndSlideInfo mappingInfo,
int mappingIndex, Range<Long> range) {
return "/DYLD/%s/%s.%d.0x%x-0x%x".formatted(dyldCacheName, getComponentName(mappingInfo),
mappingIndex, range.lowerEndpoint(), range.upperEndpoint());
private String getComponentPath(String dyldCacheName, DyldCacheMappingInfo mappingInfo,
DyldCacheMappingAndSlideInfo mappingAndSlideInfo, int mappingIndex, Range<Long> range) {
return "/DYLD/%s/%s.%d.0x%x-0x%x".formatted(dyldCacheName,
getComponentName(mappingAndSlideInfo), mappingIndex, range.lowerEndpoint(),
range.upperEndpoint());
}
}

View file

@ -0,0 +1,130 @@
/* ###
* 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.file.formats.ios.dyldcache;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.format.macho.dyld.DyldCacheMappingInfo;
import ghidra.app.util.bin.format.macho.dyld.DyldFixup;
import ghidra.app.util.opinion.DyldCacheUtils.SplitDyldCache;
import ghidra.util.task.TaskMonitor;
/**
* A {@link ByteProvider} that provides slid dyld_shared_cache bytes for a given
* {@link DyldCacheMappingInfo byte mapping}
*/
public class DyldCacheSlidProvider implements ByteProvider {
private String name;
private ByteProvider origProvider;
private DyldCacheMappingInfo mappingInfo;
private Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> slideFixupMap;
private TaskMonitor monitor;
/**
* Creates a new {@link DyldCacheSlidProvider}
*
* @param mappingInfo The {@link DyldCacheMappingInfo} that is being requested
* @param splitDyldCache The {@link SplitDyldCache}
* @param splitCacheIndex The mapping's {@link SplitDyldCache} index
* @param slideFixupMap A {@link Map} of {@link DyldFixup}s to perform
* @param monitor A {@link TaskMonitor} to monitor the reads
*/
public DyldCacheSlidProvider(DyldCacheMappingInfo mappingInfo, SplitDyldCache splitDyldCache,
int splitCacheIndex, Map<DyldCacheMappingInfo, Map<Long, DyldFixup>> slideFixupMap,
TaskMonitor monitor) {
this.name = splitDyldCache.getName(splitCacheIndex);
this.origProvider = splitDyldCache.getProvider(splitCacheIndex);
this.mappingInfo = mappingInfo;
this.slideFixupMap = slideFixupMap;
this.monitor = monitor;
}
@Override
public byte readByte(long index) throws IOException {
if (!mappingInfo.contains(index, false)) {
return origProvider.readByte(index);
}
Map<Long, DyldFixup> fixups = slideFixupMap.get(mappingInfo);
if (fixups == null) {
return origProvider.readByte(index);
}
long aligned = index & ~3;
DyldFixup fixup = fixups.get(aligned);
if (fixup == null) {
aligned = index & ~7;
fixup = fixups.get(aligned);
if (fixup != null && fixup.size() != 8) {
fixup = null;
}
}
if (fixup == null) {
return origProvider.readByte(index);
}
return (byte) (fixup.value() >> ((index - aligned) * 8));
}
@Override
public byte[] readBytes(long index, long length) throws IOException {
if (length < 0 || length > Integer.MAX_VALUE) {
throw new IllegalArgumentException("unsupported length");
}
if (index < 0) {
throw new IllegalArgumentException("invalid index");
}
byte[] ret = new byte[(int) length];
monitor.initialize(length, "Reading " + name);
// TODO: spped this up by using super.readBytes() to read the chunks that don't contain fixups
for (int i = 0; i < length; i++) {
monitor.incrementProgress();
ret[i] = readByte(index + i);
}
return ret;
}
@Override
public File getFile() {
return origProvider.getFile();
}
@Override
public String getName() {
return name;
}
@Override
public String getAbsolutePath() {
return origProvider.getAbsolutePath();
}
@Override
public long length() throws IOException {
return origProvider.length();
}
@Override
public boolean isValidIndex(long i) {
return origProvider.isValidIndex(i);
}
@Override
public void close() throws IOException {
// This is a wrapper, so don't close
}
}