Understanding Hook Techniques and Fishhook Implementation on iOS
This article explains the fundamentals of hooking on iOS, covering Method Swizzle and Facebook's fishhook, detailing the Mach‑O file structure, PIC technique, and providing complete source code examples that demonstrate how to replace system functions such as NSLog at runtime.
In the context of iOS reverse engineering, Hook technology allows developers to alter a program's execution flow, enabling custom code to run inside another app; mastering Hook concepts is essential before measuring app startup performance.
Two primary Hook schemes are discussed: Method Swizzle , which leverages Objective‑C runtime to swap SEL and IMP mappings, and fishhook , a Facebook‑provided library that can intercept static C functions by modifying Mach‑O symbol tables.
fishhook works by exploiting the Mach‑O loader’s lazy and non‑lazy symbol pointer tables. When a Mach‑O binary is loaded, dyld creates placeholder pointers in the __DATA segment; fishhook rewrites these pointers to point to user‑defined implementations, effectively hooking C functions despite their static nature.
Below is the core fishhook source code that performs the rebinding of symbols. The code is reproduced verbatim and wrapped in a tag as required:
#import <dlfcn.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "fishhook.h"
static int (*orig_close)(int);
static int (*orig_open)(const char *, int, ...);
int my_close(int fd) {
printf("Calling real close(%d)\n", fd);
return orig_close(fd);
}
int my_open(const char *path, int oflag, ...) {
va_list ap = {0};
mode_t mode = 0;
if ((oflag & O_CREAT) != 0) {
// mode only applies to O_CREAT
va_start(ap, oflag);
mode = va_arg(ap, int);
va_end(ap);
printf("Calling real open('%s', %d, %d)\n", path, oflag, mode);
return orig_open(path, oflag, mode);
} else {
printf("Calling real open('%s', %d)\n", path, oflag);
return orig_open(path, oflag, mode);
}
}
int main(int argc, char *argv[]) {
@autoreleasepool {
rebind_symbols((struct rebinding[2]){{"close", my_close, (void *)&orig_close}, {"open", my_open, (void *)&orig_open}}, 2);
int fd = open(argv[0], O_RDONLY);
uint32_t magic_number = 0;
read(fd, &magic_number, 4);
printf("Mach-O Magic Number: %x \n", magic_number);
close(fd);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}The library defines a linked list of rebinding structures, registers a callback with dyld, and walks through the Mach‑O load commands to locate the __LINKEDIT segment, symbol table, string table, and indirect symbol table. Functions such as prepend_rebindings , perform_rebinding_with_section , and rebind_symbols_for_image manipulate the pointer arrays ( __la_symbol_ptr and __nl_symbol_ptr ) to replace original implementations.
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
struct rebinding rebindings[],
size_t nel) {
struct rebindings_entry *new_entry = (struct rebindings_entry *)malloc(sizeof(struct rebindings_entry));
if (!new_entry) {
return -1;
}
new_entry->rebindings = (struct rebinding *)malloc(sizeof(struct rebinding) * nel);
if (!new_entry->rebindings) {
free(new_entry);
return -1;
}
memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel);
new_entry->rebindings_nel = nel;
new_entry->next = *rebindings_head;
*rebindings_head = new_entry;
return 0;
}
static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
section_t *section,
intptr_t slide,
nlist_t *symtab,
char *strtab,
uint32_t *indirect_symtab) {
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
vm_prot_t oldProtection = VM_PROT_READ;
if (isDataConst) {
oldProtection = get_protection(rebindings);
mprotect(indirect_symbol_bindings, section->size, PROT_READ | PROT_WRITE);
}
for (uint i = 0; i < section->size / sizeof(void *); i++) {
uint32_t symtab_index = indirect_symbol_indices[i];
if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
continue;
}
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
char *symbol_name = strtab + strtab_offset;
bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1];
struct rebindings_entry *cur = rebindings;
while (cur) {
for (uint j = 0; j < cur->rebindings_nel; j++) {
if (symbol_name_longer_than_1 &&
strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {
if (cur->rebindings[j].replaced != NULL &&
indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {
*(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];
}
indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
goto symbol_loop;
}
}
cur = cur->next;
}
symbol_loop:;
}
}To illustrate practical usage, the article provides a concrete example that hooks the NSLog function. By defining a rebinding for NSLog , storing the original pointer, and supplying a custom implementation that appends a suffix, the author shows how the log output changes at runtime.
// rebinding structure definition
struct rebinding {
const char *name; // function name to hook
void *replacement; // pointer to new implementation
void **replaced; // pointer that will receive original function address
};
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"123");
struct rebinding nslog;
nslog.name = "NSLog";
nslog.replacement = myNslog;
nslog.replaced = (void *)&sys_nslog;
struct rebinding rebs[1] = {nslog};
rebind_symbols(rebs, 1);
}
static void (*sys_nslog)(NSString *format, ...);
void myNslog(NSString *format, ...) {
format = [format stringByAppendingString:@"勾上了!\n"];
sys_nslog(format);
}Running the app shows that the original NSLog output is replaced by the custom message, confirming that fishhook successfully intercepts C‑level functions in an iOS binary. The author also demonstrates how to locate the function’s offset in the Mach‑O file using tools such as MachOView and how the lazy symbol pointer table maps to the actual implementation address.
In summary, the article provides a thorough walkthrough of Hook techniques, explains the underlying Mach‑O and PIC mechanisms that enable C‑function hooking, and supplies complete, ready‑to‑use source code for developers interested in iOS runtime manipulation and performance monitoring.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.