Skip to content

Commit e937b89

Browse files
committed
[DSC] Rework handling of .symbols files to be compatible with iOS 15
In some iOS 15 caches, the .symbols file's mapping has an address of 0. This would cause it to be returned by `SharedCache::GetEntryContaining` and loaded into the view. The .symbols file contains the local symbol tables for images in the shared cache. It is not intended to be mapped into the same address space as the rest of the shared cache. `SharedCache` now tracks the symbols cache entry separately from other entries. A dedicated `VirtualMemory` region is used when accessing the data it contains. This could be a `FileAccessor`, but that would require additional changes within `SharedCacheMachOHeader`. `SharedCacheMachOProcessor` now directly accesses the local symbols cache entry rather than needing to search for it.
1 parent 11dc8b2 commit e937b89

File tree

4 files changed

+69
-42
lines changed

4 files changed

+69
-42
lines changed

view/sharedcache/core/MachOProcessor.cpp

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -84,50 +84,58 @@ void SharedCacheMachOProcessor::ApplyHeader(const SharedCache& cache, SharedCach
8484
}
8585

8686
// Apply symbols from the .symbols cache files.
87-
for (const auto &entry: cache.GetEntries())
88-
{
89-
// NOTE: We check addr size as we only support 64bit .symbols files currently.
90-
if (entry.GetType() != CacheEntryType::Symbols && m_vm->GetAddressSize() == 8)
91-
continue;
92-
const auto& entryHeader = entry.GetHeader();
87+
ApplyUnmappedLocalSymbols(cache, header, std::move(typeLib));
88+
}
9389

94-
// This is where we get the symbol and string table information from in the .symbols file.
95-
dyld_cache_local_symbols_info localSymbolsInfo = {};
96-
auto localSymbolsInfoAddr = entry.GetMappedAddress(entryHeader.localSymbolsOffset);
97-
if (!localSymbolsInfoAddr.has_value())
98-
continue;
99-
m_vm->Read(&localSymbolsInfo, *localSymbolsInfoAddr, sizeof(dyld_cache_local_symbols_info));
90+
void SharedCacheMachOProcessor::ApplyUnmappedLocalSymbols(const SharedCache& cache, const SharedCacheMachOHeader& header, Ref<TypeLibrary> typeLib)
91+
{
92+
const auto& localSymbolsCacheEntry = cache.GetLocalSymbolsEntry();
93+
auto localSymbolsVM = cache.GetLocalSymbolsVM();
94+
if (!localSymbolsCacheEntry || !localSymbolsVM)
95+
return;
96+
97+
// NOTE: We check addr size as we only support 64bit .symbols files currently.
98+
// TODO: Support 32-bit nlist
99+
if (localSymbolsVM->GetAddressSize() != 8)
100+
return;
101+
102+
const auto& entryHeader = localSymbolsCacheEntry->GetHeader();
103+
104+
// This is where we get the symbol and string table information from in the .symbols file.
105+
dyld_cache_local_symbols_info localSymbolsInfo = {};
106+
auto localSymbolsInfoAddr = entryHeader.localSymbolsOffset;
107+
108+
localSymbolsVM->Read(&localSymbolsInfo, localSymbolsInfoAddr, sizeof(dyld_cache_local_symbols_info));
100109

101-
// Read each symbols entry, looking for the current image entry.
102-
uint64_t localEntriesAddr = *localSymbolsInfoAddr + localSymbolsInfo.entriesOffset;
103-
uint64_t localSymbolsAddr = *localSymbolsInfoAddr + localSymbolsInfo.nlistOffset;
104-
uint64_t localStringsAddr = *localSymbolsInfoAddr + localSymbolsInfo.stringsOffset;
110+
// Read each symbols entry, looking for the current image entry.
111+
uint64_t localEntriesAddr = localSymbolsInfoAddr + localSymbolsInfo.entriesOffset;
112+
uint64_t localSymbolsAddr = localSymbolsInfoAddr + localSymbolsInfo.nlistOffset;
113+
uint64_t localStringsAddr = localSymbolsInfoAddr + localSymbolsInfo.stringsOffset;
105114

115+
for (uint32_t i = 0; i < localSymbolsInfo.entriesCount; i++)
116+
{
106117
dyld_cache_local_symbols_entry_64 localSymbolsEntry = {};
107-
for (uint32_t i = 0; i < localSymbolsInfo.entriesCount; i++)
118+
localSymbolsVM->Read(&localSymbolsEntry, localEntriesAddr + i * sizeof(dyld_cache_local_symbols_entry_64),
119+
sizeof(dyld_cache_local_symbols_entry_64));
120+
121+
// The dylibOffset is the offset from the cache base address to the image header.
122+
const auto imageAddr = cache.GetBaseAddress() + localSymbolsEntry.dylibOffset;
123+
if (imageAddr != header.textBase)
124+
continue;
125+
126+
// We have found the entry to read!
127+
uint64_t symbolTableStart = localSymbolsAddr + (localSymbolsEntry.nlistStartIndex * sizeof(nlist_64));
128+
TableInfo symbolInfo = {symbolTableStart, localSymbolsEntry.nlistCount};
129+
TableInfo stringInfo = {localStringsAddr, localSymbolsInfo.stringsSize};
130+
m_view->BeginBulkModifySymbols();
131+
const auto symbols = header.ReadSymbolTable(*localSymbolsVM, symbolInfo, stringInfo);
132+
for (const auto &sym: symbols)
108133
{
109-
m_vm->Read(&localSymbolsEntry, localEntriesAddr + i * sizeof(dyld_cache_local_symbols_entry_64),
110-
sizeof(dyld_cache_local_symbols_entry_64));
111-
// The dylibOffset is the offset from the cache base address to the image header.
112-
const auto imageAddr = cache.GetBaseAddress() + localSymbolsEntry.dylibOffset;
113-
if (imageAddr == header.textBase)
114-
{
115-
// We have found the entry to read!
116-
// TODO: Support 32bit nlist
117-
uint64_t symbolTableStart = localSymbolsAddr + (localSymbolsEntry.nlistStartIndex * sizeof(nlist_64));
118-
TableInfo symbolInfo = {symbolTableStart, localSymbolsEntry.nlistCount};
119-
TableInfo stringInfo = {localStringsAddr, localSymbolsInfo.stringsSize};
120-
m_view->BeginBulkModifySymbols();
121-
const auto symbols = header.ReadSymbolTable(*m_vm, symbolInfo, stringInfo);
122-
for (const auto &sym: symbols)
123-
{
124-
auto [symbol, symbolType] = sym.GetBNSymbolAndType(*m_view);
125-
ApplySymbol(m_view, typeLib, symbol, symbolType);
126-
}
127-
m_view->EndBulkModifySymbols();
128-
break;
129-
}
134+
auto [symbol, symbolType] = sym.GetBNSymbolAndType(*m_view);
135+
ApplySymbol(m_view, typeLib, std::move(symbol), std::move(symbolType));
130136
}
137+
m_view->EndBulkModifySymbols();
138+
return;
131139
}
132140
}
133141

