GP-3565: More Mach-O markup improvements

This commit is contained in:
Ryan Kurtz 2023-07-31 09:12:45 -04:00
parent 54d2bc2997
commit 05af5149ad
10 changed files with 280 additions and 261 deletions

View File

@ -63,13 +63,13 @@ public class DataInCodeCommand extends LinkEditDataCommand {
}
@Override
public void markup(Program program, MachHeader header, Address addr, String source,
TaskMonitor monitor, MessageLog log) throws CancelledException {
if (addr == null || datasize == 0) {
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, addr, source, monitor, log);
super.markup(program, header, source, monitor, log);
try {
for (DataInCodeEntry entry : entries) {

View File

@ -64,14 +64,13 @@ public class DyldChainedFixupsCommand extends LinkEditDataCommand {
}
@Override
public void markup(Program program, MachHeader header, Address addr, String source,
TaskMonitor monitor, MessageLog log) throws CancelledException {
if (addr == null || datasize == 0) {
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, addr, source, monitor, log);
super.markup(program, header, source, monitor, log);
try {
DataUtilities.createData(program, addr, chainHeader.toDataType(), -1,

View File

@ -24,7 +24,9 @@ import ghidra.app.util.importer.MessageLog;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.ProgramModule;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
@ -32,16 +34,16 @@ import ghidra.util.task.TaskMonitor;
* Represents a dyld_info_command structure
*/
public class DyldInfoCommand extends LoadCommand {
private int rebase_off;
private int rebase_size;
private int bind_off;
private int bind_size;
private int weak_bind_off;
private int weak_bind_size;
private int lazy_bind_off;
private int lazy_bind_size;
private int export_off;
private int export_size;
private int rebaseOff;
private int rebaseSize;
private int bindOff;
private int bindSize;
private int weakBindOff;
private int weakBindSize;
private int lazyBindOff;
private int lazyBindSize;
private int exportOff;
private int exportSize;
private ExportTrie exportTrie;
@ -59,19 +61,19 @@ public class DyldInfoCommand extends LoadCommand {
throws IOException {
super(loadCommandReader);
rebase_off = loadCommandReader.readNextInt();
rebase_size = loadCommandReader.readNextInt();
bind_off = loadCommandReader.readNextInt();
bind_size = loadCommandReader.readNextInt();
weak_bind_off = loadCommandReader.readNextInt();
weak_bind_size = loadCommandReader.readNextInt();
lazy_bind_off = loadCommandReader.readNextInt();
lazy_bind_size = loadCommandReader.readNextInt();
export_off = loadCommandReader.readNextInt();
export_size = loadCommandReader.readNextInt();
rebaseOff = loadCommandReader.readNextInt();
rebaseSize = loadCommandReader.readNextInt();
bindOff = loadCommandReader.readNextInt();
bindSize = loadCommandReader.readNextInt();
weakBindOff = loadCommandReader.readNextInt();
weakBindSize = loadCommandReader.readNextInt();
lazyBindOff = loadCommandReader.readNextInt();
lazyBindSize = loadCommandReader.readNextInt();
exportOff = loadCommandReader.readNextInt();
exportSize = loadCommandReader.readNextInt();
if (export_off > 0 && export_size > 0) {
dataReader.setPointerIndex(header.getStartIndex() + export_off);
if (exportOff > 0 && exportSize > 0) {
dataReader.setPointerIndex(header.getStartIndex() + exportOff);
exportTrie = new ExportTrie(dataReader);
}
else {
@ -79,152 +81,181 @@ public class DyldInfoCommand extends LoadCommand {
}
}
/**
* {@return The rebase info offset}
*/
public int getRebaseOffset() {
return rebaseOff;
}
/**
* {@return The rebase info size}
*/
public int getRebaseSize() {
return rebaseSize;
}
/**
* {@return The bind info offset}
*/
public int getBindOffset() {
return bindOff;
}
/**
* {@return The bind info size}
*/
public int getBindSize() {
return bindSize;
}
/**
* {@return The weak bind info offset}
*/
public int getWeakBindOffset() {
return weakBindOff;
}
/**
* {@return The weak bind info size}
*/
public int getWeakBindSize() {
return weakBindSize;
}
/**
* {@return The lazy bind info offset}
*/
public int getLazyBindOffset() {
return lazyBindOff;
}
/**
* {@return The lazy bind info size}
*/
public int getLazyBindSize() {
return lazyBindSize;
}
/**
* {@return The export info offset}
*/
public int getExportOffset() {
return exportOff;
}
/**
* {@return The export info size}
*/
public int getExportSize() {
return exportSize;
}
/**
* {@return The export trie}
*/
public ExportTrie getExportTrie() {
return exportTrie;
}
@Override
public String getCommandName() {
return "dyld_info_command";
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getCommandName(), 0);
struct.add(DWORD, "cmd", null);
struct.add(DWORD, "cmdsize", null);
struct.add(DWORD, "rebase_off", "file offset to rebase info");
struct.add(DWORD, "rebase_size", "size of rebase info");
struct.add(DWORD, "bind_off", "file offset to binding info");
struct.add(DWORD, "bind_size", "size of binding info");
struct.add(DWORD, "weak_bind_off", "file offset to weak binding info");
struct.add(DWORD, "weak_bind_size", "size of weak binding info");
struct.add(DWORD, "lazy_bind_off", "file offset to lazy binding info");
struct.add(DWORD, "lazy_bind_size", "size of lazy binding info");
struct.add(DWORD, "export_off", "file offset to lazy binding info");
struct.add(DWORD, "export_size", "size of lazy binding info");
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
@Override
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);
markupExportInfo(program, header, source, monitor, log);
}
private void markupRebaseInfo(Program program, MachHeader header, String source,
TaskMonitor monitor, MessageLog log) {
markupPlateComment(program, fileOffsetToAddress(program, header, rebaseOff, rebaseSize),
source, "rebase");
}
private void markupBindInfo(Program program, MachHeader header, String source,
TaskMonitor monitor, MessageLog log) {
markupPlateComment(program, fileOffsetToAddress(program, header, bindOff, bindSize),
source, "bind");
}
private void markupWeakBindInfo(Program program, MachHeader header, String source,
TaskMonitor monitor, MessageLog log) {
markupPlateComment(program, fileOffsetToAddress(program, header, weakBindOff, weakBindSize),
source, "weak bind");
}
private void markupLazyBindInfo(Program program, MachHeader header, String source,
TaskMonitor monitor, MessageLog log) {
markupPlateComment(program, fileOffsetToAddress(program, header, lazyBindOff, lazyBindSize),
source, "lazy bind");
}
private void markupExportInfo(Program program, MachHeader header, String source,
TaskMonitor monitor, MessageLog log) {
markupPlateComment(program, fileOffsetToAddress(program, header, exportOff, exportSize),
source, "export");
}
@Override
public void markupRawBinary(MachHeader header, FlatProgramAPI api, Address baseAddress,
ProgramModule parentModule, TaskMonitor monitor, MessageLog log) {
try {
super.markupRawBinary(header, api, baseAddress, parentModule, monitor, log);
if (rebase_size > 0) {
Address start = baseAddress.getNewAddress(rebase_off);
if (rebaseSize > 0) {
Address start = baseAddress.getNewAddress(rebaseOff);
api.createFragment(parentModule, getCommandName() + "_REBASE", start,
rebase_size);
rebaseSize);
}
if (bind_size > 0) {
Address start = baseAddress.getNewAddress(bind_off);
api.createFragment(parentModule, getCommandName() + "_BIND", start, bind_size);
if (bindSize > 0) {
Address start = baseAddress.getNewAddress(bindOff);
api.createFragment(parentModule, getCommandName() + "_BIND", start, bindSize);
}
if (weak_bind_size > 0) {
Address start = baseAddress.getNewAddress(weak_bind_off);
if (weakBindSize > 0) {
Address start = baseAddress.getNewAddress(weakBindOff);
api.createFragment(parentModule, getCommandName() + "_WEAK_BIND", start,
weak_bind_size);
weakBindSize);
}
if (lazy_bind_size > 0) {
Address start = baseAddress.getNewAddress(lazy_bind_off);
if (lazyBindSize > 0) {
Address start = baseAddress.getNewAddress(lazyBindOff);
api.createFragment(parentModule, getCommandName() + "_LAZY_BIND", start,
lazy_bind_size);
lazyBindSize);
}
if (export_size > 0) {
Address start = baseAddress.getNewAddress(export_off);
if (exportSize > 0) {
Address start = baseAddress.getNewAddress(exportOff);
api.createFragment(parentModule, getCommandName() + "_EXPORT", start,
export_size);
exportSize);
}
}
catch (Exception e) {
log.appendMsg("Unable to create " + getCommandName());
}
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType struct = new StructureDataType(getCommandName(), 0);
struct.add(DWORD, "cmd", null);
struct.add(DWORD, "cmdsize", null);
struct.add(DWORD, "rebase_off", null);
struct.add(DWORD, "rebase_size", null);
struct.add(DWORD, "bind_off", null);
struct.add(DWORD, "bind_size", null);
struct.add(DWORD, "weak_bind_off", null);
struct.add(DWORD, "weak_bind_size", null);
struct.add(DWORD, "lazy_bind_off", null);
struct.add(DWORD, "lazy_bind_size", null);
struct.add(DWORD, "export_off", null);
struct.add(DWORD, "export_size", null);
struct.setCategoryPath(new CategoryPath(MachConstants.DATA_TYPE_CATEGORY));
return struct;
}
/**
* file offset to rebase info
* @return file offset to rebase info
*/
public int getRebaseOffset() {
return rebase_off;
}
/**
* size of rebase info
* @return size of rebase info
*/
public int getRebaseSize() {
return rebase_size;
}
/**
* file offset to binding info
* @return file offset to binding info
*/
public int getBindOffset() {
return bind_off;
}
/**
* size of binding info
* @return size of binding info
*/
public int getBindSize() {
return bind_size;
}
/**
* file offset to weak binding info
* @return file offset to weak binding info
*/
public int getWeakBindOffset() {
return weak_bind_off;
}
/**
* size of weak binding info
* @return size of weak binding info
*/
public int getWeakBindSize() {
return weak_bind_size;
}
/**
* file offset to lazy binding info
* @return file offset to lazy binding info
*/
public int getLazyBindOffset() {
return lazy_bind_off;
}
/**
* size of lazy binding infs
* @return
*/
public int getLazyBindSize() {
return lazy_bind_size;
}
/**
*
* @return
*/
public int getExportOffset() {
return export_off;
}
/**
*
* @return
*/
public int getExportSize() {
return export_size;
}
/**
* Gets the {@link ExportTrie}
*
* @return The {@link ExportTrie}
*/
public ExportTrie getExportTrie() {
return exportTrie;
}
}

View File

@ -67,6 +67,9 @@ public final class DyldInfoCommandConstants {
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;
/*
* The following are used on the flags byte of a terminal node
* in the export information.

View File

@ -24,7 +24,6 @@ import ghidra.app.util.bin.format.macho.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
@ -347,44 +346,32 @@ public class DynamicSymbolTableCommand extends LoadCommand {
}
@Override
public Address getDataAddress(MachHeader header, AddressSpace space) {
if (indirectsymoff != 0 && nindirectsyms != 0) {
SegmentCommand segment = getContainingSegment(header, indirectsymoff);
if (segment != null) {
return space.getAddress(
segment.getVMaddress() + (indirectsymoff - segment.getFileOffset()));
}
}
return null;
public void markup(Program program, MachHeader header, String source, TaskMonitor monitor,
MessageLog log) throws CancelledException {
markupIndirectSymbolTable(program, header, source, monitor, log);
// TODO: Handle more than just the indirect symbol table
}
@Override
public void markup(Program program, MachHeader header, Address indirectSymbolTableAddr,
String source, TaskMonitor monitor, MessageLog log) throws CancelledException {
// TODO: Handle more than just the indirect symbol table
private void markupIndirectSymbolTable(Program program, MachHeader header, String source,
TaskMonitor monitor, MessageLog log) {
Address indirectSymbolTableAddr =
fileOffsetToAddress(program, header, indirectsymoff, nindirectsyms);
if (indirectSymbolTableAddr == null) {
return;
}
markupPlateComment(program, indirectSymbolTableAddr, source, "indirect");
Listing listing = program.getListing();
ReferenceManager referenceManager = program.getReferenceManager();
String name = LoadCommandTypes.getLoadCommandName(getCommandType()) + " (indirect)";
if (source != null) {
name += " - " + source;
}
SymbolTableCommand symbolTable = header.getFirstLoadCommand(SymbolTableCommand.class);
Address symbolTableAddr = null;
Address stringTableAddr = null;
if (symbolTable != null) {
symbolTableAddr =
symbolTable.getDataAddress(header, indirectSymbolTableAddr.getAddressSpace());
if (symbolTable.getStringTableOffset() != 0) {
stringTableAddr = symbolTableAddr
.add(symbolTable.getStringTableOffset() - symbolTable.getSymbolOffset());
}
}
Address symbolTableAddr = fileOffsetToAddress(program, header,
symbolTable.getSymbolOffset(), symbolTable.getNumberOfSymbols());
Address stringTableAddr = fileOffsetToAddress(program, header,
symbolTable.getStringTableOffset(), symbolTable.getStringTableSize());
ReferenceManager referenceManager = program.getReferenceManager();
try {
listing.setComment(indirectSymbolTableAddr, CodeUnit.PLATE_COMMENT, name);
for (int i = 0; i < nindirectsyms; i++) {
int nlistIndex = indirectSymbols[i];
Address dataAddr = indirectSymbolTableAddr.add(i * DWORD.getLength());
@ -408,6 +395,7 @@ 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));
}

View File

@ -80,13 +80,13 @@ public class FunctionStartsCommand extends LinkEditDataCommand {
}
@Override
public void markup(Program program, MachHeader header, Address addr, String source,
TaskMonitor monitor, MessageLog log) throws CancelledException {
if (addr == null || datasize == 0) {
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, addr, source, monitor, log);
super.markup(program, header, source, monitor, log);
SegmentCommand textSegment = header.getSegment(SegmentNames.SEG_TEXT);
if (textSegment == null) {

View File

@ -23,9 +23,9 @@ import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.ProgramModule;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitor;
@ -73,28 +73,10 @@ public class LinkEditDataCommand extends LoadCommand {
}
@Override
public Address getDataAddress(MachHeader header, AddressSpace space) {
if (dataoff != 0 && datasize != 0) {
SegmentCommand segment = getContainingSegment(header, dataoff);
if (segment != null) {
return space
.getAddress(segment.getVMaddress() + (dataoff - segment.getFileOffset()));
}
}
return null;
}
@Override
public void markup(Program program, MachHeader header, Address addr, String source,
TaskMonitor monitor, MessageLog log) throws CancelledException {
if (addr == null || datasize == 0) {
return;
}
String name = LoadCommandTypes.getLoadCommandName(getCommandType());
if (source != null) {
name += " - " + source;
}
program.getListing().setComment(addr, CodeUnit.PLATE_COMMENT, name);
public void markup(Program program, MachHeader header, String source, TaskMonitor monitor,
MessageLog log) throws CancelledException {
markupPlateComment(program, fileOffsetToAddress(program, header, dataoff, datasize), source,
null);
}
@Override

View File

@ -105,35 +105,71 @@ public abstract class LoadCommand implements StructConverter {
return 0;
}
/**
* Gets the {@link Address} of this load command's "data"
*
* @param header The Mach-O header
* @param space The {@link AddressSpace}
* @return The {@link Address} of this load command's "data", or null if it has no data
*/
public Address getDataAddress(MachHeader header, AddressSpace space) {
return null;
}
/**
* Marks up this {@link LoadCommand} data with data structures and comments. Assumes the
* program was imported as a Mach-O.
*
* @param program The {@link Program} to mark up
* @param header The Mach-O header
* @param addr The {@link Address} of the start of load command data (could be null if no data)
* @param source A name that represents where the header came from (could be null)
* @param monitor A cancellable task monitor
* @param log The log
* @throws CancelledException if the user cancelled the operation
*/
public void markup(Program program, MachHeader header, Address addr, String source,
TaskMonitor monitor, MessageLog log) throws CancelledException {
public void markup(Program program, MachHeader header, String source, TaskMonitor monitor,
MessageLog log) throws CancelledException {
// Default is no markup
return;
}
/**
* Creates a plate comment at the given {@link Address} based on this {@link LoadCommand}'s
* name
*
* @param program The {@link Program} to mark up
* @param address The {@link Address} to put the comment at
* @param source An optional string representing the source of the {@link LoadCommand}. Could
* be empty or null.
* @param additionalDescription An optional string representing a sub-description of this
* {@link LoadCommand}. Could be empty or null.
*/
protected void markupPlateComment(Program program, Address address, String source,
String additionalDescription) {
if (address == null) {
return;
}
String comment = LoadCommandTypes.getLoadCommandName(getCommandType());
if (additionalDescription != null && !additionalDescription.isBlank()) {
comment += " (" + additionalDescription + ")";
}
if (source != null && !source.isBlank()) {
comment += " - " + source;
}
program.getListing().setComment(address, CodeUnit.PLATE_COMMENT, comment);
}
/**
* Converts the given Mach-O file offset to an {@link Address}
*
* @param program The {@link Program}
* @param header The Mach-O header
* @param fileOffset The file offset of the data (0 indicates no data)
* @param size The size (actual size not important, but 0 will cause null to be returned)
* @return The converted {@link Address}, or null if there is no corresponding {@link Address}
*/
protected Address fileOffsetToAddress(Program program, MachHeader header, int fileOffset,
int size) {
if (fileOffset != 0 && size != 0) {
AddressSpace space = program.getAddressFactory().getDefaultAddressSpace();
SegmentCommand segment = getContainingSegment(header, fileOffset);
if (segment != null) {
return space.getAddress(
segment.getVMaddress() + (fileOffset - segment.getFileOffset()));
}
}
return null;
}
/**
* Gets the {@link SegmentCommand segment} that contains the give file offset
*
@ -142,7 +178,7 @@ public abstract class LoadCommand implements StructConverter {
* @return The {@link SegmentCommand segment} that contains the give file offset, or null if
* one was not found
*/
protected SegmentCommand getContainingSegment(MachHeader header, long fileOffset) {
private SegmentCommand getContainingSegment(MachHeader header, long fileOffset) {
for (SegmentCommand segment : header.getAllSegments()) {
if (fileOffset >= segment.getFileOffset() &&
fileOffset < segment.getFileOffset() + segment.getFileSize()) {

View File

@ -26,7 +26,6 @@ import ghidra.app.util.bin.format.macho.MachHeader;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
@ -171,38 +170,20 @@ public class SymbolTableCommand extends LoadCommand {
}
@Override
public Address getDataAddress(MachHeader header, AddressSpace space) {
if (symoff != 0 && nsyms != 0) {
SegmentCommand segment = getContainingSegment(header, symoff);
if (segment != null) {
return space
.getAddress(segment.getVMaddress() + (symoff - segment.getFileOffset()));
}
}
return null;
}
public void markup(Program program, MachHeader header, String source, TaskMonitor monitor,
MessageLog log) throws CancelledException {
@Override
public void markup(Program program, MachHeader header, Address symbolTableAddr,
String source, TaskMonitor monitor, MessageLog log) throws CancelledException {
Address symbolTableAddr = fileOffsetToAddress(program, header, symoff, nsyms);
if (symbolTableAddr == null) {
return;
}
Address stringTableAddr = stroff != 0 ? symbolTableAddr.add(stroff - symoff) : null;
Address stringTableAddr = fileOffsetToAddress(program, header, stroff, strsize);
markupPlateComment(program, symbolTableAddr, source, "symbols");
markupPlateComment(program, stringTableAddr, source, "strings");
Listing listing = program.getListing();
ReferenceManager referenceManager = program.getReferenceManager();
String symbolsName = LoadCommandTypes.getLoadCommandName(getCommandType()) + " (symbols)";
String stringsName = LoadCommandTypes.getLoadCommandName(getCommandType()) + " (strings)";
if (source != null) {
symbolsName += " - " + source;
stringsName += " - " + source;
}
try {
listing.setComment(symbolTableAddr, CodeUnit.PLATE_COMMENT, symbolsName);
if (stringTableAddr != null) {
listing.setComment(stringTableAddr, CodeUnit.PLATE_COMMENT, stringsName);
}
for (int i = 0; i < nsyms; i++) {
NList nlist = symbols.get(i);
DataType dt = nlist.toDataType();
@ -222,6 +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));
}

View File

@ -1258,10 +1258,7 @@ public class MachoProgramBuilder {
*/
protected void markupLoadCommandData(MachHeader header, String source) throws Exception {
for (LoadCommand cmd : header.getLoadCommands()) {
Address dataAddr = cmd.getDataAddress(header, space);
if (dataAddr != null) {
cmd.markup(program, header, dataAddr, source, monitor, log);
}
cmd.markup(program, header, source, monitor, log);
}
}