diff options
| author | kat <kat@vector35.com> | 2025-07-06 15:05:01 -0400 |
|---|---|---|
| committer | kat <kat@vector35.com> | 2025-07-07 07:37:23 -0400 |
| commit | 768f7c78465fb93936e5ca50a0ca712664fe54e7 (patch) | |
| tree | 78c73e0022d6006882e365a257595a5b55a37432 /view/kernelcache/core/KernelCache.cpp | |
| parent | 8f3e251c42169fb4fe8db9403d900571434d88ff (diff) | |
KernelCache rewrite
Diffstat (limited to 'view/kernelcache/core/KernelCache.cpp')
| -rw-r--r-- | view/kernelcache/core/KernelCache.cpp | 2520 |
1 files changed, 113 insertions, 2407 deletions
diff --git a/view/kernelcache/core/KernelCache.cpp b/view/kernelcache/core/KernelCache.cpp index 6cb3fca6..36f3331b 100644 --- a/view/kernelcache/core/KernelCache.cpp +++ b/view/kernelcache/core/KernelCache.cpp @@ -1,1101 +1,127 @@ -// -// Created by kat on 5/19/23. -// - -#include "binaryninjaapi.h" - -/* --- - * This is the primary image loader logic for Kernelcaches. - * */ - #include "KernelCache.h" -#include <filesystem> -#include <utility> -#include <fcntl.h> -#include <memory> -#include <chrono> -#include <thread> +#include <regex> +#include <filesystem> using namespace BinaryNinja; -using namespace KernelCacheCore; - -namespace KernelCacheCore { - - - -#ifdef _MSC_VER - -int count_trailing_zeros(uint64_t value) { - unsigned long index; // 32-bit long on Windows - if (_BitScanForward64(&index, value)) { - return index; - } else { - return 64; // If the value is 0, return 64. - } -} -#else -int count_trailing_zeros(uint64_t value) { - return value == 0 ? 64 : __builtin_ctzll(value); -} -#endif - -// State that does not change after `PerformInitialLoad`. -struct KernelCache::CacheInfo : - public MetadataSerializable<KernelCache::CacheInfo, std::optional<KernelCache::CacheInfo>> -{ - // std::unordered_map<uint64_t, KernelCacheMachOHeader> headers; - std::vector<KernelCacheImage> images; - std::unordered_map<std::string, uint64_t> imageStarts; - - KernelCacheFormat cacheFormat = FilesetCacheFormat; - -#ifndef NDEBUG - void Verify() const; -#endif - - uint64_t BaseAddress() const; - - void Store(SerializationContext&) const; - static std::optional<KernelCache::CacheInfo> Load(DeserializationContext&); -}; - -struct State : public MetadataSerializable<State> -{ - std::unordered_map<uint64_t, KernelCacheImage> loadedImages; - // Store only. Loading is done via `ModifiedState`. - void Store(SerializationContext&, std::optional<KCViewState> viewState) const; -}; - -struct KernelCache::ModifiedState : public State, public MetadataSerializable<KernelCache::ModifiedState> -{ - std::optional<KCViewState> viewState; - - using Base = MetadataSerializable<KernelCache::ModifiedState>; - using Base::AsMetadata; - using Base::LoadFromString; - - void Store(SerializationContext&) const; - static KernelCache::ModifiedState Load(DeserializationContext&); - static KernelCache::ModifiedState LoadAll(BinaryNinja::BinaryView*, const CacheInfo&); - - void Merge(KernelCache::ModifiedState&& other); -}; - -struct KernelCache::ViewSpecificState -{ - std::mutex typeLibraryMutex; - std::unordered_map<std::string, Ref<TypeLibrary>> typeLibraries; - - std::mutex viewOperationsThatInfluenceMetadataMutex; - - std::atomic<BNKCViewLoadProgress> progress; - - std::mutex cacheInfoMutex; - std::shared_ptr<const KernelCache::CacheInfo> cacheInfo; - - std::mutex stateMutex; - struct State state; - - std::atomic<KCViewState> viewState; - uint64_t savedModifications = 0; -}; - -namespace { - - std::shared_ptr<KernelCache::ViewSpecificState> ViewSpecificStateForId( - uint64_t viewIdentifier, bool insertIfNeeded = true) - { - static std::mutex viewSpecificStateMutex; - static std::unordered_map<uint64_t, std::weak_ptr<KernelCache::ViewSpecificState>> viewSpecificState; - - std::lock_guard lock(viewSpecificStateMutex); - - if (auto it = viewSpecificState.find(viewIdentifier); it != viewSpecificState.end()) - { - if (auto statePtr = it->second.lock()) - return statePtr; - } - - if (!insertIfNeeded) - return nullptr; - - auto statePtr = std::make_shared<KernelCache::ViewSpecificState>(); - viewSpecificState[viewIdentifier] = statePtr; - - // Prune entries for any views that are no longer in use. - for (auto it = viewSpecificState.begin(); it != viewSpecificState.end();) - { - if (it->second.expired()) - it = viewSpecificState.erase(it); - else - ++it; - } - - return statePtr; - } - - std::shared_ptr<KernelCache::ViewSpecificState> ViewSpecificStateForView(Ref<BinaryNinja::BinaryView> view) - { - return ViewSpecificStateForId(view->GetFile()->GetSessionId()); - } - - std::string base_name(std::string const& path) - { - return path.substr(path.find_last_of("/\\") + 1); - } - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" - static int64_t readSLEB128(DataBuffer& buffer, size_t length, size_t& offset) - { - uint8_t cur; - int64_t value = 0; - size_t shift = 0; - while (offset < length) - { - cur = buffer[offset++]; - value |= (cur & 0x7f) << shift; - shift += 7; - if ((cur & 0x80) == 0) - break; - } - value = (value << (64 - shift)) >> (64 - shift); - return value; - } -#pragma clang diagnostic pop - - - static uint64_t readLEB128(DataBuffer& p, size_t end, size_t& offset) - { - uint64_t result = 0; - int bit = 0; - do - { - if (offset >= end) - return -1; - - uint64_t slice = p[offset] & 0x7f; - - if (bit > 63) - return -1; - else - { - result |= (slice << bit); - bit += 7; - } - } while (p[offset++] & 0x80); - return result; - } - - - uint64_t readValidULEB128(DataBuffer& buffer, size_t& cursor) - { - uint64_t value = readLEB128(buffer, buffer.GetLength(), cursor); - if ((int64_t)value == -1) - throw ReadException(); - return value; - } - -} // namespace - -void KernelCache::PerformInitialLoad(std::lock_guard<std::mutex>& lock) -{ - bool is64 = m_kcView->GetAddressSize() == 8; - - m_logger->LogInfo("Performing initial load of Kernel Cache"); - - m_viewSpecificState->progress = LoadProgressLoadingCaches; - - CacheInfo initialState; - initialState.cacheFormat = FilesetCacheFormat; - - m_viewSpecificState->progress = LoadProgressLoadingImages; - - // We have set up enough metadata to map VM now. - // Iterate load comands: - BinaryReader reader(m_kcView->GetParentView()); - - uint32_t magic = reader.Read32(); - if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit - { - m_logger->LogError("Invalid magic number in KernelCache"); - return; - } - uint32_t cpuType = reader.Read32(); - reader.SeekRelative(4); - uint32_t fileType = reader.Read32(); - if (fileType != MH_FILESET) - { - m_logger->LogError("Invalid file type in KernelCache"); - return; - } - uint32_t ncmds = reader.Read32(); - reader.SeekRelative(8); - if ((cpuType & MachOABIMask) != MachOABI64) - { - m_logger->LogError("Invalid ABI in KernelCache. 32 bit not yet supported."); - return; - } - if (is64) - reader.SeekRelative(4); - - uint64_t off = reader.GetOffset(); - - while (ncmds--) - { - reader.Seek(off); - uint32_t cmd = reader.Read32(); - uint32_t cmdsize = reader.Read32(); - if (cmd == LC_FILESET_ENTRY) - { - // uint64_t vmAddr =reader.Read64(); - reader.SeekRelative(m_kcView->GetAddressSize()); - uint64_t fileOff = reader.Read64(); - uint32_t entryID = reader.Read32(); - reader.Seek(entryID + off); - std::string installName = reader.ReadCString(0x1000); - initialState.imageStarts[installName] = fileOff; - } - off += cmdsize; - } - - for (const auto& start : initialState.imageStarts) - { - try { - auto imageHeader = KernelCache::LoadHeaderForAddress(m_kcView, start.second, start.first); - if (imageHeader) - { - KernelCacheImage image; - image.installName = start.first; - image.headerFileLocation = start.second; - for (const auto& segment : imageHeader->segments) - { - char segName[17]; - memcpy(segName, segment.segname, 16); - segName[16] = 0; - MemoryRegion sectionRegion; - sectionRegion.prettyName = imageHeader.value().identifierPrefix + "::" + std::string(segName); - sectionRegion.start = segment.vmaddr; - sectionRegion.size = segment.vmsize; - sectionRegion.fileOffset = segment.fileoff; - uint32_t flags = 0; - if (segment.initprot & MACHO_VM_PROT_READ) - flags |= SegmentReadable; - if (segment.initprot & MACHO_VM_PROT_WRITE) - flags |= SegmentWritable; - if (segment.initprot & MACHO_VM_PROT_EXECUTE) - flags |= SegmentExecutable; - if (((segment.initprot & MACHO_VM_PROT_WRITE) == 0) && - ((segment.maxprot & MACHO_VM_PROT_WRITE) == 0)) - flags |= SegmentDenyWrite; - if (((segment.initprot & MACHO_VM_PROT_EXECUTE) == 0) && - ((segment.maxprot & MACHO_VM_PROT_EXECUTE) == 0)) - flags |= SegmentDenyExecute; - - // if we're positive we have an entry point for some reason, force the segment - // executable. this helps with kernel images. - for (auto &entryPoint : imageHeader->m_entryPoints) - if (segment.vmaddr <= entryPoint && (entryPoint < (segment.vmaddr + segment.filesize))) - flags |= SegmentExecutable; - - sectionRegion.flags = (BNSegmentFlag)flags; - image.regions.push_back(sectionRegion); - } - initialState.images.push_back(image); - } - else - { - m_logger->LogError("Failed to load Mach-O header for %s", start.first.c_str()); - } - } - catch (std::exception& ex) - { - m_logger->LogError("Failed to load Mach-O header for %s: %s", start.first.c_str(), ex.what()); - } - } - - m_cacheInfo = std::make_shared<CacheInfo>(std::move(initialState)); - m_modifiedState->viewState = KCViewStateLoaded; - SaveCacheInfoToKCView(lock); - SaveModifiedStateToKCView(lock); - - m_logger->LogDebug("Finished initial load of KernelCache"); - - m_viewSpecificState->progress = LoadProgressFinished; -} - -void KernelCache::DeserializeFromRawView(std::lock_guard<std::mutex>& lock) -{ - std::lock_guard cacheInfoLock(m_viewSpecificState->cacheInfoMutex); - if (m_viewSpecificState->cacheInfo) - { - m_cacheInfo = m_viewSpecificState->cacheInfo; - m_modifiedState = std::make_unique<ModifiedState>(); - m_metadataValid = true; - return; - } - - if (KernelCacheMetadata::ViewHasMetadata(m_kcView)) - { - auto metadata = KernelCacheMetadata::LoadFromView(m_kcView); - if (!metadata) - { - m_metadataValid = false; - m_logger->LogError("Failed to deserialize Shared Cache metadata"); - return; - } - - m_viewSpecificState->viewState = metadata->state->viewState.value_or(KCViewStateUnloaded); - m_viewSpecificState->state = std::move(*metadata->state); - m_viewSpecificState->cacheInfo = std::move(metadata->cacheInfo); - - m_cacheInfo = m_viewSpecificState->cacheInfo; - m_modifiedState = std::make_unique<ModifiedState>(); - m_metadataValid = true; - return; - } - - m_cacheInfo = nullptr; - m_modifiedState = std::make_unique<ModifiedState>(); - m_modifiedState->viewState = KCViewStateUnloaded; - m_metadataValid = true; -} - -std::string to_hex_string(uint64_t value) +std::pair<std::string, Ref<Type>> CacheSymbol::DemangledName(BinaryView &view) const { - std::stringstream ss; - ss << std::hex << value; - return ss.str(); -} - - -KernelCache::KernelCache(BinaryNinja::Ref<BinaryNinja::BinaryView> kcView) : m_kcView(kcView), - m_viewSpecificState(ViewSpecificStateForView(kcView)) -{ - std::lock_guard lock(m_mutex); - m_logger = LogRegistry::GetLogger("KernelCache", kcView->GetFile()->GetSessionId()); - if (kcView->GetTypeName() != KC_VIEW_NAME) - { - // Unreachable? - m_logger->LogError("Attempted to create KernelCache object from non-KernelCache view"); - return; - } - - INIT_KERNELCACHE_API_OBJECT() - DeserializeFromRawView(lock); - if (!m_metadataValid) - return; - - if (m_modifiedState->viewState.value_or(m_viewSpecificState->viewState) != KCViewStateUnloaded) - { - m_viewSpecificState->progress = LoadProgressFinished; - return; - } - - std::unique_lock viewOperationsLock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex); - - try { - PerformInitialLoad(lock); - } - catch (...) - { - m_logger->LogError("Failed to perform initial load of KernelCache"); - } -} - -KernelCache::~KernelCache() { + QualifiedName qname; + Ref<Type> outType; + std::string shortName = name; + if (DemangleGeneric(view.GetDefaultArchitecture(), name, outType, qname, &view, true)) + shortName = qname.GetString(); + return { shortName, outType }; } -KernelCache* KernelCache::GetFromKCView(BinaryNinja::Ref<BinaryNinja::BinaryView> kcView) +std::pair<Ref<Symbol>, Ref<Type>> CacheSymbol::GetBNSymbolAndType(BinaryView& view) const { - if (kcView->GetTypeName() != KC_VIEW_NAME) - return nullptr; - try { - return new KernelCache(kcView); - } - catch (...) - { - return nullptr; - } + auto [shortName, demangledType] = DemangledName(view); + auto symbol = new Symbol(type, shortName, shortName, name, address, nullptr); + return {symbol, demangledType}; } - -uint64_t KernelCache::FastGetImageCount(BinaryNinja::Ref<BinaryNinja::BinaryView> kcView) +std::vector<std::string> CacheImage::GetDependencies() const { - if (!kcView->GetParentView()) - return 0; - auto reader = BinaryReader(kcView->GetParentView()); - uint32_t magic = reader.Read32(); - if (magic != MH_CIGAM_64 && magic != MH_MAGIC_64) // FIXME 32 bit - { - return 0; - } - reader.Seek(0xc); - uint32_t fileType = reader.Read32(); - if (fileType != MH_FILESET) - { - return 0; - } - uint32_t ncmds = reader.Read32(); - - uint64_t imageCount = 0; - - uint64_t off = 0x20; // FIXME 32 bit - while (ncmds--) - { - reader.Seek(off); - uint32_t cmd = reader.Read32(); - uint32_t cmdsize = reader.Read32(); - if (cmd == LC_FILESET_ENTRY) - { - imageCount++; - } - off += cmdsize; - } - - return imageCount; -} - - -const std::unordered_map<std::string, uint64_t>& KernelCache::AllImageStarts() const -{ - return m_cacheInfo->imageStarts; -} - - -const std::unordered_map<uint64_t, KernelCacheMachOHeader> KernelCache::AllImageHeaders() const -{ - std::unordered_map<uint64_t, KernelCacheMachOHeader> headers; - for (const auto& start : m_cacheInfo->imageStarts) - { - auto header = LoadHeaderForAddress(m_kcView, start.second, start.first); - if (header) - { - headers[start.second] = *header; - } - } - return headers; -} - - -KCViewState KernelCache::ViewState() const -{ - return m_viewSpecificState->viewState; -} - -std::optional<uint64_t> KernelCache::GetImageStart(std::string installName) -{ - for (const auto& [name, start] : m_cacheInfo->imageStarts) - { - if (name == installName) - { - return start; - } - } - return {}; -} - -std::optional<KernelCacheMachOHeader> KernelCache::HeaderForVMAddress(uint64_t address) -{ - for (const auto& img : m_cacheInfo->images) - { - for (const auto& region : img.regions) - { - if (region.start <= address && region.start + region.size > address) - { - return LoadHeaderForAddress(m_kcView, img.headerFileLocation, img.installName); - } - } - } - return {}; -} - -std::optional<KernelCacheMachOHeader> KernelCache::HeaderForFileAddress(uint64_t address) -{ - for (const auto& img : m_cacheInfo->images) - { - for (const auto& region : img.regions) - { - if (region.fileOffset <= address && region.fileOffset + region.size > address) - { - return LoadHeaderForAddress(m_kcView, img.headerFileLocation, img.installName); - } - } - } + if (header) + return header->dylibs; return {}; } -std::string KernelCache::NameForAddress(uint64_t address) +KernelCache::KernelCache(uint64_t addressSize) { - if (auto header = HeaderForVMAddress(address)) - { - for (const auto& section : header->sections) - { - if (section.addr <= address && section.addr + section.size > address) - { - char sectionName[17]; - strncpy(sectionName, section.sectname, 16); - sectionName[16] = '\0'; - return header->identifierPrefix + "::" + sectionName; - } - } - } - return ""; + m_namedSymMutex = std::make_unique<std::shared_mutex>(); } -std::string KernelCache::ImageNameForAddress(uint64_t address) -{ - if (auto header = HeaderForVMAddress(address)) - { - return header->identifierPrefix; - } - return ""; -} -bool KernelCache::LoadImageContainingAddress(uint64_t address) +void KernelCache::AddImage(CacheImage&& image) { - std::lock_guard<std::mutex> lock(m_mutex); - return LoadImageContainingAddress(lock, address); + m_images.insert({image.headerVirtualAddress, std::move(image)}); } -bool KernelCache::LoadImageContainingAddress(std::lock_guard<std::mutex>& lock, uint64_t address) +void KernelCache::AddSymbol(CacheSymbol symbol) { - for (const auto& img : m_cacheInfo->images) - { - for (const auto& region : img.regions) - { - if (region.start <= address && region.start + region.size > address) - { - return LoadImageWithInstallName(lock, img.installName); - } - } - } - return false; + m_symbols.insert({symbol.address, std::move(symbol)}); } - -bool KernelCache::LoadImageWithInstallName(std::string installName) +void KernelCache::AddSymbols(std::vector<CacheSymbol>&& symbols) { - std::lock_guard<std::mutex> lock(m_mutex); - return LoadImageWithInstallName(lock, installName); + for (auto& symbol : symbols) + m_symbols.insert({symbol.address, std::move(symbol)}); } - -bool KernelCache::LoadImageWithInstallName(std::lock_guard<std::mutex>& lock, std::string installName) +bool KernelCache::ProcessEntryImage(Ref<BinaryView> bv, const std::string& path, const fileset_entry_command& info) { - auto settings = m_kcView->GetLoadSettings(KC_VIEW_NAME); - - std::lock_guard viewSpecificStateLock(m_viewSpecificState->viewOperationsThatInfluenceMetadataMutex); - - m_logger->LogInfo("Loading image %s", installName.c_str()); - const KernelCacheImage* targetImage = nullptr; - - // FIXME at some point the logic func should be one with targetImage passed, and an installName wrapper can be added. - // In many use cases of this function we already have our target image. - for (auto& cacheImage : m_cacheInfo->images) - { - if (cacheImage.installName == installName) - { - targetImage = &cacheImage; - break; - } - } - if (!targetImage) - return false; - - const auto header = LoadHeaderForAddress(m_kcView, targetImage->headerFileLocation, targetImage->installName); - if (!header) + auto imageHeader = KernelCacheMachOHeader::ParseHeaderForAddress(bv, info.vmaddr, info.fileoff, path); + if (!imageHeader.has_value()) return false; - m_modifiedState->viewState = KCViewStateLoadedWithImages; + // Add the image to the cache. + CacheImage image; + image.headerFileAddress = info.fileoff; + image.headerVirtualAddress = info.vmaddr; + image.path = path; - InitializeSegmentsForHeader(m_kcView, *header, *targetImage); - - // Optimize relocations we just added en masse. - m_kcView->FinalizeNewSegments(); - - // FIXME Load TypeLibrary here when we have those for the kernel. + // Add all image regions. + for (const auto& segment : imageHeader->segments) + { + char segName[17]; + memcpy(segName, segment.segname, 16); + segName[16] = 0; - m_modifiedState->loadedImages[targetImage->headerFileLocation] = *targetImage; + CacheRegion sectionRegion; + sectionRegion.name = imageHeader->identifierPrefix + "::" + std::string(segName); + sectionRegion.start = segment.vmaddr; + sectionRegion.size = segment.vmsize; + // Associate this region with this image, this makes it easier to identify what image owns this region. + sectionRegion.imageStart = image.headerFileAddress; - SaveModifiedStateToKCView(lock); + uint32_t flags = SegmentFlagsFromMachOProtections(segment.initprot, segment.maxprot); + // if we're positive we have an entry point for some reason, force the segment + // executable. this helps with kernel images. + for (const auto& entryPoint : imageHeader->m_entryPoints) + if (segment.vmaddr <= entryPoint && (entryPoint < (segment.vmaddr + segment.filesize))) + flags |= SegmentExecutable; + sectionRegion.flags = static_cast<BNSegmentFlag>(flags); - auto h = KernelCache::LoadHeaderForAddress(m_kcView, targetImage->headerFileLocation, installName); - if (!h.has_value()) - { - return false; + image.regions.push_back(std::move(sectionRegion)); } - KernelCache::InitializeHeader(m_kcView, *h); + // Add the exported symbols to the available symbols. + std::vector<CacheSymbol> exportSymbols = imageHeader->ReadExportSymbolTrie(bv); + AddSymbols(std::move(exportSymbols)); + TableInfo symbolInfo = { imageHeader->symtab.symoff, imageHeader->symtab.nsyms }; + TableInfo stringInfo = { imageHeader->symtab.stroff, imageHeader->symtab.strsize }; + std::vector<CacheSymbol> symbols = imageHeader->ReadSymbolTable(bv, symbolInfo, stringInfo); + AddSymbols(std::move(symbols)); - m_kcView->AddAnalysisOption("linearsweep"); - m_kcView->UpdateAnalysis(); + // This is behind a shared pointer as the header itself is very large. + image.header = std::make_shared<KernelCacheMachOHeader>(std::move(*imageHeader)); + AddImage(std::move(image)); return true; } -std::optional<KernelCacheMachOHeader> KernelCache::LoadHeaderForAddress(Ref<BinaryView> view, uint64_t address, std::string installName) +void KernelCache::ProcessSymbols() { - KernelCacheMachOHeader header; - - header.installName = installName; - header.identifierPrefix = base_name(installName); - - std::string errorMsg; - // address is a Raw file offset - BinaryReader reader(view->GetParentView()); - reader.Seek(address); - - header.ident.magic = reader.Read32(); - - BNEndianness endianness; - if (header.ident.magic == MH_MAGIC || header.ident.magic == MH_MAGIC_64) - endianness = LittleEndian; - else if (header.ident.magic == MH_CIGAM || header.ident.magic == MH_CIGAM_64) - endianness = BigEndian; - else - { - return {}; - } - - reader.SetEndianness(endianness); - header.ident.cputype = reader.Read32(); - header.ident.cpusubtype = reader.Read32(); - header.ident.filetype = reader.Read32(); - header.ident.ncmds = reader.Read32(); - header.ident.sizeofcmds = reader.Read32(); - header.ident.flags = reader.Read32(); - if ((header.ident.cputype & MachOABIMask) == MachOABI64) // address size == 8 - { - header.ident.reserved = reader.Read32(); - } - header.loadCommandOffset = reader.GetOffset(); - - // Parse segment commands - try - { - for (size_t i = 0; i < header.ident.ncmds; i++) - { - // BNLogInfo("of 0x%llx", reader.GetOffset()); - load_command load; - segment_command_64 segment64; - section_64 sect; - memset(§, 0, sizeof(sect)); - size_t curOffset = reader.GetOffset(); - load.cmd = reader.Read32(); - load.cmdsize = reader.Read32(); - size_t nextOffset = curOffset + load.cmdsize; - if (load.cmdsize < sizeof(load_command)) - return {}; - - switch (load.cmd) - { - case LC_MAIN: - { - uint64_t entryPoint = reader.Read64(); - header.entryPoints.push_back({entryPoint, true}); - (void)reader.Read64(); // Stack start - break; - } - case LC_SEGMENT: // map the 32bit version to 64 bits - segment64.cmd = LC_SEGMENT_64; - reader.Read(&segment64.segname, 16); - segment64.vmaddr = reader.Read32(); - segment64.vmsize = reader.Read32(); - segment64.fileoff = reader.Read32(); - segment64.filesize = reader.Read32(); - segment64.maxprot = reader.Read32(); - segment64.initprot = reader.Read32(); - segment64.nsects = reader.Read32(); - segment64.flags = reader.Read32(); - if (strncmp(segment64.segname, "__TEXT\0", 7) == 0) - { - header.relocationBase = segment64.vmaddr; - header.textBase = segment64.vmaddr; - header.textBaseFileOffset = segment64.fileoff; - } - for (size_t j = 0; j < segment64.nsects; j++) - { - reader.Read(§.sectname, 16); - reader.Read(§.segname, 16); - sect.addr = reader.Read32(); - sect.size = reader.Read32(); - sect.offset = reader.Read32(); - sect.align = reader.Read32(); - sect.reloff = reader.Read32(); - sect.nreloc = reader.Read32(); - sect.flags = reader.Read32(); - sect.reserved1 = reader.Read32(); - sect.reserved2 = reader.Read32(); - // if the segment isn't mapped into virtual memory don't add the corresponding sections. - if (segment64.vmsize > 0) - { - header.sections.push_back(sect); - } - if (!strncmp(sect.sectname, "__mod_init_func", 15)) - header.moduleInitSections.push_back(sect); - if (!strncmp(sect.sectname, "__mod_term_func", 15)) - header.moduleTermSections.push_back(sect); - if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) - == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) - header.symbolStubSections.push_back(sect); - if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS) - header.symbolPointerSections.push_back(sect); - if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS) - header.symbolPointerSections.push_back(sect); - } - header.segments.push_back(segment64); - break; - case LC_SEGMENT_64: - segment64.cmd = LC_SEGMENT_64; - reader.Read(&segment64.segname, 16); - segment64.vmaddr = reader.Read64(); - segment64.vmsize = reader.Read64(); - segment64.fileoff = reader.Read64(); - segment64.filesize = reader.Read64(); - segment64.maxprot = reader.Read32(); - segment64.initprot = reader.Read32(); - segment64.nsects = reader.Read32(); - segment64.flags = reader.Read32(); - if (strncmp(segment64.segname, "__LINKEDIT", 10) == 0) - { - header.linkeditSegment = segment64; - header.linkeditPresent = true; - } - if (strncmp(segment64.segname, "__TEXT\0", 7) == 0) - { - header.relocationBase = segment64.vmaddr; - header.textBase = segment64.vmaddr; - header.textBaseFileOffset = segment64.fileoff; - } - for (size_t j = 0; j < segment64.nsects; j++) - { - reader.Read(§.sectname, 16); - reader.Read(§.segname, 16); - sect.addr = reader.Read64(); - sect.size = reader.Read64(); - sect.offset = reader.Read32(); - sect.align = reader.Read32(); - sect.reloff = reader.Read32(); - sect.nreloc = reader.Read32(); - sect.flags = reader.Read32(); - sect.reserved1 = reader.Read32(); - sect.reserved2 = reader.Read32(); - sect.reserved3 = reader.Read32(); - // if the segment isn't mapped into virtual memory don't add the corresponding sections. - if (segment64.vmsize > 0) - { - header.sections.push_back(sect); - } - - if (!strncmp(sect.sectname, "__mod_init_func", 15)) - header.moduleInitSections.push_back(sect); - if ((sect.flags & (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) - == (S_ATTR_SELF_MODIFYING_CODE | S_SYMBOL_STUBS)) - header.symbolStubSections.push_back(sect); - if ((sect.flags & S_NON_LAZY_SYMBOL_POINTERS) == S_NON_LAZY_SYMBOL_POINTERS) - header.symbolPointerSections.push_back(sect); - if ((sect.flags & S_LAZY_SYMBOL_POINTERS) == S_LAZY_SYMBOL_POINTERS) - header.symbolPointerSections.push_back(sect); - } - header.segments.push_back(segment64); - break; - case LC_ROUTINES: // map the 32bit version to 64bits - header.routines64.cmd = LC_ROUTINES_64; - header.routines64.init_address = reader.Read32(); - header.routines64.init_module = reader.Read32(); - header.routines64.reserved1 = reader.Read32(); - header.routines64.reserved2 = reader.Read32(); - header.routines64.reserved3 = reader.Read32(); - header.routines64.reserved4 = reader.Read32(); - header.routines64.reserved5 = reader.Read32(); - header.routines64.reserved6 = reader.Read32(); - header.routinesPresent = true; - break; - case LC_ROUTINES_64: - header.routines64.cmd = LC_ROUTINES_64; - header.routines64.init_address = reader.Read64(); - header.routines64.init_module = reader.Read64(); - header.routines64.reserved1 = reader.Read64(); - header.routines64.reserved2 = reader.Read64(); - header.routines64.reserved3 = reader.Read64(); - header.routines64.reserved4 = reader.Read64(); - header.routines64.reserved5 = reader.Read64(); - header.routines64.reserved6 = reader.Read64(); - header.routinesPresent = true; - break; - case LC_FUNCTION_STARTS: - header.functionStarts.funcoff = reader.Read32(); - header.functionStarts.funcsize = reader.Read32(); - header.functionStartsPresent = true; - break; - case LC_SYMTAB: - header.symtab.symoff = reader.Read32(); - header.symtab.nsyms = reader.Read32(); - header.symtab.stroff = reader.Read32(); - header.symtab.strsize = reader.Read32(); - break; - case LC_DYSYMTAB: - header.dysymtab.ilocalsym = reader.Read32(); - header.dysymtab.nlocalsym = reader.Read32(); - header.dysymtab.iextdefsym = reader.Read32(); - header.dysymtab.nextdefsym = reader.Read32(); - header.dysymtab.iundefsym = reader.Read32(); - header.dysymtab.nundefsym = reader.Read32(); - header.dysymtab.tocoff = reader.Read32(); - header.dysymtab.ntoc = reader.Read32(); - header.dysymtab.modtaboff = reader.Read32(); - header.dysymtab.nmodtab = reader.Read32(); - header.dysymtab.extrefsymoff = reader.Read32(); - header.dysymtab.nextrefsyms = reader.Read32(); - header.dysymtab.indirectsymoff = reader.Read32(); - header.dysymtab.nindirectsyms = reader.Read32(); - header.dysymtab.extreloff = reader.Read32(); - header.dysymtab.nextrel = reader.Read32(); - header.dysymtab.locreloff = reader.Read32(); - header.dysymtab.nlocrel = reader.Read32(); - header.dysymPresent = true; - break; - case LC_DYLD_CHAINED_FIXUPS: - header.chainedFixups.dataoff = reader.Read32(); - header.chainedFixups.datasize = reader.Read32(); - header.chainedFixupsPresent = true; - break; - case LC_DYLD_INFO: - case LC_DYLD_INFO_ONLY: - header.dyldInfo.rebase_off = reader.Read32(); - header.dyldInfo.rebase_size = reader.Read32(); - header.dyldInfo.bind_off = reader.Read32(); - header.dyldInfo.bind_size = reader.Read32(); - header.dyldInfo.weak_bind_off = reader.Read32(); - header.dyldInfo.weak_bind_size = reader.Read32(); - header.dyldInfo.lazy_bind_off = reader.Read32(); - header.dyldInfo.lazy_bind_size = reader.Read32(); - header.dyldInfo.export_off = reader.Read32(); - header.dyldInfo.export_size = reader.Read32(); - header.exportTrie.dataoff = header.dyldInfo.export_off; - header.exportTrie.datasize = header.dyldInfo.export_size; - header.exportTriePresent = true; - header.dyldInfoPresent = true; - break; - case LC_DYLD_EXPORTS_TRIE: - header.exportTrie.dataoff = reader.Read32(); - header.exportTrie.datasize = reader.Read32(); - header.exportTriePresent = true; - break; - case LC_THREAD: - case LC_UNIXTHREAD: - /*while (reader.GetOffset() < nextOffset) - { - - thread_command thread; - thread.flavor = reader.Read32(); - thread.count = reader.Read32(); - switch (m_archId) - { - case MachOx64: - m_logger->LogDebug("x86_64 Thread state\n"); - if (thread.flavor != X86_THREAD_STATE64) - { - reader.SeekRelative(thread.count * sizeof(uint32_t)); - break; - } - //This wont be big endian so we can just read the whole thing - reader.Read(&thread.statex64, sizeof(thread.statex64)); - header.entryPoints.push_back({thread.statex64.rip, false}); - break; - case MachOx86: - m_logger->LogDebug("x86 Thread state\n"); - if (thread.flavor != X86_THREAD_STATE32) - { - reader.SeekRelative(thread.count * sizeof(uint32_t)); - break; - } - //This wont be big endian so we can just read the whole thing - reader.Read(&thread.statex86, sizeof(thread.statex86)); - header.entryPoints.push_back({thread.statex86.eip, false}); - break; - case MachOArm: - m_logger->LogDebug("Arm Thread state\n"); - if (thread.flavor != _ARM_THREAD_STATE) - { - reader.SeekRelative(thread.count * sizeof(uint32_t)); - break; - } - //This wont be big endian so we can just read the whole thing - reader.Read(&thread.statearmv7, sizeof(thread.statearmv7)); - header.entryPoints.push_back({thread.statearmv7.r15, false}); - break; - case MachOAarch64: - case MachOAarch6432: - m_logger->LogDebug("Aarch64 Thread state\n"); - if (thread.flavor != _ARM_THREAD_STATE64) - { - reader.SeekRelative(thread.count * sizeof(uint32_t)); - break; - } - reader.Read(&thread.stateaarch64, sizeof(thread.stateaarch64)); - header.entryPoints.push_back({thread.stateaarch64.pc, false}); - break; - case MachOPPC: - m_logger->LogDebug("PPC Thread state\n"); - if (thread.flavor != PPC_THREAD_STATE) - { - reader.SeekRelative(thread.count * sizeof(uint32_t)); - break; - } - //Read individual entries for endian reasons - header.entryPoints.push_back({reader.Read32(), false}); - (void)reader.Read32(); - (void)reader.Read32(); - //Read the rest of the structure - (void)reader.Read(&thread.stateppc.r1, sizeof(thread.stateppc) - (3 * 4)); - break; - case MachOPPC64: - m_logger->LogDebug("PPC64 Thread state\n"); - if (thread.flavor != PPC_THREAD_STATE64) - { - reader.SeekRelative(thread.count * sizeof(uint32_t)); - break; - } - header.entryPoints.push_back({reader.Read64(), false}); - (void)reader.Read64(); - (void)reader.Read64(); // Stack start - (void)reader.Read(&thread.stateppc64.r1, sizeof(thread.stateppc64) - (3 * 8)); - break; - default: - m_logger->LogError("Unknown archid: %x", m_archId); - } - - }*/ - break; - case LC_LOAD_DYLIB: - { - uint32_t offset = reader.Read32(); - if (offset < nextOffset) - { - reader.Seek(curOffset + offset); - std::string libname = reader.ReadCString(reader.GetOffset()); - header.dylibs.push_back(libname); - } - } - break; - case LC_BUILD_VERSION: - { - // m_logger->LogDebug("LC_BUILD_VERSION:"); - header.buildVersion.platform = reader.Read32(); - header.buildVersion.minos = reader.Read32(); - header.buildVersion.sdk = reader.Read32(); - header.buildVersion.ntools = reader.Read32(); - // m_logger->LogDebug("Platform: %s", BuildPlatformToString(header.buildVersion.platform).c_str()); - // m_logger->LogDebug("MinOS: %s", BuildToolVersionToString(header.buildVersion.minos).c_str()); - // m_logger->LogDebug("SDK: %s", BuildToolVersionToString(header.buildVersion.sdk).c_str()); - for (uint32_t j = 0; (i < header.buildVersion.ntools) && (j < 10); j++) - { - uint32_t tool = reader.Read32(); - uint32_t version = reader.Read32(); - header.buildToolVersions.push_back({tool, version}); - // m_logger->LogDebug("Build Tool: %s: %s", BuildToolToString(tool).c_str(), - // BuildToolVersionToString(version).c_str()); - } - break; - } - case LC_FILESET_ENTRY: - { - throw ReadException(); - } - default: - // m_logger->LogDebug("Unhandled command: %s : %" PRIu32 "\n", CommandToString(load.cmd).c_str(), - // load.cmdsize); - break; - } - if (reader.GetOffset() != nextOffset) - { - // m_logger->LogDebug("Didn't parse load command: %s fully %" PRIx64 ":%" PRIxPTR, - // CommandToString(load.cmd).c_str(), reader.GetOffset(), nextOffset); - } - reader.Seek(nextOffset); - } - - for (auto& section : header.sections) - { - char sectionName[17]; - memcpy(sectionName, section.sectname, sizeof(section.sectname)); - sectionName[16] = 0; - char segmentName[17]; - memcpy(segmentName, section.segname, sizeof(section.segname)); - segmentName[16] = 0; - if (header.identifierPrefix.empty()) - header.sectionNames.push_back(sectionName); - else - header.sectionNames.push_back(header.identifierPrefix + "::" + segmentName + ":" + sectionName); - } - } - catch (ReadException&) - { - return {}; - } - - return header; + std::unique_lock<std::shared_mutex> lock(*m_namedSymMutex); + // Populate the named symbols from the regular symbols map. + m_namedSymbols.reserve(m_symbols.size()); + for (const auto& [address, symbol] : m_symbols) + m_namedSymbols.emplace(symbol.name, address); } -bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const KernelCacheMachOHeader& header, const KernelCacheImage& targetImage) +void KernelCache::ProcessRelocations(Ref<BinaryView> view, linkedit_data_command chained_fixup_command) { - // FIXME this uselessly loads chained fixups if an image is already loaded. - auto logger = LogRegistry::GetLogger("KernelCache", view->GetFile()->GetSessionId()); - - bool hasRegionsToLoad = false; - - auto reader = BinaryReader(view->GetParentView()); - - reader.Seek(0x10); - uint32_t ncmds = reader.Read32(); - uint64_t off = 0x20; // FIXME 32 bit - - uint64_t kernelBaseAddress = 0; - - uint32_t chainedFixupDataOff = 0; - uint32_t chainedFixupDataSize = 0; - - while (ncmds--) - { - reader.Seek(off); - uint32_t cmd = reader.Read32(); - uint32_t cmdsize = reader.Read32(); - if (cmd == LC_SEGMENT_64) - { - segment_command_64 segment {}; - reader.Read(&segment.segname, 16); - if (strncmp(segment.segname, "__TEXT\0", 7) == 0) - { - kernelBaseAddress = reader.Read64(); - } - } - if (cmd == LC_DYLD_CHAINED_FIXUPS) - { - chainedFixupDataOff = reader.Read32(); - chainedFixupDataSize = reader.Read32(); - } - off += cmdsize; - } - - BNRelocationInfo reloc; - memset(&reloc, 0, sizeof(BNRelocationInfo)); - reloc.type = StandardRelocationType; - reloc.size = 8; - reloc.nativeType = BINARYNINJA_MANUAL_RELOCATION; - logger->LogDebug("Processing Chained Fixups"); - - std::vector<std::pair<uint64_t, uint64_t>> relocations; - if (chainedFixupDataOff && chainedFixupDataSize) + m_relocations.clear(); + if (chained_fixup_command.dataoff && chained_fixup_command.datasize) { BinaryReader parentReader(view->GetParentView()); try { dyld_chained_fixups_header fixupsHeader {}; - uint64_t fixupHeaderAddress = chainedFixupDataOff; + uint64_t fixupHeaderAddress = chained_fixup_command.dataoff; parentReader.Seek(fixupHeaderAddress); fixupsHeader.fixups_version = parentReader.Read32(); fixupsHeader.starts_offset = parentReader.Read32(); @@ -1105,11 +131,11 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel fixupsHeader.imports_format = parentReader.Read32(); fixupsHeader.symbols_format = parentReader.Read32(); - logger->LogDebug("Chained Fixups: Header @ %llx // Fixups version %lx", fixupHeaderAddress, fixupsHeader.fixups_version); + LogDebug("Chained Fixups: Header @ %llx // Fixups version %lx", fixupHeaderAddress, fixupsHeader.fixups_version); if (fixupsHeader.fixups_version > 0) { - logger->LogError("Chained Fixup parsing failed. Unknown Fixups Version"); + LogError("Chained Fixup parsing failed. Unknown Fixups Version"); throw ReadException(); } @@ -1178,14 +204,14 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel break; default: { - logger->LogError("Chained Fixups: Unknown or unsupported pointer format %d, " + LogError("Chained Fixups: Unknown or unsupported pointer format %d, " "unable to process chains for segment at @llx", starts.pointer_format, starts.segment_offset); continue; } } uint16_t fmt = starts.pointer_format; - logger->LogDebug("Chained Fixups: Segment start @ %llx, fmt %d", starts.segment_offset, fmt); + LogDebug("Chained Fixups: Segment start @ %llx, fmt %d", starts.segment_offset, fmt); uint64_t pageStartsTableStartAddress = parentReader.GetOffset(); std::vector<std::vector<uint16_t>> pageStartOffsets {}; @@ -1273,8 +299,8 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel bind = false; } - logger->LogTrace("Chained Fixups: @ 0x%llx ( 0x%llx ) - %d 0x%llx", chainEntryAddress, - kernelBaseAddress + (chainEntryAddress), + LogTrace("Chained Fixups: @ 0x%llx ( 0x%llx ) - %d 0x%llx", chainEntryAddress, + view->GetStart() + (chainEntryAddress), bind, nextEntryStrideCount); if (!bind) @@ -1293,7 +319,7 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel entryOffset = pointer.arm64e.rebase.target; if ( starts.pointer_format != DYLD_CHAINED_PTR_ARM64E || pointer.arm64e.bind.auth) - entryOffset += kernelBaseAddress; + entryOffset += view->GetStart(); break; } @@ -1301,12 +327,12 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel entryOffset = pointer.generic64.rebase.target; break; case DYLD_CHAINED_PTR_64_OFFSET: - entryOffset = pointer.generic64.rebase.target + kernelBaseAddress; + entryOffset = pointer.generic64.rebase.target + view->GetStart(); break; // We expect only cases past this point will be applicable in this context. case DYLD_CHAINED_PTR_64_KERNEL_CACHE: case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: - entryOffset = pointer.kernel64.target + kernelBaseAddress; + entryOffset = pointer.kernel64.target + view->GetStart(); break; case DYLD_CHAINED_PTR_32: case DYLD_CHAINED_PTR_32_CACHE: @@ -1319,7 +345,7 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel // logger->LogInfo("Chained Fixups: Pointer at 0x%llx -> 0x%llx", view->GetStart() + chainEntryAddress, entryOffset); - relocations.emplace_back(kernelBaseAddress + chainEntryAddress, entryOffset); + m_relocations.emplace_back(view->GetStart() + chainEntryAddress, entryOffset); } chainEntryAddress += (nextEntryStrideCount * strideSize); @@ -1328,8 +354,8 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel { // Something is seriously wrong here. likely malformed binary, or our parsing failed elsewhere. // This will log the pointer in mapped memory. - logger->LogError("Chained Fixups: Pointer at 0x%llx left page", - kernelBaseAddress + ((chainEntryAddress - (nextEntryStrideCount * strideSize)))); + LogError("Chained Fixups: Pointer at 0x%llx left page", + view->GetStart() + ((chainEntryAddress - (nextEntryStrideCount * strideSize)))); fixupsDone = true; } @@ -1342,1379 +368,59 @@ bool KernelCache::InitializeSegmentsForHeader(Ref<BinaryView> view, const Kernel } catch (ReadException&) { - logger->LogError("Chained Fixup parsing failed"); + LogError("Chained Fixup parsing failed"); } } - // Critical that we sort these as this optimization allows us to save a lot of time on image loading when we - // need to apply relocations later. Now is the best time, since we aren't going to find more relocs now. - std::sort(relocations.begin(), relocations.end(), + + std::sort(m_relocations.begin(), m_relocations.end(), [](const std::pair<uint64_t, uint64_t>& a, const std::pair<uint64_t, uint64_t>& b) { return a.first < b.first; }); - - for (auto& region : targetImage.regions) - { - if (view->IsValidOffset(region.start)) - { - logger->LogDebug("Skipping region %s as it is already loaded.", region.prettyName.c_str()); - continue; - } - - hasRegionsToLoad = true; - - // Considerations at this point in processing: - // * View is already finalized - // * view (shown to user) has only one segment, at the kernel start address - // * All of our relevant data is contained in the Raw view. - view->AddAutoSegment(region.start, region.size, region.fileOffset, region.size, region.flags); - - auto begin = std::lower_bound(relocations.begin(), relocations.end(), region.start, - [](const std::pair<uint64_t, uint64_t>& reloc, uint64_t addr) { - return reloc.first < addr; - }); - - auto arch = view->GetDefaultArchitecture(); - // Process relocations until the VM address is beyond our region - for (auto it = begin; it != relocations.end() && it->first < region.start + region.size; ++it) { - reloc.address = it->first; - view->DefineRelocation(arch, reloc, it->second, reloc.address); - } - } - - if (!hasRegionsToLoad) - { - logger->LogWarn("No regions to load for image %s", header.installName.c_str()); - return false; - } - - return true; } -void KernelCache::InitializeHeader(Ref<BinaryView> view, KernelCacheMachOHeader header) +std::optional<CacheImage> KernelCache::GetImageAt(const uint64_t address) const { - Ref<Settings> settings = view->GetLoadSettings(KC_VIEW_NAME); - bool applyFunctionStarts = true; // FIXME - - view->AddAutoSection(header.identifierPrefix + "::__macho_header", header.textBase, header.ident.sizeofcmds + sizeof(mach_header_64), ReadOnlyDataSectionSemantics); - - for (size_t i = 0; i < header.sections.size(); i++) - { - if (!header.sections[i].size) - continue; - - std::string type; - BNSectionSemantics semantics = DefaultSectionSemantics; - switch (header.sections[i].flags & 0xff) - { - case S_REGULAR: - if (header.sections[i].flags & S_ATTR_PURE_INSTRUCTIONS) - { - type = "PURE_CODE"; - semantics = ReadOnlyCodeSectionSemantics; - } - else if (header.sections[i].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 (i >= header.sectionNames.size()) - break; - if (strncmp(header.sections[i].sectname, "__text", sizeof(header.sections[i].sectname)) == 0) - semantics = ReadOnlyCodeSectionSemantics; - if (strncmp(header.sections[i].sectname, "__const", sizeof(header.sections[i].sectname)) == 0) - semantics = ReadOnlyDataSectionSemantics; - if (strncmp(header.sections[i].sectname, "__data", sizeof(header.sections[i].sectname)) == 0) - semantics = ReadWriteDataSectionSemantics; - if (strncmp(header.sections[i].segname, "__DATA_CONST", sizeof(header.sections[i].segname)) == 0) - semantics = ReadOnlyDataSectionSemantics; - - view->AddAutoSection(header.sectionNames[i], header.sections[i].addr, header.sections[i].size, semantics, - type, header.sections[i].align); - } - - auto typeLib = view->GetTypeLibrary(header.installName); - - BinaryReader virtualReader(view->GetParentView()); - - view->DefineDataVariable(header.textBase, Type::NamedType(view, QualifiedName("mach_header_64"))); - view->DefineAutoSymbol( - new Symbol(DataSymbol, "__macho_header::" + header.identifierPrefix, header.textBase, LocalBinding)); - - try - { - virtualReader.Seek(header.textBaseFileOffset + sizeof(mach_header_64)); - size_t sectionNum = 0; - uint64_t curVirtualOffset = header.textBase + sizeof(mach_header_64); - uint64_t curFileOffset = header.textBaseFileOffset + sizeof(mach_header_64); - for (size_t i = 0; i < header.ident.ncmds; i++) - { - load_command load; - curFileOffset = virtualReader.GetOffset(); - load.cmd = virtualReader.Read32(); - load.cmdsize = virtualReader.Read32(); - uint64_t nextOffset = curFileOffset + load.cmdsize; - switch (load.cmd) - { - case LC_SEGMENT: - { - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("segment_command"))); - virtualReader.SeekRelative(5 * 8); - size_t numSections = virtualReader.Read32(); - virtualReader.SeekRelative(4); - for (size_t j = 0; j < numSections; j++) - { - view->DefineDataVariable( - virtualReader.GetOffset(), Type::NamedType(view, QualifiedName("section"))); - view->DefineAutoSymbol(new Symbol(DataSymbol, - "__macho_section::" + header.identifierPrefix + "_[" + std::to_string(sectionNum++) + "]", - virtualReader.GetOffset(), LocalBinding)); - virtualReader.SeekRelative((8 * 8) + 4); - } - break; - } - case LC_SEGMENT_64: - { - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("segment_command_64"))); - virtualReader.SeekRelative(7 * 8); - size_t numSections = virtualReader.Read32(); - virtualReader.SeekRelative(4); - for (size_t j = 0; j < numSections; j++) - { - view->DefineDataVariable( - virtualReader.GetOffset(), Type::NamedType(view, QualifiedName("section_64"))); - view->DefineAutoSymbol(new Symbol(DataSymbol, - "__macho_section_64::" + header.identifierPrefix + "_[" + std::to_string(sectionNum++) + "]", - virtualReader.GetOffset(), LocalBinding)); - virtualReader.SeekRelative(10 * 8); - } - break; - } - case LC_SYMTAB: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("symtab"))); - break; - case LC_DYSYMTAB: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("dysymtab"))); - break; - case LC_UUID: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(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: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("dylib_command"))); - if (load.cmdsize - 24 <= 150) - view->DefineDataVariable( - curVirtualOffset + 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: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("linkedit_data"))); - break; - case LC_ENCRYPTION_INFO: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("encryption_info"))); - break; - case LC_VERSION_MIN_MACOSX: - case LC_VERSION_MIN_IPHONEOS: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("version_min"))); - break; - case LC_DYLD_INFO: - case LC_DYLD_INFO_ONLY: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("dyld_info"))); - break; - default: - view->DefineDataVariable(curVirtualOffset, Type::NamedType(view, QualifiedName("load_command"))); - break; - } - - view->DefineAutoSymbol(new Symbol(DataSymbol, - "__macho_load_command::" + header.identifierPrefix + "_[" + std::to_string(i) + "]", curVirtualOffset, - LocalBinding)); - curVirtualOffset += (nextOffset - curFileOffset); - virtualReader.Seek(nextOffset); - } - } - catch (ReadException&) - { - LogError("Error when applying Mach-O header types at %" PRIx64, header.textBase); - } - - if (applyFunctionStarts && header.functionStartsPresent && header.linkeditPresent) - { - size_t i = 0; - uint64_t curfunc = header.textBase; - uint64_t curOffset; - - auto funcStarts = view->GetParentView()->ReadBuffer(header.functionStarts.funcoff, header.functionStarts.funcsize); - - while (i < header.functionStarts.funcsize) - { - curOffset = readLEB128(funcStarts, header.functionStarts.funcsize, i); - curfunc += curOffset; - // LogError("0x%llx, 0x%llx", header.textBase, curOffset); - if (curOffset == 0) - continue; - uint64_t target = curfunc; - Ref<Platform> targetPlatform = view->GetDefaultPlatform(); - view->AddFunctionForAnalysis(targetPlatform, target); - } - } - - view->BeginBulkModifySymbols(); - ParseSymbolTable(view, header); - - BinaryReader reader(view); - - size_t modInitFuncCnt = 0; - for (const auto& moduleInitSection : header.moduleInitSections) - { - // The mod_init section contains a list of function pointers called at initialization - // if we don't have a defined entrypoint then use the first one in the list as the entrypoint - size_t i = 0; - reader.Seek(moduleInitSection.addr); - for (; i < (moduleInitSection.size / view->GetAddressSize()); i++) - { - uint64_t target = (view->GetAddressSize() == 4) ? reader.Read32() : reader.Read64(); - if (!view->IsValidOffset(target)) - continue; - Ref<Platform> targetPlatform = view->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); - auto name = header.identifierPrefix + "_init_func_" + std::to_string(modInitFuncCnt++); - view->AddEntryPointForAnalysis(targetPlatform, target); - auto symbol = new Symbol(FunctionSymbol, name, target, GlobalBinding); - view->DefineAutoSymbol(symbol); - } - } - - view->EndBulkModifySymbols(); -} - - -std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> KernelCache::ParseSymbolTable(Ref<BinaryView> view, KernelCacheMachOHeader header, bool defineSymbolsInView) -{ - if (header.symtab.symoff != 0 && header.linkeditPresent) - { - // Mach-O View symtab processing with - // a ton of stuff cut out so it can work - // auto symtab = reader->ReadBuffer(header.symtab.symoff, header.symtab.nsyms * sizeof(nlist_64)); - auto strtab = view->GetParentView()->ReadBuffer(header.symtab.stroff, header.symtab.strsize); - nlist_64 sym; - memset(&sym, 0, sizeof(sym)); - std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> symbolInfos; - for (size_t i = 0; i < header.symtab.nsyms; i++) - { - view->GetParentView()->Read(&sym, header.symtab.symoff + i * sizeof(nlist_64), sizeof(nlist_64)); - if (sym.n_strx >= header.symtab.strsize || ((sym.n_type & N_TYPE) == N_INDR)) - continue; - - std::string symbol((char*)strtab.GetDataAt(sym.n_strx)); - // BNLogError("%s: 0x%llx", symbol.c_str(), sym.n_value); - if (symbol == "<redacted>") - continue; - - BNSymbolType type = DataSymbol; - uint32_t flags; - if ((sym.n_type & N_TYPE) == N_SECT && sym.n_sect > 0 && (size_t)(sym.n_sect - 1) < header.sections.size()) - {} - else if ((sym.n_type & N_TYPE) == N_ABS) - {} - else if ((sym.n_type & 0x1)) - { - type = ExternalSymbol; - } - else - continue; - - for (auto s : header.sections) - { - if (s.addr < sym.n_value) - { - if (s.addr + s.size > sym.n_value) - { - flags = s.flags; - } - } - } - - if (type != ExternalSymbol) - { - if ((flags & S_ATTR_PURE_INSTRUCTIONS) == S_ATTR_PURE_INSTRUCTIONS - || (flags & S_ATTR_SOME_INSTRUCTIONS) == S_ATTR_SOME_INSTRUCTIONS) - type = FunctionSymbol; - else - type = DataSymbol; - } - if ((sym.n_desc & N_ARM_THUMB_DEF) == N_ARM_THUMB_DEF) - sym.n_value++; - - std::string rawName = symbol; - std::string shortName = symbol; - std::string fullName = symbol; - Ref<Type> typeRef = nullptr; - - if (view->GetDefaultArchitecture()) - { - QualifiedName demangledName; - Ref<Type> demangledType; - bool simplify = Settings::Instance()->Get<bool>("analysis.types.templateSimplifier", view); - if (DemangleGeneric(view->GetDefaultArchitecture(), rawName, demangledType, demangledName, view, simplify)) - { - shortName = demangledName.GetString(); - fullName = shortName; - if (demangledType) - fullName += demangledType->GetStringAfterName(); - if (!typeRef && !view->GetDefaultPlatform()->GetFunctionByName(rawName)) - typeRef = demangledType; - } - else - { - LogDebug("Failed to demangle name: '%s'\n", rawName.c_str()); - } - } - - if (defineSymbolsInView) - { - auto symbolObj = new Symbol(type, shortName, fullName, rawName, sym.n_value, GlobalBinding); - - if (typeRef) - view->DefineAutoSymbolAndVariableOrFunction(view->GetDefaultPlatform(), symbolObj, typeRef); - else - { - view->DefineAutoSymbol(symbolObj); - if (type == FunctionSymbol) - { - Ref<Platform> targetPlatform = view->GetDefaultPlatform(); - view->AddFunctionForAnalysis(targetPlatform, sym.n_value); - } - } - } - symbolInfos.push_back({sym.n_value, {type, fullName}}); - } - return symbolInfos; - } - return {}; -} - - -struct ExportNode -{ - std::string text; - uint64_t offset; - uint64_t flags; -}; - - -void KernelCache::ReadExportNode(Ref<BinaryView> view, std::vector<Ref<Symbol>>& symbolList, KernelCacheMachOHeader& header, DataBuffer& buffer, uint64_t textBase, - const std::string& currentText, size_t cursor, uint32_t endGuard) -{ - - if (cursor > endGuard) - throw ReadException(); - - uint64_t terminalSize = readValidULEB128(buffer, cursor); - uint64_t childOffset = cursor + terminalSize; - if (terminalSize != 0) { - uint64_t imageOffset = 0; - uint64_t flags = readValidULEB128(buffer, cursor); - if (!(flags & EXPORT_SYMBOL_FLAGS_REEXPORT)) - { - imageOffset = readValidULEB128(buffer, cursor); - // auto symbolType = view->GetAnalysisFunctionsForAddress(textBase + imageOffset).size() ? FunctionSymbol : DataSymbol; - { - if (!currentText.empty() && textBase + imageOffset) - { - uint32_t sectionFlags; - BNSymbolType type; - for (auto s : header.sections) - { - if (s.addr < textBase + imageOffset) - { - if (s.addr + s.size > textBase + imageOffset) - { - sectionFlags = s.flags; - } - } - } - if ((sectionFlags & S_ATTR_PURE_INSTRUCTIONS) == S_ATTR_PURE_INSTRUCTIONS - || (sectionFlags & S_ATTR_SOME_INSTRUCTIONS) == S_ATTR_SOME_INSTRUCTIONS) - type = FunctionSymbol; - else - type = DataSymbol; - -#if EXPORT_TRIE_DEBUG - // BNLogInfo("export: %s -> 0x%llx", n.text.c_str(), image.baseAddress + n.offset); -#endif - auto sym = new Symbol(type, currentText, textBase + imageOffset); - symbolList.push_back(sym); - } - } - } - } - cursor = childOffset; - uint8_t childCount = buffer[cursor]; - cursor++; - if (cursor > endGuard) - throw ReadException(); - for (uint8_t i = 0; i < childCount; ++i) - { - std::string childText; - while (buffer[cursor] != 0 & cursor <= endGuard) - childText.push_back(buffer[cursor++]); - cursor++; - if (cursor > endGuard) - throw ReadException(); - auto next = readValidULEB128(buffer, cursor); - if (next == 0) - throw ReadException(); - ReadExportNode(view, symbolList, header, buffer, textBase, currentText + childText, next, endGuard); - } -} - - -std::vector<Ref<Symbol>> KernelCache::ParseExportTrie(Ref<BinaryView> view, KernelCacheMachOHeader header) -{ - if (!header.exportTriePresent || !header.exportTrie.dataoff || !header.exportTrie.datasize) - return {}; - - std::vector<Ref<Symbol>> symbols; - try - { - std::vector<ExportNode> nodes; - - DataBuffer buffer = view->GetParentView()->ReadBuffer(header.exportTrie.dataoff, header.exportTrie.datasize); - ReadExportNode(view, symbols, header, buffer, header.textBase, "", 0, header.exportTrie.datasize); - } - catch (std::exception& e) - { - BNLogError("Failed to load Export Trie"); - } - return symbols; -} - -std::vector<std::string> KernelCache::GetAvailableImages() -{ - std::vector<std::string> installNames; - for (const auto& img : m_cacheInfo->images) - { - installNames.push_back(img.installName); - } - return installNames; -} - - -std::vector<KernelCacheImage> KernelCache::GetLoadedImages() -{ - std::lock_guard lock(m_viewSpecificState->stateMutex); - std::vector<KernelCacheImage> images; - for (const auto& [fileStart, image] : m_viewSpecificState->state.loadedImages) - { - images.push_back(image); - } - return images; -} - - -bool KernelCache::IsImageLoaded(uint64_t address) -{ - std::lock_guard lock(m_viewSpecificState->stateMutex); - return m_viewSpecificState->state.loadedImages.find(address)!= m_viewSpecificState->state.loadedImages.end(); -} - - -std::vector<std::pair<uint64_t, std::pair<std::string, std::string>>> KernelCache::LoadAllSymbolsAndWait() -{ - std::vector<std::pair<uint64_t, std::pair<std::string, std::string>>> symbols; - for (const auto& img : m_cacheInfo->images) - { - auto header = HeaderForFileAddress(img.headerFileLocation); - if (header) - { - auto newSymbolList = ParseSymbolTable(m_kcView, *header, false); - for (const auto& symbol : newSymbolList) - { - if (symbol.first != 0) - symbols.push_back({symbol.first, {header->installName, symbol.second.second}}); - } - } - } - return symbols; -} - - -std::string KernelCache::SerializedImageHeaderForVMAddress(uint64_t address) -{ - auto header = HeaderForVMAddress(address); - if (header) - { - return header->AsString(); - } - return ""; -} - - -std::string KernelCache::SerializedImageHeaderForName(std::string name) -{ - if (auto it = m_cacheInfo->imageStarts.find(name); it != m_cacheInfo->imageStarts.end()) - { - if (auto header = HeaderForFileAddress(it->second)) - { - return header->AsString(); - } - } - return ""; -} - - - -bool KernelCache::SaveCacheInfoToKCView(std::lock_guard<std::mutex>&) -{ - if (!m_cacheInfo) - return false; - - // The initial load should only populate `m_cacheInfo` and should not modify any state. - assert(m_modifiedState->loadedImages.size() == 0); - - auto data = m_cacheInfo->AsMetadata(); - m_kcView->StoreMetadata(KernelCacheMetadata::Tag, data); - m_kcView->GetParentView()->StoreMetadata(KernelCacheMetadata::Tag, data); - - { - std::lock_guard lock(m_viewSpecificState->cacheInfoMutex); - if (m_cacheInfo && !m_viewSpecificState->cacheInfo) - m_viewSpecificState->cacheInfo = m_cacheInfo; - else if (m_cacheInfo != m_viewSpecificState->cacheInfo) - abort(); - } - - m_metadataValid = true; - return true; -} - - -bool KernelCache::SaveModifiedStateToKCView(std::lock_guard<std::mutex>&) -{ - if (!m_kcView) - return false; - - { - std::lock_guard lock(m_viewSpecificState->stateMutex); - - uint64_t modificationNumber = m_viewSpecificState->savedModifications++; - if (modificationNumber == 0) - { - // The cached state in the view-specific state has not yet been saved. - // For the initial load of a shared cache this will be empty, but if - // the shared cache has been loaded from a database then this will - // contain the full state that was saved. - std::string metadataKey = KernelCacheMetadata::ModifiedStateTagPrefix + std::to_string(modificationNumber); - auto data = m_viewSpecificState->state.AsMetadata(m_viewSpecificState->viewState); - - m_kcView->StoreMetadata(metadataKey, data); - m_kcView->GetParentView()->StoreMetadata(metadataKey, data); - modificationNumber = m_viewSpecificState->savedModifications++; - } - - std::string metadataKey = KernelCacheMetadata::ModifiedStateTagPrefix + std::to_string(modificationNumber); - auto data = m_modifiedState->AsMetadata(); - - m_kcView->StoreMetadata(metadataKey, data); - m_kcView->GetParentView()->StoreMetadata(metadataKey, data); - - Ref<Metadata> count = new Metadata(m_viewSpecificState->savedModifications); - m_kcView->StoreMetadata(KernelCacheMetadata::ModifiedStateCountTag, count); - m_kcView->GetParentView()->StoreMetadata(KernelCacheMetadata::ModifiedStateCountTag, count); - - m_viewSpecificState->state.loadedImages.merge(m_modifiedState->loadedImages); - // `merge` will move a node to the target map if the corresponding key does not yet exist. - // If we've redundantly loaded images, we may be left with symbols in the source maps. - m_modifiedState->loadedImages.clear(); - - // Clean up any metadata entries past the current modification number. - // These can happen after being loaded from a database as all modifications are - // merged into a single state object and the modification count is reset to zero. - for (size_t i = modificationNumber + 1; i < std::numeric_limits<size_t>::max(); ++i) - { - std::string modifiedStateMetadataKey = KernelCacheMetadata::ModifiedStateTagPrefix + std::to_string(i); - bool done = true; - if (m_kcView->QueryMetadata(modifiedStateMetadataKey)) - { - done = false; - m_kcView->RemoveMetadata(modifiedStateMetadataKey); - } - if (m_kcView->GetParentView()->QueryMetadata(modifiedStateMetadataKey)) - { - done = false; - m_kcView->GetParentView()->RemoveMetadata(modifiedStateMetadataKey); - } - if (done) - break; - } - } - - if (m_modifiedState->viewState) - { - m_viewSpecificState->viewState = m_modifiedState->viewState.value(); - m_modifiedState->viewState = std::nullopt; - } - - m_metadataValid = true; - - return true; -} - - -const std::string KernelCacheMetadata::Tag = "KERNELCACHE-KernelCacheData"; -const std::string KernelCacheMetadata::CacheInfoTag = "KERNELCACHE-CacheInfo"; -const std::string KernelCacheMetadata::ModifiedStateTagPrefix = "KERNELCACHE-ModifiedState-"; -const std::string KernelCacheMetadata::ModifiedStateCountTag = "KERNELCACHE-ModifiedState-Count"; - -KernelCacheMetadata::~KernelCacheMetadata() = default; -KernelCacheMetadata::KernelCacheMetadata(KernelCacheMetadata&&) = default; -KernelCacheMetadata& KernelCacheMetadata::operator=(KernelCacheMetadata&&) = default; - -KernelCacheMetadata::KernelCacheMetadata(KernelCache::CacheInfo cacheInfo, KernelCache::ModifiedState state) : - cacheInfo(std::make_unique<KernelCache::CacheInfo>(std::move(cacheInfo))), - state(std::make_unique<KernelCache::ModifiedState>(std::move(state))) -{} - - -// static -bool KernelCacheMetadata::ViewHasMetadata(BinaryView* view) -{ - return view->QueryMetadata(Tag); -} - -// static -std::optional<KernelCacheMetadata> KernelCacheMetadata::LoadFromView(BinaryView* view) -{ - Ref<Metadata> viewMetadata = view->QueryMetadata(Tag); - if (!viewMetadata) + const auto it = m_images.find(address); + if (it == m_images.end()) return std::nullopt; - - auto cacheInfo = KernelCache::CacheInfo::LoadFromString(viewMetadata->GetString()); - if (!cacheInfo) - return std::nullopt; - - auto modifiedState = KernelCache::ModifiedState::LoadAll(view, *cacheInfo); - return KernelCacheMetadata(std::move(*cacheInfo), std::move(modifiedState)); + return it->second; } -std::string KernelCacheMetadata::InstallNameForImageBaseAddress(uint64_t baseAddress) const +std::optional<CacheImage> KernelCache::GetImageContaining(const uint64_t address) const { - auto it = std::find_if(cacheInfo->imageStarts.begin(), cacheInfo->imageStarts.end(), [=](auto& pair) { - return pair.second == baseAddress; - }); - - if (it == cacheInfo->imageStarts.end()) - return ""; - - return it->first; -} - -std::vector<KernelCacheImage> KernelCacheMetadata::LoadedImages() -{ - std::vector<KernelCacheImage> images; - for (const auto& image : state->loadedImages) + for (const auto& [startAddress, image] : m_images) { - images.push_back(image.second); - } - return images; -} - -} - -extern "C" -{ - BNKernelCache* BNGetKernelCache(BNBinaryView* data) - { - if (!data) - return nullptr; - - Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); - if (auto cache = KernelCache::GetFromKCView(view)) + for (const auto& region : image.regions) { - cache->AddAPIRef(); - return cache->GetAPIObject(); + if (region.AsAddressRange().start <= address && address < region.AsAddressRange().end) + return image; } - - return nullptr; - } - - BNKernelCache* BNNewKernelCacheReference(BNKernelCache* cache) - { - if (!cache->object) - return nullptr; - - cache->object->AddAPIRef(); - return cache; - } - - void BNFreeKernelCacheReference(BNKernelCache* cache) - { - if (!cache->object) - return; - - cache->object->ReleaseAPIRef(); - } - - bool BNKCViewLoadImageWithInstallName(BNKernelCache* cache, const char* name) - { - std::string imageName = std::string(name); - if (cache->object) - return cache->object->LoadImageWithInstallName(imageName); - - return false; - } - - bool BNKCViewLoadImageContainingAddress(BNKernelCache* cache, uint64_t address) - { - if (cache->object) - { - return cache->object->LoadImageContainingAddress(address); - } - - return false; - } - - bool BNKCViewIsImageLoaded(BNKernelCache* cache, uint64_t address) - { - if (cache->object) - return cache->object->IsImageLoaded(address); - - return false; - } - - char** BNKCViewGetInstallNames(BNKernelCache* cache, size_t* count) - { - if (cache->object) - { - auto value = cache->object->GetAvailableImages(); - *count = value.size(); - - std::vector<const char*> cstrings; - for (size_t i = 0; i < value.size(); i++) - { - cstrings.push_back(value[i].c_str()); - } - return BNAllocStringList(cstrings.data(), cstrings.size()); - } - *count = 0; - return nullptr; - } - - BNKCSymbolRep* BNKCViewLoadAllSymbolsAndWait(BNKernelCache* cache, size_t* count) - { - if (cache->object) - { - auto value = cache->object->LoadAllSymbolsAndWait(); - *count = value.size(); - - BNKCSymbolRep* symbols = (BNKCSymbolRep*)malloc(sizeof(BNKCSymbolRep) * value.size()); - for (size_t i = 0; i < value.size(); i++) - { - symbols[i].address = value[i].first; - symbols[i].name = BNAllocString(value[i].second.second.c_str()); - symbols[i].image = BNAllocString(value[i].second.first.c_str()); - } - return symbols; - } - *count = 0; - return nullptr; - } - - void BNKCViewFreeSymbols(BNKCSymbolRep* symbols, size_t count) - { - for (size_t i = 0; i < count; i++) - { - BNFreeString(symbols[i].name); - BNFreeString(symbols[i].image); - } - delete symbols; - } - - char* BNKCViewGetNameForAddress(BNKernelCache* cache, uint64_t address) - { - if (cache->object) - { - return BNAllocString(cache->object->NameForAddress(address).c_str()); - } - - return nullptr; - } - - char* BNKCViewGetImageNameForAddress(BNKernelCache* cache, uint64_t address) - { - if (cache->object) - { - return BNAllocString(cache->object->ImageNameForAddress(address).c_str()); - } - - return nullptr; - } - - uint64_t BNKCViewLoadedImageCount(BNKernelCache* cache) - { - // FIXME? - return 0; - } - - BNKCViewState BNKCViewGetState(BNKernelCache* cache) - { - if (cache->object) - { - return (BNKCViewState)cache->object->ViewState(); - } - - return BNKCViewState::Unloaded; - } - - void BNKCViewFreeLoadedRegions(BNKCMappedMemoryRegion* images, size_t count) - { - for (size_t i = 0; i < count; i++) - { - BNFreeString(images[i].name); - } - delete images; - } - - BNKCImage* BNKCViewGetAllImages(BNKernelCache* cache, size_t* count) - { - if (cache->object) - { - auto viewImageHeaders = cache->object->AllImageHeaders(); - *count = viewImageHeaders.size(); - BNKCImage* images = (BNKCImage*)malloc(sizeof(BNKCImage) * viewImageHeaders.size()); - size_t i = 0; - for (const auto& [baseAddress, header] : viewImageHeaders) - { - images[i].name = BNAllocString(header.installName.c_str()); - images[i].headerFileAddress = baseAddress; - images[i].mappingCount = header.sections.size(); - images[i].mappings = (BNKCImageMemoryMapping*)malloc(sizeof(BNKCImageMemoryMapping) * header.sections.size()); - for (size_t j = 0; j < header.sections.size(); j++) - { - images[i].mappings[j].rawViewOffset = header.sections[j].offset; - images[i].mappings[j].vmAddress = header.sections[j].addr; - images[i].mappings[j].size = header.sections[j].size; - images[i].mappings[j].name = BNAllocString(header.sectionNames[j].c_str()); - } - i++; - } - return images; - } - *count = 0; - return nullptr; - } - - void BNKCViewFreeAllImages(BNKCImage* images, size_t count) - { - for (size_t i = 0; i < count; i++) - { - for (size_t j = 0; j < images[i].mappingCount; j++) - { - BNFreeString(images[i].mappings[j].name); - } - delete[] images[i].mappings; - BNFreeString(images[i].name); - } - delete[] images; - } - - char* BNKCViewGetImageHeaderForAddress(BNKernelCache* cache, uint64_t address) - { - if (cache->object) - { - auto header = cache->object->SerializedImageHeaderForVMAddress(address); - return BNAllocString(header.c_str()); - } - - return nullptr; - } - - char* BNKCViewGetImageHeaderForName(BNKernelCache* cache, const char* name) - { - std::string imageName = std::string(name); - if (cache->object) - { - auto header = cache->object->SerializedImageHeaderForName(imageName); - return BNAllocString(header.c_str()); - } - - return nullptr; - } - - BNKCViewLoadProgress BNKCViewGetLoadProgress(uint64_t sessionID) - { - if (auto viewSpecificState = ViewSpecificStateForId(sessionID, false)) - return viewSpecificState->progress; - - return LoadProgressNotStarted; - } - - uint64_t BNKCViewFastGetImageCount(BNBinaryView* data) - { - Ref<BinaryView> view = new BinaryView(BNNewViewReference(data)); - if (view) - return KernelCache::FastGetImageCount(view); - return 0; - } - - BNKCImage* BNKCViewGetLoadedImages(BNKernelCache* cache, size_t* count) - { - if (cache->object) - { - auto images = cache->object->GetLoadedImages(); - *count = images.size(); - BNKCImage* bnImages = new BNKCImage[images.size()]; - for (size_t i = 0; i < images.size(); i++) - { - bnImages[i].name = BNAllocString(images[i].installName.c_str()); - bnImages[i].headerFileAddress = images[i].headerFileLocation; - bnImages[i].mappingCount = images[i].regions.size(); - bnImages[i].mappings = new BNKCImageMemoryMapping[images[i].regions.size()]; - for (size_t j = 0; j < images[i].regions.size(); j++) - { - bnImages[i].mappings[j].rawViewOffset = images[i].regions[j].fileOffset; - bnImages[i].mappings[j].vmAddress = images[i].regions[j].start; - bnImages[i].mappings[j].size = images[i].regions[j].size; - bnImages[i].mappings[j].name = BNAllocString(images[i].regions[j].prettyName.c_str()); - } - } - return bnImages; - } - *count = 0; - return nullptr; - } - - void BNKCViewFreeLoadedImages(BNKCImage* images, size_t count) - { - for (size_t i = 0; i < count; i++) - { - for (size_t j = 0; j < images[i].mappingCount; j++) - { - BNFreeString(images[i].mappings[j].name); - } - delete[] images[i].mappings; - BNFreeString(images[i].name); - } - delete[] images; } + return std::nullopt; } -#ifdef __cplusplus -extern "C" { -#endif - void RegisterTransformers(); - -#ifdef __cplusplus -} -#endif - -void InitKernelcache() -{ - InitKCViewType(); - RegisterTransformers(); -} - -namespace KernelCacheCore { - -void Deserialize( - DeserializationContext& context, std::string_view name, std::optional<std::pair<uint64_t, uint64_t>>& value) -{ - if (!context.doc.HasMember(name.data())) - { - value = std::nullopt; - return; - } - - auto array = context.doc[name.data()].GetArray(); - value = {array[0].GetUint64(), array[1].GetUint64()}; -} - -void Serialize(SerializationContext& context, const Ref<Symbol>& value) +std::optional<CacheImage> KernelCache::GetImageWithName(const std::string& name) const { - context.writer.StartArray(); - Serialize(context, value->GetRawNameRef()); - Serialize(context, value->GetAddress()); - Serialize(context, value->GetType()); - context.writer.EndArray(); + for (const auto& [address, image] : m_images) + if (image.path == name) + return image; + return std::nullopt; } -void Serialize(SerializationContext& context, const std::shared_ptr<std::unordered_map<uint64_t, Ref<Symbol>>>& value) +std::optional<CacheSymbol> KernelCache::GetSymbolAt(uint64_t address) const { - context.writer.StartArray(); - for (const auto& [_, symbol] : *value) - { - Serialize(context, symbol); - } - context.writer.EndArray(); -} - -void Serialize(SerializationContext& context, const std::shared_ptr<std::vector<Ref<Symbol>>>& value) -{ - Serialize(context, *value); -} - -void Deserialize(DeserializationContext& context, std::string_view name, - std::unordered_map<uint64_t, std::shared_ptr<std::unordered_map<uint64_t, Ref<Symbol>>>>& value) -{ - auto array = context.doc[name.data()].GetArray(); - for (auto& pair : array) - { - auto symbols_array = pair[1].GetArray(); - std::unordered_map<uint64_t, Ref<Symbol>> symbols; - for (auto& symbol_value : symbols_array) - { - auto symbol_array = symbol_value.GetArray(); - std::string symbolName = symbol_array[0].GetString(); - uint64_t address = symbol_array[1].GetUint64(); - BNSymbolType type = (BNSymbolType)symbol_array[2].GetUint(); - symbols.insert({address, new Symbol(type, symbolName, address)}); - } - value[pair[0].GetUint64()] = std::make_shared<std::unordered_map<uint64_t, Ref<Symbol>>>(std::move(symbols)); - } -} - -void Deserialize(DeserializationContext& context, std::string_view name, - std::unordered_map<uint64_t, std::shared_ptr<std::vector<Ref<Symbol>>>>& value) -{ - auto array = context.doc[name.data()].GetArray(); - for (auto& pair : array) - { - auto symbols_array = pair[1].GetArray(); - std::vector<Ref<Symbol>> symbols; - symbols.reserve(symbols_array.Size()); - for (auto& symbol_value : symbols_array) - { - auto symbol_array = symbol_value.GetArray(); - std::string symbolName = symbol_array[0].GetString(); - uint64_t address = symbol_array[1].GetUint64(); - BNSymbolType type = (BNSymbolType)symbol_array[2].GetUint(); - symbols.push_back(new Symbol(type, symbolName, address)); - } - value[pair[0].GetUint64()] = std::make_shared<std::vector<Ref<Symbol>>>(std::move(symbols)); - } -} - -void Deserialize(DeserializationContext& context, std::string_view name, std::optional<KCViewState>& viewState) -{ - auto& value = context.doc[name.data()]; - if (value.IsNull()) - viewState = std::nullopt; - else - viewState = (KCViewState)value.GetUint(); -} - - -void Serialize(SerializationContext& context, const std::vector<MemoryRegion>& value) -{ - context.writer.StartArray(); - for (const auto& region : value) - { - context.writer.StartArray(); - Serialize(context, region.prettyName); - Serialize(context, region.start); - Serialize(context, region.size); - Serialize(context, region.fileOffset); - Serialize(context, (uint64_t)region.flags); - Serialize(context, (uint8_t)region.type); - context.writer.EndArray(); - } - context.writer.EndArray(); -} - -void Deserialize(DeserializationContext& context, std::string_view name, std::vector<MemoryRegion>& value) -{ - auto array = context.doc[name.data()].GetArray(); - value.reserve(array.Size()); - for (auto& region : array) - { - auto region_array = region.GetArray(); - MemoryRegion newRegion; - newRegion.prettyName = region_array[0].GetString(); - newRegion.start = region_array[1].GetUint64(); - newRegion.size = region_array[2].GetUint64(); - newRegion.fileOffset = region_array[3].GetUint64(); - newRegion.flags = (BNSegmentFlag)region_array[4].GetUint64(); - newRegion.type = (MemoryRegion::Type)region_array[5].GetUint(); - value.push_back(newRegion); - } -} - -void Serialize(SerializationContext& context, const std::vector<KernelCacheImage>& value) -{ - context.writer.StartArray(); - for (const auto& image : value) - { - Serialize(context, image); - } - context.writer.EndArray(); -} - - -void Deserialize(DeserializationContext& context, std::string_view name, std::vector<KernelCacheImage>& value) -{ - auto array = context.doc[name.data()].GetArray(); - value.reserve(array.Size()); - for (auto& image : array) - { - KernelCacheImage img = KernelCacheImage::LoadFromValue(image); - value.push_back(img); - } -} - - -void Serialize(SerializationContext& context, const std::vector<std::pair<uint64_t, uint64_t>>& value) -{ - context.writer.StartArray(); - for (const auto& pair : value) - { - context.writer.StartArray(); - Serialize(context, pair.first); - Serialize(context, pair.second); - context.writer.EndArray(); - } - context.writer.EndArray(); -} - -void Deserialize(DeserializationContext& context, std::string_view name, std::vector<std::pair<uint64_t, uint64_t>>& value) -{ - auto array = context.doc[name.data()].GetArray(); - value.reserve(array.Size()); - for (auto& pair : array) - { - auto pair_array = pair.GetArray(); - value.push_back({pair_array[0].GetUint64(), pair_array[1].GetUint64()}); - } -} - -void Serialize(SerializationContext& context, const std::unordered_map<uint64_t, KernelCacheMachOHeader>& value) -{ - context.writer.StartArray(); - for (const auto& pair : value) - { - context.writer.StartArray(); - Serialize(context, pair.first); - Serialize(context, pair.second); - context.writer.EndArray(); - } - context.writer.EndArray(); -} - -void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<uint64_t, KernelCacheMachOHeader>& value) -{ - auto array = context.doc[name.data()].GetArray(); - for (auto& pair : array) - { - auto pair_array = pair.GetArray(); - uint64_t key = pair_array[0].GetUint64(); - KernelCacheMachOHeader header = KernelCacheMachOHeader::LoadFromValue(pair_array[1]); - value[key] = header; - } -} - - -void Serialize(SerializationContext& context, const std::unordered_map<uint64_t, KernelCacheImage> value) -{ - context.writer.StartArray(); - for (const auto& pair : value) - { - context.writer.StartArray(); - Serialize(context, pair.first); - Serialize(context, pair.second); - context.writer.EndArray(); - } - context.writer.EndArray(); -} - -void Deserialize(DeserializationContext& context, std::string_view name, std::unordered_map<uint64_t, KernelCacheImage>& value) -{ - auto array = context.doc[name.data()].GetArray(); - for (auto& pair : array) - { - auto pair_array = pair.GetArray(); - uint64_t key = pair_array[0].GetUint64(); - KernelCacheImage image = KernelCacheImage::LoadFromValue(pair_array[1]); - value[key] = image; - } -} - - -void KernelCache::CacheInfo::Store(SerializationContext& context) const -{ - Serialize(context, "metadataVersion", METADATA_VERSION); - - MSS(images); - MSS(imageStarts); - MSS_CAST(cacheFormat, uint8_t); -} - -// static -std::optional<KernelCache::CacheInfo> KernelCache::CacheInfo::Load(DeserializationContext& context) -{ - if (!context.doc.HasMember("metadataVersion")) - { - LogError("Shared Cache metadata version missing"); - return std::nullopt; - } - - if (context.doc["metadataVersion"].GetUint() != METADATA_VERSION) - { - LogError("Shared Cache metadata version mismatch"); + const auto it = m_symbols.find(address); + if (it == m_symbols.end()) return std::nullopt; - } - - CacheInfo cacheInfo; - cacheInfo.MSL(images); - cacheInfo.MSL(imageStarts); - cacheInfo.MSL_CAST(cacheFormat, uint8_t, KernelCacheFormat); - return cacheInfo; -} -void State::Store(SerializationContext& context, std::optional<KCViewState> viewState) const -{ - MSS(loadedImages); - MSS(viewState); -} - -void KernelCache::ModifiedState::Store(SerializationContext& context) const -{ - State::Store(context, viewState); + return it->second; } -KernelCache::ModifiedState KernelCache::ModifiedState::Load(DeserializationContext& context) +std::optional<CacheSymbol> KernelCache::GetSymbolWithName(const std::string& name) { - KernelCache::ModifiedState state; - state.MSL(loadedImages); - state.MSL(viewState); - return state; -} - -KernelCache::ModifiedState KernelCache::ModifiedState::LoadAll(BinaryNinja::BinaryView *dscView, const CacheInfo& cacheInfo) -{ - uint64_t stateCount = dscView->GetUIntMetadata(KernelCacheMetadata::ModifiedStateCountTag); - KernelCache::ModifiedState state; - for (uint64_t i = 0; i < stateCount; ++i) - { - std::string key = KernelCacheMetadata::ModifiedStateTagPrefix + std::to_string(i); - std::string serialized = dscView->GetStringMetadata(key); - auto thisState = KernelCache::ModifiedState::LoadFromString(serialized); - state.Merge(std::move(thisState)); - } - return state; -} - -void KernelCache::ModifiedState::Merge(KernelCache::ModifiedState&& newer) -{ - loadedImages.merge(newer.loadedImages); - - if (newer.viewState) - viewState = newer.viewState; -} - -void KernelCacheImage::Store(SerializationContext& context) const -{ - MSS(installName); - MSS(headerFileLocation); - MSS(regions); -} - -// static -KernelCacheImage KernelCacheImage::Load(DeserializationContext& context) -{ - KernelCacheImage cacheImage; - cacheImage.MSL(installName); - cacheImage.MSL(headerFileLocation); - cacheImage.MSL(regions); - return cacheImage; + std::shared_lock<std::shared_mutex> lock(*m_namedSymMutex); + const auto it = m_namedSymbols.find(name); + if (it == m_namedSymbols.end()) + return std::nullopt; + return GetSymbolAt(it->second); } - - -} // namespace KernelCacheCore |
