A library that enables dynamically rebinding symbols in Mach-O binaries running on iOS.

Overview

fishhook

fishhook is a very simple library that enables dynamically rebinding symbols in Mach-O binaries running on iOS in the simulator and on device. This provides functionality that is similar to using DYLD_INTERPOSE on OS X. At Facebook, we've found it useful as a way to hook calls in libSystem for debugging/tracing purposes (for example, auditing for double-close issues with file descriptors).

Usage

Once you add fishhook.h/fishhook.c to your project, you can rebind symbols as follows:

#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);
 
    // Open our own binary and print out first 4 bytes (which is the same
    // for all Mach-O binaries on a given architecture)
    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]));
  }
}

Sample output

Calling real open('/var/mobile/Applications/161DA598-5B83-41F5-8A44-675491AF6A2C/Test.app/Test', 0)
Mach-O Magic Number: feedface 
Calling real close(3)
...

How it works

dyld binds lazy and non-lazy symbols by updating pointers in particular sections of the __DATA segment of a Mach-O binary. fishhook re-binds these symbols by determining the locations to update for each of the symbol names passed to rebind_symbols and then writing out the corresponding replacements.

For a given image, the __DATA segment may contain two sections that are relevant for dynamic symbol bindings: __nl_symbol_ptr and __la_symbol_ptr. __nl_symbol_ptr is an array of pointers to non-lazily bound data (these are bound at the time a library is loaded) and __la_symbol_ptr is an array of pointers to imported functions that is generally filled by a routine called dyld_stub_binder during the first call to that symbol (it's also possible to tell dyld to bind these at launch). In order to find the name of the symbol that corresponds to a particular location in one of these sections, we have to jump through several layers of indirection. For the two relevant sections, the section headers (struct sections from <mach-o/loader.h>) provide an offset (in the reserved1 field) into what is known as the indirect symbol table. The indirect symbol table, which is located in the __LINKEDIT segment of the binary, is just an array of indexes into the symbol table (also in __LINKEDIT) whose order is identical to that of the pointers in the non-lazy and lazy symbol sections. So, given struct section nl_symbol_ptr, the corresponding index in the symbol table of the first address in that section is indirect_symbol_table[nl_symbol_ptr->reserved1]. The symbol table itself is an array of struct nlists (see <mach-o/nlist.h>), and each nlist contains an index into the string table in __LINKEDIT which where the actual symbol names are stored. So, for each pointer __nl_symbol_ptr and __la_symbol_ptr, we are able to find the corresponding symbol and then the corresponding string to compare against the requested symbol names, and if there is a match, we replace the pointer in the section with the replacement.

The process of looking up the name of a given entry in the lazy or non-lazy pointer tables looks like this: Visual explanation

Comments
  • Update Fishhook Podspec to refer to the latest API-Changing Commit instead of Release 0.1.

    Update Fishhook Podspec to refer to the latest API-Changing Commit instead of Release 0.1.

    Related Issue : https://github.com/facebook/fishhook/issues/20

    When the pull request - https://github.com/facebook/fishhook/pull/18 was merged, it broke the API for anyone using fishhook with CocoaPods since the repo at HEAD has a different interface from the Pods Sources.

    In the Podspec, we are currently using the release version of fishhook. However, creating releases can be extremely tedious, especially with the case of a single maintainer and it would be much better if we could simply point to a specific commit.

    CLA Signed 
    opened by tirodkar 9
  • Crash when hook fonction `object_setClass`

    Crash when hook fonction `object_setClass`

    What crash

    When I hook fonction object_setClass, then call object_setClass, it works. but when I call [NSString stringWithFormat:@"%@", @""];, It crash.

    Demo code

    
    #import <UIKit/UIKit.h>
    #import "fishhook.h"
    #import <objc/runtime.h>
    
    Class _Nullable
    (*orig_object_setClass)(id _Nullable obj, Class _Nonnull cls);
    
    Class _Nullable
    my_object_setClass(id _Nullable obj, Class _Nonnull cls)
    {
        return orig_object_setClass(obj, cls);
    }
    
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            rebind_symbols((struct rebinding[]){
                {"object_setClass", my_object_setClass, (void *)&orig_object_setClass}
            }, 1);
            
            object_setClass(@"", NSObject.class);
            
            [NSString stringWithFormat:@"%@", @""];
            
            return 0;
        }
    }
    
    
    opened by 623637646 7
  • why can't hook socket function?

    why can't hook socket function?

    I hook the socket function in libsystem_network.dylib by fishhook, It's ok in simulator but not work in iphone. I debug the program, found the function address is socket function address, fishhook doesn't work 0x1903d88bc <+64>: bl 0x190364fec ; socket 0x1903d88c0 <+68>: mov x19, x0

    opened by youcanya 7
  • Thread 1: EXC_BAD_ACCESS

    Thread 1: EXC_BAD_ACCESS

    *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; FishHook will crash in this function cwsa_perform_rebinding_with_section. Do someone know solution of this issue, please tell me , thank you.

    opened by meiszhe 5
  • Two-level namespace

    Two-level namespace

    This change adds support for rebinding that leverages mach-o's two-level namespace.

    The tl;dr of two-level namespaces is that symbols imported from other libraries will reference which library they came from. When rebinding a symbol by name alone, it's possible to unintentionally rebind symbols that happen to have the same name, which can cause unexpected results. Using the two-level namespace prevents the mysterious bugs and crashes that can happen when rebinding by just symbol name alone.

    In addition to correctness, this change opens a new performance optimization, see 7cc05152e959e16f81ab61fba3b33c872fc687b8.

    This change is a breaking change: callers of rebind_symbols now need to fill in a dylib field on the rebinding struct. This field can be NULL to opt-out of two-level namespaces, and enable the previous global search.

    Resolves #32

    CLA Signed 
    opened by kastiglione 5
  • About the Symbol table ,string table address

    About the Symbol table ,string table address

    I do not understand why the linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;

    not

    linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr;

    opened by vedon 5
  • Added safety checks to fishhook.c

    Added safety checks to fishhook.c

    Fixes a crash that would occur on devices running iOS 13. fishhook.c would access bad memory, this adds safety checks to the offending line. This fixes #61

    CLA Signed 
    opened by chrisspankroy 4
  • Fix broken headings in Markdown files

    Fix broken headings in Markdown files

    GitHub changed the way Markdown headings are parsed, so this change fixes it.

    See bryant1410/readmesfix for more information.

    Tackles bryant1410/readmesfix#1

    CLA Signed 
    opened by bryant1410 4
  • Create a new release for the API change in Commit 8dbd09b and make the Podspec point to it.

    Create a new release for the API change in Commit 8dbd09b and make the Podspec point to it.

    The merging of PR #18 led to a break in the API. We have made the required changes, however the podspec still points to version 0.1. We use Fishhook rigorously for testing and this change breaks all our pods dependencies. Could we please create a new release for this change and have it added to the podspec (or just have the podspec pointing to head)?

    Also, it would be great if we regularly update the podspec with any new releases.

    opened by tirodkar 4
  • Add a Podspec to fishhook

    Add a Podspec to fishhook

    Adding a podspec to fishhook so that developers can use the library with CocoaPods instead of adding the source directly to their projects. CLA has been signed.

    @paolosoares @khandpur @sid-github

    CLA Signed 
    opened by tirodkar 4
  • How thread-safe is fishhook

    How thread-safe is fishhook

    I want to hook pthread_create in main, but I'm curious if there can be any tearing or otherwise bugs due to thread safety. I don't mind if it's undefined whether concurrent calls use the old or the new function, I just want to be sure that there won't be any peripheral bugs.

    opened by michaeleisel 3
  • Crashes with EXC_BAD_ACCESS on Apple Silicon mac when compiled as arm64e

    Crashes with EXC_BAD_ACCESS on Apple Silicon mac when compiled as arm64e

    Compiling an arm64e macOS app on an M1 Mac Mini in macOS 11.6. When a hooked function gets called, the app crashes with EXC_BAD_ACCESS. The following code when compiled as arm64e will crash on the second (hooked) call to malloc:

    #include <stdio.h>
    #include <stdlib.h>
    #include "fishhook.h"
    
    void * (*originalMalloc)(size_t);
    
    static void * overrideMalloc(size_t size) {
        void * result = originalMalloc(size);
        printf("calling overrideMalloc!\n");
        return result;
    }
    
    int main(int argc, const char * argv[]) {
        void *data;
        
        printf("Calling malloc before\n");
        data = malloc(10);
        free(data);
        
        int result = rebind_symbols((struct rebinding[2]){{"malloc", overrideMalloc, (void *)&originalMalloc}}, 1);
        
        if (result != 0) {
            printf("rebind_symbols failed with result: %d ... cannot proceed", result);
            return 0;
        }
        
        printf("Calling malloc after\n");
        data = malloc(10);
        free(data);
        
        return 0;
    }
    

    Note that in order to run arm64e code on macOS, you must disable system integrity protection and set the following boot parameter: sudo nvram boot-args=-arm64e_preview_abi

    opened by briankendall 1
  • Undefined symbol: _rebind_symbols in new commit 'aadc161ac3b80db07a9908851839a17ba63a9eb1'

    Undefined symbol: _rebind_symbols in new commit 'aadc161ac3b80db07a9908851839a17ba63a9eb1'

    I pod install fishhook from github, but an error was reported at building: Undefined symbols for architecture arm64: "_rebind_symbols", referenced from: _main in main.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

    opened by HawkEleven 1
  • iOS 15 crash

    iOS 15 crash

    There's Crash on iOS 15。iPhone7

    Exception Type: EXC_BAD_ACCESS (SIGKILL) Exception Subtype: KERN_PROTECTION_FAILURE at 0x00000001d4424da8 VM Region Info: 0x1d4424da8 is in 0x1d44249b8-0x1d4450278; bytes after start: 1008 bytes before end: 177359 REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL __DATA_CONST 1d43fc5c0-1d44249b8 [ 161K] r--/rw- SM=COW ...k/MediaRemote ---> __DATA_CONST 1d44249b8-1d4450278 [ 174K] r--/rw- SM=COW ...ork/CoreUtils __DATA_CONST 1d4450278-1d4453430 [ 12K] r--/rw- SM=COW .../FamilyCircle

    Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d Termination Description: SPRINGBOARD, <RBSTerminateContext| domain:10 code:0x8BADF00D explanation:scene-create watchdog transgression: application<ctrip.com>:444 exhausted real (wall clock) time allowance of 19.91 seconds | ProcessVisibility: Foreground | ProcessState: Running | WatchdogEvent: scene-create | WatchdogVisibility: Foreground | WatchdogCPUStatistics: ( | "Elapsed total CPU time (seconds): 19.580 (user 14.590, system 4.990), 16% CPU", | "Elapsed application CPU time (seconds): 0.034, 0% CPU" | ) reportType:CrashLog maxTerminationResistance:Interactive> Triggered by Thread: 0 perform_rebinding_with_section + 7177404 (fishhook.c:149) perform_rebinding_with_section + 7177312 (fishhook.c:143) rebind_symbols_for_image + 7176556 (fishhook.c:222)

    opened by d6638219 8
  • If hooking in __DATA_CONST/__AUTH_CONST, promise writable before trying to write

    If hooking in __DATA_CONST/__AUTH_CONST, promise writable before trying to write

    Issue: #80 and #82.

    fix:int mprotect(void *address, size_t size, int protect); function Require address alignment. Before that, I tried to use vm_protect function, I found some case that vm_protect return KERN_SUCCESS , but memory don't set VM_PROT_WRITE success.

    CLA Signed 
    opened by maniackk 5
  • Fix iOS 14.5 crash

    Fix iOS 14.5 crash

    The mprotect function requires the address to be page size aligned. In iOS 14.5 Beta, some addresses of sections of __DATA_CONST and __AUTH_CONST segments are not aligned to the page size, the write protection will be added failed, and then the process may crash when writing to these addresses. So we can use vm_protect which not require the address to be page size aligned, instead of mprotect.

    Besides, the protection of different pages in a section may be not the same. So we need to pass the address to be written into get_protection, not the first address of the section.

    Fixed #80 and #82.

    CLA Signed 
    opened by shino-996 5
