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

cpp 复制代码
#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);
#endif
    return 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);
#endif
    return ptr;
}

#pragma clang diagnostic pop
#endif  // PTRAUTH_HELPERS_H

MyTweak.mm

cpp 复制代码
#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.dylib
    handle = 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 8
            DobbyHook(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版本即可。

相关推荐
猫头虎6 小时前
如何解决 OpenClaw “Pairing required” 报错:两种官方解决方案详解
网络·windows·网络协议·macos·智能路由器·pip·scipy
游戏开发爱好者810 小时前
日常开发与测试的 App 测试方法、查看设备状态、实时日志、应用数据
android·ios·小程序·https·uni-app·iphone·webview
黑码哥10 小时前
ViewHolder设计模式深度剖析:iOS开发者掌握Android列表性能优化的实战指南
android·ios·性能优化·跨平台开发·viewholder
2501_9151063211 小时前
app 上架过程,安装包准备、证书与描述文件管理、安装测试、上传
android·ios·小程序·https·uni-app·iphone·webview
2501_9151063212 小时前
使用 Sniffmaster TCP 抓包和 Wireshark 网络分析
网络协议·tcp/ip·ios·小程序·uni-app·wireshark·iphone
熊猫钓鱼>_>12 小时前
移动端开发技术选型报告:三足鼎立时代的开发者指南(2026年2月)
android·人工智能·ios·app·鸿蒙·cpu·移动端
徐同保1 天前
通过ip访问nginx的服务时,被第一个server重定向了,通过设置default_server解决这个问题
ios·iphone
皮卡车厘子1 天前
Mac 挂载目录
macos
良逍Ai出海1 天前
在 Windows & macOS 上安装 Claude Code,并使用第三方 Key 的完整教程
windows·macos
热爱生活的五柒1 天前
linux/mac/wsl如何使用claude code,并配置免费的硅基流动API?(官方的需要付费订阅)
linux·运维·macos