summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWeiN76LQh <WeiN76LQh@github.com>2025-01-03 17:55:10 +0000
committerMason Reed <mason@vector35.com>2025-04-02 05:36:54 -0400
commitee500220baa1a46f3d7ca1486fa43dc1587f20d0 (patch)
treeee3e1549997e60849f040f5745cdac8840f25303
parent90e9ff91f02f2cb902bf0564de1f403e23172f36 (diff)
[ObjC] Create a shared ObjC processor for Macho and DSC views
Both the Macho and DSC views need to process Objective-C but have separate processor classes. It would appear that the DSC version was largely a copy and paste of the Macho view one, with some modifications. The majority of code overlaps between the 2 so it doesn't make sense to maintain 2 and copy and paste improvements/fixes between them. This commit fixes that by creating a base Objective-C processor that contains the shared code. View specific code is implemented in the respective subclasses for the views. Although there is very little view specific code for each.
-rw-r--r--objectivec/objc.cpp1549
-rw-r--r--objectivec/objc.h337
-rw-r--r--view/macho/CMakeLists.txt4
-rw-r--r--view/macho/machoview.cpp10
-rw-r--r--view/macho/machoview.h2
-rw-r--r--view/macho/objc.cpp1485
-rw-r--r--view/macho/objc.h228
-rw-r--r--view/sharedcache/core/CMakeLists.txt7
-rw-r--r--view/sharedcache/core/ObjC.cpp1518
-rw-r--r--view/sharedcache/core/ObjC.h242
-rw-r--r--view/sharedcache/core/SharedCache.cpp4
11 files changed, 2032 insertions, 3354 deletions
diff --git a/objectivec/objc.cpp b/objectivec/objc.cpp
new file mode 100644
index 00000000..4267ae23
--- /dev/null
+++ b/objectivec/objc.cpp
@@ -0,0 +1,1549 @@
+#include "objc.h"
+#include "inttypes.h"
+
+using namespace BinaryNinja;
+
+Ref<Metadata> ObjCProcessor::SerializeMethod(uint64_t loc, const Method& method)
+{
+ std::map<std::string, Ref<Metadata>> methodMeta;
+
+ methodMeta["loc"] = new Metadata(loc);
+ methodMeta["name"] = new Metadata(method.name);
+ methodMeta["types"] = new Metadata(method.types);
+ methodMeta["imp"] = new Metadata(method.imp);
+
+ return new Metadata(methodMeta);
+}
+
+
+Ref<Metadata> ObjCProcessor::SerializeClass(uint64_t loc, const Class& cls)
+{
+ std::map<std::string, Ref<Metadata>> clsMeta;
+
+ clsMeta["loc"] = new Metadata(loc);
+ clsMeta["name"] = new Metadata(cls.name);
+ clsMeta["typeName"] = new Metadata(cls.associatedName.GetString());
+
+ std::vector<uint64_t> instanceMethods;
+ std::vector<uint64_t> classMethods;
+ instanceMethods.reserve(cls.instanceClass.methodList.size());
+ classMethods.reserve(cls.metaClass.methodList.size());
+ for (const auto& [location, _] : cls.instanceClass.methodList)
+ instanceMethods.push_back(location);
+
+ clsMeta["instanceMethods"] = new Metadata(instanceMethods);
+ clsMeta["classMethods"] = new Metadata(classMethods);
+
+ return new Metadata(clsMeta);
+}
+
+Ref<Metadata> ObjCProcessor::SerializeMetadata()
+{
+ std::map<std::string, Ref<Metadata>> viewMeta;
+ viewMeta["version"] = new Metadata((uint64_t)1);
+
+ std::vector<Ref<Metadata>> classes;
+ classes.reserve(m_classes.size());
+ std::vector<Ref<Metadata>> categories;
+ categories.reserve(m_categories.size());
+ std::vector<Ref<Metadata>> methods;
+ methods.reserve(m_localMethods.size());
+
+ for (const auto& [clsLoc, cls] : m_classes)
+ classes.push_back(SerializeClass(clsLoc, cls));
+ viewMeta["classes"] = new Metadata(classes);
+ for (const auto& [catLoc, cat] : m_categories)
+ categories.push_back(SerializeClass(catLoc, cat));
+ viewMeta["categories"] = new Metadata(categories);
+ for (const auto& [methodLoc, method] : m_localMethods)
+ methods.push_back(SerializeMethod(methodLoc, method));
+ viewMeta["methods"] = new Metadata(methods);
+
+ // Required for workflow_objc type guessing, should be removed when that is no longer a thing.
+ std::vector<Ref<Metadata>> selRefToImps;
+ selRefToImps.reserve(m_selRefToImplementations.size());
+ for (const auto& [selRef, imps] : m_selRefToImplementations)
+ {
+ std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(imps)};
+ Ref<Metadata> mapObject = new Metadata(mapBase);
+ selRefToImps.push_back(mapObject);
+ }
+ viewMeta["selRefImplementations"] = new Metadata(selRefToImps);
+
+ std::vector<Ref<Metadata>> selToImps;
+ selToImps.reserve(m_selToImplementations.size());
+ for (const auto& [selRef, imps] : m_selToImplementations)
+ {
+ std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(imps)};
+ Ref<Metadata> mapObject = new Metadata(mapBase);
+ selToImps.push_back(mapObject);
+ }
+ viewMeta["selImplementations"] = new Metadata(selToImps);
+
+ std::vector<Ref<Metadata>> selRefToName;
+ selRefToName.reserve(m_selRefToName.size());
+ for (const auto& [selRef, name] : m_selRefToName)
+ {
+ std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(name)};
+ Ref<Metadata> mapObject = new Metadata(mapBase);
+ selRefToName.push_back(mapObject);
+ }
+ viewMeta["selRefToName"] = new Metadata(selRefToName);
+ // ---
+
+
+ return new Metadata(viewMeta);
+}
+
+std::vector<QualifiedNameOrType> ObjCProcessor::ParseEncodedType(const std::string& encodedType)
+{
+ std::vector<QualifiedNameOrType> result;
+ int pointerDepth = 0;
+
+ bool readingNamedType = false;
+ std::string namedType;
+ int readingStructDepth = 0;
+ std::string structType;
+ char last;
+
+ for (char c : encodedType)
+ {
+ if (readingNamedType && c != '"')
+ {
+ namedType.push_back(c);
+ last = c;
+ continue;
+ }
+ else if (readingStructDepth > 0 && c != '{' && c != '}')
+ {
+ structType.push_back(c);
+ last = c;
+ continue;
+ }
+
+ if (std::isdigit(c))
+ continue;
+
+ QualifiedNameOrType nameOrType;
+ std::string qualifiedName;
+
+ switch (c)
+ {
+ case '^':
+ pointerDepth++;
+ last = c;
+ continue;
+
+ case '"':
+ if (!readingNamedType)
+ {
+ readingNamedType = true;
+ if (last == '@')
+ result.pop_back(); // We added an 'id' in the last cycle, remove it
+ last = c;
+ continue;
+ }
+ else
+ {
+ readingNamedType = false;
+ nameOrType.name = QualifiedName(namedType);
+ nameOrType.ptrCount = 1;
+ break;
+ }
+ case '{':
+ readingStructDepth++;
+ last = c;
+ continue;
+ case '}':
+ readingStructDepth--;
+ if (readingStructDepth < 0)
+ return {}; // seriously malformed type.
+
+ if (readingStructDepth == 0)
+ {
+ // TODO: Emit real struct types
+ nameOrType.type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
+ break;
+ }
+ last = c;
+ continue;
+ case 'v':
+ nameOrType.type = Type::VoidType();
+ break;
+ case 'c':
+ nameOrType.type = Type::IntegerType(1, true);
+ break;
+ case 'A':
+ case 'C':
+ nameOrType.type = Type::IntegerType(1, false);
+ break;
+ case 's':
+ nameOrType.type = Type::IntegerType(2, true);
+ break;
+ case 'S':
+ nameOrType.type = Type::IntegerType(1, false);
+ break;
+ case 'i':
+ nameOrType.type = Type::IntegerType(4, true);
+ break;
+ case 'I':
+ nameOrType.type = Type::IntegerType(4, false);
+ break;
+ case 'l':
+ nameOrType.type = Type::IntegerType(8, true);
+ break;
+ case 'L':
+ nameOrType.type = Type::IntegerType(8, true);
+ break;
+ case 'f':
+ nameOrType.type = Type::IntegerType(4, true);
+ break;
+ case 'b':
+ case 'B':
+ nameOrType.type = Type::BoolType();
+ break;
+ case 'q':
+ qualifiedName = "NSInteger";
+ break;
+ case 'Q':
+ qualifiedName = "NSUInteger";
+ break;
+ case 'd':
+ qualifiedName = "CGFloat";
+ break;
+ case '*':
+ nameOrType.type = Type::PointerType(m_data->GetAddressSize(), Type::IntegerType(1, true));
+ break;
+ case '@':
+ qualifiedName = "id";
+ // There can be a type after this, like @"NSString", that overrides this
+ // The handler for " will catch it and drop this "id" entry.
+ break;
+ case ':':
+ qualifiedName = "SEL";
+ break;
+ case '#':
+ qualifiedName = "objc_class_t";
+ break;
+ case '?':
+ case 'T':
+ nameOrType.type = Type::PointerType(8, Type::VoidType());
+ break;
+ default:
+ // BNLogWarn("Unknown type specifier %c", c);
+ last = c;
+ continue;
+ }
+
+ while (pointerDepth)
+ {
+ if (nameOrType.type)
+ nameOrType.type = Type::PointerType(8, nameOrType.type);
+ else
+ nameOrType.ptrCount++;
+
+ pointerDepth--;
+ }
+
+ if (!qualifiedName.empty())
+ nameOrType.name = QualifiedName(qualifiedName);
+
+ if (nameOrType.type == nullptr && nameOrType.name.IsEmpty())
+ {
+ nameOrType.type = Type::VoidType();
+ }
+
+ result.push_back(nameOrType);
+ last = c;
+ }
+
+ return result;
+}
+
+void ObjCProcessor::DefineObjCSymbol(
+ BNSymbolType type, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred)
+{
+ DefineObjCSymbol(type, m_data->GetTypeByName(typeName), name, addr, deferred);
+}
+
+void ObjCProcessor::DefineObjCSymbol(
+ BNSymbolType type, Ref<Type> typeRef, const std::string& name, uint64_t addr, bool deferred)
+{
+ if (name.size() == 0 || addr == 0)
+ return;
+
+ auto process = [=]() {
+ NameSpace nameSpace = m_data->GetInternalNameSpace();
+ if (type == ExternalSymbol)
+ {
+ nameSpace = m_data->GetExternalNameSpace();
+ }
+
+ std::string shortName = name;
+ std::string fullName = name;
+
+ QualifiedName varName;
+
+ return std::pair<Ref<Symbol>, Ref<Type>>(
+ new Symbol(type, shortName, fullName, name, addr, GlobalBinding, nameSpace), typeRef);
+ };
+
+ if (deferred)
+ {
+ m_symbolQueue->Append(process, [this, addr = addr](Symbol* symbol, Type* type) {
+ // Armv7/Thumb: This will rewrite the symbol's address.
+ // e.g. We pass in 0xc001, it will rewrite it to 0xc000 and create the function w/ the "thumb2" arch.
+ if (Ref<Symbol> existingSymbol = m_data->GetSymbolByAddress(addr))
+ m_data->UndefineAutoSymbol(existingSymbol);
+ auto funcSym = m_data->DefineAutoSymbolAndVariableOrFunction(m_data->GetDefaultPlatform(), symbol, type);
+ if (funcSym->GetType() == FunctionSymbol)
+ {
+ uint64_t target = symbol->GetAddress();
+ Ref<Platform> targetPlatform =
+ m_data->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); // rewrites target.
+ if (Ref<Function> targetFunction = m_data->GetAnalysisFunction(targetPlatform, target))
+ {
+ if (!m_isBackedByDatabase)
+ targetFunction->SetUserType(type);
+ }
+ }
+ });
+ return;
+ }
+
+ if (Ref<Symbol> existingSymbol = m_data->GetSymbolByAddress(addr))
+ m_data->UndefineAutoSymbol(existingSymbol);
+ auto result = process();
+ auto sym = m_data->DefineAutoSymbolAndVariableOrFunction(m_data->GetDefaultPlatform(), result.first, result.second);
+ if (sym->GetType() == FunctionSymbol)
+ {
+ uint64_t target = result.first->GetAddress();
+ Ref<Platform> targetPlatform = m_data->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); // rewrites
+ // target.
+ if (Ref<Function> targetFunction = m_data->GetAnalysisFunction(targetPlatform, target))
+ {
+ if (!m_isBackedByDatabase)
+ targetFunction->SetUserType(result.second);
+ }
+ }
+}
+
+void ObjCProcessor::LoadClasses(ObjCReader* reader, Ref<Section> classPtrSection)
+{
+ if (!classPtrSection)
+ return;
+ auto size = classPtrSection->GetEnd() - classPtrSection->GetStart();
+ if (size == 0)
+ return;
+ auto ptrSize = m_data->GetAddressSize();
+ auto ptrCount = size / ptrSize;
+
+ auto classPtrSectionStart = classPtrSection->GetStart();
+ for (size_t i = 0; i < ptrCount; i++)
+ {
+ Class cls;
+
+ view_ptr_t classPtr;
+ class_t clsStruct;
+ class_ro_t classRO;
+
+ bool hasValidMetaClass = false;
+ bool hasValidMetaClassRO = false;
+ class_t metaClsStruct;
+ class_ro_t metaClassRO;
+
+ view_ptr_t classPointerLocation = classPtrSectionStart + (i * m_data->GetAddressSize());
+ reader->Seek(classPointerLocation);
+
+ classPtr = ReadPointerAccountingForRelocations(reader);
+ reader->Seek(classPtr);
+ try
+ {
+ clsStruct.isa = ReadPointerAccountingForRelocations(reader);
+ clsStruct.super = reader->ReadPointer();
+ clsStruct.cache = reader->ReadPointer();
+ clsStruct.vtable = reader->ReadPointer();
+ clsStruct.data = ReadPointerAccountingForRelocations(reader);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read class data at 0x%llx pointed to by @ 0x%llx", reader->GetOffset(),
+ classPointerLocation);
+ continue;
+ }
+ if (clsStruct.data & 1)
+ {
+ m_logger->LogInfo("Skipping class at 0x%llx as it contains swift types", classPtr);
+ continue;
+ }
+ // unset first two bits
+ view_ptr_t classROPtr = clsStruct.data & ~3;
+ reader->Seek(classROPtr);
+ try
+ {
+ classRO.flags = reader->Read32();
+ classRO.instanceStart = reader->Read32();
+ classRO.instanceSize = reader->Read32();
+ if (m_data->GetAddressSize() == 8)
+ classRO.reserved = reader->Read32();
+ classRO.ivarLayout = ReadPointerAccountingForRelocations(reader);
+ classRO.name = ReadPointerAccountingForRelocations(reader);
+ classRO.baseMethods = ReadPointerAccountingForRelocations(reader);
+ classRO.baseProtocols = ReadPointerAccountingForRelocations(reader);
+ classRO.ivars = ReadPointerAccountingForRelocations(reader);
+ classRO.weakIvarLayout = ReadPointerAccountingForRelocations(reader);
+ classRO.baseProperties = ReadPointerAccountingForRelocations(reader);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read class RO data at 0x%llx. 0x%llx, objc_class_t @ 0x%llx",
+ reader->GetOffset(), classPointerLocation, classROPtr);
+ continue;
+ }
+
+ auto namePtr = classRO.name;
+
+ std::string name;
+
+ reader->Seek(namePtr);
+ try
+ {
+ name = reader->ReadCString();
+ }
+ catch (...)
+ {
+ m_logger->LogWarn(
+ "Failed to read class name at 0x%llx. Class has been given the placeholder name \"0x%llx\" ", namePtr,
+ classPtr);
+ char hexString[9];
+ hexString[8] = 0;
+ snprintf(hexString, sizeof(hexString), "%" PRIx64, classPtr);
+ name = "0x" + std::string(hexString);
+ }
+
+ cls.name = name;
+
+ DefineObjCSymbol(BNSymbolType::DataSymbol,
+ Type::PointerType(m_data->GetAddressSize(), m_data->GetTypeByName(m_typeNames.cls)), "clsPtr_" + name,
+ classPointerLocation, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.cls, "cls_" + name, classPtr, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.classRO, "cls_ro_" + name, classROPtr, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, Type::ArrayType(Type::IntegerType(1, true), name.size() + 1),
+ "clsName_" + name, classRO.name, true);
+ if (classRO.baseProtocols && !m_skipClassBaseProtocols)
+ {
+ DefineObjCSymbol(BNSymbolType::DataSymbol, Type::NamedType(m_data, m_typeNames.protocolList),
+ "clsProtocols_" + name, classRO.baseProtocols, true);
+ reader->Seek(classRO.baseProtocols);
+ uint32_t count = reader->Read64();
+ view_ptr_t addr = reader->GetOffset();
+ for (uint32_t j = 0; j < count; j++)
+ {
+ m_data->DefineDataVariable(
+ addr, Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol)));
+ addr += ptrSize;
+ }
+ }
+
+ if (clsStruct.isa)
+ {
+ reader->Seek(clsStruct.isa);
+ try
+ {
+ metaClsStruct.isa = ReadPointerAccountingForRelocations(reader);
+ metaClsStruct.super = reader->ReadPointer();
+ metaClsStruct.cache = reader->ReadPointer();
+ metaClsStruct.vtable = reader->ReadPointer();
+ metaClsStruct.data = ReadPointerAccountingForRelocations(reader) & ~1;
+ DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.cls, "metacls_" + name, clsStruct.isa, true);
+ hasValidMetaClass = true;
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Failed to read metaclass data at 0x%llx pointed to by objc_class_t @ 0x%llx",
+ reader->GetOffset(), classPtr);
+ }
+ }
+ if (hasValidMetaClass && (metaClsStruct.data & 1))
+ {
+ m_logger->LogInfo("Skipping metaclass at 0x%llx as it contains swift types", classPtr);
+ hasValidMetaClass = false;
+ }
+ if (hasValidMetaClass)
+ {
+ reader->Seek(metaClsStruct.data);
+ try
+ {
+ metaClassRO.flags = reader->Read32();
+ metaClassRO.instanceStart = reader->Read32();
+ metaClassRO.instanceSize = reader->Read32();
+ if (m_data->GetAddressSize() == 8)
+ metaClassRO.reserved = reader->Read32();
+ metaClassRO.ivarLayout = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.name = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.baseMethods = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.baseProtocols = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.ivars = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.weakIvarLayout = ReadPointerAccountingForRelocations(reader);
+ metaClassRO.baseProperties = ReadPointerAccountingForRelocations(reader);
+ DefineObjCSymbol(
+ BNSymbolType::DataSymbol, m_typeNames.classRO, "metacls_ro_" + name, metaClsStruct.data, true);
+ hasValidMetaClassRO = true;
+ }
+ catch (...)
+ {
+ m_logger->LogWarn("Failed to read metaclass RO data at 0x%llx pointed to by meta objc_class_t @ 0x%llx",
+ reader->GetOffset(), clsStruct.isa);
+ }
+ }
+
+ if (classRO.baseMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, cls.instanceClass, name, classRO.baseMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read the method list for class pointed to by 0x%llx", clsStruct.data);
+ }
+ }
+ if (hasValidMetaClassRO && metaClassRO.baseMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, cls.metaClass, name, metaClassRO.baseMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read the method list for metaclass pointed to by 0x%llx", clsStruct.data);
+ }
+ }
+
+ if (classRO.ivars)
+ {
+ try
+ {
+ ReadIvarList(reader, cls.instanceClass, name, classRO.ivars);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to process ivars for class at 0x%llx", clsStruct.data);
+ }
+ }
+ m_classes[classPtr] = cls;
+ }
+}
+
+void ObjCProcessor::LoadCategories(ObjCReader* reader, Ref<Section> classPtrSection)
+{
+ if (!classPtrSection)
+ return;
+ auto size = classPtrSection->GetEnd() - classPtrSection->GetStart();
+ if (size == 0)
+ return;
+ auto ptrSize = m_data->GetAddressSize();
+
+ auto classPtrSectionStart = classPtrSection->GetStart();
+ auto classPtrSectionEnd = classPtrSection->GetEnd();
+
+ auto catType = Type::NamedType(m_data, m_typeNames.category);
+ auto ptrType = Type::PointerType(m_data->GetDefaultArchitecture(), catType);
+ for (size_t i = classPtrSectionStart; i < classPtrSectionEnd; i += ptrSize)
+ {
+ Class category;
+ category_t cat;
+
+ reader->Seek(i);
+ auto catLocation = ReadPointerAccountingForRelocations(reader);
+ reader->Seek(catLocation);
+
+ try
+ {
+ cat.name = ReadPointerAccountingForRelocations(reader);
+ cat.cls = ReadPointerAccountingForRelocations(reader);
+ cat.instanceMethods = ReadPointerAccountingForRelocations(reader);
+ cat.classMethods = ReadPointerAccountingForRelocations(reader);
+ cat.protocols = ReadPointerAccountingForRelocations(reader);
+ cat.instanceProperties = ReadPointerAccountingForRelocations(reader);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read category pointed to by 0x%llx", i);
+ continue;
+ }
+
+ std::string categoryAdditionsName;
+ std::string categoryBaseClassName;
+
+ if (const auto& it = m_classes.find(cat.cls); it != m_classes.end())
+ {
+ 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)
+ {
+ const auto& symbolName = symbol->GetFullName();
+ if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0)
+ categoryBaseClassName = symbolName.substr(14, symbolName.size() - 14);
+ }
+ }
+ if (categoryBaseClassName.empty())
+ {
+ m_logger->LogError(
+ "Failed to determine base classname for category at 0x%llx. Using base address as stand-in classname",
+ catLocation);
+ categoryBaseClassName = std::to_string(catLocation);
+ }
+ try
+ {
+ reader->Seek(cat.name);
+ categoryAdditionsName = reader->ReadCString();
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read category name for category at 0x%llx. Using base address as stand-in category name",
+ catLocation);
+ categoryAdditionsName = std::to_string(catLocation);
+ }
+ category.name = categoryBaseClassName + " (" + categoryAdditionsName + ")";
+ DefineObjCSymbol(BNSymbolType::DataSymbol, ptrType, "categoryPtr_" + category.name, i, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, catType, "category_" + category.name, catLocation, true);
+
+ if (cat.instanceMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, category.instanceClass, category.name, cat.instanceMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read the instance method list for category pointed to by 0x%llx", catLocation);
+ }
+ }
+ if (cat.classMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, category.metaClass, category.name, cat.classMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read the class method list for category pointed to by 0x%llx", catLocation);
+ }
+ }
+ m_categories[catLocation] = category;
+ }
+}
+
+void ObjCProcessor::LoadProtocols(ObjCReader* reader, Ref<Section> listSection)
+{
+ if (!listSection)
+ return;
+ auto size = listSection->GetEnd() - listSection->GetStart();
+ if (size == 0)
+ return;
+ auto ptrSize = m_data->GetAddressSize();
+
+ auto listSectionStart = listSection->GetStart();
+ auto listSectionEnd = listSection->GetEnd();
+
+ auto protocolType = Type::NamedType(m_data, m_typeNames.protocol);
+ auto ptrType = Type::PointerType(m_data->GetDefaultArchitecture(), protocolType);
+ for (size_t i = listSectionStart; i < listSectionEnd; i += ptrSize)
+ {
+ protocol_t protocol;
+ reader->Seek(i);
+ auto protocolLocation = ReadPointerAccountingForRelocations(reader);
+ reader->Seek(protocolLocation);
+
+ try
+ {
+ protocol.isa = ReadPointerAccountingForRelocations(reader);
+ protocol.mangledName = ReadPointerAccountingForRelocations(reader);
+ protocol.protocols = ReadPointerAccountingForRelocations(reader);
+ protocol.instanceMethods = ReadPointerAccountingForRelocations(reader);
+ protocol.classMethods = ReadPointerAccountingForRelocations(reader);
+ protocol.optionalInstanceMethods = ReadPointerAccountingForRelocations(reader);
+ protocol.optionalClassMethods = ReadPointerAccountingForRelocations(reader);
+ protocol.instanceProperties = ReadPointerAccountingForRelocations(reader);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read protocol pointed to by 0x%llx", i);
+ continue;
+ }
+
+ std::string protocolName;
+ try
+ {
+ reader->Seek(protocol.mangledName);
+ protocolName = reader->ReadCString();
+ DefineObjCSymbol(BNSymbolType::DataSymbol,
+ Type::ArrayType(Type::IntegerType(1, true), protocolName.size() + 1), "protocolName_" + protocolName,
+ protocol.mangledName, true);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read protocol name for protocol at 0x%llx. Using base address as stand-in protocol name",
+ protocolLocation);
+ protocolName = std::to_string(protocolLocation);
+ }
+
+ Protocol protocolClass;
+ protocolClass.name = protocolName;
+ DefineObjCSymbol(BNSymbolType::DataSymbol, ptrType, "protocolPtr_" + protocolName, i, true);
+ DefineObjCSymbol(BNSymbolType::DataSymbol, protocolType, "protocol_" + protocolName, protocolLocation, true);
+ if (protocol.protocols)
+ {
+ DefineObjCSymbol(BNSymbolType::DataSymbol, Type::NamedType(m_data, m_typeNames.protocolList),
+ "protoProtocols_" + protocolName, protocol.protocols, true);
+ reader->Seek(protocol.protocols);
+ uint32_t count = reader->Read64();
+ view_ptr_t addr = reader->GetOffset();
+ for (uint32_t j = 0; j < count; j++)
+ {
+ m_data->DefineDataVariable(
+ addr, Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol)));
+ addr += ptrSize;
+ }
+ }
+
+ if (protocol.instanceMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, protocolClass.instanceMethods, protocolName, protocol.instanceMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read the instance method list for protocol pointed to by 0x%llx", protocolLocation);
+ }
+ }
+ if (protocol.classMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, protocolClass.classMethods, protocolName, protocol.classMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to read the class method list for protocol pointed to by 0x%llx", protocolLocation);
+ }
+ }
+ if (protocol.optionalInstanceMethods)
+ {
+ try
+ {
+ ReadMethodList(
+ reader, protocolClass.optionalInstanceMethods, protocolName, protocol.optionalInstanceMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read the optional instance method list for protocol pointed to by 0x%llx",
+ protocolLocation);
+ }
+ }
+ if (protocol.optionalClassMethods)
+ {
+ try
+ {
+ ReadMethodList(reader, protocolClass.optionalClassMethods, protocolName, protocol.optionalClassMethods);
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to read the optional class method list for protocol pointed to by 0x%llx",
+ protocolLocation);
+ }
+ }
+ m_protocols[protocolLocation] = protocolClass;
+ }
+}
+
+void ObjCProcessor::GetRelativeMethod(ObjCReader* reader, method_t& meth)
+{
+ meth.name = reader->GetOffset() + reader->ReadS32();
+ meth.types = reader->GetOffset() + reader->ReadS32();
+ meth.imp = reader->GetOffset() + reader->ReadS32();
+}
+
+void ObjCProcessor::ReadListOfMethodLists(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start)
+{
+ reader->Seek(start);
+ method_list_t head;
+ head.entsizeAndFlags = reader->Read32();
+ head.count = reader->Read32();
+
+ // TODO(WeiN76LQh): probably can be removed at this point
+ if (head.count > 0x1000)
+ {
+ m_logger->LogError("List of method lists at 0x%llx has an invalid count of 0x%x", start, head.count);
+ return;
+ }
+
+ for (size_t i = 0; i < head.count; ++i) {
+ relative_list_list_entry_t list_entry;
+ reader->Read(&list_entry, sizeof(list_entry));
+
+ ReadMethodList(reader, cls, name, reader->GetOffset() - sizeof(list_entry) + list_entry.listOffset);
+ // Reset the cursor to immediately past the list entry.
+ reader->Seek(start + sizeof(method_list_t) + ((i + 1) * sizeof(relative_list_list_entry_t)));
+ }
+}
+
+void ObjCProcessor::ReadMethodList(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start)
+{
+ // Lower two bits indicate the type of method list.
+ switch (start & 0b11) {
+ case 0:
+ break;
+ case 1:
+ return ReadListOfMethodLists(reader, cls, name, start - 1);
+ default:
+ m_logger->LogDebug("ReadMethodList: Unknown method list type at 0x%llx: %d", start, start & 0x3);
+ return;
+ }
+
+ reader->Seek(start);
+ method_list_t head;
+ head.entsizeAndFlags = reader->Read32();
+ head.count = reader->Read32();
+
+ // TODO(WeiN76LQh): probably can be removed at this point
+ if (head.count > 0x1000)
+ {
+ m_logger->LogError("Method list at 0x%llx has an invalid count of 0x%x", start, head.count);
+ return;
+ }
+
+ uint64_t pointerSize = m_data->GetAddressSize();
+ bool relativeOffsets = (head.entsizeAndFlags & 0xFFFF0000) & 0x80000000;
+ bool directSelectors = (head.entsizeAndFlags & 0xFFFF0000) & 0x40000000;
+ auto methodSize = relativeOffsets ? 12 : pointerSize * 3;
+ DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + std::string(name), start, true);
+
+ for (unsigned i = 0; i < head.count; i++)
+ {
+ try
+ {
+ Method method;
+ auto cursor = start + sizeof(method_list_t) + (i * methodSize);
+ reader->Seek(cursor);
+ method_t meth;
+ // workflow_objc support
+ uint64_t selRefAddr = 0;
+ uint64_t selAddr = 0;
+ // --
+ if (relativeOffsets)
+ {
+ GetRelativeMethod(reader, meth);
+ }
+ else
+ {
+ meth.name = ReadPointerAccountingForRelocations(reader);
+ meth.types = ReadPointerAccountingForRelocations(reader);
+ meth.imp = ReadPointerAccountingForRelocations(reader);
+ }
+ if (!relativeOffsets || directSelectors)
+ {
+ reader->Seek(meth.name);
+ selAddr = meth.name;
+ method.name = reader->ReadCString();
+ reader->Seek(meth.types);
+ method.types = reader->ReadCString();
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.name.size() + 1),
+ "sel_" + method.name, meth.name, true);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.types.size() + 1),
+ "selTypes_" + method.name, meth.types, true);
+ }
+ else
+ {
+ std::string sel;
+ view_ptr_t selRef;
+ reader->Seek(meth.name);
+ selRefAddr = meth.name;
+ selRef = ReadPointerAccountingForRelocations(reader);
+ reader->Seek(meth.types);
+ method.types = reader->ReadCString();
+ selAddr = selRef;
+ if (const auto& it = m_selectorCache.find(selRef); it != m_selectorCache.end())
+ method.name = it->second;
+ else
+ {
+ reader->Seek(selRef);
+ method.name = reader->ReadCString();
+ m_selectorCache[selRef] = method.name;
+ }
+ auto selType = Type::ArrayType(Type::IntegerType(1, true), method.name.size() + 1);
+ DefineObjCSymbol(DataSymbol, selType, "sel_" + method.name, selRef, true);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.types.size() + 1),
+ "selTypes_" + method.name, meth.types, true);
+ DefineObjCSymbol(DataSymbol, Type::PointerType(m_data->GetAddressSize(), selType),
+ "selRef_" + method.name, meth.name, true);
+ }
+ // workflow objc support
+ if (selAddr)
+ m_selToImplementations[selAddr].push_back(meth.imp);
+ if (selRefAddr)
+ m_selRefToImplementations[selRefAddr].push_back(meth.imp);
+ // --
+
+ DefineObjCSymbol(DataSymbol, relativeOffsets ? m_typeNames.methodEntry : m_typeNames.method,
+ "method_" + method.name, cursor, true);
+ method.imp = meth.imp;
+ cls.methodList[cursor] = method;
+ m_localMethods[cursor] = method;
+ }
+ catch (...)
+ {
+ m_logger->LogError(
+ "Failed to process a method at offset 0x%llx", start + sizeof(method_list_t) + (i * methodSize));
+ }
+ }
+}
+
+void ObjCProcessor::ReadIvarList(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start)
+{
+ reader->Seek(start);
+ ivar_list_t head;
+ head.entsizeAndFlags = reader->Read32();
+ head.count = reader->Read32();
+ auto addressSize = m_data->GetAddressSize();
+ DefineObjCSymbol(DataSymbol, m_typeNames.ivarList, "ivar_list_" + std::string(name), start, true);
+ for (unsigned i = 0; i < head.count; i++)
+ {
+ try
+ {
+ Ivar ivar;
+ ivar_t ivarStruct;
+ uint64_t cursor = start + (sizeof(ivar_list_t)) + (i * ((addressSize * 3) + 8));
+ reader->Seek(cursor);
+ ivarStruct.offset = ReadPointerAccountingForRelocations(reader);
+ ivarStruct.name = ReadPointerAccountingForRelocations(reader);
+ ivarStruct.type = ReadPointerAccountingForRelocations(reader);
+ ivarStruct.alignmentRaw = reader->Read32();
+ ivarStruct.size = reader->Read32();
+
+ reader->Seek(ivarStruct.offset);
+ ivar.offset = reader->Read32();
+ reader->Seek(ivarStruct.name);
+ ivar.name = reader->ReadCString();
+ reader->Seek(ivarStruct.type);
+ ivar.type = reader->ReadCString();
+
+ DefineObjCSymbol(DataSymbol, m_typeNames.ivar, "ivar_" + ivar.name, cursor, true);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), ivar.name.size() + 1),
+ "ivarName_" + ivar.name, ivarStruct.name, true);
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), ivar.type.size() + 1),
+ "ivarType_" + ivar.name, ivarStruct.type, true);
+
+ cls.ivarList[cursor] = ivar;
+ }
+ catch (...)
+ {
+ m_logger->LogError("Failed to process an ivar at offset 0x%llx",
+ start + (sizeof(ivar_list_t)) + (i * ((addressSize * 3) + 8)));
+ }
+ }
+}
+
+
+std::pair<QualifiedName, Ref<Type>> finalizeStructureBuilder(
+ Ref<BinaryView> m_data, StructureBuilder sb, std::string name)
+{
+ auto classTypeStruct = sb.Finalize();
+
+ QualifiedName classTypeName(name);
+ auto classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
+ auto classType = Type::StructureType(classTypeStruct);
+ auto classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
+
+ return {classQualName, classType};
+}
+
+std::pair<QualifiedName, Ref<Type>> finalizeEnumerationBuilder(
+ Ref<BinaryView> m_data, EnumerationBuilder eb, uint64_t size, QualifiedName name)
+{
+ auto enumTypeStruct = eb.Finalize();
+
+ auto enumTypeId = Type::GenerateAutoTypeId("objc", name);
+ auto enumType = Type::EnumerationType(enumTypeStruct, size);
+ auto enumQualName = m_data->DefineType(enumTypeId, name, enumType);
+
+ return {enumQualName, enumType};
+}
+
+inline QualifiedName defineTypedef(Ref<BinaryView> m_data, const QualifiedName name, Ref<Type> type)
+{
+ auto typeID = Type::GenerateAutoTypeId("objc", name);
+ m_data->DefineType(typeID, name, type);
+ return m_data->GetTypeNameById(typeID);
+}
+
+void ObjCProcessor::GenerateClassTypes()
+{
+ for (auto& [_, cls] : m_classes)
+ {
+ QualifiedName typeName;
+ StructureBuilder classTypeBuilder;
+ bool failedToDecodeType = false;
+ for (const auto& [ivarLoc, ivar] : cls.instanceClass.ivarList)
+ {
+ auto encodedTypeList = ParseEncodedType(ivar.type);
+ if (encodedTypeList.empty())
+ {
+ failedToDecodeType = true;
+ break;
+ }
+ auto encodedType = encodedTypeList.at(0);
+
+ Ref<Type> type;
+
+ if (encodedType.type)
+ type = encodedType.type;
+ else
+ {
+ type = Type::NamedType(encodedType.name, Type::PointerType(m_data->GetAddressSize(), Type::VoidType()));
+ for (size_t i = encodedType.ptrCount; i > 0; i--)
+ type = Type::PointerType(m_data->GetAddressSize(), type);
+ }
+
+ if (!type)
+ type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
+
+ classTypeBuilder.AddMemberAtOffset(type, ivar.name, ivar.offset);
+ }
+ if (failedToDecodeType)
+ continue;
+ auto classTypeStruct = classTypeBuilder.Finalize();
+ QualifiedName classTypeName = cls.name;
+ std::string classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
+ Ref<Type> classType = Type::StructureType(classTypeStruct);
+ QualifiedName classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
+ cls.associatedName = classTypeName;
+ }
+}
+
+bool ObjCProcessor::ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod)
+{
+ if (!method.imp || !m_data->IsValidOffset(method.imp)) {
+ return false;
+ }
+
+ std::stringstream r(method.name);
+
+ std::string token;
+ std::vector<std::string> selectorTokens;
+ while (std::getline(r, token, ':'))
+ selectorTokens.push_back(token);
+
+ std::vector<QualifiedNameOrType> typeTokens = ParseEncodedType(method.types);
+ if (typeTokens.empty())
+ return false;
+
+ auto typeForQualifiedNameOrType = [this](QualifiedNameOrType nameOrType) {
+ Ref<Type> type;
+
+ if (nameOrType.type)
+ {
+ type = nameOrType.type;
+ if (!type)
+ type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
+ }
+ else
+ {
+ type = Type::NamedType(nameOrType.name, Type::PointerType(m_data->GetAddressSize(), Type::VoidType()));
+ for (size_t i = nameOrType.ptrCount; i > 0; i--)
+ type = Type::PointerType(m_data->GetAddressSize(), type);
+ }
+
+ return type;
+ };
+
+ BinaryNinja::QualifiedNameAndType nameAndType;
+ std::set<BinaryNinja::QualifiedName> typesAllowRedefinition;
+
+ auto retType = typeForQualifiedNameOrType(typeTokens[0]);
+
+ std::vector<BinaryNinja::FunctionParameter> params;
+ auto cc = m_data->GetDefaultPlatform()->GetDefaultCallingConvention();
+
+ params.push_back({"self",
+ cls.associatedName.IsEmpty() ?
+ Type::NamedType(m_data, {"id"}) :
+ Type::PointerType(m_data->GetAddressSize(), Type::NamedType(m_data, cls.associatedName)),
+ true, BinaryNinja::Variable()});
+
+ params.push_back({"sel", Type::NamedType(m_data, {"SEL"}), true, BinaryNinja::Variable()});
+
+ for (size_t i = 3; i < typeTokens.size(); i++)
+ {
+ std::string suffix;
+
+ params.push_back({selectorTokens.size() > i - 3 ? selectorTokens[i - 3] : "arg",
+ typeForQualifiedNameOrType(typeTokens[i]), true, BinaryNinja::Variable()});
+ }
+
+ auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params);
+
+ // Search for the method's implementation function; apply the type if found.
+ std::string prefix = isInstanceMethod ? "-" : "+";
+ auto name = prefix + "[" + cls.name + " " + method.name + "]";
+
+ DefineObjCSymbol(FunctionSymbol, funcType, name, method.imp, true);
+
+ return true;
+}
+
+void ObjCProcessor::ApplyMethodTypes(Class& cls)
+{
+ for (auto& [_, method] : cls.instanceClass.methodList)
+ {
+ ApplyMethodType(cls, method, true);
+ }
+ for (auto& [_, method] : cls.metaClass.methodList)
+ {
+ ApplyMethodType(cls, method, false);
+ }
+}
+
+Ref<Section> ObjCProcessor::GetSectionForImage(std::optional<std::string> imageName, const char* sectionName)
+{
+ if (imageName)
+ {
+ return m_data->GetSectionByName(*imageName + "::" + sectionName);
+ }
+ else
+ {
+ return m_data->GetSectionByName(sectionName);
+ }
+}
+
+void ObjCProcessor::PostProcessObjCSections(ObjCReader* reader, std::optional<std::string> imageName)
+{
+ auto ptrSize = m_data->GetAddressSize();
+ if (auto imageInfo = GetSectionForImage(imageName, "__objc_imageinfo"))
+ {
+ auto start = imageInfo->GetStart();
+ auto type = Type::NamedType(m_data, m_typeNames.imageInfo);
+ m_data->DefineDataVariable(start, type);
+ }
+ if (auto selrefs = GetSectionForImage(imageName, "__objc_selrefs"))
+ {
+ auto start = selrefs->GetStart();
+ auto end = selrefs->GetEnd();
+ auto type = Type::PointerType(ptrSize, Type::IntegerType(1, false));
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ reader->Seek(i);
+ auto selLoc = ReadPointerAccountingForRelocations(reader);
+ std::string sel;
+ if (const auto& it = m_selectorCache.find(selLoc); it != m_selectorCache.end())
+ sel = it->second;
+ else
+ {
+ reader->Seek(selLoc);
+ sel = reader->ReadCString();
+ m_selectorCache[selLoc] = sel;
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), sel.size() + 1), "sel_" + sel,
+ selLoc, true);
+ }
+ DefineObjCSymbol(DataSymbol, type, "selRef_" + sel, i, true);
+ }
+ }
+ if (auto superRefs = GetSectionForImage(imageName, "__objc_classrefs"))
+ {
+ auto start = superRefs->GetStart();
+ auto end = superRefs->GetEnd();
+ auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ reader->Seek(i);
+ auto clsLoc = ReadPointerAccountingForRelocations(reader);
+ if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
+ {
+ auto& cls = it->second;
+ std::string name = cls.name;
+ if (!name.empty())
+ DefineObjCSymbol(DataSymbol, type, "clsRef_" + name, i, true);
+ }
+ }
+ }
+ if (auto superRefs = GetSectionForImage(imageName, "__objc_superrefs"))
+ {
+ auto start = superRefs->GetStart();
+ auto end = superRefs->GetEnd();
+ auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ reader->Seek(i);
+ auto clsLoc = ReadPointerAccountingForRelocations(reader);
+ if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
+ {
+ auto& cls = it->second;
+ std::string name = cls.name;
+ if (!name.empty())
+ DefineObjCSymbol(DataSymbol, type, "superRef_" + name, i, true);
+ }
+ }
+ }
+ if (auto protoRefs = GetSectionForImage(imageName, "__objc_protorefs"))
+ {
+ auto start = protoRefs->GetStart();
+ auto end = protoRefs->GetEnd();
+ auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol));
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ reader->Seek(i);
+ auto protoLoc = ReadPointerAccountingForRelocations(reader);
+ if (const auto& it = m_protocols.find(protoLoc); it != m_protocols.end())
+ {
+ auto& proto = it->second;
+ std::string name = proto.name;
+ if (!name.empty())
+ DefineObjCSymbol(DataSymbol, type, "protoRef_" + name, i, true);
+ }
+ }
+ }
+ if (auto ivars = GetSectionForImage(imageName, "__objc_ivar"))
+ {
+ auto start = ivars->GetStart();
+ auto end = ivars->GetEnd();
+ auto ivarSectionEntryTypeBuilder = new TypeBuilder(Type::IntegerType(8, false));
+ ivarSectionEntryTypeBuilder->SetConst(true);
+ auto type = ivarSectionEntryTypeBuilder->Finalize();
+ for (view_ptr_t i = start; i < end; i += ptrSize)
+ {
+ m_data->DefineDataVariable(i, type);
+ }
+ }
+}
+
+uint64_t ObjCProcessor::ReadPointerAccountingForRelocations(ObjCReader* reader)
+{
+ if (auto it = m_relocationPointerRewrites.find(reader->GetOffset()); it != m_relocationPointerRewrites.end())
+ {
+ reader->SeekRelative(m_data->GetAddressSize());
+ return it->second;
+ }
+ return reader->ReadPointer();
+}
+
+
+ObjCProcessor::ObjCProcessor(BinaryView* data, const char* loggerName, bool isBackedByDatabase, bool skipClassBaseProtocols) :
+ m_isBackedByDatabase(isBackedByDatabase), m_skipClassBaseProtocols(skipClassBaseProtocols), m_data(data)
+{
+ m_logger = m_data->CreateLogger(loggerName);
+}
+
+uint64_t ObjCProcessor::GetObjCRelativeMethodBaseAddress(ObjCReader* reader)
+{
+ return 0;
+}
+
+void ObjCProcessor::ProcessObjCData(std::optional<std::string> imageName)
+{
+ m_symbolQueue = new SymbolQueue();
+ auto addrSize = m_data->GetAddressSize();
+
+ m_typeNames.relativePtr = defineTypedef(m_data, {"rptr_t"}, Type::IntegerType(4, true));
+ auto rptr_t = Type::NamedType(m_data, m_typeNames.relativePtr);
+
+ m_typeNames.id = defineTypedef(m_data, {"id"}, Type::PointerType(addrSize, Type::VoidType()));
+ m_typeNames.sel = defineTypedef(m_data, {"SEL"}, Type::PointerType(addrSize, Type::IntegerType(1, false)));
+
+ m_typeNames.BOOL = defineTypedef(m_data, {"BOOL"}, Type::IntegerType(1, false));
+ m_typeNames.nsInteger = defineTypedef(m_data, {"NSInteger"}, Type::IntegerType(addrSize, true));
+ m_typeNames.nsuInteger = defineTypedef(m_data, {"NSUInteger"}, Type::IntegerType(addrSize, false));
+ m_typeNames.cgFloat = defineTypedef(m_data, {"CGFloat"}, Type::FloatType(addrSize));
+
+ Ref<Type> relativeSelectorPtr;
+ auto reader = GetReader();
+ if (auto objCRelativeMethodsBaseAddr = GetObjCRelativeMethodBaseAddress(reader.get())) {
+ m_logger->LogDebug("RelativeMethodSelector Base: 0x%llx", objCRelativeMethodsBaseAddr);
+
+ auto type = TypeBuilder::PointerType(4, Type::PointerType(addrSize, Type::IntegerType(1, false)))
+ .SetPointerBase(RelativeToConstantPointerBaseType, objCRelativeMethodsBaseAddr)
+ .Finalize();
+ auto relativeSelectorPtrName = defineTypedef(m_data, {"relative_SEL"}, type);
+ relativeSelectorPtr = Type::NamedType(m_data, relativeSelectorPtrName);
+ }
+
+ // https://github.com/apple-oss-distributions/objc4/blob/196363c165b175ed925ef6b9b99f558717923c47/runtime/objc-abi.h
+ EnumerationBuilder imageInfoFlagBuilder;
+ imageInfoFlagBuilder.AddMemberWithValue("IsReplacement", 1 << 0);
+ imageInfoFlagBuilder.AddMemberWithValue("SupportsGC", 1 << 1);
+ imageInfoFlagBuilder.AddMemberWithValue("RequiresGC", 1 << 2);
+ imageInfoFlagBuilder.AddMemberWithValue("OptimizedByDyld", 1 << 3);
+ imageInfoFlagBuilder.AddMemberWithValue("CorrectedSynthesize", 1 << 4);
+ imageInfoFlagBuilder.AddMemberWithValue("IsSimulated", 1 << 5);
+ imageInfoFlagBuilder.AddMemberWithValue("HasCategoryClassProperties", 1 << 6);
+ imageInfoFlagBuilder.AddMemberWithValue("OptimizedByDyldClosure", 1 << 7);
+ imageInfoFlagBuilder.AddMemberWithValue("SwiftUnstableVersionMask", 0xff << 8);
+ imageInfoFlagBuilder.AddMemberWithValue("SwiftStableVersionMask", 0xFFFF << 16);
+ auto imageInfoFlagType = finalizeEnumerationBuilder(m_data, imageInfoFlagBuilder, 4, {"objc_image_info_flags"});
+ m_typeNames.imageInfoFlags = imageInfoFlagType.first;
+
+ EnumerationBuilder swiftVersionBuilder;
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion1", 1);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion1_2", 2);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion2", 3);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion3", 4);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion4", 5);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion4_1", 6); // [sic]
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion4_2", 6);
+ swiftVersionBuilder.AddMemberWithValue("SwiftVersion5", 7);
+ auto swiftVersionType =
+ finalizeEnumerationBuilder(m_data, swiftVersionBuilder, 4, {"objc_image_info_swift_version"});
+ m_typeNames.imageInfoSwiftVersion = swiftVersionType.first;
+
+ StructureBuilder imageInfoBuilder;
+ imageInfoBuilder.AddMember(Type::IntegerType(4, false), "version");
+ imageInfoBuilder.AddMember(Type::NamedType(m_data, m_typeNames.imageInfoFlags), "flags");
+ auto imageInfoType = finalizeStructureBuilder(m_data, imageInfoBuilder, "objc_image_info_t");
+ m_typeNames.imageInfo = imageInfoType.first;
+
+ StructureBuilder methodEntry;
+ methodEntry.AddMember(relativeSelectorPtr ? relativeSelectorPtr : rptr_t, "name");
+ methodEntry.AddMember(rptr_t, "types");
+ methodEntry.AddMember(rptr_t, "imp");
+ auto type = finalizeStructureBuilder(m_data, methodEntry, "objc_method_entry_t");
+ m_typeNames.methodEntry = type.first;
+
+ StructureBuilder method;
+ method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
+ method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "types");
+ method.AddMember(Type::PointerType(addrSize, Type::VoidType()), "imp");
+ type = finalizeStructureBuilder(m_data, method, "objc_method_t");
+ m_typeNames.method = type.first;
+
+ StructureBuilder methList;
+ methList.AddMember(Type::IntegerType(4, false), "obsolete");
+ methList.AddMember(Type::IntegerType(4, false), "count");
+ type = finalizeStructureBuilder(m_data, methList, "objc_method_list_t");
+ m_typeNames.methodList = type.first;
+
+ StructureBuilder ivarBuilder;
+ ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(4, false)), "offset");
+ ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
+ ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "type");
+ ivarBuilder.AddMember(Type::IntegerType(4, false), "alignment");
+ ivarBuilder.AddMember(Type::IntegerType(4, false), "size");
+ type = finalizeStructureBuilder(m_data, ivarBuilder, "objc_ivar_t");
+ m_typeNames.ivar = type.first;
+
+ StructureBuilder ivarList;
+ ivarList.AddMember(Type::IntegerType(4, false), "entsize");
+ ivarList.AddMember(Type::IntegerType(4, false), "count");
+ type = finalizeStructureBuilder(m_data, ivarList, "objc_ivar_list_t");
+ m_typeNames.ivarList = type.first;
+
+ StructureBuilder protocolListBuilder;
+ protocolListBuilder.AddMember(Type::IntegerType(addrSize, false), "count");
+ m_typeNames.protocolList = finalizeStructureBuilder(m_data, protocolListBuilder, "objc_protocol_list_t").first;
+
+ StructureBuilder classROBuilder;
+ classROBuilder.AddMember(Type::IntegerType(4, false), "flags");
+ classROBuilder.AddMember(Type::IntegerType(4, false), "start");
+ classROBuilder.AddMember(Type::IntegerType(4, false), "size");
+ if (addrSize == 8)
+ classROBuilder.AddMember(Type::IntegerType(4, false), "reserved");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "ivar_layout");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "methods");
+ classROBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.protocolList)), "protocols");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.ivarList)), "ivars");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "weak_ivar_layout");
+ classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "properties");
+ type = finalizeStructureBuilder(m_data, classROBuilder, "objc_class_ro_t");
+ m_typeNames.classRO = type.first;
+
+ QualifiedName classTypeName("objc_class_t");
+ auto classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
+ auto isaType = Type::PointerType(m_data->GetDefaultArchitecture(),
+ TypeBuilder::NamedType(
+ new NamedTypeReferenceBuilder(StructNamedTypeClass, "", classTypeName), m_data->GetAddressSize(), 4)
+ .Finalize());
+
+ StructureBuilder classBuilder;
+ classBuilder.AddMember(isaType, "isa");
+ classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "super");
+ classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "cache");
+ classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "vtable");
+ classBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.classRO)), "data");
+
+ auto classTypeStruct = classBuilder.Finalize();
+ auto classType = Type::StructureType(classTypeStruct);
+ auto classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
+
+ m_typeNames.cls = classQualName;
+
+ StructureBuilder categoryBuilder;
+ categoryBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "category_name");
+ categoryBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.cls)), "class");
+ categoryBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "inst_methods");
+ categoryBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "class_methods");
+ categoryBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "protocols");
+ categoryBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "properties");
+ m_typeNames.category = finalizeStructureBuilder(m_data, categoryBuilder, "objc_category_t").first;
+
+ StructureBuilder protocolBuilder;
+ protocolBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "isa");
+ protocolBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "mangledName");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.protocolList)), "protocols");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "instanceMethods");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "classMethods");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "optionalInstanceMethods");
+ protocolBuilder.AddMember(
+ Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "optionalClassMethods");
+ protocolBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "instanceProperties");
+ protocolBuilder.AddMember(Type::IntegerType(4, false), "size");
+ protocolBuilder.AddMember(Type::IntegerType(4, false), "flags");
+ m_typeNames.protocol = finalizeStructureBuilder(m_data, protocolBuilder, "objc_protocol_t").first;
+
+ m_data->BeginBulkModifySymbols();
+ if (auto classList = GetSectionForImage(imageName, "__objc_classlist"))
+ LoadClasses(reader.get(), classList);
+ if (auto nonLazyClassList = GetSectionForImage(imageName, "__objc_nlclslist"))
+ LoadClasses(reader.get(), nonLazyClassList); // See: https://stackoverflow.com/a/15318325
+
+ GenerateClassTypes();
+ for (auto& [_, cls] : m_classes)
+ ApplyMethodTypes(cls);
+
+ if (auto catList = GetSectionForImage(imageName, "__objc_catlist")) // Do this after loading class type data.
+ LoadCategories(reader.get(), catList);
+ if (auto nonLazyCatList = GetSectionForImage(imageName, "__objc_nlcatlist")) // Do this after loading class type data.
+ LoadCategories(reader.get(), nonLazyCatList);
+ for (auto& [_, cat] : m_categories)
+ ApplyMethodTypes(cat);
+
+ if (auto protoList = GetSectionForImage(imageName, "__objc_protolist"))
+ LoadProtocols(reader.get(), protoList);
+
+ PostProcessObjCSections(reader.get(), imageName);
+
+ auto id = m_data->BeginUndoActions();
+ m_symbolQueue->Process();
+ m_data->EndBulkModifySymbols();
+ delete m_symbolQueue;
+ m_data->ForgetUndoActions(id);
+
+ auto meta = SerializeMetadata();
+ m_data->StoreMetadata("Objective-C", meta, true);
+
+ m_relocationPointerRewrites.clear();
+}
+
+
+void ObjCProcessor::ProcessCFStrings(std::optional<std::string> imageName)
+{
+ m_symbolQueue = new SymbolQueue();
+ uint64_t ptrSize = m_data->GetAddressSize();
+ // https://github.com/apple/llvm-project/blob/next/clang/lib/CodeGen/CodeGenModule.cpp#L6129
+ // See also ASTContext.cpp ctrl+f __NSConstantString_tag
+
+ // The place these flags are used is unclear, along with any clear flag definitions, but they are useful for
+ // introspection
+ EnumerationBuilder __cfStringFlagBuilder;
+ __cfStringFlagBuilder.AddMemberWithValue("SwiftABI", 0b1);
+ __cfStringFlagBuilder.AddMemberWithValue("Swift4_1", 0b100);
+ // LLVM also sets 0x7c0 (0b11111000000) on both UTF8 and UTF16 strings however it is unclear what this denotes.
+ __cfStringFlagBuilder.AddMemberWithValue("UTF8", 0b1000);
+ __cfStringFlagBuilder.AddMemberWithValue("UTF16", 0b10000);
+ auto type = finalizeEnumerationBuilder(m_data, __cfStringFlagBuilder, ptrSize, {"CFStringFlag"});
+ m_typeNames.cfStringFlag = type.first;
+
+ StructureBuilder __cfStringStructBuilder;
+ __cfStringStructBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
+ __cfStringStructBuilder.AddMember(Type::NamedType(m_data, m_typeNames.cfStringFlag), "flags");
+ __cfStringStructBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(1, true)), "data");
+ __cfStringStructBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
+ type = finalizeStructureBuilder(m_data, __cfStringStructBuilder, "__NSConstantString");
+ m_typeNames.cfString = type.first;
+
+ StructureBuilder __cfStringUTF16StructBuilder;
+ __cfStringUTF16StructBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
+ __cfStringUTF16StructBuilder.AddMember(Type::NamedType(m_data, m_typeNames.cfStringFlag), "flags");
+ __cfStringUTF16StructBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(2, true)), "data");
+ __cfStringUTF16StructBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
+ type = finalizeStructureBuilder(m_data, __cfStringUTF16StructBuilder, "__NSConstantString_UTF16");
+ m_typeNames.cfStringUTF16 = type.first;
+
+ auto reader = GetReader();
+ if (auto cfstrings = GetSectionForImage(imageName, "__cfstring"))
+ {
+ auto start = cfstrings->GetStart();
+ auto end = cfstrings->GetEnd();
+ auto typeWidth = Type::NamedType(m_data, m_typeNames.cfString)->GetWidth();
+ m_data->BeginBulkModifySymbols();
+ for (view_ptr_t i = start; i < end; i += typeWidth)
+ {
+ reader->Seek(i + ptrSize);
+ uint64_t flags = reader->ReadPointer();
+ auto strLoc = ReadPointerAccountingForRelocations(reader.get());
+ auto size = reader->ReadPointer();
+ std::string str;
+ if (flags & 0b10000) // UTF16
+ {
+ auto data = m_data->ReadBuffer(strLoc, size * 2);
+
+ str = "";
+ for (uint64_t bufferOff = 0; bufferOff < size * 2; bufferOff += 2)
+ {
+ uint8_t* rawData = static_cast<uint8_t*>(data.GetData());
+ uint8_t* offsetAddress = rawData + bufferOff;
+ uint16_t c = *reinterpret_cast<uint16_t*>(offsetAddress);
+ if (c == 0x20)
+ str.push_back('_');
+ else if (c < 0x80)
+ str.push_back(c);
+ else
+ str.push_back('?');
+ }
+ DefineObjCSymbol(
+ DataSymbol, Type::ArrayType(Type::WideCharType(2), size + 1), "ustr_" + str, strLoc, true);
+ DefineObjCSymbol(
+ DataSymbol, Type::NamedType(m_data, m_typeNames.cfStringUTF16), "cfstr_" + str, i, true);
+ }
+ else // UTF8 / ASCII
+ {
+ reader->Seek(strLoc);
+ str = reader->ReadCString();
+ for (auto& c : str)
+ {
+ if (c == ' ')
+ c = '_';
+ }
+ DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), str.size() + 1), "cstr_" + str,
+ strLoc, true);
+ DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.cfString), "cfstr_" + str, i, true);
+ }
+ }
+ auto id = m_data->BeginUndoActions();
+ m_symbolQueue->Process();
+ m_data->EndBulkModifySymbols();
+ m_data->ForgetUndoActions(id);
+ }
+ delete m_symbolQueue;
+}
+
+void ObjCProcessor::AddRelocatedPointer(uint64_t location, uint64_t rewrite)
+{
+ m_relocationPointerRewrites[location] = rewrite;
+}
diff --git a/objectivec/objc.h b/objectivec/objc.h
new file mode 100644
index 00000000..2939a3e5
--- /dev/null
+++ b/objectivec/objc.h
@@ -0,0 +1,337 @@
+#pragma once
+
+#include <binaryninjaapi.h>
+
+namespace BinaryNinja {
+ // This set of structs is based on the objc4 source,
+ // however pointers have been replaced with view_ptr_t
+
+ // Used for pointers within BinaryView, primarily to make it far more clear in typedefs
+ // whether the size of a field can vary between architectures.
+ // These should _not_ be used in sizeof or direct Read() calls.
+ typedef uint64_t view_ptr_t;
+
+ typedef struct {
+ view_ptr_t name;
+ view_ptr_t types;
+ view_ptr_t imp;
+ } method_t;
+ typedef struct {
+ uint32_t name;
+ uint32_t types;
+ uint32_t imp;
+ } method_entry_t;
+ typedef struct {
+ view_ptr_t offset;
+ view_ptr_t name;
+ view_ptr_t type;
+ uint32_t alignmentRaw;
+ uint32_t size;
+ } ivar_t;
+ typedef struct {
+ view_ptr_t name;
+ view_ptr_t attributes;
+ } property_t;
+ typedef struct {
+ uint32_t entsizeAndFlags;
+ uint32_t count;
+ } method_list_t;
+ typedef struct {
+ uint32_t entsizeAndFlags;
+ uint32_t count;
+ } ivar_list_t;
+ typedef struct {
+ uint32_t entsizeAndFlags;
+ uint32_t count;
+ } property_list_t;
+ typedef struct {
+ uint64_t count;
+ } protocol_list_t;
+ struct relative_list_list_entry_t {
+ uint64_t imageIndex: 16;
+ int64_t listOffset: 48;
+ };
+ typedef struct {
+ view_ptr_t isa;
+ view_ptr_t mangledName;
+ view_ptr_t protocols;
+ view_ptr_t instanceMethods;
+ view_ptr_t classMethods;
+ view_ptr_t optionalInstanceMethods;
+ view_ptr_t optionalClassMethods;
+ view_ptr_t instanceProperties;
+ uint32_t size;
+ uint32_t flags;
+ } protocol_t;
+ typedef struct {
+ uint32_t flags;
+ uint32_t instanceStart;
+ uint32_t instanceSize;
+ uint32_t reserved;
+ view_ptr_t ivarLayout;
+ view_ptr_t name;
+ view_ptr_t baseMethods;
+ view_ptr_t baseProtocols;
+ view_ptr_t ivars;
+ view_ptr_t weakIvarLayout;
+ view_ptr_t baseProperties;
+ } class_ro_t;
+ typedef struct {
+ view_ptr_t isa;
+ view_ptr_t super;
+ view_ptr_t cache;
+ view_ptr_t vtable;
+ view_ptr_t data;
+ } class_t;
+ typedef struct {
+ view_ptr_t name;
+ view_ptr_t cls;
+ view_ptr_t instanceMethods;
+ view_ptr_t classMethods;
+ view_ptr_t protocols;
+ view_ptr_t instanceProperties;
+ } category_t;
+ typedef struct {
+ view_ptr_t receiver;
+ view_ptr_t current_class;
+ } objc_super2;
+ typedef struct {
+ view_ptr_t imp;
+ view_ptr_t sel;
+ } message_ref_t;
+
+ struct Method {
+ std::string name;
+ std::string types;
+ view_ptr_t imp;
+ };
+
+ struct Ivar {
+ uint32_t offset;
+ std::string name;
+ std::string type;
+ uint32_t alignment;
+ uint32_t size;
+ };
+
+ struct Property {
+ std::string name;
+ std::string attributes;
+ };
+
+ struct ClassBase {
+ std::map<uint64_t, Method> methodList;
+ std::map<uint64_t, Ivar> ivarList;
+ };
+
+ struct Class {
+ std::string name;
+ ClassBase instanceClass;
+ ClassBase metaClass;
+
+ // Loaded by type processing
+ QualifiedName associatedName;
+ };
+
+ class Protocol {
+ public:
+ std::string name;
+ std::vector<QualifiedName> protocols;
+ ClassBase instanceMethods;
+ ClassBase classMethods;
+ ClassBase optionalInstanceMethods;
+ ClassBase optionalClassMethods;
+ };
+
+ struct QualifiedNameOrType {
+ BinaryNinja::Ref<BinaryNinja::Type> type = nullptr;
+ BinaryNinja::QualifiedName name;
+ size_t ptrCount = 0;
+ };
+
+ class ObjCReader {
+ public:
+ virtual ~ObjCReader() = default;
+
+ /*! Read from the current cursor position into buffer `dest` and advance the cursor that many bytes
+
+ \throws Exception
+ \param dest Address to write the read bytes to
+ \param len Number of bytes to write
+ */
+ virtual void Read(void* dest, size_t len) = 0;
+
+ /*! Read a null-terminated string from the current cursor position
+
+ \throws Exception
+ \return the string
+ */
+ virtual std::string ReadCString() = 0;
+
+ /*! Read a uint8_t from the current cursor position and advance the cursor by 1 byte
+
+ \throws Exception
+ \return The read value
+ */
+ virtual uint8_t Read8() = 0;
+
+ /*! Read a uint16_t from the current cursor position and advance the cursor by 2 bytes
+
+ \throws Exception
+ \return The read value
+ */
+ virtual uint16_t Read16() = 0;
+
+ /*! Read a uint32_t from the current cursor position and advance the cursor by 4 bytes
+
+ \throws Exception
+ \return The read value
+ */
+ virtual uint32_t Read32() = 0;
+
+ /*! Read a uint64_t from the current cursor position and advance the cursor by 8 bytes
+
+ \throws Exception
+ \return The read value
+ */
+ virtual uint64_t Read64() = 0;
+
+ /*! Read a int8_t from the current cursor position and advance the cursor by 1 byte
+
+ \throws Exception
+ \return The read value
+ */
+ virtual int8_t ReadS8() = 0;
+
+ /*! Read a int16_t from the current cursor position and advance the cursor by 2 bytes
+
+ \throws Exception
+ \return The read value
+ */
+ virtual int16_t ReadS16() = 0;
+
+ /*! Read a int32_t from the current cursor position and advance the cursor by 4 bytes
+
+ \throws Exception
+ \return The read value
+ */
+ virtual int32_t ReadS32() = 0;
+
+ /*! Read a int64_t from the current cursor position and advance the cursor by 8 bytes
+
+ \throws Exception
+ \return The read value
+ */
+ virtual int64_t ReadS64() = 0;
+
+ /*! Read a pointer from the current cursor position and advance it that many bytes
+
+ \throws Exception
+ \return The value that was read
+ */
+ virtual uint64_t ReadPointer() = 0;
+
+ /*! Get the current cursor position
+
+ \return The current cursor position
+ */
+ virtual uint64_t GetOffset() const = 0;
+
+ /*! Set the cursor position
+
+ \param offset The new cursor position
+ */
+ virtual void Seek(uint64_t offset) = 0;
+
+ /*! Set the cursor position, relative to the current position
+
+ \param offset Offset to the current cursor position
+ */
+ virtual void SeekRelative(int64_t offset) = 0;
+ };
+
+ class ObjCProcessor {
+ struct Types {
+ QualifiedName relativePtr;
+ QualifiedName id;
+ QualifiedName sel;
+ QualifiedName BOOL;
+ QualifiedName nsInteger;
+ QualifiedName nsuInteger;
+ QualifiedName cgFloat;
+ QualifiedName cfStringFlag;
+ QualifiedName cfString;
+ QualifiedName cfStringUTF16;
+ QualifiedName imageInfoFlags;
+ QualifiedName imageInfoSwiftVersion;
+ QualifiedName imageInfo;
+ QualifiedName methodEntry;
+ QualifiedName method;
+ QualifiedName methodList;
+ QualifiedName classRO;
+ QualifiedName cls;
+ QualifiedName category;
+ QualifiedName protocol;
+ QualifiedName protocolList;
+ QualifiedName ivar;
+ QualifiedName ivarList;
+ } m_typeNames;
+
+ bool m_isBackedByDatabase;
+ // TODO(WeiN76LQh): this is to avoid a bug with defining a classes protocol list in the DSC plugin. Remove once fixed
+ bool m_skipClassBaseProtocols;
+
+ SymbolQueue* m_symbolQueue = nullptr;
+ std::map<uint64_t, Class> m_classes;
+ std::map<uint64_t, Class> m_categories;
+ std::map<uint64_t, Protocol> m_protocols;
+ std::unordered_map<uint64_t, std::string> m_selectorCache;
+ std::unordered_map<uint64_t, Method> m_localMethods;
+
+ // Required for workflow_objc type heuristics, should be removed when that is no longer a thing.
+ std::map<uint64_t, std::string> m_selRefToName;
+ std::map<uint64_t, std::vector<uint64_t>> m_selRefToImplementations;
+ std::map<uint64_t, std::vector<uint64_t>> m_selToImplementations;
+ // --
+
+ uint64_t ReadPointerAccountingForRelocations(ObjCReader* reader);
+ std::unordered_map<uint64_t, uint64_t> m_relocationPointerRewrites;
+
+ static Ref<Metadata> SerializeMethod(uint64_t loc, const Method& method);
+ static Ref<Metadata> SerializeClass(uint64_t loc, const Class& cls);
+ Ref<Metadata> SerializeMetadata();
+
+ std::vector<QualifiedNameOrType> ParseEncodedType(const std::string& type);
+ void DefineObjCSymbol(BNSymbolType symbolType, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred);
+ void DefineObjCSymbol(BNSymbolType symbolType, Ref<Type> type, const std::string& name, uint64_t addr, bool deferred);
+ void ReadIvarList(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
+ void ReadMethodList(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
+ void ReadListOfMethodLists(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
+ void LoadClasses(ObjCReader* reader, Ref<Section> listSection);
+ void LoadCategories(ObjCReader* reader, Ref<Section> listSection);
+ void LoadProtocols(ObjCReader* reader, Ref<Section> listSection);
+ void GenerateClassTypes();
+ bool ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod);
+ void ApplyMethodTypes(Class& cls);
+
+ Ref<Section> GetSectionForImage(std::optional<std::string> imageName, const char* sectionName);
+ void PostProcessObjCSections(ObjCReader* reader, std::optional<std::string> imageName);
+
+ protected:
+ Ref<BinaryView> m_data;
+ Ref<Logger> m_logger;
+
+ virtual uint64_t GetObjCRelativeMethodBaseAddress(ObjCReader* reader);
+ virtual void GetRelativeMethod(ObjCReader* reader, method_t& meth);
+ virtual std::shared_ptr<ObjCReader> GetReader() = 0;
+
+ public:
+ virtual ~ObjCProcessor() = default;
+
+ ObjCProcessor(BinaryView* data, const char* loggerName, bool isBackedByDatabase, bool skipClassBaseProtocols = false);
+ void ProcessObjCData(std::optional<std::string> imageName);
+ void ProcessCFStrings(std::optional<std::string> imageName);
+ void AddRelocatedPointer(uint64_t location, uint64_t rewrite);
+ };
+}
+
diff --git a/view/macho/CMakeLists.txt b/view/macho/CMakeLists.txt
index 6f89250a..fc8a8f7b 100644
--- a/view/macho/CMakeLists.txt
+++ b/view/macho/CMakeLists.txt
@@ -6,9 +6,7 @@ if(NOT BN_INTERNAL_BUILD)
add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api)
endif()
-file(GLOB SOURCES
- *.cpp
- *.h)
+file(GLOB SOURCES *.cpp *.h ../../objectivec/*)
if(DEMO)
add_library(view_macho STATIC ${SOURCES})
diff --git a/view/macho/machoview.cpp b/view/macho/machoview.cpp
index 2a927671..8a320545 100644
--- a/view/macho/machoview.cpp
+++ b/view/macho/machoview.cpp
@@ -1840,13 +1840,13 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_
parseObjCStructs = settings->Get<bool>("loader.macho.processObjectiveC", this);
if (settings && settings->Contains("loader.macho.processCFStrings"))
parseCFStrings = settings->Get<bool>("loader.macho.processCFStrings", this);
- if (!ObjCProcessor::ViewHasObjCMetadata(this))
+ if (!MachoObjCProcessor::ViewHasObjCMetadata(this))
parseObjCStructs = false;
if (!GetSectionByName("__cfstring"))
parseCFStrings = false;
if (parseObjCStructs || parseCFStrings)
{
- m_objcProcessor = new ObjCProcessor(this, m_backedByDatabase);
+ m_objcProcessor = new MachoObjCProcessor(this, m_backedByDatabase);
}
if (parseObjCStructs)
{
@@ -2332,7 +2332,7 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_
if (parseCFStrings)
{
try {
- m_objcProcessor->ProcessCFStrings();
+ m_objcProcessor->ProcessCFStrings(std::nullopt);
}
catch (std::exception& ex)
{
@@ -2344,7 +2344,7 @@ bool MachoView::InitializeHeader(MachOHeader& header, bool isMainHeader, uint64_
if (parseObjCStructs)
{
try {
- m_objcProcessor->ProcessObjCData();
+ m_objcProcessor->ProcessObjCData(std::nullopt);
}
catch (std::exception& ex)
{
@@ -3872,7 +3872,7 @@ Ref<Settings> MachoViewType::GetLoadSettingsForData(BinaryView* data)
settings->UpdateProperty(override, "readOnly", false);
}
- if (ObjCProcessor::ViewHasObjCMetadata(viewRef))
+ if (MachoObjCProcessor::ViewHasObjCMetadata(viewRef))
{
settings->RegisterSetting("loader.macho.processObjectiveC",
R"({
diff --git a/view/macho/machoview.h b/view/macho/machoview.h
index e7927383..11a17bff 100644
--- a/view/macho/machoview.h
+++ b/view/macho/machoview.h
@@ -1446,7 +1446,7 @@ namespace BinaryNinja
QualifiedName filesetEntryCommandQualName;
} m_typeNames;
- ObjCProcessor* m_objcProcessor = nullptr;
+ MachoObjCProcessor* m_objcProcessor = nullptr;
uint64_t m_universalImageOffset;
bool m_parseOnly, m_backedByDatabase;
diff --git a/view/macho/objc.cpp b/view/macho/objc.cpp
index 5dec6e90..6663a44b 100644
--- a/view/macho/objc.cpp
+++ b/view/macho/objc.cpp
@@ -1,1506 +1,93 @@
#include "objc.h"
-#include "machoview.h"
-#include "inttypes.h"
-#include "rapidjson/rapidjson.h"
-#include "rapidjson/document.h"
-#include "rapidjson/stringbuffer.h"
-#include "rapidjson/prettywriter.h"
using namespace BinaryNinja;
-Ref<Metadata> ObjCProcessor::SerializeMethod(uint64_t loc, const Method& method)
+MachoObjCReader::MachoObjCReader(BinaryView* data) : m_reader(BinaryReader(data))
{
- std::map<std::string, Ref<Metadata>> methodMeta;
-
- methodMeta["loc"] = new Metadata(loc);
- methodMeta["name"] = new Metadata(method.name);
- methodMeta["types"] = new Metadata(method.types);
- methodMeta["imp"] = new Metadata(method.imp);
-
- return new Metadata(methodMeta);
-}
-
-
-Ref<Metadata> ObjCProcessor::SerializeClass(uint64_t loc, const Class& cls)
-{
- std::map<std::string, Ref<Metadata>> clsMeta;
-
- clsMeta["loc"] = new Metadata(loc);
- clsMeta["name"] = new Metadata(cls.name);
- clsMeta["typeName"] = new Metadata(cls.associatedName.GetString());
-
- std::vector<uint64_t> instanceMethods;
- std::vector<uint64_t> classMethods;
- instanceMethods.reserve(cls.instanceClass.methodList.size());
- classMethods.reserve(cls.metaClass.methodList.size());
- for (const auto& [location, _] : cls.instanceClass.methodList)
- instanceMethods.push_back(location);
-
- clsMeta["instanceMethods"] = new Metadata(instanceMethods);
- clsMeta["classMethods"] = new Metadata(classMethods);
-
- return new Metadata(clsMeta);
-}
-
-Ref<Metadata> ObjCProcessor::SerializeMetadata()
-{
- std::map<std::string, Ref<Metadata>> viewMeta;
- viewMeta["version"] = new Metadata((uint64_t)1);
-
- std::vector<Ref<Metadata>> classes;
- classes.reserve(m_classes.size());
- std::vector<Ref<Metadata>> categories;
- categories.reserve(m_categories.size());
- std::vector<Ref<Metadata>> methods;
- methods.reserve(m_localMethods.size());
-
- for (const auto& [clsLoc, cls] : m_classes)
- classes.push_back(SerializeClass(clsLoc, cls));
- viewMeta["classes"] = new Metadata(classes);
- for (const auto& [catLoc, cat] : m_categories)
- categories.push_back(SerializeClass(catLoc, cat));
- viewMeta["categories"] = new Metadata(categories);
- for (const auto& [methodLoc, method] : m_localMethods)
- methods.push_back(SerializeMethod(methodLoc, method));
- viewMeta["methods"] = new Metadata(methods);
-
- // Required for workflow_objc type guessing, should be removed when that is no longer a thing.
- std::vector<Ref<Metadata>> selRefToImps;
- selRefToImps.reserve(m_selRefToImplementations.size());
- for (const auto& [selRef, imps] : m_selRefToImplementations)
- {
- std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(imps)};
- Ref<Metadata> mapObject = new Metadata(mapBase);
- selRefToImps.push_back(mapObject);
- }
- viewMeta["selRefImplementations"] = new Metadata(selRefToImps);
-
- std::vector<Ref<Metadata>> selToImps;
- selToImps.reserve(m_selToImplementations.size());
- for (const auto& [selRef, imps] : m_selToImplementations)
- {
- std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(imps)};
- Ref<Metadata> mapObject = new Metadata(mapBase);
- selToImps.push_back(mapObject);
- }
- viewMeta["selImplementations"] = new Metadata(selToImps);
-
- std::vector<Ref<Metadata>> selRefToName;
- selRefToName.reserve(m_selRefToName.size());
- for (const auto& [selRef, name] : m_selRefToName)
- {
- std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(name)};
- Ref<Metadata> mapObject = new Metadata(mapBase);
- selRefToName.push_back(mapObject);
- }
- viewMeta["selRefToName"] = new Metadata(selRefToName);
- // ---
-
-
- return new Metadata(viewMeta);
-}
-
-std::vector<QualifiedNameOrType> ObjCProcessor::ParseEncodedType(const std::string& encodedType)
-{
- std::vector<QualifiedNameOrType> result;
- int pointerDepth = 0;
-
- bool readingNamedType = false;
- std::string namedType;
- int readingStructDepth = 0;
- std::string structType;
- char last;
-
- for (char c : encodedType)
- {
- if (readingNamedType && c != '"')
- {
- namedType.push_back(c);
- last = c;
- continue;
- }
- else if (readingStructDepth > 0 && c != '{' && c != '}')
- {
- structType.push_back(c);
- last = c;
- continue;
- }
-
- if (std::isdigit(c))
- continue;
-
- QualifiedNameOrType nameOrType;
- std::string qualifiedName;
-
- switch (c)
- {
- case '^':
- pointerDepth++;
- last = c;
- continue;
-
- case '"':
- if (!readingNamedType)
- {
- readingNamedType = true;
- if (last == '@')
- result.pop_back(); // We added an 'id' in the last cycle, remove it
- last = c;
- continue;
- }
- else
- {
- readingNamedType = false;
- nameOrType.name = QualifiedName(namedType);
- nameOrType.ptrCount = 1;
- break;
- }
- case '{':
- readingStructDepth++;
- last = c;
- continue;
- case '}':
- readingStructDepth--;
- if (readingStructDepth < 0)
- return {}; // seriously malformed type.
-
- if (readingStructDepth == 0)
- {
- // TODO: Emit real struct types
- nameOrType.type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
- break;
- }
- last = c;
- continue;
- case 'v':
- nameOrType.type = Type::VoidType();
- break;
- case 'c':
- nameOrType.type = Type::IntegerType(1, true);
- break;
- case 'A':
- case 'C':
- nameOrType.type = Type::IntegerType(1, false);
- break;
- case 's':
- nameOrType.type = Type::IntegerType(2, true);
- break;
- case 'S':
- nameOrType.type = Type::IntegerType(1, false);
- break;
- case 'i':
- nameOrType.type = Type::IntegerType(4, true);
- break;
- case 'I':
- nameOrType.type = Type::IntegerType(4, false);
- break;
- case 'l':
- nameOrType.type = Type::IntegerType(8, true);
- break;
- case 'L':
- nameOrType.type = Type::IntegerType(8, true);
- break;
- case 'f':
- nameOrType.type = Type::IntegerType(4, true);
- break;
- case 'b':
- case 'B':
- nameOrType.type = Type::BoolType();
- break;
- case 'q':
- qualifiedName = "NSInteger";
- break;
- case 'Q':
- qualifiedName = "NSUInteger";
- break;
- case 'd':
- qualifiedName = "CGFloat";
- break;
- case '*':
- nameOrType.type = Type::PointerType(m_data->GetAddressSize(), Type::IntegerType(1, true));
- break;
- case '@':
- qualifiedName = "id";
- // There can be a type after this, like @"NSString", that overrides this
- // The handler for " will catch it and drop this "id" entry.
- break;
- case ':':
- qualifiedName = "SEL";
- break;
- case '#':
- qualifiedName = "objc_class_t";
- break;
- case '?':
- case 'T':
- nameOrType.type = Type::PointerType(8, Type::VoidType());
- break;
- default:
- // BNLogWarn("Unknown type specifier %c", c);
- last = c;
- continue;
- }
-
- while (pointerDepth)
- {
- if (nameOrType.type)
- nameOrType.type = Type::PointerType(8, nameOrType.type);
- else
- nameOrType.ptrCount++;
-
- pointerDepth--;
- }
-
- if (!qualifiedName.empty())
- nameOrType.name = QualifiedName(qualifiedName);
-
- if (nameOrType.type == nullptr && nameOrType.name.IsEmpty())
- {
- nameOrType.type = Type::VoidType();
- }
-
- result.push_back(nameOrType);
- last = c;
- }
-
- return result;
}
-void ObjCProcessor::DefineObjCSymbol(
- BNSymbolType type, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred)
+void MachoObjCReader::Read(void* dest, size_t len)
{
- DefineObjCSymbol(type, m_data->GetTypeByName(typeName), name, addr, deferred);
+ m_reader.Read(dest, len);
}
-void ObjCProcessor::DefineObjCSymbol(
- BNSymbolType type, Ref<Type> typeRef, const std::string& name, uint64_t addr, bool deferred)
+std::string MachoObjCReader::ReadCString()
{
- if (name.size() == 0 || addr == 0)
- return;
-
- auto process = [=]() {
- NameSpace nameSpace = m_data->GetInternalNameSpace();
- if (type == ExternalSymbol)
- {
- nameSpace = m_data->GetExternalNameSpace();
- }
-
- std::string shortName = name;
- std::string fullName = name;
-
- QualifiedName varName;
-
- return std::pair<Ref<Symbol>, Ref<Type>>(
- new Symbol(type, shortName, fullName, name, addr, GlobalBinding, nameSpace), typeRef);
- };
-
- if (deferred)
- {
- m_symbolQueue->Append(process, [this, addr = addr](Symbol* symbol, Type* type) {
- // Armv7/Thumb: This will rewrite the symbol's address.
- // e.g. We pass in 0xc001, it will rewrite it to 0xc000 and create the function w/ the "thumb2" arch.
- if (Ref<Symbol> existingSymbol = m_data->GetSymbolByAddress(addr))
- m_data->UndefineAutoSymbol(existingSymbol);
- auto funcSym = m_data->DefineAutoSymbolAndVariableOrFunction(m_data->GetDefaultPlatform(), symbol, type);
- if (funcSym->GetType() == FunctionSymbol)
- {
- uint64_t target = symbol->GetAddress();
- Ref<Platform> targetPlatform =
- m_data->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); // rewrites target.
- if (Ref<Function> targetFunction = m_data->GetAnalysisFunction(targetPlatform, target))
- {
- if (!m_isBackedByDatabase)
- targetFunction->SetUserType(type);
- }
- }
- });
- return;
- }
-
- if (Ref<Symbol> existingSymbol = m_data->GetSymbolByAddress(addr))
- m_data->UndefineAutoSymbol(existingSymbol);
- auto result = process();
- auto sym = m_data->DefineAutoSymbolAndVariableOrFunction(m_data->GetDefaultPlatform(), result.first, result.second);
- if (sym->GetType() == FunctionSymbol)
- {
- uint64_t target = result.first->GetAddress();
- Ref<Platform> targetPlatform = m_data->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); // rewrites
- // target.
- if (Ref<Function> targetFunction = m_data->GetAnalysisFunction(targetPlatform, target))
- {
- if (!m_isBackedByDatabase)
- targetFunction->SetUserType(result.second);
- }
- }
-}
-
-void ObjCProcessor::LoadClasses(BinaryReader* reader, Ref<Section> classPtrSection)
-{
- if (!classPtrSection)
- return;
- auto size = classPtrSection->GetEnd() - classPtrSection->GetStart();
- if (size == 0)
- return;
- auto ptrSize = m_data->GetAddressSize();
- auto ptrCount = size / ptrSize;
-
- auto classPtrSectionStart = classPtrSection->GetStart();
- for (size_t i = 0; i < ptrCount; i++)
- {
- Class cls;
-
- view_ptr_t classPtr;
- class_t clsStruct;
- class_ro_t classRO;
-
- bool hasValidMetaClass = false;
- bool hasValidMetaClassRO = false;
- class_t metaClsStruct;
- class_ro_t metaClassRO;
-
- view_ptr_t classPointerLocation = classPtrSectionStart + (i * m_data->GetAddressSize());
- reader->Seek(classPointerLocation);
-
- classPtr = ReadPointerAccountingForRelocations(reader);
- reader->Seek(classPtr);
- try
- {
- clsStruct.isa = ReadPointerAccountingForRelocations(reader);
- clsStruct.super = reader->ReadPointer();
- clsStruct.cache = reader->ReadPointer();
- clsStruct.vtable = reader->ReadPointer();
- clsStruct.data = ReadPointerAccountingForRelocations(reader);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to read class data at 0x%llx pointed to by @ 0x%llx", reader->GetOffset(),
- classPointerLocation);
- continue;
- }
- if (clsStruct.data & 1)
- {
- m_logger->LogInfo("Skipping class at 0x%llx as it contains swift types", classPtr);
- continue;
- }
- // unset first two bits
- view_ptr_t classROPtr = clsStruct.data & ~3;
- reader->Seek(classROPtr);
- try
- {
- classRO.flags = reader->Read32();
- classRO.instanceStart = reader->Read32();
- classRO.instanceSize = reader->Read32();
- if (m_data->GetAddressSize() == 8)
- classRO.reserved = reader->Read32();
- classRO.ivarLayout = ReadPointerAccountingForRelocations(reader);
- classRO.name = ReadPointerAccountingForRelocations(reader);
- classRO.baseMethods = ReadPointerAccountingForRelocations(reader);
- classRO.baseProtocols = ReadPointerAccountingForRelocations(reader);
- classRO.ivars = ReadPointerAccountingForRelocations(reader);
- classRO.weakIvarLayout = ReadPointerAccountingForRelocations(reader);
- classRO.baseProperties = ReadPointerAccountingForRelocations(reader);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to read class RO data at 0x%llx. 0x%llx, objc_class_t @ 0x%llx",
- reader->GetOffset(), classPointerLocation, classROPtr);
- continue;
- }
-
- auto namePtr = classRO.name;
-
- std::string name;
-
- reader->Seek(namePtr);
- try
- {
- name = reader->ReadCString(500);
- }
- catch (ReadException& ex)
- {
- m_logger->LogWarn(
- "Failed to read class name at 0x%llx. Class has been given the placeholder name \"0x%llx\" ", namePtr,
- classPtr);
- char hexString[9];
- hexString[8] = 0;
- snprintf(hexString, sizeof(hexString), "%" PRIx64, classPtr);
- name = "0x" + std::string(hexString);
- }
-
- cls.name = name;
-
- DefineObjCSymbol(BNSymbolType::DataSymbol,
- Type::PointerType(m_data->GetAddressSize(), m_data->GetTypeByName(m_typeNames.cls)), "clsPtr_" + name,
- classPointerLocation, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.cls, "cls_" + name, classPtr, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.classRO, "cls_ro_" + name, classROPtr, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, Type::ArrayType(Type::IntegerType(1, true), name.size() + 1),
- "clsName_" + name, classRO.name, true);
- if (classRO.baseProtocols)
- {
- DefineObjCSymbol(BNSymbolType::DataSymbol, Type::NamedType(m_data, m_typeNames.protocolList),
- "clsProtocols_" + name, classRO.baseProtocols, true);
- reader->Seek(classRO.baseProtocols);
- uint32_t count = reader->Read64();
- view_ptr_t addr = reader->GetOffset();
- for (uint32_t j = 0; j < count; j++)
- {
- m_data->DefineDataVariable(
- addr, Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol)));
- addr += ptrSize;
- }
- }
-
- if (clsStruct.isa)
- {
- reader->Seek(clsStruct.isa);
- try
- {
- metaClsStruct.isa = ReadPointerAccountingForRelocations(reader);
- metaClsStruct.super = reader->ReadPointer();
- metaClsStruct.cache = reader->ReadPointer();
- metaClsStruct.vtable = reader->ReadPointer();
- metaClsStruct.data = ReadPointerAccountingForRelocations(reader) & ~1;
- DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.cls, "metacls_" + name, clsStruct.isa, true);
- hasValidMetaClass = true;
- }
- catch (ReadException& ex)
- {
- m_logger->LogWarn("Failed to read metaclass data at 0x%llx pointed to by objc_class_t @ 0x%llx",
- reader->GetOffset(), classPtr);
- }
- }
- if (hasValidMetaClass && (metaClsStruct.data & 1))
- {
- m_logger->LogInfo("Skipping metaclass at 0x%llx as it contains swift types", classPtr);
- hasValidMetaClass = false;
- }
- if (hasValidMetaClass)
- {
- reader->Seek(metaClsStruct.data);
- try
- {
- metaClassRO.flags = reader->Read32();
- metaClassRO.instanceStart = reader->Read32();
- metaClassRO.instanceSize = reader->Read32();
- if (m_data->GetAddressSize() == 8)
- metaClassRO.reserved = reader->Read32();
- metaClassRO.ivarLayout = ReadPointerAccountingForRelocations(reader);
- metaClassRO.name = ReadPointerAccountingForRelocations(reader);
- metaClassRO.baseMethods = ReadPointerAccountingForRelocations(reader);
- metaClassRO.baseProtocols = ReadPointerAccountingForRelocations(reader);
- metaClassRO.ivars = ReadPointerAccountingForRelocations(reader);
- metaClassRO.weakIvarLayout = ReadPointerAccountingForRelocations(reader);
- metaClassRO.baseProperties = ReadPointerAccountingForRelocations(reader);
- DefineObjCSymbol(
- BNSymbolType::DataSymbol, m_typeNames.classRO, "metacls_ro_" + name, metaClsStruct.data, true);
- hasValidMetaClassRO = true;
- }
- catch (ReadException& ex)
- {
- m_logger->LogWarn("Failed to read metaclass RO data at 0x%llx pointed to by meta objc_class_t @ 0x%llx",
- reader->GetOffset(), clsStruct.isa);
- }
- }
-
- if (classRO.baseMethods)
- {
- try
- {
- ReadMethodList(reader, cls.instanceClass, name, classRO.baseMethods);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to read the method list for class pointed to by 0x%llx", clsStruct.data);
- }
- }
- if (hasValidMetaClassRO && metaClassRO.baseMethods)
- {
- try
- {
- ReadMethodList(reader, cls.metaClass, name, metaClassRO.baseMethods);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to read the method list for metaclass pointed to by 0x%llx", clsStruct.data);
- }
- }
-
- if (classRO.ivars)
- {
- try
- {
- ReadIvarList(reader, cls.instanceClass, name, classRO.ivars);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to process ivars for class at 0x%llx", clsStruct.data);
- }
- }
- m_classes[classPtr] = cls;
- }
-}
-
-void ObjCProcessor::LoadCategories(BinaryReader* reader, Ref<Section> classPtrSection)
-{
- if (!classPtrSection)
- return;
- auto size = classPtrSection->GetEnd() - classPtrSection->GetStart();
- if (size == 0)
- return;
- auto ptrSize = m_data->GetAddressSize();
-
- auto classPtrSectionStart = classPtrSection->GetStart();
- auto classPtrSectionEnd = classPtrSection->GetEnd();
-
- auto catType = Type::NamedType(m_data, m_typeNames.category);
- auto ptrType = Type::PointerType(m_data->GetDefaultArchitecture(), catType);
- for (size_t i = classPtrSectionStart; i < classPtrSectionEnd; i += ptrSize)
- {
- Class category;
- category_t cat;
-
- reader->Seek(i);
- auto catLocation = ReadPointerAccountingForRelocations(reader);
- reader->Seek(catLocation);
-
- try
- {
- cat.name = ReadPointerAccountingForRelocations(reader);
- cat.cls = ReadPointerAccountingForRelocations(reader);
- cat.instanceMethods = ReadPointerAccountingForRelocations(reader);
- cat.classMethods = ReadPointerAccountingForRelocations(reader);
- cat.protocols = ReadPointerAccountingForRelocations(reader);
- cat.instanceProperties = ReadPointerAccountingForRelocations(reader);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to read category pointed to by 0x%llx", i);
- continue;
- }
-
- std::string categoryAdditionsName;
- std::string categoryBaseClassName;
-
- if (const auto& it = m_classes.find(cat.cls); it != m_classes.end())
- {
- 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)
- {
- const auto& symbolName = symbol->GetFullName();
- if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0)
- categoryBaseClassName = symbolName.substr(14, symbolName.size() - 14);
- }
- }
- if (categoryBaseClassName.empty())
- {
- m_logger->LogError(
- "Failed to determine base classname for category at 0x%llx. Using base address as stand-in classname",
- catLocation);
- categoryBaseClassName = std::to_string(catLocation);
- }
- try
- {
- reader->Seek(cat.name);
- categoryAdditionsName = reader->ReadCString();
- }
- catch (ReadException& ex)
- {
- m_logger->LogError(
- "Failed to read category name for category at 0x%llx. Using base address as stand-in category name",
- catLocation);
- categoryAdditionsName = std::to_string(catLocation);
- }
- category.name = categoryBaseClassName + " (" + categoryAdditionsName + ")";
- DefineObjCSymbol(BNSymbolType::DataSymbol, ptrType, "categoryPtr_" + category.name, i, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, catType, "category_" + category.name, catLocation, true);
-
- if (cat.instanceMethods)
- {
- try
- {
- ReadMethodList(reader, category.instanceClass, category.name, cat.instanceMethods);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError(
- "Failed to read the instance method list for category pointed to by 0x%llx", catLocation);
- }
- }
- if (cat.classMethods)
- {
- try
- {
- ReadMethodList(reader, category.metaClass, category.name, cat.classMethods);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError(
- "Failed to read the class method list for category pointed to by 0x%llx", catLocation);
- }
- }
- m_categories[catLocation] = category;
- }
-}
-
-void ObjCProcessor::LoadProtocols(BinaryReader* reader, Ref<Section> listSection)
-{
- if (!listSection)
- return;
- auto size = listSection->GetEnd() - listSection->GetStart();
- if (size == 0)
- return;
- auto ptrSize = m_data->GetAddressSize();
-
- auto listSectionStart = listSection->GetStart();
- auto listSectionEnd = listSection->GetEnd();
-
- auto protocolType = Type::NamedType(m_data, m_typeNames.protocol);
- auto ptrType = Type::PointerType(m_data->GetDefaultArchitecture(), protocolType);
- for (size_t i = listSectionStart; i < listSectionEnd; i += ptrSize)
- {
- protocol_t protocol;
- reader->Seek(i);
- auto protocolLocation = ReadPointerAccountingForRelocations(reader);
- reader->Seek(protocolLocation);
-
- try
- {
- protocol.isa = ReadPointerAccountingForRelocations(reader);
- protocol.mangledName = ReadPointerAccountingForRelocations(reader);
- protocol.protocols = ReadPointerAccountingForRelocations(reader);
- protocol.instanceMethods = ReadPointerAccountingForRelocations(reader);
- protocol.classMethods = ReadPointerAccountingForRelocations(reader);
- protocol.optionalInstanceMethods = ReadPointerAccountingForRelocations(reader);
- protocol.optionalClassMethods = ReadPointerAccountingForRelocations(reader);
- protocol.instanceProperties = ReadPointerAccountingForRelocations(reader);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to read protocol pointed to by 0x%llx", i);
- continue;
- }
-
- std::string protocolName;
- try
- {
- reader->Seek(protocol.mangledName);
- protocolName = reader->ReadCString();
- DefineObjCSymbol(BNSymbolType::DataSymbol,
- Type::ArrayType(Type::IntegerType(1, true), protocolName.size() + 1), "protocolName_" + protocolName,
- protocol.mangledName, true);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError(
- "Failed to read protocol name for protocol at 0x%llx. Using base address as stand-in protocol name",
- protocolLocation);
- protocolName = std::to_string(protocolLocation);
- }
-
- Protocol protocolClass;
- protocolClass.name = protocolName;
- DefineObjCSymbol(BNSymbolType::DataSymbol, ptrType, "protocolPtr_" + protocolName, i, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, protocolType, "protocol_" + protocolName, protocolLocation, true);
- if (protocol.protocols)
- {
- DefineObjCSymbol(BNSymbolType::DataSymbol, Type::NamedType(m_data, m_typeNames.protocolList),
- "protoProtocols_" + protocolName, protocol.protocols, true);
- reader->Seek(protocol.protocols);
- uint32_t count = reader->Read64();
- view_ptr_t addr = reader->GetOffset();
- for (uint32_t j = 0; j < count; j++)
- {
- m_data->DefineDataVariable(
- addr, Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol)));
- addr += ptrSize;
- }
- }
-
- if (protocol.instanceMethods)
- {
- try
- {
- ReadMethodList(reader, protocolClass.instanceMethods, protocolName, protocol.instanceMethods);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError(
- "Failed to read the instance method list for protocol pointed to by 0x%llx", protocolLocation);
- }
- }
- if (protocol.classMethods)
- {
- try
- {
- ReadMethodList(reader, protocolClass.classMethods, protocolName, protocol.classMethods);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError(
- "Failed to read the class method list for protocol pointed to by 0x%llx", protocolLocation);
- }
- }
- if (protocol.optionalInstanceMethods)
- {
- try
- {
- ReadMethodList(
- reader, protocolClass.optionalInstanceMethods, protocolName, protocol.optionalInstanceMethods);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to read the optional instance method list for protocol pointed to by 0x%llx",
- protocolLocation);
- }
- }
- if (protocol.optionalClassMethods)
- {
- try
- {
- ReadMethodList(reader, protocolClass.optionalClassMethods, protocolName, protocol.optionalClassMethods);
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to read the optional class method list for protocol pointed to by 0x%llx",
- protocolLocation);
- }
- }
- m_protocols[protocolLocation] = protocolClass;
- }
+ return m_reader.ReadCString();
}
-void ObjCProcessor::ReadMethodList(BinaryReader* reader, ClassBase& cls, std::string name, view_ptr_t start)
+uint8_t MachoObjCReader::Read8()
{
- reader->Seek(start);
- method_list_t head;
- head.entsizeAndFlags = reader->Read32();
- head.count = reader->Read32();
- uint64_t pointerSize = m_data->GetAddressSize();
- bool relativeOffsets = (head.entsizeAndFlags & 0xFFFF0000) & 0x80000000;
- bool directSelectors = (head.entsizeAndFlags & 0xFFFF0000) & 0x40000000;
- auto methodSize = relativeOffsets ? 12 : pointerSize * 3;
- DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + name, start, true);
-
- for (unsigned i = 0; i < head.count; i++)
- {
- try
- {
- Method method;
- auto cursor = start + sizeof(method_list_t) + (i * methodSize);
- reader->Seek(cursor);
- method_t meth;
- // workflow_objc support
- uint64_t selRefAddr = 0;
- uint64_t selAddr = 0;
- // --
- if (relativeOffsets)
- {
- meth.name = cursor + static_cast<int32_t>(reader->Read32());
- meth.types = cursor + 4 + static_cast<int32_t>(reader->Read32());
- meth.imp = cursor + 8 + static_cast<int32_t>(reader->Read32());
- }
- else
- {
- meth.name = ReadPointerAccountingForRelocations(reader);
- meth.types = ReadPointerAccountingForRelocations(reader);
- meth.imp = ReadPointerAccountingForRelocations(reader);
- }
- if (!relativeOffsets || directSelectors)
- {
- reader->Seek(meth.name);
- selAddr = meth.name;
- method.name = reader->ReadCString();
- reader->Seek(meth.types);
- method.types = reader->ReadCString();
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.name.size() + 1),
- "sel_" + method.name, meth.name, true);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.types.size() + 1),
- "selTypes_" + method.name, meth.types, true);
- }
- else
- {
- std::string sel;
- view_ptr_t selRef;
- reader->Seek(meth.name);
- selRefAddr = meth.name;
- selRef = ReadPointerAccountingForRelocations(reader);
- reader->Seek(meth.types);
- method.types = reader->ReadCString();
- selAddr = selRef;
- if (const auto& it = m_selectorCache.find(selRef); it != m_selectorCache.end())
- method.name = it->second;
- else
- {
- reader->Seek(selRef);
- method.name = reader->ReadCString(selRef);
- m_selectorCache[selRef] = method.name;
- }
- auto selType = Type::ArrayType(Type::IntegerType(1, true), method.name.size() + 1);
- DefineObjCSymbol(DataSymbol, selType, "sel_" + method.name, selRef, true);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.types.size() + 1),
- "selTypes_" + method.name, meth.types, true);
- DefineObjCSymbol(DataSymbol, Type::PointerType(m_data->GetAddressSize(), selType),
- "selRef_" + method.name, meth.name, true);
- }
- // workflow objc support
- if (selAddr)
- m_selToImplementations[selAddr].push_back(meth.imp);
- if (selRefAddr)
- m_selRefToImplementations[selRefAddr].push_back(meth.imp);
- // --
-
- DefineObjCSymbol(DataSymbol, relativeOffsets ? m_typeNames.methodEntry : m_typeNames.method,
- "method_" + method.name, cursor, true);
- method.imp = meth.imp;
- cls.methodList[cursor] = method;
- m_localMethods[cursor] = method;
- }
- catch (ReadException& ex)
- {
- m_logger->LogError(
- "Failed to process a method at offset 0x%llx", start + sizeof(method_list_t) + (i * methodSize));
- }
- }
+ return m_reader.Read8();
}
-void ObjCProcessor::ReadIvarList(BinaryReader* reader, ClassBase& cls, std::string name, view_ptr_t start)
+uint16_t MachoObjCReader::Read16()
{
- reader->Seek(start);
- ivar_list_t head;
- head.entsizeAndFlags = reader->Read32();
- head.count = reader->Read32();
- auto addressSize = m_data->GetAddressSize();
- DefineObjCSymbol(DataSymbol, m_typeNames.ivarList, "ivar_list_" + name, start, true);
- for (unsigned i = 0; i < head.count; i++)
- {
- try
- {
- Ivar ivar;
- ivar_t ivarStruct;
- uint64_t cursor = start + (sizeof(ivar_list_t)) + (i * ((addressSize * 3) + 8));
- reader->Seek(cursor);
- ivarStruct.offset = ReadPointerAccountingForRelocations(reader);
- ivarStruct.name = ReadPointerAccountingForRelocations(reader);
- ivarStruct.type = ReadPointerAccountingForRelocations(reader);
- ivarStruct.alignmentRaw = reader->Read32();
- ivarStruct.size = reader->Read32();
-
- reader->Seek(ivarStruct.offset);
- ivar.offset = reader->Read32();
- reader->Seek(ivarStruct.name);
- ivar.name = reader->ReadCString();
- reader->Seek(ivarStruct.type);
- ivar.type = reader->ReadCString();
-
- DefineObjCSymbol(DataSymbol, m_typeNames.ivar, "ivar_" + ivar.name, cursor, true);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), ivar.name.size() + 1),
- "ivarName_" + ivar.name, ivarStruct.name, true);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), ivar.type.size() + 1),
- "ivarType_" + ivar.name, ivarStruct.type, true);
-
- cls.ivarList[cursor] = ivar;
- }
- catch (ReadException& ex)
- {
- m_logger->LogError("Failed to process an ivar at offset 0x%llx",
- start + (sizeof(ivar_list_t)) + (i * ((addressSize * 3) + 8)));
- }
- }
+ return m_reader.Read16();
}
-bool ObjCProcessor::ViewHasObjCMetadata(BinaryNinja::BinaryView* data)
+uint32_t MachoObjCReader::Read32()
{
- return (data->GetSectionByName("__objc_classlist") || data->GetSectionByName("__objc_catlist")
- || data->GetSectionByName("__objc_protolist"));
+ return m_reader.Read32();
}
-
-std::pair<QualifiedName, Ref<Type>> finalizeStructureBuilder(
- Ref<BinaryView> m_data, StructureBuilder sb, std::string name)
+uint64_t MachoObjCReader::Read64()
{
- auto classTypeStruct = sb.Finalize();
-
- QualifiedName classTypeName(name);
- auto classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
- auto classType = Type::StructureType(classTypeStruct);
- auto classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
-
- return {classQualName, classType};
+ return m_reader.Read64();
}
-std::pair<QualifiedName, Ref<Type>> finalizeEnumerationBuilder(
- Ref<BinaryView> m_data, EnumerationBuilder eb, uint64_t size, QualifiedName name)
+int8_t MachoObjCReader::ReadS8()
{
- auto enumTypeStruct = eb.Finalize();
-
- auto enumTypeId = Type::GenerateAutoTypeId("objc", name);
- auto enumType = Type::EnumerationType(enumTypeStruct, size);
- auto enumQualName = m_data->DefineType(enumTypeId, name, enumType);
-
- return {enumQualName, enumType};
+ return static_cast<int8_t>(m_reader.Read8());
}
-inline QualifiedName defineTypedef(Ref<BinaryView> m_data, const QualifiedName name, Ref<Type> type)
+int16_t MachoObjCReader::ReadS16()
{
- auto typeID = Type::GenerateAutoTypeId("objc", name);
- m_data->DefineType(typeID, name, type);
- return m_data->GetTypeNameById(typeID);
+ return static_cast<int16_t>(m_reader.Read16());
}
-void ObjCProcessor::GenerateClassTypes()
+int32_t MachoObjCReader::ReadS32()
{
- for (auto& [_, cls] : m_classes)
- {
- QualifiedName typeName;
- StructureBuilder classTypeBuilder;
- bool failedToDecodeType = false;
- for (const auto& [ivarLoc, ivar] : cls.instanceClass.ivarList)
- {
- auto encodedTypeList = ParseEncodedType(ivar.type);
- if (encodedTypeList.empty())
- {
- failedToDecodeType = true;
- break;
- }
- auto encodedType = encodedTypeList.at(0);
-
- Ref<Type> type;
-
- if (encodedType.type)
- type = encodedType.type;
- else
- {
- type = Type::NamedType(encodedType.name, Type::PointerType(m_data->GetAddressSize(), Type::VoidType()));
- for (size_t i = encodedType.ptrCount; i > 0; i--)
- type = Type::PointerType(m_data->GetAddressSize(), type);
- }
-
- if (!type)
- type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
-
- classTypeBuilder.AddMemberAtOffset(type, ivar.name, ivar.offset);
- }
- if (failedToDecodeType)
- continue;
- auto classTypeStruct = classTypeBuilder.Finalize();
- QualifiedName classTypeName = cls.name;
- std::string classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
- Ref<Type> classType = Type::StructureType(classTypeStruct);
- QualifiedName classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
- cls.associatedName = classTypeName;
- }
+ return static_cast<int32_t>(m_reader.Read32());
}
-bool ObjCProcessor::ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod)
+int64_t MachoObjCReader::ReadS64()
{
- std::stringstream r(method.name);
-
- std::string token;
- std::vector<std::string> selectorTokens;
- while (std::getline(r, token, ':'))
- selectorTokens.push_back(token);
-
- std::vector<QualifiedNameOrType> typeTokens = ParseEncodedType(method.types);
- if (typeTokens.empty())
- return false;
-
- auto typeForQualifiedNameOrType = [this](QualifiedNameOrType nameOrType) {
- Ref<Type> type;
-
- if (nameOrType.type)
- {
- type = nameOrType.type;
- if (!type)
- type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
- }
- else
- {
- type = Type::NamedType(nameOrType.name, Type::PointerType(m_data->GetAddressSize(), Type::VoidType()));
- for (size_t i = nameOrType.ptrCount; i > 0; i--)
- type = Type::PointerType(m_data->GetAddressSize(), type);
- }
-
- return type;
- };
-
- BinaryNinja::QualifiedNameAndType nameAndType;
- std::set<BinaryNinja::QualifiedName> typesAllowRedefinition;
-
- auto retType = typeForQualifiedNameOrType(typeTokens[0]);
-
- std::vector<BinaryNinja::FunctionParameter> params;
- auto cc = m_data->GetDefaultPlatform()->GetDefaultCallingConvention();
-
- params.push_back({"self",
- cls.associatedName.IsEmpty() ?
- Type::NamedType(m_data, {"id"}) :
- Type::PointerType(m_data->GetAddressSize(), Type::NamedType(m_data, cls.associatedName)),
- true, BinaryNinja::Variable()});
-
- params.push_back({"sel", Type::NamedType(m_data, {"SEL"}), true, BinaryNinja::Variable()});
-
- for (size_t i = 3; i < typeTokens.size(); i++)
- {
- std::string suffix;
-
- params.push_back({selectorTokens.size() > i - 3 ? selectorTokens[i - 3] : "arg",
- typeForQualifiedNameOrType(typeTokens[i]), true, BinaryNinja::Variable()});
- }
-
- auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params);
-
- // Search for the method's implementation function; apply the type if found.
- std::string prefix = isInstanceMethod ? "-" : "+";
- auto name = prefix + "[" + cls.name + " " + method.name + "]";
-
- DefineObjCSymbol(FunctionSymbol, funcType, name, method.imp, true);
-
- return true;
+ return static_cast<int64_t>(m_reader.Read64());
}
-void ObjCProcessor::ApplyMethodTypes(Class& cls)
+uint64_t MachoObjCReader::ReadPointer()
{
- for (auto& [_, method] : cls.instanceClass.methodList)
- {
- ApplyMethodType(cls, method, true);
- }
- for (auto& [_, method] : cls.metaClass.methodList)
- {
- ApplyMethodType(cls, method, false);
- }
+ return m_reader.ReadPointer();
}
-void ObjCProcessor::PostProcessObjCSections(BinaryReader* reader)
+uint64_t MachoObjCReader::GetOffset() const
{
- auto ptrSize = m_data->GetAddressSize();
- if (auto imageInfo = m_data->GetSectionByName("__objc_imageinfo"))
- {
- auto start = imageInfo->GetStart();
- auto type = Type::NamedType(m_data, m_typeNames.imageInfo);
- m_data->DefineDataVariable(start, type);
- }
- if (auto selrefs = m_data->GetSectionByName("__objc_selrefs"))
- {
- auto start = selrefs->GetStart();
- auto end = selrefs->GetEnd();
- auto type = Type::PointerType(ptrSize, Type::IntegerType(1, false));
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- reader->Seek(i);
- auto selLoc = ReadPointerAccountingForRelocations(reader);
- std::string sel;
- if (const auto& it = m_selectorCache.find(selLoc); it != m_selectorCache.end())
- sel = it->second;
- else
- {
- reader->Seek(selLoc);
- sel = reader->ReadCString();
- m_selectorCache[selLoc] = sel;
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), sel.size() + 1), "sel_" + sel,
- selLoc, true);
- }
- DefineObjCSymbol(DataSymbol, type, "selRef_" + sel, i, true);
- }
- }
- if (auto superRefs = m_data->GetSectionByName("__objc_classrefs"))
- {
- auto start = superRefs->GetStart();
- auto end = superRefs->GetEnd();
- auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- reader->Seek(i);
- auto clsLoc = ReadPointerAccountingForRelocations(reader);
- if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
- {
- auto& cls = it->second;
- std::string name = cls.name;
- if (!name.empty())
- DefineObjCSymbol(DataSymbol, type, "clsRef_" + name, i, true);
- }
- }
- }
- if (auto superRefs = m_data->GetSectionByName("__objc_superrefs"))
- {
- auto start = superRefs->GetStart();
- auto end = superRefs->GetEnd();
- auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- reader->Seek(i);
- auto clsLoc = ReadPointerAccountingForRelocations(reader);
- if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
- {
- auto& cls = it->second;
- std::string name = cls.name;
- if (!name.empty())
- DefineObjCSymbol(DataSymbol, type, "superRef_" + name, i, true);
- }
- }
- }
- if (auto protoRefs = m_data->GetSectionByName("__objc_protorefs"))
- {
- auto start = protoRefs->GetStart();
- auto end = protoRefs->GetEnd();
- auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol));
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- reader->Seek(i);
- auto protoLoc = ReadPointerAccountingForRelocations(reader);
- if (const auto& it = m_protocols.find(protoLoc); it != m_protocols.end())
- {
- auto& proto = it->second;
- std::string name = proto.name;
- if (!name.empty())
- DefineObjCSymbol(DataSymbol, type, "protoRef_" + name, i, true);
- }
- }
- }
- if (auto ivars = m_data->GetSectionByName("__objc_ivar"))
- {
- auto start = ivars->GetStart();
- auto end = ivars->GetEnd();
- auto ivarSectionEntryTypeBuilder = new TypeBuilder(Type::IntegerType(8, false));
- ivarSectionEntryTypeBuilder->SetConst(true);
- auto type = ivarSectionEntryTypeBuilder->Finalize();
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- m_data->DefineDataVariable(i, type);
- }
- }
+ return m_reader.GetOffset();
}
-uint64_t ObjCProcessor::ReadPointerAccountingForRelocations(BinaryReader* reader)
+void MachoObjCReader::Seek(uint64_t offset)
{
- if (auto it = m_relocationPointerRewrites.find(reader->GetOffset()); it != m_relocationPointerRewrites.end())
- {
- reader->SeekRelative(m_data->GetAddressSize());
- return it->second;
- }
- return reader->ReadPointer();
+ m_reader.Seek(offset);
}
-
-ObjCProcessor::ObjCProcessor(BinaryNinja::BinaryView* data, bool isBackedByDatabase) :
- m_isBackedByDatabase(isBackedByDatabase), m_data(data)
+void MachoObjCReader::SeekRelative(int64_t offset)
{
- m_logger = m_data->CreateLogger("macho.objc");
+ m_reader.SeekRelative(offset);
}
-void ObjCProcessor::ProcessObjCData()
+std::shared_ptr<ObjCReader> MachoObjCProcessor::GetReader()
{
- m_symbolQueue = new SymbolQueue();
- auto addrSize = m_data->GetAddressSize();
-
- // m_typeNames.relativePtr = defineTypedef(m_data, {"rptr_t"}, Type::IntegerType(4, true));
- // auto rptr_t = Type::NamedType(m_data, m_typeNames.relativePtr);
- auto rptr_t = Type::NamedType(m_data, defineTypedef(m_data, {"rptr_t"}, Type::IntegerType(4, true)));
-
- // m_typeNames.id = defineTypedef(m_data, {"id"}, Type::PointerType(addrSize, Type::VoidType()));
- // m_typeNames.sel = defineTypedef(m_data, {"SEL"}, Type::PointerType(addrSize, Type::IntegerType(1, false)));
- //
- // m_typeNames.BOOL = defineTypedef(m_data, {"BOOL"}, Type::IntegerType(1, false));
- // m_typeNames.nsInteger = defineTypedef(m_data, {"NSInteger"}, Type::IntegerType(addrSize, true));
- // m_typeNames.nsuInteger = defineTypedef(m_data, {"NSUInteger"}, Type::IntegerType(addrSize, false));
- // m_typeNames.cgFloat = defineTypedef(m_data, {"CGFloat"}, Type::FloatType(addrSize));
-
- // https://github.com/apple-oss-distributions/objc4/blob/196363c165b175ed925ef6b9b99f558717923c47/runtime/objc-abi.h
- EnumerationBuilder imageInfoFlagBuilder;
- imageInfoFlagBuilder.AddMemberWithValue("IsReplacement", 1 << 0);
- imageInfoFlagBuilder.AddMemberWithValue("SupportsGC", 1 << 1);
- imageInfoFlagBuilder.AddMemberWithValue("RequiresGC", 1 << 2);
- imageInfoFlagBuilder.AddMemberWithValue("OptimizedByDyld", 1 << 3);
- imageInfoFlagBuilder.AddMemberWithValue("CorrectedSynthesize", 1 << 4);
- imageInfoFlagBuilder.AddMemberWithValue("IsSimulated", 1 << 5);
- imageInfoFlagBuilder.AddMemberWithValue("HasCategoryClassProperties", 1 << 6);
- imageInfoFlagBuilder.AddMemberWithValue("OptimizedByDyldClosure", 1 << 7);
- imageInfoFlagBuilder.AddMemberWithValue("SwiftUnstableVersionMask", 0xff << 8);
- imageInfoFlagBuilder.AddMemberWithValue("SwiftStableVersionMask", 0xFFFF << 16);
- auto imageInfoFlagType = finalizeEnumerationBuilder(m_data, imageInfoFlagBuilder, 4, {"objc_image_info_flags"});
- m_typeNames.imageInfoFlags = imageInfoFlagType.first;
-
- EnumerationBuilder swiftVersionBuilder;
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion1", 1);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion1_2", 2);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion2", 3);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion3", 4);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion4", 5);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion4_1", 6); // [sic]
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion4_2", 6);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion5", 7);
- auto swiftVersionType =
- finalizeEnumerationBuilder(m_data, swiftVersionBuilder, 4, {"objc_image_info_swift_version"});
- m_typeNames.imageInfoSwiftVersion = swiftVersionType.first;
-
- StructureBuilder imageInfoBuilder;
- imageInfoBuilder.AddMember(Type::IntegerType(4, false), "version");
- imageInfoBuilder.AddMember(Type::NamedType(m_data, m_typeNames.imageInfoFlags), "flags");
- auto imageInfoType = finalizeStructureBuilder(m_data, imageInfoBuilder, "objc_image_info_t");
- m_typeNames.imageInfo = imageInfoType.first;
-
- StructureBuilder methodEntry;
- methodEntry.AddMember(rptr_t, "name");
- methodEntry.AddMember(rptr_t, "types");
- methodEntry.AddMember(rptr_t, "imp");
- auto type = finalizeStructureBuilder(m_data, methodEntry, "objc_method_entry_t");
- m_typeNames.methodEntry = type.first;
-
- StructureBuilder method;
- method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
- method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "types");
- method.AddMember(Type::PointerType(addrSize, Type::VoidType()), "imp");
- type = finalizeStructureBuilder(m_data, method, "objc_method_t");
- m_typeNames.method = type.first;
-
- StructureBuilder methList;
- methList.AddMember(Type::IntegerType(4, false), "obsolete");
- methList.AddMember(Type::IntegerType(4, false), "count");
- type = finalizeStructureBuilder(m_data, methList, "objc_method_list_t");
- m_typeNames.methodList = type.first;
-
- StructureBuilder ivarBuilder;
- ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(4, false)), "offset");
- ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
- ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "type");
- ivarBuilder.AddMember(Type::IntegerType(4, false), "alignment");
- ivarBuilder.AddMember(Type::IntegerType(4, false), "size");
- type = finalizeStructureBuilder(m_data, ivarBuilder, "objc_ivar_t");
- m_typeNames.ivar = type.first;
-
- StructureBuilder ivarList;
- ivarList.AddMember(Type::IntegerType(4, false), "entsize");
- ivarList.AddMember(Type::IntegerType(4, false), "count");
- type = finalizeStructureBuilder(m_data, ivarList, "objc_ivar_list_t");
- m_typeNames.ivarList = type.first;
-
- StructureBuilder protocolListBuilder;
- protocolListBuilder.AddMember(Type::IntegerType(addrSize, false), "count");
- m_typeNames.protocolList = finalizeStructureBuilder(m_data, protocolListBuilder, "objc_protocol_list_t").first;
-
- StructureBuilder classROBuilder;
- classROBuilder.AddMember(Type::IntegerType(4, false), "flags");
- classROBuilder.AddMember(Type::IntegerType(4, false), "start");
- classROBuilder.AddMember(Type::IntegerType(4, false), "size");
- if (addrSize == 8)
- classROBuilder.AddMember(Type::IntegerType(4, false), "reserved");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "ivar_layout");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "methods");
- classROBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.protocolList)), "protocols");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.ivarList)), "ivars");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "weak_ivar_layout");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "properties");
- type = finalizeStructureBuilder(m_data, classROBuilder, "objc_class_ro_t");
- m_typeNames.classRO = type.first;
-
- QualifiedName classTypeName("objc_class_t");
- auto classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
- auto isaType = Type::PointerType(m_data->GetDefaultArchitecture(),
- TypeBuilder::NamedType(
- new NamedTypeReferenceBuilder(StructNamedTypeClass, "", classTypeName), m_data->GetAddressSize(), 4)
- .Finalize());
-
- StructureBuilder classBuilder;
- classBuilder.AddMember(isaType, "isa");
- classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "super");
- classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "cache");
- classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "vtable");
- classBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.classRO)), "data");
-
- auto classTypeStruct = classBuilder.Finalize();
- auto classType = Type::StructureType(classTypeStruct);
- auto classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
-
- m_typeNames.cls = classQualName;
-
- StructureBuilder categoryBuilder;
- categoryBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "category_name");
- categoryBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.cls)), "class");
- categoryBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "inst_methods");
- categoryBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "class_methods");
- categoryBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "protocols");
- categoryBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "properties");
- m_typeNames.category = finalizeStructureBuilder(m_data, categoryBuilder, "objc_category_t").first;
-
- StructureBuilder protocolBuilder;
- protocolBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "isa");
- protocolBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "mangledName");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.protocolList)), "protocols");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "instanceMethods");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "classMethods");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "optionalInstanceMethods");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "optionalClassMethods");
- protocolBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "instanceProperties");
- protocolBuilder.AddMember(Type::IntegerType(4, false), "size");
- protocolBuilder.AddMember(Type::IntegerType(4, false), "flags");
- m_typeNames.protocol = finalizeStructureBuilder(m_data, protocolBuilder, "objc_protocol_t").first;
-
- auto reader = BinaryReader(m_data);
- m_data->BeginBulkModifySymbols();
- if (auto classList = m_data->GetSectionByName("__objc_classlist"))
- LoadClasses(&reader, classList);
- if (auto nonLazyClassList = m_data->GetSectionByName("__objc_nlclslist"))
- LoadClasses(&reader, nonLazyClassList); // See: https://stackoverflow.com/a/15318325
-
- GenerateClassTypes();
- for (auto& [_, cls] : m_classes)
- ApplyMethodTypes(cls);
-
- if (auto catList = m_data->GetSectionByName("__objc_catlist")) // Do this after loading class type data.
- LoadCategories(&reader, catList);
- if (auto nonLazyCatList = m_data->GetSectionByName("__objc_nlcatlist")) // Do this after loading class type data.
- LoadCategories(&reader, nonLazyCatList);
- for (auto& [_, cat] : m_categories)
- ApplyMethodTypes(cat);
-
- if (auto protoList = m_data->GetSectionByName("__objc_protolist"))
- LoadProtocols(&reader, protoList);
-
- PostProcessObjCSections(&reader);
-
- auto id = m_data->BeginUndoActions();
- m_symbolQueue->Process();
- m_data->EndBulkModifySymbols();
- delete m_symbolQueue;
- m_data->ForgetUndoActions(id);
-
- auto meta = SerializeMetadata();
- m_data->StoreMetadata("Objective-C", meta, true);
-
- m_relocationPointerRewrites.clear();
+ return std::make_shared<MachoObjCReader>(m_data);
}
-
-void ObjCProcessor::ProcessCFStrings()
+bool MachoObjCProcessor::ViewHasObjCMetadata(BinaryView* data)
{
- m_symbolQueue = new SymbolQueue();
- uint64_t ptrSize = m_data->GetAddressSize();
- // https://github.com/apple/llvm-project/blob/next/clang/lib/CodeGen/CodeGenModule.cpp#L6129
- // See also ASTContext.cpp ctrl+f __NSConstantString_tag
-
- // The place these flags are used is unclear, along with any clear flag definitions, but they are useful for
- // introspection
- EnumerationBuilder __cfStringFlagBuilder;
- __cfStringFlagBuilder.AddMemberWithValue("SwiftABI", 0b1);
- __cfStringFlagBuilder.AddMemberWithValue("Swift4_1", 0b100);
- // LLVM also sets 0x7c0 (0b11111000000) on both UTF8 and UTF16 strings however it is unclear what this denotes.
- __cfStringFlagBuilder.AddMemberWithValue("UTF8", 0b1000);
- __cfStringFlagBuilder.AddMemberWithValue("UTF16", 0b10000);
- auto type = finalizeEnumerationBuilder(m_data, __cfStringFlagBuilder, ptrSize, {"CFStringFlag"});
- m_typeNames.cfStringFlag = type.first;
-
- StructureBuilder __cfStringStructBuilder;
- __cfStringStructBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
- __cfStringStructBuilder.AddMember(Type::NamedType(m_data, m_typeNames.cfStringFlag), "flags");
- __cfStringStructBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(1, true)), "data");
- __cfStringStructBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
- type = finalizeStructureBuilder(m_data, __cfStringStructBuilder, "__NSConstantString");
- m_typeNames.cfString = type.first;
-
- StructureBuilder __cfStringUTF16StructBuilder;
- __cfStringUTF16StructBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
- __cfStringUTF16StructBuilder.AddMember(Type::NamedType(m_data, m_typeNames.cfStringFlag), "flags");
- __cfStringUTF16StructBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(2, true)), "data");
- __cfStringUTF16StructBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
- type = finalizeStructureBuilder(m_data, __cfStringUTF16StructBuilder, "__NSConstantString_UTF16");
- m_typeNames.cfStringUTF16 = type.first;
-
- auto reader = BinaryReader(m_data);
- if (auto cfstrings = m_data->GetSectionByName("__cfstring"))
- {
- auto start = cfstrings->GetStart();
- auto end = cfstrings->GetEnd();
- auto typeWidth = Type::NamedType(m_data, m_typeNames.cfString)->GetWidth();
- m_data->BeginBulkModifySymbols();
- for (view_ptr_t i = start; i < end; i += typeWidth)
- {
- reader.Seek(i + ptrSize);
- uint64_t flags = reader.ReadPointer();
- auto strLoc = ReadPointerAccountingForRelocations(&reader);
- auto size = reader.ReadPointer();
- std::string str;
- if (flags & 0b10000) // UTF16
- {
- auto data = m_data->ReadBuffer(strLoc, size * 2);
-
- str = "";
- for (uint64_t bufferOff = 0; bufferOff < size * 2; bufferOff += 2)
- {
- uint8_t* rawData = static_cast<uint8_t*>(data.GetData());
- uint8_t* offsetAddress = rawData + bufferOff;
- uint16_t c = *reinterpret_cast<uint16_t*>(offsetAddress);
- if (c == 0x20) {
- str.push_back('_');
- } else if (c == '\r') {
- str.push_back('\\');
- str.push_back('r');
- } else if (c == '\n') {
- str.push_back('\\');
- str.push_back('n');
- } else if (c == '\t') {
- str.push_back('\\');
- str.push_back('t');
- } else if (c > 0x20 && c < 0x80) {
- str.push_back(c);
- } else {
- str.push_back('?');
- }
- }
- DefineObjCSymbol(
- DataSymbol, Type::ArrayType(Type::WideCharType(2), size + 1), "ustr_" + str, strLoc, true);
- DefineObjCSymbol(
- DataSymbol, Type::NamedType(m_data, m_typeNames.cfStringUTF16), "cfstr_" + str, i, true);
- }
- else // UTF8 / ASCII
- {
- reader.Seek(strLoc);
- std::string rawStr = reader.ReadCString(size + 1);
- str = "";
- for (signed char c : rawStr)
- {
- if (c == 0x20) {
- str.push_back('_');
- } else if (c == '\r') {
- str.push_back('\\');
- str.push_back('r');
- } else if (c == '\n') {
- str.push_back('\\');
- str.push_back('n');
- } else if (c == '\t') {
- str.push_back('\\');
- str.push_back('t');
- } else if (c > 0x20 || c < 0) {
- str.push_back(c);
- } else {
- str.push_back('?');
- }
- }
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), str.size() + 1), "cstr_" + str,
- strLoc, true);
- DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.cfString), "cfstr_" + str, i, true);
- }
- }
- auto id = m_data->BeginUndoActions();
- m_symbolQueue->Process();
- m_data->EndBulkModifySymbols();
- m_data->ForgetUndoActions(id);
- }
- delete m_symbolQueue;
+ return (data->GetSectionByName("__objc_classlist") || data->GetSectionByName("__objc_catlist")
+ || data->GetSectionByName("__objc_protolist"));
}
-void ObjCProcessor::AddRelocatedPointer(uint64_t location, uint64_t rewrite)
+MachoObjCProcessor::MachoObjCProcessor(BinaryView* data, bool isBackedByDatabase) :
+ ObjCProcessor(data, "macho.objc", isBackedByDatabase)
{
- m_relocationPointerRewrites[location] = rewrite;
}
diff --git a/view/macho/objc.h b/view/macho/objc.h
index 452cfed0..42aec8d0 100644
--- a/view/macho/objc.h
+++ b/view/macho/objc.h
@@ -1,219 +1,39 @@
#pragma once
#include <binaryninjaapi.h>
+#include <objectivec/objc.h>
namespace BinaryNinja {
- // This set of structs is based on the objc4 source,
- // however pointers have been replaced with view_ptr_t
+ class MachoObjCReader : public ObjCReader {
+ private:
+ BinaryReader m_reader;
- // Used for pointers within BinaryView, primarily to make it far more clear in typedefs
- // whether the size of a field can vary between architectures.
- // These should _not_ be used in sizeof or direct Read() calls.
- typedef uint64_t view_ptr_t;
-
- typedef struct {
- view_ptr_t name;
- view_ptr_t types;
- view_ptr_t imp;
- } method_t;
- typedef struct {
- uint32_t name;
- uint32_t types;
- uint32_t imp;
- } method_entry_t;
- typedef struct {
- view_ptr_t offset;
- view_ptr_t name;
- view_ptr_t type;
- uint32_t alignmentRaw;
- uint32_t size;
- } ivar_t;
- typedef struct {
- view_ptr_t name;
- view_ptr_t attributes;
- } property_t;
- typedef struct {
- uint32_t entsizeAndFlags;
- uint32_t count;
- } method_list_t;
- typedef struct {
- uint32_t entsizeAndFlags;
- uint32_t count;
- } ivar_list_t;
- typedef struct {
- uint32_t entsizeAndFlags;
- uint32_t count;
- } property_list_t;
- typedef struct {
- uint64_t count;
- } protocol_list_t;
- typedef struct {
- view_ptr_t isa;
- view_ptr_t mangledName;
- view_ptr_t protocols;
- view_ptr_t instanceMethods;
- view_ptr_t classMethods;
- view_ptr_t optionalInstanceMethods;
- view_ptr_t optionalClassMethods;
- view_ptr_t instanceProperties;
- uint32_t size;
- uint32_t flags;
- } protocol_t;
- typedef struct {
- uint32_t flags;
- uint32_t instanceStart;
- uint32_t instanceSize;
- uint32_t reserved;
- view_ptr_t ivarLayout;
- view_ptr_t name;
- view_ptr_t baseMethods;
- view_ptr_t baseProtocols;
- view_ptr_t ivars;
- view_ptr_t weakIvarLayout;
- view_ptr_t baseProperties;
- } class_ro_t;
- typedef struct {
- view_ptr_t isa;
- view_ptr_t super;
- view_ptr_t cache;
- view_ptr_t vtable;
- view_ptr_t data;
- } class_t;
- typedef struct {
- view_ptr_t name;
- view_ptr_t cls;
- view_ptr_t instanceMethods;
- view_ptr_t classMethods;
- view_ptr_t protocols;
- view_ptr_t instanceProperties;
- } category_t;
- typedef struct {
- view_ptr_t receiver;
- view_ptr_t current_class;
- } objc_super2;
- typedef struct {
- view_ptr_t imp;
- view_ptr_t sel;
- } message_ref_t;
-
- struct Method {
- std::string name;
- std::string types;
- view_ptr_t imp;
- };
-
- struct Ivar {
- uint32_t offset;
- std::string name;
- std::string type;
- uint32_t alignment;
- uint32_t size;
- };
-
- struct Property {
- std::string name;
- std::string attributes;
- };
-
- struct ClassBase {
- std::map<uint64_t, Method> methodList;
- std::map<uint64_t, Ivar> ivarList;
- };
-
- struct Class {
- std::string name;
- ClassBase instanceClass;
- ClassBase metaClass;
-
- // Loaded by type processing
- QualifiedName associatedName;
- };
-
- class Protocol {
public:
- std::string name;
- std::vector<QualifiedName> protocols;
- ClassBase instanceMethods;
- ClassBase classMethods;
- ClassBase optionalInstanceMethods;
- ClassBase optionalClassMethods;
- };
+ void Read(void* dest, size_t len) override;
+ std::string ReadCString() override;
+ uint8_t Read8() override;
+ uint16_t Read16() override;
+ uint32_t Read32() override;
+ uint64_t Read64() override;
+ int8_t ReadS8() override;
+ int16_t ReadS16() override;
+ int32_t ReadS32() override;
+ int64_t ReadS64() override;
+ uint64_t ReadPointer() override;
+ uint64_t GetOffset() const override;
+ void Seek(uint64_t offset) override;
+ void SeekRelative(int64_t offset) override;
- struct QualifiedNameOrType {
- BinaryNinja::Ref<BinaryNinja::Type> type = nullptr;
- BinaryNinja::QualifiedName name;
- size_t ptrCount = 0;
+ MachoObjCReader(BinaryView* data);
};
- class ObjCProcessor {
- struct Types {
- // QualifiedName relativePtr;
- // QualifiedName id;
- // QualifiedName sel;
- // QualifiedName BOOL;
- // QualifiedName nsInteger;
- // QualifiedName nsuInteger;
- // QualifiedName cgFloat;
- QualifiedName cfStringFlag;
- QualifiedName cfString;
- QualifiedName cfStringUTF16;
- QualifiedName imageInfoFlags;
- QualifiedName imageInfoSwiftVersion;
- QualifiedName imageInfo;
- QualifiedName methodEntry;
- QualifiedName method;
- QualifiedName methodList;
- QualifiedName classRO;
- QualifiedName cls;
- QualifiedName category;
- QualifiedName protocol;
- QualifiedName protocolList;
- QualifiedName ivar;
- QualifiedName ivarList;
- } m_typeNames;
-
- bool m_isBackedByDatabase;
-
- BinaryView* m_data;
- SymbolQueue* m_symbolQueue = nullptr;
- Ref<Logger> m_logger;
- std::map<uint64_t, Class> m_classes;
- std::map<uint64_t, Class> m_categories;
- std::map<uint64_t, Protocol> m_protocols;
- std::unordered_map<uint64_t, std::string> m_selectorCache;
- std::unordered_map<uint64_t, Method> m_localMethods;
-
- // Required for workflow_objc type heuristics, should be removed when that is no longer a thing.
- std::map<uint64_t, std::string> m_selRefToName;
- std::map<uint64_t, std::vector<uint64_t>> m_selRefToImplementations;
- std::map<uint64_t, std::vector<uint64_t>> m_selToImplementations;
- // --
-
- uint64_t ReadPointerAccountingForRelocations(BinaryReader* reader);
- std::unordered_map<uint64_t, uint64_t> m_relocationPointerRewrites;
-
- static Ref<Metadata> SerializeMethod(uint64_t loc, const Method& method);
- static Ref<Metadata> SerializeClass(uint64_t loc, const Class& cls);
-
- Ref<Metadata> SerializeMetadata();
- std::vector<QualifiedNameOrType> ParseEncodedType(const std::string& type);
- void DefineObjCSymbol(BNSymbolType symbolType, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred);
- void DefineObjCSymbol(BNSymbolType symbolType, Ref<Type> type, const std::string& name, uint64_t addr, bool deferred);
- void ReadIvarList(BinaryReader* reader, ClassBase& cls, std::string name, view_ptr_t start);
- void ReadMethodList(BinaryReader* reader, ClassBase& cls, std::string name, view_ptr_t start);
- void LoadClasses(BinaryReader* reader, Ref<Section> listSection);
- void LoadCategories(BinaryReader* reader, Ref<Section> listSection);
- void LoadProtocols(BinaryReader* reader, Ref<Section> listSection);
- void GenerateClassTypes();
- bool ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod);
- void ApplyMethodTypes(Class& cls);
- void PostProcessObjCSections(BinaryReader* reader);
+ class MachoObjCProcessor : public ObjCProcessor {
+ std::shared_ptr<ObjCReader> GetReader() override;
+
public:
+ MachoObjCProcessor(BinaryView* data, bool isBackedByDatabase);
+
static bool ViewHasObjCMetadata(BinaryView* data);
- ObjCProcessor(BinaryView* data, bool isBackedByDatabase);
- void ProcessObjCData();
- void ProcessCFStrings();
- void AddRelocatedPointer(uint64_t location, uint64_t rewrite);
};
}
diff --git a/view/sharedcache/core/CMakeLists.txt b/view/sharedcache/core/CMakeLists.txt
index 22c7ea28..03766920 100644
--- a/view/sharedcache/core/CMakeLists.txt
+++ b/view/sharedcache/core/CMakeLists.txt
@@ -8,11 +8,8 @@ if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD))
message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH")
endif()
endif()
-file(GLOB COMMON_SOURCES
- *.cpp
- *.h
- )
-set(SOURCES ${COMMON_SOURCES})
+
+file(GLOB SOURCES *.cpp *.h ../../../objectivec/*)
add_library(sharedcachecore OBJECT ${SOURCES})
diff --git a/view/sharedcache/core/ObjC.cpp b/view/sharedcache/core/ObjC.cpp
index fa0e603c..c763b321 100644
--- a/view/sharedcache/core/ObjC.cpp
+++ b/view/sharedcache/core/ObjC.cpp
@@ -1,1540 +1,116 @@
#include "ObjC.h"
-#include "inttypes.h"
-#include "rapidjson/rapidjson.h"
-#include "rapidjson/document.h"
-#include "rapidjson/stringbuffer.h"
-#include "rapidjson/prettywriter.h"
using namespace BinaryNinja;
using namespace DSCObjC;
using namespace SharedCacheCore;
-Ref<Metadata> DSCObjCProcessor::SerializeMethod(uint64_t loc, const Method& method)
+DSCObjCReader::DSCObjCReader(SharedCache* cache, size_t addressSize) :
+ m_reader(VMReader(cache->GetVMMap())), m_addressSize(addressSize)
{
- std::map<std::string, Ref<Metadata>> methodMeta;
-
- methodMeta["loc"] = new Metadata(loc);
- methodMeta["name"] = new Metadata(method.name);
- methodMeta["types"] = new Metadata(method.types);
- methodMeta["imp"] = new Metadata(method.imp);
-
- return new Metadata(methodMeta);
}
-
-Ref<Metadata> DSCObjCProcessor::SerializeClass(uint64_t loc, const Class& cls)
+void DSCObjCReader::Read(void* dest, size_t len)
{
- std::map<std::string, Ref<Metadata>> clsMeta;
-
- clsMeta["loc"] = new Metadata(loc);
- clsMeta["name"] = new Metadata(cls.name);
- clsMeta["typeName"] = new Metadata(cls.associatedName.GetString());
-
- std::vector<uint64_t> instanceMethods;
- std::vector<uint64_t> classMethods;
- instanceMethods.reserve(cls.instanceClass.methodList.size());
- classMethods.reserve(cls.metaClass.methodList.size());
- for (const auto& [location, _] : cls.instanceClass.methodList)
- instanceMethods.push_back(location);
-
- clsMeta["instanceMethods"] = new Metadata(instanceMethods);
- clsMeta["classMethods"] = new Metadata(classMethods);
-
- return new Metadata(clsMeta);
+ m_reader.Read(dest, len);
}
-Ref<Metadata> DSCObjCProcessor::SerializeMetadata()
+std::string DSCObjCReader::ReadCString()
{
- std::map<std::string, Ref<Metadata>> viewMeta;
- viewMeta["version"] = new Metadata((uint64_t)1);
-
- std::vector<Ref<Metadata>> classes;
- classes.reserve(m_classes.size());
- std::vector<Ref<Metadata>> categories;
- categories.reserve(m_categories.size());
- std::vector<Ref<Metadata>> methods;
- methods.reserve(m_localMethods.size());
-
- for (const auto& [clsLoc, cls] : m_classes)
- classes.push_back(SerializeClass(clsLoc, cls));
- viewMeta["classes"] = new Metadata(classes);
- for (const auto& [catLoc, cat] : m_categories)
- categories.push_back(SerializeClass(catLoc, cat));
- viewMeta["categories"] = new Metadata(categories);
- for (const auto& [methodLoc, method] : m_localMethods)
- methods.push_back(SerializeMethod(methodLoc, method));
- viewMeta["methods"] = new Metadata(methods);
-
- // Required for workflow_objc type guessing, should be removed when that is no longer a thing.
- std::vector<Ref<Metadata>> selRefToImps;
- selRefToImps.reserve(m_selRefToImplementations.size());
- for (const auto& [selRef, imps] : m_selRefToImplementations)
- {
- std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(imps)};
- Ref<Metadata> mapObject = new Metadata(mapBase);
- selRefToImps.push_back(mapObject);
- }
- viewMeta["selRefImplementations"] = new Metadata(selRefToImps);
-
- std::vector<Ref<Metadata>> selToImps;
- selToImps.reserve(m_selToImplementations.size());
- for (const auto& [selRef, imps] : m_selToImplementations)
- {
- std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(imps)};
- Ref<Metadata> mapObject = new Metadata(mapBase);
- selToImps.push_back(mapObject);
- }
- viewMeta["selImplementations"] = new Metadata(selToImps);
-
- std::vector<Ref<Metadata>> selRefToName;
- selRefToName.reserve(m_selRefToName.size());
- for (const auto& [selRef, name] : m_selRefToName)
- {
- std::vector<Ref<Metadata>> mapBase = {new Metadata(selRef), new Metadata(name)};
- Ref<Metadata> mapObject = new Metadata(mapBase);
- selRefToName.push_back(mapObject);
- }
- viewMeta["selRefToName"] = new Metadata(selRefToName);
- // ---
-
-
- return new Metadata(viewMeta);
+ return m_reader.ReadCString(m_reader.GetOffset());
}
-std::vector<DSCObjC::QualifiedNameOrType> DSCObjCProcessor::ParseEncodedType(const std::string& encodedType)
+uint8_t DSCObjCReader::Read8()
{
- std::vector<QualifiedNameOrType> result;
- int pointerDepth = 0;
-
- bool readingNamedType = false;
- std::string namedType;
- int readingStructDepth = 0;
- std::string structType;
- char last;
-
- for (char c : encodedType)
- {
- if (readingNamedType && c != '"')
- {
- namedType.push_back(c);
- last = c;
- continue;
- }
- else if (readingStructDepth > 0 && c != '{' && c != '}')
- {
- structType.push_back(c);
- last = c;
- continue;
- }
-
- if (std::isdigit(c))
- continue;
-
- QualifiedNameOrType nameOrType;
- std::string qualifiedName;
-
- switch (c)
- {
- case '^':
- pointerDepth++;
- last = c;
- continue;
-
- case '"':
- if (!readingNamedType)
- {
- readingNamedType = true;
- if (last == '@')
- result.pop_back(); // We added an 'id' in the last cycle, remove it
- last = c;
- continue;
- }
- else
- {
- readingNamedType = false;
- nameOrType.name = QualifiedName(namedType);
- nameOrType.ptrCount = 1;
- break;
- }
- case '{':
- readingStructDepth++;
- last = c;
- continue;
- case '}':
- readingStructDepth--;
- if (readingStructDepth < 0)
- return {}; // seriously malformed type.
-
- if (readingStructDepth == 0)
- {
- // TODO: Emit real struct types
- nameOrType.type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
- break;
- }
- last = c;
- continue;
- case 'v':
- nameOrType.type = Type::VoidType();
- break;
- case 'c':
- nameOrType.type = Type::IntegerType(1, true);
- break;
- case 'A':
- case 'C':
- nameOrType.type = Type::IntegerType(1, false);
- break;
- case 's':
- nameOrType.type = Type::IntegerType(2, true);
- break;
- case 'S':
- nameOrType.type = Type::IntegerType(1, false);
- break;
- case 'i':
- nameOrType.type = Type::IntegerType(4, true);
- break;
- case 'I':
- nameOrType.type = Type::IntegerType(4, false);
- break;
- case 'l':
- nameOrType.type = Type::IntegerType(8, true);
- break;
- case 'L':
- nameOrType.type = Type::IntegerType(8, true);
- break;
- case 'f':
- nameOrType.type = Type::IntegerType(4, true);
- break;
- case 'b':
- case 'B':
- nameOrType.type = Type::BoolType();
- break;
- case 'q':
- qualifiedName = "NSInteger";
- break;
- case 'Q':
- qualifiedName = "NSUInteger";
- break;
- case 'd':
- qualifiedName = "CGFloat";
- break;
- case '*':
- nameOrType.type = Type::PointerType(m_data->GetAddressSize(), Type::IntegerType(1, true));
- break;
- case '@':
- qualifiedName = "id";
- // There can be a type after this, like @"NSString", that overrides this
- // The handler for " will catch it and drop this "id" entry.
- break;
- case ':':
- qualifiedName = "SEL";
- break;
- case '#':
- qualifiedName = "objc_class_t";
- break;
- case '?':
- case 'T':
- nameOrType.type = Type::PointerType(8, Type::VoidType());
- break;
- default:
- // BNLogWarn("Unknown type specifier %c", c);
- last = c;
- continue;
- }
-
- while (pointerDepth)
- {
- if (nameOrType.type)
- nameOrType.type = Type::PointerType(8, nameOrType.type);
- else
- nameOrType.ptrCount++;
-
- pointerDepth--;
- }
-
- if (!qualifiedName.empty())
- nameOrType.name = QualifiedName(qualifiedName);
-
- if (nameOrType.type == nullptr && nameOrType.name.IsEmpty())
- {
- nameOrType.type = Type::VoidType();
- }
-
- result.push_back(nameOrType);
- last = c;
- }
-
- return result;
+ return m_reader.Read8();
}
-void DSCObjCProcessor::DefineObjCSymbol(
- BNSymbolType type, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred)
+uint16_t DSCObjCReader::Read16()
{
- DefineObjCSymbol(type, m_data->GetTypeByName(typeName), name, addr, deferred);
+ return m_reader.Read16();
}
-void DSCObjCProcessor::DefineObjCSymbol(
- BNSymbolType type, Ref<Type> typeRef, const std::string& name, uint64_t addr, bool deferred)
+uint32_t DSCObjCReader::Read32()
{
- if (name.size() == 0 || addr == 0)
- return;
-
- auto process = [=]() {
- NameSpace nameSpace = m_data->GetInternalNameSpace();
- if (type == ExternalSymbol)
- {
- nameSpace = m_data->GetExternalNameSpace();
- }
-
- std::string shortName = name;
- std::string fullName = name;
-
- QualifiedName varName;
-
- return std::pair<Ref<Symbol>, Ref<Type>>(
- new Symbol(type, shortName, fullName, name, addr, GlobalBinding, nameSpace), typeRef);
- };
-
- if (deferred)
- {
- m_symbolQueue->Append(process, [this, addr = addr](Symbol* symbol, Type* type) {
- // Armv7/Thumb: This will rewrite the symbol's address.
- // e.g. We pass in 0xc001, it will rewrite it to 0xc000 and create the function w/ the "thumb2" arch.
- if (Ref<Symbol> existingSymbol = m_data->GetSymbolByAddress(addr))
- m_data->UndefineAutoSymbol(existingSymbol);
- auto funcSym = m_data->DefineAutoSymbolAndVariableOrFunction(m_data->GetDefaultPlatform(), symbol, type);
- if (funcSym->GetType() == FunctionSymbol)
- {
- uint64_t target = symbol->GetAddress();
- Ref<Platform> targetPlatform =
- m_data->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); // rewrites target.
- if (Ref<Function> targetFunction = m_data->GetAnalysisFunction(targetPlatform, target))
- {
- if (!m_isBackedByDatabase)
- targetFunction->SetUserType(type);
- }
- }
- });
- return;
- }
-
- if (Ref<Symbol> existingSymbol = m_data->GetSymbolByAddress(addr))
- m_data->UndefineAutoSymbol(existingSymbol);
- auto result = process();
- auto sym = m_data->DefineAutoSymbolAndVariableOrFunction(m_data->GetDefaultPlatform(), result.first, result.second);
- if (sym->GetType() == FunctionSymbol)
- {
- uint64_t target = result.first->GetAddress();
- Ref<Platform> targetPlatform = m_data->GetDefaultPlatform()->GetAssociatedPlatformByAddress(target); // rewrites
- // target.
- if (Ref<Function> targetFunction = m_data->GetAnalysisFunction(targetPlatform, target))
- {
- if (!m_isBackedByDatabase)
- targetFunction->SetUserType(result.second);
- }
- }
+ return m_reader.Read32();
}
-void DSCObjCProcessor::LoadClasses(VMReader* reader, Ref<Section> classPtrSection)
+uint64_t DSCObjCReader::Read64()
{
- if (!classPtrSection)
- return;
- auto size = classPtrSection->GetEnd() - classPtrSection->GetStart();
- if (size == 0)
- return;
- auto ptrSize = m_data->GetAddressSize();
- auto ptrCount = size / ptrSize;
-
- auto classPtrSectionStart = classPtrSection->GetStart();
- for (size_t i = 0; i < ptrCount; i++)
- {
- Class cls;
-
- view_ptr_t classPtr;
- class_t clsStruct;
- class_ro_t classRO;
-
- bool hasValidMetaClass = false;
- bool hasValidMetaClassRO = false;
- class_t metaClsStruct;
- class_ro_t metaClassRO;
-
- view_ptr_t classPointerLocation = classPtrSectionStart + (i * m_data->GetAddressSize());
- reader->Seek(classPointerLocation);
-
- classPtr = ReadPointerAccountingForRelocations(reader);
- reader->Seek(classPtr);
- try
- {
- clsStruct.isa = ReadPointerAccountingForRelocations(reader);
- clsStruct.super = reader->ReadPointer();
- clsStruct.cache = reader->ReadPointer();
- clsStruct.vtable = reader->ReadPointer();
- clsStruct.data = ReadPointerAccountingForRelocations(reader);
- }
- catch (...)
- {
- m_logger->LogError("Failed to read class data at 0x%llx pointed to by @ 0x%llx", reader->GetOffset(),
- classPointerLocation);
- continue;
- }
- if (clsStruct.data & 1)
- {
- m_logger->LogInfo("Skipping class at 0x%llx as it contains swift types", classPtr);
- continue;
- }
- // unset first two bits
- view_ptr_t classROPtr = clsStruct.data & ~3;
- reader->Seek(classROPtr);
- try
- {
- classRO.flags = reader->Read32();
- classRO.instanceStart = reader->Read32();
- classRO.instanceSize = reader->Read32();
- if (m_data->GetAddressSize() == 8)
- classRO.reserved = reader->Read32();
- classRO.ivarLayout = ReadPointerAccountingForRelocations(reader);
- classRO.name = ReadPointerAccountingForRelocations(reader);
- classRO.baseMethods = ReadPointerAccountingForRelocations(reader);
- classRO.baseProtocols = ReadPointerAccountingForRelocations(reader);
- classRO.ivars = ReadPointerAccountingForRelocations(reader);
- classRO.weakIvarLayout = ReadPointerAccountingForRelocations(reader);
- classRO.baseProperties = ReadPointerAccountingForRelocations(reader);
- }
- catch (...)
- {
- m_logger->LogError("Failed to read class RO data at 0x%llx. 0x%llx, objc_class_t @ 0x%llx",
- reader->GetOffset(), classPointerLocation, classROPtr);
- continue;
- }
-
- auto namePtr = classRO.name;
-
- std::string name;
-
- reader->Seek(namePtr);
- try
- {
- name = reader->ReadCString(namePtr);
- }
- catch (...)
- {
- m_logger->LogWarn(
- "Failed to read class name at 0x%llx. Class has been given the placeholder name \"0x%llx\" ", namePtr,
- classPtr);
- char hexString[9];
- hexString[8] = 0;
- snprintf(hexString, sizeof(hexString), "%llx", classPtr);
- name = "0x" + std::string(hexString);
- }
-
- cls.name = name;
-
- DefineObjCSymbol(BNSymbolType::DataSymbol,
- Type::PointerType(m_data->GetAddressSize(), m_data->GetTypeByName(m_typeNames.cls)), "clsPtr_" + name,
- classPointerLocation, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.cls, "cls_" + name, classPtr, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.classRO, "cls_ro_" + name, classROPtr, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, Type::ArrayType(Type::IntegerType(1, true), name.size() + 1),
- "clsName_" + name, classRO.name, true);
- if (0 && classRO.baseProtocols)
- {
- DefineObjCSymbol(BNSymbolType::DataSymbol, Type::NamedType(m_data, m_typeNames.protocolList),
- "clsProtocols_" + name, classRO.baseProtocols, true);
- reader->Seek(classRO.baseProtocols);
- uint32_t count = reader->Read64();
- view_ptr_t addr = reader->GetOffset();
- for (uint32_t j = 0; j < count; j++)
- {
- m_data->DefineDataVariable(
- addr, Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol)));
- addr += ptrSize;
- }
- }
-
- if (clsStruct.isa)
- {
- reader->Seek(clsStruct.isa);
- try
- {
- metaClsStruct.isa = ReadPointerAccountingForRelocations(reader);
- metaClsStruct.super = reader->ReadPointer();
- metaClsStruct.cache = reader->ReadPointer();
- metaClsStruct.vtable = reader->ReadPointer();
- metaClsStruct.data = ReadPointerAccountingForRelocations(reader) & ~1;
- DefineObjCSymbol(BNSymbolType::DataSymbol, m_typeNames.cls, "metacls_" + name, clsStruct.isa, true);
- hasValidMetaClass = true;
- }
- catch (...)
- {
- m_logger->LogWarn("Failed to read metaclass data at 0x%llx pointed to by objc_class_t @ 0x%llx",
- reader->GetOffset(), classPtr);
- }
- }
- if (hasValidMetaClass && (metaClsStruct.data & 1))
- {
- m_logger->LogInfo("Skipping metaclass at 0x%llx as it contains swift types", classPtr);
- hasValidMetaClass = false;
- }
- if (hasValidMetaClass)
- {
- reader->Seek(metaClsStruct.data);
- try
- {
- metaClassRO.flags = reader->Read32();
- metaClassRO.instanceStart = reader->Read32();
- metaClassRO.instanceSize = reader->Read32();
- if (m_data->GetAddressSize() == 8)
- metaClassRO.reserved = reader->Read32();
- metaClassRO.ivarLayout = ReadPointerAccountingForRelocations(reader);
- metaClassRO.name = ReadPointerAccountingForRelocations(reader);
- metaClassRO.baseMethods = ReadPointerAccountingForRelocations(reader);
- metaClassRO.baseProtocols = ReadPointerAccountingForRelocations(reader);
- metaClassRO.ivars = ReadPointerAccountingForRelocations(reader);
- metaClassRO.weakIvarLayout = ReadPointerAccountingForRelocations(reader);
- metaClassRO.baseProperties = ReadPointerAccountingForRelocations(reader);
- DefineObjCSymbol(
- BNSymbolType::DataSymbol, m_typeNames.classRO, "metacls_ro_" + name, metaClsStruct.data, true);
- hasValidMetaClassRO = true;
- }
- catch (...)
- {
- m_logger->LogWarn("Failed to read metaclass RO data at 0x%llx pointed to by meta objc_class_t @ 0x%llx",
- reader->GetOffset(), clsStruct.isa);
- }
- }
-
- if (classRO.baseMethods)
- {
- try
- {
- ReadMethodList(reader, cls.instanceClass, name, classRO.baseMethods);
- }
- catch (...)
- {
- m_logger->LogError("Failed to read the method list for class pointed to by 0x%llx", clsStruct.data);
- }
- }
- if (hasValidMetaClassRO && metaClassRO.baseMethods)
- {
- try
- {
- ReadMethodList(reader, cls.metaClass, name, metaClassRO.baseMethods);
- }
- catch (...)
- {
- m_logger->LogError("Failed to read the method list for metaclass pointed to by 0x%llx", clsStruct.data);
- }
- }
-
- if (classRO.ivars)
- {
- try
- {
- ReadIvarList(reader, cls.instanceClass, name, classRO.ivars);
- }
- catch (...)
- {
- m_logger->LogError("Failed to process ivars for class at 0x%llx", clsStruct.data);
- }
- }
- m_classes[classPtr] = cls;
- }
-}
-
-void DSCObjCProcessor::LoadCategories(VMReader* reader, Ref<Section> classPtrSection)
-{
- if (!classPtrSection)
- return;
- auto size = classPtrSection->GetEnd() - classPtrSection->GetStart();
- if (size == 0)
- return;
- auto ptrSize = m_data->GetAddressSize();
-
- auto classPtrSectionStart = classPtrSection->GetStart();
- auto classPtrSectionEnd = classPtrSection->GetEnd();
-
- auto catType = Type::NamedType(m_data, m_typeNames.category);
- auto ptrType = Type::PointerType(m_data->GetDefaultArchitecture(), catType);
- for (size_t i = classPtrSectionStart; i < classPtrSectionEnd; i += ptrSize)
- {
- Class category;
- category_t cat;
-
- reader->Seek(i);
- auto catLocation = ReadPointerAccountingForRelocations(reader);
- reader->Seek(catLocation);
-
- try
- {
- cat.name = ReadPointerAccountingForRelocations(reader);
- cat.cls = ReadPointerAccountingForRelocations(reader);
- cat.instanceMethods = ReadPointerAccountingForRelocations(reader);
- cat.classMethods = ReadPointerAccountingForRelocations(reader);
- cat.protocols = ReadPointerAccountingForRelocations(reader);
- cat.instanceProperties = ReadPointerAccountingForRelocations(reader);
- }
- catch (...)
- {
- m_logger->LogError("Failed to read category pointed to by 0x%llx", i);
- continue;
- }
-
- std::string categoryAdditionsName;
- std::string categoryBaseClassName;
-
- if (const auto& it = m_classes.find(cat.cls); it != m_classes.end())
- {
- 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)
- {
- const auto& symbolName = symbol->GetFullName();
- if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0)
- categoryBaseClassName = symbolName.substr(14, symbolName.size() - 14);
- }
- }
- if (categoryBaseClassName.empty())
- {
- m_logger->LogError(
- "Failed to determine base classname for category at 0x%llx. Using base address as stand-in classname",
- catLocation);
- categoryBaseClassName = std::to_string(catLocation);
- }
- try
- {
- reader->Seek(cat.name);
- categoryAdditionsName = reader->ReadCString(cat.name);
- }
- catch (...)
- {
- m_logger->LogError(
- "Failed to read category name for category at 0x%llx. Using base address as stand-in category name",
- catLocation);
- categoryAdditionsName = std::to_string(catLocation);
- }
- category.name = categoryBaseClassName + " (" + categoryAdditionsName + ")";
- DefineObjCSymbol(BNSymbolType::DataSymbol, ptrType, "categoryPtr_" + category.name, i, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, catType, "category_" + category.name, catLocation, true);
-
- if (cat.instanceMethods)
- {
- try
- {
- ReadMethodList(reader, category.instanceClass, category.name, cat.instanceMethods);
- }
- catch (...)
- {
- m_logger->LogError(
- "Failed to read the instance method list for category pointed to by 0x%llx", catLocation);
- }
- }
- if (cat.classMethods)
- {
- try
- {
- ReadMethodList(reader, category.metaClass, category.name, cat.classMethods);
- }
- catch (...)
- {
- m_logger->LogError(
- "Failed to read the class method list for category pointed to by 0x%llx", catLocation);
- }
- }
- m_categories[catLocation] = category;
- }
+ return m_reader.Read64();
}
-void DSCObjCProcessor::LoadProtocols(VMReader* reader, Ref<Section> listSection)
+int8_t DSCObjCReader::ReadS8()
{
- if (!listSection)
- return;
- auto size = listSection->GetEnd() - listSection->GetStart();
- if (size == 0)
- return;
- auto ptrSize = m_data->GetAddressSize();
-
- auto listSectionStart = listSection->GetStart();
- auto listSectionEnd = listSection->GetEnd();
-
- auto protocolType = Type::NamedType(m_data, m_typeNames.protocol);
- auto ptrType = Type::PointerType(m_data->GetDefaultArchitecture(), protocolType);
- for (size_t i = listSectionStart; i < listSectionEnd; i += ptrSize)
- {
- protocol_t protocol;
- reader->Seek(i);
- auto protocolLocation = ReadPointerAccountingForRelocations(reader);
- reader->Seek(protocolLocation);
-
- try
- {
- protocol.isa = ReadPointerAccountingForRelocations(reader);
- protocol.mangledName = ReadPointerAccountingForRelocations(reader);
- protocol.protocols = ReadPointerAccountingForRelocations(reader);
- protocol.instanceMethods = ReadPointerAccountingForRelocations(reader);
- protocol.classMethods = ReadPointerAccountingForRelocations(reader);
- protocol.optionalInstanceMethods = ReadPointerAccountingForRelocations(reader);
- protocol.optionalClassMethods = ReadPointerAccountingForRelocations(reader);
- protocol.instanceProperties = ReadPointerAccountingForRelocations(reader);
- }
- catch (...)
- {
- m_logger->LogError("Failed to read protocol pointed to by 0x%llx", i);
- continue;
- }
-
- std::string protocolName;
- try
- {
- reader->Seek(protocol.mangledName);
- protocolName = reader->ReadCString(protocol.mangledName);
- DefineObjCSymbol(BNSymbolType::DataSymbol,
- Type::ArrayType(Type::IntegerType(1, true), protocolName.size() + 1), "protocolName_" + protocolName,
- protocol.mangledName, true);
- }
- catch (...)
- {
- m_logger->LogError(
- "Failed to read protocol name for protocol at 0x%llx. Using base address as stand-in protocol name",
- protocolLocation);
- protocolName = std::to_string(protocolLocation);
- }
-
- Protocol protocolClass;
- protocolClass.name = protocolName;
- DefineObjCSymbol(BNSymbolType::DataSymbol, ptrType, "protocolPtr_" + protocolName, i, true);
- DefineObjCSymbol(BNSymbolType::DataSymbol, protocolType, "protocol_" + protocolName, protocolLocation, true);
- if (protocol.protocols)
- {
- DefineObjCSymbol(BNSymbolType::DataSymbol, Type::NamedType(m_data, m_typeNames.protocolList),
- "protoProtocols_" + protocolName, protocol.protocols, true);
- reader->Seek(protocol.protocols);
- uint32_t count = reader->Read64();
- view_ptr_t addr = reader->GetOffset();
- for (uint32_t j = 0; j < count; j++)
- {
- m_data->DefineDataVariable(
- addr, Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol)));
- addr += ptrSize;
- }
- }
-
- if (protocol.instanceMethods)
- {
- try
- {
- ReadMethodList(reader, protocolClass.instanceMethods, protocolName, protocol.instanceMethods);
- }
- catch (...)
- {
- m_logger->LogError(
- "Failed to read the instance method list for protocol pointed to by 0x%llx", protocolLocation);
- }
- }
- if (protocol.classMethods)
- {
- try
- {
- ReadMethodList(reader, protocolClass.classMethods, protocolName, protocol.classMethods);
- }
- catch (...)
- {
- m_logger->LogError(
- "Failed to read the class method list for protocol pointed to by 0x%llx", protocolLocation);
- }
- }
- if (protocol.optionalInstanceMethods)
- {
- try
- {
- ReadMethodList(
- reader, protocolClass.optionalInstanceMethods, protocolName, protocol.optionalInstanceMethods);
- }
- catch (...)
- {
- m_logger->LogError("Failed to read the optional instance method list for protocol pointed to by 0x%llx",
- protocolLocation);
- }
- }
- if (protocol.optionalClassMethods)
- {
- try
- {
- ReadMethodList(reader, protocolClass.optionalClassMethods, protocolName, protocol.optionalClassMethods);
- }
- catch (...)
- {
- m_logger->LogError("Failed to read the optional class method list for protocol pointed to by 0x%llx",
- protocolLocation);
- }
- }
- m_protocols[protocolLocation] = protocolClass;
- }
+ return m_reader.ReadS8();
}
-void DSCObjCProcessor::ReadListOfMethodLists(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start)
+int16_t DSCObjCReader::ReadS16()
{
- reader->Seek(start);
- method_list_t head;
- head.entsizeAndFlags = reader->Read32();
- head.count = reader->Read32();
- if (head.count > 0x1000)
- {
- m_logger->LogError("List of method lists at 0x%llx has an invalid count of 0x%x", start, head.count);
- return;
- }
-
- for (size_t i = 0; i < head.count; ++i) {
- relative_list_list_entry_t list_entry;
- reader->Read(&list_entry, sizeof(list_entry));
-
- ReadMethodList(reader, cls, name, reader->GetOffset() - sizeof(list_entry) + list_entry.listOffset);
- // Reset the cursor to immediately past the list entry.
- reader->Seek(start + sizeof(method_list_t) + ((i + 1) * sizeof(relative_list_list_entry_t)));
- }
+ return m_reader.ReadS16();
}
-void DSCObjCProcessor::ReadMethodList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start)
+int32_t DSCObjCReader::ReadS32()
{
- // Lower two bits indicate the type of method list.
- switch (start & 0b11) {
- case 0:
- break;
- case 1:
- return ReadListOfMethodLists(reader, cls, name, start - 1);
- default:
- m_logger->LogDebug("ReadMethodList: Unknown method list type at 0x%llx: %d", start, start & 0x3);
- return;
- }
-
- reader->Seek(start);
- method_list_t head;
- head.entsizeAndFlags = reader->Read32();
- head.count = reader->Read32();
-
- if (head.count > 0x1000)
- {
- m_logger->LogError("Method list at 0x%llx has an invalid count of 0x%x", start, head.count);
- return;
- }
-
- uint64_t pointerSize = m_data->GetAddressSize();
- bool relativeOffsets = (head.entsizeAndFlags & 0xFFFF0000) & 0x80000000;
- bool directSelectors = (head.entsizeAndFlags & 0xFFFF0000) & 0x40000000;
- auto methodSize = relativeOffsets ? 12 : pointerSize * 3;
- DefineObjCSymbol(DataSymbol, m_typeNames.methodList, "method_list_" + std::string(name), start, true);
-
- for (unsigned i = 0; i < head.count; i++)
- {
- try
- {
- Method method;
- auto cursor = start + sizeof(method_list_t) + (i * methodSize);
- reader->Seek(cursor);
- method_t meth;
- // workflow_objc support
- uint64_t selRefAddr = 0;
- uint64_t selAddr = 0;
- // --
- if (relativeOffsets)
- {
- auto selectorBaseOffset = reader->GetOffset();
- if (directSelectors && m_customRelativeMethodSelectorBase.has_value()) {
- selectorBaseOffset = m_customRelativeMethodSelectorBase.value();
- }
-
- meth.name = selectorBaseOffset + reader->Read32();
- meth.types = reader->GetOffset() + reader->ReadS32();
- meth.imp = reader->GetOffset() + reader->ReadS32();
- }
- else
- {
- meth.name = ReadPointerAccountingForRelocations(reader);
- meth.types = ReadPointerAccountingForRelocations(reader);
- meth.imp = ReadPointerAccountingForRelocations(reader);
- }
- if (!relativeOffsets || directSelectors)
- {
- selAddr = meth.name;
- method.name = reader->ReadCString(meth.name);
- reader->Seek(meth.types);
- method.types = reader->ReadCString(meth.types);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.name.size() + 1),
- "sel_" + method.name, meth.name, true);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.types.size() + 1),
- "selTypes_" + method.name, meth.types, true);
- }
- else
- {
- std::string sel;
- view_ptr_t selRef;
- reader->Seek(meth.name);
- selRefAddr = meth.name;
- selRef = ReadPointerAccountingForRelocations(reader);
- reader->Seek(meth.types);
- method.types = reader->ReadCString(meth.types);
- selAddr = selRef;
- if (const auto& it = m_selectorCache.find(selRef); it != m_selectorCache.end())
- method.name = it->second;
- else
- {
- reader->Seek(selRef);
- method.name = reader->ReadCString(selRef);
- m_selectorCache[selRef] = method.name;
- }
- auto selType = Type::ArrayType(Type::IntegerType(1, true), method.name.size() + 1);
- DefineObjCSymbol(DataSymbol, selType, "sel_" + method.name, selRef, true);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), method.types.size() + 1),
- "selTypes_" + method.name, meth.types, true);
- DefineObjCSymbol(DataSymbol, Type::PointerType(m_data->GetAddressSize(), selType),
- "selRef_" + method.name, meth.name, true);
- }
- // workflow objc support
- if (selAddr)
- m_selToImplementations[selAddr].push_back(meth.imp);
- if (selRefAddr)
- m_selRefToImplementations[selRefAddr].push_back(meth.imp);
- // --
-
- DefineObjCSymbol(DataSymbol, relativeOffsets ? m_typeNames.methodEntry : m_typeNames.method,
- "method_" + method.name, cursor, true);
- method.imp = meth.imp;
- cls.methodList[cursor] = method;
- m_localMethods[cursor] = method;
- }
- catch (...)
- {
- // m_logger->LogError("Failed to process a method at offset 0x%llx", start + sizeof(method_list_t) + (i * methodSize));
- }
- }
+ return m_reader.ReadS32();
}
-void DSCObjCProcessor::ReadIvarList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start)
+int64_t DSCObjCReader::ReadS64()
{
- reader->Seek(start);
- ivar_list_t head;
- head.entsizeAndFlags = reader->Read32();
- head.count = reader->Read32();
- auto addressSize = m_data->GetAddressSize();
- DefineObjCSymbol(DataSymbol, m_typeNames.ivarList, "ivar_list_" + std::string(name), start, true);
- if (head.count > 0x1000)
- {
- m_logger->LogError("Ivar list at 0x%llx has an invalid count of 0x%llx", start, head.count);
- return;
- }
- for (unsigned i = 0; i < head.count; i++)
- {
- try
- {
- Ivar ivar;
- ivar_t ivarStruct;
- uint64_t cursor = start + (sizeof(ivar_list_t)) + (i * ((addressSize * 3) + 8));
- reader->Seek(cursor);
- ivarStruct.offset = ReadPointerAccountingForRelocations(reader);
- ivarStruct.name = ReadPointerAccountingForRelocations(reader);
- ivarStruct.type = ReadPointerAccountingForRelocations(reader);
- ivarStruct.alignmentRaw = reader->Read32();
- ivarStruct.size = reader->Read32();
-
- reader->Seek(ivarStruct.offset);
- ivar.offset = reader->Read32();
- reader->Seek(ivarStruct.name);
- ivar.name = reader->ReadCString(ivarStruct.name);
- reader->Seek(ivarStruct.type);
- ivar.type = reader->ReadCString(ivarStruct.type);
-
- DefineObjCSymbol(DataSymbol, m_typeNames.ivar, "ivar_" + ivar.name, cursor, true);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), ivar.name.size() + 1),
- "ivarName_" + ivar.name, ivarStruct.name, true);
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), ivar.type.size() + 1),
- "ivarType_" + ivar.name, ivarStruct.type, true);
-
- cls.ivarList[cursor] = ivar;
- }
- catch (...)
- {
- m_logger->LogError("Failed to process an ivar at offset 0x%llx",
- start + (sizeof(ivar_list_t)) + (i * ((addressSize * 3) + 8)));
- }
- }
+ return m_reader.ReadS64();
}
-
-std::pair<QualifiedName, Ref<Type>> finalizeStructureBuilder(
- Ref<BinaryView> m_data, StructureBuilder sb, std::string name)
+uint64_t DSCObjCReader::ReadPointer()
{
- auto classTypeStruct = sb.Finalize();
-
- QualifiedName classTypeName(name);
- auto classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
- auto classType = Type::StructureType(classTypeStruct);
- auto classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
-
- return {classQualName, classType};
+ return m_reader.ReadPointer();
}
-std::pair<QualifiedName, Ref<Type>> finalizeEnumerationBuilder(
- Ref<BinaryView> m_data, EnumerationBuilder eb, uint64_t size, QualifiedName name)
+uint64_t DSCObjCReader::GetOffset() const
{
- auto enumTypeStruct = eb.Finalize();
-
- auto enumTypeId = Type::GenerateAutoTypeId("objc", name);
- auto enumType = Type::EnumerationType(enumTypeStruct, size);
- auto enumQualName = m_data->DefineType(enumTypeId, name, enumType);
-
- return {enumQualName, enumType};
+ return m_reader.GetOffset();
}
-inline QualifiedName defineTypedef(Ref<BinaryView> m_data, const QualifiedName name, Ref<Type> type)
+void DSCObjCReader::Seek(uint64_t offset)
{
- auto typeID = Type::GenerateAutoTypeId("objc", name);
- m_data->DefineType(typeID, name, type);
- return m_data->GetTypeNameById(typeID);
+ m_reader.Seek(offset);
}
-void DSCObjCProcessor::GenerateClassTypes()
+void DSCObjCReader::SeekRelative(int64_t offset)
{
- for (auto& [_, cls] : m_classes)
- {
- QualifiedName typeName;
- StructureBuilder classTypeBuilder;
- bool failedToDecodeType = false;
- for (const auto& [ivarLoc, ivar] : cls.instanceClass.ivarList)
- {
- auto encodedTypeList = ParseEncodedType(ivar.type);
- if (encodedTypeList.empty())
- {
- failedToDecodeType = true;
- break;
- }
- auto encodedType = encodedTypeList.at(0);
-
- Ref<Type> type;
-
- if (encodedType.type)
- type = encodedType.type;
- else
- {
- type = Type::NamedType(encodedType.name, Type::PointerType(m_data->GetAddressSize(), Type::VoidType()));
- for (size_t i = encodedType.ptrCount; i > 0; i--)
- type = Type::PointerType(m_data->GetAddressSize(), type);
- }
-
- if (!type)
- type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
-
- classTypeBuilder.AddMemberAtOffset(type, ivar.name, ivar.offset);
- }
- if (failedToDecodeType)
- continue;
- auto classTypeStruct = classTypeBuilder.Finalize();
- QualifiedName classTypeName = cls.name;
- std::string classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
- Ref<Type> classType = Type::StructureType(classTypeStruct);
- QualifiedName classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
- cls.associatedName = classTypeName;
- }
+ m_reader.SeekRelative(offset);
}
-bool DSCObjCProcessor::ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod)
+VMReader& DSCObjCReader::GetVMReader()
{
- if (!method.imp || !m_data->IsValidOffset(method.imp)) {
- return false;
- }
-
- std::stringstream r(method.name);
-
- std::string token;
- std::vector<std::string> selectorTokens;
- while (std::getline(r, token, ':'))
- selectorTokens.push_back(token);
-
- std::vector<QualifiedNameOrType> typeTokens = ParseEncodedType(method.types);
- if (typeTokens.empty())
- return false;
-
- auto typeForQualifiedNameOrType = [this](QualifiedNameOrType nameOrType) {
- Ref<Type> type;
-
- if (nameOrType.type)
- {
- type = nameOrType.type;
- if (!type)
- type = Type::PointerType(m_data->GetAddressSize(), Type::VoidType());
- }
- else
- {
- type = Type::NamedType(nameOrType.name, Type::PointerType(m_data->GetAddressSize(), Type::VoidType()));
- for (size_t i = nameOrType.ptrCount; i > 0; i--)
- type = Type::PointerType(m_data->GetAddressSize(), type);
- }
-
- return type;
- };
-
- BinaryNinja::QualifiedNameAndType nameAndType;
- std::set<BinaryNinja::QualifiedName> typesAllowRedefinition;
-
- auto retType = typeForQualifiedNameOrType(typeTokens[0]);
-
- std::vector<BinaryNinja::FunctionParameter> params;
- auto cc = m_data->GetDefaultPlatform()->GetDefaultCallingConvention();
-
- params.push_back({"self",
- cls.associatedName.IsEmpty() ?
- Type::NamedType(m_data, {"id"}) :
- Type::PointerType(m_data->GetAddressSize(), Type::NamedType(m_data, cls.associatedName)),
- true, BinaryNinja::Variable()});
-
- params.push_back({"sel", Type::NamedType(m_data, {"SEL"}), true, BinaryNinja::Variable()});
-
- for (size_t i = 3; i < typeTokens.size(); i++)
- {
- std::string suffix;
-
- params.push_back({selectorTokens.size() > i - 3 ? selectorTokens[i - 3] : "arg",
- typeForQualifiedNameOrType(typeTokens[i]), true, BinaryNinja::Variable()});
- }
-
- auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params);
-
- // Search for the method's implementation function; apply the type if found.
- std::string prefix = isInstanceMethod ? "-" : "+";
- auto name = prefix + "[" + cls.name + " " + method.name + "]";
-
- DefineObjCSymbol(FunctionSymbol, funcType, name, method.imp, true);
-
- return true;
+ return m_reader;
}
-void DSCObjCProcessor::ApplyMethodTypes(Class& cls)
+std::shared_ptr<ObjCReader> DSCObjCProcessor::GetReader()
{
- for (auto& [_, method] : cls.instanceClass.methodList)
- {
- ApplyMethodType(cls, method, true);
- }
- for (auto& [_, method] : cls.metaClass.methodList)
- {
- ApplyMethodType(cls, method, false);
- }
+ return std::make_shared<DSCObjCReader>(m_cache, m_data->GetAddressSize());
}
-void DSCObjCProcessor::PostProcessObjCSections(VMReader* reader, std::string baseName)
+void DSCObjCProcessor::GetRelativeMethod(ObjCReader* reader, method_t& meth)
{
- auto ptrSize = m_data->GetAddressSize();
- if (auto imageInfo = m_data->GetSectionByName(baseName + "::__objc_imageinfo"))
- {
- auto start = imageInfo->GetStart();
- auto type = Type::NamedType(m_data, m_typeNames.imageInfo);
- m_data->DefineDataVariable(start, type);
- }
- if (auto selrefs = m_data->GetSectionByName(baseName + "::__objc_selrefs"))
- {
- auto start = selrefs->GetStart();
- auto end = selrefs->GetEnd();
- auto type = Type::PointerType(ptrSize, Type::IntegerType(1, false));
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- reader->Seek(i);
- auto selLoc = ReadPointerAccountingForRelocations(reader);
- std::string sel;
- if (const auto& it = m_selectorCache.find(selLoc); it != m_selectorCache.end())
- sel = it->second;
- else
- {
- reader->Seek(selLoc);
- sel = reader->ReadCString(selLoc);
- m_selectorCache[selLoc] = sel;
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), sel.size() + 1), "sel_" + sel,
- selLoc, true);
- }
- DefineObjCSymbol(DataSymbol, type, "selRef_" + sel, i, true);
- }
- }
- if (auto superRefs = m_data->GetSectionByName(baseName + "::__objc_classrefs"))
- {
- auto start = superRefs->GetStart();
- auto end = superRefs->GetEnd();
- auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- reader->Seek(i);
- auto clsLoc = ReadPointerAccountingForRelocations(reader);
- if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
- {
- auto& cls = it->second;
- std::string name = cls.name;
- if (!name.empty())
- DefineObjCSymbol(DataSymbol, type, "clsRef_" + name, i, true);
- }
- }
- }
- if (auto superRefs = m_data->GetSectionByName(baseName + "::__objc_superrefs"))
- {
- auto start = superRefs->GetStart();
- auto end = superRefs->GetEnd();
- auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- reader->Seek(i);
- auto clsLoc = ReadPointerAccountingForRelocations(reader);
- if (const auto& it = m_classes.find(clsLoc); it != m_classes.end())
- {
- auto& cls = it->second;
- std::string name = cls.name;
- if (!name.empty())
- DefineObjCSymbol(DataSymbol, type, "superRef_" + name, i, true);
- }
- }
- }
- if (auto protoRefs = m_data->GetSectionByName(baseName + "::__objc_protorefs"))
+ if (m_customRelativeMethodSelectorBase.has_value())
{
- auto start = protoRefs->GetStart();
- auto end = protoRefs->GetEnd();
- auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.protocol));
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- reader->Seek(i);
- auto protoLoc = ReadPointerAccountingForRelocations(reader);
- if (const auto& it = m_protocols.find(protoLoc); it != m_protocols.end())
- {
- auto& proto = it->second;
- std::string name = proto.name;
- if (!name.empty())
- DefineObjCSymbol(DataSymbol, type, "protoRef_" + name, i, true);
- }
- }
+ meth.name = m_customRelativeMethodSelectorBase.value() + reader->ReadS32();
+ meth.types = reader->GetOffset() + reader->ReadS32();
+ meth.imp = reader->GetOffset() + reader->ReadS32();
}
- if (auto ivars = m_data->GetSectionByName(baseName + "::__objc_ivar"))
+ else
{
- auto start = ivars->GetStart();
- auto end = ivars->GetEnd();
- auto ivarSectionEntryTypeBuilder = new TypeBuilder(Type::IntegerType(8, false));
- ivarSectionEntryTypeBuilder->SetConst(true);
- auto type = ivarSectionEntryTypeBuilder->Finalize();
- for (view_ptr_t i = start; i < end; i += ptrSize)
- {
- m_data->DefineDataVariable(i, type);
- }
+ ObjCProcessor::GetRelativeMethod(reader, meth);
}
}
-uint64_t DSCObjCProcessor::ReadPointerAccountingForRelocations(VMReader* reader)
+uint64_t DSCObjCProcessor::GetObjCRelativeMethodBaseAddress(ObjCReader* reader)
{
- if (auto it = m_relocationPointerRewrites.find(reader->GetOffset()); it != m_relocationPointerRewrites.end())
- {
- reader->SeekRelative(m_data->GetAddressSize());
- return it->second;
- }
- // hack
- return reader->ReadPointer();
-}
-
-
-DSCObjCProcessor::DSCObjCProcessor(BinaryNinja::BinaryView* data, SharedCache* cache, bool isBackedByDatabase) :
- m_isBackedByDatabase(isBackedByDatabase), m_data(data), m_cache(cache)
-{
- m_logger = LogRegistry::GetLogger("SharedCache.ObjC", m_data->GetFile()->GetSessionId());
-}
-
-void DSCObjCProcessor::ProcessObjCData(std::shared_ptr<VM> vm, std::string baseName)
-{
- m_symbolQueue = new SymbolQueue();
- auto addrSize = m_data->GetAddressSize();
-
- // m_typeNames.relativePtr = );
- auto rptr_t = Type::NamedType(m_data, defineTypedef(m_data, {"rptr_t"}, Type::IntegerType(4, true)));
- //
- // m_typeNames.id = defineTypedef(m_data, {"id"}, Type::PointerType(addrSize, Type::VoidType()));
- // m_typeNames.sel = defineTypedef(m_data, {"SEL"}, Type::PointerType(addrSize, Type::IntegerType(1, false)));
- //
- // m_typeNames.BOOL = defineTypedef(m_data, {"BOOL"}, Type::IntegerType(1, false));
- // m_typeNames.nsInteger = defineTypedef(m_data, {"NSInteger"}, Type::IntegerType(addrSize, true));
- // m_typeNames.nsuInteger = defineTypedef(m_data, {"NSUInteger"}, Type::IntegerType(addrSize, false));
- // m_typeNames.cgFloat = defineTypedef(m_data, {"CGFloat"}, Type::FloatType(addrSize));
-
- Ref<Type> relativeSelectorPtr;
- auto reader = VMReader(vm);
- if (auto objCRelativeMethodsBaseAddr = m_cache->GetObjCRelativeMethodBaseAddress(reader)) {
- m_logger->LogDebug("RelativeMethodSelector Base: 0x%llx", objCRelativeMethodsBaseAddr);
- m_customRelativeMethodSelectorBase = objCRelativeMethodsBaseAddr;
-
- auto type = TypeBuilder::PointerType(4, Type::PointerType(addrSize, Type::IntegerType(1, false)))
- .SetPointerBase(RelativeToConstantPointerBaseType, objCRelativeMethodsBaseAddr)
- .Finalize();
- auto relativeSelectorPtrName = defineTypedef(m_data, {"relative_SEL"}, type);
- relativeSelectorPtr = Type::NamedType(m_data, relativeSelectorPtrName);
- }
-
- // https://github.com/apple-oss-distributions/objc4/blob/196363c165b175ed925ef6b9b99f558717923c47/runtime/objc-abi.h
- EnumerationBuilder imageInfoFlagBuilder;
- imageInfoFlagBuilder.AddMemberWithValue("IsReplacement", 1 << 0);
- imageInfoFlagBuilder.AddMemberWithValue("SupportsGC", 1 << 1);
- imageInfoFlagBuilder.AddMemberWithValue("RequiresGC", 1 << 2);
- imageInfoFlagBuilder.AddMemberWithValue("OptimizedByDyld", 1 << 3);
- imageInfoFlagBuilder.AddMemberWithValue("CorrectedSynthesize", 1 << 4);
- imageInfoFlagBuilder.AddMemberWithValue("IsSimulated", 1 << 5);
- imageInfoFlagBuilder.AddMemberWithValue("HasCategoryClassProperties", 1 << 6);
- imageInfoFlagBuilder.AddMemberWithValue("OptimizedByDyldClosure", 1 << 7);
- imageInfoFlagBuilder.AddMemberWithValue("SwiftUnstableVersionMask", 0xff << 8);
- imageInfoFlagBuilder.AddMemberWithValue("SwiftStableVersionMask", 0xFFFF << 16);
- auto imageInfoFlagType = finalizeEnumerationBuilder(m_data, imageInfoFlagBuilder, 4, {"objc_image_info_flags"});
- m_typeNames.imageInfoFlags = imageInfoFlagType.first;
-
- EnumerationBuilder swiftVersionBuilder;
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion1", 1);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion1_2", 2);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion2", 3);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion3", 4);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion4", 5);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion4_1", 6); // [sic]
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion4_2", 6);
- swiftVersionBuilder.AddMemberWithValue("SwiftVersion5", 7);
- auto swiftVersionType =
- finalizeEnumerationBuilder(m_data, swiftVersionBuilder, 4, {"objc_image_info_swift_version"});
- m_typeNames.imageInfoSwiftVersion = swiftVersionType.first;
-
- StructureBuilder imageInfoBuilder;
- imageInfoBuilder.AddMember(Type::IntegerType(4, false), "version");
- imageInfoBuilder.AddMember(Type::NamedType(m_data, m_typeNames.imageInfoFlags), "flags");
- auto imageInfoType = finalizeStructureBuilder(m_data, imageInfoBuilder, "objc_image_info_t");
- m_typeNames.imageInfo = imageInfoType.first;
-
- StructureBuilder methodEntry;
- methodEntry.AddMember(relativeSelectorPtr ? relativeSelectorPtr : rptr_t, "name");
- methodEntry.AddMember(rptr_t, "types");
- methodEntry.AddMember(rptr_t, "imp");
- auto type = finalizeStructureBuilder(m_data, methodEntry, "objc_method_entry_t");
- m_typeNames.methodEntry = type.first;
-
- StructureBuilder method;
- method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
- method.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "types");
- method.AddMember(Type::PointerType(addrSize, Type::VoidType()), "imp");
- type = finalizeStructureBuilder(m_data, method, "objc_method_t");
- m_typeNames.method = type.first;
-
- StructureBuilder methList;
- methList.AddMember(Type::IntegerType(4, false), "obsolete");
- methList.AddMember(Type::IntegerType(4, false), "count");
- type = finalizeStructureBuilder(m_data, methList, "objc_method_list_t");
- m_typeNames.methodList = type.first;
-
- StructureBuilder ivarBuilder;
- ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(4, false)), "offset");
- ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
- ivarBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "type");
- ivarBuilder.AddMember(Type::IntegerType(4, false), "alignment");
- ivarBuilder.AddMember(Type::IntegerType(4, false), "size");
- type = finalizeStructureBuilder(m_data, ivarBuilder, "objc_ivar_t");
- m_typeNames.ivar = type.first;
-
- StructureBuilder ivarList;
- ivarList.AddMember(Type::IntegerType(4, false), "entsize");
- ivarList.AddMember(Type::IntegerType(4, false), "count");
- type = finalizeStructureBuilder(m_data, ivarList, "objc_ivar_list_t");
- m_typeNames.ivarList = type.first;
-
- StructureBuilder protocolListBuilder;
- protocolListBuilder.AddMember(Type::IntegerType(addrSize, false), "count");
- m_typeNames.protocolList = finalizeStructureBuilder(m_data, protocolListBuilder, "objc_protocol_list_t").first;
-
- StructureBuilder classROBuilder;
- classROBuilder.AddMember(Type::IntegerType(4, false), "flags");
- classROBuilder.AddMember(Type::IntegerType(4, false), "start");
- classROBuilder.AddMember(Type::IntegerType(4, false), "size");
- if (addrSize == 8)
- classROBuilder.AddMember(Type::IntegerType(4, false), "reserved");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "ivar_layout");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "name");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "methods");
- classROBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.protocolList)), "protocols");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.ivarList)), "ivars");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "weak_ivar_layout");
- classROBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "properties");
- type = finalizeStructureBuilder(m_data, classROBuilder, "objc_class_ro_t");
- m_typeNames.classRO = type.first;
-
- QualifiedName classTypeName("objc_class_t");
- auto classTypeId = Type::GenerateAutoTypeId("objc", classTypeName);
- auto isaType = Type::PointerType(m_data->GetDefaultArchitecture(),
- TypeBuilder::NamedType(
- new NamedTypeReferenceBuilder(StructNamedTypeClass, "", classTypeName), m_data->GetAddressSize(), 4)
- .Finalize());
-
- StructureBuilder classBuilder;
- classBuilder.AddMember(isaType, "isa");
- classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "super");
- classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "cache");
- classBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "vtable");
- classBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.classRO)), "data");
-
- auto classTypeStruct = classBuilder.Finalize();
- auto classType = Type::StructureType(classTypeStruct);
- auto classQualName = m_data->DefineType(classTypeId, classTypeName, classType);
-
- m_typeNames.cls = classQualName;
-
- StructureBuilder categoryBuilder;
- categoryBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "category_name");
- categoryBuilder.AddMember(Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.cls)), "class");
- categoryBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "inst_methods");
- categoryBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "class_methods");
- categoryBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "protocols");
- categoryBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "properties");
- m_typeNames.category = finalizeStructureBuilder(m_data, categoryBuilder, "objc_category_t").first;
-
- StructureBuilder protocolBuilder;
- protocolBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "isa");
- protocolBuilder.AddMember(Type::PointerType(addrSize, Type::IntegerType(1, true)), "mangledName");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.protocolList)), "protocols");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "instanceMethods");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "classMethods");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "optionalInstanceMethods");
- protocolBuilder.AddMember(
- Type::PointerType(addrSize, Type::NamedType(m_data, m_typeNames.methodList)), "optionalClassMethods");
- protocolBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "instanceProperties");
- protocolBuilder.AddMember(Type::IntegerType(4, false), "size");
- protocolBuilder.AddMember(Type::IntegerType(4, false), "flags");
- m_typeNames.protocol = finalizeStructureBuilder(m_data, protocolBuilder, "objc_protocol_t").first;
-
- m_data->BeginBulkModifySymbols();
- if (auto classList = m_data->GetSectionByName(baseName + "::__objc_classlist"))
- LoadClasses(&reader, classList);
- if (auto nonLazyClassList = m_data->GetSectionByName(baseName + "::__objc_nlclslist"))
- LoadClasses(&reader, nonLazyClassList); // See: https://stackoverflow.com/a/15318325
-
- GenerateClassTypes();
- for (auto& [_, cls] : m_classes)
- ApplyMethodTypes(cls);
-
- if (auto catList = m_data->GetSectionByName(baseName + "::__objc_catlist")) // Do this after loading class type data.
- LoadCategories(&reader, catList);
- if (auto nonLazyCatList = m_data->GetSectionByName(baseName + "::__objc_nlcatlist")) // Do this after loading class type data.
- LoadCategories(&reader, nonLazyCatList);
- for (auto& [_, cat] : m_categories)
- ApplyMethodTypes(cat);
-
- if (auto protoList = m_data->GetSectionByName(baseName + "::__objc_protolist"))
- LoadProtocols(&reader, protoList);
-
- PostProcessObjCSections(&reader, baseName);
-
- auto id = m_data->BeginUndoActions();
- m_symbolQueue->Process();
- m_data->EndBulkModifySymbols();
- delete m_symbolQueue;
- m_data->ForgetUndoActions(id);
-
- auto meta = SerializeMetadata();
- m_data->StoreMetadata("Objective-C", meta, true);
-
- m_relocationPointerRewrites.clear();
-}
-
-
-void DSCObjCProcessor::ProcessCFStrings(std::shared_ptr<VM> vm, std::string baseName)
-{
- m_symbolQueue = new SymbolQueue();
- uint64_t ptrSize = m_data->GetAddressSize();
- // https://github.com/apple/llvm-project/blob/next/clang/lib/CodeGen/CodeGenModule.cpp#L6129
- // See also ASTContext.cpp ctrl+f __NSConstantString_tag
-
- // The place these flags are used is unclear, along with any clear flag definitions, but they are useful for
- // introspection
- EnumerationBuilder __cfStringFlagBuilder;
- __cfStringFlagBuilder.AddMemberWithValue("SwiftABI", 0b1);
- __cfStringFlagBuilder.AddMemberWithValue("Swift4_1", 0b100);
- // LLVM also sets 0x7c0 (0b11111000000) on both UTF8 and UTF16 strings however it is unclear what this denotes.
- __cfStringFlagBuilder.AddMemberWithValue("UTF8", 0b1000);
- __cfStringFlagBuilder.AddMemberWithValue("UTF16", 0b10000);
- auto type = finalizeEnumerationBuilder(m_data, __cfStringFlagBuilder, ptrSize, {"CFStringFlag"});
- m_typeNames.cfStringFlag = type.first;
-
- StructureBuilder __cfStringStructBuilder;
- __cfStringStructBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
- __cfStringStructBuilder.AddMember(Type::NamedType(m_data, m_typeNames.cfStringFlag), "flags");
- __cfStringStructBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(1, true)), "data");
- __cfStringStructBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
- type = finalizeStructureBuilder(m_data, __cfStringStructBuilder, "__NSConstantString");
- m_typeNames.cfString = type.first;
-
- StructureBuilder __cfStringUTF16StructBuilder;
- __cfStringUTF16StructBuilder.AddMember(Type::PointerType(ptrSize, Type::VoidType()), "isa");
- __cfStringUTF16StructBuilder.AddMember(Type::NamedType(m_data, m_typeNames.cfStringFlag), "flags");
- __cfStringUTF16StructBuilder.AddMember(Type::PointerType(ptrSize, Type::IntegerType(2, true)), "data");
- __cfStringUTF16StructBuilder.AddMember(Type::IntegerType(ptrSize, false), "length");
- type = finalizeStructureBuilder(m_data, __cfStringUTF16StructBuilder, "__NSConstantString_UTF16");
- m_typeNames.cfStringUTF16 = type.first;
-
- auto reader = VMReader(vm);
- if (auto cfstrings = m_data->GetSectionByName(baseName + "::__cfstring"))
- {
- auto start = cfstrings->GetStart();
- auto end = cfstrings->GetEnd();
- auto typeWidth = Type::NamedType(m_data, m_typeNames.cfString)->GetWidth();
- m_data->BeginBulkModifySymbols();
- for (view_ptr_t i = start; i < end; i += typeWidth)
- {
- reader.Seek(i + ptrSize);
- uint64_t flags = reader.ReadPointer();
- auto strLoc = ReadPointerAccountingForRelocations(&reader);
- auto size = reader.ReadPointer();
- std::string str;
- if (flags & 0b10000) // UTF16
- {
- auto data = m_data->ReadBuffer(strLoc, size * 2);
-
- str = "";
- for (uint64_t bufferOff = 0; bufferOff < size * 2; bufferOff += 2)
- {
- uint8_t* rawData = static_cast<uint8_t*>(data.GetData());
- uint8_t* offsetAddress = rawData + bufferOff;
- uint16_t c = *reinterpret_cast<uint16_t*>(offsetAddress);
- if (c == 0x20)
- str.push_back('_');
- else if (c < 0x80)
- str.push_back(c);
- else
- str.push_back('?');
- }
- DefineObjCSymbol(
- DataSymbol, Type::ArrayType(Type::WideCharType(2), size + 1), "ustr_" + str, strLoc, true);
- DefineObjCSymbol(
- DataSymbol, Type::NamedType(m_data, m_typeNames.cfStringUTF16), "cfstr_" + str, i, true);
- }
- else // UTF8 / ASCII
- {
- reader.Seek(strLoc);
- str = reader.ReadCString(strLoc);
- for (auto& c : str)
- {
- if (c == ' ')
- c = '_';
- }
- DefineObjCSymbol(DataSymbol, Type::ArrayType(Type::IntegerType(1, true), str.size() + 1), "cstr_" + str,
- strLoc, true);
- DefineObjCSymbol(DataSymbol, Type::NamedType(m_data, m_typeNames.cfString), "cfstr_" + str, i, true);
- }
- }
- auto id = m_data->BeginUndoActions();
- m_symbolQueue->Process();
- m_data->EndBulkModifySymbols();
- m_data->ForgetUndoActions(id);
- }
- delete m_symbolQueue;
+ auto objCRelativeMethodsBaseAddr = m_cache->GetObjCRelativeMethodBaseAddress(static_cast<DSCObjCReader*>(reader)->GetVMReader());
+ m_customRelativeMethodSelectorBase = objCRelativeMethodsBaseAddr;
+ return objCRelativeMethodsBaseAddr;
}
-void DSCObjCProcessor::AddRelocatedPointer(uint64_t location, uint64_t rewrite)
+DSCObjCProcessor::DSCObjCProcessor(BinaryView* data, SharedCache* cache, bool isBackedByDatabase) :
+ ObjCProcessor(data, "SharedCache.ObjC", isBackedByDatabase, true), m_cache(cache)
{
- m_relocationPointerRewrites[location] = rewrite;
}
diff --git a/view/sharedcache/core/ObjC.h b/view/sharedcache/core/ObjC.h
index a308c23a..13790364 100644
--- a/view/sharedcache/core/ObjC.h
+++ b/view/sharedcache/core/ObjC.h
@@ -6,233 +6,47 @@
#define SHAREDCACHE_OBJC_H
#include <binaryninjaapi.h>
-#include "VM.h"
+#include <objectivec/objc.h>
#include "SharedCache.h"
-using namespace BinaryNinja;
-
-
namespace DSCObjC {
+ class DSCObjCReader : public ObjCReader {
+ private:
+ VMReader m_reader;
+ size_t m_addressSize;
- // This set of structs is based on the objc4 source,
- // however pointers have been replaced with view_ptr_t
-
- // Used for pointers within BinaryView, primarily to make it far more clear in typedefs
- // whether the size of a field can vary between architectures.
- // These should _not_ be used in sizeof or direct Read() calls.
- typedef uint64_t view_ptr_t;
-
- typedef struct {
- view_ptr_t name;
- view_ptr_t types;
- view_ptr_t imp;
- } method_t;
- typedef struct {
- uint32_t name;
- uint32_t types;
- uint32_t imp;
- } method_entry_t;
- typedef struct {
- view_ptr_t offset;
- view_ptr_t name;
- view_ptr_t type;
- uint32_t alignmentRaw;
- uint32_t size;
- } ivar_t;
- typedef struct {
- view_ptr_t name;
- view_ptr_t attributes;
- } property_t;
- typedef struct {
- uint32_t entsizeAndFlags;
- uint32_t count;
- } method_list_t;
- typedef struct {
- uint32_t entsizeAndFlags;
- uint32_t count;
- } ivar_list_t;
- typedef struct {
- uint32_t entsizeAndFlags;
- uint32_t count;
- } property_list_t;
- typedef struct {
- uint64_t count;
- } protocol_list_t;
- struct relative_list_list_entry_t {
- uint64_t imageIndex: 16;
- int64_t listOffset: 48;
- };
- typedef struct {
- view_ptr_t isa;
- view_ptr_t mangledName;
- view_ptr_t protocols;
- view_ptr_t instanceMethods;
- view_ptr_t classMethods;
- view_ptr_t optionalInstanceMethods;
- view_ptr_t optionalClassMethods;
- view_ptr_t instanceProperties;
- uint32_t size;
- uint32_t flags;
- } protocol_t;
- typedef struct {
- uint32_t flags;
- uint32_t instanceStart;
- uint32_t instanceSize;
- uint32_t reserved;
- view_ptr_t ivarLayout;
- view_ptr_t name;
- view_ptr_t baseMethods;
- view_ptr_t baseProtocols;
- view_ptr_t ivars;
- view_ptr_t weakIvarLayout;
- view_ptr_t baseProperties;
- } class_ro_t;
- typedef struct {
- view_ptr_t isa;
- view_ptr_t super;
- view_ptr_t cache;
- view_ptr_t vtable;
- view_ptr_t data;
- } class_t;
- typedef struct {
- view_ptr_t name;
- view_ptr_t cls;
- view_ptr_t instanceMethods;
- view_ptr_t classMethods;
- view_ptr_t protocols;
- view_ptr_t instanceProperties;
- } category_t;
- typedef struct {
- view_ptr_t receiver;
- view_ptr_t current_class;
- } objc_super2;
- typedef struct {
- view_ptr_t imp;
- view_ptr_t sel;
- } message_ref_t;
-
- struct Method {
- std::string name;
- std::string types;
- view_ptr_t imp;
- };
-
- struct Ivar {
- uint32_t offset;
- std::string name;
- std::string type;
- uint32_t alignment;
- uint32_t size;
- };
-
- struct Property {
- std::string name;
- std::string attributes;
- };
-
- struct ClassBase {
- std::map<uint64_t, Method> methodList;
- std::map<uint64_t, Ivar> ivarList;
- };
-
- struct Class {
- std::string name;
- ClassBase instanceClass;
- ClassBase metaClass;
-
- // Loaded by type processing
- QualifiedName associatedName;
- };
-
- class Protocol {
public:
- std::string name;
- std::vector<QualifiedName> protocols;
- ClassBase instanceMethods;
- ClassBase classMethods;
- ClassBase optionalInstanceMethods;
- ClassBase optionalClassMethods;
- };
-
- struct QualifiedNameOrType {
- BinaryNinja::Ref<BinaryNinja::Type> type = nullptr;
- BinaryNinja::QualifiedName name;
- size_t ptrCount = 0;
- };
+ void Read(void* dest, size_t len) override;
+ std::string ReadCString() override;
+ uint8_t Read8() override;
+ uint16_t Read16() override;
+ uint32_t Read32() override;
+ uint64_t Read64() override;
+ int8_t ReadS8() override;
+ int16_t ReadS16() override;
+ int32_t ReadS32() override;
+ int64_t ReadS64() override;
+ uint64_t ReadPointer() override;
+ uint64_t GetOffset() const override;
+ void Seek(uint64_t offset) override;
+ void SeekRelative(int64_t offset) override;
- class DSCObjCProcessor {
- struct Types {
- // QualifiedName relativePtr;
- // QualifiedName id;
- // QualifiedName sel;
- // QualifiedName BOOL;
- // QualifiedName nsInteger;
- // QualifiedName nsuInteger;
- // QualifiedName cgFloat;
- QualifiedName cfStringFlag;
- QualifiedName cfString;
- QualifiedName cfStringUTF16;
- QualifiedName imageInfoFlags;
- QualifiedName imageInfoSwiftVersion;
- QualifiedName imageInfo;
- QualifiedName methodEntry;
- QualifiedName method;
- QualifiedName methodList;
- QualifiedName classRO;
- QualifiedName cls;
- QualifiedName category;
- QualifiedName protocol;
- QualifiedName protocolList;
- QualifiedName ivar;
- QualifiedName ivarList;
- } m_typeNames;
-
- bool m_isBackedByDatabase;
-
- BinaryView* m_data;
- SymbolQueue* m_symbolQueue = nullptr;
- Ref<Logger> m_logger;
- std::map<uint64_t, Class> m_classes;
- std::map<uint64_t, Class> m_categories;
- std::map<uint64_t, Protocol> m_protocols;
- std::unordered_map<uint64_t, std::string> m_selectorCache;
- std::unordered_map<uint64_t, Method> m_localMethods;
-
- // Required for workflow_objc type heuristics, should be removed when that is no longer a thing.
- std::map<uint64_t, std::string> m_selRefToName;
- std::map<uint64_t, std::vector<uint64_t>> m_selRefToImplementations;
- std::map<uint64_t, std::vector<uint64_t>> m_selToImplementations;
- // --
+ VMReader& GetVMReader();
+ DSCObjCReader(SharedCacheCore::SharedCache* cache, size_t addressSize);
+ };
+ class DSCObjCProcessor : public ObjCProcessor {
std::optional<uint64_t> m_customRelativeMethodSelectorBase = std::nullopt;
SharedCacheCore::SharedCache* m_cache;
- uint64_t ReadPointerAccountingForRelocations(VMReader* reader);
- std::unordered_map<uint64_t, uint64_t> m_relocationPointerRewrites;
-
- static Ref<Metadata> SerializeMethod(uint64_t loc, const Method& method);
- static Ref<Metadata> SerializeClass(uint64_t loc, const Class& cls);
-
- Ref<Metadata> SerializeMetadata();
- std::vector<QualifiedNameOrType> ParseEncodedType(const std::string& type);
- void DefineObjCSymbol(BNSymbolType symbolType, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred);
- void DefineObjCSymbol(BNSymbolType symbolType, Ref<Type> type, const std::string& name, uint64_t addr, bool deferred);
- void ReadIvarList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
- void ReadMethodList(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
- void ReadListOfMethodLists(VMReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start);
- void LoadClasses(VMReader* reader, Ref<Section> listSection);
- void LoadCategories(VMReader* reader, Ref<Section> listSection);
- void LoadProtocols(VMReader* reader, Ref<Section> listSection);
- void GenerateClassTypes();
- bool ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod);
- void ApplyMethodTypes(Class& cls);
- void PostProcessObjCSections(VMReader* reader, std::string baseName);
+ std::shared_ptr<ObjCReader> GetReader() override;
+ void GetRelativeMethod(ObjCReader* reader, method_t& meth) override;
+
public:
DSCObjCProcessor(BinaryView* data, SharedCacheCore::SharedCache* cache, bool isBackedByDatabase);
- void ProcessObjCData(std::shared_ptr<VM> vm, std::string baseName);
- void ProcessCFStrings(std::shared_ptr<VM> vm, std::string baseName);
- void AddRelocatedPointer(uint64_t location, uint64_t rewrite);
+
+ uint64_t GetObjCRelativeMethodBaseAddress(ObjCReader* reader) override;
};
}
#endif //SHAREDCACHE_OBJC_H
diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp
index 5709759b..2b18c064 100644
--- a/view/sharedcache/core/SharedCache.cpp
+++ b/view/sharedcache/core/SharedCache.cpp
@@ -1673,9 +1673,9 @@ static void ProcessObjCSectionsForImageWithName(std::string baseName, std::share
try
{
if (processObjCMetadata)
- objc->ProcessObjCData(vm, baseName);
+ objc->ProcessObjCData(baseName);
if (processCFStrings)
- objc->ProcessCFStrings(vm, baseName);
+ objc->ProcessCFStrings(baseName);
}
catch (const std::exception& ex)
{