mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 13:42:06 +00:00
GP-3701: Adding support for Mach-O BIND_OPCODE_THREADED
This commit is contained in:
parent
fd3c351297
commit
b840f6632c
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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...");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user