summaryrefslogtreecommitdiff
path: root/view
diff options
context:
space:
mode:
authorMark Rowe <mark@vector35.com>2025-09-12 10:01:37 -0700
committerMark Rowe <mark@vector35.com>2025-09-17 20:45:26 -0700
commit11dc8b216d0852c83ff5c9bfd7d3ca146a93fe84 (patch)
tree2ba043a08032a73ad53b762716ada0cd64f84608 /view
parentbdb62855f9b6cb85fcf64dba0a067d9056f09af5 (diff)
[DSC] Support relative direct selectors in older shared cache versions
Prior to macOS 13 / iOS 16, the base offset to use for relative direct selector references within Objective-C message lists was stored within the `__TEXT,__objc_opt_ro` section of /usr/lib/libobjc.A.dylib.
Diffstat (limited to 'view')
-rw-r--r--view/sharedcache/core/ObjC.cpp37
-rw-r--r--view/sharedcache/core/ObjC.h16
2 files changed, 52 insertions, 1 deletions
diff --git a/view/sharedcache/core/ObjC.cpp b/view/sharedcache/core/ObjC.cpp
index b6de8ba6..b411ed87 100644
--- a/view/sharedcache/core/ObjC.cpp
+++ b/view/sharedcache/core/ObjC.cpp
@@ -136,17 +136,52 @@ std::optional<ObjCOptimizationHeader> GetObjCOptimizationHeader(SharedCache& cac
return header;
}
+std::optional<std::pair<uint64_t, LegacyObjCOptimizationHeader>> GetLegacyObjCOptimizationHeader(SharedCache& cache, VirtualMemoryReader& reader)
+{
+ // In older versions the header lives in the `__TEXT,__objc_opt_ro` section within /usr/lib/libobjc.A.dylib
+ auto libObjC = cache.GetImageWithName("/usr/lib/libobjc.A.dylib");
+ if (!libObjC)
+ return std::nullopt;
+
+ // Convert the header's `char[16]` to a `string_view`.
+ auto AsStringView = []<size_t N>(const char (&arr)[N]) {
+ const char* end = std::find(arr, arr + N, '\0');
+ return std::string_view(arr, end - arr);
+ };
+
+ for (auto section : libObjC->header->sections) {
+ if (AsStringView(section.segname) != "__TEXT" || AsStringView(section.sectname) != "__objc_opt_ro")
+ continue;
+
+ LegacyObjCOptimizationHeader header = {};
+ reader.Read(&header, section.addr, sizeof(LegacyObjCOptimizationHeader));
+
+ // The `relativeMethodSelectorBaseAddressOffset` field was added in version 16 (the final version of this struct).
+ if (header.version >= 16)
+ return {{section.addr, header}};
+
+ break;
+ }
+
+ return std::nullopt;
+}
+
uint64_t SharedCacheObjCProcessor::GetObjCRelativeMethodBaseAddress(ObjCReader* reader)
{
// Try and retrieve the base address of the selector stuff.
if (const auto controller = DSC::SharedCacheController::FromView(*m_data))
{
- auto baseAddress = controller->GetCache().GetBaseAddress();
auto dangerReader = dynamic_cast<SharedCacheObjCReader*>(reader)->GetVMReader();
if (const auto header = GetObjCOptimizationHeader(controller->GetCache(), dangerReader); header.has_value())
{
+ auto baseAddress = controller->GetCache().GetBaseAddress();
m_customRelativeMethodSelectorBase = baseAddress + header->relativeMethodSelectorBaseAddressOffset;
}
+ else if (const auto info = GetLegacyObjCOptimizationHeader(controller->GetCache(), dangerReader); info.has_value())
+ {
+ const auto [optSectionAddr, header] = *info;
+ m_customRelativeMethodSelectorBase = optSectionAddr + header.relativeMethodSelectorBaseAddressOffset;
+ }
}
return m_customRelativeMethodSelectorBase.value_or(0);
diff --git a/view/sharedcache/core/ObjC.h b/view/sharedcache/core/ObjC.h
index a30b9de4..e8c47e70 100644
--- a/view/sharedcache/core/ObjC.h
+++ b/view/sharedcache/core/ObjC.h
@@ -16,6 +16,22 @@ struct ObjCOptimizationHeader
uint64_t relativeMethodSelectorBaseAddressOffset;
};
+// `objc_opt_t` from dyld/include/objc-shared-cache.h
+struct LegacyObjCOptimizationHeader
+{
+ uint32_t version;
+ uint32_t flags;
+ int32_t selopt_offset;
+ int32_t headeropt_ro_offset;
+ int32_t unused_clsopt_offset;
+ int32_t unused_protocolopt_offset;
+ int32_t headeropt_rw_offset;
+ int32_t unused_protocolopt2_offset;
+ int32_t largeSharedCachesClassOffset;
+ int32_t largeSharedCachesProtocolOffset;
+ int64_t relativeMethodSelectorBaseAddressOffset;
+};
+
namespace DSCObjC {
class SharedCacheObjCReader : public BinaryNinja::ObjCReader
{