Owner
Meta
We are working to build community through open source technology. NB: members must have two-factor auth.
Meta
iOS library to help detecting retain cycles in runtime.

FBRetainCycleDetector An iOS library that finds retain cycles using runtime analysis. About Retain cycles are one of the most common ways of creating

Facebook 4.1k Dec 26, 2022
SwiftCop is a validation library fully written in Swift and inspired by the clarity of Ruby On Rails Active Record validations.

SwiftCop is a validation library fully written in Swift and inspired by the clarity of Ruby On Rails Active Record validations. Objective Build a stan

Andres Canal 542 Sep 17, 2022
Trackable is a simple analytics integration helper library. It’s especially designed for easy and comfortable integration with existing projects.

Trackable Trackable is a simple analytics integration helper library. It’s especially designed for easy and comfortable integration with existing proj

Vojta Stavik 145 Apr 14, 2022
Aardvark is a library that makes it dead simple to create actionable bug reports.

Aardvark Aardvark makes it dead simple to create actionable bug reports. Aardvark is made up of a collection of frameworks that provide different bug

Square 257 Dec 19, 2022
A dynamic library for Cornerstone macOS.

Cornerstone-Hook A dynamic library for Cornerstone macOS. Screenshot Overview Quick Start sudo ./install.sh Install or Upgrade CornerstoneStart.app su

