summaryrefslogtreecommitdiff
path: root/view/sharedcache/core/SlideInfo.cpp
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-03-10 11:05:40 -0400
committerMason Reed <mason@vector35.com>2025-04-02 05:36:54 -0400
commit25cc02431b61097b2adfc2fbc493b648b0300c3b (patch)
treea79d9c4f4f67234d3bf9bda413e8608f479a4cc8 /view/sharedcache/core/SlideInfo.cpp
parentfa85bf28502286c4821427c5d0ed91a7ed46f8f6 (diff)
[SharedCache] Refactor Shared Cache
In absence of a better name, this commit refactors the shared cache code.
Diffstat (limited to 'view/sharedcache/core/SlideInfo.cpp')
-rw-r--r--view/sharedcache/core/SlideInfo.cpp292
1 files changed, 292 insertions, 0 deletions
diff --git a/view/sharedcache/core/SlideInfo.cpp b/view/sharedcache/core/SlideInfo.cpp
new file mode 100644
index 00000000..e258a90c
--- /dev/null
+++ b/view/sharedcache/core/SlideInfo.cpp
@@ -0,0 +1,292 @@
+#include "SlideInfo.h"
+
+#include "Dyld.h"
+#include "SharedCache.h"
+#include "Utility.h"
+
+SlideInfoProcessor::SlideInfoProcessor(uint64_t baseAddress)
+{
+ m_logger = new BinaryNinja::Logger("SlideInfoProcessor");
+ m_baseAddress = baseAddress;
+}
+
+void ApplySlideInfoV5(VirtualMemory& vm, const SlideMappingInfo& mapping)
+{
+ uint64_t pageStartsOffset = mapping.address + sizeof(dyld_cache_slide_info_v5);
+ uint64_t pageStartCount = mapping.slideInfoV5.page_starts_count;
+ uint64_t pageSize = mapping.slideInfoV5.page_size;
+
+ uint64_t offset;
+ // Retrieve this once so we don't need to keep querying the region through the VM.
+ auto region = vm.GetRegionAtAddress(mapping.address, offset);
+ if (!region.has_value())
+ throw UnmappedRegionException(mapping.address);
+
+ auto cursor = pageStartsOffset;
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ uint16_t delta = vm.ReadUInt16(cursor);
+ cursor += sizeof(uint16_t);
+ if (delta == DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE)
+ continue;
+
+ delta = delta / sizeof(uint64_t); // initial offset is byte based
+ uint64_t loc = mapping.mappingInfo.fileOffset + (pageSize * i);
+ do
+ {
+ loc += delta * sizeof(dyld_cache_slide_pointer5);
+ dyld_cache_slide_pointer5 slideInfo = {vm.ReadUInt64(loc)};
+ delta = slideInfo.regular.next;
+ if (slideInfo.auth.auth)
+ {
+ uint64_t value = mapping.slideInfoV5.value_add + slideInfo.auth.runtimeOffset;
+ vm.WritePointer(loc, value);
+ }
+ else
+ {
+ uint64_t value = mapping.slideInfoV5.value_add + slideInfo.regular.runtimeOffset;
+ vm.WritePointer(loc, value);
+ }
+ } while (delta != 0);
+ }
+}
+
+void ApplySlideInfoV3(VirtualMemory& vm, const SlideMappingInfo& mapping)
+{
+ uint64_t pageStartsOffset = mapping.address + sizeof(dyld_cache_slide_info_v3);
+ uint64_t pageStartCount = mapping.slideInfoV3.page_starts_count;
+ uint64_t pageSize = mapping.slideInfoV3.page_size;
+
+ auto cursor = pageStartsOffset;
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ uint16_t delta = vm.ReadUInt16(cursor);
+ cursor += sizeof(uint16_t);
+ if (delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE)
+ continue;
+
+ delta = delta / sizeof(uint64_t); // initial offset is byte based
+ uint64_t loc = mapping.mappingInfo.fileOffset + (pageSize * i);
+ do
+ {
+ loc += delta * sizeof(dyld_cache_slide_pointer3);
+ dyld_cache_slide_pointer3 slideInfo = {vm.ReadUInt64(loc)};
+ delta = slideInfo.plain.offsetToNextPointer;
+
+ if (slideInfo.auth.authenticated)
+ {
+ uint64_t value = slideInfo.auth.offsetFromSharedCacheBase;
+ value += mapping.slideInfoV3.auth_value_add;
+ vm.WritePointer(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;
+ vm.WritePointer(loc, value);
+ }
+ } while (delta != 0);
+ }
+}
+
+void ApplySlideInfoV2(VirtualMemory& vm, const SlideMappingInfo& mapping)
+{
+ auto rebaseChain = [&](const dyld_cache_slide_info_v2& slideInfo, uint64_t pageContent, uint16_t startOffset) {
+ // TODO: This is always zero?
+ // TODO: This is probably something for runtime offsets provided to the shared cache.
+ uintptr_t slideAmount = 0;
+
+ auto deltaMask = slideInfo.delta_mask;
+ auto valueMask = ~deltaMask;
+ auto valueAdd = slideInfo.value_add;
+
+ auto deltaShift = CountTrailingZeros(deltaMask) - 2;
+
+ uint32_t pageOffset = startOffset;
+ uint32_t delta = 1;
+ while (delta != 0)
+ {
+ uint64_t loc = pageContent + pageOffset;
+ uintptr_t rawValue = vm.ReadUInt64(loc);
+ delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
+ uintptr_t value = (rawValue & valueMask);
+ if (value != 0)
+ {
+ value += valueAdd;
+ // TODO: slideAmount += value?
+ // TODO: slideAmount is always zero? What is this suppose to do?
+ value += slideAmount;
+ }
+ pageOffset += delta;
+ vm.WritePointer(loc, value);
+ }
+ };
+
+ uint64_t extrasOffset = mapping.address + mapping.slideInfoV2.page_extras_offset;
+ uint64_t pageStartsOffset = mapping.address + sizeof(dyld_cache_slide_info_v2);
+ uint64_t pageStartCount = mapping.slideInfoV2.page_starts_count;
+ uint64_t pageSize = mapping.slideInfoV2.page_size;
+
+ auto cursor = pageStartsOffset;
+ for (size_t i = 0; i < pageStartCount; i++)
+ {
+ uint16_t start = vm.ReadUInt16(cursor);
+ cursor += sizeof(uint16_t);
+ if (start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE)
+ continue;
+
+ if (start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA)
+ {
+ int j = (start & 0x3FFF);
+ bool done = false;
+ do
+ {
+ uint64_t extraCursor = extrasOffset + (j * sizeof(uint16_t));
+ auto extra = vm.ReadUInt16(extraCursor);
+ uint16_t aStart = extra;
+ uint64_t page = mapping.mappingInfo.fileOffset + (pageSize * i);
+ uint16_t pageStartOffset = (aStart & 0x3FFF) * 4;
+ rebaseChain(mapping.slideInfoV2, page, pageStartOffset);
+ done = (extra & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
+ ++j;
+ } while (!done);
+ }
+ else
+ {
+ uint64_t page = mapping.mappingInfo.fileOffset + (pageSize * i);
+ uint16_t pageStartOffset = start * 4;
+ rebaseChain(mapping.slideInfoV2, page, pageStartOffset);
+ }
+ }
+}
+
+std::vector<SlideMappingInfo> SlideInfoProcessor::ReadEntryInfo(VirtualMemory& vm, const CacheEntry& entry) const
+{
+ auto& baseHeader = entry.GetHeader();
+
+ // Handle legacy, single mapping slide info.
+ if (baseHeader.slideInfoOffsetUnused)
+ {
+ auto slideInfoAddress = entry.GetMappedAddress(baseHeader.slideInfoOffsetUnused);
+ if (!slideInfoAddress.has_value())
+ {
+ m_logger->LogError("Unmapped slide info address %llx", slideInfoAddress);
+ return {};
+ }
+ auto slideInfoVersion = vm.ReadUInt32(*slideInfoAddress);
+ if (slideInfoVersion != 2 && slideInfoVersion != 3)
+ {
+ m_logger->LogError("Unsupported slide info version %d", slideInfoVersion);
+ return {};
+ }
+
+ SlideMappingInfo singleMapping = {};
+ singleMapping.address = *slideInfoAddress;
+
+ auto mappingAddress = entry.GetMappedAddress(baseHeader.mappingOffset + sizeof(dyld_cache_mapping_info));
+ if (!mappingAddress.has_value())
+ {
+ m_logger->LogError("Unmapped mapping address %llx", mappingAddress);
+ return {};
+ }
+ vm.Read(&singleMapping.mappingInfo, *mappingAddress, sizeof(dyld_cache_mapping_info));
+ singleMapping.slideInfoVersion = slideInfoVersion;
+ if (singleMapping.slideInfoVersion == 2)
+ vm.Read(&singleMapping.slideInfoV2, *slideInfoAddress, sizeof(dyld_cache_slide_info_v2));
+ else if (singleMapping.slideInfoVersion == 3)
+ vm.Read(&singleMapping.slideInfoV3, *slideInfoAddress, sizeof(dyld_cache_slide_info_v3));
+
+ return {singleMapping};
+ }
+
+ std::vector<SlideMappingInfo> mappings = {};
+ for (auto i = 0; i < baseHeader.mappingWithSlideCount; i++)
+ {
+ dyld_cache_mapping_and_slide_info mappingAndSlideInfo = {};
+ auto mappingAndSlideInfoAddress =
+ entry.GetMappedAddress(baseHeader.mappingWithSlideOffset + (i * sizeof(dyld_cache_mapping_and_slide_info)));
+ if (!mappingAndSlideInfoAddress.has_value())
+ {
+ m_logger->LogError("Unmapped mapping and slide info address %llx", mappingAndSlideInfoAddress);
+ continue;
+ }
+
+ vm.Read(&mappingAndSlideInfo, *mappingAndSlideInfoAddress, sizeof(dyld_cache_mapping_and_slide_info));
+ if (mappingAndSlideInfo.size == 0 || mappingAndSlideInfo.slideInfoFileOffset == 0)
+ continue;
+
+ SlideMappingInfo map = {};
+ map.address = *entry.GetMappedAddress(mappingAndSlideInfo.slideInfoFileOffset);
+ map.slideInfoVersion = vm.ReadUInt32(map.address);
+ map.mappingInfo.address = mappingAndSlideInfo.address;
+ map.mappingInfo.size = mappingAndSlideInfo.size;
+ map.mappingInfo.fileOffset = *entry.GetMappedAddress(mappingAndSlideInfo.fileOffset);
+ if (map.slideInfoVersion == 2)
+ {
+ vm.Read(&map.slideInfoV2, map.address, sizeof(dyld_cache_slide_info_v2));
+ }
+ else if (map.slideInfoVersion == 3)
+ {
+ vm.Read(&map.slideInfoV3, map.address, sizeof(dyld_cache_slide_info_v3));
+ map.slideInfoV3.auth_value_add = m_baseAddress;
+ }
+ else if (map.slideInfoVersion == 5)
+ {
+ vm.Read(&map.slideInfoV5, map.address, sizeof(dyld_cache_slide_info_v5));
+ map.slideInfoV5.value_add = m_baseAddress;
+ }
+ else
+ {
+ m_logger->LogError("Unknown slide info version: %d", map.slideInfoVersion);
+ continue;
+ }
+
+ mappings.emplace_back(map);
+ m_logger->LogDebug("File: %s", entry.GetFilePath().c_str());
+ m_logger->LogDebug("Slide Info Address: 0x%llx", map.address);
+ m_logger->LogDebug("Mapping Address: 0x%llx", map.mappingInfo.address);
+ m_logger->LogDebug("Slide Info Version: %d", map.slideInfoVersion);
+ }
+
+ return mappings;
+}
+
+void SlideInfoProcessor::ApplyMappings(VirtualMemory& vm, const std::vector<SlideMappingInfo>& mappings)
+{
+ // Apply the slide information to the mapped file.
+ for (const auto& mapping : mappings)
+ {
+ switch (mapping.slideInfoVersion)
+ {
+ case 2:
+ ApplySlideInfoV2(vm, mapping);
+ break;
+ case 3:
+ ApplySlideInfoV3(vm, mapping);
+ break;
+ case 5:
+ ApplySlideInfoV5(vm, mapping);
+ break;
+ default:
+ m_logger->LogError(
+ "Cannot apply slide info version: %d @ %llx", mapping.slideInfoVersion, mapping.mappingInfo.address);
+ break;
+ }
+ }
+}
+
+void SlideInfoProcessor::ProcessEntry(VirtualMemory& vm, const CacheEntry& entry)
+{
+ try
+ {
+ ApplyMappings(vm, ReadEntryInfo(vm, entry));
+ }
+ catch (const std::exception& e)
+ {
+ // Just log an error, we technically can continue as if the slide info is not applied for a given entry it does
+ // not necessarily mean we cannot do analysis on others.
+ m_logger->LogError("Error processing slide info for entry `%s`: %s", entry.GetFileName().c_str(), e.what());
+ }
+}