diff options
| author | WeiN76LQh <WeiN76LQh@github.com> | 2025-01-03 17:55:10 +0000 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-04-02 05:36:54 -0400 |
| commit | ee500220baa1a46f3d7ca1486fa43dc1587f20d0 (patch) | |
| tree | ee3e1549997e60849f040f5745cdac8840f25303 /view/sharedcache/core/ObjC.cpp | |
| parent | 90e9ff91f02f2cb902bf0564de1f403e23172f36 (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.
Diffstat (limited to 'view/sharedcache/core/ObjC.cpp')
| -rw-r--r-- | view/sharedcache/core/ObjC.cpp | 1518 |
1 files changed, 47 insertions, 1471 deletions
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; } |
