diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/SectionNames.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/SectionNames.java index f93b8e0f60..ff446c765a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/SectionNames.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/SectionNames.java @@ -73,4 +73,9 @@ public final class SectionNames { public final static String IMPORT_POINTERS = "__pointers"; /** Section dedicated to holding global program variables */ public final static String PROGRAM_VARS = "__program_vars"; + + /** Section containing dyld_chained_starts_offsets structure */ + public final static String CHAIN_STARTS = "__chain_starts"; + /** Section containing chained fixups */ + public final static String THREAD_STARTS = "__thread_starts"; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldChainedStartsOffsets.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldChainedStartsOffsets.java new file mode 100644 index 0000000000..b8abf9f61f --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/dyld/DyldChainedStartsOffsets.java @@ -0,0 +1,87 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.bin.format.macho.dyld; + +import java.io.IOException; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.bin.format.macho.MachConstants; +import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.DyldChainType; +import ghidra.program.model.data.*; +import ghidra.util.exception.DuplicateNameException; + +/** + * Represents a dyld_chained_starts_offsets structure. + * + * @see mach-o/fixup-chains.h + */ +public class DyldChainedStartsOffsets implements StructConverter { + + private int pointerFormat; + private int startsCount; + private int[] chainStarts; + + /** + * Creates a new {@link DyldChainedStartsOffsets} + * + * @param reader A {@link BinaryReader} positioned at the start of the structure + * @throws IOException if there was an IO-related problem creating the structure + */ + public DyldChainedStartsOffsets(BinaryReader reader) throws IOException { + pointerFormat = reader.readNextInt(); + startsCount = reader.readNextInt(); + chainStarts = reader.readNextIntArray(startsCount); + } + + /** + * Gets the pointer format + * + * @return The pointer format + */ + public DyldChainType getPointerFormat() { + return DyldChainType.lookupChainPtr(pointerFormat); + } + + /** + * Gets the starts count + * + * @return The starts count + */ + public int getStartsCount() { + return startsCount; + } + + /** + * Gets the chain start offsets + * + * @return The chain start offsets + */ + public int[] getChainStartOffsets() { + return chainStarts; + } + + @Override + public DataType toDataType() throws DuplicateNameException, IOException { + StructureDataType struct = new StructureDataType("dyld_chained_starts_offset", 0); + struct.add(DWORD, "pointer_format", "DYLD_CHAINED_PTR_*"); + struct.add(DWORD, "starts_count", "number of starts in array"); + struct.add(new ArrayDataType(IBO32, startsCount, 1), "chain_starts", + "array chain start offsets"); + struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY)); + return struct; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java index 0d161a9589..1b17cf5e0c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java @@ -21,8 +21,7 @@ import java.math.BigInteger; import java.util.*; import ghidra.app.util.MemoryBlockUtils; -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.StructConverter; +import ghidra.app.util.bin.*; import ghidra.app.util.bin.format.RelocationException; import ghidra.app.util.bin.format.macho.*; import ghidra.app.util.bin.format.macho.commands.*; @@ -30,6 +29,7 @@ import ghidra.app.util.bin.format.macho.commands.ExportTrie.ExportEntry; import ghidra.app.util.bin.format.macho.commands.dyld.*; import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr; import ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.DyldChainType; +import ghidra.app.util.bin.format.macho.dyld.DyldChainedStartsOffsets; import ghidra.app.util.bin.format.macho.relocation.*; import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand; import ghidra.app.util.bin.format.objectiveC.ObjectiveC1_Constants; @@ -1117,7 +1117,16 @@ public class MachoProgramBuilder { continue; } - if (section.getType() == SectionTypes.S_CSTRING_LITERALS) { + if (section.getSectionName().equals(SectionNames.CHAIN_STARTS)) { + ByteProvider p = new MemoryByteProvider(memory, block.getStart()); + BinaryReader reader = new BinaryReader(p, machoHeader.isLittleEndian()); + DyldChainedStartsOffsets chainedStartsOffsets = + new DyldChainedStartsOffsets(reader); + DataUtilities.createData(program, block.getStart(), + chainedStartsOffsets.toDataType(), -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + else if (section.getType() == SectionTypes.S_CSTRING_LITERALS) { markupBlock(block, new TerminatedStringDataType()); } else if (section.getType() == SectionTypes.S_4BYTE_LITERALS) { @@ -1580,60 +1589,61 @@ public class MachoProgramBuilder { * @return A list of addresses where chained fixups were performed. * @throws Exception if there was a problem reading/writing memory. */ - protected List
processChainedFixups() throws Exception { + public List
processChainedFixups() throws Exception { + monitor.setMessage("Fixing up chained pointers..."); List
fixedAddresses = new ArrayList<>(); - // if has Chained Fixups load command, use it + // First look for a DyldChainedFixupsCommand List loadCommands = machoHeader.getLoadCommands(DyldChainedFixupsCommand.class); - for (LoadCommand loadCommand : loadCommands) { - DyldChainedFixupsCommand linkCmd = (DyldChainedFixupsCommand) loadCommand; - - DyldChainedFixupHeader chainHeader = linkCmd.getChainHeader(); - + for (DyldChainedFixupsCommand loadCommand : loadCommands) { + DyldChainedFixupHeader chainHeader = loadCommand.getChainHeader(); DyldChainedStartsInImage chainedStartsInImage = chainHeader.getChainedStartsInImage(); - DyldChainedStartsInSegment[] chainedStarts = chainedStartsInImage.getChainedStarts(); for (DyldChainedStartsInSegment chainStart : chainedStarts) { fixedAddresses.addAll(processSegmentPointerChain(chainHeader, chainStart)); } log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers."); } - - // if pointer chains fixed by DyldChainedFixupsCommands, then all finished - if (loadCommands.size() > 0) { + if (!loadCommands.isEmpty()) { return fixedAddresses; } - // if has thread_starts use to fixup chained pointers - Section threadStarts = machoHeader.getSection(SegmentNames.SEG_TEXT, "__thread_starts"); - if (threadStarts == null) { - return Collections.emptyList(); - } + // Didn't find a DyldChainedFixupsCommand, so look for the sections with fixup info + Section chainStartsSection = + machoHeader.getSection(SegmentNames.SEG_TEXT, SectionNames.CHAIN_STARTS); + Section threadStartsSection = + machoHeader.getSection(SegmentNames.SEG_TEXT, SectionNames.THREAD_STARTS); - Address threadSectionStart = null; - Address threadSectionEnd = null; - threadSectionStart = space.getAddress(threadStarts.getAddress()); - threadSectionEnd = threadSectionStart.add(threadStarts.getSize() - 1); - - monitor.setMessage("Fixing up chained pointers..."); - - long nextOffSize = (memory.getInt(threadSectionStart) & 1) * 4 + 4; - Address chainHead = threadSectionStart.add(4); - - while (chainHead.compareTo(threadSectionEnd) < 0 && !monitor.isCancelled()) { - int headStartOffset = memory.getInt(chainHead); - if (headStartOffset == 0xFFFFFFFF || headStartOffset == 0) { - break; + if (chainStartsSection != null) { + Address sectionStart = space.getAddress(chainStartsSection.getAddress()); + ByteProvider p = new MemoryByteProvider(memory, sectionStart); + BinaryReader reader = new BinaryReader(p, machoHeader.isLittleEndian()); + DyldChainedStartsOffsets chainedStartsOffsets = new DyldChainedStartsOffsets(reader); + for (int offset : chainedStartsOffsets.getChainStartOffsets()) { + processPointerChain(null, fixedAddresses, chainedStartsOffsets.getPointerFormat(), + program.getImageBase().add(offset).getOffset(), 0, 0); } - - Address chainStart = program.getImageBase().add(headStartOffset & 0xffffffffL); - fixedAddresses.addAll(processPointerChain(chainStart, nextOffSize)); - chainHead = chainHead.add(4); + log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers."); + } + else if (threadStartsSection != null) { + Address threadSectionStart = space.getAddress(threadStartsSection.getAddress()); + Address threadSectionEnd = threadSectionStart.add(threadStartsSection.getSize() - 1); + long nextOffSize = (memory.getInt(threadSectionStart) & 1) * 4 + 4; + Address chainHead = threadSectionStart.add(4); + while (chainHead.compareTo(threadSectionEnd) < 0 && !monitor.isCancelled()) { + int headStartOffset = memory.getInt(chainHead); + if (headStartOffset == 0xFFFFFFFF || headStartOffset == 0) { + break; + } + Address chainStart = program.getImageBase().add(headStartOffset & 0xffffffffL); + fixedAddresses.addAll(processPointerChain(chainStart, nextOffSize)); + chainHead = chainHead.add(4); + } + log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers."); } - log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers."); return fixedAddresses; } @@ -1721,7 +1731,7 @@ public class MachoProgramBuilder { /** * Fixes up any chained pointers, starting at the given address. * - * @param chainHeader fixup header chains + * @param chainHeader fixup header chains (could be null) * @param unchainedLocList list of locations that were unchained * @param pointerFormat format of pointers within this chain * @param page within data pages that has pointers to be unchained @@ -1736,7 +1746,7 @@ public class MachoProgramBuilder { long auth_value_add) throws MemoryAccessException, CancelledException { long imageBaseOffset = program.getImageBase().getOffset(); - Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page); + Address chainStart = space.getAddress(page); long next = -1; boolean start = true; @@ -1750,6 +1760,13 @@ public class MachoProgramBuilder { boolean isAuthenticated = DyldChainedPtr.isAuthenticated(pointerFormat, chainValue); boolean isBound = DyldChainedPtr.isBound(pointerFormat, chainValue); + if (isBound && chainHeader == null) { + log.appendMsg( + "Error: dyld_chained_fixups_header required to process bound chain fixup at " + + chainLoc); + return; + } + String symName = null; if (isAuthenticated && !isBound) {