ivar 3 Oct 8, 2022
iOS tool that helps with profiling iOS Memory usage.

FBMemoryProfiler An iOS library providing developer tools for browsing objects in memory over time, using FBAllocationTracker and FBRetainCycleDetecto

Facebook Archive 3.4k Dec 7, 2022
iOS project bootstrap aimed at high quality coding.

iOS Project bootstrap How do you setup your iOS projects? Since we are approaching 2015 I’m working on refreshing my project bootstrap. I’ve decided t

Krzysztof Zabłocki 2k Dec 23, 2022
Find memory leaks in your iOS app at develop time.

中文介绍 | FAQ中文 MLeaksFinder MLeaksFinder helps you find memory leaks in your iOS apps at develop time. It can automatically find leaks in UIView and UIV

Tencent 5.3k Dec 22, 2022
Find memory issues & leaks in your iOS app without instruments

HeapInspector Find memory issues & leaks in your iOS app HeapInspector is a debug tool that monitors the memory heap with backtrace recording in your

Christian Menschel 1.8k Nov 24, 2022
Awesome bug reporting for iOS apps

Buglife is an awesome bug reporting SDK & web platform for iOS apps. Here's how it works: User takes a screenshot, or stops screen recording User anno

Buglife 498 Dec 17, 2022
In-app memory usage monitoring for iOS

