plthook issues on MacOS
I've come across multiple issues with hooking imported functions on MacOS, resulting hook either not working at all or hooking wrong function (after "fixing" issue with hook not working at all).
So here is what I found out after many many hours of debugging:
- in read_chained_fixups function value of
const uint8_t *ptris calculated incorrectly, making that pointer off bylinkedit->vmaddr - linkedit->fileoffbytes, which results in rest of the things to be read incorrectly and ends up with "unknown imports format 0" error message in my case. I've changed code a bit and it fixed that error for me:
//const uint8_t *ptr = (const uint8_t *)mh + d->chained_fixups->dataoff;
struct segment_command_64* linkedit = d->segments[d->linkedit_segment_idx];
const uint8_t* ptr = (uint8_t*)(linkedit->vmaddr - linkedit->fileoff + d->slide + d->chained_fixups->dataoff);
- after "fixing" above error another one surfaced which results in plthook hooking incorrect functions, which results in app crashing when that hooked function is called, more specifically it is hooking functions that are located at address that is 0x30 bytes lower than intended (in my case), for example if target function's __got entry is located at 0x4BF2C8, plthook hooks function with __got entry at 0x4BF298 instead... At this point calculated addr already has incorrect value:
void **addr = (void**)(d->got_addr + i * sizeof(void*));
DEBUG_FIXUPS(" lib_ordinal %u, weak_import %u, name_offset %u (%s), addr %p, addend %llu\n",
imp.lib_ordinal, imp.weak_import, imp.name_offset, name, addr, imp.addend);
d->plthook->entries[i].name = name;
d->plthook->entries[i].addr = addr;
lib_base 0x10d9a7000
lib_ordinal 1, weak_import 0, name_offset 1387 (_dlopen), addr 0x10de66298, addend 0
dlopen=0x10de66298-0x10d9a7000=0x4BF298 while real dlopen is at 0x4BF2C8 if you look at IDA
Rough code I'm using:
void* dlopen_hook(const char* filename, int flags)
{
// stuff
}
void* dlsym_hook(void* handle, const char* symbol)
{
// stuff
}
bool hook_stuff()
{
plthook_t* plthook;
if (plthook_open(&plthook, "libname.dylib") != 0) {
return false;
}
if (plthook_replace(plthook, "dlopen", (void*)dlopen_hook, nullptr) != 0) {
plthook_close(plthook);
return false;
}
if (plthook_replace(plthook, "dlsym", (void*)dlsym_hook, nullptr) != 0) {
plthook_close(plthook);
return false;
}
}
I think I found why this offset of 0x30 error happens, but I don't know how to fix it...
There's some __got entries in the library i'm hooking that are followed by 8+8+8 (0x18) bytes more data that should be skipped, but your code doesn't skip it and that causes rest of the functions to have invalid addresses that is shifted by 8 bytes per each entry that should be skipped
void **addr = (void**)(d->got_addr + i * sizeof(void*));
Here you can see __Unwind_Resume and __ZSt9terminatev are 8 bytes structs followed by 8+8+8 more bytes which shifts all __got entries after them by 0x18+0x18=0x30 bytes, which is exactly the error I encountered and it breaks everything...
After more digging it seems like some __got entries must be skipped, specifically those that have dyld_chained_ptr_64_rebase::bind value equal to 0, that should make things work again... And from looks of it that data is only available in file on disk, but not in memory?
dyld_chained_ptr_64_bind __Unwind_Resume
ordinal 19
addend 0
reserved 0
next 2
bind 1
dyld_chained_ptr_64_rebase must skip
target 3912432
high8 0
reserved 0
next 2
bind 0
dyld_chained_ptr_64_rebase must skip
target 431472
high8 0
reserved 0
next 2
bind 0
dyld_chained_ptr_64_rebase must skip
target 383136
high8 0
reserved 0
next 2
bind 0
dyld_chained_ptr_64_bind __ZSt9terminatev
ordinal 29
addend 0
reserved 0
next 2
bind 1
dyld_chained_ptr_64_rebase must skip
target 5070584
high8 0
reserved 0
next 2
bind 0
dyld_chained_ptr_64_rebase must skip
target 5060176
high8 0
reserved 0
next 2
bind 0
dyld_chained_ptr_64_rebase must skip
target 5026720
high8 0
reserved 0
next 2
bind 0
Thanks for your report.
@tomrus88
I updated plthook.h and plthook_osx.c. Could you check whether it works for you?
Sure, i'll check it out soon and report back.
Tested your fixes and it seems to work fine now. No more crash and hooked functions being called correctly in my case.