libMobileGestalt.dylib crashed when Hooking MGCopyAnswer for ARM64

You cannot hook MGCopyAnswer directly because it is too short.

When CydiaSubstrate hooks a C function, it sorts of overwrites an assembly version of goto your_function; at the beginning of the original function. This "goto" in ARM64 is 16 bytes in size, which means if the original function is too short (< 16 bytes), CydiaSubstrate can spill over and corrupt the neighboring functions.

This is exactly why the problem of MGCopyAnswer. The implementation of MGCopyAnswer is basically (on 9.3.2 arm64):

01 00 80 d2        movz x1, #0
01 00 00 14        b    MGCopyAnswer_internal

which is just 8 bytes (< 16 bytes) in size. So CydiaSubstrate will corrupt the 8 bytes after the end of MGCopyAnswer.

Unfortunately, MGCopyAnswer_internal is right after MGCopyAnswer, and even worse this function and is called by MGGetBoolAnswer as well. Since MGCopyAnswer_internal is corrupt, you get an EXC_BAD_INSTRUCTION crash inside libMobileGestalt.

A good news for MGCopyAnswer is that, you could just hook MGCopyAnswer_internal! This has an additional benefit that many related functions like MGGetBoolAnswer, MGCopyAnswerWithError, MGCopyMultipleAnswers etc. can respond to your change as well. The bad thing is that MGCopyAnswer_internal is completely internal, and there is no symbols pointing to it. We could rely on the fact that MGCopyAnswer_internal is exactly 8 bytes after MGCopyAnswer on ARM64, and develop this ugly hack:

static CFPropertyListRef (*orig_MGCopyAnswer_internal)(CFStringRef prop, uint32_t* outTypeCode);
CFPropertyListRef new_MGCopyAnswer_internal(CFStringRef prop, uint32_t* outTypeCode) {
    return orig_MGCopyAnswer_internal(prop, outTypeCode);

extern "C" MGCopyAnswer(CFStringRef prop);

static CFPropertyListRef (*orig_MGCopyAnswer)(CFStringRef prop);
CFPropertyListRef new_MGCopyAnswer(CFStringRef prop) {
    return orig_MGCopyAnswer(prop);

%ctor {
    uint8_t MGCopyAnswer_arm64_impl[8] = {0x01, 0x00, 0x80, 0xd2, 0x01, 0x00, 0x00, 0x14};
    const uint8_t* MGCopyAnswer_ptr = (const uint8_t*) MGCopyAnswer;
    if (memcmp(MGCopyAnswer_ptr, MGCopyAnswer_arm64_impl, 8) == 0) {
        MSHookFunction(MGCopyAnswer_ptr + 8, (void*)new_MGCopyAnswer_internal, (void**)&orig_MGCopyAnswer_internal);
    } else {
        MSHookFunction(MGCopyAnswer_ptr, (void*)new_MGCopyAnswer, (void**)&orig_MGCopyAnswer);

(This only checks for arm64 on 9.3.2. Other platforms may crash in different ways, and have different assembly code, so you may need to add additional conditions into enter the hook-MGCopyAnswer_internal branch. YMMV!)


Powered By Z-BlogPHP 1.5.1 Zero

Copyright Rights Reserved.