|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <binaryninjaapi.h> |
| 4 | + |
| 5 | +namespace BinaryNinja { |
| 6 | + // This set of structs is based on the objc4 source, |
| 7 | + // however pointers have been replaced with view_ptr_t |
| 8 | + |
| 9 | + // Used for pointers within BinaryView, primarily to make it far more clear in typedefs |
| 10 | + // whether the size of a field can vary between architectures. |
| 11 | + // These should _not_ be used in sizeof or direct Read() calls. |
| 12 | + typedef uint64_t view_ptr_t; |
| 13 | + |
| 14 | + typedef struct { |
| 15 | + view_ptr_t name; |
| 16 | + view_ptr_t types; |
| 17 | + view_ptr_t imp; |
| 18 | + } method_t; |
| 19 | + typedef struct { |
| 20 | + uint32_t name; |
| 21 | + uint32_t types; |
| 22 | + uint32_t imp; |
| 23 | + } method_entry_t; |
| 24 | + typedef struct { |
| 25 | + view_ptr_t offset; |
| 26 | + view_ptr_t name; |
| 27 | + view_ptr_t type; |
| 28 | + uint32_t alignmentRaw; |
| 29 | + uint32_t size; |
| 30 | + } ivar_t; |
| 31 | + typedef struct { |
| 32 | + view_ptr_t name; |
| 33 | + view_ptr_t attributes; |
| 34 | + } property_t; |
| 35 | + typedef struct { |
| 36 | + uint32_t entsizeAndFlags; |
| 37 | + uint32_t count; |
| 38 | + } method_list_t; |
| 39 | + typedef struct { |
| 40 | + uint32_t entsizeAndFlags; |
| 41 | + uint32_t count; |
| 42 | + } ivar_list_t; |
| 43 | + typedef struct { |
| 44 | + uint32_t entsizeAndFlags; |
| 45 | + uint32_t count; |
| 46 | + } property_list_t; |
| 47 | + typedef struct { |
| 48 | + uint64_t count; |
| 49 | + } protocol_list_t; |
| 50 | + struct relative_list_list_entry_t { |
| 51 | + uint64_t imageIndex: 16; |
| 52 | + int64_t listOffset: 48; |
| 53 | + }; |
| 54 | + typedef struct { |
| 55 | + view_ptr_t isa; |
| 56 | + view_ptr_t mangledName; |
| 57 | + view_ptr_t protocols; |
| 58 | + view_ptr_t instanceMethods; |
| 59 | + view_ptr_t classMethods; |
| 60 | + view_ptr_t optionalInstanceMethods; |
| 61 | + view_ptr_t optionalClassMethods; |
| 62 | + view_ptr_t instanceProperties; |
| 63 | + uint32_t size; |
| 64 | + uint32_t flags; |
| 65 | + } protocol_t; |
| 66 | + typedef struct { |
| 67 | + uint32_t flags; |
| 68 | + uint32_t instanceStart; |
| 69 | + uint32_t instanceSize; |
| 70 | + uint32_t reserved; |
| 71 | + view_ptr_t ivarLayout; |
| 72 | + view_ptr_t name; |
| 73 | + view_ptr_t baseMethods; |
| 74 | + view_ptr_t baseProtocols; |
| 75 | + view_ptr_t ivars; |
| 76 | + view_ptr_t weakIvarLayout; |
| 77 | + view_ptr_t baseProperties; |
| 78 | + } class_ro_t; |
| 79 | + typedef struct { |
| 80 | + view_ptr_t isa; |
| 81 | + view_ptr_t super; |
| 82 | + view_ptr_t cache; |
| 83 | + view_ptr_t vtable; |
| 84 | + view_ptr_t data; |
| 85 | + } class_t; |
| 86 | + typedef struct { |
| 87 | + view_ptr_t name; |
| 88 | + view_ptr_t cls; |
| 89 | + view_ptr_t instanceMethods; |
| 90 | + view_ptr_t classMethods; |
| 91 | + view_ptr_t protocols; |
| 92 | + view_ptr_t instanceProperties; |
| 93 | + } category_t; |
| 94 | + typedef struct { |
| 95 | + view_ptr_t receiver; |
| 96 | + view_ptr_t current_class; |
| 97 | + } objc_super2; |
| 98 | + typedef struct { |
| 99 | + view_ptr_t imp; |
| 100 | + view_ptr_t sel; |
| 101 | + } message_ref_t; |
| 102 | + |
| 103 | + struct Method { |
| 104 | + std::string name; |
| 105 | + std::string types; |
| 106 | + view_ptr_t imp; |
| 107 | + }; |
| 108 | + |
| 109 | + struct Ivar { |
| 110 | + uint32_t offset; |
| 111 | + std::string name; |
| 112 | + std::string type; |
| 113 | + uint32_t alignment; |
| 114 | + uint32_t size; |
| 115 | + }; |
| 116 | + |
| 117 | + struct Property { |
| 118 | + std::string name; |
| 119 | + std::string attributes; |
| 120 | + }; |
| 121 | + |
| 122 | + struct ClassBase { |
| 123 | + std::map<uint64_t, Method> methodList; |
| 124 | + std::map<uint64_t, Ivar> ivarList; |
| 125 | + }; |
| 126 | + |
| 127 | + struct Class { |
| 128 | + std::string name; |
| 129 | + ClassBase instanceClass; |
| 130 | + ClassBase metaClass; |
| 131 | + |
| 132 | + // Loaded by type processing |
| 133 | + QualifiedName associatedName; |
| 134 | + }; |
| 135 | + |
| 136 | + class Protocol { |
| 137 | + public: |
| 138 | + std::string name; |
| 139 | + std::vector<QualifiedName> protocols; |
| 140 | + ClassBase instanceMethods; |
| 141 | + ClassBase classMethods; |
| 142 | + ClassBase optionalInstanceMethods; |
| 143 | + ClassBase optionalClassMethods; |
| 144 | + }; |
| 145 | + |
| 146 | + struct QualifiedNameOrType { |
| 147 | + BinaryNinja::Ref<BinaryNinja::Type> type = nullptr; |
| 148 | + BinaryNinja::QualifiedName name; |
| 149 | + size_t ptrCount = 0; |
| 150 | + }; |
| 151 | + |
| 152 | + class ObjCReader { |
| 153 | + public: |
| 154 | + virtual ~ObjCReader() = default; |
| 155 | + |
| 156 | + /*! Read from the current cursor position into buffer `dest` and advance the cursor that many bytes |
| 157 | +
|
| 158 | + \throws Exception |
| 159 | + \param dest Address to write the read bytes to |
| 160 | + \param len Number of bytes to write |
| 161 | + */ |
| 162 | + virtual void Read(void* dest, size_t len) = 0; |
| 163 | + |
| 164 | + /*! Read a null-terminated string from the current cursor position |
| 165 | +
|
| 166 | + \throws Exception |
| 167 | + \return the string |
| 168 | + */ |
| 169 | + virtual std::string ReadCString(size_t maxLength = -1) = 0; |
| 170 | + |
| 171 | + /*! Read a uint8_t from the current cursor position and advance the cursor by 1 byte |
| 172 | +
|
| 173 | + \throws Exception |
| 174 | + \return The read value |
| 175 | + */ |
| 176 | + virtual uint8_t Read8() = 0; |
| 177 | + |
| 178 | + /*! Read a uint16_t from the current cursor position and advance the cursor by 2 bytes |
| 179 | +
|
| 180 | + \throws Exception |
| 181 | + \return The read value |
| 182 | + */ |
| 183 | + virtual uint16_t Read16() = 0; |
| 184 | + |
| 185 | + /*! Read a uint32_t from the current cursor position and advance the cursor by 4 bytes |
| 186 | +
|
| 187 | + \throws Exception |
| 188 | + \return The read value |
| 189 | + */ |
| 190 | + virtual uint32_t Read32() = 0; |
| 191 | + |
| 192 | + /*! Read a uint64_t from the current cursor position and advance the cursor by 8 bytes |
| 193 | +
|
| 194 | + \throws Exception |
| 195 | + \return The read value |
| 196 | + */ |
| 197 | + virtual uint64_t Read64() = 0; |
| 198 | + |
| 199 | + /*! Read a int8_t from the current cursor position and advance the cursor by 1 byte |
| 200 | +
|
| 201 | + \throws Exception |
| 202 | + \return The read value |
| 203 | + */ |
| 204 | + virtual int8_t ReadS8() = 0; |
| 205 | + |
| 206 | + /*! Read a int16_t from the current cursor position and advance the cursor by 2 bytes |
| 207 | +
|
| 208 | + \throws Exception |
| 209 | + \return The read value |
| 210 | + */ |
| 211 | + virtual int16_t ReadS16() = 0; |
| 212 | + |
| 213 | + /*! Read a int32_t from the current cursor position and advance the cursor by 4 bytes |
| 214 | +
|
| 215 | + \throws Exception |
| 216 | + \return The read value |
| 217 | + */ |
| 218 | + virtual int32_t ReadS32() = 0; |
| 219 | + |
| 220 | + /*! Read a int64_t from the current cursor position and advance the cursor by 8 bytes |
| 221 | +
|
| 222 | + \throws Exception |
| 223 | + \return The read value |
| 224 | + */ |
| 225 | + virtual int64_t ReadS64() = 0; |
| 226 | + |
| 227 | + /*! Read a pointer from the current cursor position and advance it that many bytes |
| 228 | +
|
| 229 | + \throws Exception |
| 230 | + \return The value that was read |
| 231 | + */ |
| 232 | + virtual uint64_t ReadPointer() = 0; |
| 233 | + |
| 234 | + /*! Get the current cursor position |
| 235 | +
|
| 236 | + \return The current cursor position |
| 237 | + */ |
| 238 | + virtual uint64_t GetOffset() const = 0; |
| 239 | + |
| 240 | + /*! Set the cursor position |
| 241 | +
|
| 242 | + \param offset The new cursor position |
| 243 | + */ |
| 244 | + virtual void Seek(uint64_t offset) = 0; |
| 245 | + |
| 246 | + /*! Set the cursor position, relative to the current position |
| 247 | +
|
| 248 | + \param offset Offset to the current cursor position |
| 249 | + */ |
| 250 | + virtual void SeekRelative(int64_t offset) = 0; |
| 251 | + }; |
| 252 | + |
| 253 | + class ObjCProcessor { |
| 254 | + struct Types { |
| 255 | + QualifiedName relativePtr; |
| 256 | + QualifiedName id; |
| 257 | + QualifiedName sel; |
| 258 | + QualifiedName BOOL; |
| 259 | + QualifiedName nsInteger; |
| 260 | + QualifiedName nsuInteger; |
| 261 | + QualifiedName cgFloat; |
| 262 | + QualifiedName cfStringFlag; |
| 263 | + QualifiedName cfString; |
| 264 | + QualifiedName cfStringUTF16; |
| 265 | + QualifiedName imageInfoFlags; |
| 266 | + QualifiedName imageInfoSwiftVersion; |
| 267 | + QualifiedName imageInfo; |
| 268 | + QualifiedName methodEntry; |
| 269 | + QualifiedName method; |
| 270 | + QualifiedName methodList; |
| 271 | + QualifiedName classRO; |
| 272 | + QualifiedName cls; |
| 273 | + QualifiedName category; |
| 274 | + QualifiedName protocol; |
| 275 | + QualifiedName protocolList; |
| 276 | + QualifiedName ivar; |
| 277 | + QualifiedName ivarList; |
| 278 | + } m_typeNames; |
| 279 | + |
| 280 | + bool m_isBackedByDatabase; |
| 281 | + // TODO(WeiN76LQh): this is to avoid a bug with defining a classes protocol list in the DSC plugin. Remove once fixed |
| 282 | + bool m_skipClassBaseProtocols; |
| 283 | + |
| 284 | + SymbolQueue* m_symbolQueue = nullptr; |
| 285 | + std::map<uint64_t, Class> m_classes; |
| 286 | + std::map<uint64_t, Class> m_categories; |
| 287 | + std::map<uint64_t, Protocol> m_protocols; |
| 288 | + std::unordered_map<uint64_t, std::string> m_selectorCache; |
| 289 | + std::unordered_map<uint64_t, Method> m_localMethods; |
| 290 | + |
| 291 | + // Required for workflow_objc type heuristics, should be removed when that is no longer a thing. |
| 292 | + std::map<uint64_t, std::string> m_selRefToName; |
| 293 | + std::map<uint64_t, std::vector<uint64_t>> m_selRefToImplementations; |
| 294 | + std::map<uint64_t, std::vector<uint64_t>> m_selToImplementations; |
| 295 | + // -- |
| 296 | + |
| 297 | + uint64_t ReadPointerAccountingForRelocations(ObjCReader* reader); |
| 298 | + std::unordered_map<uint64_t, uint64_t> m_relocationPointerRewrites; |
| 299 | + |
| 300 | + static Ref<Metadata> SerializeMethod(uint64_t loc, const Method& method); |
| 301 | + static Ref<Metadata> SerializeClass(uint64_t loc, const Class& cls); |
| 302 | + Ref<Metadata> SerializeMetadata(); |
| 303 | + |
| 304 | + std::vector<QualifiedNameOrType> ParseEncodedType(const std::string& type); |
| 305 | + void DefineObjCSymbol(BNSymbolType symbolType, QualifiedName typeName, const std::string& name, uint64_t addr, bool deferred); |
| 306 | + void DefineObjCSymbol(BNSymbolType symbolType, Ref<Type> type, const std::string& name, uint64_t addr, bool deferred); |
| 307 | + void ReadIvarList(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start); |
| 308 | + void ReadMethodList(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start); |
| 309 | + void ReadListOfMethodLists(ObjCReader* reader, ClassBase& cls, std::string_view name, view_ptr_t start); |
| 310 | + void LoadClasses(ObjCReader* reader, Ref<Section> listSection); |
| 311 | + void LoadCategories(ObjCReader* reader, Ref<Section> listSection); |
| 312 | + void LoadProtocols(ObjCReader* reader, Ref<Section> listSection); |
| 313 | + void GenerateClassTypes(); |
| 314 | + bool ApplyMethodType(Class& cls, Method& method, bool isInstanceMethod); |
| 315 | + void ApplyMethodTypes(Class& cls); |
| 316 | + |
| 317 | + Ref<Section> GetSectionForImage(std::optional<std::string> imageName, const char* sectionName); |
| 318 | + void PostProcessObjCSections(ObjCReader* reader, std::optional<std::string> imageName); |
| 319 | + |
| 320 | + protected: |
| 321 | + Ref<BinaryView> m_data; |
| 322 | + Ref<Logger> m_logger; |
| 323 | + |
| 324 | + virtual uint64_t GetObjCRelativeMethodBaseAddress(ObjCReader* reader); |
| 325 | + virtual void GetRelativeMethod(ObjCReader* reader, method_t& meth); |
| 326 | + virtual std::shared_ptr<ObjCReader> GetReader() = 0; |
| 327 | + // Because an objective-c processor might have access to other non-view symbols that we want to retrieve. |
| 328 | + // By default, this will just get symbol at the address in the view. |
| 329 | + virtual Ref<Symbol> GetSymbol(uint64_t address); |
| 330 | + |
| 331 | + public: |
| 332 | + virtual ~ObjCProcessor() = default; |
| 333 | + |
| 334 | + ObjCProcessor(BinaryView* data, const char* loggerName, bool isBackedByDatabase, bool skipClassBaseProtocols = false); |
| 335 | + void ProcessObjCData(std::optional<std::string> imageName); |
| 336 | + void ProcessCFStrings(std::optional<std::string> imageName); |
| 337 | + void AddRelocatedPointer(uint64_t location, uint64_t rewrite); |
| 338 | + }; |
| 339 | +} |
| 340 | + |
0 commit comments