当我尝试挂接MGCopyAnswer时,我遇到了崩溃。我正在iOS 5.3,arm64二进制文件中越狱的iPhone 8.3s上尝试此操作。
#import <substrate.h>
extern "C" CFTypeRef MGCopyAnswer(CFStringRef);
MSHook(CFTypeRef, MGCopyAnswer, CFStringRef key)
{
return _MGCopyAnswer(key);
}
%ctor
{
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
if ( appID && [appID isEqualToString:@"com.test.test"]) {
MSHookFunction(MGCopyAnswer, MSHake(MGCopyAnswer));
}
}
制作文件:
ARCHS = armv7 armv7s arm64
TARGET = iphone:latest:8.0
test2_FRAMEWORKS = UIKit
include theos/makefiles/common.mk
TWEAK_NAME = test2
test2_FILES = Tweak.xm
test2_LIBRARIES = MobileGestalt
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
install.exec "killall -9 SpringBoard"
崩溃日志:
Version: 1.44 (1.4)
Code Type: ARM-64 (Native)
Parent Process: launchd [1]
Date/Time: 2016-04-25 01:09:31.810 +0800
Launch Time: 2016-04-25 01:09:31.564 +0800
OS Version: iOS 8.3 (12F70)
Report Version: 105
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x000000000068fe68
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libMobileGestalt.dylib 0x0000000195af7e84 0x195af4000 + 16004
1 libMobileGestalt.dylib 0x0000000195af82bc MGGetBoolAnswer + 32
2 AppSupport 0x000000018b020594 __CPIsInternalDevice_block_invoke + 16
3 libdispatch.dylib 0x0000000196c99950 _dispatch_client_callout + 12
4 libdispatch.dylib 0x0000000196c9a828 dispatch_once_f + 92
5 AppSupport 0x000000018b02057c CPIsInternalDevice + 60
6 UIKit 0x0000000189b58750 ___UIApplicationUsesAlternateUI_block_invoke + 12
7 libdispatch.dylib 0x0000000196c99950 _dispatch_client_callout + 12
8 libdispatch.dylib 0x0000000196c9a828 dispatch_once_f + 92
9 UIKit 0x0000000189923750 UIApplicationInitialize + 1872
10 UIKit 0x0000000189922b1c UIApplicationMain + 320
MGCopyAnswer:
-> 0x193a7fe84 <+0>: .long 0x002d7c28 ; unknown opcode
0x193a7fe88 <+4>: .long 0x00000001 ; unknown opcode
0x193a7fe8c <+8>: stp x20, x19, [sp, #32]
0x193a7fe90 <+12>: stp x29, x30, [sp, #48]
0x193a7fe94 <+16>: add x29, sp, #48
0x193a7fe98 <+20>: sub sp, sp, #48
0x193a7fe9c <+24>: mov x19, x1
0x193a7fea0 <+28>: mov x22, x0
0x193a7fea4 <+32>: movz w0, #0
0x193a7fea8 <+36>: bl 0x193a7f564 ; ___lldb_unnamed_function54$$libMobileGestalt.dylib
0x193a7feac <+40>: orr w1, wzr, #0x1
0x193a7feb0 <+44>: mov x0, x22
0x193a7feb4 <+48>: bl 0x193a7f5fc ; ___lldb_unnamed_function56$$libMobileGestalt.dylib
0x193a7feb8 <+52>: mov x21, x0
0x193a7febc <+56>: movz w20, #0
0x193a7fec0 <+60>: cbz x21, 0x193a7fefc ; <+120>
0x193a7fec4 <+64>: ldr w20, [x21, #148]
0x193a7fec8 <+68>: mov x0, x21
orig_MGCopyAnswer
0x104234000: movz x1, #0
0x104234004: stp x24, x23, [sp, #-64]!
0x104234008: stp x22, x21, [sp, #16]
0x10423400c: ldr x16, #8
0x104234010: br x16
0x104234014: .long 0x93a7fe8c
0x104234018: .long 0x00000001 ; unknown opcode
我做错了什么?
你不能直接钩住MGCopyAnswer
,因为它太短了。
当 CydiaSubstrate 挂接 C 函数时,它会在原始函数的开头覆盖goto your_function;
的汇编版本。ARM64中的这个"goto"大小为16字节,这意味着如果原始函数太短(<16字节),CydiaSubstrate可能会溢出并破坏相邻的功能。
这正是MGCopyAnswer
问题的原因。MGCopyAnswer
的实现基本上是(在 9.3.2 arm64 上):
01 00 80 d2 movz x1, #0
01 00 00 14 b MGCopyAnswer_internal
大小仅为 8 字节(<16 字节)。所以CydiaSubstrate会在MGCopyAnswer
结束后损坏8个字节。
不幸的是,MGCopyAnswer_internal
是在MGCopyAnswer
之后,更糟糕的是这个函数,也被MGGetBoolAnswer
调用。由于MGCopyAnswer_internal
已损坏,因此您在libMobileGestalt中会EXC_BAD_INSTRUCTION崩溃。
对MGCopyAnswer
来说,一个好消息是,你可以钩住MGCopyAnswer_internal
!这还有一个额外的好处,许多相关功能,如MGGetBoolAnswer
、MGCopyAnswerWithError
、MGCopyMultipleAnswers
等也可以响应您的更改。不好的是,MGCopyAnswer_internal
完全是内部的,没有符号指向它。我们可以依靠 ARM64MGCopyAnswer
后MGCopyAnswer_internal
正好是 8 个字节的事实,并开发这个丑陋的黑客:
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);
}
}
(这仅检查 9.3.2 上的 arm64。其他平台可能会以不同的方式崩溃,并且具有不同的汇编代码,因此您可能需要在 Enter hook-MGCopyAnswer_internal
分支中添加其他条件。扬子晚报!
试试这段代码:
#import <substrate.h>
static CFTypeRef (*orig_MGCopyAnswer)(CFStringRef str);
CFTypeRef new_MGCopyAnswer(CFStringRef str)
{
return orig_MGCopyAnswer(str);
}
%ctor
{
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
if ( appID && [appID isEqualToString:@"com.test.test"]) {
void * MGCopyAnswerFn = MSFindSymbol(NULL, "_MGCopyAnswer");
MSHookFunction(MGCopyAnswerFn, (void *) new_MGCopyAnswer, (void **)& orig_MGCopyAnswer);
}
}