macOS Hook系统调用

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

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

#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在加载动态库时对该地址进行替换。如这里对进程执行的系统调用execve和posix_spawn进行替换,将映像更换为echo,读者可自行查看是否完成替换。注意,调用printf函数不一定可以打印出来,具体原因不太明白,猜测是因为printf属于懒加载函数,注入动态库时该函数地址没有更新,无法调用。

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指南

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容