parsePrelinkXml(ByteProvider provider, TaskMonitor monitor)
throws IOException, JDOMException {
try {
@@ -89,39 +89,13 @@ public class MachoPrelinkUtils {
mainHeader.parse(); // make sure first Mach-O header is valid....
monitor.setMessage("Parsing PRELINK XML...");
- return new PrelinkParser(mainHeader, provider).parse(monitor);
+ return new MachoPrelinkParser(mainHeader, provider).parse(monitor);
}
catch (NoPreLinkSectionException | MachException e) {
return Collections.emptyList();
}
}
- /**
- * Check if the Macho has a DYLD_CHAINED_FIXUPS_COMMAND
- *
- * @param provider The provider to parse.
- * @param monitor A monitor.
- * @return A list of discovered {@link PrelinkMap}s. An empty list indicates that the provider
- * did not represent valid Mach-O PRELINK binary.
- * @throws IOException if there was an IO-related issue.
- * @throws JDOMException if there was a issue parsing the PRELINK XML.
- */
- public static boolean hasChainedLoadCommand(ByteProvider provider, TaskMonitor monitor)
- throws IOException, JDOMException {
-
- try {
- MachHeader mainHeader = new MachHeader(provider);
- mainHeader.parse(); // make sure first Mach-O header is valid....
-
- DyldChainedFixupsCommand cmd =
- mainHeader.getFirstLoadCommand(DyldChainedFixupsCommand.class);
- return cmd != null;
- }
- catch (MachException e) {
- return false;
- }
- }
-
/**
* Scans the provider looking for PRELINK Mach-O headers.
*
@@ -166,7 +140,7 @@ public class MachoPrelinkUtils {
* Forms a bidirectional mapping of PRELINK XML to Mach-O header offset in the given provider.
*
* @param provider The PRELINK Mach-O provider.
- * @param prelinkList A list of {@link PrelinkMap}s.
+ * @param prelinkList A list of {@link MachoPrelinkMap}s.
* @param machoHeaderOffsets A list of provider offsets where PRELINK Mach-O headers start (not
* including the "System" Mach-O at offset 0).
* @param monitor A monitor
@@ -174,14 +148,14 @@ public class MachoPrelinkUtils {
* @throws MachException If there was a problem parsing a Mach-O header.
* @throws IOException If there was an IO-related issue mapping PRELINK XML to Mach-O headers.
*/
- public static BidiMap matchPrelinkToMachoHeaderOffsets(ByteProvider provider,
- List prelinkList, List machoHeaderOffsets, TaskMonitor monitor)
+ public static BidiMap matchPrelinkToMachoHeaderOffsets(ByteProvider provider,
+ List prelinkList, List machoHeaderOffsets, TaskMonitor monitor)
throws MachException, IOException {
monitor.setMessage("Matching PRELINK to Mach-O headers...");
monitor.initialize(prelinkList.size());
- BidiMap map = new DualHashBidiMap<>();
+ BidiMap map = new DualHashBidiMap<>();
// For pre-iOS 12, we can use the PrelinkExecutableLoadAddr field to match PrelinkMap
// entries to Mach-O offsets. For iOS 12, PrelinkExecutableLoadAddr is gone so we use
@@ -199,7 +173,7 @@ public class MachoPrelinkUtils {
maxModuleIndex + 1, machoHeaderOffsets.size()));
}
- for (PrelinkMap info : prelinkList) {
+ for (MachoPrelinkMap info : prelinkList) {
if (monitor.isCancelled()) {
break;
}
@@ -222,7 +196,7 @@ public class MachoPrelinkUtils {
MachHeader machoHeader = new MachHeader(provider, 0, true);
machoHeader.parse();
long prelinkStart = MachoPrelinkUtils.getPrelinkStartAddr(machoHeader);
- for (PrelinkMap info : prelinkList) {
+ for (MachoPrelinkMap info : prelinkList) {
if (monitor.isCancelled()) {
break;
}
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 b1238b7ef2..d8c32cefff 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
@@ -15,6 +15,8 @@
*/
package ghidra.app.util.opinion;
+import static ghidra.app.util.bin.format.macho.dyld.DyldChainedPtr.*;
+
import java.math.BigInteger;
import java.util.*;
@@ -24,6 +26,8 @@ import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
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.relocation.*;
import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand;
import ghidra.app.util.bin.format.objectiveC.ObjectiveC1_Constants;
@@ -57,6 +61,7 @@ public class MachoProgramBuilder {
protected Program program;
protected ByteProvider provider;
protected FileBytes fileBytes;
+ protected boolean shouldAddChainedFixupsRelocations;
protected MessageLog log;
protected TaskMonitor monitor;
protected Memory memory;
@@ -69,14 +74,17 @@ public class MachoProgramBuilder {
* @param program The {@link Program} to build up.
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
* @param fileBytes Where the Mach-O's bytes came from.
+ * @param shouldAddChainedFixupsRelocations True if relocations should be added for chained
+ * fixups; otherwise, false.
* @param log The log.
* @param monitor A cancelable task monitor.
*/
protected MachoProgramBuilder(Program program, ByteProvider provider, FileBytes fileBytes,
- MessageLog log, TaskMonitor monitor) {
+ boolean shouldAddChainedFixupsRelocations, MessageLog log, TaskMonitor monitor) {
this.program = program;
this.provider = provider;
this.fileBytes = fileBytes;
+ this.shouldAddChainedFixupsRelocations = shouldAddChainedFixupsRelocations;
this.log = log;
this.monitor = monitor;
this.memory = program.getMemory();
@@ -90,16 +98,18 @@ public class MachoProgramBuilder {
* @param program The {@link Program} to build up.
* @param provider The {@link ByteProvider} that contains the Mach-O's bytes.
* @param fileBytes Where the Mach-O's bytes came from.
+ * @param addChainedFixupsRelocations True if relocations should be added for chained fixups;
+ * otherwise, false.
* @param log The log.
* @param monitor A cancelable task monitor.
* @throws Exception if a problem occurs.
*/
public static void buildProgram(Program program, ByteProvider provider, FileBytes fileBytes,
- MessageLog log, TaskMonitor monitor) throws Exception {
- MachoProgramBuilder machoProgramBuilder =
- new MachoProgramBuilder(program, provider, fileBytes, log, monitor);
+ boolean addChainedFixupsRelocations, MessageLog log, TaskMonitor monitor)
+ throws Exception {
+ MachoProgramBuilder machoProgramBuilder = new MachoProgramBuilder(program, provider,
+ fileBytes, addChainedFixupsRelocations, log, monitor);
machoProgramBuilder.build();
- machoProgramBuilder.doRelocations();
}
protected void build() throws Exception {
@@ -123,19 +133,23 @@ public class MachoProgramBuilder {
renameObjMsgSendRtpSymbol();
processUndefinedSymbols();
processAbsoluteSymbols();
- }
-
- protected void doRelocations() throws Exception {
- processDyldInfo(true);
+ List chainedFixups = processChainedFixups();
+ processDyldInfo(false);
markupHeaders(machoHeader, setupHeaderAddr(machoHeader.getAllSegments()));
markupSections();
processProgramVars();
processSectionRelocations();
processExternalRelocations();
processLocalRelocations();
+ markupChainedFixups(chainedFixups);
}
- private void setImageBase() throws Exception {
+ /**
+ * Sets the image base
+ *
+ * @throws Exception if there was a problem setting the image base
+ */
+ protected void setImageBase() throws Exception {
Address imageBaseAddr = null;
for (SegmentCommand segment : machoHeader.getAllSegments()) {
if (segment.getFileSize() > 0) {
@@ -161,7 +175,7 @@ public class MachoProgramBuilder {
*
* @throws Exception if there was a problem detecting the encrypted block ranges
*/
- private void processEncryption() throws Exception {
+ protected void processEncryption() throws Exception {
monitor.setMessage("Processing encryption...");
for (EncryptedInformationCommand cmd : machoHeader
.getLoadCommands(EncryptedInformationCommand.class)) {
@@ -177,7 +191,7 @@ public class MachoProgramBuilder {
*
* @throws Exception If there was a problem discovering or setting the entry point.
*/
- private void processEntryPoint() throws Exception {
+ protected void processEntryPoint() throws Exception {
monitor.setMessage("Processing entry point...");
Address entryPointAddr = null;
@@ -389,7 +403,7 @@ public class MachoProgramBuilder {
*
* @throws CancelledException if the operation was cancelled.
*/
- private void processUnsupportedLoadCommands() throws CancelledException {
+ protected void processUnsupportedLoadCommands() throws CancelledException {
monitor.setMessage("Processing unsupported load commands...");
for (LoadCommand loadCommand : machoHeader.getLoadCommands(UnsupportedLoadCommand.class)) {
@@ -398,7 +412,7 @@ public class MachoProgramBuilder {
}
}
- private void processSymbolTables() throws Exception {
+ protected void processSymbolTables() throws Exception {
monitor.setMessage("Processing symbol tables...");
List commands = machoHeader.getLoadCommands(SymbolTableCommand.class);
for (SymbolTableCommand symbolTableCommand : commands) {
@@ -466,7 +480,7 @@ public class MachoProgramBuilder {
*
* @throws Exception if there is a problem
*/
- private void processIndirectSymbols() throws Exception {
+ protected void processIndirectSymbols() throws Exception {
monitor.setMessage("Processing indirect symbols...");
@@ -533,7 +547,7 @@ public class MachoProgramBuilder {
}
}
- private void setRelocatableProperty() {
+ protected void setRelocatableProperty() {
Options props = program.getOptions(Program.PROGRAM_INFO);
switch (machoHeader.getFileType()) {
case MachHeaderFileTypes.MH_EXECUTE:
@@ -545,7 +559,7 @@ public class MachoProgramBuilder {
}
}
- private void processLibraries() {
+ protected void processLibraries() {
monitor.setMessage("Processing libraries...");
List commands = machoHeader.getLoadCommands();
for (LoadCommand command : commands) {
@@ -568,7 +582,7 @@ public class MachoProgramBuilder {
}
}
- private void processProgramDescription() {
+ protected void processProgramDescription() {
Options props = program.getOptions(Program.PROGRAM_INFO);
props.setString("Mach-O File Type",
MachHeaderFileTypes.getFileTypeName(machoHeader.getFileType()));
@@ -607,7 +621,7 @@ public class MachoProgramBuilder {
}
}
- private void processUndefinedSymbols() throws Exception {
+ protected void processUndefinedSymbols() throws Exception {
monitor.setMessage("Processing undefined symbols...");
List undefinedSymbols = new ArrayList<>();
@@ -670,7 +684,7 @@ public class MachoProgramBuilder {
}
}
- private void processAbsoluteSymbols() throws Exception {
+ protected void processAbsoluteSymbols() throws Exception {
monitor.setMessage("Processing absolute symbols...");
List absoluteSymbols = new ArrayList<>();
List commands = machoHeader.getLoadCommands();
@@ -857,6 +871,16 @@ public class MachoProgramBuilder {
DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
+ DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
+ StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
+ false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
+ }
+ else if (loadCommand instanceof FileSetEntryCommand) {
+ FileSetEntryCommand fileSetEntryCommand = (FileSetEntryCommand) loadCommand;
+ LoadCommandString name = fileSetEntryCommand.getFileSetEntryId();
+ DataUtilities.createData(program, loadCommandAddr.add(name.getOffset()),
+ StructConverter.STRING, loadCommand.getCommandSize() - name.getOffset(),
+ false, DataUtilities.ClearDataMode.CHECK_FOR_SPACE);
}
else if (loadCommand instanceof LinkerOptionCommand) {
LinkerOptionCommand linkerOptionCommand = (LinkerOptionCommand) loadCommand;
@@ -1435,4 +1459,349 @@ public class MachoProgramBuilder {
}
return originalRelocationBytes;
}
+
+ /**
+ * Fixes up any chained fixups. Relies on the __thread_starts section being present.
+ *
+ * @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 {
+
+ List fixedAddresses = new ArrayList<>();
+
+ // if has Chained Fixups load command, use it
+ List loadCommands =
+ machoHeader.getLoadCommands(DyldChainedFixupsCommand.class);
+ for (LoadCommand loadCommand : loadCommands) {
+ DyldChainedFixupsCommand linkCmd = (DyldChainedFixupsCommand) loadCommand;
+
+ DyldChainedFixupHeader chainHeader = linkCmd.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) {
+ 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();
+ }
+
+ 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;
+ }
+
+ Address chainStart = program.getImageBase().add(headStartOffset & 0xffffffffL);
+ fixedAddresses.addAll(processPointerChain(chainStart, nextOffSize));
+ chainHead = chainHead.add(4);
+ }
+
+ log.appendMsg("Fixed up " + fixedAddresses.size() + " chained pointers.");
+ return fixedAddresses;
+ }
+
+ private List processSegmentPointerChain(DyldChainedFixupHeader chainHeader,
+ DyldChainedStartsInSegment chainStart)
+ throws MemoryAccessException, CancelledException {
+
+ List fixedAddresses = new ArrayList();
+ long fixedAddressCount = 0;
+
+ if (chainStart.getPointerFormat() == 0) {
+ return fixedAddresses;
+ }
+
+ long dataPageStart = chainStart.getSegmentOffset();
+ dataPageStart = dataPageStart + program.getImageBase().getOffset();
+ long pageSize = chainStart.getPageSize();
+ long pageStartsCount = chainStart.getPageCount();
+
+ long authValueAdd = 0;
+
+ short[] pageStarts = chainStart.getPage_starts();
+
+ short ptrFormatValue = chainStart.getPointerFormat();
+ DyldChainType ptrFormat = DyldChainType.lookupChainPtr(ptrFormatValue);
+
+ monitor.setMessage("Fixing " + ptrFormat.getName() + " chained pointers...");
+
+ monitor.setMaximum(pageStartsCount);
+ for (int index = 0; index < pageStartsCount; index++) {
+ monitor.checkCanceled();
+
+ long page = dataPageStart + (pageSize * index);
+
+ monitor.setProgress(index);
+
+ int pageEntry = pageStarts[index] & 0xffff;
+ if (pageEntry == DYLD_CHAINED_PTR_START_NONE) {
+ continue;
+ }
+
+ List unchainedLocList = new ArrayList<>(1024);
+
+ long pageOffset = pageEntry; // first entry is byte based
+
+ switch (ptrFormat) {
+ case DYLD_CHAINED_PTR_ARM64E:
+ case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+ case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+ case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+ processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
+ authValueAdd);
+ break;
+
+ // These might work, but have not been fully tested!
+ case DYLD_CHAINED_PTR_64:
+ case DYLD_CHAINED_PTR_64_OFFSET:
+ case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+ case DYLD_CHAINED_PTR_32:
+ case DYLD_CHAINED_PTR_32_CACHE:
+ case DYLD_CHAINED_PTR_32_FIRMWARE:
+ case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
+ processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
+ authValueAdd);
+ break;
+
+ case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
+ default:
+ log.appendMsg(
+ "WARNING: Pointer Chain format " + ptrFormat + " not processed yet!");
+ break;
+ }
+
+ fixedAddressCount += unchainedLocList.size();
+
+ fixedAddresses.addAll(unchainedLocList);
+ }
+
+ log.appendMsg(
+ "Fixed " + fixedAddressCount + " " + ptrFormat.getName() + " chained pointers.");
+
+ return fixedAddresses;
+ }
+
+ /**
+ * Fixes up any chained pointers, starting at the given address.
+ *
+ * @param chainHeader fixup header chains
+ * @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
+ * @param nextOff offset within the page that is the chain start
+ * @param auth_value_add value to be added to each chain pointer
+ *
+ * @throws MemoryAccessException IO problem reading file
+ * @throws CancelledException user cancels
+ */
+ private void processPointerChain(DyldChainedFixupHeader chainHeader,
+ List unchainedLocList, DyldChainType pointerFormat, long page, long nextOff,
+ long auth_value_add) throws MemoryAccessException, CancelledException {
+
+ long imageBaseOffset = program.getImageBase().getOffset();
+ Address chainStart = memory.getProgram().getLanguage().getDefaultSpace().getAddress(page);
+
+ byte origBytes[] = new byte[8];
+
+ long next = -1;
+ boolean start = true;
+ while (next != 0) {
+ monitor.checkCanceled();
+
+ Address chainLoc = chainStart.add(nextOff);
+ final long chainValue = DyldChainedPtr.getChainValue(memory, chainLoc, pointerFormat);
+ long newChainValue = chainValue;
+
+ boolean isAuthenticated = DyldChainedPtr.isAuthenticated(pointerFormat, chainValue);
+ boolean isBound = DyldChainedPtr.isBound(pointerFormat, chainValue);
+
+ String symName = null;
+
+ if (isAuthenticated && !isBound) {
+ long offsetFromSharedCacheBase =
+ DyldChainedPtr.getTarget(pointerFormat, chainValue);
+ //long diversityData = DyldChainedPtr.getDiversity(pointerFormat, chainValue);
+ //boolean hasAddressDiversity =
+ // DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
+ //long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
+ newChainValue = imageBaseOffset + offsetFromSharedCacheBase + auth_value_add;
+ }
+ else if (!isAuthenticated && isBound) {
+ int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
+ long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
+ DyldChainedImports chainedImports = chainHeader.getChainedImports();
+ DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
+ //int libOrdinal = chainedImport.getLibOrdinal();
+ symName = chainedImport.getName();
+ // lookup the symbol, and then add addend
+ List globalSymbols = program.getSymbolTable().getGlobalSymbols(symName);
+ if (globalSymbols.size() == 1) {
+ newChainValue = globalSymbols.get(0).getAddress().getOffset();
+ }
+ newChainValue += addend;
+ }
+ else if (isAuthenticated && isBound) {
+ int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
+ //long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
+ //long diversityData = DyldChainedPtr.getDiversity(pointerFormat, chainValue);
+ //boolean hasAddressDiversity =
+ // DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
+ //long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
+
+ DyldChainedImports chainedImports = chainHeader.getChainedImports();
+ DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
+ symName = chainedImport.getName();
+
+ // lookup the symbol, and then add addend
+ List globalSymbols = program.getSymbolTable().getGlobalSymbols(symName);
+ if (globalSymbols.size() == 1) {
+ newChainValue = globalSymbols.get(0).getAddress().getOffset();
+ }
+ newChainValue = newChainValue + auth_value_add;
+ }
+ else {
+ newChainValue = DyldChainedPtr.getTarget(pointerFormat, chainValue);
+ newChainValue += imageBaseOffset;
+ }
+
+ if (!start || program.getRelocationTable().getRelocation(chainLoc) == null) {
+ addRelocationTableEntry(chainLoc,
+ (start ? 0x8000 : 0x4000) | (isAuthenticated ? 4 : 0) | (isBound ? 2 : 0) | 1,
+ newChainValue, origBytes, symName);
+ DyldChainedPtr.setChainValue(memory, chainLoc, pointerFormat, newChainValue);
+ }
+ // delay creating data until after memory has been changed
+ unchainedLocList.add(chainLoc);
+
+ start = false;
+ next = DyldChainedPtr.getNext(pointerFormat, chainValue);
+ nextOff += next * DyldChainedPtr.getStride(pointerFormat);
+ }
+ }
+
+ private void addRelocationTableEntry(Address chainLoc, int type, long chainValue,
+ byte[] origBytes, String name) throws MemoryAccessException {
+ if (shouldAddChainedFixupsRelocations) {
+ // Add entry to relocation table for the pointer fixup
+ memory.getBytes(chainLoc, origBytes);
+ program.getRelocationTable()
+ .add(chainLoc, type, new long[] { chainValue }, origBytes, name);
+ }
+ }
+
+ /**
+ * Fixes up any chained pointers, starting at the given address.
+ *
+ * @param chainStart The starting of address of the pointer chain to fix.
+ * @param nextOffSize The size of the next offset.
+ * @return A list of addresses where pointer fixes were performed.
+ * @throws MemoryAccessException if there was a problem reading/writing memory.
+ */
+ private List processPointerChain(Address chainStart, long nextOffSize)
+ throws MemoryAccessException {
+ List fixedAddresses = new ArrayList<>();
+
+ while (!monitor.isCancelled()) {
+ long chainValue = memory.getLong(chainStart);
+
+ fixupPointer(chainStart, chainValue);
+ fixedAddresses.add(chainStart);
+
+ long nextValueOff = ((chainValue >> 51L) & 0x7ffL) * nextOffSize;
+ if (nextValueOff == 0) {
+ break;
+ }
+ chainStart = chainStart.add(nextValueOff);
+ }
+
+ return fixedAddresses;
+ }
+
+ /**
+ * Fixes up the pointer at the given address.
+ *
+ * @param pointerAddr The address of the pointer to fix.
+ * @param pointerValue The value at the address of the pointer to fix.
+ * @throws MemoryAccessException if there was a problem reading/writing memory.
+ */
+ private void fixupPointer(Address pointerAddr, long pointerValue) throws MemoryAccessException {
+
+ final long BIT63 = (0x1L << 63);
+ final long BIT62 = (0x1L << 62);
+
+ // Bad chain value
+ if ((pointerValue & BIT62) != 0) {
+ // this is a pointer, but is good now
+ }
+
+ long fixedPointerValue = 0;
+ long fixedPointerType = 0;
+
+ // Pointer checked value
+ if ((pointerValue & BIT63) != 0) {
+ //long tagType = (pointerValue >> 49L) & 0x3L;
+ long pacMod = ((pointerValue >> 32) & 0xffff);
+ fixedPointerType = pacMod;
+ fixedPointerValue = program.getImageBase().getOffset() + (pointerValue & 0xffffffffL);
+ }
+ else {
+ fixedPointerValue =
+ ((pointerValue << 13) & 0xff00000000000000L) | (pointerValue & 0x7ffffffffffL);
+ if ((pointerValue & 0x40000000000L) != 0) {
+ fixedPointerValue |= 0xfffc0000000000L;
+ }
+ }
+
+ // Add entry to relocation table for the pointer fixup
+ byte origBytes[] = new byte[8];
+ memory.getBytes(pointerAddr, origBytes);
+ program.getRelocationTable()
+ .add(pointerAddr, (int) fixedPointerType, new long[] { fixedPointerValue },
+ origBytes, null);
+
+ // Fixup the pointer
+ memory.setLong(pointerAddr, fixedPointerValue);
+ }
+
+ /**
+ * Markup the given {@link List} of chained fixups by creating pointers at their locations,
+ * if possible
+ *
+ * @param chainedFixups The {@link List} of chained fixups to markup
+ * @throws CancelledException if the operation was cancelled
+ */
+ protected void markupChainedFixups(List chainedFixups) throws CancelledException {
+ for (Address addr : chainedFixups) {
+ monitor.checkCanceled();
+ try {
+ listing.createData(addr, Pointer64DataType.dataType);
+ }
+ catch (CodeUnitInsertionException e) {
+ // No worries, something presumably more important was there already
+ }
+ }
+ }
}
diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/prelink/PrelinkFileSystem.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/prelink/MachoPrelinkFileSystem.java
similarity index 91%
rename from Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/prelink/PrelinkFileSystem.java
rename to Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/prelink/MachoPrelinkFileSystem.java
index 73dc35ddc9..d0cd428b18 100644
--- a/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/prelink/PrelinkFileSystem.java
+++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/file/formats/ios/prelink/MachoPrelinkFileSystem.java
@@ -26,8 +26,8 @@ import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.bin.*;
import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.bin.format.macho.commands.*;
-import ghidra.app.util.bin.format.macho.prelink.PrelinkConstants;
-import ghidra.app.util.bin.format.macho.prelink.PrelinkMap;
+import ghidra.app.util.bin.format.macho.prelink.MachoPrelinkConstants;
+import ghidra.app.util.bin.format.macho.prelink.MachoPrelinkMap;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.*;
import ghidra.formats.gfilesystem.*;
@@ -49,19 +49,19 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException;
import ghidra.util.task.TaskMonitor;
-@FileSystemInfo(type = PrelinkFileSystem.IOS_PRELINK_FSTYPE, description = PrelinkConstants.TITLE, priority = FileSystemInfo.PRIORITY_HIGH, factory = GFileSystemBaseFactory.class)
-public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemProgramProvider {
+@FileSystemInfo(type = MachoPrelinkFileSystem.IOS_PRELINK_FSTYPE, description = MachoPrelinkConstants.TITLE, priority = FileSystemInfo.PRIORITY_HIGH, factory = GFileSystemBaseFactory.class)
+public class MachoPrelinkFileSystem extends GFileSystemBase implements GFileSystemProgramProvider {
public final static String IOS_PRELINK_FSTYPE = "iosprelink";
private final static String SYSTEM_KEXT = "System.kext";
- private Map fileToPrelinkInfoMap = new HashMap<>();
+ private Map fileToPrelinkInfoMap = new HashMap<>();
private Map unnamedMachoFileMap = new HashMap<>();
private Map fileToMachoOffsetMap = new HashMap<>();
private GFileImpl systemKextFile;
private GFileImpl kernelCacheDirectory;
- public PrelinkFileSystem(String fileSystemName, ByteProvider provider) {
+ public MachoPrelinkFileSystem(String fileSystemName, ByteProvider provider) {
super(fileSystemName, provider);
}
@@ -89,7 +89,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
List machoHeaderOffsets =
MachoPrelinkUtils.findPrelinkMachoHeaderOffsets(provider, monitor);
try {
- List prelinkList = MachoPrelinkUtils.parsePrelinkXml(provider, monitor);
+ List prelinkList = MachoPrelinkUtils.parsePrelinkXml(provider, monitor);
if (!prelinkList.isEmpty()) {
processPrelinkWithMacho(prelinkList, machoHeaderOffsets, monitor);
}
@@ -110,7 +110,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
@Override
public FileAttributes getFileAttributes(GFile file, TaskMonitor monitor) {
- PrelinkMap info = fileToPrelinkInfoMap.get(file);
+ MachoPrelinkMap info = fileToPrelinkInfoMap.get(file);
return FileAttributes.of(info != null
? FileAttribute.create(FileAttributeType.COMMENT_ATTR, info.toString())
: null);
@@ -181,8 +181,8 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
provider.length() - offset, monitor);
ByteProvider providerWrapper =
new ByteProviderWrapper(provider, offset, provider.length() - offset);
- MachoProgramBuilder.buildProgram(program, providerWrapper, fileBytes, new MessageLog(),
- monitor);
+ MachoProgramBuilder.buildProgram(program, providerWrapper, fileBytes, false,
+ new MessageLog(), monitor);
AbstractProgramLoader.setProgramProperties(program, providerWrapper,
MachoLoader.MACH_O_NAME);
@@ -237,22 +237,22 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
* Processes PRELINK and Macho-O offsets in order to map files to their Mach-O offsets in the
* providers.
*
- * @param prelinkList The list of discovered {@link PrelinkMap}s.
+ * @param prelinkList The list of discovered {@link MachoPrelinkMap}s.
* @param machoHeaderOffsets The list of provider offsets where prelinked Mach-O headers start.
* @param monitor A monitor
* @throws IOException if an IO-related problem occurred.
* @throws MachException if there was a problem parsing Mach-O headers.
*/
- private void processPrelinkWithMacho(List prelinkList,
+ private void processPrelinkWithMacho(List prelinkList,
List machoHeaderOffsets, TaskMonitor monitor) throws IOException, MachException {
monitor.setMessage("Processing PRELINK with found Mach-O headers...");
monitor.initialize(prelinkList.size());
- BidiMap map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(provider,
+ BidiMap map = MachoPrelinkUtils.matchPrelinkToMachoHeaderOffsets(provider,
prelinkList, machoHeaderOffsets, monitor);
- for (PrelinkMap info : map.keySet()) {
+ for (MachoPrelinkMap info : map.keySet()) {
if (monitor.isCancelled()) {
break;
@@ -293,7 +293,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
continue;
}
- PrelinkMap prelinkMap = fileToPrelinkInfoMap.get(file);
+ MachoPrelinkMap prelinkMap = fileToPrelinkInfoMap.get(file);
if (prelinkMap == null || prelinkMap.getPrelinkExecutableLoadAddr() == -1) {
continue;
}
@@ -331,7 +331,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
}
}
- private GFileImpl storeFile(GFileImpl file, PrelinkMap info) {
+ private GFileImpl storeFile(GFileImpl file, MachoPrelinkMap info) {
if (file == null) {
return file;
}
@@ -359,7 +359,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
}
else if (fileToPrelinkInfoMap.containsKey(asFile) &&
fileToPrelinkInfoMap.get(asFile) != null && file.isDirectory()) {
- PrelinkMap value = fileToPrelinkInfoMap.remove(asFile);
+ MachoPrelinkMap value = fileToPrelinkInfoMap.remove(asFile);
fileToPrelinkInfoMap.put(asDir, value);
Long offset = fileToMachoOffsetMap.remove(asFile);
fileToMachoOffsetMap.put(asDir, offset);
@@ -392,7 +392,7 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
private void processKModInfoStructures(List machoHeaderOffsets, TaskMonitor monitor)
throws IOException {
- Map infoToMachoMap = new HashMap<>();
+ Map infoToMachoMap = new HashMap<>();
kernelCacheDirectory = GFileImpl.fromFilename(this, root, "kernelcache", true, -1, null);
diff --git a/Ghidra/Features/FileFormats/src/main/java/ghidra/plugins/fileformats/FileFormatsPlugin.java b/Ghidra/Features/FileFormats/src/main/java/ghidra/plugins/fileformats/FileFormatsPlugin.java
index 1960457bc6..f34a4aee3d 100644
--- a/Ghidra/Features/FileFormats/src/main/java/ghidra/plugins/fileformats/FileFormatsPlugin.java
+++ b/Ghidra/Features/FileFormats/src/main/java/ghidra/plugins/fileformats/FileFormatsPlugin.java
@@ -34,7 +34,7 @@ import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.services.ProgramManager;
import ghidra.file.crypto.CryptoKeyFileTemplateWriter;
import ghidra.file.eclipse.AndroidProjectCreator;
-import ghidra.file.formats.ios.prelink.PrelinkFileSystem;
+import ghidra.file.formats.ios.prelink.MachoPrelinkFileSystem;
import ghidra.file.jad.JadProcessWrapper;
import ghidra.file.jad.JarDecompiler;
import ghidra.formats.gfilesystem.*;
@@ -273,7 +273,7 @@ public class FileFormatsPlugin extends Plugin implements FrontEndable {
}
FSBRootNode rootNode = ac.getRootOfSelectedNode();
return rootNode != null && rootNode.getFSRef() != null &&
- rootNode.getFSRef().getFilesystem() instanceof PrelinkFileSystem;
+ rootNode.getFSRef().getFilesystem() instanceof MachoPrelinkFileSystem;
})
.popupMenuPath("Load iOS Kernel")
.popupMenuIcon(ImageManager.iOS)