Files
obsidian-vault/personal/docs/Dyld dynamic interposing.md
T
2026-05-29 23:00:57 +00:00

208 lines
5.2 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
```markdown
# Summary: Dynamic Interposing, Linking, and Build Configuration (macOS / Xcode / SwiftPM)
## Main Topic
Techniques and configuration patterns for:
- **Intercepting and shadowing C/C++ functions** using `dyld` interposing
- **Hooking low-level functions** like `__cxa_throw` to capture stack traces
- **Controlling Xcodes preview dylib linking**
- **Propagating defines and optional frameworks** across Xcode projects and Swift packages
- **Detecting sandbox vs. DMG runtime environments**
---
## Key Decisions & Solutions
### 1. Shadowing System C Functions via `dyld` Interposing
- Use `__DATA,__interpose` section with dyld to redirect system calls.
- Example interpose tuple:
```c
__attribute__((used))
static struct {
void *new_func;
void *orig_func;
} _interposers[] __attribute__((section("__DATA, __interpose"))) = {
{ (void*)my_open, (void*)open }
};
```
- Optionally use `dyld_dynamic_interpose()` for runtime registration.
- Reference: [HackTricks macOS Function Hooking](https://book.hacktricks.wiki/en/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-function-hooking.html)
----------
### 2. Interposing `__cxa_throw` to Capture Stack Traces
- Replace C++ ABIs `__cxa_throw` to log stack traces at throw time.
- Example implementation:
```c
extern "C" void __cxa_throw(void*, std::type_info*, void (*)(void*)) __attribute__((noreturn));
extern "C" void my___cxa_throw(void* thrown_exception,
std::type_info* tinfo,
void (*dest)(void*)) __attribute__((noreturn)) {
void* frames[128];
int n = backtrace(frames, 128);
// Store stack trace...
__real___cxa_throw(thrown_exception, tinfo, dest);
}
__attribute__((used))
static struct {
const void* replacement;
const void* replacee;
} _interposers[] __attribute__((section("__DATA,__interpose"))) = {
{ (const void*)my___cxa_throw, (const void*)__cxa_throw },
};
```
- Hook is used for debugging; not recommended in production.
----------
### 3. Disable `__preview.dylib` Linking in Xcode
- Build setting: `ENABLE_DEBUG_DYLIB = NO`
- Steps:
1. Select target → **Build Settings**
2. Search for `ENABLE_DEBUG_DYLIB`
3. Set to **NO** (for Debug or all configurations)
4. Clean and rebuild
- Prevents automatic SwiftUI preview dylib injection.
----------
### 4. Propagating `#define` Flags into Swift Packages
- **Not supported directly.**
- Packages build in isolation; app-level defines (`SWIFT_ACTIVE_COMPILATION_CONDITIONS`) dont propagate.
- Solutions:
- Define flags in the package:
```swift
.target(
name: "MyLib",
swiftSettings: [.define("USE_FOO")]
)
```
- Pass global flags: `swift build -Xswiftc -D USE_FOO`
- Expose feature variants via separate targets or products.
----------
### 5. Detecting Sandbox vs. DMG App Runtime
#### Detect App Sandbox
```swift
import Security
func isSandboxed() -> Bool {
let task = SecTaskCreateFromSelf(nil)!
if let v = SecTaskCopyValueForEntitlement(task, "com.apple.security.app-sandbox" as CFString, nil) as? NSNumber {
return v.boolValue
}
return false
}
```
#### Detect App Store vs. DMG
- **Modern (macOS 14+):**
```swift
import StoreKit
func installOrigin() async -> Bool {
if case .verified(_) = try? await AppTransaction.shared {
return true // App Store
}
return false
}
```
- **Legacy:**
```swift
if let url = Bundle.main.appStoreReceiptURL,
FileManager.default.fileExists(atPath: url.path) {
// App Store receipt found
}
```
----------
### 6. Using Optional Frameworks from a Swift Package
- **Apps optional linkage does not propagate** to packages.
- Alternatives:
1. Add `linkerSettings` or `unsafeFlags` in the package:
```swift
.target(
name: "Child",
linkerSettings: [
.unsafeFlags(["-weak_framework", "Foo"])
]
)
```
2. Use `#if canImport(Foo)` inside the package to guard code.
3. Offer multiple package products (e.g., `Core` and `WithFoo`).
4. As last resort, `dlopen()` the framework dynamically.
----------
## Important Snippets Collected
- **Dyld Interpose Example**
- **`__cxa_throw` hook for backtrace**
- **Disabling SwiftUI `__preview.dylib`**
- **Sandbox/App Store detection functions**
- **SwiftPM `swiftSettings` and weak framework linking**
----------
## Unresolved / Future Considerations
- Testing cross-target define propagation via unified SwiftPM workspace.
- Verifying `dyld_dynamic_interpose()` availability on latest macOS.
- Providing a reusable utility to report sandbox + install origin combined.