Skip to content

Commit 9b3e31d

Browse files
committed
[ObjC] Fix another case where a category's class name was not being resolved
Handle the case where the `class` field of the category is a direct reference to an external symbol via a bind fixup. In that case, the pointer's value when read from the view is 0. To determine the class name it is necessary to look up the relocation for the pointer's address and parse the class name from the symbol's name.
1 parent ec3d735 commit 9b3e31d

File tree

2 files changed

+64
-31
lines changed

2 files changed

+64
-31
lines changed

objectivec/objc.cpp

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,33 @@
11
#include "objc.h"
22
#include "inttypes.h"
3+
#include <optional>
34

45
using namespace BinaryNinja;
56

7+
namespace {
8+
9+
// Attempt to recover an Objective-C class name from the symbol's name.
10+
// Note: classes defined in the current image should be looked up in m_classes
11+
// rather than using this function.
12+
std::optional<std::string> ClassNameFromSymbolName(const Ref<Symbol>& symbol)
13+
{
14+
std::string_view symbolName = symbol->GetFullNameRef();
15+
16+
// Symbols named `_OBJC_CLASS_$_` are references to external classes.
17+
if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0)
18+
return std::string(symbolName.substr(14));
19+
20+
// Symbols named `cls_` are classes defined in a loaded image other than
21+
// the image currently being analyzed.
22+
if (symbolName.size() > 4 && symbolName.rfind("cls_", 0) == 0)
23+
return std::string(symbolName.substr(4));
24+
25+
return std::nullopt;
26+
}
27+
28+
} // namespace
29+
30+
631
Ref<Metadata> ObjCProcessor::SerializeMethod(uint64_t loc, const Method& method)
732
{
833
std::map<std::string, Ref<Metadata>> methodMeta;
@@ -535,6 +560,39 @@ void ObjCProcessor::LoadClasses(ObjCReader* reader, Ref<Section> classPtrSection
535560
}
536561
}
537562

563+
std::optional<std::string> ObjCProcessor::ClassNameForTargetOfPointerAt(ObjCReader* reader, uint64_t offset)
564+
{
565+
auto savedOffset = reader->GetOffset();
566+
reader->Seek(offset);
567+
auto target = ReadPointerAccountingForRelocations(reader);
568+
reader->Seek(savedOffset);
569+
570+
if (target) {
571+
// Classes defined in the current image must be looked up in m_classes
572+
// as adding their symbol may be deferred.
573+
if (auto it = m_classes.find(target); it != m_classes.end())
574+
return it->second.name;
575+
576+
// Classes defined in other images are looked up by their symbol name.
577+
// This is common for cross-image references in the shared cache.
578+
if (auto symbol = GetSymbol(target))
579+
{
580+
if (auto className = ClassNameFromSymbolName(symbol))
581+
return *className;
582+
}
583+
}
584+
585+
// If there's no target, or we can't find a symbol for it, check whether the pointer has a relocation
586+
// that contains a symbol. This is the case for cross-image references outside of the shared cache.
587+
for (const auto& relocation : m_data->GetRelocationsAt(offset))
588+
{
589+
if (auto symbol = relocation->GetSymbol())
590+
return ClassNameFromSymbolName(symbol);
591+
}
592+
593+
return std::nullopt;
594+
}
595+
538596
void ObjCProcessor::LoadCategories(ObjCReader* reader, Ref<Section> classPtrSection)
539597
{
540598
if (!classPtrSection)
@@ -574,29 +632,9 @@ void ObjCProcessor::LoadCategories(ObjCReader* reader, Ref<Section> classPtrSect
574632
}
575633

576634
std::string categoryAdditionsName;
577-
std::string categoryBaseClassName;
635+
std::string categoryBaseClassName =
636+
ClassNameForTargetOfPointerAt(reader, catLocation + ptrSize).value_or(std::string());
578637

579-
if (const auto& it = m_classes.find(cat.cls); it != m_classes.end())
580-
{
581-
categoryBaseClassName = it->second.name;
582-
category.associatedName = it->second.associatedName;
583-
}
584-
else if (const auto symbol = GetSymbol(cat.cls))
585-
{
586-
if (symbol->GetType() == ImportedDataSymbol || symbol->GetType() == ImportAddressSymbol
587-
|| symbol->GetType() == DataSymbol || symbol->GetType() == ExternalSymbol)
588-
{
589-
// Symbols named `_OBJC_CLASS_$_` are references to external classes.
590-
// Symbols named `cls_` are classes defined in a loaded image other than
591-
// the image currently being analyzed. Classes from the current image
592-
// are found via `m_classes`.
593-
const std::string_view symbolName = symbol->GetFullNameRef();
594-
if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0)
595-
categoryBaseClassName = symbolName.substr(14);
596-
else if (symbolName.size() > 4 && symbolName.rfind("cls_", 0) == 0)
597-
categoryBaseClassName = symbolName.substr(4);
598-
}
599-
}
600638
if (categoryBaseClassName.empty())
601639
{
602640
m_logger->LogInfo("Using base address as stand-in classname for category at 0x%llx", catLocation);
@@ -1171,15 +1209,8 @@ void ObjCProcessor::PostProcessObjCSections(ObjCReader* reader)
11711209
auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
11721210
for (view_ptr_t i = start; i < end; i += ptrSize)
11731211
{
1174-
reader->Seek(i);
1175-
auto clsLoc = ReadPointerAccountingForRelocations(reader);
1176-
if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
1177-
{
1178-
auto& cls = it->second;
1179-
std::string name = cls.name;
1180-
if (!name.empty())
1181-
DefineObjCSymbol(DataSymbol, type, "clsRef_" + name, i, true);
1182-
}
1212+
if (auto className = ClassNameForTargetOfPointerAt(reader, i))
1213+
DefineObjCSymbol(DataSymbol, type, "clsRef_" + *className, i, true);
11831214
}
11841215
}
11851216
if (auto superRefs = GetSectionWithName("__objc_superrefs"))

objectivec/objc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ namespace BinaryNinja {
314314
bool ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod);
315315
void ApplyMethodTypes(Class& cls);
316316

317+
std::optional<std::string> ClassNameForTargetOfPointerAt(ObjCReader* reader, uint64_t offset);
318+
317319
void PostProcessObjCSections(ObjCReader* reader);
318320

319321
protected:

0 commit comments

Comments
 (0)