mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-10-02 16:33:49 +00:00
GP-3565: More Mach-O markup improvements
This commit is contained in:
parent
7073651f1f
commit
cf3cc03f09
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,10 @@ public class ExportTrie {
|
|||
|
||||
private BinaryReader reader;
|
||||
private long base;
|
||||
|
||||
private List<ExportEntry> exports;
|
||||
private List<Long> ulebOffsets;
|
||||
private List<Long> 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<Node> parseNode(String name, int offset) throws IOException {
|
||||
LinkedList<Node> 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<Long> getUlebOffsets() {
|
||||
return ulebOffsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return String offsets from the start of the export trie}
|
||||
*/
|
||||
public List<Long> getStringOffsets() {
|
||||
return stringOffsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* A trie node
|
||||
*/
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<DynamicLibraryCommand> 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<SegmentCommand> segments = header.getLoadCommands(SegmentCommand.class);
|
||||
SegmentCommand segment = segments.get(segmentIndex);
|
||||
return segment.getVMaddress();
|
||||
}
|
||||
|
||||
protected String getSegmentName() {
|
||||
List<SegmentCommand> segments = header.getLoadCommands(SegmentCommand.class);
|
||||
SegmentCommand segment = segments.get(segmentIndex);
|
||||
List<FileSetEntryCommand> fileSetEntries =
|
||||
header.getLoadCommands(FileSetEntryCommand.class);
|
||||
for (FileSetEntryCommand fileSetEntryCommand : fileSetEntries) {
|
||||
if (fileSetEntryCommand.getFileOffset() == segment.getFileOffset()) {
|
||||
return fileSetEntryCommand.getFileSetEntryId().getString();
|
||||
}
|
||||
}
|
||||
return segment.getSegmentName();
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <a href="https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h">EXTERNAL_HEADERS/mach-o/loader.h</a>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Section> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <a href="https://github.com/apple-oss-distributions/dyld/blob/main/common/MachOLayout.cpp">common/MachOLayout.cpp</a>
|
||||
* @see <a href="https://github.com/apple-oss-distributions/dyld/blob/main/common/MachOAnalyzer.cpp">common/MachOAnalyzer.cpp</a>
|
||||
*/
|
||||
public class BindingTable {
|
||||
|
||||
private List<Binding> bindings;
|
||||
private List<Long> opcodeOffsets;
|
||||
private List<Long> ulebOffsets;
|
||||
private List<Long> slebOffsets;
|
||||
private List<Long> 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<Binding> getBindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return opcode offsets from the start of the bind data}
|
||||
*/
|
||||
public List<Long> getOpcodeOffsets() {
|
||||
return opcodeOffsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return ULEB128 offsets from the start of the bind data}
|
||||
*/
|
||||
public List<Long> getUlebOffsets() {
|
||||
return ulebOffsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return SLEB128 offsets from the start of the bind data}
|
||||
*/
|
||||
public List<Long> getSlebOffsets() {
|
||||
return slebOffsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return string offsets from the start of the bind data}
|
||||
*/
|
||||
public List<Long> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Section> 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Binding> bindings = new ArrayList<>();
|
||||
List<DyldInfoCommand> 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<SegmentCommand> 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<Symbol> 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue