GP-3701: Adding support for Mach-O BIND_OPCODE_THREADED

This commit is contained in:
Ryan Kurtz 2023-08-03 11:38:23 -04:00
parent fd3c351297
commit b840f6632c
5 changed files with 116 additions and 63 deletions

View File

@ -177,8 +177,8 @@ public class DyldChainedFixups {
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
authValueAdd);
processPointerChain(chainHeader.getChainedImports(), unchainedLocList,
ptrFormat, page, pageOffset, authValueAdd);
break;
// These might work, but have not been fully tested!
@ -189,8 +189,8 @@ public class DyldChainedFixups {
case DYLD_CHAINED_PTR_32_CACHE:
case DYLD_CHAINED_PTR_32_FIRMWARE:
case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
processPointerChain(chainHeader, unchainedLocList, ptrFormat, page, pageOffset,
authValueAdd);
processPointerChain(chainHeader.getChainedImports(), unchainedLocList,
ptrFormat, page, pageOffset, authValueAdd);
break;
case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
@ -214,7 +214,7 @@ public class DyldChainedFixups {
/**
* Fixes up any chained pointers, starting at the given address.
*
* @param chainHeader fixup header chains (could be null)
* @param chainedImports chained imports (could be null)
* @param unchainedLocList list of locations that were unchained
* @param pointerFormat format of pointers within this chain
* @param page within data pages that has pointers to be unchained
@ -224,7 +224,7 @@ public class DyldChainedFixups {
* @throws MemoryAccessException IO problem reading file
* @throws CancelledException user cancels
*/
private void processPointerChain(DyldChainedFixupHeader chainHeader,
public void processPointerChain(DyldChainedImports chainedImports,
List<Address> unchainedLocList, DyldChainType pointerFormat, long page, long nextOff,
long auth_value_add) throws MemoryAccessException, CancelledException {
@ -243,9 +243,9 @@ public class DyldChainedFixups {
boolean isAuthenticated = DyldChainedPtr.isAuthenticated(pointerFormat, chainValue);
boolean isBound = DyldChainedPtr.isBound(pointerFormat, chainValue);
if (isBound && chainHeader == null) {
if (isBound && chainedImports == null) {
log.appendMsg(
"Error: dyld_chained_fixups_header required to process bound chain fixup at " +
"Error: dyld_chained_import array required to process bound chain fixup at " +
chainLoc);
return;
}
@ -264,7 +264,6 @@ public class DyldChainedFixups {
else if (!isAuthenticated && isBound) {
int chainOrdinal = (int) DyldChainedPtr.getOrdinal(pointerFormat, chainValue);
long addend = DyldChainedPtr.getAddend(pointerFormat, chainValue);
DyldChainedImports chainedImports = chainHeader.getChainedImports();
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
//int libOrdinal = chainedImport.getLibOrdinal();
symName = chainedImport.getName();
@ -283,7 +282,6 @@ public class DyldChainedFixups {
// DyldChainedPtr.hasAddrDiversity(pointerFormat, chainValue);
//long key = DyldChainedPtr.getKey(pointerFormat, chainValue);
DyldChainedImports chainedImports = chainHeader.getChainedImports();
DyldChainedImport chainedImport = chainedImports.getChainedImport(chainOrdinal);
symName = chainedImport.getName();

View File

@ -20,6 +20,7 @@ import java.io.IOException;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.macho.MachConstants;
import ghidra.app.util.bin.format.macho.commands.dyld.BindingTable.Binding;
import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException;
@ -72,6 +73,15 @@ public class DyldChainedImport implements StructConverter {
}
}
public DyldChainedImport(Binding binding) {
this.imports_format = 0;
this.lib_ordinal = binding.getLibraryOrdinal();
this.weak_import = binding.isWeak();
this.name_offset = 0;
this.addend = 0;
this.symbolName = binding.getSymbolName();
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
StructureDataType dt = new StructureDataType("dyld_chained_import", 0);

View File

@ -17,9 +17,11 @@ package ghidra.app.util.bin.format.macho.commands.chained;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.StructConverter;
import ghidra.app.util.bin.format.macho.commands.dyld.BindingTable.Binding;
import ghidra.program.model.data.ArrayDataType;
import ghidra.program.model.data.DataType;
import ghidra.util.exception.DuplicateNameException;
@ -34,7 +36,7 @@ public class DyldChainedImports implements StructConverter {
private int importsCount;
private int importsFormat;
private long importsOffset;
private DyldChainedImport chainedImports[];
private DyldChainedImport[] chainedImports;
DyldChainedImports(BinaryReader reader, DyldChainedFixupHeader cfh) throws IOException {
long ptrIndex = reader.getPointerIndex();
@ -50,6 +52,15 @@ public class DyldChainedImports implements StructConverter {
chainedImports = starts.toArray(DyldChainedImport[]::new);
}
public DyldChainedImports(List<Binding> bindings) {
importsCount = bindings.size();
chainedImports = new DyldChainedImport[bindings.size()];
int i = 0;
for (Binding binding : bindings) {
chainedImports[i++] = new DyldChainedImport(binding);
}
}
@Override
public DataType toDataType() throws DuplicateNameException, IOException {
DataType chainedImportDt = chainedImports[0].toDataType();

View File

@ -35,6 +35,7 @@ import ghidra.program.model.data.LEB128;
public class BindingTable {
private List<Binding> bindings;
private List<Binding> threadedBindings;
private List<Long> opcodeOffsets;
private List<Long> ulebOffsets;
private List<Long> slebOffsets;
@ -45,6 +46,7 @@ public class BindingTable {
*/
public BindingTable() {
bindings = new ArrayList<>();
threadedBindings = null;
opcodeOffsets = new ArrayList<>();
ulebOffsets = new ArrayList<>();
slebOffsets = new ArrayList<>();
@ -133,7 +135,9 @@ public class BindingTable {
}
case BIND_OPCODE_DO_BIND: { // 0x90
bindings.add(new Binding(binding));
binding.segmentOffset += pointerSize;
if (threadedBindings == null) {
binding.segmentOffset += pointerSize;
}
break;
}
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: { // 0xA0
@ -158,17 +162,15 @@ public class BindingTable {
}
break;
}
/*case BIND_OPCODE_THREADED: { // 0xD0
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
}
int numThreaded = reader.readNextVarInt(LEB128::unsigned);
threadedBindings = new ArrayList<>(numThreaded);
break;
case BIND_SUBOPCODE_THREADED_APPLY: {
// TODO
threadedBindings.add(new Binding(binding));
break;
}
default: {
@ -179,7 +181,7 @@ public class BindingTable {
}
}
break;
}*/
}
default: {
Binding unknownBinding = new Binding(binding);
unknownBinding.unknownOpcode = Byte.toUnsignedInt(b) & BIND_OPCODE_MASK;
@ -197,6 +199,13 @@ public class BindingTable {
return bindings;
}
/**
* {@return the threaded bindings, or null if threaded bindings are not being used}
*/
public List<Binding> getThreadedBindings() {
return threadedBindings;
}
/**
* {@return opcode offsets from the start of the bind data}
*/
@ -234,7 +243,7 @@ public class BindingTable {
private int type;
private int libraryOrdinal;
private long segmentOffset;
private int segmentIndex;
private int segmentIndex = -1;
private long addend;
private boolean weak;
private Integer unknownOpcode;

View File

@ -24,11 +24,10 @@ import ghidra.app.util.bin.format.RelocationException;
import ghidra.app.util.bin.format.macho.*;
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.chained.*;
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.dyld.DyldChainedPtr.DyldChainType;
import ghidra.app.util.bin.format.macho.relocation.*;
import ghidra.app.util.bin.format.macho.threadcommand.ThreadCommand;
import ghidra.app.util.bin.format.objectiveC.ObjectiveC1_Constants;
@ -761,48 +760,13 @@ public class MachoProgramBuilder {
return dyldChainedFixups.processChainedFixups();
}
protected void processBindings(boolean doClassic) {
DataConverter converter = DataConverter.getInstance(program.getLanguage().isBigEndian());
SymbolTable symbolTable = program.getSymbolTable();
protected void processBindings(boolean doClassic) throws Exception {
List<Binding> bindings = new ArrayList<>();
List<DyldInfoCommand> commands = machoHeader.getLoadCommands(DyldInfoCommand.class);
for (DyldInfoCommand command : commands) {
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;
}
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());
}
processBindings(command.getBindingTable());
processBindings(command.getLazyBindingTable());
processBindings(command.getWeakBindingTable());
}
if (commands.size() == 0 && doClassic) {
@ -826,6 +790,67 @@ public class MachoProgramBuilder {
}
}
private void processBindings(BindingTable bindingTable) throws Exception {
DataConverter converter = DataConverter.getInstance(program.getLanguage().isBigEndian());
SymbolTable symbolTable = program.getSymbolTable();
List<Binding> bindings = bindingTable.getBindings();
List<Binding> threadedBindings = bindingTable.getThreadedBindings();
List<SegmentCommand> segments = machoHeader.getAllSegments();
if (threadedBindings != null) {
DyldChainedFixups dyldChainedFixups =
new DyldChainedFixups(program, machoHeader, log, monitor);
DyldChainedImports chainedImports = new DyldChainedImports(bindings);
for (Binding threadedBinding : threadedBindings) {
List<Address> fixedAddresses = new ArrayList<>();
SegmentCommand segment = segments.get(threadedBinding.getSegmentIndex());
dyldChainedFixups.processPointerChain(chainedImports, fixedAddresses,
DyldChainType.DYLD_CHAINED_PTR_ARM64E,
segments.get(threadedBinding.getSegmentIndex()).getVMaddress(),
threadedBinding.getSegmentOffset(), 0);
log.appendMsg("Fixed up %d chained pointers in %s".formatted(fixedAddresses.size(),
segment.getSegmentName()));
}
}
else {
for (Binding binding : bindings) {
if (binding.getUnknownOpcode() != null) {
log.appendMsg(
"Unknown bind opcode: 0x%x".formatted(binding.getUnknownOpcode()));
continue;
}
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());
boolean success = false;
try {
program.getMemory().setBytes(addr, bytes);
success = true;
}
catch (MemoryAccessException e) {
handleRelocationError(addr, String.format(
"Relocation failure at address %s: error accessing memory.", addr));
}
finally {
program.getRelocationTable()
.add(addr, success ? Status.APPLIED_OTHER : Status.FAILURE,
binding.getType(), null, bytes.length, binding.getSymbolName());
}
}
}
}
protected void markupHeaders(MachHeader header, Address headerAddr) throws Exception {
monitor.setMessage("Processing header markup...");