208 lines
5.2 KiB
Markdown
Executable File
208 lines
5.2 KiB
Markdown
Executable File
```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 Xcode’s 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++ ABI’s `__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`) don’t 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
|
||
|
||
- **App’s 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. |