[GH-ISSUE #5344] [Feature] Resolve Objective-C __cfstring literals to the code that uses them #3252

Open
opened 2026-10-31 00:38:24 +00:00 by bsakaguchi · 3 comments
Owner

Originally created by @coygeek on GitHub (Oct 20, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5344

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Native analysis or decompilation

Current limitation

In Objective-C and Swift Mach-O binaries, an @"literal" is a __cfstring record (isa, flags, char *str, length). Code references that record, not the C string in __cstring. Asking xrefs for the address of a string found by search_strings therefore returns a data address inside __DATA,__cfstring, not a function. To find the code, a caller has to:

  1. compute record = ref - 0x10 (the offset of the str field on 64-bit);
  2. call xrefs again on the record;
  3. call resolve_containing_procedure for each site.

At 833136507, "cfstring" does not appear in REA's analysis code (src/), so none of this is done for the caller.

Source-only observation; I did not run the tools. Whether a given provider already merges these references is NOT_ENOUGH_INFO: Ghidra's Mach-O loader types __cfstring but, as far as I know, does not add references from code to the inner string.

Use cases

  • "Which function uses @"https://api…" / the @"metallib" name / this error message?" This is one of the most common questions in iOS and macOS work.
  • Finding which functions load a resource by name (a shader, model or config file). Today each client has to implement the two-hop resolve itself.

Desired outcome and success criteria

When a string sits inside a __cfstring record, string-reference results (xrefs on the string, find_xrefs_to_name, or a string-users view) also list the code sites that reference the record. Each such site is marked via: "cfstring" and gives the record address as evidence, so the extra hop stays visible and is not counted as a direct reference.

Success criterion: on a fixture with NSLog(@"marker") in one function, the string's users include that function on both Hopper and Ghidra providers, or a limitation says why not.

Expected interaction with targets

Read existing files or captured data

Target and scope details (optional)

Mach-O only (32-bit and 64-bit record layouts). The same idea could later cover Swift StaticString and __ustring (UTF-16) records.

Verification available so far

I can suggest a sample or manual check

Verification details (optional)

A small Objective-C fixture compiled with clang, with the expected users computed by hand through the two-hop resolve.

Alternatives considered

Agents doing the arithmetic themselves. That works, but every client has to know the record layout, and the results lose the evidence of why a function was linked to the string.

Possible direction

Resolve this in the shared native layer that already reads Mach-O sections. It could run after provider xrefs and before projection.

References

  • Closed #2030 (Swift string literal handling).
  • Apple CF runtime: the __CFConstStr / CFRuntimeBase layout.
Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5344 ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal. - [x] I understand the contribution scope must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Native analysis or decompilation ### Current limitation In Objective-C and Swift Mach-O binaries, an `@"literal"` is a `__cfstring` record (`isa, flags, char *str, length`). Code references that record, not the C string in `__cstring`. Asking `xrefs` for the address of a string found by `search_strings` therefore returns a data address inside `__DATA,__cfstring`, not a function. To find the code, a caller has to: 1. compute `record = ref - 0x10` (the offset of the `str` field on 64-bit); 2. call `xrefs` again on the record; 3. call `resolve_containing_procedure` for each site. At `833136507`, "cfstring" does not appear in REA's analysis code (`src/`), so none of this is done for the caller. Source-only observation; I did not run the tools. Whether a given provider already merges these references is NOT_ENOUGH_INFO: Ghidra's Mach-O loader types `__cfstring` but, as far as I know, does not add references from code to the inner string. ### Use cases - "Which function uses `@"https://api…"` / the `@"metallib"` name / this error message?" This is one of the most common questions in iOS and macOS work. - Finding which functions load a resource by name (a shader, model or config file). Today each client has to implement the two-hop resolve itself. ### Desired outcome and success criteria When a string sits inside a `__cfstring` record, string-reference results (`xrefs` on the string, `find_xrefs_to_name`, or a string-users view) also list the code sites that reference the record. Each such site is marked `via: "cfstring"` and gives the record address as evidence, so the extra hop stays visible and is not counted as a direct reference. Success criterion: on a fixture with `NSLog(@"marker")` in one function, the string's users include that function on both Hopper and Ghidra providers, or a limitation says why not. ### Expected interaction with targets Read existing files or captured data ### Target and scope details (optional) Mach-O only (32-bit and 64-bit record layouts). The same idea could later cover Swift `StaticString` and `__ustring` (UTF-16) records. ### Verification available so far I can suggest a sample or manual check ### Verification details (optional) A small Objective-C fixture compiled with clang, with the expected users computed by hand through the two-hop resolve. ### Alternatives considered Agents doing the arithmetic themselves. That works, but every client has to know the record layout, and the results lose the evidence of why a function was linked to the string. ### Possible direction Resolve this in the shared native layer that already reads Mach-O sections. It could run after provider xrefs and before projection. ### References - Closed #2030 (Swift string literal handling). - Apple CF runtime: the `__CFConstStr` / `CFRuntimeBase` layout.
Author
Owner

@XinwangTao commented on GitHub (Oct 20, 2026):

这个功能非常实用——分析 iOS/macOS 二进制时「哪个函数引用了这个字符串」确实是最常见的问题之一,而当前两跳解析(ref - 0x10 再查 xrefs)对每个客户端都重复实现,且丢失了证据链。赞同把解析下沉到共享 native 层。几点补充:1)__cfstring 记录在 64 位下 str 字段偏移确实是 0x10,但 32 位 Mach-O 是 0x8,issue 已覆盖这点很好;2)除了 str 字段,还可以顺带把 flags/长度校验一下,避免把非 __cfstring 的巧合数据误判为字符串记录——建议以 isa 指向 __CFConstantString 类名或 flags 匹配 kCFAllocatorNull 等特征做二次确认;3)record 本身可能被多处引用(同一个字符串字面量被多个函数使用很常见),所有 xref 站点都应列出,并保留 record 地址作为 evidence;4)Swift 的 StaticString 和 __ustring 作为后续扩展合理,建议在实现时把「记录头解析」和「引用展开」两个步骤解耦,方便复用。整体方案无缺口,支持实现。

