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!)