Objective-C runtime techniques: swizzling, fishhook, and dyld interposing
Apple platforms provide several runtime techniques for intercepting or replacing function behavior. This article compares dyld interposing, Objective-C method swizzling, and fishhook by layer, scope, and deployment constraints.
Technique comparison
| Technique | Layer | Scope | Use case |
|---|---|---|---|
| dyld interposing | Linker/loader | Entire binary or dylib | Replace C functions globally |
| Method swizzling | ObjC runtime | Per class or method | Swap Objective-C method implementations |
| Fishhook | Runtime GOT patching | C functions via GOT | Late-binding symbol override |
dyld interposing
dyld interposing specifies function overrides at binary load time.
Example: interposing malloc
void *my_malloc(size_t size) {
printf("Intercepted malloc: %zu\n", size);
static void *(*orig_malloc)(size_t) = NULL;
if (!orig_malloc) {
orig_malloc = dlsym(RTLD_NEXT, "malloc");
}
return orig_malloc(size);
}
__attribute__((used))
static struct {
const void *replacement;
const void *original;
} interposers[] __attribute__((section("__DATA,__interpose"))) = {
{ (const void *)my_malloc, (const void *)malloc }
};
Key points
- The interposed function replaces all GOT entries for the original.
- Use
dlsym(RTLD_NEXT, ...)when the replacement must call the original implementation. - Be careful to avoid infinite recursion by accidentally calling your own interposed function.
Fishhook
Fishhook is a Facebook library for rebinding function pointers in the Global Offset Table (GOT) at runtime.
How it works
- Scans
__la_symbol_ptrand__nl_symbol_ptrsections. - Locates and rewrites symbol pointers (GOT entries).
- Can be used after app launch (unlike
dyldinterposing).
Example: hooking open
#include "fishhook.h"
int (*orig_open)(const char *path, int oflag);
int my_open(const char *path, int oflag) {
printf("Intercepted open: %s\n", path);
return orig_open(path, oflag);
}
void hook() {
rebind_symbols((struct rebinding[1]){{"open", my_open, (void *)&orig_open}}, 1);
}
Objective-C method swizzling
Swizzling swaps method implementations dynamically using the Objective-C runtime.
Example
Method original = class_getInstanceMethod([MyClass class], @selector(viewDidLoad));
Method swizzled = class_getInstanceMethod([MyClass class], @selector(my_viewDidLoad));
method_exchangeImplementations(original, swizzled);
Pros
- Works at runtime
- Compatible with App Store apps when used for your own classes and stable APIs
- Useful for analytics, aspect-oriented behavior, and debugging
Cons
- Fragile when method signatures do not match
- Risky for UIKit internals and private APIs
iOS compatibility matrix
| Technique | App Store Apps | Jailbroken Devices | Custom Frameworks |
|---|---|---|---|
| dyld interposing | Not allowed | Yes | Not reliable |
| Fishhook | Risky (undocumented) | Yes | Yes |
| Swizzling | Allowed | Yes | Allowed |
What is the GOT?
The Global Offset Table (GOT) stores addresses of dynamically linked functions. Calls to external functions like malloc() or open() go through this table.
dyldresolves these during load (interposing works here).- Fishhook rewrites them at runtime.
Relevant sections
__la_symbol_ptr- Lazy bindings__nl_symbol_ptr- Non-lazy bindings
Inspecting GOT
With otool
otool -Iv MyAppBinary | grep __la_symbol_ptr
With Radare2
r2 MyAppBinary
> iS~la_symbol_ptr
> is~malloc
Summary: should you call the original?
| Behavior | Is it Required? | How? |
|---|---|---|
| Replace function globally | Yes | Use interposing or fishhook |
| Call original function | Optional | Use dlsym(RTLD_NEXT, "symbol") |
| Block or modify behavior | Your choice | Skip calling the original implementation |
Recommendations
- Use swizzling for Objective-C methods in code you control.
- Use fishhook when you need runtime interception of C functions.
- Use dyld interposing in macOS tools or jailbreak contexts only.
Each technique changes runtime behavior in a different layer of the process. Choose the narrowest mechanism that fits the target symbol and deployment environment, and keep a path to the original implementation when the replacement is observational rather than substitutive.