macOS Hook 系统调用

孙康

基于 Kauth 或者 EndpointSecurity 框架可以监视系统的各类文件、进程事件,在审计后阻断或放通事件的执行。其中基于内核拓展的方案除了可以阻断执行,还可以修改函数调用参数,进行诸如文件保护、网络隔离等操作。

编写内核拓展较为复杂且可能导致系统崩溃等严重后果。Apple 提供了在用户态 hook 函数调用的机制,使用较为方便,称为动态库注入。动态库注入是 dyld 加载器提供的功能,通过修改环境变量DYLD_INSERT_LIBRARIES可向二进制注入动态库。注入的动态库需实现函数替换,Apple 提供了 dyld-interposing 方法,使用如下。

1
2
3
4
5
#ifndef DYLD_INTERPOSE
#define DYLD_INTERPOSE(_replacement,_replacee) \
__attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee \
__attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee };
#endif

该段宏定义的目的是修改 MachO 文件的 DATA 区的 interpose 字段,目的是将原始函数的地址替换为自定义函数地址,dyld 在加载动态库时对该地址进行替换。如这里对进程执行的系统调用execveposix_spawn进行替换,将映像更换为 echo,读者可自行查看是否完成替换。注意,调用printf函数不一定可以打印出来,具体原因不太明白,猜测是因为printf属于懒加载函数,注入动态库时该函数地址没有更新,无法调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static const char *s_repalce_path = "/bin/echo";

int fh_execve(const char *file, char *const *argv, char *const *envp) {
printf("[FishHook - execve] pid: %d, process path: %s.", getpid(), file);
return execve(s_repalce_path, argv, envp);
}

int fh_posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *actions, const posix_spawnattr_t *attr, char *const *argv, char *const *envp) {
printf("[FishHook - posix_spawn] pid: %d, process path: %s.", *pid, path);
return posix_spawn(pid, s_repalce_path, actions, attr, argv, envp);
}

DYLD_INTERPOSE(fh_execve, execve)
DYLD_INTERPOSE(fh_posix_spawn, posix_spawn)

需要注意的是,在开启 SIP (System Integrity Protection) 机制的机器上,签名的应用程序可能无法继承该环境变量,原因是内核会进行防止可执行文件被修改的检查,可参考 Apple 官方的 SIP 指南

  • Title: macOS Hook 系统调用
  • Author: 孙康
  • Created at : 2023-06-24 13:12:57
  • Updated at : 2023-08-31 19:55:35
  • Link: https://conradsun.github.io/2023/0676d00749.html
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
macOS Hook 系统调用