我在之前 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);
}