12

macOS IPC Study Notes

 3 years ago
source link: https://o0xmuhe.github.io/2019/09/20/macOS-IPC-Study-basic-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

macOS IPC Study Notes

-w303

-w303

-w843

-w843

然而一切都是在Mach Msg的基础之上的。

2. 一些基础概念

2.1 什么是Port

个人理解就是类似Windows上handle的概念。
用户态经过处理是一个类似socket的整数,内核态(namep)索引到与之对应的消息队列,IPC时通过Port传递数据到消息队列,或者从消息队列取出数据。

2.2.2 port name

2.2.3 (port) right

一个port和对这个port的访问权限,有对应的权限才能做对应的操作,比如recv,接收数据;send,发送数据。

1
2
3
4
5
6
7
#define MACH_PORT_RIGHT_SEND		((mach_port_right_t) 0)
#define MACH_PORT_RIGHT_RECEIVE ((mach_port_right_t) 1)
#define MACH_PORT_RIGHT_SEND_ONCE ((mach_port_right_t) 2)
#define MACH_PORT_RIGHT_PORT_SET ((mach_port_right_t) 3)
#define MACH_PORT_RIGHT_DEAD_NAME ((mach_port_right_t) 4)
#define MACH_PORT_RIGHT_LABELH ((mach_port_right_t) 5)
#define MACH_PORT_RIGHT_NUMBER ((mach_port_right_t) 6)

2.2 创建流程

示例代码:

1
2
mach_port_t p;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p);

mach_port_allocate

函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
kern_return_t
mach_port_allocate(
ipc_space_t space,
mach_port_right_t right,
mach_port_name_t *namep)
{
kern_return_t kr;
mach_port_qos_t qos = qos_template;

kr = mach_port_allocate_full (space, right, MACH_PORT_NULL,
&qos, namep);
return (kr);
}

mach_port_allocate_full
根据不同的right走不同的分配逻辑:

-w680

-w680

ipc_port_alloc /ipc_port_alloc_name

-w614

-w614

两个函数区别只是是否指定了name。

ipc_object_alloc

-w596

-w596

348行通过一个宏,把port转name的方式获取namep,之后对ipc entry的关键结构进行初始化。

ipc_port_init

初始化port结构

-w641

-w641

至此,port初始化完成,namep初始化完成,可以根据namep索引到对应的内核中的消息队列。

port与ipc entry的关系,来自Mac OS X Internals:

15651519972092.jpg

其中的一些概念:

  • 用户态 mach_port_t

port在用户态表示,类似socket的一个整数

  • 内核态 mach_port_name_t

    1
    typedef natural_t mach_port_name_t;

    port在内核态表示

  • 1
    Structure used to pass information about port allocation requests.Must be padded to 64-bits total length.
  • ipc space

1
2
Each task has a private IPC spacea namespace for portsthat is represented by the ipc_space structure in the kernel. 
Mac OS X Internals

每个task都有自己的唯一一个ipc sapce,

其中 ipc_entry_t is_table; /* an array of entries */ 字段是放着所有的ipc entry。

task的结构体实在是太大了,从task的struct ipc_space *itk_space;字段索引到其对应的ipc space。

  • ipc entry
15651508934037.jpg

看源码发现,图中的ipc_tree_entry结构没了:

1
2
3
4
5
6
7
8
9
10
11
12
struct ipc_space {
lck_spin_t is_lock_data;
ipc_space_refs_t is_bits; /* holds refs, active, growing */
ipc_entry_num_t is_table_size; /* current size of table */
ipc_entry_num_t is_table_free; /* count of free elements */
ipc_entry_t is_table; /* an array of entries */
task_t is_task; /* associated task */
struct ipc_table_size *is_table_next; /* info for larger table */
ipc_entry_num_t is_low_mod; /* lowest modified entry during growth */
ipc_entry_num_t is_high_mod; /* highest modified entry during growth */
int is_node_id; /* HOST_LOCAL_NODE, or remote node if proxy space */
};
  • port的user reference计数是啥
    一个port的user reference只表示了某个entry在task的space中被多少个地方使用,和entry实际指向哪个port没有关系

2.3 发送MACH MSG

