{"id":20957922,"url":"https://github.com/jonpalmisc/objc-kb","last_synced_at":"2025-05-14T06:31:41.479Z","repository":{"id":124278310,"uuid":"494083894","full_name":"jonpalmisc/objc-kb","owner":"jonpalmisc","description":"Notes on the Objective-C ABI and related topics","archived":true,"fork":false,"pushed_at":"2023-11-20T17:20:16.000Z","size":24,"stargazers_count":45,"open_issues_count":0,"forks_count":0,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-07T01:49:43.276Z","etag":null,"topics":["apple","ios","macos","objc","objc-runtime","objective-c","reverse-engineering"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonpalmisc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-19T13:29:56.000Z","updated_at":"2025-03-03T01:06:41.000Z","dependencies_parsed_at":"2023-11-20T18:43:12.823Z","dependency_job_id":null,"html_url":"https://github.com/jonpalmisc/objc-kb","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonpalmisc%2Fobjc-kb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonpalmisc%2Fobjc-kb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonpalmisc%2Fobjc-kb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonpalmisc%2Fobjc-kb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonpalmisc","download_url":"https://codeload.github.com/jonpalmisc/objc-kb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254084643,"owners_count":22011915,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple","ios","macos","objc","objc-runtime","objective-c","reverse-engineering"],"created_at":"2024-11-19T01:44:30.089Z","updated_at":"2025-05-14T06:31:41.164Z","avatar_url":"https://github.com/jonpalmisc.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Objective-C ABI Knowledge Base\n\n_**2023 Update:** I am also not actively working on projects related to\nObjective-C anymore, and this document has not been updated in quite some\ntime. Some of the terminology here, etc. has always been wrong; other parts\nhave aged out since it was written. Some portions of this document remain\naccurate, but in general, it would be wise to verify any claims made below._\n\n---\n\nThis repository is meant to serve as a continuously-growing knowledge base\nregarding the Objective-C ABI. It is a **work in progress** and is currently\njust my personal observations and notes. It is likely that there are errors\nand/or misunderstandings. Pull requests with corrections or new knowledge are\nencouraged.\n\nNOTE: In examples where structures/types are shown, some names have been\nmodified for clarity. Structure and member names shown do not always match\nObjective-C's source code or their canonical names.\n\n## Pointer types\n\nThe Objective-C ABI utilizes numerous different encoding techniques for\npointers. Different techniques are used depending on the part of the ABI in\nquestion, the architecture of the binary, and the OS version the binary was\ncompiled for. Objective-C's internal structures make heavy use of (wacky)\npointers, so understanding different pointer types is important before looking\nat structures in detail.\n\nThe following table is a **rough outline** of how pointers are typically\nencoded, given an OS version and architecture:\n\n| OS        | Architecture | Tagged | Type           |\n|-----------|--------------|--------|----------------|\n| macOS     | x86_64       | No     | Absolute       |\n| macOS 11  | arm64        | Yes    | Absolute       |\n| macOS 12+ | arm64e       | Ye     | Image-relative |\n\nThe table above is **not** all-encompassing and ignores certain scenarios; use\nthis for a general understanding, not for writing tools.\n\n### Absolute (C-style) pointers\n\nAbsolute (C-style) pointers are sometimes used, but are becoming less common.\nThey look like \"normal\" pointers (shown below) and don't have any quirks. As of\n2022, absolute pointers are mostly only still used on x86_64.\n\n```\n0x1000311a0\n0x100031240\n```\n\n### Image-relative pointers\n\n**Image-relative pointers** are really offsets relative to the image base.\nAssuming a standard Mach-O image base of 4 GB (`0x100000000`), the\nimage-relative pointer `0x25207` is equivalent to `0x100025207`. Image-relative\npointers can be combined with some of the other pointer types detailed below,\ne.g. a tagged pointer.\n\n### Tagged pointers\n\n**Tagged pointers**\u0026mdash;used in arm64(e) binaries\u0026mdash;pointers that carry\nmetadata with them in their upper bits. They look like the following:\n\n```\n0x800d6ae1000331c8\n0x800000002c1d8\n```\n\nThe metadata portion of the pointer can be removed by performing a bitwise AND\nagainst `0x7ffffffff`.\n\n```\n0x800d6ae1000331c8 \u0026 0x7ffffffff = 0x1000331c8\n0x800000002c1d8 \u0026 0x7ffffffff = 0x2c1d8\n```\n\nThis illustrates an important point: even after removing the metadata, the\nresulting pointer can be **absolute** or **image-relative**.\n\n### Fast pointers\n\n_WARNING: This section is likely incomplete._\n\n**Fast pointers** are a type of pointer often used by Swift's ABI, which\noverlaps Objective-C's ABI. Fast pointers store metadata in the two least\nsignificant bits of the pointer. These two bits should be removed when\nattempting to dereference the pointer.\n\nHere is an example of a fast pointer, taken from the arm64e slice of the main\nbinary of macOS 12's \"Console.app\":\n\n```\n0x20000000096d62\n```\n\nThe pointer is fast, tagged, and relative. After removing the tags and adding\nthe image base, the resulting pointer is produced:\n\n```\n0x100096d62\n```\n\nHowever, if we dereferenced this pointer as is, we would be (incorrectly)\npointing into the middle of the following structure:\n\n```c\n100096d60 struct class_ro_t ro__TtC7Console21ReportsViewController = {\n100096d60     uint32_t flags = 0x184\n100096d64     uint32_t start = 0x48\n100096d68     uint32_t size = 0x68\n100096d6c     uint32_t reserved = 0x0\n100096d70     void* ivar_layout = NULL\n100096d78     void* name = nm__TtC7Console21ReportsViewController\n100096d80     void* methods = ml__TtC7Console21ReportsViewController\n100096d88     void* protocols = NULL\n100096d90     void* vars = 0x10008c068\n100096d98     void* weak_ivar_layout = NULL\n100096da0     void* properties = 0x10008c0f0\n100096da8 }\n```\n\n\u003e The definition of this strucutre isn't really important here, but is used to\n\u003e illustrate an example.\n\nAfter removing the flags in the two least significant bits, we get the correct\npointer, which points to the structure's base:\n\n```\n0x100096d62 \u0026 (~0b11) = 0x100096d60\n```\n\n## Types and structures\n\nThis section details structures used by the Objective-C ABI. The structure\nlayouts shown in the following subsections represent how structures are laid out\n_at rest_, i.e. in a binary, not necessarily how they are laid out in memory at\nruntime.\n\n### Classes\n\n**Class** structures are defined in the `__objc_data` section and are the basis\nfor how classes are stored inside the binary. Class structures have the\nfollowing layout:\n\n```c\nstruct class_t {\n    const void* isa;\n    const class_t* super;       /* Superclass' `class_t` structure */\n    void* cache;                /* Commonly `nullptr` */\n    void* vtable;               /* Commonly `nullptr` */\n    const class_ro_t* data;     /* Associated class RO structure */\n};\n```\n\n\u003e Any of the members which are pointers may be tagged or image-relative. The\n\u003e `data` member may be a fast pointer.\n\n### Class RO\n\n**Class RO** (read-only) structures are defined inside of the `__objc_const`\nsection. Most of the \"interesting\" information about classes\u0026mdash;such as name,\nmethods, or instance variables\u0026mdash;is stored in class RO structures. The\nlayout of class RO structures is as follows:\n\n```c\nstruct class_ro_t {\n    uint32_t flags;             /* Flags */\n    uint32_t start;\n    uint32_t size;\n    uint32_t reserved;          /* Reserved for future use */\n    const void* ivar_layout;\n    const char* name;             /* Class name */\n    const method_list_t* methods; /* Base method list */\n    const void* protocols;\n    const void* vars;\n    const void* weak_ivar_layout;\n    const void* properties;\n};\n```\n\n\u003e Any of the members which are pointers may be tagged or image-relative.\n\n### Method lists\n\n**Method lists** are found in the `__objc_const` section on x86_64, or under\n`__objc_methlist` section under arm64(e). Method lists describe all of the base\nmethods associated with a class.\n\n#### Header\n\nA method list begins with a **method list header**, which has the following\nformat:\n\n```c\nstruct method_list_t {\n    uint32_t size_and_flags;    /* Entry size and flags */\n    uint32_t count;             /* Number of entries */\n};\n```\n\nThe `size_and_flags` field tells the size of the each entry, and optionally has\nflags in high bits. Flags can be isolated by performing a bitwise AND of the\n`size_and_flags` field with `0xffff0000`. The following flags may be present in\nthe `size_and_flags` field:\n\n| Flag                   | Value        |\n|------------------------|--------------|\n| `HAS_RELATIVE_OFFSETS` | `0x80000000` |\n| `HAS_DIRECT_SELECTORS` | `0x40000000` |\n\nThe `HAS_RELATIVE_OFFSETS` flag tells whether the pointers in the method list's\nentries should be treated as absolute pointers or relative offsets (explained in\nmore detail below).\n\nThe `HAS_DIRECT_SELECTORS` flag tells what the name/selector field in this\nmethod lists's entries points to. If the flag is set, the field points directly\nto a string; if the flag is unset, it points to a selector reference.\n\n#### Entries\n\nImmediately following the method list's header comes one or more **entries**.\nEntries may have either of the following layouts:\n\n```c\nstruct method_t {\n    const char* name;           /* Pointer to name (or selector reference?) */\n    const char* types;          /* Pointer to type info */\n    void* imp;                  /* Pointer to implementation (code) */\n};\n\nstruct method_entry_t {\n    int32_t name;               /* Relative offset to name or selector reference */\n    int32_t types;              /* Relative offset to type info */\n    int32_t imp;                /* Relative offset to implementation (code) */\n};\n```\n\nThe former entry layout (hereafter the \"legacy\" format) is older and is\nprimarily used on x86_64. The latter format (hereafter the \"modern\" format) is\nnewer and is used on arm64(e).\n\nThe modern format always utilizes **relative offsets**\u0026mdash;not to be confused\nwith image-relative pointers\u0026mdash;to point to its associated data. These\noffsets are to be interpreted as offsets from the structure member's absolute\nposition in memory.\n\nTo illustrate the difference between the two formats, have a look at the method\nlist for the `BitFieldBox` in macOS 12's Calculator.app. Below is an excerpt of\nthe method list from the x86_64 slice, which uses the legacy format:\n\n```c\n100028198 struct method_list_t ml_BitFieldBox = {\n100028198     uint32_t size_and_flags = 0x18 /* No flags; 24-byte entries */\n10002819c     uint32_t count = 0x4           /* 4 methods (only 1 shown here) */\n1000281a0 }\n1000281a0 struct method_t mt_initWithFrame_ = {\n1000281a0     const char* name = 0x10001c6e8  /* \u0026\"initWithFrame:\" */\n1000281a8     const char* types = 0x1000214c5 /* \u0026\"@48@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16\" */\n1000281b0     void* imp = 0x100006829\t      /* [BitFieldBox initWithFrame:] */\n1000281b8 }\n```\n\nIn comparison, here is an excerpt of the same method list in the arm64e slice of\nthe binary:\n\n```c\n10001f3b0  struct method_list_t ml_BitFieldBox = {\n10001f3b0      uint32_t size_and_flags = 0x8000000c /* HAS_RELATIVE_OFFSETS; 12-byte entries */\n10001f3b4      uint32_t count = 0x4                 /* 4 entries (only 1 shown here) */\n10001f3b8  }\n10001f3b8  struct method_entry_t mt_initWithFrame_ = {\n10001f3b8      int32_t name = 0x12498 /* 0x100031850 = \u0026\u0026\"initWithFrame:\" */\n10001f3bc      int32_t types = 0x6165 /* 0x100025521 = \u0026\"@48@0:8{CGRect={CGPoint=dd}{CGSize=dd}}16\" */\n10001f3c0      int32_t imp = -0x16d34 /* 0x10000868c = [BitFieldBox initWithFrame:] */\n10001f3c4  }\n```\n\n\n## Further reading\n\nBelow are some resources and projects related to the Objective-C ABI that may be\nuseful references.\n\n**Resources**\n\n- https://developpaper.com/in-depth-analysis-of-the-structure-of-the-method-in-objc/\n- https://www.fortinet.com/blog/threat-research/rewriting-idapython-script-objc2-xrefs-helper-py-for-hopper\n\n**Projects**\n\n- https://github.com/cxnder/ktool\n- https://github.com/blacktop/ipsw\n- https://github.com/jonpalmisc/ObjectiveNinja\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonpalmisc%2Fobjc-kb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonpalmisc%2Fobjc-kb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonpalmisc%2Fobjc-kb/lists"}