summaryrefslogtreecommitdiff
path: root/view
diff options
context:
space:
mode:
authorkat <kat@vector35.com>2024-06-10 12:30:19 -0400
committerkat <kat@vector35.com>2024-06-11 14:28:26 -0400
commitdda39e82169e7bb5747c2e58d22e2844f5832936 (patch)
tree71aa1567a0634a0425debae0f121035404a5781c /view
parentffb69f582d9fce114c1c7ec5e0c1868c331a74c9 (diff)
[Mach-O] Add support for DYLD_INFO rebase information
Diffstat (limited to 'view')
-rw-r--r--view/macho/machoview.cpp164
-rw-r--r--view/macho/machoview.h23
2 files changed, 187 insertions, 0 deletions
diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp
index 5d4712c8..ac3ab884 100644
--- a/view/macho/machoview.cpp
+++ b/view/macho/machoview.cpp
@@ -2011,6 +2011,17 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_
EndBulkModifySymbols();
+ for (auto& relocation : header.rebaseRelocations)
+ {
+ uint64_t relocationLocation = relocation.address;
+ virtualReader.Seek(relocationLocation);
+ uint64_t target = virtualReader.ReadPointer();
+ uint64_t slidTarget = target + m_imageBaseAdjustment;
+ relocation.address = slidTarget;
+ DefineRelocation(m_arch, relocation, slidTarget, relocationLocation);
+ if (m_objcProcessor)
+ m_objcProcessor->AddRelocatedPointer(relocationLocation, slidTarget);
+ }
for (auto& [relocation, name] : header.externalRelocations)
{
if (auto symbol = GetSymbolByRawName(name, GetExternalNameSpace()); symbol)
@@ -2497,6 +2508,157 @@ void MachoView::ReadExportNode(uint64_t viewStart, DataBuffer& buffer, const std
}
}
+
+void MachoView::ParseRebaseTable(BinaryReader& reader, MachOHeader& header, uint32_t tableOffset, uint32_t tableSize)
+{
+ if (tableSize == 0 || tableOffset == 0)
+ return;
+
+ std::function segmentActualLoadAddress = [&](uint64_t segmentIndex) {
+ if (segmentIndex >= header.segments.size())
+ throw ReadException();
+ return header.segments[segmentIndex].vmaddr;
+ };
+ std::function segmentActualEndAddress = [&](uint64_t segmentIndex) {
+ if (segmentIndex >= header.segments.size())
+ throw ReadException();
+ return header.segments[segmentIndex].vmaddr + header.segments[segmentIndex].vmsize;
+ };
+
+ try {
+ reader.Seek(tableOffset);
+ auto table = reader.Read(tableSize);
+
+ BNRelocationInfo rebaseRelocation;
+
+ RebaseType type = RebaseTypeInvalid;
+ uint64_t segmentIndex = 0;
+ uint64_t address = segmentActualLoadAddress(0);
+ uint64_t segmentStartAddress = segmentActualLoadAddress(0);
+ uint64_t segmentEndAddress = segmentActualEndAddress(0);
+ uint64_t count;
+ uint64_t size;
+ uint64_t skip;
+ bool done = false;
+ size_t i = 0;
+ while ( !done && (i < tableSize))
+ {
+ uint8_t opAndIm = table[i];
+ uint8_t opcode = opAndIm & RebaseOpcodeMask;
+ uint64_t immediate = opAndIm & RebaseImmediateMask;
+ m_logger->LogDebug("Rebase opcode 0x%llx (im: 0x%llx)", opcode, immediate);
+ i++;
+ switch (opcode)
+ {
+ case RebaseOpcodeDone:
+ done = true;
+ break;
+ case RebaseOpcodeSetTypeImmediate:
+ type = (RebaseType)immediate;
+ break;
+ case RebaseOpcodeSetSegmentAndOffsetUleb:
+ segmentIndex = immediate;
+ address = segmentActualLoadAddress(segmentIndex) + readLEB128(table, tableSize, i);
+ segmentStartAddress = segmentActualLoadAddress(segmentIndex);
+ segmentEndAddress = segmentActualEndAddress(segmentIndex);
+ break;
+ case RebaseOpcodeAddAddressUleb:
+ address += readLEB128(table, tableSize, i);
+ break;
+ case RebaseOpcodeAddAddressImmediateScaled:
+ address += immediate * m_addressSize;
+ break;
+ case RebaseOpcodeDoRebaseImmediateTimes:
+ count = immediate;
+ for (uint64_t j = 0; j < count; ++j)
+ {
+ m_logger->LogDebug("Rebasing address %llx", address);
+ if (address < segmentStartAddress || address >= segmentEndAddress)
+ {
+ m_logger->LogError("Rebase address out of segment bounds");
+ throw ReadException();
+ }
+ memset(&rebaseRelocation, 0, sizeof(rebaseRelocation));
+ rebaseRelocation.nativeType = BINARYNINJA_MANUAL_RELOCATION;
+ rebaseRelocation.address = address;
+ rebaseRelocation.size = m_addressSize;
+ rebaseRelocation.pcRelative = false;
+ rebaseRelocation.external = false;
+ header.rebaseRelocations.push_back(rebaseRelocation);
+ address += m_addressSize;
+ }
+ break;
+ case RebaseOpcodeDoRebaseUlebTimes:
+ count = readLEB128(table, tableSize, i);
+ for (uint64_t j = 0; j < count; ++j)
+ {
+ m_logger->LogDebug("Rebasing address %llx", address);
+ if (address < segmentStartAddress || address >= segmentEndAddress)
+ {
+ m_logger->LogError("Rebase address out of segment bounds");
+ throw ReadException();
+ }
+ memset(&rebaseRelocation, 0, sizeof(rebaseRelocation));
+ rebaseRelocation.nativeType = BINARYNINJA_MANUAL_RELOCATION;
+ rebaseRelocation.address = address;
+ rebaseRelocation.size = m_addressSize;
+ rebaseRelocation.pcRelative = false;
+ rebaseRelocation.external = false;
+ header.rebaseRelocations.push_back(rebaseRelocation);
+ address += m_addressSize;
+ }
+ break;
+ case RebaseOpcodeDoRebaseAddAddressUleb:
+ m_logger->LogDebug("Rebasing address %llx", address);
+ if (address < segmentStartAddress || address >= segmentEndAddress)
+ {
+ m_logger->LogError("Rebase address out of segment bounds");
+ throw ReadException();
+ }
+ memset(&rebaseRelocation, 0, sizeof(rebaseRelocation));
+ rebaseRelocation.nativeType = BINARYNINJA_MANUAL_RELOCATION;
+ rebaseRelocation.address = address;
+ rebaseRelocation.size = m_addressSize;
+ rebaseRelocation.pcRelative = false;
+ rebaseRelocation.external = false;
+ header.rebaseRelocations.push_back(rebaseRelocation);
+ address += readLEB128(table, tableSize, i) + m_addressSize;
+ break;
+ case RebaseOpcodeDoRebaseUlebTimesSkippingUleb:
+ count = readLEB128(table, tableSize, i);
+ skip = readLEB128(table, tableSize, i);
+ for (uint64_t j = 0; j < count; ++j)
+ {
+ m_logger->LogDebug("Rebasing address %llx", address);
+ if (address < segmentStartAddress || address >= segmentEndAddress)
+ {
+ m_logger->LogError("Rebase address out of segment bounds");
+ throw ReadException();
+ }
+ memset(&rebaseRelocation, 0, sizeof(rebaseRelocation));
+ rebaseRelocation.nativeType = BINARYNINJA_MANUAL_RELOCATION;
+ rebaseRelocation.address = address;
+ rebaseRelocation.size = m_addressSize;
+ rebaseRelocation.pcRelative = false;
+ rebaseRelocation.external = false;
+ header.rebaseRelocations.push_back(rebaseRelocation);
+ address += skip + m_addressSize;
+ }
+ break;
+ default:
+ m_logger->LogError("Unknown rebase opcode %d", opcode);
+ throw ReadException();
+ break;
+ }
+ }
+ }
+ catch (ReadException&)
+ {
+ m_logger->LogError("Error while parsing Rebase Table");
+ }
+}
+
+
void MachoView::ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNSymbolType incomingType, uint32_t tableOffset,
uint32_t tableSize, BNSymbolBinding binding)
{
@@ -2665,6 +2827,8 @@ void MachoView::ParseSymbolTable(BinaryReader& reader, MachOHeader& header, cons
m_logger->LogDebug("Lazy symbols");
ParseDynamicTable(reader, header, ImportAddressSymbol, header.dyldInfo.lazy_bind_off,
header.dyldInfo.lazy_bind_size, GlobalBinding);
+ m_logger->LogDebug("Parsing rebase table");
+ ParseRebaseTable(reader, header, header.dyldInfo.rebase_off, header.dyldInfo.rebase_size);
}
if (header.chainedFixupsPresent)
{
diff --git a/view/macho/machoview.h b/view/macho/machoview.h
index 13086ffc..dbc0f172 100644
--- a/view/macho/machoview.h
+++ b/view/macho/machoview.h
@@ -681,6 +681,27 @@ namespace BinaryNinja
MachOPPC64 = MachOABI64 | MachOPPC,
};
+ enum RebaseType {
+ RebaseTypeInvalid = 0,
+ RebaseTypePointer = 1,
+ RebaseTypeTextAbsolute32 = 2,
+ RebaseTypeTextPCRel32 = 3
+ };
+
+ enum RebaseOpcode {
+ RebaseOpcodeMask = 0xF0u, // REBASE_OPCODE_MASK
+ RebaseImmediateMask = 0x0Fu, // REBASE_IMMEDIATE_MASK
+ RebaseOpcodeDone = 0x00u, // REBASE_OPCODE_DONE
+ RebaseOpcodeSetTypeImmediate = 0x10u, // REBASE_OPCODE_SET_TYPE_IMM
+ RebaseOpcodeSetSegmentAndOffsetUleb = 0x20u, // REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+ RebaseOpcodeAddAddressUleb = 0x30u, // REBASE_OPCODE_ADD_ADDR_ULEB
+ RebaseOpcodeAddAddressImmediateScaled = 0x40u, // REBASE_OPCODE_ADD_ADDR_IMM_SCALED
+ RebaseOpcodeDoRebaseImmediateTimes = 0x50u, // REBASE_OPCODE_DO_REBASE_IMM_TIMES
+ RebaseOpcodeDoRebaseUlebTimes = 0x60u, // REBASE_OPCODE_DO_REBASE_ULEB_TIMES
+ RebaseOpcodeDoRebaseAddAddressUleb = 0x70u, // REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
+ RebaseOpcodeDoRebaseUlebTimesSkippingUleb = 0x80u, // REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
+ };
+
enum BindOpcode {
BindOpcodeMask = 0xF0u, // BIND_OPCODE_MASK
BindImmediateMask = 0x0Fu, // BIND_IMMEDIATE_MASK
@@ -1240,6 +1261,7 @@ namespace BinaryNinja
std::vector<uint64_t> m_entryPoints; //list of entrypoints
std::vector<std::pair<BNRelocationInfo, std::string>> externalRelocations;
+ std::vector<BNRelocationInfo> rebaseRelocations;
symtab_command symtab;
dysymtab_command dysymtab;
@@ -1346,6 +1368,7 @@ namespace BinaryNinja
void ReadExportNode(uint64_t viewStart, DataBuffer& buffer, const std::string& currentText,
size_t cursor, uint32_t endGuard);
+ void ParseRebaseTable(BinaryReader& reader, MachOHeader& header, uint32_t tableOffset, uint32_t tableSize);
void ParseDynamicTable(BinaryReader& reader, MachOHeader& header, BNSymbolType type, uint32_t tableOffset, uint32_t tableSize,
BNSymbolBinding binding);
bool GetSectionPermissions(MachOHeader& header, uint64_t address, uint32_t &flags);