如何在macOS上使用Mach内核设置主机例外端口?

5

我想使用 Mach异常端口 来处理在macOS上运行的所有任务(进程)的异常。 我的理解是,此时应使用 host_set_exception_ports。但是,即使我用 sudo 执行程序,host_set_exception_ports 也返回 KERN_NO_ACCESS (错误代码8)。我的实验性代码可以使用 task_set_exception_ports 处理单个任务的异常。

我已经查看了 host_set_exception_portsMach内核代码。有一行代码从该函数返回 KERN_NO_ACCESS。我有点难以理解那里发生了什么。它似乎检查我传递给 host_set_exception_ports 的异常掩码。我使用不同的异常掩码进行测试,但始终获得相同的负面结果。

我的问题:这是否意味着在用户空间应用程序中使用 host_set_exception_ports 存在一般限制?如果没有,我该如何在我的应用程序中设置主机异常端口以接收系统范围的异常?

下面的程序是一个最小示例,展示了该行为,否则没有太多用处。使用 gcc example.c 编译该程序,使用 sudo ./a.out 执行它。

#include <mach/mach.h>
#include <stdio.h>
#include <stdlib.h>

void catchMachExceptions() {
    mach_port_t exception_port;
    kern_return_t rc;

    rc = mach_port_allocate(mach_task_self(),
                            MACH_PORT_RIGHT_RECEIVE,
                            &exception_port);
    if (rc != KERN_SUCCESS) {
        fprintf(stderr, "Unable to allocate exception port: %d\n", rc);
        exit(-1);
    }

    rc = mach_port_insert_right(mach_task_self(),
                                exception_port,
                                exception_port,
                                MACH_MSG_TYPE_MAKE_SEND);
    if (rc != KERN_SUCCESS) {
        fprintf(stderr, "Unable to insert right: %d\n", rc);
        exit(-1);
    }

    rc = host_set_exception_ports(mach_host_self(),
                                  EXC_MASK_ALL,
                                  exception_port,
                                  EXCEPTION_STATE_IDENTITY,
                                  MACHINE_THREAD_STATE);
    if (rc != KERN_SUCCESS) {
        fprintf(stderr, "Unable to set exception: %d\n", rc);
        exit(-1);
    }
}

int main(int argc, char **argv) {
    catchMachExceptions();
}

2
只是猜测,但这可能需要内核模式,请尝试将您的程序作为内核扩展。 - Eugene Dudnyk
1
我相信@DisableR指向了正确的方向...特别是如果你正在尝试为内核进程安装异常处理程序。尝试将你要监听的进程限制在用户级别进程上,看看是否解决了你的问题。我怀疑(不确定)"这是否意味着在用户空间应用程序中使用host_set_exception_ports存在一般限制"的答案是肯定的。 - Myst
2个回答

3
浏览源代码,host_set_exception_ports 调用了你注意到的 mac_task_check_set_host_exception_ports
深入研究后,你可以发现 mac_task_check_set_host_exception_ports 基于任务检索凭证,然后调用 MAC_CHECK(proc_check_set_host_exception_port, cred, exception)
这是在 security/mac_internals.h 中定义的宏。请查看此处 xnu 代码。阅读此代码,遍历策略模块列表并检查策略模块是否允许或拒绝请求。
根据代码,所有策略模块都必须同意且不会发布错误。因此,看起来您需要具有正确的特权。GNU Mach 项目中有一些文档表明,发送到特权主机端口的权利授予第一个任务,并且只能从那里传递。请参见GNU Mach 定义的 host_ports、等的相关说明
综上所述,不仅您不能作为用户 proc 进行更改,而且还需要通过一条从启动时开始的链专门继承特权。

1
额外说明一下,尽管相当古老,但书籍《Mac OS X Internals: A Systems Approach》似乎提供了关于这个主题的很好的信息。 - Hod

2
补充一下上面的答案:
GNU Mach对主机端口的定义与XNU已经无关了。缺失的部分是沙盒。mac_task_check_set_host_exception_ports()确实会检查Policy模块。大多数操作都涉及两个策略模块:Sandbox.kext和(对于端口)AppleMobileFileIntegrity.kext。前者使用Darwin 18-19中的钩子#127来挂钩它,由于启用了SIP,平台配置文件会被查询,因为几乎所有进程都在低级别上进行沙箱处理。
您可以通过授权或者如果您未经沙箱处理(如launchd(PID 1)),则可以绕过此限制,并且它确实具有在主机异常端口上设置接收权限的功能,然后将其转移到指定为/ System / Library / LaunchDaemons属性列表中的HostExceptionServer的任何人身上- 默认情况下为ReportCrash。如果您可以添加自己的LaunchDaemons(这需要至少暂时禁用SIP的文件系统限制),则可以从launchd获取端口。
FYI,Mac OS X Internals是一本很棒的书,但它已经13年了,只涵盖到10.4。MACF从10.5开始使用。考虑阅读它的非官方续集,《*OS Internals》(http://NewOSXBook.com/),它详细介绍了这方面的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接