summaryrefslogtreecommitdiff
path: root/view/sharedcache/core/SharedCache.cpp
diff options
context:
space:
mode:
authorkat <kat@vector35.com>2024-10-23 21:56:29 -0400
committerkat <kat@vector35.com>2024-10-23 21:58:47 -0400
commit3006c68e108a18f9b8782bc937dc9ec7e2cb3b54 (patch)
treef589bb38909349142c09b6215244205aef0a57ac /view/sharedcache/core/SharedCache.cpp
parent80fcccec8f686e0e5c89626b60af121a97baf856 (diff)
Initial commit of the alpha dyld_shared_cache view API Plugin.
This is an early release of our DSC processing plugin. We're still hard at work improving this feature. You should be able to just drop in a dyld_shared_cache and use the 'Shared Cache Triage' view to load and analyze images.
Diffstat (limited to 'view/sharedcache/core/SharedCache.cpp')
-rw-r--r--view/sharedcache/core/SharedCache.cpp3286
1 files changed, 3286 insertions, 0 deletions
diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp
new file mode 100644
index 00000000..d468ff9a
--- /dev/null
+++ b/view/sharedcache/core/SharedCache.cpp
@@ -0,0 +1,3286 @@
+//
+// Created by kat on 5/19/23.
+//
+
+#include "binaryninjaapi.h"
+
+/* ---
+ * This is the primary image loader logic for Shared Caches
+ *
+ * It is standalone code that operates on a DSCView.
+ *
+ * This has to recreate _all_ of the Mach-O View logic, but slightly differently, as everything is spicy and weird and
+ * different enough that it's not worth trying to make a shared base class.
+ *
+ * Essentially:
+ * 1. Make an API call through this class' FFI wrapper like SharedCache::GetAvailableImages(BinaryView* view)
+ * 2. That hops into core and makes a `new SharedCache(view)` core object (every time, yes) that holds a bv member
+ * variable
+ * 3. Deserializes information from that BinaryView.
+ * 4. Does this api call require reading the original cache? Then map VM pages
+ * 5. Do your stuff, save any changes to the BinaryView's metadata
+ * 6. Unmap VM pages (freeing file pointers, making sure this SharedCache object doesnt leak, etc)
+ * 7. Return
+ *
+ * Since we do everything properly (mmaped files, etc) this is not actually that slow. It can handle being done for
+ * several context menu items without visual lag.
+ *
+ *
+ * Strategy notes:
+ *
+ * While it is probably faster to map a file and read out metadata direct from disk,
+ * this results in an extreme amount of duplicate code.
+ *
+ * So, we try to pre-load a ton of metadata out of the cache and store it on the view, for developer sanity reasons.
+ *
+ * For performance reasons related to serdes speeds we cache this view metadata deserialized, per application lifetime.
+ * We could probably clear this when the file is closed?
+ *
+ * */
+
+#include "SharedCache.h"
+#include "ObjC.h"
+#include <filesystem>
+#include <utility>
+#include <fcntl.h>
+#include <memory>
+#include <chrono>
+#include <thread>
+
+
+using namespace BinaryNinja;
+using namespace SharedCacheCore;
+
+#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
+
+struct ViewStateCacheStore {
+ SharedCache::SharedCacheFormat m_cacheFormat;
+
+ DSCViewState m_viewState;
+
+ std::unordered_map<std::string, uint64_t> m_imageStarts;
+ std::unordered_map<uint64_t, SharedCacheMachOHeader> m_headers;
+
+ std::vector<CacheImage> m_images;
+ std::vector<MemoryRegion> m_regionsMappedIntoMemory;
+
+ std::vector<BackingCache> m_backingCaches;
+ std::vector<MemoryRegion> m_stubIslandRegions; // TODO honestly both of these should be refactored into nonImageRegions. :p
+ std::vector<MemoryRegion> m_dyldDataRegions;
+ std::vector<MemoryRegion> m_nonImageRegions;
+
+ std::string m_baseFilePath;
+
+ std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>> m_exportInfos;
+ std::unordered_map<uint64_t, std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>>> m_symbolInfos;
+};
+
+static std::recursive_mutex viewStateMutex;
+static std::unordered_map<uint64_t, ViewStateCacheStore> viewStateCache;
+
+std::mutex progressMutex;
+std::unordered_map<uint64_t, BNDSCViewLoadProgress> progressMap;
+
+struct ViewSpecificMutexes {
+ std::mutex viewOperationsThatInfluenceMetadataMutex;
+ std::mutex typeLibraryLookupAndApplicationMutex;
+};
+
+static std::unordered_map<uint64_t, ViewSpecificMutexes> viewSpecificMutexes;
+
+
+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;
+}
+
+
+uint64_t SharedCache::FastGetBackingCacheCount(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView)
+{
+ auto baseFile = MMappedFileAccessor::Open(dscView->GetFile()->GetSessionId(), dscView->GetFile()->GetOriginalFilename())->lock();
+
+ dyld_cache_header header {};
+ size_t header_size = baseFile->ReadUInt32(16);
+ baseFile->Read(&header, 0, std::min(header_size, sizeof(dyld_cache_header)));
+
+ SharedCacheFormat cacheFormat;
+
+ if (header.imagesCountOld != 0)
+ cacheFormat = RegularCacheFormat;
+
+ size_t subCacheOff = offsetof(struct dyld_cache_header, subCacheArrayOffset);
+ size_t headerEnd = header.mappingOffset;
+ if (headerEnd > subCacheOff)
+ {
+ if (header.cacheType != 2)
+ {
+ if (std::filesystem::exists(baseFile->Path() + ".01"))
+ cacheFormat = LargeCacheFormat;
+ else
+ cacheFormat = SplitCacheFormat;
+ }
+ else
+ cacheFormat = iOS16CacheFormat;
+ }
+
+ switch (cacheFormat)
+ {
+ case RegularCacheFormat:
+ {
+ return 1;
+ }
+ case LargeCacheFormat:
+ {
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = header.subCacheArrayCount;
+ return subCacheCount + 1;
+ }
+ case SplitCacheFormat:
+ {
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = header.subCacheArrayCount;
+ return subCacheCount + 2;
+ }
+ case iOS16CacheFormat:
+ {
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = header.subCacheArrayCount;
+ return subCacheCount + 2;
+ }
+ }
+}
+
+
+void SharedCache::PerformInitialLoad()
+{
+ m_logger->LogInfo("Performing initial load of Shared Cache");
+ auto path = m_dscView->GetFile()->GetOriginalFilename();
+ auto baseFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), path)->lock();
+
+ progressMutex.lock();
+ progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingCaches;
+ progressMutex.unlock();
+
+ m_baseFilePath = path;
+
+ DataBuffer sig = *baseFile->ReadBuffer(0, 4);
+ if (sig.GetLength() != 4)
+ abort();
+ const char* magic = (char*)sig.GetData();
+ if (strncmp(magic, "dyld", 4) != 0)
+ abort();
+
+ m_cacheFormat = RegularCacheFormat;
+
+ dyld_cache_header primaryCacheHeader {};
+ size_t header_size = baseFile->ReadUInt32(16);
+ baseFile->Read(&primaryCacheHeader, 0, std::min(header_size, sizeof(dyld_cache_header)));
+
+ if (primaryCacheHeader.imagesCountOld != 0)
+ m_cacheFormat = RegularCacheFormat;
+
+ size_t subCacheOff = offsetof(struct dyld_cache_header, subCacheArrayOffset);
+ size_t headerEnd = primaryCacheHeader.mappingOffset;
+ if (headerEnd > subCacheOff)
+ {
+ if (primaryCacheHeader.cacheType != 2)
+ {
+ if (std::filesystem::exists(baseFile->Path() + ".01"))
+ m_cacheFormat = LargeCacheFormat;
+ else
+ m_cacheFormat = SplitCacheFormat;
+ }
+ else
+ m_cacheFormat = iOS16CacheFormat;
+ }
+
+ switch (m_cacheFormat)
+ {
+ case RegularCacheFormat:
+ {
+ dyld_cache_mapping_info mapping {};
+ BackingCache cache;
+ cache.isPrimary = true;
+ cache.path = baseFile->Path();
+
+ for (size_t i = 0; i < primaryCacheHeader.mappingCount; i++)
+ {
+ baseFile->Read(&mapping, primaryCacheHeader.mappingOffset + (i * sizeof(mapping)), sizeof(mapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = mapping.fileOffset;
+ mapRawToAddrAndSize.second.first = mapping.address;
+ mapRawToAddrAndSize.second.second = mapping.size;
+ cache.mappings.push_back(mapRawToAddrAndSize);
+ }
+ m_backingCaches.push_back(cache);
+
+ dyld_cache_image_info img {};
+
+ for (size_t i = 0; i < primaryCacheHeader.imagesCountOld; i++)
+ {
+ baseFile->Read(&img, primaryCacheHeader.imagesOffsetOld + (i * sizeof(img)), sizeof(img));
+ auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
+ m_imageStarts[iname] = img.address;
+ }
+
+ m_logger->LogInfo("Found %d images in the shared cache", primaryCacheHeader.imagesCountOld);
+
+ if (primaryCacheHeader.branchPoolsCount)
+ {
+ std::vector<uint64_t> addresses;
+ for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
+ {
+ addresses.push_back(baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize())));
+ }
+ baseFile.reset(); // No longer needed, we're about to remap this file into VM space so we can load these.
+ uint64_t i = 0;
+ for (auto address : addresses)
+ {
+ i++;
+ auto vm = GetVMMap(true);
+ auto machoHeader = SharedCache::LoadHeaderForAddress(vm, address, "dyld_shared_cache_branch_islands_" + std::to_string(i));
+ if (machoHeader)
+ {
+ for (const auto& segment : machoHeader->segments)
+ {
+ MemoryRegion stubIslandRegion;
+ stubIslandRegion.start = segment.vmaddr;
+ stubIslandRegion.size = segment.filesize;
+ char segName[17];
+ memcpy(segName, segment.segname, 16);
+ segName[16] = 0;
+ std::string segNameStr = std::string(segName);
+ stubIslandRegion.prettyName = "dyld_shared_cache_branch_islands_" + std::to_string(i) + "::" + segNameStr;
+ stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_stubIslandRegions.push_back(stubIslandRegion);
+ }
+ }
+ }
+ }
+
+ m_logger->LogInfo("Found %d branch pools in the shared cache", primaryCacheHeader.branchPoolsCount);
+
+ break;
+ }
+ case LargeCacheFormat:
+ {
+ dyld_cache_mapping_info mapping {}; // We're going to reuse this for all of the mappings. We only need it
+ // briefly.
+
+ BackingCache cache;
+ cache.isPrimary = true;
+ cache.path = baseFile->Path();
+
+ for (size_t i = 0; i < primaryCacheHeader.mappingCount; i++)
+ {
+ baseFile->Read(&mapping, primaryCacheHeader.mappingOffset + (i * sizeof(mapping)), sizeof(mapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = mapping.fileOffset;
+ mapRawToAddrAndSize.second.first = mapping.address;
+ mapRawToAddrAndSize.second.second = mapping.size;
+ cache.mappings.push_back(mapRawToAddrAndSize);
+ }
+ m_backingCaches.push_back(cache);
+
+ dyld_cache_image_info img {};
+
+ for (size_t i = 0; i < primaryCacheHeader.imagesCount; i++)
+ {
+ baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
+ auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
+ m_imageStarts[iname] = img.address;
+ }
+
+ if (primaryCacheHeader.branchPoolsCount)
+ {
+ std::vector<uint64_t> pool {};
+ for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
+ {
+ m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ }
+ }
+
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = primaryCacheHeader.subCacheArrayCount;
+
+ dyld_subcache_entry2 _entry {};
+ std::vector<dyld_subcache_entry2> subCacheEntries;
+ for (size_t i = 0; i < subCacheCount; i++)
+ {
+ baseFile->Read(&_entry, primaryCacheHeader.subCacheArrayOffset + (i * sizeof(dyld_subcache_entry2)),
+ sizeof(dyld_subcache_entry2));
+ subCacheEntries.push_back(_entry);
+ }
+
+ baseFile.reset();
+ for (const auto& entry : subCacheEntries)
+ {
+ std::string subCachePath;
+ if (std::string(entry.fileExtension).find('.') != std::string::npos)
+ subCachePath = mainFileName + entry.fileExtension;
+ else
+ subCachePath = mainFileName + "." + entry.fileExtension;
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (headerSize > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected (0x%llx), using default size (0x%llx)", headerSize,
+ sizeof(dyld_cache_header));
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ dyld_cache_mapping_info subCacheMapping {};
+ BackingCache subCache;
+ subCache.isPrimary = false;
+ subCache.path = subCachePath;
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
+ && subCacheHeader.imagesTextOffset == 0)
+ {
+ auto pathBasename = subCachePath.substr(subCachePath.find_last_of("/\\") + 1);
+ uint64_t address = subCacheMapping.address;
+ uint64_t size = subCacheMapping.size;
+ MemoryRegion stubIslandRegion;
+ stubIslandRegion.start = address;
+ stubIslandRegion.size = size;
+ stubIslandRegion.prettyName = pathBasename + "::_stubs";
+ stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_stubIslandRegions.push_back(stubIslandRegion);
+ }
+
+ m_backingCaches.push_back(subCache);
+ }
+ break;
+ }
+ case SplitCacheFormat:
+ {
+ dyld_cache_mapping_info mapping {}; // We're going to reuse this for all of the mappings. We only need it
+ // briefly.
+ BackingCache cache;
+ cache.isPrimary = true;
+ cache.path = baseFile->Path();
+
+ for (size_t i = 0; i < primaryCacheHeader.mappingCount; i++)
+ {
+ baseFile->Read(&mapping, primaryCacheHeader.mappingOffset + (i * sizeof(mapping)), sizeof(mapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = mapping.fileOffset;
+ mapRawToAddrAndSize.second.first = mapping.address;
+ mapRawToAddrAndSize.second.second = mapping.size;
+ cache.mappings.push_back(mapRawToAddrAndSize);
+ }
+ m_backingCaches.push_back(cache);
+
+ dyld_cache_image_info img {};
+
+ for (size_t i = 0; i < primaryCacheHeader.imagesCount; i++)
+ {
+ baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
+ auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
+ m_imageStarts[iname] = img.address;
+ }
+
+ if (primaryCacheHeader.branchPoolsCount)
+ {
+ std::vector<uint64_t> pool {};
+ for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
+ {
+ m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ }
+ }
+
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = primaryCacheHeader.subCacheArrayCount;
+
+ baseFile.reset();
+
+ for (size_t i = 1; i <= subCacheCount; i++)
+ {
+ auto subCachePath = mainFileName + "." + std::to_string(i);
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (headerSize > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected (0x%llx), using default size (0x%llx)", headerSize,
+ sizeof(dyld_cache_header));
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ BackingCache subCache;
+ subCache.isPrimary = false;
+ subCache.path = subCachePath;
+
+ dyld_cache_mapping_info subCacheMapping {};
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ m_backingCaches.push_back(subCache);
+
+ if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
+ && subCacheHeader.imagesTextOffset == 0)
+ {
+ auto pathBasename = subCachePath.substr(subCachePath.find_last_of("/\\") + 1);
+ uint64_t address = subCacheMapping.address;
+ uint64_t size = subCacheMapping.size;
+ MemoryRegion stubIslandRegion;
+ stubIslandRegion.start = address;
+ stubIslandRegion.size = size;
+ stubIslandRegion.prettyName = pathBasename + "::_stubs";
+ stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_stubIslandRegions.push_back(stubIslandRegion);
+ }
+ }
+
+ // Load .symbols subcache
+
+ auto subCachePath = mainFileName + ".symbols";
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (headerSize > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected (0x%llx), using default size (0x%llx)", headerSize,
+ sizeof(dyld_cache_header));
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ dyld_cache_mapping_info subCacheMapping {};
+ BackingCache subCache;
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ m_backingCaches.push_back(subCache);
+ break;
+ }
+ case iOS16CacheFormat:
+ {
+ dyld_cache_mapping_info mapping {};
+
+ BackingCache cache;
+ cache.isPrimary = true;
+ cache.path = baseFile->Path();
+
+ for (size_t i = 0; i < primaryCacheHeader.mappingCount; i++)
+ {
+ baseFile->Read(&mapping, primaryCacheHeader.mappingOffset + (i * sizeof(mapping)), sizeof(mapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = mapping.fileOffset;
+ mapRawToAddrAndSize.second.first = mapping.address;
+ mapRawToAddrAndSize.second.second = mapping.size;
+ cache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ m_backingCaches.push_back(cache);
+
+ dyld_cache_image_info img {};
+
+ for (size_t i = 0; i < primaryCacheHeader.imagesCount; i++)
+ {
+ baseFile->Read(&img, primaryCacheHeader.imagesOffset + (i * sizeof(img)), sizeof(img));
+ auto iname = baseFile->ReadNullTermString(img.pathFileOffset);
+ m_imageStarts[iname] = img.address;
+ }
+
+ if (primaryCacheHeader.branchPoolsCount)
+ {
+ std::vector<uint64_t> pool {};
+ for (size_t i = 0; i < primaryCacheHeader.branchPoolsCount; i++)
+ {
+ m_imageStarts["dyld_shared_cache_branch_islands_" + std::to_string(i)] = baseFile->ReadULong(primaryCacheHeader.branchPoolsOffset + (i * m_dscView->GetAddressSize()));
+ }
+ }
+
+ auto mainFileName = baseFile->Path();
+ auto subCacheCount = primaryCacheHeader.subCacheArrayCount;
+
+ dyld_subcache_entry2 _entry {};
+
+ std::vector<dyld_subcache_entry2> subCacheEntries;
+ for (size_t i = 0; i < subCacheCount; i++)
+ {
+ baseFile->Read(&_entry, primaryCacheHeader.subCacheArrayOffset + (i * sizeof(dyld_subcache_entry2)),
+ sizeof(dyld_subcache_entry2));
+ subCacheEntries.push_back(_entry);
+ }
+
+ baseFile.reset();
+
+ for (const auto& entry : subCacheEntries)
+ {
+ std::string subCachePath;
+ if (std::string(entry.fileExtension).find('.') != std::string::npos)
+ subCachePath = mainFileName + entry.fileExtension;
+ else
+ subCachePath = mainFileName + "." + entry.fileExtension;
+
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (headerSize > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected (0x%llx), using default size (0x%llx)", headerSize,
+ sizeof(dyld_cache_header));
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ dyld_cache_mapping_info subCacheMapping {};
+
+ BackingCache subCache;
+ subCache.isPrimary = false;
+ subCache.path = subCachePath;
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+
+ if (subCachePath.find(".dylddata") != std::string::npos)
+ {
+ auto pathBasename = subCachePath.substr(subCachePath.find_last_of("/\\") + 1);
+ uint64_t address = subCacheMapping.address;
+ uint64_t size = subCacheMapping.size;
+ MemoryRegion dyldDataRegion;
+ dyldDataRegion.start = address;
+ dyldDataRegion.size = size;
+ dyldDataRegion.prettyName = pathBasename + "::_data" + std::to_string(j);
+ dyldDataRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable);
+ m_dyldDataRegions.push_back(dyldDataRegion);
+ }
+ }
+
+ m_backingCaches.push_back(subCache);
+
+ if (subCacheHeader.mappingCount == 1 && subCacheHeader.imagesCountOld == 0 && subCacheHeader.imagesCount == 0
+ && subCacheHeader.imagesTextOffset == 0)
+ {
+ auto pathBasename = subCachePath.substr(subCachePath.find_last_of("/\\") + 1);
+ uint64_t address = subCacheMapping.address;
+ uint64_t size = subCacheMapping.size;
+ MemoryRegion stubIslandRegion;
+ stubIslandRegion.start = address;
+ stubIslandRegion.size = size;
+ stubIslandRegion.prettyName = pathBasename + "::_stubs";
+ stubIslandRegion.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_stubIslandRegions.push_back(stubIslandRegion);
+ }
+ }
+
+ // Load .symbols subcache
+ try
+ {
+ auto subCachePath = mainFileName + ".symbols";
+ auto subCacheFile = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), subCachePath)->lock();
+ dyld_cache_header subCacheHeader {};
+ uint64_t headerSize = subCacheFile->ReadUInt32(16);
+ if (subCacheFile->ReadUInt32(16) > sizeof(dyld_cache_header))
+ {
+ m_logger->LogDebug("Header size is larger than expected, using default size");
+ headerSize = sizeof(dyld_cache_header);
+ }
+ subCacheFile->Read(&subCacheHeader, 0, headerSize);
+
+ BackingCache subCache;
+ subCache.isPrimary = false;
+ subCache.path = subCachePath;
+
+ dyld_cache_mapping_info subCacheMapping {};
+
+ for (size_t j = 0; j < subCacheHeader.mappingCount; j++)
+ {
+ subCacheFile->Read(&subCacheMapping, subCacheHeader.mappingOffset + (j * sizeof(subCacheMapping)),
+ sizeof(subCacheMapping));
+ std::pair<uint64_t, std::pair<uint64_t, uint64_t>> mapRawToAddrAndSize;
+ mapRawToAddrAndSize.first = subCacheMapping.fileOffset;
+ mapRawToAddrAndSize.second.first = subCacheMapping.address;
+ mapRawToAddrAndSize.second.second = subCacheMapping.size;
+ subCache.mappings.push_back(mapRawToAddrAndSize);
+ }
+
+ m_backingCaches.push_back(subCache);
+ }
+ catch (...)
+ {}
+ break;
+ }
+ }
+ baseFile.reset();
+ progressMutex.lock();
+ progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressLoadingImages;
+ progressMutex.unlock();
+
+ // We have set up enough metadata to map VM now.
+
+ auto vm = GetVMMap(true);
+ if (!vm)
+ {
+ m_logger->LogError("Failed to map VM pages for Shared Cache on initial load, this is fatal.");
+ return;
+ }
+ for (const auto &start : m_imageStarts)
+ {
+ try {
+ auto imageHeader = SharedCache::LoadHeaderForAddress(vm, start.second, start.first);
+ if (imageHeader)
+ {
+ if (imageHeader->linkeditPresent && vm->AddressIsMapped(imageHeader->linkeditSegment.vmaddr))
+ {
+ auto mapping = vm->MappingAtAddress(imageHeader->linkeditSegment.vmaddr);
+ imageHeader->exportTriePath = mapping.first.filePath;
+ }
+ m_headers[start.second] = imageHeader.value();
+ CacheImage image;
+ image.installName = start.first;
+ image.headerLocation = start.second;
+ for (const auto& segment : imageHeader->segments)
+ {
+ char segName[17];
+ memcpy(segName, segment.segname, 16);
+ segName[16] = 0;
+ MemoryRegion sectionRegion;
+ sectionRegion.prettyName = std::string(segName);
+ sectionRegion.start = segment.vmaddr;
+ sectionRegion.size = segment.vmsize;
+ 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);
+ }
+ m_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_logger->LogInfo("Loaded %d Mach-O headers", m_headers.size());
+
+ for (const auto& cache : m_backingCaches)
+ {
+ size_t i = 0;
+ for (const auto& mapping : cache.mappings)
+ {
+ MemoryRegion region;
+ region.start = mapping.second.first;
+ region.size = mapping.second.second;
+ region.prettyName = base_name(cache.path) + "::" + std::to_string(i);
+ // FIXME flags!!! BackingCache.mapping needs refactored to store this information!
+ region.flags = (BNSegmentFlag)(BNSegmentFlag::SegmentReadable | BNSegmentFlag::SegmentExecutable);
+ m_nonImageRegions.push_back(region);
+ i++;
+ }
+ }
+
+ // Iterate through each Mach-O header
+ if (!m_dyldDataRegions.empty())
+ {
+ for (const auto& [headerKey, header] : m_headers)
+ {
+ // Iterate through each segment of the header
+ for (const auto& segment : header.segments)
+ {
+ uint64_t segmentStart = segment.vmaddr;
+ uint64_t segmentEnd = segmentStart + segment.vmsize;
+
+ // Iterate through each region in m_dyldDataRegions
+ for (auto it = m_dyldDataRegions.begin(); it != m_dyldDataRegions.end();)
+ {
+ uint64_t regionStart = it->start;
+ uint64_t regionSize = it->size;
+ uint64_t regionEnd = regionStart + regionSize;
+
+ // Check if the region overlaps with the segment
+ if (segmentStart < regionEnd && segmentEnd > regionStart)
+ {
+ // Split the region into two, removing the overlapped portion
+ std::vector<MemoryRegion> newRegions;
+
+ // Part before the overlap
+ if (regionStart < segmentStart)
+ {
+ MemoryRegion newRegion;
+ newRegion.start = regionStart;
+ newRegion.size = segmentStart - regionStart;
+ newRegion.prettyName = it->prettyName;
+ newRegions.push_back(newRegion);
+ }
+
+ // Part after the overlap
+ if (regionEnd > segmentEnd)
+ {
+ MemoryRegion newRegion;
+ newRegion.start = segmentEnd;
+ newRegion.size = regionEnd - segmentEnd;
+ newRegion.prettyName = it->prettyName;
+ newRegions.push_back(newRegion);
+ }
+
+ // Erase the original region
+ it = m_dyldDataRegions.erase(it);
+
+ // Insert the new regions (if any)
+ for (const auto& newRegion : newRegions)
+ {
+ it = m_dyldDataRegions.insert(it, newRegion);
+ ++it; // Move iterator to the next position
+ }
+ }
+ else
+ {
+ ++it; // No overlap, move to the next region
+ }
+ }
+ }
+ }
+ }
+
+ // Iterate through each Mach-O header
+ if (!m_nonImageRegions.empty())
+ {
+ for (const auto& [headerKey, header] : m_headers)
+ {
+ // Iterate through each segment of the header
+ for (const auto& segment : header.segments)
+ {
+ uint64_t segmentStart = segment.vmaddr;
+ uint64_t segmentEnd = segmentStart + segment.vmsize;
+
+ // Iterate through each region in m_dyldDataRegions
+ for (auto it = m_nonImageRegions.begin(); it != m_nonImageRegions.end();)
+ {
+ uint64_t regionStart = it->start;
+ uint64_t regionSize = it->size;
+ uint64_t regionEnd = regionStart + regionSize;
+
+ // Check if the region overlaps with the segment
+ if (segmentStart < regionEnd && segmentEnd > regionStart)
+ {
+ // Split the region into two, removing the overlapped portion
+ std::vector<MemoryRegion> newRegions;
+
+ // Part before the overlap
+ if (regionStart < segmentStart)
+ {
+ MemoryRegion newRegion;
+ newRegion.start = regionStart;
+ newRegion.size = segmentStart - regionStart;
+ newRegion.prettyName = it->prettyName;
+ newRegions.push_back(newRegion);
+ }
+
+ // Part after the overlap
+ if (regionEnd > segmentEnd)
+ {
+ MemoryRegion newRegion;
+ newRegion.start = segmentEnd;
+ newRegion.size = regionEnd - segmentEnd;
+ newRegion.prettyName = it->prettyName;
+ newRegions.push_back(newRegion);
+ }
+
+ // Erase the original region
+ it = m_nonImageRegions.erase(it);
+
+ // Insert the new regions (if any)
+ for (const auto& newRegion : newRegions)
+ {
+ it = m_nonImageRegions.insert(it, newRegion);
+ ++it; // Move iterator to the next position
+ }
+ }
+ else
+ {
+ ++it; // No overlap, move to the next region
+ }
+ }
+ }
+ }
+ }
+ SaveToDSCView();
+
+ m_logger->LogDebug("Finished initial load of Shared Cache");
+
+ progressMutex.lock();
+ progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressFinished;
+ progressMutex.unlock();
+}
+
+std::shared_ptr<VM> SharedCache::GetVMMap(bool mapPages)
+{
+ std::shared_ptr<VM> vm = std::make_shared<VM>(0x1000);
+
+ if (mapPages)
+ {
+ for (const auto& cache : m_backingCaches)
+ {
+ for (const auto& mapping : cache.mappings)
+ {
+ vm->MapPages(m_dscView->GetFile()->GetSessionId(), mapping.second.first, mapping.first, mapping.second.second, cache.path,
+ [this, vm=vm](std::shared_ptr<MMappedFileAccessor> mmap){
+ ParseAndApplySlideInfoForFile(mmap);
+ });
+ }
+ }
+ }
+
+ return vm;
+}
+
+
+void SharedCache::DeserializeFromRawView()
+{
+ if (m_dscView->QueryMetadata(SharedCacheMetadataTag))
+ {
+ std::unique_lock<std::recursive_mutex> viewStateCacheLock(viewStateMutex);
+ if (viewStateCache.find(m_dscView->GetFile()->GetSessionId()) != viewStateCache.end())
+ {
+ auto c = viewStateCache[m_dscView->GetFile()->GetSessionId()];
+ m_imageStarts = c.m_imageStarts;
+ m_cacheFormat = c.m_cacheFormat;
+ m_backingCaches = c.m_backingCaches;
+ m_viewState = c.m_viewState;
+ m_headers = c.m_headers;
+ m_images = c.m_images;
+ m_regionsMappedIntoMemory = c.m_regionsMappedIntoMemory;
+ m_stubIslandRegions = c.m_stubIslandRegions;
+ m_dyldDataRegions = c.m_dyldDataRegions;
+ m_nonImageRegions = c.m_nonImageRegions;
+ m_baseFilePath = c.m_baseFilePath;
+ m_exportInfos = c.m_exportInfos;
+ m_symbolInfos = c.m_symbolInfos;
+ }
+ else
+ {
+ LoadFromString(m_dscView->GetStringMetadata(SharedCacheMetadataTag));
+ }
+ }
+ else
+ {
+ m_viewState = DSCViewStateUnloaded;
+ m_images.clear(); // fixme ??
+ }
+}
+
+
+std::string to_hex_string(uint64_t value)
+{
+ std::stringstream ss;
+ ss << std::hex << value;
+ return ss.str();
+}
+
+
+void SharedCache::ParseAndApplySlideInfoForFile(std::shared_ptr<MMappedFileAccessor> file)
+{
+ if (file->SlideInfoWasApplied())
+ return;
+ std::vector<std::pair<uint64_t, uint64_t>> rewrites;
+
+ dyld_cache_header baseHeader;
+ file->Read(&baseHeader, 0, sizeof(dyld_cache_header));
+ uint64_t base = UINT64_MAX;
+ for (const auto& backingCache : m_backingCaches)
+ {
+ for (const auto& mapping : backingCache.mappings)
+ {
+ if (mapping.second.first < base)
+ {
+ base = mapping.second.first;
+ break;
+ }
+ }
+ }
+
+ std::vector<std::pair<uint64_t, MappingInfo>> mappings;
+
+ if (baseHeader.slideInfoOffsetUnused)
+ {
+ // Legacy
+
+ auto slideInfoOff = baseHeader.slideInfoOffsetUnused;
+ auto slideInfoVersion = file->ReadUInt32(slideInfoOff);
+ if (slideInfoVersion != 2 && slideInfoVersion != 3)
+ {
+ abort();
+ }
+
+ MappingInfo map;
+
+ file->Read(&map.mappingInfo, baseHeader.mappingOffset + sizeof(dyld_cache_mapping_info), sizeof(dyld_cache_mapping_info));
+ map.file = file;
+ map.slideInfoVersion = slideInfoVersion;
+ if (map.slideInfoVersion == 2)
+ file->Read(&map.slideInfoV2, slideInfoOff, sizeof(dyld_cache_slide_info_v2));
+ else if (map.slideInfoVersion == 3)
+ file->Read(&map.slideInfoV3, slideInfoOff, sizeof(dyld_cache_slide_info_v3));
+
+ mappings.emplace_back(slideInfoOff, map);
+ }
+ else
+ {
+ dyld_cache_header targetHeader;
+ file->Read(&targetHeader, 0, sizeof(dyld_cache_header));
+
+ if (targetHeader.mappingWithSlideCount == 0)
+ {
+ m_logger->LogDebug("No mappings with slide info found");
+ }
+
+ for (auto i = 0; i < targetHeader.mappingWithSlideCount; i++)
+ {
+ dyld_cache_mapping_and_slide_info mappingAndSlideInfo;
+ file->Read(&mappingAndSlideInfo, targetHeader.mappingWithSlideOffset + (i * sizeof(dyld_cache_mapping_and_slide_info)), sizeof(dyld_cache_mapping_and_slide_info));
+ if (mappingAndSlideInfo.slideInfoFileOffset)
+ {
+ MappingInfo map;
+ map.file = file;
+ if (mappingAndSlideInfo.size == 0)
+ continue;
+ map.slideInfoVersion = file->ReadUInt32(mappingAndSlideInfo.slideInfoFileOffset);
+ m_logger->LogDebug("Slide Info Version: %d", map.slideInfoVersion);
+ map.mappingInfo.address = mappingAndSlideInfo.address;
+ map.mappingInfo.size = mappingAndSlideInfo.size;
+ map.mappingInfo.fileOffset = mappingAndSlideInfo.fileOffset;
+ if (map.slideInfoVersion == 2)
+ {
+ file->Read(
+ &map.slideInfoV2, mappingAndSlideInfo.slideInfoFileOffset, sizeof(dyld_cache_slide_info_v2));
+ }
+ else if (map.slideInfoVersion == 3)
+ {
+ file->Read(
+ &map.slideInfoV3, mappingAndSlideInfo.slideInfoFileOffset, sizeof(dyld_cache_slide_info_v3));
+ map.slideInfoV3.auth_value_add = base;
+ }
+ else if (map.slideInfoVersion == 5)
+ {
+ file->Read(
+ &map.slideInfoV5, mappingAndSlideInfo.slideInfoFileOffset, sizeof(dyld_cache_slide_info5));
+ map.slideInfoV5.value_add = base;
+ }
+ else
+ {
+ m_logger->LogError("Unknown slide info version: %d", map.slideInfoVersion);
+ continue;
+ }
+
+ uint64_t slideInfoOffset = mappingAndSlideInfo.slideInfoFileOffset;
+ mappings.emplace_back(slideInfoOffset, map);
+ m_logger->LogDebug("Filename: %s", file->Path().c_str());
+ m_logger->LogDebug("Slide Info Offset: 0x%llx", slideInfoOffset);
+ m_logger->LogDebug("Mapping Address: 0x%llx", map.mappingInfo.address);
+ m_logger->LogDebug("Slide Info v", map.slideInfoVersion);
+ }
+ }
+ }
+
+ if (mappings.empty())
+ {
+ m_logger->LogDebug("No slide info found");
+ file->SetSlideInfoWasApplied(true);
+ return;
+ }
+
+ for (const auto& [off, mapping] : mappings)
+ {
+ m_logger->LogDebug("Slide Info Version: %d", mapping.slideInfoVersion);
+ uint64_t pageStartsOffset = off;
+ uint64_t pageStartCount;
+ uint64_t pageSize;
+ std::vector<uint64_t> pageStartFileOffsets;
+
+ if (mapping.slideInfoVersion == 2)
+ {
+ pageStartsOffset += mapping.slideInfoV2.page_starts_offset;
+ pageStartCount = mapping.slideInfoV2.page_starts_count;
+ pageSize = mapping.slideInfoV2.page_size;
+
+ pageStartFileOffsets.reserve(pageStartCount);
+ size_t cursor = pageStartsOffset;
+
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ try
+ {
+ uint64_t pageStart = mapping.file->ReadUShort(cursor) * 4;
+ cursor += 2;
+ if (pageStart & 0x4000)
+ continue;
+ pageStartFileOffsets.push_back(mapping.mappingInfo.fileOffset + (pageSize * i) + pageStart);
+ }
+ catch (MappingReadException& ex)
+ {
+
+ }
+ }
+ }
+ else if (mapping.slideInfoVersion == 3) {
+ // Slide Info Version 3 Logic
+ pageStartsOffset += sizeof(dyld_cache_slide_info_v3);
+ pageStartCount = mapping.slideInfoV3.page_starts_count;
+ pageSize = mapping.slideInfoV3.page_size;
+
+ pageStartFileOffsets.reserve(pageStartCount);
+ size_t cursor = pageStartsOffset;
+
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ try
+ {
+ uint64_t pageStart = mapping.file->ReadUShort(cursor);
+ cursor += 2;
+ if (pageStart & 0x4000)
+ continue;
+ pageStartFileOffsets.push_back(mapping.mappingInfo.fileOffset + (pageSize * i) + pageStart);
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", cursor);
+ }
+ }
+ }
+ else if (mapping.slideInfoVersion == 5)
+ {
+ pageStartsOffset += sizeof(dyld_cache_slide_info5);
+ pageStartCount = mapping.slideInfoV5.page_starts_count;
+ m_logger->LogDebug("Page Start Count: %d", pageStartCount);
+ pageSize = mapping.slideInfoV5.page_size;
+ auto cursor = pageStartsOffset;
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ try
+ {
+ uint64_t pageStart = mapping.file->ReadUShort(cursor);
+ cursor += 2;
+ if (pageStart == DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE)
+ continue;
+ pageStartFileOffsets.push_back(mapping.mappingInfo.fileOffset + (pageSize * i) + pageStart);
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", cursor);
+ }
+ }
+ }
+ if (pageStartFileOffsets.empty())
+ {
+ m_logger->LogDebug("No page start file offsets found");
+ }
+ for (auto pageStart : pageStartFileOffsets)
+ {
+ if (mapping.slideInfoVersion == 2)
+ {
+ auto deltaMask = mapping.slideInfoV2.delta_mask;
+ auto valueMask = ~deltaMask;
+ auto valueAdd = mapping.slideInfoV2.value_add;
+
+ auto deltaShift = count_trailing_zeros(deltaMask) - 2;
+
+ uint64_t delta = 1;
+ uint64_t loc = pageStart;
+ while (delta != 0)
+ {
+ try {
+ uint64_t rawValue = file->ReadULong(loc);
+ delta = (rawValue & deltaMask) >> deltaShift;
+ uint64_t value = (rawValue & valueMask);
+ if (valueMask != 0)
+ {
+ value += valueAdd;
+ }
+ rewrites.emplace_back(loc, value);
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", loc);
+ delta = 0;
+ }
+ loc += delta;
+ }
+ }
+ else if (mapping.slideInfoVersion == 3)
+ {
+ uint64_t loc = pageStart;
+ uint64_t delta = 1;
+ while (delta != 0)
+ {
+ dyld_cache_slide_pointer3 slideInfo;
+ try
+ {
+ file->Read(&slideInfo, loc, 8);
+ delta = slideInfo.plain.offsetToNextPointer * 8;
+
+ if (slideInfo.auth.authenticated)
+ {
+ uint64_t value = slideInfo.auth.offsetFromSharedCacheBase;
+ value += mapping.slideInfoV3.auth_value_add;
+ rewrites.emplace_back(loc, value);
+ }
+ else
+ {
+ uint64_t value51 = slideInfo.plain.pointerValue;
+ uint64_t top8Bits = value51 & 0x0007F80000000000;
+ uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFF;
+ uint64_t value = (uint64_t)top8Bits << 13 | bottom43Bits;
+ rewrites.emplace_back(loc, value);
+ }
+ loc += delta;
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", loc);
+ delta = 0;
+ }
+ }
+ }
+ else if (mapping.slideInfoVersion == 5)
+ {
+ uint64_t loc = pageStart;
+ uint64_t delta = 1;
+ while (delta != 0)
+ {
+ dyld_cache_slide_pointer5 slideInfo;
+ try
+ {
+ file->Read(&slideInfo, loc, 8);
+ delta = slideInfo.regular.next * 8;
+ if (slideInfo.auth.auth)
+ {
+ uint64_t value = slideInfo.auth.runtimeOffset + mapping.slideInfoV5.value_add;
+ rewrites.emplace_back(loc, value);
+ }
+ else
+ {
+ uint64_t value = base + slideInfo.regular.runtimeOffset;
+ rewrites.emplace_back(loc, value);
+ }
+ loc += delta;
+ }
+ catch (MappingReadException& ex)
+ {
+ m_logger->LogError("Failed to read slide info at 0x%llx\n", loc);
+ delta = 0;
+ }
+ }
+ }
+ }
+ }
+ for (const auto& [loc, value] : rewrites)
+ {
+ file->WritePointer(loc, value);
+#ifdef SLIDEINFO_DEBUG_TAGS
+ uint64_t vmAddr = 0;
+ {
+ for (uint64_t off = baseHeader.mappingOffset; off < baseHeader.mappingOffset + baseHeader.mappingCount * sizeof(dyld_cache_mapping_info); off += sizeof(dyld_cache_mapping_info))
+ {
+ dyld_cache_mapping_info mapping;
+ file->Read(&mapping, off, sizeof(dyld_cache_mapping_info));
+ if (mapping.fileOffset <= loc && loc < mapping.fileOffset + mapping.size)
+ {
+ vmAddr = mapping.address + (loc - mapping.fileOffset);
+ break;
+ }
+ }
+ }
+ Ref<TagType> type = m_dscView->GetTagType("slideinfo");
+ if (!type)
+ {
+ m_dscView->AddTagType(new TagType(m_dscView, "slideinfo", "\xF0\x9F\x9A\x9E"));
+ type = m_dscView->GetTagType("slideinfo");
+ }
+ m_dscView->AddAutoDataTag(vmAddr, new Tag(type, "0x" + to_hex_string(file->ReadULong(loc)) + " => 0x" + to_hex_string(value)));
+#endif
+ }
+ m_logger->LogDebug("Applied slide info for %s (0x%llx rewrites)", file->Path().c_str(), rewrites.size());
+ file->SetSlideInfoWasApplied(true);
+}
+
+
+SharedCache::SharedCache(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView) : m_dscView(dscView)
+{
+ sharedCacheReferences++;
+ INIT_SHAREDCACHE_API_OBJECT()
+ m_logger = LogRegistry::GetLogger("SharedCache", dscView->GetFile()->GetSessionId());
+ DeserializeFromRawView();
+ if (m_viewState == DSCViewStateUnloaded)
+ {
+ if (m_viewState == DSCViewStateUnloaded)
+ {
+ // maybe we are getting called from UI thread. probably. do this on a separate thread just in case.
+ // TODO `wait` argument
+ WorkerEnqueue([this]() {
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ try {
+ PerformInitialLoad();
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to perform initial load of Shared Cache");
+ }
+
+ for (const auto& [_, header] : m_headers)
+ {
+ if (header.installName.find("libsystem_c.dylib") != std::string::npos)
+ {
+ lock.unlock();
+ m_logger->LogInfo("Loading core libsystem_c.dylib library");
+ LoadImageWithInstallName(header.installName);
+ lock.lock();
+ break ;
+ }
+ }
+
+ m_viewState = DSCViewStateLoaded;
+ SaveToDSCView();
+ });
+ }
+ }
+ else
+ {
+ progressMutex.lock();
+ progressMap[m_dscView->GetFile()->GetSessionId()] = LoadProgressFinished;
+ progressMutex.unlock();
+ }
+}
+
+SharedCache::~SharedCache() {
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ sharedCacheReferences--;
+}
+
+SharedCache* SharedCache::GetFromDSCView(BinaryNinja::Ref<BinaryNinja::BinaryView> dscView)
+{
+ try {
+ return new SharedCache(dscView);
+ }
+ catch (...)
+ {
+ return nullptr;
+ }
+}
+
+std::optional<uint64_t> SharedCache::GetImageStart(std::string installName)
+{
+ for (const auto& [name, start] : m_imageStarts)
+ {
+ if (name == installName)
+ {
+ return start;
+ }
+ }
+ return {};
+}
+
+std::optional<SharedCacheMachOHeader> SharedCache::HeaderForAddress(uint64_t address)
+{
+ // We _could_ mark each page with the image start? :grimacing emoji:
+ // But that'd require mapping pages :grimacing emoji: :grimacing emoji:
+ // There's not really any other hacks that could make this faster, that I can think of...
+ for (const auto& [start, header] : m_headers)
+ {
+ for (const auto& segment : header.segments)
+ {
+ if (segment.vmaddr <= address && segment.vmaddr + segment.vmsize > address)
+ {
+ return header;
+ }
+ }
+ }
+ return {};
+}
+
+std::string SharedCache::NameForAddress(uint64_t address)
+{
+ for (const auto& stubIsland : m_stubIslandRegions)
+ {
+ if (stubIsland.start <= address && stubIsland.start + stubIsland.size > address)
+ {
+ return stubIsland.prettyName;
+ }
+ }
+ for (const auto& dyldData : m_dyldDataRegions)
+ {
+ if (dyldData.start <= address && dyldData.start + dyldData.size > address)
+ {
+ return dyldData.prettyName;
+ }
+ }
+ for (const auto& nonImageRegion : m_nonImageRegions)
+ {
+ if (nonImageRegion.start <= address && nonImageRegion.start + nonImageRegion.size > address)
+ {
+ return nonImageRegion.prettyName;
+ }
+ }
+ if (auto header = HeaderForAddress(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 "";
+}
+
+std::string SharedCache::ImageNameForAddress(uint64_t address)
+{
+ if (auto header = HeaderForAddress(address))
+ {
+ return header->identifierPrefix;
+ }
+ return "";
+}
+
+bool SharedCache::LoadImageContainingAddress(uint64_t address)
+{
+ for (const auto& [start, header] : m_headers)
+ {
+ for (const auto& segment : header.segments)
+ {
+ if (segment.vmaddr <= address && segment.vmaddr + segment.vmsize > address)
+ {
+ return LoadImageWithInstallName(header.installName);
+ }
+ }
+ }
+
+ return false;
+}
+
+bool SharedCache::LoadSectionAtAddress(uint64_t address)
+{
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ DeserializeFromRawView();
+ auto vm = GetVMMap();
+ if (!vm)
+ {
+ m_logger->LogError("Failed to map VM pages for Shared Cache.");
+ return false;
+ }
+
+ SharedCacheMachOHeader targetHeader;
+ CacheImage* targetImage = nullptr;
+ MemoryRegion* targetSegment = nullptr;
+
+ for (auto& image : m_images)
+ {
+ for (auto& region : image.regions)
+ {
+ if (region.start <= address && region.start + region.size > address)
+ {
+ targetHeader = m_headers[image.headerLocation];
+ targetImage = &image;
+ targetSegment = &region;
+ break;
+ }
+ }
+ if (targetSegment)
+ break;
+ }
+ if (!targetSegment)
+ {
+ for (auto& stubIsland : m_stubIslandRegions)
+ {
+ if (stubIsland.start <= address && stubIsland.start + stubIsland.size > address)
+ {
+ if (stubIsland.loaded)
+ {
+ return true;
+ }
+ m_logger->LogInfo("Loading stub island %s @ 0x%llx", stubIsland.prettyName.c_str(), stubIsland.start);
+ auto targetFile = vm->MappingAtAddress(stubIsland.start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+ auto reader = VMReader(vm);
+ auto buff = reader.ReadBuffer(stubIsland.start, stubIsland.size);
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+
+ auto name = stubIsland.prettyName;
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(
+ m_dscView->GetParentView()->GetParentView()->GetEnd(), *buff);
+ m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, stubIsland.size, rawViewEnd, stubIsland.size,
+ SegmentReadable | SegmentExecutable);
+ m_dscView->AddUserSegment(stubIsland.start, stubIsland.size, rawViewEnd, stubIsland.size,
+ SegmentReadable | SegmentExecutable);
+ m_dscView->AddUserSection(name, stubIsland.start, stubIsland.size, ReadOnlyCodeSectionSemantics);
+ m_dscView->WriteBuffer(stubIsland.start, *buff);
+
+ stubIsland.loaded = true;
+
+ stubIsland.rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(stubIsland);
+
+ SaveToDSCView();
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ return true;
+ }
+ }
+
+ for (auto& dyldData : m_dyldDataRegions)
+ {
+ if (dyldData.start <= address && dyldData.start + dyldData.size > address)
+ {
+ if (dyldData.loaded)
+ {
+ return true;
+ }
+ m_logger->LogInfo("Loading dyld data %s", dyldData.prettyName.c_str());
+ auto targetFile = vm->MappingAtAddress(dyldData.start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+ auto reader = VMReader(vm);
+ auto buff = reader.ReadBuffer(dyldData.start, dyldData.size);
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+
+ auto name = dyldData.prettyName;
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(
+ m_dscView->GetParentView()->GetParentView()->GetEnd(), *buff);
+ m_dscView->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+ m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, dyldData.size, rawViewEnd, dyldData.size,
+ SegmentReadable);
+ m_dscView->AddUserSegment(dyldData.start, dyldData.size, rawViewEnd, dyldData.size, SegmentReadable);
+ m_dscView->AddUserSection(name, dyldData.start, dyldData.size, ReadOnlyCodeSectionSemantics);
+ m_dscView->WriteBuffer(dyldData.start, *buff);
+
+ dyldData.loaded = true;
+ dyldData.rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(dyldData);
+
+ SaveToDSCView();
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ return true;
+ }
+ }
+
+ for (auto& region : m_nonImageRegions)
+ {
+ if (region.start <= address && region.start + region.size > address)
+ {
+ if (region.loaded)
+ {
+ return true;
+ }
+ m_logger->LogInfo("Loading non-image region %s", region.prettyName.c_str());
+ auto targetFile = vm->MappingAtAddress(region.start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+ auto reader = VMReader(vm);
+ auto buff = reader.ReadBuffer(region.start, region.size);
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+
+ auto name = region.prettyName;
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(
+ m_dscView->GetParentView()->GetParentView()->GetEnd(), *buff);
+ m_dscView->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+ m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, region.size, rawViewEnd, region.size, region.flags);
+ m_dscView->AddUserSegment(region.start, region.size, rawViewEnd, region.size, region.flags);
+ m_dscView->AddUserSection(name, region.start, region.size, ReadOnlyCodeSectionSemantics);
+ m_dscView->WriteBuffer(region.start, *buff);
+
+ region.loaded = true;
+ region.rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(region);
+
+ SaveToDSCView();
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ return true;
+ }
+ }
+
+ m_logger->LogError("Failed to find a segment containing address 0x%llx", address);
+ return false;
+ }
+
+ auto id = m_dscView->BeginUndoActions();
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+ auto reader = VMReader(vm);
+
+ m_logger->LogDebug("Partial loading image %s", targetHeader.installName.c_str());
+
+ auto targetFile = vm->MappingAtAddress(targetSegment->start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+ auto buff = reader.ReadBuffer(targetSegment->start, targetSegment->size);
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(
+ m_dscView->GetParentView()->GetParentView()->GetEnd(), *buff);
+ m_dscView->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+ m_dscView->GetParentView()->AddAutoSegment(
+ rawViewEnd, targetSegment->size, rawViewEnd, targetSegment->size, SegmentReadable);
+ m_dscView->AddUserSegment(
+ targetSegment->start, targetSegment->size, rawViewEnd, targetSegment->size, targetSegment->flags);
+ m_dscView->WriteBuffer(targetSegment->start, *buff);
+
+ targetSegment->loaded = true;
+ targetSegment->rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(*targetSegment);
+
+ SaveToDSCView();
+
+ if (!targetSegment->headerInitialized)
+ {
+ SharedCache::InitializeHeader(m_dscView, vm.get(), targetHeader, {targetSegment});
+ }
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ m_dscView->CommitUndoActions(id);
+
+ return true;
+}
+
+bool SharedCache::LoadImageWithInstallName(std::string installName)
+{
+ auto settings = m_dscView->GetLoadSettings(VIEW_NAME);
+
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+
+ DeserializeFromRawView();
+ m_logger->LogInfo("Loading image %s", installName.c_str());
+
+ auto vm = GetVMMap();
+ CacheImage* targetImage = nullptr;
+
+ for (auto& cacheImage : m_images)
+ {
+ if (cacheImage.installName == installName)
+ {
+ targetImage = &cacheImage;
+ break;
+ }
+ }
+
+ auto header = m_headers[targetImage->headerLocation];
+
+ auto id = m_dscView->BeginUndoActions();
+ m_viewState = DSCViewStateLoadedWithImages;
+
+ auto reader = VMReader(vm);
+ reader.Seek(targetImage->headerLocation);
+
+ std::vector<MemoryRegion*> regionsToLoad;
+
+ for (auto& region : targetImage->regions)
+ {
+ bool allowLoadingLinkedit = false;
+ if (settings && settings->Contains("loader.dsc.allowLoadingLinkeditSegments"))
+ allowLoadingLinkedit = settings->Get<bool>("loader.dsc.allowLoadingLinkeditSegments", m_dscView);
+ if ((region.prettyName.find("__LINKEDIT") != std::string::npos) && !allowLoadingLinkedit)
+ continue;
+
+ if (region.loaded)
+ {
+ m_logger->LogDebug("Skipping region %s as it is already loaded.", region.prettyName.c_str());
+ continue;
+ }
+
+ auto targetFile = vm->MappingAtAddress(region.start).first.fileAccessor->lock();
+ ParseAndApplySlideInfoForFile(targetFile);
+
+ auto rawViewEnd = m_dscView->GetParentView()->GetEnd();
+
+ auto buff = reader.ReadBuffer(region.start, region.size);
+ m_dscView->GetParentView()->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+ m_dscView->GetParentView()->WriteBuffer(rawViewEnd, *buff);
+
+ region.loaded = true;
+ region.rawViewOffsetIfLoaded = rawViewEnd;
+
+ m_regionsMappedIntoMemory.push_back(region);
+
+ m_dscView->GetParentView()->AddAutoSegment(rawViewEnd, region.size, rawViewEnd, region.size, region.flags);
+ m_dscView->AddUserSegment(region.start, region.size, rawViewEnd, region.size, region.flags);
+ m_dscView->WriteBuffer(region.start, *buff);
+
+ regionsToLoad.push_back(&region);
+ }
+
+ if (regionsToLoad.empty())
+ {
+ m_logger->LogWarn("No regions to load for image %s", installName.c_str());
+ return false;
+ }
+
+ std::unique_lock<std::mutex> typelibLock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].typeLibraryLookupAndApplicationMutex);
+ auto typeLib = m_dscView->GetTypeLibrary(header.installName);
+
+ if (!typeLib)
+ {
+ auto typeLibs = m_dscView->GetDefaultPlatform()->GetTypeLibrariesByName(header.installName);
+ if (!typeLibs.empty())
+ {
+ typeLib = typeLibs[0];
+ m_dscView->AddTypeLibrary(typeLib);
+ m_logger->LogInfo("shared-cache: adding type library for '%s': %s (%s)",
+ targetImage->installName.c_str(), typeLib->GetName().c_str(), typeLib->GetGuid().c_str());
+ }
+ }
+ typelibLock.unlock();
+
+ SaveToDSCView();
+
+ auto h = SharedCache::LoadHeaderForAddress(vm, targetImage->headerLocation, installName);
+ if (!h.has_value())
+ {
+ return false;
+ }
+
+ std::vector<MemoryRegion*> regions;
+ for (auto& region : regionsToLoad)
+ {
+ regions.push_back(region);
+ }
+
+ SharedCache::InitializeHeader(m_dscView, vm.get(), *h, regions);
+
+ try
+ {
+ auto objc = std::make_unique<DSCObjC::DSCObjCProcessor>(m_dscView, this, false);
+
+ bool processCFStrings = true;
+ bool processObjCMetadata = true;
+ if (settings && settings->Contains("loader.dsc.processCFStrings"))
+ processCFStrings = settings->Get<bool>("loader.dsc.processCFStrings", m_dscView);
+ if (settings && settings->Contains("loader.dsc.processObjC"))
+ processObjCMetadata = settings->Get<bool>("loader.dsc.processObjC", m_dscView);
+ if (processObjCMetadata)
+ objc->ProcessObjCData(vm, h->identifierPrefix);
+ if (processCFStrings)
+ objc->ProcessCFStrings(vm, h->identifierPrefix);
+ }
+ catch (const std::exception& ex)
+ {
+ m_logger->LogWarn("Error processing ObjC data: %s", ex.what());
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Error processing ObjC data");
+ }
+
+ m_dscView->AddAnalysisOption("linearsweep");
+ m_dscView->UpdateAnalysis();
+
+ m_dscView->CommitUndoActions(id);
+
+ return true;
+}
+
+std::optional<SharedCacheMachOHeader> SharedCache::LoadHeaderForAddress(std::shared_ptr<VM> vm, uint64_t address, std::string installName)
+{
+ SharedCacheMachOHeader header;
+
+ header.textBase = address;
+ header.installName = installName;
+ header.identifierPrefix = base_name(installName);
+
+ std::string errorMsg;
+ // address is a Raw file offset
+ VMReader reader(vm);
+ 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();
+
+ bool first = true;
+ // 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(&sect, 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 (first)
+ {
+ if (!((header.ident.flags & MH_SPLIT_SEGS) || header.ident.cputype == MACHO_CPU_TYPE_X86_64)
+ || (segment64.flags & MACHO_VM_PROT_WRITE))
+ {
+ header.relocationBase = segment64.vmaddr;
+ first = false;
+ }
+ }
+ for (size_t j = 0; j < segment64.nsects; j++)
+ {
+ reader.Read(&sect.sectname, 16);
+ reader.Read(&sect.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 ((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 (first)
+ {
+ if (!((header.ident.flags & MH_SPLIT_SEGS) || header.ident.cputype == MACHO_CPU_TYPE_X86_64)
+ || (segment64.flags & MACHO_VM_PROT_WRITE))
+ {
+ header.relocationBase = segment64.vmaddr;
+ first = false;
+ }
+ }
+ for (size_t j = 0; j < segment64.nsects; j++)
+ {
+ reader.Read(&sect.sectname, 16);
+ reader.Read(&sect.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;
+ if (header.identifierPrefix.empty())
+ header.sectionNames.push_back(sectionName);
+ else
+ header.sectionNames.push_back(header.identifierPrefix + "::" + sectionName);
+ }
+ }
+ catch (ReadException&)
+ {
+ return {};
+ }
+
+ return header;
+}
+
+void SharedCache::InitializeHeader(
+ Ref<BinaryView> view, VM* vm, SharedCacheMachOHeader header, std::vector<MemoryRegion*> regionsToLoad)
+{
+
+ Ref<Settings> settings = view->GetLoadSettings(VIEW_NAME);
+ bool applyFunctionStarts = true;
+ if (settings && settings->Contains("loader.dsc.processFunctionStarts"))
+ applyFunctionStarts = settings->Get<bool>("loader.dsc.processFunctionStarts", view);
+
+ for (size_t i = 0; i < header.sections.size(); i++)
+ {
+ bool skip = false;
+ for (const auto& region : regionsToLoad)
+ {
+ if (header.sections[i].addr >= region->start && header.sections[i].addr < region->start + region->size)
+ {
+ if (region->headerInitialized)
+ {
+ skip = true;
+ }
+ break;
+ }
+ }
+ if (!header.sections[i].size || skip)
+ 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->AddUserSection(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);
+
+ bool applyHeaderTypes = false;
+ for (const auto& region : regionsToLoad)
+ {
+ if (header.textBase >= region->start && header.textBase < region->start + region->size)
+ {
+ if (!region->headerInitialized)
+ applyHeaderTypes = true;
+ break;
+ }
+ }
+ if (applyHeaderTypes)
+ {
+ 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.textBase + sizeof(mach_header_64));
+ size_t sectionNum = 0;
+ for (size_t i = 0; i < header.ident.ncmds; i++)
+ {
+ load_command load;
+ uint64_t curOffset = virtualReader.GetOffset();
+ load.cmd = virtualReader.Read32();
+ load.cmdsize = virtualReader.Read32();
+ uint64_t nextOffset = curOffset + load.cmdsize;
+ switch (load.cmd)
+ {
+ case LC_SEGMENT:
+ {
+ view->DefineDataVariable(curOffset, 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->DefineUserSymbol(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(curOffset, 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->DefineUserSymbol(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(curOffset, Type::NamedType(view, QualifiedName("symtab")));
+ break;
+ case LC_DYSYMTAB:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("dysymtab")));
+ break;
+ case LC_UUID:
+ view->DefineDataVariable(curOffset, 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(curOffset, Type::NamedType(view, QualifiedName("dylib_command")));
+ if (load.cmdsize - 24 <= 150)
+ view->DefineDataVariable(
+ curOffset + 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(curOffset, Type::NamedType(view, QualifiedName("linkedit_data")));
+ break;
+ case LC_ENCRYPTION_INFO:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("encryption_info")));
+ break;
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("version_min")));
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("dyld_info")));
+ break;
+ default:
+ view->DefineDataVariable(curOffset, Type::NamedType(view, QualifiedName("load_command")));
+ break;
+ }
+
+ view->DefineAutoSymbol(new Symbol(DataSymbol,
+ "__macho_load_command::" + header.identifierPrefix + "_[" + std::to_string(i) + "]", curOffset,
+ LocalBinding));
+ virtualReader.Seek(nextOffset);
+ }
+ }
+ catch (ReadException&)
+ {
+ LogError("Error when applying Mach-O header types at %" PRIx64, header.textBase);
+ }
+ }
+
+ if (applyFunctionStarts && header.functionStartsPresent && header.linkeditPresent && vm->AddressIsMapped(header.linkeditSegment.vmaddr))
+ {
+ auto funcStarts =
+ vm->MappingAtAddress(header.linkeditSegment.vmaddr)
+ .first.fileAccessor->lock()
+ ->ReadBuffer(header.functionStarts.funcoff, header.functionStarts.funcsize);
+ size_t i = 0;
+ uint64_t curfunc = header.textBase;
+ uint64_t curOffset;
+
+ while (i < header.functionStarts.funcsize)
+ {
+ curOffset = readLEB128(*funcStarts, header.functionStarts.funcsize, i);
+ bool addFunction = false;
+ for (const auto& region : regionsToLoad)
+ {
+ if (curfunc >= region->start && curfunc < region->start + region->size)
+ {
+ if (!region->headerInitialized)
+ addFunction = true;
+ }
+ }
+ // LogError("0x%llx, 0x%llx", header.textBase, curOffset);
+ if (curOffset == 0 || !addFunction)
+ continue;
+ curfunc += curOffset;
+ uint64_t target = curfunc;
+ Ref<Platform> targetPlatform = view->GetDefaultPlatform();
+ view->AddFunctionForAnalysis(targetPlatform, target);
+ }
+ }
+
+ view->BeginBulkModifySymbols();
+ if (header.symtab.symoff != 0 && header.linkeditPresent && vm->AddressIsMapped(header.linkeditSegment.vmaddr))
+ {
+ // Mach-O View symtab processing with
+ // a ton of stuff cut out so it can work
+
+ auto reader = vm->MappingAtAddress(header.linkeditSegment.vmaddr).first.fileAccessor->lock();
+ // auto symtab = reader->ReadBuffer(header.symtab.symoff, header.symtab.nsyms * sizeof(nlist_64));
+ auto strtab = reader->ReadBuffer(header.symtab.stroff, header.symtab.strsize);
+ nlist_64 sym;
+ memset(&sym, 0, sizeof(sym));
+ auto N_TYPE = 0xE; // idk
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> symbolInfos;
+ for (size_t i = 0; i < header.symtab.nsyms; i++)
+ {
+ reader->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++;
+
+ auto symbolObj = new Symbol(type, symbol, sym.n_value, GlobalBinding);
+ if (type == FunctionSymbol)
+ {
+ Ref<Platform> targetPlatform = view->GetDefaultPlatform();
+ view->AddFunctionForAnalysis(targetPlatform, sym.n_value);
+ }
+ if (typeLib)
+ {
+ auto _type = m_dscView->ImportTypeLibraryObject(typeLib, {symbolObj->GetFullName()});
+ if (_type)
+ {
+ view->DefineAutoSymbolAndVariableOrFunction(view->GetDefaultPlatform(), symbolObj, _type);
+ }
+ else
+ view->DefineAutoSymbol(symbolObj);
+ }
+ else
+ view->DefineAutoSymbol(symbolObj);
+ symbolInfos.push_back({sym.n_value, {type, symbol}});
+ }
+ m_symbolInfos[header.textBase] = symbolInfos;
+ }
+
+ if (header.exportTriePresent && header.linkeditPresent && vm->AddressIsMapped(header.linkeditSegment.vmaddr))
+ {
+ auto symbols = SharedCache::ParseExportTrie(vm->MappingAtAddress(header.linkeditSegment.vmaddr).first.fileAccessor->lock(), header);
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportMapping;
+ for (const auto& symbol : symbols)
+ {
+ exportMapping.push_back({symbol->GetAddress(), {symbol->GetType(), symbol->GetRawName()}});
+ if (typeLib)
+ {
+ auto type = m_dscView->ImportTypeLibraryObject(typeLib, {symbol->GetFullName()});
+
+ if (type)
+ {
+ view->DefineAutoSymbolAndVariableOrFunction(view->GetDefaultPlatform(), symbol, type);
+ }
+ else
+ view->DefineAutoSymbol(symbol);
+
+ if (view->GetAnalysisFunction(view->GetDefaultPlatform(), symbol->GetAddress()))
+ {
+ auto func = view->GetAnalysisFunction(view->GetDefaultPlatform(), symbol->GetAddress());
+ if (symbol->GetFullName() == "_objc_msgSend")
+ {
+ func->SetHasVariableArguments(false);
+ }
+ else if (symbol->GetFullName().find("_objc_retain_x") != std::string::npos || symbol->GetFullName().find("_objc_release_x") != std::string::npos)
+ {
+ auto x = symbol->GetFullName().rfind("x");
+ auto num = symbol->GetFullName().substr(x + 1);
+
+ std::vector<BinaryNinja::FunctionParameter> callTypeParams;
+ auto cc = m_dscView->GetDefaultArchitecture()->GetCallingConventionByName("apple-arm64-objc-fast-arc-" + num);
+
+ callTypeParams.push_back({"obj", m_dscView->GetTypeByName({ "id" }), true, BinaryNinja::Variable()});
+
+ auto funcType = BinaryNinja::Type::FunctionType(m_dscView->GetTypeByName({ "id" }), cc, callTypeParams);
+ func->SetUserType(funcType);
+ }
+ }
+ }
+ else
+ view->DefineAutoSymbol(symbol);
+ }
+ m_exportInfos[header.textBase] = exportMapping;
+ }
+ view->EndBulkModifySymbols();
+
+ for (auto region : regionsToLoad)
+ {
+ region->headerInitialized = true;
+ }
+}
+
+struct ExportNode
+{
+ std::string text;
+ uint64_t offset;
+ uint64_t flags;
+};
+
+
+void SharedCache::ReadExportNode(std::vector<Ref<Symbol>>& symbolList, SharedCacheMachOHeader& 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 = m_dscView->GetAnalysisFunctionsForAddress(textBase + imageOffset).size() ? FunctionSymbol : DataSymbol;
+ {
+ if (!currentText.empty() && textBase + imageOffset)
+ {
+ uint32_t flags;
+ BNSymbolType type;
+ for (auto s : header.sections)
+ {
+ if (s.addr < textBase + imageOffset)
+ {
+ if (s.addr + s.size > textBase + imageOffset)
+ {
+ flags = s.flags;
+ }
+ }
+ }
+ 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 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(symbolList, header, buffer, textBase, currentText + childText, next, endGuard);
+ }
+}
+
+
+std::vector<Ref<Symbol>> SharedCache::ParseExportTrie(std::shared_ptr<MMappedFileAccessor> linkeditFile, SharedCacheMachOHeader header)
+{
+ std::vector<Ref<Symbol>> symbols;
+ try
+ {
+ auto reader = linkeditFile;
+
+ std::vector<ExportNode> nodes;
+
+ DataBuffer* buffer = reader->ReadBuffer(header.exportTrie.dataoff, header.exportTrie.datasize);
+ ReadExportNode(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> SharedCache::GetAvailableImages()
+{
+ std::vector<std::string> installNames;
+ for (const auto& header : m_headers)
+ {
+ installNames.push_back(header.second.installName);
+ }
+ return installNames;
+}
+
+
+std::vector<std::pair<std::string, Ref<Symbol>>> SharedCache::LoadAllSymbolsAndWait()
+{
+ std::unique_lock<std::mutex> initialLoadBlock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+
+ std::vector<std::pair<std::string, Ref<Symbol>>> symbols;
+ for (const auto& img : m_images)
+ {
+ auto header = HeaderForAddress(img.headerLocation);
+ std::shared_ptr<MMappedFileAccessor> mapping;
+ try {
+ mapping = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), header->exportTriePath)->lock();
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Serious Error: Failed to open export trie for %s", header->installName.c_str());
+ continue;
+ }
+ auto exportList = SharedCache::ParseExportTrie(mapping, *header);
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportMapping;
+ for (const auto& sym : exportList)
+ {
+ exportMapping.push_back({sym->GetAddress(), {sym->GetType(), sym->GetRawName()}});
+ symbols.push_back({img.installName, sym});
+ }
+ m_exportInfos[header->textBase] = exportMapping;
+ }
+
+ SaveToDSCView();
+
+ return symbols;
+}
+
+
+std::string SharedCache::SerializedImageHeaderForAddress(uint64_t address)
+{
+ auto header = HeaderForAddress(address);
+ if (header)
+ {
+ return header->AsString();
+ }
+ return "";
+}
+
+
+std::string SharedCache::SerializedImageHeaderForName(std::string name)
+{
+ auto header = HeaderForAddress(m_imageStarts[name]);
+ if (header)
+ {
+ return header->AsString();
+ }
+ return "";
+}
+
+
+void SharedCache::FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis)
+{
+ std::string prefix = "";
+ if (symbolLocation != targetLocation)
+ prefix = "j_";
+ if (auto preexistingSymbol = m_dscView->GetSymbolByAddress(targetLocation))
+ {
+ if (preexistingSymbol->GetFullName().find("j_") != std::string::npos)
+ return;
+ }
+ auto id = m_dscView->BeginUndoActions();
+ if (auto loadedSymbol = m_dscView->GetSymbolByAddress(symbolLocation))
+ {
+ if (m_dscView->GetAnalysisFunction(m_dscView->GetDefaultPlatform(), targetLocation))
+ m_dscView->DefineUserSymbol(new Symbol(FunctionSymbol, prefix + loadedSymbol->GetFullName(), targetLocation));
+ else
+ m_dscView->DefineUserSymbol(new Symbol(loadedSymbol->GetType(), prefix + loadedSymbol->GetFullName(), targetLocation));
+ }
+ else if (auto sym = m_dscView->GetSymbolByAddress(symbolLocation))
+ {
+ if (m_dscView->GetAnalysisFunction(m_dscView->GetDefaultPlatform(), targetLocation))
+ m_dscView->DefineUserSymbol(new Symbol(FunctionSymbol, prefix + sym->GetFullName(), targetLocation));
+ else
+ m_dscView->DefineUserSymbol(new Symbol(sym->GetType(), prefix + sym->GetFullName(), targetLocation));
+ }
+ m_dscView->ForgetUndoActions(id);
+ auto header = HeaderForAddress(symbolLocation);
+ if (header)
+ {
+ std::shared_ptr<MMappedFileAccessor> mapping;
+ try {
+ mapping = MMappedFileAccessor::Open(m_dscView->GetFile()->GetSessionId(), header->exportTriePath)->lock();
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Serious Error: Failed to open export trie for %s", header->installName.c_str());
+ return;
+ }
+ auto exportList = SharedCache::ParseExportTrie(mapping, *header);
+ std::vector<std::pair<uint64_t, std::pair<BNSymbolType, std::string>>> exportMapping;
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].typeLibraryLookupAndApplicationMutex);
+ auto typeLib = m_dscView->GetTypeLibrary(header->installName);
+ if (!typeLib)
+ {
+ auto typeLibs = m_dscView->GetDefaultPlatform()->GetTypeLibrariesByName(header->installName);
+ if (!typeLibs.empty())
+ {
+ typeLib = typeLibs[0];
+ m_dscView->AddTypeLibrary(typeLib);
+ }
+ }
+ lock.unlock();
+ id = m_dscView->BeginUndoActions();
+ m_dscView->BeginBulkModifySymbols();
+ for (const auto& sym : exportList)
+ {
+ exportMapping.push_back({sym->GetAddress(), {sym->GetType(), sym->GetRawName()}});
+ if (sym->GetAddress() == symbolLocation)
+ {
+ if (auto func = m_dscView->GetAnalysisFunction(m_dscView->GetDefaultPlatform(), targetLocation))
+ {
+ m_dscView->DefineUserSymbol(
+ new Symbol(FunctionSymbol, prefix + sym->GetFullName(), targetLocation));
+
+ if (typeLib)
+ if (auto type = m_dscView->ImportTypeLibraryObject(typeLib, {sym->GetFullName()}))
+ func->SetUserType(type);
+ }
+ else
+ {
+ m_dscView->DefineUserSymbol(
+ new Symbol(sym->GetType(), prefix + sym->GetFullName(), targetLocation));
+
+ if (typeLib)
+ if (auto type = m_dscView->ImportTypeLibraryObject(typeLib, {sym->GetFullName()}))
+ m_dscView->DefineUserDataVariable(targetLocation, type);
+ }
+ if (triggerReanalysis)
+ {
+ auto func = m_dscView->GetAnalysisFunction(m_dscView->GetDefaultPlatform(), targetLocation);
+ if (func)
+ func->Reanalyze();
+ }
+ break;
+ }
+ }
+ {
+ std::unique_lock<std::mutex> _lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ m_exportInfos[header->textBase] = exportMapping;
+ }
+ m_dscView->EndBulkModifySymbols();
+ m_dscView->ForgetUndoActions(id);
+ }
+}
+
+
+bool SharedCache::SaveToDSCView()
+{
+ if (m_dscView)
+ {
+ auto data = AsMetadata();
+ m_dscView->StoreMetadata(SharedCacheMetadataTag, data);
+ m_dscView->GetParentView()->GetParentView()->StoreMetadata(SharedCacheMetadataTag, data);
+ std::unique_lock<std::recursive_mutex> viewStateCacheLock(viewStateMutex);
+ ViewStateCacheStore c;
+ c.m_imageStarts = m_imageStarts;
+ c.m_cacheFormat = m_cacheFormat;
+ c.m_backingCaches = m_backingCaches;
+ c.m_viewState = m_viewState;
+ c.m_headers = m_headers;
+ c.m_images = m_images;
+ c.m_regionsMappedIntoMemory = m_regionsMappedIntoMemory;
+ c.m_stubIslandRegions = m_stubIslandRegions;
+ c.m_dyldDataRegions = m_dyldDataRegions;
+ c.m_nonImageRegions = m_nonImageRegions;
+ c.m_baseFilePath = m_baseFilePath;
+ c.m_exportInfos = m_exportInfos;
+ c.m_symbolInfos = m_symbolInfos;
+ viewStateCache[m_dscView->GetFile()->GetSessionId()] = c;
+
+ return true;
+ }
+ return false;
+}
+std::vector<MemoryRegion> SharedCache::GetMappedRegions() const
+{
+ std::unique_lock<std::mutex> lock(viewSpecificMutexes[m_dscView->GetFile()->GetSessionId()].viewOperationsThatInfluenceMetadataMutex);
+ return m_regionsMappedIntoMemory;
+}
+
+extern "C"
+{
+ BNSharedCache* BNGetSharedCache(BNBinaryView* data)
+ {
+ if (!data)
+ return nullptr;
+
+ Ref<BinaryView> view = new BinaryView(BNNewViewReference(data));
+ if (auto cache = SharedCache::GetFromDSCView(view))
+ {
+ cache->AddAPIRef();
+ return cache->GetAPIObject();
+ }
+
+ return nullptr;
+ }
+
+ BNSharedCache* BNNewSharedCacheReference(BNSharedCache* cache)
+ {
+ if (!cache->object)
+ return nullptr;
+
+ cache->object->AddAPIRef();
+ return cache;
+ }
+
+ void BNFreeSharedCacheReference(BNSharedCache* cache)
+ {
+ if (!cache->object)
+ return;
+
+ cache->object->ReleaseAPIRef();
+ }
+
+ bool BNDSCViewLoadImageWithInstallName(BNSharedCache* cache, char* name)
+ {
+ std::string imageName = std::string(name);
+ BNFreeString(name);
+
+ if (cache->object)
+ return cache->object->LoadImageWithInstallName(imageName);
+
+ return false;
+ }
+
+ bool BNDSCViewLoadSectionAtAddress(BNSharedCache* cache, uint64_t addr)
+ {
+ if (cache->object)
+ {
+ return cache->object->LoadSectionAtAddress(addr);
+ }
+
+ return false;
+ }
+
+ bool BNDSCViewLoadImageContainingAddress(BNSharedCache* cache, uint64_t address)
+ {
+ if (cache->object)
+ {
+ return cache->object->LoadImageContainingAddress(address);
+ }
+
+ return false;
+ }
+
+ char** BNDSCViewGetInstallNames(BNSharedCache* 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;
+ }
+
+ BNDSCSymbolRep* BNDSCViewLoadAllSymbolsAndWait(BNSharedCache* cache, size_t* count)
+ {
+ if (cache->object)
+ {
+ auto value = cache->object->LoadAllSymbolsAndWait();
+ *count = value.size();
+
+ BNDSCSymbolRep* symbols = (BNDSCSymbolRep*)malloc(sizeof(BNDSCSymbolRep) * value.size());
+ for (size_t i = 0; i < value.size(); i++)
+ {
+ symbols[i].address = value[i].second->GetAddress();
+ symbols[i].name = BNAllocString(value[i].second->GetRawName().c_str());
+ symbols[i].image = BNAllocString(value[i].first.c_str());
+ }
+ return symbols;
+ }
+ *count = 0;
+ return nullptr;
+ }
+
+ void BNDSCViewFreeSymbols(BNDSCSymbolRep* symbols, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ {
+ BNFreeString(symbols[i].name);
+ BNFreeString(symbols[i].image);
+ }
+ delete symbols;
+ }
+
+ char* BNDSCViewGetNameForAddress(BNSharedCache* cache, uint64_t address)
+ {
+ if (cache->object)
+ {
+ return BNAllocString(cache->object->NameForAddress(address).c_str());
+ }
+
+ return nullptr;
+ }
+
+ char* BNDSCViewGetImageNameForAddress(BNSharedCache* cache, uint64_t address)
+ {
+ if (cache->object)
+ {
+ return BNAllocString(cache->object->ImageNameForAddress(address).c_str());
+ }
+
+ return nullptr;
+ }
+
+ uint64_t BNDSCViewLoadedImageCount(BNSharedCache* cache)
+ {
+ // FIXME?
+ return 0;
+ }
+
+ BNDSCViewState BNDSCViewGetState(BNSharedCache* cache)
+ {
+ if (cache->object)
+ {
+ return (BNDSCViewState)cache->object->State();
+ }
+
+ return BNDSCViewState::Unloaded;
+ }
+
+
+ BNDSCMappedMemoryRegion* BNDSCViewGetLoadedRegions(BNSharedCache* cache, size_t* count)
+ {
+ if (cache->object)
+ {
+ auto regions = cache->object->GetMappedRegions();
+ *count = regions.size();
+ BNDSCMappedMemoryRegion* mappedRegions = (BNDSCMappedMemoryRegion*)malloc(sizeof(BNDSCMappedMemoryRegion) * regions.size());
+ for (size_t i = 0; i < regions.size(); i++)
+ {
+ mappedRegions[i].vmAddress = regions[i].start;
+ mappedRegions[i].size = regions[i].size;
+ mappedRegions[i].name = BNAllocString(regions[i].prettyName.c_str());
+ }
+ return mappedRegions;
+ }
+ *count = 0;
+ return nullptr;
+ }
+
+ void BNDSCViewFreeLoadedRegions(BNDSCMappedMemoryRegion* images, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ {
+ BNFreeString(images[i].name);
+ }
+ delete images;
+ }
+
+
+ BNDSCBackingCache* BNDSCViewGetBackingCaches(BNSharedCache* cache, size_t* count)
+ {
+ BNDSCBackingCache* caches = nullptr;
+
+ if (cache->object)
+ {
+ auto viewCaches = cache->object->BackingCaches();
+ *count = viewCaches.size();
+ caches = (BNDSCBackingCache*)malloc(sizeof(BNDSCBackingCache) * viewCaches.size());
+ for (size_t i = 0; i < viewCaches.size(); i++)
+ {
+ caches[i].path = BNAllocString(viewCaches[i].path.c_str());
+ caches[i].isPrimary = viewCaches[i].isPrimary;
+
+ BNDSCBackingCacheMapping* mappings;
+ mappings = (BNDSCBackingCacheMapping*)malloc(sizeof(BNDSCBackingCacheMapping) * viewCaches[i].mappings.size());
+
+ size_t j = 0;
+ for (const auto& [fileOffset, mapping] : viewCaches[i].mappings)
+ {
+ mappings[j].vmAddress = mapping.first;
+ mappings[j].size = mapping.second;
+ mappings[j].fileOffset = fileOffset;
+ j++;
+ }
+ caches[i].mappings = mappings;
+ caches[i].mappingCount = viewCaches[i].mappings.size();
+ }
+ }
+
+ return caches;
+ }
+
+ void BNDSCViewFreeBackingCaches(BNDSCBackingCache* caches, size_t count)
+ {
+ for (size_t i = 0; i < count; i++)
+ {
+ delete[] caches[i].mappings;
+ BNFreeString(caches[i].path);
+ }
+ delete[] caches;
+ }
+
+ void BNDSCFindSymbolAtAddressAndApplyToAddress(BNSharedCache* cache, uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis)
+ {
+ if (cache->object)
+ {
+ cache->object->FindSymbolAtAddrAndApplyToAddr(symbolLocation, targetLocation, triggerReanalysis);
+ }
+ }
+
+ BNDSCImage* BNDSCViewGetAllImages(BNSharedCache* cache, size_t* count)
+ {
+ if (cache->object)
+ {
+ auto vm = cache->object->GetVMMap(true);
+ auto viewImageHeaders = cache->object->AllImageHeaders();
+ *count = viewImageHeaders.size();
+ BNDSCImage* images = (BNDSCImage*)malloc(sizeof(BNDSCImage) * viewImageHeaders.size());
+ size_t i = 0;
+ for (const auto& [baseAddress, header] : viewImageHeaders)
+ {
+ images[i].name = BNAllocString(header.installName.c_str());
+ images[i].headerAddress = baseAddress;
+ images[i].mappingCount = header.sections.size();
+ images[i].mappings = (BNDSCImageMemoryMapping*)malloc(sizeof(BNDSCImageMemoryMapping) * 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());
+ images[i].mappings[j].filePath = BNAllocString(vm->MappingAtAddress(header.sections[j].addr).first.filePath.c_str());
+ }
+ i++;
+ }
+ return images;
+ }
+ *count = 0;
+ return nullptr;
+ }
+
+ void BNDSCViewFreeAllImages(BNDSCImage* 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);
+ BNFreeString(images[i].mappings[j].filePath);
+ }
+ delete[] images[i].mappings;
+ BNFreeString(images[i].name);
+ }
+ delete[] images;
+ }
+
+ char* BNDSCViewGetImageHeaderForAddress(BNSharedCache* cache, uint64_t address)
+ {
+ if (cache->object)
+ {
+ auto header = cache->object->SerializedImageHeaderForAddress(address);
+ return BNAllocString(header.c_str());
+ }
+
+ return nullptr;
+ }
+
+ char* BNDSCViewGetImageHeaderForName(BNSharedCache* cache, char* name)
+ {
+ std::string imageName = std::string(name);
+ BNFreeString(name);
+ if (cache->object)
+ {
+ auto header = cache->object->SerializedImageHeaderForName(imageName);
+ return BNAllocString(header.c_str());
+ }
+
+ return nullptr;
+ }
+
+ BNDSCMemoryUsageInfo BNDSCViewGetMemoryUsageInfo()
+ {
+ BNDSCMemoryUsageInfo info;
+ info.mmapRefs = mmapCount.load();
+ info.sharedCacheRefs = sharedCacheReferences.load();
+ return info;
+ }
+
+ BNDSCViewLoadProgress BNDSCViewGetLoadProgress(uint64_t sessionID)
+ {
+ progressMutex.lock();
+ if (progressMap.find(sessionID) == progressMap.end())
+ {
+ progressMutex.unlock();
+ return LoadProgressNotStarted;
+ }
+ auto progress = progressMap[sessionID];
+ progressMutex.unlock();
+ return progress;
+ }
+
+ uint64_t BNDSCViewFastGetBackingCacheCount(BNBinaryView* data)
+ {
+ Ref<BinaryView> view = new BinaryView(BNNewViewReference(data));
+ return SharedCache::FastGetBackingCacheCount(view);
+ }
+}
+
+[[maybe_unused]] DSCViewType* g_dscViewType;
+[[maybe_unused]] DSCRawViewType* g_dscRawViewType;
+
+void InitDSCViewType()
+{
+ MMappedFileAccessor::InitialVMSetup();
+ std::atexit(VMShutdown);
+
+ static DSCRawViewType rawType;
+ BinaryViewType::Register(&rawType);
+ static DSCViewType type;
+ BinaryViewType::Register(&type);
+ g_dscViewType = &type;
+ g_dscRawViewType = &rawType;
+}