What's Stats Stats displays load statuses such as the memory usage, the CPU load, and the number of subviews in-app, and in realtime. How to use Just

Shuichi Tsutsumi 170 Sep 18, 2022
Makes it easier to support older versions of iOS by fixing things and adding missing methods

PSTModernizer PSTModernizer carefully applies patches to UIKit and related Apple frameworks to fix known radars with the least impact. The current set

PSPDFKit Labs 217 Aug 9, 2022
decoupling between modules in your iOS Project. iOS模块化过程中模块间解耦方案

DecouplingKit 中文readme Podfile platform :ios, '7.0' pod 'DecouplingKit', '~> 0.0.2' DecouplingKit, decoupling between modules in your iOS Project. D

coderyi 139 Aug 23, 2022
Flexible bug report framework for iOS

Clue is a simple smart-bug report framework for iOS, which allows your users to record full bug/crash report and send it to you as a single .clue file

Ahmed Sulaiman 279 Nov 3, 2022
The project used in the iOS Architect Crash Course lectures

iOS Architect Crash Course • August 2nd-8th • EssentialDeveloper.com https://www.essentialdeveloper.com/ios-architect-crash-course/aug-2021-a5220 It's

Aleksei Korolev 1 Jul 20, 2022
Skredvarsel app - an iOS, iPadOS, and macOS application that provides daily avalanche warnings from the Norwegian Avalanche Warning Service API

Skredvarsel (Avalanche warning) app is an iOS, iPadOS, and macOS application that provides daily avalanche warnings from the Norwegian Avalanche Warning Service API

Jonas Follesø 8 Dec 15, 2022
Simple iOS app blackbox assessment tool. Powered by frida.re and vuejs.

Discontinued Project This project has been discontinued. Please use the new Grapefruit #74 frida@14 compatibility issues frida@14 introduces lots of b

Chaitin Tech 1.6k Dec 16, 2022
Abusing dlopen to load & run mach binaries on iOS

MobileTerminal Proof-of-concept for sandboxed Terminal environment for iOS. Uses dlopen() to load mach binaries as libraries, then finds & calls main(

Steven Troughton-Smith 184 Dec 18, 2022
A graphical Mach-O viewer for macOS. Powered by Mach-O Kit.

Mach-O Explorer is a graphical Mach-O viewer for macOS. It aims to provide an interface and feature set that are similar to the venerable MachOView ap

Devin 581 Dec 31, 2022
A view that shows selectable symbols, similar to UITableView's `sectionIndexTitles` API but with support for symbols and more flexibility

?? TableOfContentsSelector Are you familiar with UITableView's sectionIndexTitles API? The little alphabet on the side of some tables for quickly jump

Christian Selig 106 Dec 19, 2022