macOS IPC - Inter Process Communication
通过端口进行Mach消息传递
基本信息
Mach使用任务作为共享资源的最小单位,每个任务可以包含多个线程。这些任务和线程与POSIX进程和线程一一映射。
任务之间的通信通过Mach进程间通信(IPC)进行,利用单向通信通道。消息在端口之间传递,端口类似于由内核管理的消息队列。
端口是Mach IPC的基本元素。它可用于发送消息和接收消息。
每个进程都有一个IPC表,可以在其中找到进程的Mach端口。Mach端口的名称实际上是一个数字(指向内核对象的指针)。
进程还可以将带有某些权限的端口名称发送给另一个任务,内核将在其他任务的IPC表中创建此条目。
端口权限
端口权限定义了任务可以执行的操作,对于这种通信至关重要。可能的端口权限包括(此处的定义):
接收权限,允许接收发送到端口的消息。Mach端口是MPSC(多生产者,单消费者)队列,这意味着整个系统中可能只有一个接收权限与每个端口相关联(与管道不同,在管道的读端可以有多个进程持有文件描述符)。
具有接收权限的任务可以接收消息并创建发送权限,从而允许其发送消息。最初,只有自己的任务对其端口具有接收权限。
如果拥有接收权限的所有者死亡或终止它,发送权限将变得无效(死命名)。
发送权限,允许向端口发送消息。
发送权限可以克隆,因此拥有发送权限的任务可以克隆权限并将其授予第三个任务。
注意端口权限也可以通过Mac消息传递。
一次性发送权限,允许向端口发送一条消息,然后消失。
此权限无法克隆,但可以移动。
端口集权限,表示一个_端口集_而不是单个端口。从端口集中出列消息会从其中一个包含的端口中出列消息。端口集可用于同时监听多个端口,类似于Unix中的
select
/poll
/epoll
/kqueue
。死命名,它不是实际的端口权限,而仅是一个占位符。当端口被销毁时,所有现有的端口权限将变成死命名。
任务可以将发送权限传递给其他任务,使其能够发送消息回来。发送权限也可以被克隆,因此任务可以复制并将权限授予第三个任务。结合一个称为引导服务器的中间进程,可以实现任务之间的有效通信。
文件端口
文件端口允许在Mac端口中封装文件描述符(使用Mach端口权限)。可以使用fileport_makeport
从给定的FD创建fileport
,并使用fileport_makefd
从fileport
创建FD。
建立通信
如前所述,可以使用Mach消息发送权限,但是,您不能在没有发送Mach消息的权限的情况下发送权限。那么,如何建立第一次通信呢?
为此,涉及引导服务器(mac中的launchd),因为每个人都可以获得发送权限到引导服务器,因此可以要求它为发送消息到另一个进程的权限:
任务A创建一个新端口,获得其上的接收权限。
作为接收权限的持有者,任务A为端口生成一个发送权限。
任务A与引导服务器建立一个连接,并将其在开始时生成的端口的发送权限发送给它。
请记住,任何人都可以获得发送权限到引导服务器。
任务A向引导服务器发送
bootstrap_register
消息,以将给定端口与名称(如com.apple.taska
)关联。任务B与引导服务器交互,执行服务名称的引导查找(
bootstrap_lookup
)。因此,引导服务器可以响应,任务B将在查找消息中向其发送先前创建的端口的发送权限。如果查找成功,服务器会复制从任务A接收的发送权限,并传输给任务B。
请记住,任何人都可以获得发送权限到引导服务器。
有了这个发送权限,任务B能够向任务A发送消息。
对于双向通信,通常任务B生成一个具有接收权限和发送权限的新端口,并将发送权限提供给任务A,以便它可以向任务B发送消息(双向通信)。
引导服务器无法验证任务声明的服务名称。这意味着任务可能潜在地冒充任何系统任务,例如虚假声明授权服务名称,然后批准每个请求。
然后,Apple将系统提供的服务名称存储在安全配置文件中,位于受SIP保护的目录中:/System/Library/LaunchDaemons
和/System/Library/LaunchAgents
。引导服务器将为这些服务名称中的每一个创建并持有一个接收权限。
对于这些预定义服务,查找过程略有不同。当查找服务名称时,launchd会动态启动服务。新的工作流程如下:
任务B启动服务名称的引导查找。
launchd检查任务是否正在运行,如果没有,则启动它。
任务A(服务)执行引导签入(
bootstrap_check_in()
)。在这里,引导服务器创建一个发送权限,保留它,并将接收权限传递给任务A。launchd复制发送权限并将其发送给任务B。
任务B生成一个具有接收权限和发送权限的新端口,并将发送权限提供给任务A(服务),以便它可以向任务B发送消息(双向通信)。
但是,此过程仅适用于预定义的系统任务。非系统任务仍按最初描述的方式运行,这可能导致潜在的冒充。
因此,launchd绝不能崩溃,否则整个系统将崩溃。
一个 Mach 消息
mach_msg
函数,本质上是一个系统调用,用于发送和接收 Mach 消息。该函数要求将消息作为初始参数发送。这条消息必须以 mach_msg_header_t
结构开头,后跟实际的消息内容。该结构定义如下:
进程拥有 接收权限 可以在 Mach 端口上接收消息。相反,发送方 被授予 发送权限 或 一次性发送权限。一次性发送权限专门用于发送一条消息,之后将变为无效。
初始字段 msgh_bits
是一个位图:
第一个位(最重要的)用于指示消息是否复杂(下文详述)
第 3 和第 4 位由内核使用
第 2 字节的 最不重要的 5 位 可用于 凭证:另一种用于发送键/值组合的端口类型。
第 3 字节的 最不重要的 5 位 可用于 本地端口
第 4 字节的 最不重要的 5 位 可用于 远程端口
凭证、本地端口和远程端口中可以指定的类型为(来自 mach/message.h):
例如,MACH_MSG_TYPE_MAKE_SEND_ONCE
可用于指示应为此端口派生并传输一次性发送权。也可以指定 MACH_PORT_NULL
以防止接收方能够回复。
为了实现简单的双向通信,进程可以在名为 reply port(msgh_local_port
)的 mach 消息头中指定一个mach端口,接收方可以通过该端口向此消息发送回复。
请注意,这种双向通信在期望回复的 XPC 消息中使用(xpc_connection_send_message_with_reply
和 xpc_connection_send_message_with_reply_sync
)。但通常会创建不同的端口,如前所述创建双向通信。
消息头的其他字段包括:
msgh_size
:整个数据包的大小。msgh_remote_port
:发送此消息的端口。msgh_voucher_port
:mach凭证。msgh_id
:此消息的ID,由接收方解释。
请注意,mach消息通过 mach端口
发送,这是内置于 mach 内核中的单接收方、多发送方通信通道。多个进程可以向 mach 端口发送消息,但在任何时刻只有一个进程可以从中读取。
然后,消息由**mach_msg_header_t
头部、主体和尾部**(如果有)组成,并且可以授予回复权限。在这些情况下,内核只需将消息从一个任务传递到另一个任务。
尾部是由内核添加到消息中的信息(用户无法设置),可以在消息接收时使用标志 MACH_RCV_TRAILER_<trailer_opt>
请求(可以请求不同的信息)。
复杂消息
然而,还有其他更复杂的消息,比如传递附加端口权限或共享内存的消息,内核还需要将这些对象发送给接收方。在这种情况下,头部 msgh_bits
的最高位被设置。
可传递的描述符在 mach/message.h
中定义:
Mac 端口 API
请注意,端口与任务命名空间相关联,因此要创建或搜索端口,还需要查询任务命名空间(更多信息请参见 mach/mach_port.h
):
mach_port_allocate
|mach_port_construct
:创建一个端口。mach_port_allocate
还可以创建一个端口集:接收一组端口的接收权。每当接收到消息时,都会指示消息来自哪个端口。mach_port_allocate_name
:更改端口的名称(默认为32位整数)。mach_port_names
:从目标获取端口名称。mach_port_type
:获取任务对名称的权限。mach_port_rename
:重命名端口(类似于 FD 的 dup2)。mach_port_allocate
:分配新的接收、端口集或死端口。mach_port_insert_right
:在具有接收权的端口中创建新的权限。mach_port_...
mach_msg
|mach_msg_overwrite
:用于发送和接收 mach 消息的函数。覆盖版本允许指定不同的缓冲区用于消息接收(另一个版本将仅重用它)。
调试 mach_msg
由于函数**mach_msg
和mach_msg_overwrite
**是用于发送和接收消息的函数,设置在它们上的断点将允许检查发送和接收的消息。
例如,开始调试任何可以调试的应用程序,因为它将加载**libSystem.B
,该库将使用此函数**。
要获取**mach_msg
**的参数,请检查寄存器。这些是参数(来自 mach/message.h):
从注册表中获取值:
检查消息头,检查第一个参数:
那种 mach_msg_bits_t
类型非常常见,用于允许回复。
枚举端口
名称 是给端口的默认名称(检查前3个字节如何增加)。ipc-object
是端口的混淆唯一标识符。
还要注意,只有**send
** 权限的端口是用来标识其所有者(端口名称 + pid)。
还要注意使用 +
表示连接到同一端口的其他任务。
也可以使用 procesxp 来查看还有注册的服务名称(由于需要 com.apple.system-task-port
,因此需要禁用 SIP):
您可以从http://newosxbook.com/tools/binpack64-256.tar.gz下载iOS上的这个工具。
代码示例
请注意发送方如何分配一个端口,为名称org.darlinghq.example
创建一个发送权限,并将其发送到引导服务器,而发送方请求该名称的发送权限并使用它来发送消息。
sender.c
特权端口
有一些特殊端口允许在具有对其发送权限的任务中执行某些敏感操作或访问某些敏感数据。这使得这些端口从攻击者的角度非常有趣,不仅因为其功能,还因为可以在任务之间共享发送权限。
主机特殊端口
这些端口由一个数字表示。
通过调用**host_get_special_port
获取发送权限,通过调用host_set_special_port
获取接收权限。然而,这两个调用都需要host_priv
端口,只有 root 用户可以访问。此外,在过去,root 用户可以调用host_set_special_port
**并劫持任意端口,例如通过劫持HOST_KEXTD_PORT
绕过代码签名(SIP 现在阻止了这一点)。
这些端口分为 2 组:前 7 个端口由内核拥有,其中 1 是 HOST_PORT
,2 是 HOST_PRIV_PORT
,3 是 HOST_IO_MASTER_PORT
,7 是 HOST_MAX_SPECIAL_KERNEL_PORT
。
从数字 8开始的端口由系统守护程序拥有,它们可以在host_special_ports.h
中找到声明。
Host port:如果一个进程对此端口具有发送权限,他可以通过调用其例程获取有关系统的信息,例如:
host_processor_info
:获取处理器信息host_info
:获取主机信息host_virtual_physical_table_info
:虚拟/物理页表(需要 MACH_VMDEBUG)host_statistics
:获取主机统计信息mach_memory_info
:获取内核内存布局Host Priv port:具有对此端口的发送权限的进程可以执行特权操作,例如显示引导数据或尝试加载内核扩展。进程需要是 root 用户才能获得此权限。
此外,为了调用**
kext_request
** API,需要具有其他授权**com.apple.private.kext*
**,这些授权仅提供给 Apple 二进制文件。可以调用的其他例程包括:
host_get_boot_info
:获取machine_boot_info()
host_priv_statistics
:获取特权统计信息vm_allocate_cpm
:分配连续物理内存host_processors
:向主机处理器发送权限mach_vm_wire
:使内存常驻由于root用户可以访问此权限,因此可以调用
host_set_[special/exception]_port[s]
来劫持主机特殊或异常端口。
可以通过运行以下命令查看所有主机特殊端口:
任务端口
最初,Mach 没有"进程",而是有"任务",被认为更像是线程的容器。当 Mach 与 BSD 合并时,每个任务与一个 BSD 进程相关联。因此,每个 BSD 进程具有成为进程所需的详细信息,每个 Mach 任务也有其内部工作方式(除了不存在的 pid 0,即 kernel_task
)。
有两个与此相关的非常有趣的函数:
task_for_pid(target_task_port, pid, &task_port_of_pid)
: 获取与由pid
指定的任务相关联的任务端口的 SEND 权限,并将其提供给指定的target_task_port
(通常是调用者任务,使用了mach_task_self()
,但也可以是不同任务上的 SEND 端口)。pid_for_task(task, &pid)
: 给定一个任务的 SEND 权限,找到该任务相关联的 PID。
为了在任务内执行操作,任务需要对自身调用 mach_task_self()
(使用 task_self_trap
(28))获得一个 SEND
权限。有了这个权限,任务可以执行多个操作,如:
task_threads
: 获取所有任务线程的 SEND 权限task_info
: 获取有关任务的信息task_suspend/resume
: 暂停或恢复任务task_[get/set]_special_port
thread_create
: 创建一个线程task_[get/set]_state
: 控制任务状态更多内容可以在 mach/task.h 中找到。
请注意,使用不同任务的任务端口的 SEND 权限,可以在不同任务上执行此类操作。
此外,任务端口也是 vm_map
端口,允许使用诸如 vm_read()
和 vm_write()
等函数在任务内部读取和操作内存。这基本上意味着具有对不同任务的任务端口的 SEND 权限的任务将能够向该任务注入代码。
请记住,因为内核也是一个任务,如果有人设法获得对 kernel_task
的 SEND 权限,它将能够让内核执行任何操作(越狱)。
调用
mach_task_self()
以获取调用者任务的端口名称。此端口仅在exec()
期间被 继承;使用fork()
创建的新任务会获得一个新的任务端口(作为一个特例,在 suid 二进制文件中的exec()
之后,任务也会获得一个新的任务端口)。生成任务并获取其端口的唯一方法是在执行fork()
时执行 "port swap dance"。这些是访问端口的限制(来自二进制文件
AppleMobileFileIntegrity
的macos_task_policy
):如果应用程序具有
com.apple.security.get-task-allow
权限,来自相同用户的进程可以访问任务端口(通常由 Xcode 用于调试)。未经过验证的进程不允许将其用于生产发布。具有
com.apple.system-task-ports
权限的应用程序可以获取任何进程的任务端口,但不能获取内核的。在旧版本中,它被称为task_for_pid-allow
。这仅授予给 Apple 应用程序。Root 可以访问未使用 强化运行时编译的应用程序的任务端口(且不是来自 Apple)。
任务名称端口: 任务端口的非特权版本。它引用任务,但不允许控制它。似乎唯一可以通过它获得的是 task_info()
。
通过任务端口在线程中注入 Shellcode
您可以从以下位置获取 shellcode:
pageIntroduction to ARM64v8macOS Entitlements
macOS Entitlements Overview
macOS applications are granted specific privileges and capabilities through entitlements. Entitlements are key-value pairs embedded in the code signature of an application. They define the resources an application can access and the actions it can perform on a system.
Viewing Entitlements
You can view the entitlements of an application using the codesign
tool in Terminal. Run the following command:
This command will display the entitlements associated with the specified application.
Modifying Entitlements
Entitlements can be modified by changing the entitlements plist file associated with the application. This file is typically named entitlements.plist
and is located within the application bundle.
To modify entitlements, edit the entitlements.plist
file using a text editor, then re-codesign the application with the updated entitlements file.
Common Entitlements
Some common entitlements include:
com.apple.security.network.client: Allows the application to act as a network client.
com.apple.security.files.user-selected.read-write: Grants read and write access to files selected by the user.
com.apple.security.print: Allows the application to print documents.
Conclusion
Understanding macOS entitlements is crucial for managing the security and capabilities of applications on the macOS platform. By reviewing and modifying entitlements, you can control the resources and actions available to an application, enhancing the overall security posture of the system.
编译前面的程序并添加权限以能够使用相同用户注入代码(如果不行,则需要使用sudo)。
最后更新于