Two approaches use different methods:
A) fishhook works, as you said, on characters in the character table. This means that if a character is exported using dylib or a frame, and you can change the import table, you can redirect it to your implementation.
B) mach_override uses the Mach VM API for OS X (and iOS) to fix functions when they are already loaded, that is, already in memory. It does this by binary correction of the beginning of the function implementation, to go to another address (your implementation), and then bounce back.
Fishhook is more stable in the sense that it (i) simplifies implementation and (ii) is seamless as soon as the process loads dyld and the characters are connected. However, it will not work with characters that are not directly loaded by the executable. In other words, if you want to fix printf (3), for example, it will only work on printf calls from your executable file, and not on calls made from libSystem or other libraries. However, Mach_override is not so stable as it relies on certain prologue functions that can be overridden - approximately 90% of the time, but not 100%.
While validating the information on iOS pages is read-only, in some cases it may be possible to get around this (if you fix the executable, you can also fix LC_SEGMENT commands and sections), and you can use the Mach VM apis to unprotect from pages (only if the device is locked).
source share