iOS arm64e hook MGCopyAnswer got Crash or Only Partial results got hooked
直接hook MGCopyAnswer会崩溃,按照stackoverflow的9年前的帖子说法,hook MGCopyAnswer+8仅部分生效。且hook到的都是bool类型的值。
崩溃的原因就是因为指令长度过短。
libMobileGestalt.dylib`MGCopyAnswer:
-> 0x1a483b484 <+0>: mov x1, #0x0 ; =0
0x1a483b488 <+4>: b 0x1a483af00 ; ___lldb_unnamed_symbol1060
在某些比较久(9年前)版本的ios上,libMobileGestalt的MGCopyAnswer+8对应的是MGCopyAnswer调用的内部函数。这也是原来代码+8的原因。但是新版本变成了MGGetBoolAnswer函数的开头,所以只有bool类型的MG返回值被hook到了。
方法要么是把所有类型的MGGet都改了,或者hook “b 0x1a483af00 ; ___lldb_unnamed_symbol1060”内部的长函数。
其他的例如hook opendir崩溃,也是同样的原因。
但是这里直接计算解析地址hook,还是会崩溃。怎么回事呢?
通过调试排查,发现dobby在执行完hook代码逻辑返回的时候,dobby hook框架是一个blr x8,但是lldb si单步进入之后,直接崩溃,x8变成了一个很奇怪的值:例如X8 89E3262AB23B0041 | A.;.*&.. |
这个地方解析一下就发现是高位bit set成了1,所以是pac的指针。然后就出现了EXC_BAD_ACCESS。
所以现在第二次崩溃的原因是因为:PAC指针签名的原因。
系统函数进入的时候函数开头和结尾的时候多了pac指令:例如pacibsp(pac,ib 使用key b,sp指定寄存器),retab(key b验证签名)。
解决办法:我们在hook的时候,对代码签名一下即可。
pac_helper.h
#ifndef PTRAUTH_HELPERS_H
#define PTRAUTH_HELPERS_H// Helpers for PAC archs.// If the compiler understands __arm64e__, assume it's paired with an SDK that has
// ptrauth.h. Otherwise, it'll probably error if we try to include it so don't.
#if __arm64e__
#include <ptrauth.h>
#endif#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"// Given a pointer to instructions, sign it so you can call it like a normal fptr.
static void *make_sym_callable(void *ptr) {
#if __arm64e__if (!ptr) return ptr;ptr = ptrauth_sign_unauthenticated(ptrauth_strip(ptr, ptrauth_key_function_pointer), ptrauth_key_function_pointer, 0);
#endifreturn ptr;
}// Given a function pointer, strip the PAC so you can read the instructions.
static void *make_sym_readable(void *ptr) {
#if __arm64e__if (!ptr) return ptr;ptr = ptrauth_strip(ptr, ptrauth_key_function_pointer);
#endifreturn ptr;
}#pragma clang diagnostic pop
#endif // PTRAUTH_HELPERS_H
MyTweak.mm
#import <dlfcn.h>
#import <substrate.h>
#import <Foundation/Foundation.h>
#import <CaptainHook/CaptainHook.h>
#import "dobby.h"
#import "pac_helper.h"#define _FUNC_ADDR_(A, O) (const void *)((long)(A) + (O))
#define ABS(x) ((x) < 0 ? -(x) : (x))// 日志宏定义
#define DLog(fmt, ...) NSLog(@"[AA] " fmt, ##__VA_ARGS__)// 全局变量
static void *handle = NULL;
static void *ptrMGCopyAnswer = NULL;// 原始函数指针
static CFTypeRef (*original_MGCopyAnswer_internal)(CFStringRef key, uint32_t *status) = NULL;// 替换函数实现
static CFTypeRef replaced_MGCopyAnswer_internal(CFStringRef key, uint32_t *status)
{DLog(@"MGCopyAnswer called with key: %@", key);// 在这里可以修改返回值,例如修改设备信息/*if (CFStringCompare(key, CFSTR("DeviceClass"), 0) == kCFCompareEqualTo) {return CFRetain(CFSTR("iPhone"));}if (CFStringCompare(key, CFSTR("ProductType"), 0) == kCFCompareEqualTo) {return CFRetain(CFSTR("iPhone14,2"));}if (CFStringCompare(key, CFSTR("SerialNumber"), 0) == kCFCompareEqualTo) {return CFRetain(CFSTR("FAKE123456"));}*/// 调用原始函数CFTypeRef result = original_MGCopyAnswer_internal(key, status);if (result) {DLog(@"MGCopyAnswer result: %@", result);}return result;
}// 初始化函数
static void InitializeMGCopyAnswer()
{// 加载 libMobileGestalt.dylibhandle = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_NOW);if (!handle) {DLog(@"Failed to load libMobileGestalt.dylib: %s", dlerror());return;}// 获取 MGCopyAnswer 函数地址ptrMGCopyAnswer = dlsym(handle, "MGCopyAnswer");if (!ptrMGCopyAnswer) {DLog(@"Failed to find MGCopyAnswer: %s", dlerror());return;}DLog(@"MGCopyAnswer address: %p", ptrMGCopyAnswer);DLog(@"Handle base address: %p", handle);
}// 构造函数
CHConstructor
{@autoreleasepool {DLog(@"Initializing MGCopyAnswer hook...");// 初始化InitializeMGCopyAnswer();if (!ptrMGCopyAnswer) {DLog(@"ptrMGCopyAnswer is NULL");return;}// 读取函数开头的字节码uint8_t *readablePtr = (uint8_t *)make_sym_readable(ptrMGCopyAnswer);// 检查是否是旧版本 (mov w1, #0; b <offset>)if (memcmp(readablePtr, "\x01\x00\x80\xd2\x01\x00\x00\x14", 8) == 0){// 旧版本:直接跳转到 offset 8DobbyHook(make_sym_callable((void *)_FUNC_ADDR_(ptrMGCopyAnswer, 8)),(void *)replaced_MGCopyAnswer_internal,(void **)&original_MGCopyAnswer_internal);DLog(@"Hooked legacy MGCopyAnswer_internal");}// 检查是否是新版本 (mov w1, #0; b <offset>)else if (memcmp(readablePtr, "\x01\x00\x80\xd2", 4) == 0){// 新版本:解析 B 指令获取真实函数地址// ARM64 B 指令格式: 0x14000000 | (offset >> 2)void *bInstPtr = (void *)((uint8_t *)ptrMGCopyAnswer + 4);int32_t bInst = *((int32_t *)make_sym_readable(bInstPtr));// 检查是否是 B 指令 (opcode: 000101)if ((bInst & 0xFC000000) != 0x14000000) {DLog(@"MGCopyAnswer_internal: Invalid branch instruction: 0x%x", bInst);return;}DLog(@"B instruction: 0x%x", bInst);// 提取 26 位偏移量并进行符号扩展int32_t offset = bInst & 0x3FFFFFF;if (offset & 0x2000000) {offset |= 0xFC000000; // 符号扩展}offset <<= 2; // 偏移量以 4 字节为单位DLog(@"Offset: 0x%x (%d bytes)", offset, offset);// 计算真实函数地址void *mPtrMGCopyAnswer = (void *)_FUNC_ADDR_(bInstPtr, offset);DLog(@"Real MGCopyAnswer_internal address: %p", mPtrMGCopyAnswer);DLog(@"File offset of MGCopyAnswer_internal: 0x%lx",ABS((long)mPtrMGCopyAnswer - (long)handle));// Hook 真实函数DobbyHook(make_sym_callable(mPtrMGCopyAnswer),(void *)replaced_MGCopyAnswer_internal,(void **)&original_MGCopyAnswer_internal);DLog(@"Hooked modern MGCopyAnswer_internal");}else{DLog(@"Unsupported MGCopyAnswer implementation");DLog(@"First 8 bytes: %02x %02x %02x %02x %02x %02x %02x %02x",readablePtr[0], readablePtr[1], readablePtr[2], readablePtr[3],readablePtr[4], readablePtr[5], readablePtr[6], readablePtr[7]);}}
}
成功。
项目只需链接:CoreFoundation.framework, Foundation.framework, libdobby.a即可。
dobby.h libdobby.a从这里下载(github-actions released this Mar 14, 2024 latest 5dfc854):https://github.com/jmpews/Dobby/releases/download/latest/dobby-iphoneos-all.tar.gz
选取里面的universal版本即可。
