From cf3cc03f09eb32a5601e3fee18c58c53f56dbdf1 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Wed, 2 Aug 2023 07:25:32 -0400 Subject: [PATCH] GP-3565: More Mach-O markup improvements --- .../macho/commands/DataInCodeCommand.java | 4 +- .../commands/DyldChainedFixupsCommand.java | 4 +- .../commands/DyldExportsTrieCommand.java | 32 ++ .../macho/commands/DyldInfoCommand.java | 133 +++++++- .../commands/DyldInfoCommandConstants.java | 28 +- .../commands/DynamicSymbolTableCommand.java | 3 +- .../bin/format/macho/commands/ExportTrie.java | 27 ++ .../macho/commands/FunctionStartsCommand.java | 4 +- .../format/macho/commands/LoadCommand.java | 21 +- .../macho/commands/SymbolTableCommand.java | 4 +- .../dyld/AbstractDyldInfoProcessor.java | 120 ------- .../commands/dyld/AbstractDyldInfoState.java | 160 --------- .../macho/commands/dyld/BindOpcode.java | 88 +++++ .../macho/commands/dyld/BindProcessor.java | 145 -------- .../format/macho/commands/dyld/BindState.java | 82 ----- .../macho/commands/dyld/BindingTable.java | 321 ++++++++++++++++++ .../commands/dyld/LazyBindProcessor.java | 138 -------- .../macho/commands/dyld/LazyBindState.java | 72 ---- .../app/util/opinion/MachoProgramBuilder.java | 62 ++-- 19 files changed, 666 insertions(+), 782 deletions(-) delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoProcessor.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindOpcode.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindProcessor.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindState.java create mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindingTable.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/LazyBindProcessor.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/LazyBindState.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DataInCodeCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DataInCodeCommand.java index 739ba5b5ec..77cbad768a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DataInCodeCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DataInCodeCommand.java @@ -79,8 +79,8 @@ public class DataInCodeCommand extends LinkEditDataCommand { } } catch (Exception e) { - log.appendMsg(DyldChainedFixupsCommand.class.getSimpleName(), "Failed to markup %s." - .formatted(LoadCommandTypes.getLoadCommandName(getCommandType()))); + log.appendMsg(DataInCodeCommand.class.getSimpleName(), + "Failed to markup: " + getContextualName(source, null)); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupsCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupsCommand.java index 36beec73c5..e3c0628759 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupsCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldChainedFixupsCommand.java @@ -100,8 +100,8 @@ public class DyldChainedFixupsCommand extends LinkEditDataCommand { } } catch (Exception e) { - log.appendMsg(DyldChainedFixupsCommand.class.getSimpleName(), "Failed to markup %s." - .formatted(LoadCommandTypes.getLoadCommandName(getCommandType()))); + log.appendMsg(DyldChainedFixupsCommand.class.getSimpleName(), + "Failed to markup: " + getContextualName(source, null)); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldExportsTrieCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldExportsTrieCommand.java index 2ac95cdb13..1ee6c75957 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldExportsTrieCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldExportsTrieCommand.java @@ -18,6 +18,13 @@ package ghidra.app.util.bin.format.macho.commands; import java.io.IOException; import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.macho.MachHeader; +import ghidra.app.util.importer.MessageLog; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.DataUtilities; +import ghidra.program.model.listing.Program; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; /** * Represents a LC_DYLD_EXPORTS_TRIE command @@ -49,4 +56,29 @@ public class DyldExportsTrieCommand extends LinkEditDataCommand { public ExportTrie getExportTrie() { return exportTrie; } + + @Override + public void markup(Program program, MachHeader header, String source, TaskMonitor monitor, + MessageLog log) throws CancelledException { + Address addr = fileOffsetToAddress(program, header, dataoff, datasize); + if (addr == null) { + return; + } + super.markup(program, header, source, monitor, log); + + try { + for (long offset : exportTrie.getUlebOffsets()) { + DataUtilities.createData(program, addr.add(offset), ULEB128, -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + for (long offset : exportTrie.getStringOffsets()) { + DataUtilities.createData(program, addr.add(offset), STRING, -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + } + catch (Exception e) { + log.appendMsg(DyldExportsTrieCommand.class.getSimpleName(), + "Failed to markup: " + getContextualName(source, null)); + } + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommand.java index 535b68262c..192feb9584 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommand.java @@ -20,6 +20,8 @@ import java.io.IOException; import ghidra.app.util.bin.BinaryReader; import ghidra.app.util.bin.format.macho.MachConstants; import ghidra.app.util.bin.format.macho.MachHeader; +import ghidra.app.util.bin.format.macho.commands.dyld.BindOpcode; +import ghidra.app.util.bin.format.macho.commands.dyld.BindingTable; import ghidra.app.util.importer.MessageLog; import ghidra.program.flatapi.FlatProgramAPI; import ghidra.program.model.address.Address; @@ -45,6 +47,9 @@ public class DyldInfoCommand extends LoadCommand { private int exportOff; private int exportSize; + private BindingTable bindingTable; + private BindingTable weakBindingTable; + private BindingTable lazyBindingTable; private ExportTrie exportTrie; /** @@ -72,6 +77,32 @@ public class DyldInfoCommand extends LoadCommand { exportOff = loadCommandReader.readNextInt(); exportSize = loadCommandReader.readNextInt(); + // TODO: rebase + + if (bindOff > 0 && bindSize > 0) { + dataReader.setPointerIndex(header.getStartIndex() + bindOff); + bindingTable = new BindingTable(dataReader, header, bindSize, false); + } + else { + bindingTable = new BindingTable(); + } + + if (weakBindOff > 0 && weakBindSize > 0) { + dataReader.setPointerIndex(header.getStartIndex() + weakBindOff); + weakBindingTable = new BindingTable(dataReader, header, weakBindSize, false); + } + else { + weakBindingTable = new BindingTable(); + } + + if (lazyBindOff > 0 && lazyBindSize > 0) { + dataReader.setPointerIndex(header.getStartIndex() + lazyBindOff); + lazyBindingTable = new BindingTable(dataReader, header, lazyBindSize, true); + } + else { + lazyBindingTable = new BindingTable(); + } + if (exportOff > 0 && exportSize > 0) { dataReader.setPointerIndex(header.getStartIndex() + exportOff); exportTrie = new ExportTrie(dataReader); @@ -151,6 +182,27 @@ public class DyldInfoCommand extends LoadCommand { return exportSize; } + /** + * {@return The binding table} + */ + public BindingTable getBindingTable() { + return bindingTable; + } + + /** + * {@return The lazy binding table} + */ + public BindingTable getLazyBindingTable() { + return lazyBindingTable; + } + + /** + * {@return The weak binding table} + */ + public BindingTable getWeakBindingTable() { + return weakBindingTable; + } + /** * {@return The export trie} */ @@ -186,11 +238,10 @@ public class DyldInfoCommand extends LoadCommand { public void markup(Program program, MachHeader header, String source, TaskMonitor monitor, MessageLog log) throws CancelledException { markupRebaseInfo(program, header, source, monitor, log); - markupBindInfo(program, header, source, monitor, log); - markupWeakBindInfo(program, header, source, monitor, log); - markupLazyBindInfo(program, header, source, monitor, log); + markupBindings(program, header, source, monitor, log); + markupWeakBindings(program, header, source, monitor, log); + markupLazyBindings(program, header, source, monitor, log); markupExportInfo(program, header, source, monitor, log); - } private void markupRebaseInfo(Program program, MachHeader header, String source, @@ -199,28 +250,80 @@ public class DyldInfoCommand extends LoadCommand { source, "rebase"); } - private void markupBindInfo(Program program, MachHeader header, String source, + private void markupBindings(Program program, MachHeader header, String source, TaskMonitor monitor, MessageLog log) { - markupPlateComment(program, fileOffsetToAddress(program, header, bindOff, bindSize), - source, "bind"); + Address bindAddr = fileOffsetToAddress(program, header, bindOff, bindSize); + markupPlateComment(program, bindAddr, source, "bind"); + markupBindingTable(program, bindAddr, bindingTable, source, "bind", log); } - private void markupWeakBindInfo(Program program, MachHeader header, String source, + private void markupWeakBindings(Program program, MachHeader header, String source, TaskMonitor monitor, MessageLog log) { - markupPlateComment(program, fileOffsetToAddress(program, header, weakBindOff, weakBindSize), - source, "weak bind"); + Address addr = fileOffsetToAddress(program, header, weakBindOff, weakBindSize); + markupPlateComment(program, addr, source, "weak bind"); + markupBindingTable(program, addr, weakBindingTable, source, "weak bind", log); + } - private void markupLazyBindInfo(Program program, MachHeader header, String source, + private void markupLazyBindings(Program program, MachHeader header, String source, TaskMonitor monitor, MessageLog log) { - markupPlateComment(program, fileOffsetToAddress(program, header, lazyBindOff, lazyBindSize), - source, "lazy bind"); + Address addr = fileOffsetToAddress(program, header, lazyBindOff, lazyBindSize); + markupPlateComment(program, addr, source, "lazy bind"); + markupBindingTable(program, addr, lazyBindingTable, source, "lazy bind", log); + } + + private void markupBindingTable(Program program, Address addr, BindingTable table, + String source, String additionalDescription, MessageLog log) { + if (addr == null) { + return; + } + try { + DataType bindOpcodeDataType = BindOpcode.toDataType(); + for (long offset : table.getOpcodeOffsets()) { + DataUtilities.createData(program, addr.add(offset), bindOpcodeDataType, -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + for (long offset : table.getUlebOffsets()) { + DataUtilities.createData(program, addr.add(offset), ULEB128, -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + for (long offset : table.getSlebOffsets()) { + DataUtilities.createData(program, addr.add(offset), SLEB128, -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + for (long offset : table.getStringOffsets()) { + DataUtilities.createData(program, addr.add(offset), STRING, -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + } + catch (Exception e) { + log.appendMsg(DyldInfoCommand.class.getSimpleName(), + "Failed to markup: " + getContextualName(source, additionalDescription)); + } } private void markupExportInfo(Program program, MachHeader header, String source, TaskMonitor monitor, MessageLog log) { - markupPlateComment(program, fileOffsetToAddress(program, header, exportOff, exportSize), - source, "export"); + Address exportAddr = fileOffsetToAddress(program, header, exportOff, exportSize); + if (exportAddr == null) { + return; + } + markupPlateComment(program, exportAddr, source, "export"); + + try { + for (long offset : exportTrie.getUlebOffsets()) { + DataUtilities.createData(program, exportAddr.add(offset), ULEB128, -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + for (long offset : exportTrie.getStringOffsets()) { + DataUtilities.createData(program, exportAddr.add(offset), STRING, -1, + DataUtilities.ClearDataMode.CHECK_FOR_SPACE); + } + } + catch (Exception e) { + log.appendMsg(DyldInfoCommand.class.getSimpleName(), + "Failed to markup: " + getContextualName(source, "export")); + } } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommandConstants.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommandConstants.java index ecab5ebd56..4dcaa23d3f 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommandConstants.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DyldInfoCommandConstants.java @@ -52,20 +52,20 @@ public final class DyldInfoCommandConstants { public final static int BIND_OPCODE_MASK = 0xF0; public final static int BIND_IMMEDIATE_MASK = 0x0F; - public final static int BIND_OPCODE_DONE = 0x00; - public final static int BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10; - public final static int BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20; - public final static int BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30; - public final static int BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40; - public final static int BIND_OPCODE_SET_TYPE_IMM = 0x50; - public final static int BIND_OPCODE_SET_ADDEND_SLEB = 0x60; - public final static int BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70; - public final static int BIND_OPCODE_ADD_ADDR_ULEB = 0x80; - public final static int BIND_OPCODE_DO_BIND = 0x90; - public final static int BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0; - public final static int BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0; - public final static int BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0; - public final static int BIND_OPCODE_THREADED = 0xD0; + public final static int BIND_OPCODE_DONE = 0x00; + public final static int BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10; + public final static int BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20; + public final static int BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30; + public final static int BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40; + public final static int BIND_OPCODE_SET_TYPE_IMM = 0x50; + public final static int BIND_OPCODE_SET_ADDEND_SLEB = 0x60; + public final static int BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70; + public final static int BIND_OPCODE_ADD_ADDR_ULEB = 0x80; + public final static int BIND_OPCODE_DO_BIND = 0x90; + public final static int BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0; + public final static int BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0; + public final static int BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0; + public final static int BIND_OPCODE_THREADED = 0xD0; public final static int BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00; public final static int BIND_SUBOPCODE_THREADED_APPLY = 0x01; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java index 7f2d714617..d60e8bcbf0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/DynamicSymbolTableCommand.java @@ -395,9 +395,8 @@ public class DynamicSymbolTableCommand extends LoadCommand { } } catch (Exception e) { - String name = LoadCommandTypes.getLoadCommandName(getCommandType()) + " (indirect)"; log.appendMsg(DynamicSymbolTableCommand.class.getSimpleName(), - "Failed to markup %s.".formatted(name)); + "Failed to markup: " + getContextualName(source, "indirect")); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/ExportTrie.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/ExportTrie.java index 98689570f3..cf3e08adf7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/ExportTrie.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/ExportTrie.java @@ -35,7 +35,10 @@ public class ExportTrie { private BinaryReader reader; private long base; + private List exports; + private List ulebOffsets; + private List stringOffsets; /** * Creates an empty {@link ExportTrie}. This is useful for export trie load commands that are @@ -43,6 +46,8 @@ public class ExportTrie { */ public ExportTrie() { this.exports = new ArrayList<>(); + this.ulebOffsets = new ArrayList<>(); + this.stringOffsets = new ArrayList<>(); } /** @@ -105,20 +110,25 @@ public class ExportTrie { private LinkedList parseNode(String name, int offset) throws IOException { LinkedList children = new LinkedList<>(); reader.setPointerIndex(base + offset); + ulebOffsets.add(reader.getPointerIndex() - base); int terminalSize = reader.readNextUnsignedVarIntExact(LEB128::unsigned); long childrenIndex = reader.getPointerIndex() + terminalSize; if (terminalSize != 0) { + ulebOffsets.add(reader.getPointerIndex() - base); long flags = reader.readNext(LEB128::unsigned); long address = 0; long other = 0; String importName = null; if ((flags & EXPORT_SYMBOL_FLAGS_REEXPORT) != 0) { + ulebOffsets.add(reader.getPointerIndex() - base); other = reader.readNext(LEB128::unsigned); // dylib ordinal importName = reader.readNextAsciiString(); } else { + ulebOffsets.add(reader.getPointerIndex() - base); address = reader.readNext(LEB128::unsigned); if ((flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) != 0) { + ulebOffsets.add(reader.getPointerIndex() - base); other = reader.readNext(LEB128::unsigned); } } @@ -126,9 +136,12 @@ public class ExportTrie { exports.add(export); } reader.setPointerIndex(childrenIndex); + ulebOffsets.add(reader.getPointerIndex() - base); int numChildren = reader.readNextUnsignedVarIntExact(LEB128::unsigned); for (int i = 0; i < numChildren; i++) { + stringOffsets.add(reader.getPointerIndex() - base); String childName = reader.readNextAsciiString(); + ulebOffsets.add(reader.getPointerIndex() - base); int childOffset = reader.readNextUnsignedVarIntExact(LEB128::unsigned); children.add(new Node(childName, childOffset)); } @@ -163,6 +176,20 @@ public class ExportTrie { } } + /** + * {@return ULEB128 offsets from the start of the export trie} + */ + public List getUlebOffsets() { + return ulebOffsets; + } + + /** + * {@return String offsets from the start of the export trie} + */ + public List getStringOffsets() { + return stringOffsets; + } + /** * A trie node */ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/FunctionStartsCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/FunctionStartsCommand.java index 132bd0a03c..f117b1d180 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/FunctionStartsCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/FunctionStartsCommand.java @@ -112,8 +112,8 @@ public class FunctionStartsCommand extends LinkEditDataCommand { } catch (Exception e) { - log.appendMsg(DyldChainedFixupsCommand.class.getSimpleName(), "Failed to markup %s." - .formatted(LoadCommandTypes.getLoadCommandName(getCommandType()))); + log.appendMsg(FunctionStartsCommand.class.getSimpleName(), + "Failed to markup: " + getContextualName(source, null)); } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommand.java index 8dee355558..46fd65296e 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/LoadCommand.java @@ -138,14 +138,27 @@ public abstract class LoadCommand implements StructConverter { if (address == null) { return; } - String comment = LoadCommandTypes.getLoadCommandName(getCommandType()); + String comment = getContextualName(source, additionalDescription); + program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment); + } + + /** + * Gets the name of this {@link LoadCommand} which includes contextual information + * + * @param source The source of this {@link LoadCommand} (could be null or empty) + * @param additionalDescription Additional information to associate with the {@link LoadCommand} + * name + * @return The name of this {@link LoadCommand} which includes contextual information + */ + protected String getContextualName(String source, String additionalDescription) { + String markupName = LoadCommandTypes.getLoadCommandName(getCommandType()); if (additionalDescription != null && !additionalDescription.isBlank()) { - comment += " (" + additionalDescription + ")"; + markupName += " (" + additionalDescription + ")"; } if (source != null && !source.isBlank()) { - comment += " - " + source; + markupName += " - " + source; } - program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment); + return markupName; } /** diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SymbolTableCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SymbolTableCommand.java index a1eab64d36..46cb9f3c08 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SymbolTableCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/SymbolTableCommand.java @@ -203,10 +203,8 @@ public class SymbolTableCommand extends LoadCommand { } catch (Exception e) { - String symbolsName = - LoadCommandTypes.getLoadCommandName(getCommandType()) + " (symbols)"; log.appendMsg(SymbolTableCommand.class.getSimpleName(), - "Failed to markup %s.".formatted(symbolsName)); + "Failed to markup: " + getContextualName(source, "symbols")); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoProcessor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoProcessor.java deleted file mode 100644 index 837f1fc313..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoProcessor.java +++ /dev/null @@ -1,120 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * 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.commands.dyld; - -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.format.macho.MachHeader; -import ghidra.app.util.bin.format.macho.commands.DyldInfoCommand; -import ghidra.program.model.listing.Program; -import ghidra.util.task.TaskMonitor; - -import java.io.ByteArrayInputStream; - -abstract public class AbstractDyldInfoProcessor { - protected MachHeader header; - protected Program program; - protected ByteProvider provider; - protected DyldInfoCommand command; - - protected AbstractDyldInfoProcessor( MachHeader header, Program program, ByteProvider provider, DyldInfoCommand command ) { - super(); - this.header = header; - this.program = program; - this.provider = provider; - this.command = command; - } - - abstract public void process( TaskMonitor monitor ) throws Exception; - - /** - * Unsigned Little-endian Base-128 - */ - protected long uleb128( ByteArrayInputStream byteStream, TaskMonitor monitor ) throws Exception { - long result = 0; - int bit = 0; - - while ( !monitor.isCancelled() ) { - - int value = byteStream.read(); - - if ( value == -1 ) { - break; - } - - byte b = (byte) value; - - long slice = b & 0x7f; - - if ( ( b & 0x80 ) == 0x80 ) {//if upper bit is set - if ( bit >= 64 || slice << bit >> bit != slice ) {//then left shift and right shift - throw new RuntimeException( "uleb128 too big" ); - } - } - - result |= ( slice << bit ); - bit += 7; - - if ( ( b & 0x80 ) == 0 ) {//if upper bit NOT set, then we are done - break; - } - } - return result; - } - - /** - * Signed Little-endian Base-128 - */ - protected long sleb128( ByteArrayInputStream byteStream, TaskMonitor monitor ) throws Exception { - long result = 0; - int bit = 0; - while ( !monitor.isCancelled() ) { - - int value = byteStream.read(); - - if ( value == -1 ) { - break; - } - - byte nextByte = (byte) value; - - result |= ( ( nextByte & 0x7f ) << bit ); - bit += 7; - - if ( ( nextByte & 0x80 ) == 0 ) { - break; - } - } - return result; - } - - protected String readString( ByteArrayInputStream byteStream, TaskMonitor monitor ) { - StringBuffer buffer = new StringBuffer(); - while ( !monitor.isCancelled() ) { - int value = byteStream.read(); - if ( value == -1 ) { - break; - } - byte b = (byte) value; - if ( b == '\0' ) { - break; - } - buffer.append( (char) ( b & 0xff ) ); - } - return buffer.toString(); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java deleted file mode 100644 index 5f5e5aa0ab..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/AbstractDyldInfoState.java +++ /dev/null @@ -1,160 +0,0 @@ -/* ### - * 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.commands.dyld; - -import java.util.List; - -import ghidra.app.util.bin.format.macho.MachHeader; -import ghidra.app.util.bin.format.macho.commands.*; -import ghidra.program.model.address.*; -import ghidra.program.model.listing.Program; -import ghidra.program.model.reloc.Relocation.Status; -import ghidra.program.model.symbol.Symbol; -import ghidra.program.model.symbol.SymbolIterator; -import ghidra.util.DataConverter; -import ghidra.util.task.TaskMonitor; - -abstract public class AbstractDyldInfoState { - protected MachHeader header; - protected Program program; - - String symbolName; - int type = 0; - int libraryOrdinal = 0; - long segmentOffset = 0; - int segmentIndex = 0; - long addend = 0; - - protected AbstractDyldInfoState(MachHeader header, Program program) { - this.header = header; - this.program = program; - } - - abstract public String print(); - - final public void perform(TaskMonitor monitor) throws Exception { -// if ( SystemUtilities.isInDevelopmentMode() ) { -// System.out.println( print( ) ); -// } - - monitor.setMessage("Performing bind: " + symbolName); - - Symbol symbol = getSymbol(); - if (symbol == null) { - return; - } - - long offset = symbol.getAddress().getOffset(); - - DataConverter converter = DataConverter.getInstance(program.getLanguage().isBigEndian()); - - byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset) - : converter.getBytes((int) offset); - - Address address = getAddress(); - - byte[] originalBytes = new byte[bytes.length]; - program.getMemory().getBytes(address, originalBytes); - - program.getMemory().setBytes(address, bytes); - - program.getRelocationTable() - .add(address, Status.APPLIED_OTHER, type, null, bytes.length, symbolName); - - //ReferenceManager referenceManager = program.getReferenceManager(); - //Reference reference = referenceManager.addMemoryReference( address, symbol.getAddress(), RefType.READ, SourceType.IMPORTED, 0 ); - //referenceManager.setPrimary( reference, true ); - } - - private Symbol getSymbol() { - SymbolIterator symbolIterator = program.getSymbolTable().getSymbols(symbolName); - if (symbolIterator.hasNext()) { - return symbolIterator.next(); - } - return null; - } - - protected Address getAddress() { - long result = getSegmentStartAddress() + segmentOffset;//TODO - AddressFactory factory = program.getAddressFactory(); - AddressSpace space = factory.getDefaultAddressSpace(); - if (program.getDefaultPointerSize() == 8) { - return space.getAddress(result); - } - return space.getAddress(result & 0xffffffffL); - } - - protected String getTypeName() { - switch (type) { - case DyldInfoCommandConstants.BIND_TYPE_POINTER: { - return "pointer"; - } - case DyldInfoCommandConstants.BIND_TYPE_TEXT_ABSOLUTE32: { - return "text_absolute32"; - } - case DyldInfoCommandConstants.BIND_TYPE_TEXT_PCREL32: { - return "text_pcrel32"; - } - } - throw new RuntimeException("unknown dyld info type: " + Integer.toHexString(type)); - } - - protected String getOrdinalName() { - switch (libraryOrdinal) { - case DyldInfoCommandConstants.BIND_SPECIAL_DYLIB_SELF: { - return "this-image"; - } - case DyldInfoCommandConstants.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: { - return "main-executable"; - } - case DyldInfoCommandConstants.BIND_SPECIAL_DYLIB_FLAT_LOOKUP: { - return "flat-namespace"; - } - } - if (libraryOrdinal < DyldInfoCommandConstants.BIND_SPECIAL_DYLIB_FLAT_LOOKUP) { - return "unknown dyld info special ordinal" + Integer.toHexString(libraryOrdinal); - } - List dylibCommands = - header.getLoadCommands(DynamicLibraryCommand.class); - if (libraryOrdinal > dylibCommands.size()) { - return "dyld info library ordinal out of range" + Integer.toHexString(libraryOrdinal); - } - DynamicLibraryCommand dylibCommand = dylibCommands.get(libraryOrdinal - 1); - DynamicLibrary dynamicLibrary = dylibCommand.getDynamicLibrary(); - LoadCommandString name = dynamicLibrary.getName(); - return name.getString(); - } - - protected long getSegmentStartAddress() { - List segments = header.getLoadCommands(SegmentCommand.class); - SegmentCommand segment = segments.get(segmentIndex); - return segment.getVMaddress(); - } - - protected String getSegmentName() { - List segments = header.getLoadCommands(SegmentCommand.class); - SegmentCommand segment = segments.get(segmentIndex); - List fileSetEntries = - header.getLoadCommands(FileSetEntryCommand.class); - for (FileSetEntryCommand fileSetEntryCommand : fileSetEntries) { - if (fileSetEntryCommand.getFileOffset() == segment.getFileOffset()) { - return fileSetEntryCommand.getFileSetEntryId().getString(); - } - } - return segment.getSegmentName(); - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindOpcode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindOpcode.java new file mode 100644 index 0000000000..085b865396 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindOpcode.java @@ -0,0 +1,88 @@ +/* ### + * 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.commands.dyld; + +import ghidra.app.util.bin.format.macho.MachConstants; +import ghidra.program.model.data.*; + +/** + * Bind opcodes + * + * @see EXTERNAL_HEADERS/mach-o/loader.h + */ +public enum BindOpcode { + + BIND_OPCODE_DONE(0x00), + BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(0x10), + BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB(0x20), + BIND_OPCODE_SET_DYLIB_SPECIAL_IMM(0x30), + BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x40), + BIND_OPCODE_SET_TYPE_IMM(0x50), + BIND_OPCODE_SET_ADDEND_SLEB(0x60), + BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x70), + BIND_OPCODE_ADD_ADDR_ULEB(0x80), + BIND_OPCODE_DO_BIND(0x90), + BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB(0xA0), + BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED(0xB0), + BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB(0xC0), + BIND_OPCODE_THREADED(0xD0); + + private int opcode; + + /** + * Creates a new {@link BindOpcode} for the given opcode value + * + * @param opcode The opcode value + */ + private BindOpcode(int opcode) { + this.opcode = opcode; + } + + /** + * {@return the opcode value} + */ + public int getOpcode() { + return opcode; + } + + /** + * {@return a new data type from this enum} + */ + public static DataType toDataType() { + EnumDataType enumDataType = new EnumDataType("bind_opcode", 1); + enumDataType.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY)); + for (BindOpcode bindOpcode : BindOpcode.values()) { + enumDataType.add(bindOpcode.toString(), bindOpcode.getOpcode()); + } + return enumDataType; + } + + /** + * Gets the {@link BindOpcode} that corresponds to the given opcode value + * + * @param opcode The opcode value + * @return The {@link BindOpcode} that corresponds to the given opcode value, or null if it does + * not exist + */ + public static BindOpcode forOpcode(int opcode) { + for (BindOpcode bindOpcode : BindOpcode.values()) { + if (bindOpcode.getOpcode() == opcode) { + return bindOpcode; + } + } + return null; + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindProcessor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindProcessor.java deleted file mode 100644 index 158ddf4625..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindProcessor.java +++ /dev/null @@ -1,145 +0,0 @@ -/* ### - * 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.commands.dyld; - -import java.io.ByteArrayInputStream; - -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.format.macho.MachHeader; -import ghidra.app.util.bin.format.macho.commands.DyldInfoCommand; -import ghidra.app.util.bin.format.macho.commands.DyldInfoCommandConstants; -import ghidra.program.model.listing.Program; -import ghidra.util.task.TaskMonitor; - -public class BindProcessor extends AbstractDyldInfoProcessor { - - public BindProcessor( Program program, MachHeader header, ByteProvider provider, DyldInfoCommand command ) { - super( header, program, provider, command ); - } - - @Override - public void process( TaskMonitor monitor ) throws Exception { - - BindState bind = new BindState( header, program ); - - boolean done = false; - - byte [] commandBytes = provider.readBytes( command.getBindOffset(), command.getBindSize() ); - ByteArrayInputStream byteServer = new ByteArrayInputStream( commandBytes ); - - while ( !done ) { - - if ( monitor.isCancelled() ) { - break; - } - - int value = byteServer.read(); - - if ( value == -1 ) { - break; - } - - byte b = (byte) value; - - int opcode = b & DyldInfoCommandConstants.BIND_OPCODE_MASK; - int immediate = b & DyldInfoCommandConstants.BIND_IMMEDIATE_MASK; - - switch ( opcode ) { - case DyldInfoCommandConstants.BIND_OPCODE_ADD_ADDR_ULEB: { - bind.segmentOffset += uleb128( byteServer, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DO_BIND: { - bind.perform( monitor ); - bind.segmentOffset += program.getDefaultPointerSize(); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: { - bind.perform( monitor ); - bind.segmentOffset += ( immediate * program.getDefaultPointerSize() ) + program.getDefaultPointerSize(); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: { - bind.perform( monitor ); - bind.segmentOffset += uleb128( byteServer, monitor ) + program.getDefaultPointerSize(); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { - long count = uleb128( byteServer, monitor ); - long skip = uleb128( byteServer, monitor ); - for ( int i = 0 ; i < count ; ++i ) { - if ( monitor.isCancelled() ) { - break; - } - bind.perform( monitor ); - bind.segmentOffset += skip + program.getDefaultPointerSize(); - } - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DONE: { - done = true; - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_ADDEND_SLEB: { - bind.addend = sleb128( byteServer, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: { - bind.libraryOrdinal = immediate; - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: { - bind.libraryOrdinal = (int) uleb128( byteServer, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: { - //the special ordinals are negative numbers - if ( immediate == 0 ) { - bind.libraryOrdinal = 0; - } - else { - byte signExtended = (byte) ( DyldInfoCommandConstants.BIND_OPCODE_MASK | immediate ); - bind.libraryOrdinal = signExtended; - } - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: { - bind.segmentIndex = immediate; - bind.segmentOffset = uleb128( byteServer, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: { - bind.symbolName = readString( byteServer, monitor ); - if ( ( immediate & DyldInfoCommandConstants.BIND_SYMBOL_FLAGS_WEAK_IMPORT ) != 0 ) { - bind.setWeak( true ); - } - else { - bind.setWeak( false ); - } - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_TYPE_IMM: { - bind.type = immediate; - break; - } - default: { - throw new Exception( - "Unknown dyld info bind opcode " + Integer.toHexString(opcode)); - } - } - } - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindState.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindState.java deleted file mode 100644 index 59e2fcb061..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindState.java +++ /dev/null @@ -1,82 +0,0 @@ -/* ### - * 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.commands.dyld; - -import java.io.File; -import java.util.List; - -import ghidra.app.util.bin.format.macho.MachHeader; -import ghidra.app.util.bin.format.macho.Section; -import ghidra.program.model.address.Address; -import ghidra.program.model.listing.Program; -import ghidra.util.StringUtilities; - -public class BindState extends AbstractDyldInfoState { - - private boolean weak = false; - - public BindState( MachHeader header, Program program ) { - super( header, program ); - } - - public String print( ) { - Address sectionAddress = getAddress( ); - - String sectionName = "no section"; - List
sections = header.getAllSections(); - for ( Section section : sections ) { - long start = section.getAddress(); - long end = section.getAddress() + section.getSize(); - if ( sectionAddress.getOffset() >= start && sectionAddress.getOffset() < end ) { - sectionName = section.getSectionName(); - } - } - - File file = new File( getOrdinalName( ) ); - - StringBuffer buffer = new StringBuffer(); - buffer.append( getSegmentName( ) ); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append(StringUtilities.pad(sectionName, ' ', -20)); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append( sectionAddress ); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append( getTypeName() ); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append( weak ); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append( addend ); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append(StringUtilities.pad(file.getName(), ' ', -20)); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append( symbolName ); - buffer.append( ' ' ); - buffer.append( ' ' ); - return buffer.toString(); - } - - public void setWeak( boolean weak ) { - this.weak = weak; - } - -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindingTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindingTable.java new file mode 100644 index 0000000000..ea3f9eccca --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/BindingTable.java @@ -0,0 +1,321 @@ +/* ### + * 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.commands.dyld; + +import static ghidra.app.util.bin.format.macho.commands.DyldInfoCommandConstants.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.util.bin.BinaryReader; +import ghidra.app.util.bin.format.macho.MachHeader; +import ghidra.app.util.bin.format.macho.commands.DyldInfoCommandConstants; +import ghidra.program.model.data.LEB128; + +/** + * A Mach-O binding table + * + * @see common/MachOLayout.cpp + * @see common/MachOAnalyzer.cpp + */ +public class BindingTable { + + private List bindings; + private List opcodeOffsets; + private List ulebOffsets; + private List slebOffsets; + private List stringOffsets; + + /** + * Creates an empty {@link BindingTable} + */ + public BindingTable() { + bindings = new ArrayList<>(); + opcodeOffsets = new ArrayList<>(); + ulebOffsets = new ArrayList<>(); + slebOffsets = new ArrayList<>(); + stringOffsets = new ArrayList<>(); + } + + /** + * Creates and parses a new {@link BindingTable} + * + * @param reader A {@link BinaryReader reader} positioned at the start of the binding table + * @param header The header + * @param tableSize The size of the table, in bytes + * @param lazy True if this is a lazy binding table; otherwise, false + * @throws IOException if an IO-related error occurs while parsing + */ + public BindingTable(BinaryReader reader, MachHeader header, int tableSize, boolean lazy) + throws IOException { + this(); + + int pointerSize = header.getAddressSize(); + long origIndex = reader.getPointerIndex(); + Binding binding = new Binding(); + + while (reader.getPointerIndex() < origIndex + tableSize) { + + opcodeOffsets.add(reader.getPointerIndex() - origIndex); + byte b = reader.readNextByte(); + BindOpcode opcode = BindOpcode.forOpcode(b & BIND_OPCODE_MASK); + int immediate = b & BIND_IMMEDIATE_MASK; + + switch (opcode) { + case BIND_OPCODE_DONE: { // 0x00 + if (lazy) { + //bind.lazyOffset = command.getLazyBindSize() - byteStream.available();//Note: this only works because we are using a ByteArrayInputStream! + break; + } + return; + } + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: { // 0x10 + binding.libraryOrdinal = immediate; + break; + } + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: { // 0x20 + ulebOffsets.add(reader.getPointerIndex() - origIndex); + binding.libraryOrdinal = reader.readNextUnsignedVarIntExact(LEB128::unsigned); + break; + } + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: { // 0x30 + //the special ordinals are negative numbers + if (immediate == 0) { + binding.libraryOrdinal = 0; + } + else { + byte signExtended = + (byte) (DyldInfoCommandConstants.BIND_OPCODE_MASK | immediate); + binding.libraryOrdinal = signExtended; + } + break; + } + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: { // 0x40 + stringOffsets.add(reader.getPointerIndex() - origIndex); + binding.symbolName = reader.readNextAsciiString(); + binding.weak = + (immediate & DyldInfoCommandConstants.BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0; + break; + } + case BIND_OPCODE_SET_TYPE_IMM: { // 0x50 + binding.type = immediate; + break; + } + case BIND_OPCODE_SET_ADDEND_SLEB: { // 0x60 + slebOffsets.add(reader.getPointerIndex() - origIndex); + binding.addend = reader.readNext(LEB128::signed); + break; + } + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: { // 0x70 + ulebOffsets.add(reader.getPointerIndex() - origIndex); + binding.segmentOffset = reader.readNext(LEB128::unsigned); + binding.segmentIndex = immediate; + break; + } + case BIND_OPCODE_ADD_ADDR_ULEB: { // 0x80 + ulebOffsets.add(reader.getPointerIndex() - origIndex); + binding.segmentOffset += reader.readNext(LEB128::unsigned); + break; + } + case BIND_OPCODE_DO_BIND: { // 0x90 + bindings.add(new Binding(binding)); + binding.segmentOffset += pointerSize; + break; + } + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: { // 0xA0 + bindings.add(new Binding(binding)); + ulebOffsets.add(reader.getPointerIndex() - origIndex); + binding.segmentOffset += reader.readNext(LEB128::unsigned) + pointerSize; + break; + } + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: { // 0xB0 + bindings.add(new Binding(binding)); + binding.segmentOffset += (immediate * pointerSize) + pointerSize; + break; + } + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { // 0xC0 + ulebOffsets.add(reader.getPointerIndex() - origIndex); + long count = reader.readNext(LEB128::unsigned); + ulebOffsets.add(reader.getPointerIndex() - origIndex); + long skip = reader.readNext(LEB128::unsigned); + for ( int i = 0 ; i < count ; ++i ) { + bindings.add(new Binding(binding)); + binding.segmentOffset += skip + pointerSize; + } + break; + } + /*case BIND_OPCODE_THREADED: { // 0xD0 + switch (immediate) { + case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB: + ulebOffsets.add(reader.getPointerIndex() - origIndex); + long count = reader.readNext(LEB128::unsigned); + for (int i = 0; i < count; ++i) { + // TODO + } + break; + case BIND_SUBOPCODE_THREADED_APPLY: { + // TODO + break; + } + default: { + Binding unknownBinding = new Binding(binding); + unknownBinding.unknownOpcode = Byte.toUnsignedInt(b); + bindings.add(unknownBinding); + return; + } + } + break; + }*/ + default: { + Binding unknownBinding = new Binding(binding); + unknownBinding.unknownOpcode = Byte.toUnsignedInt(b) & BIND_OPCODE_MASK; + bindings.add(unknownBinding); + return; + } + } + } + } + + /** + * {@return the bindings} + */ + public List getBindings() { + return bindings; + } + + /** + * {@return opcode offsets from the start of the bind data} + */ + public List getOpcodeOffsets() { + return opcodeOffsets; + } + + /** + * {@return ULEB128 offsets from the start of the bind data} + */ + public List getUlebOffsets() { + return ulebOffsets; + } + + /** + * {@return SLEB128 offsets from the start of the bind data} + */ + public List getSlebOffsets() { + return slebOffsets; + } + + /** + * {@return string offsets from the start of the bind data} + */ + public List getStringOffsets() { + return stringOffsets; + } + + /** + * A piece of binding information from a {@link BindingTable} + */ + public static class Binding { + + private String symbolName; + private int type; + private int libraryOrdinal; + private long segmentOffset; + private int segmentIndex; + private long addend; + private boolean weak; + private Integer unknownOpcode; + + /** + * Creates a new {@link Binding} + */ + public Binding() { + // Nothing to do + } + + /** + * Creates a copy of the given {@link Binding} + * + * @param binding The {@link Binding} to copy + */ + public Binding(Binding binding) { + this.symbolName = binding.symbolName; + this.type = binding.type; + this.libraryOrdinal = binding.libraryOrdinal; + this.segmentOffset = binding.segmentOffset; + this.segmentIndex = binding.segmentIndex; + this.addend = binding.addend; + this.weak = binding.weak; + this.unknownOpcode = binding.unknownOpcode; + } + + /** + * {@return The symbol name} + */ + public String getSymbolName() { + return symbolName; + } + + /** + * {@return The type} + */ + public int getType() { + return type; + } + + /** + * {@return The library ordinal} + */ + public int getLibraryOrdinal() { + return libraryOrdinal; + } + + /** + * {@return The segment offset} + */ + public long getSegmentOffset() { + return segmentOffset; + } + + /** + * {@return The segment index} + */ + public int getSegmentIndex() { + return segmentIndex; + } + + /** + * {@return The addend} + */ + public long getAddend() { + return addend; + } + + /** + * {@return True if the binding is "weak"; otherwise false} + */ + public boolean isWeak() { + return weak; + } + + /** + * {@return null if the opcode is known; otherwise, returns the unknown opcode's value} + */ + public Integer getUnknownOpcode() { + return unknownOpcode; + } + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/LazyBindProcessor.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/LazyBindProcessor.java deleted file mode 100644 index fd3dc9b646..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/LazyBindProcessor.java +++ /dev/null @@ -1,138 +0,0 @@ -/* ### - * IP: GHIDRA - * REVIEWED: YES - * - * 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.commands.dyld; - -import ghidra.app.util.bin.ByteProvider; -import ghidra.app.util.bin.format.macho.MachHeader; -import ghidra.app.util.bin.format.macho.commands.DyldInfoCommand; -import ghidra.app.util.bin.format.macho.commands.DyldInfoCommandConstants; -import ghidra.program.model.listing.Program; -import ghidra.util.task.TaskMonitor; - -import java.io.ByteArrayInputStream; - -public class LazyBindProcessor extends AbstractDyldInfoProcessor { - - public LazyBindProcessor( Program program, MachHeader header, ByteProvider provider, DyldInfoCommand command ) { - super( header, program, provider, command ); - } - - public void process( TaskMonitor monitor ) throws Exception { - - LazyBindState bind = new LazyBindState( header, program ); - - boolean done = false; - - byte [] commandBytes = provider.readBytes( command.getLazyBindOffset(), command.getLazyBindSize() ); - ByteArrayInputStream byteStream = new ByteArrayInputStream( commandBytes ); - - while ( !done ) { - - if ( monitor.isCancelled() ) { - break; - } - - int value = byteStream.read(); - - if ( value == -1 ) { - break; - } - - byte b = (byte) value; - - int opcode = b & DyldInfoCommandConstants.BIND_OPCODE_MASK; - int immediate = b & DyldInfoCommandConstants.BIND_IMMEDIATE_MASK; - - switch ( opcode ) { - case DyldInfoCommandConstants.BIND_OPCODE_ADD_ADDR_ULEB: { - bind.segmentOffset += uleb128( byteStream, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DO_BIND: { - bind.perform( monitor ); - bind.segmentOffset += program.getDefaultPointerSize(); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: { - bind.perform( monitor ); - bind.segmentOffset += ( immediate * program.getDefaultPointerSize() ) + program.getDefaultPointerSize(); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: { - bind.perform( monitor ); - bind.segmentOffset += uleb128( byteStream, monitor ) + program.getDefaultPointerSize(); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { - long count = uleb128( byteStream, monitor ); - long skip = uleb128( byteStream, monitor ); - for ( int i = 0 ; i < count ; ++i ) { - if ( monitor.isCancelled() ) { - break; - } - bind.perform( monitor ); - bind.segmentOffset += skip + program.getDefaultPointerSize(); - } - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_DONE: { - bind.lazyOffset = command.getLazyBindSize() - byteStream.available();//Note: this only works because we are using a ByteArrayInputStream! - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_ADDEND_SLEB: { - bind.addend = sleb128( byteStream, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: { - bind.libraryOrdinal = immediate; - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: { - bind.libraryOrdinal = (int) uleb128( byteStream, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: { - //the special ordinals are negative numbers - if ( immediate == 0 ) { - bind.libraryOrdinal = 0; - } - else { - byte signExtended = (byte) ( DyldInfoCommandConstants.BIND_OPCODE_MASK | immediate ); - bind.libraryOrdinal = signExtended; - } - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: { - bind.segmentIndex = immediate; - bind.segmentOffset = uleb128( byteStream, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: { - bind.symbolName = readString( byteStream, monitor ); - break; - } - case DyldInfoCommandConstants.BIND_OPCODE_SET_TYPE_IMM: { - bind.type = immediate; - break; - } - default: { - throw new RuntimeException( "unknown dyld info lazy bind opcode " + Integer.toHexString( opcode ) ); - } - } - } - } -} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/LazyBindState.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/LazyBindState.java deleted file mode 100644 index 3252de9a25..0000000000 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/macho/commands/dyld/LazyBindState.java +++ /dev/null @@ -1,72 +0,0 @@ -/* ### - * 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.commands.dyld; - -import java.io.File; -import java.util.List; - -import ghidra.app.util.bin.format.macho.MachHeader; -import ghidra.app.util.bin.format.macho.Section; -import ghidra.program.model.address.Address; -import ghidra.program.model.listing.Program; -import ghidra.util.StringUtilities; - -public class LazyBindState extends AbstractDyldInfoState { - - long lazyOffset; - - LazyBindState( MachHeader header, Program program ) { - super( header, program ); - } - - public String print() { - Address sectionAddress = getAddress(); - - String sectionName = "no section"; - List
sections = header.getAllSections(); - for ( Section section : sections ) { - long start = section.getAddress(); - long end = section.getAddress() + section.getSize(); - if ( sectionAddress.getOffset() >= start && sectionAddress.getOffset() < end ) { - sectionName = section.getSectionName(); - } - } - - File file = new File( getOrdinalName( ) ); - - StringBuffer buffer = new StringBuffer(); - buffer.append( getSegmentName( ) ); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append(StringUtilities.pad(sectionName, ' ', -20)); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append( sectionAddress ); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append( Long.toHexString( lazyOffset ) ); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append(StringUtilities.pad(file.getName(), ' ', -20)); - buffer.append( ' ' ); - buffer.append( ' ' ); - buffer.append( symbolName ); - buffer.append( ' ' ); - buffer.append( ' ' ); - return buffer.toString(); - } - -} 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 7d8b67fb8a..3407cf57ef 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 @@ -26,7 +26,9 @@ import ghidra.app.util.bin.format.macho.commands.*; import ghidra.app.util.bin.format.macho.commands.ExportTrie.ExportEntry; import ghidra.app.util.bin.format.macho.commands.chained.DyldChainedFixups; import ghidra.app.util.bin.format.macho.commands.chained.DyldChainedStartsOffsets; -import ghidra.app.util.bin.format.macho.commands.dyld.*; +import ghidra.app.util.bin.format.macho.commands.dyld.BindingTable.Binding; +import ghidra.app.util.bin.format.macho.commands.dyld.ClassicBindProcessor; +import ghidra.app.util.bin.format.macho.commands.dyld.ClassicLazyBindProcessor; 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; @@ -46,8 +48,7 @@ import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.symbol.*; import ghidra.program.model.util.CodeUnitInsertionException; import ghidra.program.util.ExternalSymbolResolver; -import ghidra.util.Msg; -import ghidra.util.NumericUtilities; +import ghidra.util.*; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; @@ -761,27 +762,46 @@ public class MachoProgramBuilder { } protected void processBindings(boolean doClassic) { + DataConverter converter = DataConverter.getInstance(program.getLanguage().isBigEndian()); + SymbolTable symbolTable = program.getSymbolTable(); + + List bindings = new ArrayList<>(); List commands = machoHeader.getLoadCommands(DyldInfoCommand.class); for (DyldInfoCommand command : commands) { - if (command.getBindSize() > 0) { - BindProcessor processor = - new BindProcessor(program, machoHeader, provider, command); - try { - processor.process(monitor); - } - catch (Exception e) { - log.appendMsg(e.getMessage()); - } + bindings.addAll(command.getBindingTable().getBindings()); + bindings.addAll(command.getLazyBindingTable().getBindings()); + bindings.addAll(command.getWeakBindingTable().getBindings()); + } + + List segments = machoHeader.getAllSegments(); + for (Binding binding : bindings) { + if (binding.getUnknownOpcode() != null) { + log.appendMsg("Unknown bind opcode: 0x%x".formatted(binding.getUnknownOpcode())); + continue; } - if (command.getLazyBindSize() > 0) { - LazyBindProcessor processor = - new LazyBindProcessor(program, machoHeader, provider, command); - try { - processor.process(monitor); - } - catch (Exception e) { - log.appendException(e); - } + List symbols = symbolTable.getGlobalSymbols(binding.getSymbolName()); + if (symbols.isEmpty()) { + continue; + } + Symbol symbol = symbols.get(0); + long offset = symbol.getAddress().getOffset(); + byte[] bytes = (program.getDefaultPointerSize() == 8) ? converter.getBytes(offset) + : converter.getBytes((int) offset); + Address addr = space.getAddress(segments.get(binding.getSegmentIndex()).getVMaddress() + + binding.getSegmentOffset()); + + try { + program.getMemory().setBytes(addr, bytes); + program.getRelocationTable() + .add(addr, Status.APPLIED, binding.getType(), null, bytes.length, + binding.getSymbolName()); + } + catch (MemoryAccessException e) { + handleRelocationError(addr, String.format( + "Relocation failure at address %s: error accessing memory.", addr)); + program.getRelocationTable() + .add(addr, Status.FAILURE, binding.getType(), null, bytes.length, + binding.getSymbolName()); } }