macOS MIG - Mach Interface Generator

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS红队专家)

支持HackTricks的其他方式:

基本信息

MIG被创建用于简化Mach IPC代码的生成过程。它基本上生成了所需的代码,以便服务器和客户端可以根据给定的定义进行通信。即使生成的代码看起来很丑陋,开发人员只需导入它,他的代码将比以前简单得多。

定义是使用接口定义语言(IDL)使用.defs扩展名指定的。

这些定义有5个部分:

  • 子系统声明:关键字子系统用于指示名称ID。还可以将其标记为**KernelServer**,如果服务器应在内核中运行。

  • 包含和导入:MIG使用C预处理器,因此可以使用导入。此外,可以使用uimportsimport用于用户或服务器生成的代码。

  • 类型声明:可以定义数据类型,尽管通常会导入mach_types.defsstd_types.defs。对于自定义类型,可以使用一些语法:

    • [i`n/out]tran:需要从传入消息翻译或传出消息翻译的函数

    • c[user/server]type:映射到另一个C类型。

    • destructor:在释放类型时调用此函数。

  • 操作:这些是RPC方法的定义。有5种不同类型:

    • routine:期望回复

    • simpleroutine:不期望回复

    • procedure:期望回复

    • simpleprocedure:不期望回复

    • function:期望回复

示例

创建一个定义文件,这里是一个非常简单的函数:

myipc.defs
subsystem myipc 500; // Arbitrary name and id

userprefix USERPREF;        // Prefix for created functions in the client
serverprefix SERVERPREF;    // Prefix for created functions in the server

#include <mach/mach_types.defs>
#include <mach/std_types.defs>

simpleroutine Subtract(
server_port :  mach_port_t;
n1          :  uint32_t;
n2          :  uint32_t);

请注意,第一个参数是要绑定的端口,MIG将自动处理回复端口(除非在客户端代码中调用mig_get_reply_port())。此外,操作的ID将是连续的,从指定的子系统ID开始(因此,如果一个操作已被弃用,则会被删除,并且使用skip来仍然使用其ID)。

现在使用MIG生成服务器和客户端代码,这些代码将能够相互通信以调用Subtract函数:

mig -header myipcUser.h -sheader myipcServer.h myipc.defs

在当前目录中将创建几个新文件。

您可以在系统中找到一个更复杂的示例:mdfind mach_port.defs 您可以从与文件相同的文件夹中编译它:mig -DLIBSYSCALL_INTERFACE mach_ports.defs

在文件**myipcServer.cmyipcServer.h中,您可以找到结构体SERVERPREFmyipc_subsystem**的声明和定义,该结构体基本上定义了根据接收的消息ID调用的函数(我们指定了起始编号为500):

/* Description of this subsystem, for use in direct RPC */
const struct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = {
myipc_server_routine,
500, // start ID
501, // end ID
(mach_msg_size_t)sizeof(union __ReplyUnion__SERVERPREFmyipc_subsystem),
(vm_address_t)0,
{
{ (mig_impl_routine_t) 0,
// Function to call
(mig_stub_routine_t) _XSubtract, 3, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__Subtract_t)},
}
};

NDR_record

NDR_record由libsystem_kernel.dylib导出,它是一个结构体,允许MIG转换数据,使其对系统不可知,因为MIG被设计用于在不同系统之间使用(而不仅仅是在同一台机器上)。

这很有趣,因为如果在二进制文件中发现_NDR_record作为一个依赖项(jtool2 -S <binary> | grep NDRnm),这意味着该二进制文件是一个MIG客户端或服务器。

此外,MIG服务器__DATA.__const(或macOS内核中的__CONST.__constdata和其他*OS内核中的__DATA_CONST.__const)中具有调度表。这可以通过**jtool2**来转储。

MIG客户端将使用__NDR_record通过__mach_msg发送到服务器。

二进制分析

jtool

由于许多二进制文件现在使用MIG来公开mach端口,了解如何识别MIG的使用以及MIG在每个消息ID上执行的函数是很有趣的。

jtool2可以从Mach-O二进制文件中解析MIG信息,指示消息ID并识别要执行的函数:

jtool2 -d __DATA.__const myipc_server | grep MIG

此外,MIG 函数只是被调用的实际函数的包装器,这意味着通过获取其反汇编并使用 BL 进行过滤,您可能会找到被调用的实际函数:

jtool2 -d __DATA.__const myipc_server | grep BL

汇编

先前提到负责根据接收到的消息ID调用正确函数的函数是myipc_server。然而,通常不会有二进制文件的符号(没有函数名称),因此有兴趣查看反编译后的样子,因为它总是非常相似的(此函数的代码与暴露的函数无关):

int _myipc_server(int arg0, int arg1) {
var_10 = arg0;
var_18 = arg1;
// 初始指令以找到正确的函数指针
*(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f;
*(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
*(int32_t *)(var_18 + 0x4) = 0x24;
*(int32_t *)(var_18 + 0xc) = 0x0;
*(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
*(int32_t *)(var_18 + 0x10) = 0x0;
if (*(int32_t *)(var_10 + 0x14) <= 0x1f4 && *(int32_t *)(var_10 + 0x14) >= 0x1f4) {
rax = *(int32_t *)(var_10 + 0x14);
// 调用 sign_extend_64 可以帮助识别此函数
// 这将在 rax 中存储需要调用的指针
// 检查地址 0x100004040 的使用(函数地址数组)
// 0x1f4 = 500(起始ID)
            rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
            var_20 = rax;
// 如果 - else,如果 if 返回 false,而 else 调用正确的函数并返回 true
            if (rax == 0x0) {
                    *(var_18 + 0x18) = **_NDR_record;
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
var_4 = 0x0;
}
else {
// 计算地址,使用 2 个参数调用正确的函数
                    (var_20)(var_10, var_18);
                    var_4 = 0x1;
}
}
else {
*(var_18 + 0x18) = **_NDR_record;
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
var_4 = 0x0;
}
rax = var_4;
return rax;
}

实际上,如果转到函数**0x100004000,您将找到routine_descriptor结构体的数组。结构体的第一个元素是函数实现的地址**,结构体占用 0x28 字节,因此每 0x28 字节(从字节 0 开始)您可以获得 8 字节,那将是将要调用的函数的地址

此数据可以使用此 Hopper 脚本提取。

调试

MIG 生成的代码还调用 kernel_debug 来生成关于进入和退出操作的日志。可以使用 tracekdv 来检查它们:kdv all | grep MIG

参考资料

从零开始学习 AWS 黑客技术,成为专家 htARTE (HackTricks AWS Red Team Expert)!

支持 HackTricks 的其他方式:

最后更新于