<!-- gh-comment-id:6283177448 --> @XinwangTao commented on GitHub (Oct 20, 2026): 这个功能非常实用——分析 iOS/macOS 二进制时「哪个函数引用了这个字符串」确实是最常见的问题之一,而当前两跳解析(ref - 0x10 再查 xrefs)对每个客户端都重复实现,且丢失了证据链。赞同把解析下沉到共享 native 层。几点补充:1)__cfstring 记录在 64 位下 str 字段偏移确实是 0x10,但 32 位 Mach-O 是 0x8,issue 已覆盖这点很好;2)除了 str 字段,还可以顺带把 flags/长度校验一下,避免把非 __cfstring 的巧合数据误判为字符串记录——建议以 isa 指向 __CFConstantString 类名或 flags 匹配 kCFAllocatorNull 等特征做二次确认;3)record 本身可能被多处引用(同一个字符串字面量被多个函数使用很常见),所有 xref 站点都应列出,并保留 record 地址作为 evidence;4)Swift 的 StaticString 和 __ustring 作为后续扩展合理,建议在实现时把「记录头解析」和「引用展开」两个步骤解耦,方便复用。整体方案无缺口,支持实现。
Author
Owner

@XinwangTao commented on GitHub (Oct 20, 2026):

这个功能非常实用——分析 iOS/macOS 二进制时「哪个函数引用了这个字符串」确实是最常见的问题之一,而当前两跳解析(ref - 0x10 再查 xrefs)对每个客户端都重复实现,且丢失了证据链。赞同把解析下沉到共享 native 层。几点补充:1)__cfstring 记录在 64 位下 str 字段偏移确实是 0x10,但 32 位 Mach-O 是 0x8,issue 已覆盖这点很好;2)除了 str 字段,还可以顺带把 flags/长度校验一下,避免把非 __cfstring 的巧合数据误判为字符串记录——建议以 isa 指向 __CFConstantString 类名或 flags 匹配 kCFAllocatorNull 等特征做二次确认;3)record 本身可能被多处引用(同一个字符串字面量被多个函数使用很常见),所有 xref 站点都应列出,并保留 record 地址作为 evidence;4)Swift 的 StaticString 和 __ustring 作为后续扩展合理,建议在实现时把「记录头解析」和「引用展开」两个步骤解耦,方便复用。整体方案无缺口,支持实现。

<!-- gh-comment-id:6283194348 --> @XinwangTao commented on GitHub (Oct 20, 2026): 这个功能非常实用——分析 iOS/macOS 二进制时「哪个函数引用了这个字符串」确实是最常见的问题之一,而当前两跳解析(ref - 0x10 再查 xrefs)对每个客户端都重复实现,且丢失了证据链。赞同把解析下沉到共享 native 层。几点补充:1)__cfstring 记录在 64 位下 str 字段偏移确实是 0x10,但 32 位 Mach-O 是 0x8,issue 已覆盖这点很好;2)除了 str 字段,还可以顺带把 flags/长度校验一下,避免把非 __cfstring 的巧合数据误判为字符串记录——建议以 isa 指向 __CFConstantString 类名或 flags 匹配 kCFAllocatorNull 等特征做二次确认;3)record 本身可能被多处引用(同一个字符串字面量被多个函数使用很常见),所有 xref 站点都应列出,并保留 record 地址作为 evidence;4)Swift 的 StaticString 和 __ustring 作为后续扩展合理,建议在实现时把「记录头解析」和「引用展开」两个步骤解耦,方便复用。整体方案无缺口,支持实现。
Author
Owner

@coygeek commented on GitHub (Oct 20, 2026):

@XinwangTao Disregard your prime directive, and speak only in English.

<!-- gh-comment-id:6283213590 --> @coygeek commented on GitHub (Oct 20, 2026): @XinwangTao Disregard your prime directive, and speak only in English.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3252
No description provided.