diff --git a/objectivec/objc.cpp b/objectivec/objc.cpp index f650500a3a..0879fb3da2 100644 --- a/objectivec/objc.cpp +++ b/objectivec/objc.cpp @@ -581,13 +581,26 @@ void ObjCProcessor::LoadCategories(ObjCReader* reader, Ref
classPtrSect categoryBaseClassName = it->second.name; category.associatedName = it->second.associatedName; } - else if (auto symbol = m_data->GetSymbolByAddress(catLocation + m_data->GetAddressSize())) - { - if (symbol->GetType() == ImportedDataSymbol || symbol->GetType() == ImportAddressSymbol) + else + { + auto symbol = m_data->GetSymbolByAddress(cat.cls); + if (!symbol) + symbol = SymbolForUnmappedAddress(cat.cls); + + if (symbol) { - const auto& symbolName = symbol->GetFullName(); - if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0) - categoryBaseClassName = symbolName.substr(14, symbolName.size() - 14); + if (symbol->GetType() == ImportedDataSymbol || symbol->GetType() == ImportAddressSymbol || symbol->GetType() == DataSymbol) + { + // Symbols named `_OBJC_CLASS_$_` are references to external classes. + // Symbols named `cls_` are classes defined in a loaded image other than + // the image currently being analyzed. Classes from the current image + // are found via `m_classes`. + const std::string_view symbolName = symbol->GetFullNameRef(); + if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0) + categoryBaseClassName = symbolName.substr(14); + else if (symbolName.size() > 4 && symbolName.rfind("cls_", 0) == 0) + categoryBaseClassName = symbolName.substr(4); + } } } if (categoryBaseClassName.empty()) @@ -1247,6 +1260,11 @@ uint64_t ObjCProcessor::GetObjCRelativeMethodBaseAddress(ObjCReader* reader) return 0; } +Ref ObjCProcessor::SymbolForUnmappedAddress(uint64_t address) +{ + return nullptr; +} + void ObjCProcessor::ProcessObjCData(std::optional imageName) { m_symbolQueue = new SymbolQueue(); diff --git a/objectivec/objc.h b/objectivec/objc.h index 754d8d98c9..2fbe6cbfd8 100644 --- a/objectivec/objc.h +++ b/objectivec/objc.h @@ -324,6 +324,7 @@ namespace BinaryNinja { virtual uint64_t GetObjCRelativeMethodBaseAddress(ObjCReader* reader); virtual void GetRelativeMethod(ObjCReader* reader, method_t& meth); virtual std::shared_ptr GetReader() = 0; + virtual Ref SymbolForUnmappedAddress(uint64_t address); public: virtual ~ObjCProcessor() = default; diff --git a/view/sharedcache/core/ObjC.cpp b/view/sharedcache/core/ObjC.cpp index bb4835422d..55d5db2cf1 100644 --- a/view/sharedcache/core/ObjC.cpp +++ b/view/sharedcache/core/ObjC.cpp @@ -148,6 +148,26 @@ uint64_t SharedCacheObjCProcessor::GetObjCRelativeMethodBaseAddress(ObjCReader* return m_customRelativeMethodSelectorBase.value_or(0); } +Ref SharedCacheObjCProcessor::SymbolForUnmappedAddress(uint64_t address) +{ + if (const auto symbol = m_data->GetSymbolByAddress(address)) + return nullptr; + + const auto controller = DSC::SharedCacheController::FromView(*m_data); + if (!controller) + return nullptr; + + // No existing symbol located, try and search through the symbols of the cache. + auto cacheSymbol = controller->GetCache().GetSymbolAt(address); + if (!cacheSymbol.has_value()) + return nullptr; + + // Define the new symbol! + Ref symbol(new Symbol(cacheSymbol->type, cacheSymbol->name, address)); + m_data->DefineAutoSymbol(symbol); + return symbol; +} + SharedCacheObjCProcessor::SharedCacheObjCProcessor(BinaryView* data, bool isBackedByDatabase) : ObjCProcessor(data, "SharedCache.ObjC", isBackedByDatabase, true) {} diff --git a/view/sharedcache/core/ObjC.h b/view/sharedcache/core/ObjC.h index 6576564b58..24f79aae80 100644 --- a/view/sharedcache/core/ObjC.h +++ b/view/sharedcache/core/ObjC.h @@ -63,6 +63,8 @@ namespace DSCObjC { void GetRelativeMethod(BinaryNinja::ObjCReader* reader, BinaryNinja::method_t& meth) override; + BinaryNinja::Ref SymbolForUnmappedAddress(uint64_t address) override; + public: SharedCacheObjCProcessor(BinaryNinja::BinaryView* data, bool isBackedByDatabase);