Contact

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


Fishhook

Fishhook is a Facebook library for rebinding function pointers in the Global Offset Table (GOT) at runtime.

How it works

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

Cons


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.

Relevant sections


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

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.