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版本即可。

相关推荐
私人珍藏库4 小时前
[Mac] Unclutter 2.2.15 Mac上的文件暂存 智能剪贴板历史记录管理器工具
macos
哈茶真的c4 小时前
【Mac】开发环境使用/维护
macos
~~李木子~~5 小时前
贪心算法实验1
算法·ios·贪心算法
大炮走火5 小时前
iOS在制作framework时,oc与swift混编的流程及坑点!
开发语言·ios·swift
醇氧7 小时前
mac 安装 LibreOffice
macos
马拉萨的春天8 小时前
iOS中如果一个大图500M直接加载显示会崩溃,那么如何设置加载显示呢?
macos·ios·cocoa
0***148 小时前
Swift资源
开发语言·ios·swift
z***I3948 小时前
Swift Tips
开发语言·ios·swift
J***Q2928 小时前
Swift Solutions
开发语言·ios·swift