Skip to content

Commit 8ebc99b

Browse files
authored
Merge pull request #1328 from HackTricks-wiki/research_update_src_macos-hardening_macos-security-and-privilege-escalation_macos-apps-inspecting-debugging-and-fuzzing_objects-in-memory_20250823_082246
Research Update Enhanced src/macos-hardening/macos-security-...
2 parents 6c6b04a + 1ab8617 commit 8ebc99b

File tree

1 file changed

+166
-41
lines changed
  • src/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing

1 file changed

+166
-41
lines changed

src/macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/objects-in-memory.md

Lines changed: 166 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
## CFRuntimeClass
66

7-
CF\* objects come from CoreFOundation, which provides more than 50 classes of objects like `CFString`, `CFNumber` or `CFAllocatior`.
7+
CF* objects come from CoreFoundation, which provides more than 50 classes of objects like `CFString`, `CFNumber` or `CFAllocator`.
88

9-
All these clases are instances of the class `CFRuntimeClass`, which when called it returns an index to the `__CFRuntimeClassTable`. The CFRuntimeClass is defined in [**CFRuntime.h**](https://opensource.apple.com/source/CF/CF-1153.18/CFRuntime.h.auto.html):
9+
All these classes are instances of the class `CFRuntimeClass`, which when called it returns an index to the `__CFRuntimeClassTable`. The CFRuntimeClass is defined in [**CFRuntime.h**](https://opensource.apple.com/source/CF/CF-1153.18/CFRuntime.h.auto.html):
1010

1111
```objectivec
1212
// Some comments were added to the original code
@@ -59,36 +59,47 @@ typedef struct __CFRuntimeClass {
5959

6060
### Memory sections used
6161

62-
Most of the data used by ObjectiveC runtime will change during the execution, therefore it uses some sections from the **\_\_DATA** segment in memory:
63-
64-
- **`__objc_msgrefs`** (`message_ref_t`): Message references
65-
- **`__objc_ivar`** (`ivar`): Instance variables
66-
- **`__objc_data`** (`...`): Mutable data
67-
- **`__objc_classrefs`** (`Class`): Class references
68-
- **`__objc_superrefs`** (`Class`): Superclass references
69-
- **`__objc_protorefs`** (`protocol_t *`): Protocol references
70-
- **`__objc_selrefs`** (`SEL`): Selector references
71-
- **`__objc_const`** (`...`): Class `r/o` data and other (hopefully) constant data
72-
- **`__objc_imageinfo`** (`version, flags`): Used during image load: Version currently `0`; Flags specify preoptimized GC support, etc.
73-
- **`__objc_protolist`** (`protocol_t *`): Protocol list
74-
- **`__objc_nlcatlist`** (`category_t`): Pointer to Non-Lazy Categories defined in this binary
75-
- **`__objc_catlist`** (`category_t`): Pointer to Categories defined in this binary
76-
- **`__objc_nlclslist`** (`classref_t`): Pointer to Non-Lazy Objective-C classes defined in this binary
77-
- **`__objc_classlist`** (`classref_t`): Pointers to all Objective-C classes defined in this binary
78-
79-
It also uses a few sections in the **`__TEXT`** segment to store constan values of it's not possible to write in this section:
80-
81-
- **`__objc_methname`** (C-String): Method names
82-
- **`__objc_classname`** (C-String): Class names
83-
- **`__objc_methtype`** (C-String): Method types
62+
Most of the data used by Objective‑C runtime will change during execution, therefore it uses a number of sections from the Mach‑O `__DATA` family of segments in memory. Historically these included:
63+
64+
- `__objc_msgrefs` (`message_ref_t`): Message references
65+
- `__objc_ivar` (`ivar`): Instance variables
66+
- `__objc_data` (`...`): Mutable data
67+
- `__objc_classrefs` (`Class`): Class references
68+
- `__objc_superrefs` (`Class`): Superclass references
69+
- `__objc_protorefs` (`protocol_t *`): Protocol references
70+
- `__objc_selrefs` (`SEL`): Selector references
71+
- `__objc_const` (`...`): Class r/o data and other (hopefully) constant data
72+
- `__objc_imageinfo` (`version, flags`): Used during image load: Version currently `0`; Flags specify preoptimized GC support, etc.
73+
- `__objc_protolist` (`protocol_t *`): Protocol list
74+
- `__objc_nlcatlist` (`category_t`): Pointer to Non-Lazy Categories defined in this binary
75+
- `__objc_catlist` (`category_t`): Pointer to Categories defined in this binary
76+
- `__objc_nlclslist` (`classref_t`): Pointer to Non-Lazy Objective‑C classes defined in this binary
77+
- `__objc_classlist` (`classref_t`): Pointers to all Objective‑C classes defined in this binary
78+
79+
It also uses a few sections in the `__TEXT` segment to store constants:
80+
81+
- `__objc_methname` (C‑String): Method names
82+
- `__objc_classname` (C‑String): Class names
83+
- `__objc_methtype` (C‑String): Method types
84+
85+
Modern macOS/iOS (especially on Apple Silicon) also place Objective‑C/Swift metadata in:
86+
87+
- `__DATA_CONST`: immutable Objective‑C metadata that can be shared read‑only across processes (for example many `__objc_*` lists now live here).
88+
- `__AUTH` / `__AUTH_CONST`: segments containing pointers that must be authenticated at load or use‑time on arm64e (Pointer Authentication). You will also see `__auth_got` in `__AUTH_CONST` instead of the legacy `__la_symbol_ptr`/`__got` only. When instrumenting or hooking, remember to account for both `__got` and `__auth_got` entries in modern binaries.
89+
90+
For background on dyld pre‑optimization (e.g., selector uniquing and class/protocol precomputation) and why many of these sections are "already fixed up" when coming from the shared cache, check the Apple `objc-opt` sources and dyld shared cache notes. This affects where and how you can patch metadata at runtime.
91+
92+
{{#ref}}
93+
../macos-files-folders-and-binaries/universal-binaries-and-mach-o-format.md
94+
{{#endref}}
8495

8596
### Type Encoding
8697

87-
Objective-c uses some mangling to encode selector and variable types of simple and complex types:
98+
Objective‑C uses mangling to encode selector and variable types of simple and complex types:
8899

89-
- Primitive types use their first letter of the type `i` for `int`, `c` for `char`, `l` for `long`... and uses the capital letter in case it's unsigned (`L` for `unsigned Long`).
90-
- Other data types whose letters are used or are special, use other letters or symbols like `q` for `long long`, `b` for `bitfields`, `B` for `booleans`, `#` for `classes`, `@` for `id`, `*` for `char pointers` , `^` for generic `pointers` and `?` for `undefined`.
91-
- Arrays, structures and unions use `[`, `{` and `(`
100+
- Primitive types use their first letter of the type `i` for `int`, `c` for `char`, `l` for `long`... and use the capital letter in case it's unsigned (`L` for `unsigned long`).
101+
- Other data types use other letters or symbols like `q` for `long long`, `b` for bitfields, `B` for booleans, `#` for classes, `@` for `id`, `*` for `char *`, `^` for generic pointers and `?` for undefined.
102+
- Arrays, structures and unions use `[`, `{` and `(` respectively.
92103

93104
#### Example Method Declaration
94105

@@ -111,18 +122,18 @@ The complete type encoding for the method is:
111122

112123
#### Detailed Breakdown
113124

114-
1. **Return Type (`NSString *`)**: Encoded as `@` with length 24
115-
2. **`self` (object instance)**: Encoded as `@`, at offset 0
116-
3. **`_cmd` (selector)**: Encoded as `:`, at offset 8
117-
4. **First argument (`char * input`)**: Encoded as `*`, at offset 16
118-
5. **Second argument (`NSDictionary * options`)**: Encoded as `@`, at offset 20
119-
6. **Third argument (`NSError ** error`)**: Encoded as `^@`, at offset 24
125+
1. Return Type (`NSString *`): Encoded as `@` with length 24
126+
2. `self` (object instance): Encoded as `@`, at offset 0
127+
3. `_cmd` (selector): Encoded as `:`, at offset 8
128+
4. First argument (`char * input`): Encoded as `*`, at offset 16
129+
5. Second argument (`NSDictionary * options`): Encoded as `@`, at offset 20
130+
6. Third argument (`NSError ** error`): Encoded as `^@`, at offset 24
120131

121-
**With the selector + the encoding you can reconstruct the method.**
132+
With the selector + the encoding you can reconstruct the method.
122133

123-
### **Classes**
134+
### Classes
124135

125-
Clases in Objective-C is a struct with properties, method pointers... It's possible to find the struct `objc_class` in the [**source code**](https://opensource.apple.com/source/objc4/objc4-756.2/runtime/objc-runtime-new.h.auto.html):
136+
Classes in Objective‑C are C structs with properties, method pointers, etc. It's possible to find the struct `objc_class` in the [**source code**](https://opensource.apple.com/source/objc4/objc4-756.2/runtime/objc-runtime-new.h.auto.html):
126137

127138
```objectivec
128139
struct objc_class : objc_object {
@@ -145,12 +156,126 @@ struct objc_class : objc_object {
145156
[...]
146157
```
147158
148-
This class use some bits of the isa field to indicate some information about the class.
159+
This class uses some bits of the `isa` field to indicate information about the class.
149160
150-
Then, the struct has a pointer to the struct `class_ro_t` stored on disk which contains attributes of the class like its name, base methods, properties and instance variables.\
151-
During runtime and additional structure `class_rw_t` is used containing pointers which can be altered such as methods, protocols, properties...
161+
Then, the struct has a pointer to the struct `class_ro_t` stored on disk which contains attributes of the class like its name, base methods, properties and instance variables. During runtime an additional structure `class_rw_t` is used containing pointers which can be altered such as methods, protocols, properties.
152162
153-
{{#include ../../../banners/hacktricks-training.md}}
163+
{{#ref}}
164+
../macos-basic-objective-c.md
165+
{{#endref}}
166+
167+
---
168+
169+
## Modern object representations in memory (arm64e, tagged pointers, Swift)
170+
171+
### Non‑pointer `isa` and Pointer Authentication (arm64e)
172+
173+
On Apple Silicon and recent runtimes the Objective‑C `isa` is not always a raw class pointer. On arm64e it is a packed structure that may also carry a Pointer Authentication Code (PAC). Depending on the platform it may include fields like `nonpointer`, `has_assoc`, `weakly_referenced`, `extra_rc`, and the class pointer itself (shifted or signed). This means blindly dereferencing the first 8 bytes of an Objective‑C object will not always yield a valid `Class` pointer.
174+
175+
Practical notes when debugging on arm64e:
176+
177+
- LLDB will usually strip PAC bits for you when printing Objective‑C objects with `po`, but when working with raw pointers you may need to strip authentication manually:
178+
179+
```lldb
180+
(lldb) expr -l objc++ -- #include <ptrauth.h>
181+
(lldb) expr -l objc++ -- void *raw = ptrauth_strip((void*)0x000000016f123abc, ptrauth_key_asda);
182+
(lldb) expr -l objc++ -O -- (Class)object_getClass((id)raw)
183+
```
184+
185+
- Many function/data pointers in Mach‑O will reside in `__AUTH`/`__AUTH_CONST` and require authentication before use. If you are interposing or re‑binding (e.g., fishhook‑style), ensure you also handle `__auth_got` in addition to legacy `__got`.
186+
187+
For a deep dive into language/ABI guarantees and the `<ptrauth.h>` intrinsics available from Clang/LLVM, see the reference in the end of this page.
188+
189+
### Tagged pointer objects
190+
191+
Some Foundation classes avoid heap allocation by encoding the object’s payload directly in the pointer value (tagged pointers). Detection differs by platform (e.g., the most‑significant bit on arm64, least‑significant on x86_64 macOS). Tagged objects don’t have a regular `isa` stored in memory; the runtime resolves the class from the tag bits. When inspecting arbitrary `id` values:
192+
193+
- Use runtime APIs instead of poking the `isa` field: `object_getClass(obj)` / `[obj class]`.
194+
- In LLDB, just `po (id)0xADDR` will print tagged pointer instances correctly because the runtime is consulted to resolve the class.
195+
196+
### Swift heap objects and metadata
197+
198+
Pure Swift classes are also objects with a header pointing to Swift metadata (not Objective‑C `isa`). To introspect live Swift processes without modifying them you can use the Swift toolchain’s `swift-inspect`, which leverages the Remote Mirror library to read runtime metadata:
199+
200+
```bash
201+
# Xcode toolchain (or Swift.org toolchain) provides swift-inspect
202+
swift-inspect dump-raw-metadata <pid-or-name>
203+
swift-inspect dump-arrays <pid-or-name>
204+
# On Darwin additionally:
205+
swift-inspect dump-concurrency <pid-or-name>
206+
```
207+
208+
This is very useful to map Swift heap objects and protocol conformances when reversing mixed Swift/ObjC apps.
209+
210+
---
154211

212+
## Runtime inspection cheatsheet (LLDB / Frida)
155213

214+
### LLDB
156215

216+
- Print object or class from a raw pointer:
217+
218+
```lldb
219+
(lldb) expr -l objc++ -O -- (id)0x0000000101234560
220+
(lldb) expr -l objc++ -O -- (Class)object_getClass((id)0x0000000101234560)
221+
```
222+
223+
- Inspect Objective‑C class from a pointer to an object method’s `self` in a breakpoint:
224+
225+
```lldb
226+
(lldb) br se -n '-[NSFileManager fileExistsAtPath:]'
227+
(lldb) r
228+
... breakpoint hit ...
229+
(lldb) po (id)$x0 # self
230+
(lldb) expr -l objc++ -O -- (Class)object_getClass((id)$x0)
231+
```
232+
233+
- Dump sections that carry Objective‑C metadata (note: many are now in `__DATA_CONST` / `__AUTH_CONST`):
234+
235+
```lldb
236+
(lldb) image dump section --section __DATA_CONST.__objc_classlist
237+
(lldb) image dump section --section __DATA_CONST.__objc_selrefs
238+
(lldb) image dump section --section __AUTH_CONST.__auth_got
239+
```
240+
241+
- Read memory for a known class object to pivot to `class_ro_t` / `class_rw_t` when reversing method lists:
242+
243+
```lldb
244+
(lldb) image lookup -r -n _OBJC_CLASS_$_NSFileManager
245+
(lldb) memory read -fx -s8 0xADDRESS_OF_CLASS_OBJECT
246+
```
247+
248+
### Frida (Objective‑C and Swift)
249+
250+
Frida provides high‑level runtime bridges that are very handy to discover and instrument live objects without symbols:
251+
252+
- Enumerate classes and methods, resolve actual class names at runtime, and intercept Objective‑C selectors:
253+
254+
```js
255+
if (ObjC.available) {
256+
// List a class' methods
257+
console.log(ObjC.classes.NSFileManager.$ownMethods);
258+
259+
// Intercept and inspect arguments/return values
260+
const impl = ObjC.classes.NSFileManager['- fileExistsAtPath:isDirectory:'].implementation;
261+
Interceptor.attach(impl, {
262+
onEnter(args) {
263+
this.path = new ObjC.Object(args[2]).toString();
264+
},
265+
onLeave(retval) {
266+
console.log('fileExistsAtPath:', this.path, '=>', retval);
267+
}
268+
});
269+
}
270+
```
271+
272+
- Swift bridge: enumerate Swift types and interact with Swift instances (requires recent Frida; very useful on Apple Silicon targets).
273+
274+
---
275+
276+
## References
277+
278+
- Clang/LLVM: Pointer Authentication and the `<ptrauth.h>` intrinsics (arm64e ABI). https://clang.llvm.org/docs/PointerAuthentication.html
279+
- Apple objc runtime headers (tagged pointers, non‑pointer `isa`, etc.) e.g., `objc-object.h`. https://opensource.apple.com/source/objc4/objc4-818.2/runtime/objc-object.h.auto.html
280+
281+
{{#include ../../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)