GP-3565: More Mach-O markup improvements

This commit is contained in:
Ryan Kurtz 2023-08-02 07:25:32 -04:00
parent 7073651f1f
commit cf3cc03f09
19 changed files with 666 additions and 782 deletions

View file

@ -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));
}
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}
}

View file

@ -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

View file

@ -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;

View file

@ -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"));
}
}

View file

@ -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
*/

View file

@ -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));
}
}
}

View file

@ -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;
}
/**

View file

@ -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"));
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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));
}
}
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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 ) );
}
}
}
}
}

View file

@ -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();
}
}

View file

@ -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());
}
}