mach msg的结构不再赘述,这部分直接看message.h头文件里的定义即可,下面着重看发送和接收过程。

其实是一个把mach msg转换成kmsg结构,然后入队(目标消息队列)的操作,目标进程获取就是一个出队的操作。

用户态(client) <--> 内核态 <--> 用户态(server)

收/发都是用mach_msg,使用options参数区别是收还是发。

2.3.1 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mach_msg(...)
mach_msg_trap(...)
mach_msg_overwrite_trap(...)
option & MACH_SEND_MSG --> mach_msg_send
// mach_msg --> kmsg
// alloc a kernel msg buffer(kmsg), and copy user mach_msg to kernel msg buffer
ipc_kmsg_get(msg_addr, send_size, &kmsg);
ipc_kmsg_copyin
ipc_kmsg_copyin_hearder(kmsg, space, override, optionp)
ipc_kmsg_copyin_body(kmsg, space, map, optionp)
ipc_kmsg_send
ipc_voucher_send_preprocessing(kmsg);
ipc_mqueue_send(&port->ip_messages, kmsg,option,send_timeout);
option & MACH_RCV_MSG //TODO
ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
mach_msg_rcv_link_special_reply_port(...)
ipc_mqueue_receive
mach_msg_receive_results

2.3.2 发送

  • mach_msg_send( mach_msg_header_t *msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_timeout_t send_timeout, mach_msg_priority_t override)

    根据消息大小重新分配了内存,并且把消息拷贝进来,并且消息尾部增加了一些字段:

1
2
3
4
5
trailer = (mach_msg_max_trailer_t *) ((vm_offset_t)kmsg->ikm_header + send_size);
trailer->msgh_sender = current_thread()->task->sec_token;
trailer->msgh_audit = current_thread()->task->audit_token;
trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;

之前审服务的时候遇到过,还以为这部分是可控的,造成乌龙。 囧

  • ipc_kmsg_copyin(kmsg, space, map, override, &option);

    此时kmsg是新分配的内存,里面放的是要发送的mach msg, space和map都是当前task的space和map,直接获取,这部分有个图,可以看Macos Internals

    • ipc_kmsg_copyin_header(kmsg, space, override, optionp);
      拷贝 port rights,成功的话,原本消息中(kmsg)的port name都会被替换成对应的对象的指针。
    • ipc_kmsg_copyin_body( kmsg, space, map);
      拷贝msg body部分,中间验证了size、desc部分的size,类型等字段。
      desc_count < 0x3fff 。
      descriptor_size部分,必须是desc*16 == descriptor_size,不满足会为了对齐而调整。
      最终完成拷贝,把用户态的mach msg拷贝到了kmsg中。
  • ipc_kmsg_send(kmsg, option, send_timeout);
    到这里的时候port right拷贝了,消息内容也拷贝了,该直接发送了。把消息发送到dst的消息队列里。
    对于发送给内核的消息和非内核的消息分开处理
    内核:kmsg = ipc_kobject_server(kmsg, option);
    其他:ipc_mqueue_send(&port->ip_messages, kmsg, option, send_timeout);

2.3.3 接收

  • ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
    Convert a name in a space to a message queue.
    根据这个recv_name在space里找到ipc_entry结构,从而找到其中ipc_object->ipmessage结构。

    -w532

    -w532

  • mach_msg_rcv_link_special_reply_port(…)

  • ipc_mqueue_receive(mqueue, option, rcv_size, msg_timeout, THREAD_ABORTSAFE);
    Receive a message from a message queue
    之前得到了消息队列mqueue,这个函数就是从这个消息队列中取出消息。

    • ipc_mqueue_receive_on_thread
      使用指定thread从消息队列中接收消息。
      接受分port set(imq_is_set()) 和 单个port(imq_is_queue()),这部分看message queue的结构体也能看出来必须要这么处理。

      消息队列是一个循环双向链表,取消息的过程就是一个 unlink的过程:

      -w629

      -w629

  • mach_msg_receive_results
    Receive a message, copy out的操作,把之前“解链”的消息拷贝出来。

再谈Mach-IPC
Mac OS X Internals
MOXil
Auditing and Exploiting Apple IPC – Ianbeer
bazad


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK