From 25cc02431b61097b2adfc2fbc493b648b0300c3b Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 10 Mar 2025 11:05:40 -0400 Subject: [SharedCache] Refactor Shared Cache In absence of a better name, this commit refactors the shared cache code. --- view/sharedcache/core/MachOProcessor.cpp | 320 +++++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100644 view/sharedcache/core/MachOProcessor.cpp (limited to 'view/sharedcache/core/MachOProcessor.cpp') diff --git a/view/sharedcache/core/MachOProcessor.cpp b/view/sharedcache/core/MachOProcessor.cpp new file mode 100644 index 00000000..be2e9007 --- /dev/null +++ b/view/sharedcache/core/MachOProcessor.cpp @@ -0,0 +1,320 @@ +#include "MachOProcessor.h" +#include "SharedCache.h" + +using namespace BinaryNinja; + +SharedCacheMachOProcessor::SharedCacheMachOProcessor(Ref view, std::shared_ptr vm) +{ + m_view = view; + m_logger = new Logger("SharedCacheMachOProcessor", view->GetFile()->GetSessionId()); + m_vm = std::move(vm); + + // Adjust processor settings. + if (Ref settings = m_view->GetLoadSettings(VIEW_NAME)) + { + if (settings->Contains("loader.dsc.processFunctionStarts")) + m_applyFunctions = settings->Get("loader.dsc.processFunctionStarts", m_view); + } +} + +void SharedCacheMachOProcessor::ApplyHeader(SharedCacheMachOHeader& header) +{ + // Add a section for the header itself. + std::string headerSection = fmt::format("{}::__macho_header", header.identifierPrefix); + // TODO: Support mach_header (non 64bit) + uint64_t headerSectionSize = sizeof(mach_header_64) + header.ident.sizeofcmds; + m_view->AddUserSection(headerSection, header.textBase, headerSectionSize, ReadOnlyDataSectionSemantics); + + ApplyHeaderSections(header); + ApplyHeaderDataVariables(header); + + if (header.linkeditPresent && m_vm->IsAddressMapped(header.linkeditSegment.vmaddr)) + { + if (m_applyFunctions && header.functionStartsPresent) + { + auto targetPlatform = m_view->GetDefaultPlatform(); + auto functions = header.ReadFunctionTable(*m_vm); + for (const auto& func : functions) + { + // TODO: Check to make sure the func exists in a loaded region? + // TODO: ^ this check existed prior so we should add it back. + m_view->AddFunctionForAnalysis(targetPlatform, func); + } + } + + auto typeLib = m_view->GetTypeLibrary(header.installName); + m_view->BeginBulkModifySymbols(); + + // TODO: Why does this need to only happen in linkeditSegment? + // Apply symbols from symbol table. + if (header.symtab.symoff != 0) + { + // Mach-O View symtab processing with + // a ton of stuff cut out so it can work + // NOTE: This table is read relative to the link edit segment file base. + // TODO: Remove this and use the m_symbols in the cache? + const auto symbols = header.ReadSymbolTable(*m_view, *m_vm); + for (const auto& sym : symbols) + ApplySymbol(m_view, typeLib, sym.ToBNSymbol()); + } + + // Apply symbols from export trie. + if (header.exportTriePresent) + { + // NOTE: This table is read relative to the link edit segment file base. + // TODO: Remove this and use the m_symbols in the cache? + const auto exportSymbols = header.ReadExportSymbolTrie(*m_vm); + for (const auto& sym : exportSymbols) + ApplySymbol(m_view, typeLib, sym.ToBNSymbol()); + } + m_view->EndBulkModifySymbols(); + } +} + +uint64_t SharedCacheMachOProcessor::ApplyHeaderSections(SharedCacheMachOHeader& header) +{ + auto initSection = [&](const section_64& section, const std::string& sectionName) { + if (!section.size) + return false; + + std::string type; + BNSectionSemantics semantics = DefaultSectionSemantics; + switch (section.flags & 0xff) + { + case S_REGULAR: + if (section.flags & S_ATTR_PURE_INSTRUCTIONS) + { + type = "PURE_CODE"; + semantics = ReadOnlyCodeSectionSemantics; + } + else if (section.flags & S_ATTR_SOME_INSTRUCTIONS) + { + type = "CODE"; + semantics = ReadOnlyCodeSectionSemantics; + } + else + { + type = "REGULAR"; + } + break; + case S_ZEROFILL: + type = "ZEROFILL"; + semantics = ReadWriteDataSectionSemantics; + break; + case S_CSTRING_LITERALS: + type = "CSTRING_LITERALS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_4BYTE_LITERALS: + type = "4BYTE_LITERALS"; + break; + case S_8BYTE_LITERALS: + type = "8BYTE_LITERALS"; + break; + case S_LITERAL_POINTERS: + type = "LITERAL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_NON_LAZY_SYMBOL_POINTERS: + type = "NON_LAZY_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_LAZY_SYMBOL_POINTERS: + type = "LAZY_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_SYMBOL_STUBS: + type = "SYMBOL_STUBS"; + semantics = ReadOnlyCodeSectionSemantics; + break; + case S_MOD_INIT_FUNC_POINTERS: + type = "MOD_INIT_FUNC_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_MOD_TERM_FUNC_POINTERS: + type = "MOD_TERM_FUNC_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_COALESCED: + type = "COALESCED"; + break; + case S_GB_ZEROFILL: + type = "GB_ZEROFILL"; + semantics = ReadWriteDataSectionSemantics; + break; + case S_INTERPOSING: + type = "INTERPOSING"; + break; + case S_16BYTE_LITERALS: + type = "16BYTE_LITERALS"; + break; + case S_DTRACE_DOF: + type = "DTRACE_DOF"; + break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: + type = "LAZY_DYLIB_SYMBOL_POINTERS"; + semantics = ReadOnlyDataSectionSemantics; + break; + case S_THREAD_LOCAL_REGULAR: + type = "THREAD_LOCAL_REGULAR"; + break; + case S_THREAD_LOCAL_ZEROFILL: + type = "THREAD_LOCAL_ZEROFILL"; + break; + case S_THREAD_LOCAL_VARIABLES: + type = "THREAD_LOCAL_VARIABLES"; + break; + case S_THREAD_LOCAL_VARIABLE_POINTERS: + type = "THREAD_LOCAL_VARIABLE_POINTERS"; + break; + case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS: + type = "THREAD_LOCAL_INIT_FUNCTION_POINTERS"; + break; + default: + type = "UNKNOWN"; + break; + } + + if (strncmp(section.sectname, "__text", sizeof(section.sectname)) == 0) + semantics = ReadOnlyCodeSectionSemantics; + if (strncmp(section.sectname, "__const", sizeof(section.sectname)) == 0) + semantics = ReadOnlyDataSectionSemantics; + if (strncmp(section.sectname, "__data", sizeof(section.sectname)) == 0) + semantics = ReadWriteDataSectionSemantics; + if (strncmp(section.segname, "__DATA_CONST", sizeof(section.segname)) == 0) + semantics = ReadOnlyDataSectionSemantics; + if (strncmp(section.segname, "__auth_got", sizeof(section.segname)) == 0) + semantics = ReadOnlyDataSectionSemantics; + + m_view->AddUserSection(sectionName, section.addr, section.size, semantics, type, section.align); + + return true; + }; + + uint64_t addedSections = 0; + for (size_t i = 0; i < header.sections.size() && i < header.sectionNames.size(); i++) + { + if (initSection(header.sections[i], header.sectionNames[i])) + addedSections++; + } + return addedSections; +} + +void SharedCacheMachOProcessor::ApplyHeaderDataVariables(SharedCacheMachOHeader& header) +{ + // TODO: By using a binary reader we assume the sections have all been mapped. + // TODO: Maybe we should just use the virtual memory reader... + // TODO: We can define symbols and data variables even if there is no backing region FWIW + BinaryReader reader(m_view); + // TODO: Do we support non 64 bit header? + reader.Seek(header.textBase + sizeof(mach_header_64)); + + m_view->DefineDataVariable(header.textBase, Type::NamedType(m_view, QualifiedName("mach_header_64"))); + m_view->DefineAutoSymbol( + new Symbol(DataSymbol, "__macho_header::" + header.identifierPrefix, header.textBase, LocalBinding)); + + auto applyLoadCommand = [&](uint64_t cmdAddr, const load_command& load) { + switch (load.cmd) + { + case LC_SEGMENT: + { + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("segment_command"))); + reader.SeekRelative(5 * 8); + size_t numSections = reader.Read32(); + reader.SeekRelative(4); + for (size_t j = 0; j < numSections; j++) + { + m_view->DefineDataVariable(reader.GetOffset(), Type::NamedType(m_view, QualifiedName("section"))); + auto sectionSymName = + fmt::format("__macho_section::{}_[{}]", header.identifierPrefix, std::to_string(j)); + auto sectionSym = new Symbol(DataSymbol, sectionSymName, reader.GetOffset(), LocalBinding); + m_view->DefineAutoSymbol(sectionSym); + reader.SeekRelative((8 * 8) + 4); + } + break; + } + case LC_SEGMENT_64: + { + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("segment_command_64"))); + reader.SeekRelative(7 * 8); + size_t numSections = reader.Read32(); + reader.SeekRelative(4); + for (size_t j = 0; j < numSections; j++) + { + m_view->DefineDataVariable(reader.GetOffset(), Type::NamedType(m_view, QualifiedName("section_64"))); + auto sectionSymName = + fmt::format("__macho_section_64::{}_[{}]", header.identifierPrefix, std::to_string(j)); + auto sectionSym = new Symbol(DataSymbol, sectionSymName, reader.GetOffset(), LocalBinding); + m_view->DefineAutoSymbol(sectionSym); + reader.SeekRelative(10 * 8); + } + break; + } + case LC_SYMTAB: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("symtab"))); + break; + case LC_DYSYMTAB: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("dysymtab"))); + break; + case LC_UUID: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("uuid"))); + break; + case LC_ID_DYLIB: + case LC_LOAD_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("dylib_command"))); + if (load.cmdsize - 24 <= 150) + m_view->DefineDataVariable( + cmdAddr + 24, Type::ArrayType(Type::IntegerType(1, true), load.cmdsize - 24)); + break; + case LC_CODE_SIGNATURE: + case LC_SEGMENT_SPLIT_INFO: + case LC_FUNCTION_STARTS: + case LC_DATA_IN_CODE: + case LC_DYLIB_CODE_SIGN_DRS: + case LC_DYLD_EXPORTS_TRIE: + case LC_DYLD_CHAINED_FIXUPS: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("linkedit_data"))); + break; + case LC_ENCRYPTION_INFO: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("encryption_info"))); + break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("version_min"))); + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("dyld_info"))); + break; + default: + m_view->DefineDataVariable(cmdAddr, Type::NamedType(m_view, QualifiedName("load_command"))); + break; + } + }; + + try + { + for (size_t i = 0; i < header.ident.ncmds; i++) + { + load_command load {}; + uint64_t curOffset = reader.GetOffset(); + load.cmd = reader.Read32(); + load.cmdsize = reader.Read32(); + + applyLoadCommand(curOffset, load); + m_view->DefineAutoSymbol(new Symbol(DataSymbol, + "__macho_load_command::" + header.identifierPrefix + "_[" + std::to_string(i) + "]", curOffset, + LocalBinding)); + + uint64_t nextOffset = curOffset + load.cmdsize; + reader.Seek(nextOffset); + } + } + catch (ReadException&) + { + m_logger->LogError("Error when applying Mach-O header types at %llx", header.textBase); + } +} -- cgit v1.3.1