只 hook 指定模块里的函数的一种方法

我在之前 hook 一个第三方插件后,SpringBoard 崩溃并且 iPhone 白苹果进入激活模式。然后我在 DynamicLibraies 目录找到这个插件的 plist 文件,发现这个 dylib 注入到了系统的很多进程。我立马意识到是把系统重要调用也 hook 到了。

现在问题就变成了,如何只 hook 指定模块里的调用。

#include <substrate.h>
#include <dlfcn.h>
#import <Foundation/Foundation.h>

extern CFTypeRef MGCopyAnswer(CFStringRef question, CFDictionaryRef options);

CFTypeRef my_MGCopyAnswer_internal(CFStringRef question, CFDictionaryRef options) {
    if (CFStringCompare(question, CFSTR("UniqueDeviceID"), kNilOptions) == kCFCompareEqualTo) {
        return (__bridge_retained CFStringRef)@"😜";
    }
    return (__bridge_retained CFStringRef)@"...";
}

CFTypeRef (*orig_MGCopyAnswer_internal)(CFStringRef question, CFDictionaryRef options);

CFTypeRef hook_MGCopyAnswer_internal(CFStringRef question, CFDictionaryRef options) {
    static void *addrs[10]; // 缓存大小根据实际情况设置
    static int addrlen;

    // 最关键部分,获取上一层调用者函数的地址
    // 0 是上一层调用,1 是上上一层调用以此类推
    // __builtin_return_address 文档 https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
    void *addr = __builtin_return_address(0);

    // 先在缓存中查找
    for (int i = 0; i < addrlen; i++) {
        if (addrs[i] == addr) {
            return my_MGCopyAnswer_internal(question, options);
        }
    }

    Dl_info info = {0};
    // 获取函数地址的符号信息,比较有用的有 `dli_fname` 模块名 和 `dli_sname` 函数名
    // dladdr 文档 https://man7.org/linux/man-pages/man3/dladdr.3.html
    dladdr(addr, &info);

    // 在这里判断符合条件的调用
    if (strstr(info.dli_fname, "TetherMe")) {
        // 把地址保存到缓存,下次调用使用缓存快速查找
        if (addrlen < 10) {
            addrs[addrlen] = addr;
            addrlen++;
        }
        return my_MGCopyAnswer_internal(question, options);
    }
    return orig_MGCopyAnswer_internal(question, options);
}