diff options
Diffstat (limited to 'objectivec/objc.h')
| -rw-r--r-- | objectivec/objc.h | 337 |
1 files changed, 337 insertions, 0 deletions
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); + }; +} + |