view/sharedcache/core/MachOProcessor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ class SharedCacheMachOProcessor
2121
uint64_t ApplyHeaderSections(SharedCacheMachOHeader& header);
2222

2323
void ApplyHeaderDataVariables(SharedCacheMachOHeader& header);
24+
25+
void ApplyUnmappedLocalSymbols(const SharedCache& cache, const SharedCacheMachOHeader& header, BinaryNinja::Ref<BinaryNinja::TypeLibrary> typeLib);
2426
};

view/sharedcache/core/SharedCache.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,8 @@ CacheEntry CacheEntry::FromFile(const std::string& filePath, const std::string&
9191
{
9292
// We found a single symbols cache entry file. Mark it as such!
9393
type = CacheEntryType::Symbols;
94-
// Adjust the mapping for the symbol file, they seem to be only for the header.
95-
// If we do not adjust the mapping than we will not be able to read the symbol table through the virtual memory.
96-
mappings[0].fileOffset = 0;
97-
mappings[0].size = file->Length();
94+
// Symbol files are not mapped into the address space.
95+
mappings.clear();
9896
}
9997
else if (mappings.size() == 1 && header.imagesCountOld == 0 && header.imagesCount == 0
10098
&& header.imagesTextOffset == 0)
@@ -231,6 +229,17 @@ void SharedCache::AddEntry(CacheEntry entry)
231229
// Get the file accessor to associate with the virtual memory region.
232230
auto fileAccessor = FileAccessorCache::Global().Open(entry.GetFilePath());
233231

232+
if (entry.GetType() == CacheEntryType::Symbols)
233+
{
234+
m_localSymbolsEntry = std::move(entry);
235+
// Map the entire file into its own virtual memory space.
236+
// This is necessary due to code that processes symbols being written in terms of a `VirtualMemory`
237+
// rather than something more generic.
238+
m_localSymbolsVM = std::make_shared<VirtualMemory>(m_vm->GetAddressSize());
239+
m_localSymbolsVM->MapRegion(fileAccessor, {0, fileAccessor.lock()->Length()}, 0);
240+
return;
241+
}
242+
234243
// Populate virtual memory using the entry mappings, by doing so we can now
235244
// read the memory of the mapped regions of the cache entry file.
236245
const auto& mappings = entry.GetMappings();

view/sharedcache/core/SharedCache.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ class SharedCache
188188
// NOTE: Wrapped in unique_ptr to keep SharedCache movable.
189189
std::unique_ptr<std::shared_mutex> m_namedSymMutex;
190190

191+
// Local symbols entry and its mapping, used to read symbol tables from the .symbols file.
192+
// These are handled separately as they are not mapped into the main virtual memory of the cache.
193+
std::optional<CacheEntry> m_localSymbolsEntry;
194+
std::shared_ptr<VirtualMemory> m_localSymbolsVM;
195+
191196
bool ProcessEntryImage(const std::string& path, const dyld_cache_image_info& info);
192197

193198
// Add a region known not to overlap with another, otherwise use AddRegion.
@@ -211,6 +216,9 @@ class SharedCache
211216
const std::unordered_map<uint64_t, CacheImage>& GetImages() const { return m_images; }
212217
const std::unordered_map<uint64_t, CacheSymbol>& GetSymbols() const { return m_symbols; }
213218

219+
const std::optional<CacheEntry>& GetLocalSymbolsEntry() const { return m_localSymbolsEntry; }
220+
std::shared_ptr<VirtualMemory> GetLocalSymbolsVM() const { return m_localSymbolsVM; }
221+
214222
void AddImage(CacheImage&& image);
215223

216224
// Add a region that may overlap with another.

0 commit comments

Comments
 (0)