From c4749673565dcd5e27760fa510d2d3f6bcb4241e Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Tue, 11 Feb 2025 13:34:27 -0500 Subject: [PATCH] Improving the efficiency of fixing up dyld_shared_cache slide pointers --- .../analysis/MachoFunctionStartsAnalyzer.java | 2 +- .../macho/commands/LoadCommandFactory.java | 6 +- .../format/macho/commands/SegmentCommand.java | 8 +- .../format/macho/dyld/DyldCacheHeader.java | 11 +- .../dyld/DyldCacheMappingAndSlideInfo.java | 10 +- .../macho/dyld/DyldCacheMappingInfo.java | 22 ++- .../macho/dyld/DyldCacheSlideInfo1.java | 14 +- .../macho/dyld/DyldCacheSlideInfo2.java | 14 +- .../macho/dyld/DyldCacheSlideInfo3.java | 14 +- .../macho/dyld/DyldCacheSlideInfo4.java | 14 +- .../macho/dyld/DyldCacheSlideInfo5.java | 14 +- .../macho/dyld/DyldCacheSlideInfoCommon.java | 58 ++------ .../file/formats/ios/ExtractedMacho.java | 46 +++---- .../formats/ios/dyldcache/DyldCacheEntry.java | 10 +- .../ios/dyldcache/DyldCacheExtractor.java | 124 ++++------------- .../ios/dyldcache/DyldCacheFileSystem.java | 61 ++++---- .../ios/dyldcache/DyldCacheSlidProvider.java | 130 ++++++++++++++++++ 17 files changed, 299 insertions(+), 259 deletions(-) create mode 100644 Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheSlidProvider.java 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 4e3df6eb1e..b7958b3086 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 @@ -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; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandFactory.java index 3c83019c5f..02a368eaf3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommandFactory.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. @@ -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); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java index c8efe570af..51bd05adda 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SegmentCommand.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. @@ -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 * 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 bf14834a37..f388ee3867 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 @@ -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 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; } 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 a6af0f5e69..62ca15c475 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 @@ -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 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 7e3ce46373..4fe469fdb8 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 @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java index 7d4c4d2227..44277cb0ae 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo1.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. @@ -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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java index 8fba1eb1cf..cc224b1e1a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo2.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. @@ -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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java index d20096a458..1f9970985f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo3.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. @@ -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 diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java index e17f932821..362e08d2b3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo4.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. @@ -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(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo5.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo5.java index 2bfb271ace..7d280452fe 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo5.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldCacheSlideInfo5.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. @@ -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 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 890d7c250d..84427ee984 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 @@ -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()); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/ExtractedMacho.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/ExtractedMacho.java index d67ccd03e9..4c4de2f9c3 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/ExtractedMacho.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/ExtractedMacho.java @@ -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); diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheEntry.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheEntry.java index 99d94cbcc5..7a28f45387 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheEntry.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheEntry.java @@ -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 rangeSet, - DyldCacheMappingAndSlideInfo mappingInfo, int mappingIndex) {} + DyldCacheMappingInfo mappingInfo, DyldCacheMappingAndSlideInfo mappingAndSlideInfo, + int mappingIndex) {} diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheExtractor.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheExtractor.java index 289fa135ed..d5c748f00e 100644 --- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheExtractor.java +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheExtractor.java @@ -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> slideFixupMap, - FSRL fsrl, TaskMonitor monitor) throws IOException, MachException, CancelledException { + Map> 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> slideFixupMap, FSRL fsrl, + SplitDyldCache splitDyldCache, + Map> slideFixupMap, FSRL fsrl, TaskMonitor monitor) throws IOException, MachException, CancelledException { int magic = MachConstants.MH_MAGIC_64; List> 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 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 segments = new ArrayList<>(); List 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 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> getSlideFixups( + public static Map> getSlideFixups( SplitDyldCache splitDyldCache, TaskMonitor monitor) throws CancelledException, IOException { - Map> slideFixupMap = new HashMap<>(); + Map> 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 fixups = slideInfo.getSlideFixups(wrapperReader, arch.is64bit() ? 8 : 4, log, monitor); - slideFixupMap.put(slideInfo, fixups); + HashMap 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> slideFixupMap; + private Map> 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> slideFixupMap, + byte[] footer, Map> 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; - } } } 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 620c132ddb..65127f3860 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 @@ -52,7 +52,7 @@ public class DyldCacheFileSystem extends AbstractFileSystem { private ByteProvider provider; private SplitDyldCache splitDyldCache; private boolean parsedLocalSymbols = false; - private Map> slideFixupMap; + private Map> slideFixupMap; private RangeMap rangeMap = TreeRangeMap.create(); /** @@ -94,7 +94,7 @@ public class DyldCacheFileSystem extends AbstractFileSystem { 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 { monitor.increment(); DyldCacheHeader header = splitDyldCache.getDyldCacheHeader(i); String name = splitDyldCache.getName(i); - List mappingInfos = header.getCacheMappingAndSlideInfos(); + List mappingInfos = header.getMappingInfos(); + List 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 mappingRange = Range.openClosed(mappingInfo.getAddress(), mappingInfo.getAddress() + mappingInfo.getSize()); RangeSet reducedRangeSet = TreeRangeSet.create(); reducedRangeSet.add(mappingRange); reducedRangeSet.removeAll(allDylibRanges); for (Range 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 { 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 { 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 { * @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 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 range) { + return "/DYLD/%s/%s.%d.0x%x-0x%x".formatted(dyldCacheName, + getComponentName(mappingAndSlideInfo), mappingIndex, range.lowerEndpoint(), + range.upperEndpoint()); } } diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheSlidProvider.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheSlidProvider.java new file mode 100644 index 0000000000..0f5c065a33 --- /dev/null +++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/dyldcache/DyldCacheSlidProvider.java @@ -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> 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> 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 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 + } +}