作者:FloatingGuy 转载请注明出处:https://floatingguy.github.io/


在《深入解析MAC OS X & iOS 操作系统》书上,代码清单10-1 中有一个例子 “Mach 端口导出器”, 分析代码流程搞清楚 MIG 的作用。

mach_port_names – MIG 分析

mach_port_names 此函数可以导出给定任务的端口名称空间。分析器原理+ 调用流程。

这个函数 在 mach 子系统 mach_port 中。该系统对应的 MIG 描述文件是/osfmk/mach/mach_port.defs。(这个目录下还有其他的子系统 defs描述文件)

在没有使用 mig 工具预编译 defs 文件时, 源码中是没有 mach_port.h 文件的。
mach_port.h 文件中 包含 mach_port 子系统 用于操作任务端口的所有函数。

步骤:

1. 使用 MIG 生成 mach_port.h ..文件
2. 分析 MIG 生成的目录结构
3. 分析代码
4. 搞清楚 mach_port_names 原理

使用 MIG 生成 mach_port.h ..文件

直接运行命令就自动生成相关文件

mig xxx.defs

分析 MIG 生成的目录结构

一共4个文件

1
2
3
4
├── mach_port.defs
├── mach_port.h
├── mach_portServer.c
├── mach_portUser.c

查看文件

这里有 4个关键的函数
_Xmach_port_names(…) {
MIG_checkRequest__mach_port_names_t

RetCode = mach_port_names(In0P->Head.msgh_request_port, (mach_port_name_array_t *)&(OutP->names.address), &OutP->namesCnt, (mach_port_type_array_t *)&(OutP->types.address), &OutP->typesCnt);

}

_kernelrpc_mach_port_names(….) {

__MIG_check__Reply__mach_port_names_t

设置返回值

}

reply code 比 request 大100, code 对应InP->Head.msgh_id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
mach_port.h #########################
kern_return_t _kernelrpc_mach_port_names
(
ipc_space_t task,
mach_port_name_array_t *names,
mach_msg_type_number_t *namesCnt,
mach_port_type_array_t *types,
mach_msg_type_number_t *typesCnt
);
mach_portUser.c #########################
mig_internal kern_return_t __MIG_check__Reply__mach_port_names_t(__Reply__mach_port_names_t *Out0P)
{
typedef __Reply__mach_port_names_t __Reply __attribute__((unused));
boolean_t msgh_simple;
#if __MigTypeCheck
unsigned int msgh_size;
#endif /* __MigTypeCheck */
if (Out0P->Head.msgh_id != 3300) {
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
{ return MIG_SERVER_DIED; }
else
{ return MIG_REPLY_MISMATCH; }
}
msgh_simple = !(Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX);
#if __MigTypeCheck
msgh_size = Out0P->Head.msgh_size;
if ((msgh_simple || Out0P->msgh_body.msgh_descriptor_count != 2 ||
msgh_size != (mach_msg_size_t)sizeof(__Reply)) &&
(!msgh_simple || msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
((mig_reply_error_t *)Out0P)->RetCode == KERN_SUCCESS))
{ return MIG_TYPE_ERROR ; }
#endif /* __MigTypeCheck */
if (msgh_simple) {
return ((mig_reply_error_t *)Out0P)->RetCode;
}
#if __MigTypeCheck
if (Out0P->names.type != MACH_MSG_OOL_DESCRIPTOR) {
return MIG_TYPE_ERROR;
}
#endif /* __MigTypeCheck */
#if __MigTypeCheck
if (Out0P->types.type != MACH_MSG_OOL_DESCRIPTOR) {
return MIG_TYPE_ERROR;
}
#endif /* __MigTypeCheck */
return MACH_MSG_SUCCESS;
}
#endif /* !defined(__MIG_check__Reply__mach_port_names_t__defined) */
#endif /* __MIG_check__Reply__mach_port_subsystem__ */
#endif /* ( __MigTypeCheck ) */
/* Routine mach_port_names */
mig_external kern_return_t _kernelrpc_mach_port_names
(
ipc_space_t task,
mach_port_name_array_t *names,
mach_msg_type_number_t *namesCnt,
mach_port_type_array_t *types,
mach_msg_type_number_t *typesCnt
)
{
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
} Request __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_ool_descriptor_t names;
mach_msg_ool_descriptor_t types;
/* end of the kernel processed data */
NDR_record_t NDR;
mach_msg_type_number_t namesCnt;
mach_msg_type_number_t typesCnt;
mach_msg_trailer_t trailer;
} Reply __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_ool_descriptor_t names;
mach_msg_ool_descriptor_t types;
/* end of the kernel processed data */
NDR_record_t NDR;
mach_msg_type_number_t namesCnt;
mach_msg_type_number_t typesCnt;
} __Reply __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
union {
Request In;
Reply Out;
} Mess;
Request *InP = &Mess.In;
Reply *Out0P = &Mess.Out;
mach_msg_return_t msg_result;
#ifdef __MIG_check__Reply__mach_port_names_t__defined
kern_return_t check_result;
#endif /* __MIG_check__Reply__mach_port_names_t__defined */
__DeclareSendRpc(3200, "mach_port_names") //[将mach_port_names函数 绑定到对应的编号 ]
// 准备消息头
InP->Head.msgh_bits =
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
/* msgh_size passed as argument */
InP->Head.msgh_request_port = task;
InP->Head.msgh_reply_port = mig_get_reply_port();
InP->Head.msgh_id = 3200;
InP->Head.msgh_reserved = 0;
/* BEGIN VOUCHER CODE */
#ifdef USING_VOUCHERS
if (voucher_mach_msg_set != NULL) {
voucher_mach_msg_set(&InP->Head);
}
#endif // USING_VOUCHERS
/* END VOUCHER CODE */
__BeforeSendRpc(3200, "mach_port_names")
// 由 MIG 生成的 调用 mach_msg
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
__AfterSendRpc(3200, "mach_port_names")
if (msg_result != MACH_MSG_SUCCESS) {
__MachMsgErrorWithoutTimeout(msg_result);
{ return msg_result; }
}
#if defined(__MIG_check__Reply__mach_port_names_t__defined)
check_result = __MIG_check__Reply__mach_port_names_t((__Reply__mach_port_names_t *)Out0P);
if (check_result != MACH_MSG_SUCCESS)
{ return check_result; }
#endif /* defined(__MIG_check__Reply__mach_port_names_t__defined) */
// 返回 数据到 userspace
*names = (mach_port_name_array_t)(Out0P->names.address);
*namesCnt = Out0P->namesCnt;
*types = (mach_port_type_array_t)(Out0P->types.address);
*typesCnt = Out0P->typesCnt;
return KERN_SUCCESS;
}
mach_portServer.c #########################
// 检查用户传入的参数,直接返回结果
mig_internal kern_return_t __MIG_check__Request__mach_port_names_t(__attribute__((__unused__)) __Request__mach_port_names_t *In0P)
{
typedef __Request__mach_port_names_t __Request;
#if __MigTypeCheck
if ((In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
(In0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Request)))
return MIG_BAD_ARGUMENTS;
#endif /* __MigTypeCheck */
return MACH_MSG_SUCCESS;
}
mig_internal novalue _Xmach_port_names
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
{
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
mach_msg_trailer_t trailer;
} Request __attribute__((unused));
#ifdef __MigPackStructs
#pragma pack()
#endif
typedef __Request__mach_port_names_t __Request;
typedef __Reply__mach_port_names_t Reply __attribute__((unused));
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
Request *In0P = (Request *) InHeadP;
Reply *OutP = (Reply *) OutHeadP;
#ifdef __MIG_check__Request__mach_port_names_t__defined
kern_return_t check_result;
#endif /* __MIG_check__Request__mach_port_names_t__defined */
#if UseStaticTemplates
const static mach_msg_ool_descriptor_t namesTemplate = {
/* addr = */ (void *)0,
/* size = */ 0,
/* deal = */ FALSE,
/* copy = */ MACH_MSG_VIRTUAL_COPY,
/* pad2 = */ 0,
/* type = */ MACH_MSG_OOL_DESCRIPTOR,
};
#endif /* UseStaticTemplates */
#if UseStaticTemplates
const static mach_msg_ool_descriptor_t typesTemplate = {
/* addr = */ (void *)0,
/* size = */ 0,
/* deal = */ FALSE,
/* copy = */ MACH_MSG_VIRTUAL_COPY,
/* pad2 = */ 0,
/* type = */ MACH_MSG_OOL_DESCRIPTOR,
};
#endif /* UseStaticTemplates */
kern_return_t RetCode;
__DeclareRcvRpc(3200, "mach_port_names")
__BeforeRcvRpc(3200, "mach_port_names")
// 检查用户参数
#if defined(__MIG_check__Request__mach_port_names_t__defined)
check_result = __MIG_check__Request__mach_port_names_t((__Request *)In0P);
if (check_result != MACH_MSG_SUCCESS)
{ MIG_RETURN_ERROR(OutP, check_result); }
#endif /* defined(__MIG_check__Request__mach_port_names_t__defined) */
#if UseStaticTemplates
OutP->names = namesTemplate;
#else /* UseStaticTemplates */
OutP->names.deallocate = FALSE;
OutP->names.copy = MACH_MSG_VIRTUAL_COPY;
OutP->names.pad1 = 0;
OutP->names.type = MACH_MSG_OOL_DESCRIPTOR;
#if defined(KERNEL) && !defined(__LP64__)
OutP->names.pad_end = 0;
#endif
#endif /* UseStaticTemplates */
#if UseStaticTemplates
OutP->types = typesTemplate;
#else /* UseStaticTemplates */
OutP->types.deallocate = FALSE;
OutP->types.copy = MACH_MSG_VIRTUAL_COPY;
OutP->types.pad1 = 0;
OutP->types.type = MACH_MSG_OOL_DESCRIPTOR;
#if defined(KERNEL) && !defined(__LP64__)
OutP->types.pad_end = 0;
#endif
#endif /* UseStaticTemplates */
OutP->namesCnt = 0;
OutP->typesCnt = 0;
// 核心, 调用 mach_port.c 中的 mach_port_names 函数
RetCode = mach_port_names(In0P->Head.msgh_request_port, (mach_port_name_array_t *)&(OutP->names.address), &OutP->namesCnt, (mach_port_type_array_t *)&(OutP->types.address), &OutP->typesCnt);
if (RetCode != KERN_SUCCESS) {
MIG_RETURN_ERROR(OutP, RetCode);
}
OutP->names.size = OutP->namesCnt * 4;
OutP->types.size = OutP->typesCnt * 4;
OutP->NDR = NDR_record;
OutP->Head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply));
OutP->msgh_body.msgh_descriptor_count = 2;
__AfterRcvRpc(3200, "mach_port_names")
}

分析代码

现在看下 osfmk/mach/mach_port.c 中mach_port_names 的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
kern_return_t
mach_port_names(
ipc_space_t space,
mach_port_name_t **namesp,
mach_msg_type_number_t *namesCnt,
mach_port_type_t **typesp,
mach_msg_type_number_t *typesCnt)
{
ipc_entry_t table;
ipc_entry_num_t tsize;
mach_port_index_t index;
ipc_entry_num_t actual; /* this many names */
ipc_port_timestamp_t timestamp; /* logical time of this operation */
mach_port_name_t *names;
mach_port_type_t *types;
kern_return_t kr;
vm_size_t size; /* size of allocated memory */
vm_offset_t addr1; /* allocated memory, for names */
vm_offset_t addr2; /* allocated memory, for types */
vm_map_copy_t memory1; /* copied-in memory, for names */
vm_map_copy_t memory2; /* copied-in memory, for types */
/* safe simplifying assumption */
static_assert(sizeof(mach_port_name_t) == sizeof(mach_port_type_t));
if (space == IS_NULL)
return KERN_INVALID_TASK;
size = 0;
for (;;) {
ipc_entry_num_t bound;
vm_size_t size_needed;
is_read_lock(space);
if (!is_active(space)) {
is_read_unlock(space);
if (size != 0) {
kmem_free(ipc_kernel_map, addr1, size);
kmem_free(ipc_kernel_map, addr2, size);
}
return KERN_INVALID_TASK;
}
/* upper bound on number of names in the space */
bound = space->is_table_size;
size_needed = vm_map_round_page(
(bound * sizeof(mach_port_name_t)),
VM_MAP_PAGE_MASK(ipc_kernel_map));
if (size_needed <= size)
break;
...
if (size != 0) {
kmem_free(ipc_kernel_map, addr1, size);
kmem_free(ipc_kernel_map, addr2, size);
}
size = size_needed;
kr = vm_allocate(ipc_kernel_map, &addr1, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_KERN_MEMORY_IPC));
if (kr != KERN_SUCCESS)
return KERN_RESOURCE_SHORTAGE;
kr = vm_allocate(ipc_kernel_map, &addr2, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_KERN_MEMORY_IPC));
if (kr != KERN_SUCCESS) {
kmem_free(ipc_kernel_map, addr1, size);
return KERN_RESOURCE_SHORTAGE;
}
kr = vm_map_wire(
ipc_kernel_map,
vm_map_trunc_page(addr1,
VM_MAP_PAGE_MASK(ipc_kernel_map)),
vm_map_round_page(addr1 + size,
VM_MAP_PAGE_MASK(ipc_kernel_map)),
VM_PROT_READ|VM_PROT_WRITE|VM_PROT_MEMORY_TAG_MAKE(VM_KERN_MEMORY_IPC),
FALSE);
if (kr != KERN_SUCCESS) {
kmem_free(ipc_kernel_map, addr1, size);
kmem_free(ipc_kernel_map, addr2, size);
return KERN_RESOURCE_SHORTAGE;
}
...
}
/* space is read-locked and active */
names = (mach_port_name_t *) addr1;
types = (mach_port_type_t *) addr2;
actual = 0;
timestamp = ipc_port_timestamp();
table = space->is_table;
tsize = space->is_table_size;
// 这里 从target task 的 ipc_space 中取出 space->is_table 中所有 entry.
// 根据 entry->ie_bits 可知该 entry 是否分配给了 port。
for (index = 0; index < tsize; index++) {
ipc_entry_t entry = &table[index];
ipc_entry_bits_t bits = entry->ie_bits;
if (IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE) {
mach_port_name_t name;
name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
// 将 name和对应的 type(entry->ie_bits) 存储到数组 names 和 types中, actual 是数组的长度。
mach_port_names_helper(timestamp, entry, name, names,
types, &actual);
}
}
is_read_unlock(space);
if (actual == 0) {
memory1 = VM_MAP_COPY_NULL;
memory2 = VM_MAP_COPY_NULL;
if (size != 0) {
kmem_free(ipc_kernel_map, addr1, size);
kmem_free(ipc_kernel_map, addr2, size);
}
} else {
vm_size_t size_used;
vm_size_t vm_size_used;
size_used = actual * sizeof(mach_port_name_t);
vm_size_used =
vm_map_round_page(size_used,
VM_MAP_PAGE_MASK(ipc_kernel_map));
kr = vm_map_unwire(
ipc_kernel_map,
vm_map_trunc_page(addr1,
VM_MAP_PAGE_MASK(ipc_kernel_map)),
vm_map_round_page(addr1 + vm_size_used,
VM_MAP_PAGE_MASK(ipc_kernel_map)),
FALSE);
assert(kr == KERN_SUCCESS);
// 释放addr1, addr2没有的空间
kr = vm_map_unwire(
ipc_kernel_map,
vm_map_trunc_page(addr2,
VM_MAP_PAGE_MASK(ipc_kernel_map)),
vm_map_round_page(addr2 + vm_size_used,
VM_MAP_PAGE_MASK(ipc_kernel_map)),
FALSE);
assert(kr == KERN_SUCCESS);
//将 addr1 中的 ports 拷贝到 memory1
kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr1,
(vm_map_size_t)size_used, TRUE, &memory1);
assert(kr == KERN_SUCCESS);
kr = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr2,
(vm_map_size_t)size_used, TRUE, &memory2);
assert(kr == KERN_SUCCESS);
....
}
*namesp = (mach_port_name_t *) memory1;
*namesCnt = actual;
*typesp = (mach_port_type_t *) memory2;
*typesCnt = actual;
return KERN_SUCCESS;
}
// 生成 gen
#define IE_BITS_GEN_MASK 0xff000000 /* 8 bits for generation */
#define IE_BITS_GEN(bits) ((bits) & IE_BITS_GEN_MASK)
void
mach_port_names_helper(
ipc_port_timestamp_t timestamp,
ipc_entry_t entry,
mach_port_name_t name,
mach_port_name_t *names,
mach_port_type_t *types,
ipc_entry_num_t *actualp)
{
ipc_entry_bits_t bits;
ipc_port_request_index_t request;
mach_port_type_t type = 0;
ipc_entry_num_t actual;
ipc_port_t port;
bits = entry->ie_bits;
request = entry->ie_request;
__IGNORE_WCASTALIGN(port = (ipc_port_t) entry->ie_object);
if (bits & MACH_PORT_TYPE_RECEIVE) {
assert(IP_VALID(port));
if (request != IE_REQ_NONE) {
...
type |= ipc_port_request_type(port, name, request);
..
}
} else if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
...
reqtype = (request != IE_REQ_NONE) ?
ipc_port_request_type(port, name, request) : 0;
if (ip_active(port) || IP_TIMESTAMP_ORDER(timestamp, port->ip_timestamp)) {
type |= reqtype;
} else {
bits &= ~(IE_BITS_TYPE_MASK);
bits |= MACH_PORT_TYPE_DEAD_NAME;
/* account for additional reference for dead-name notification */
if (reqtype != 0)
bits++;
}
}
type |= IE_BITS_TYPE(bits);
actual = *actualp;
names[actual] = name;
types[actual] = type;
*actualp = actual+1;
}
mach_port_type_t
ipc_port_request_type(
ipc_port_t port,
__assert_only mach_port_name_t name,
ipc_port_request_index_t index)
{
ipc_port_request_t ipr, table;
mach_port_type_t type = 0;
table = port->ip_requests;
assert (table != IPR_NULL);
assert(index != IE_REQ_NONE);
ipr = &table[index];
assert(ipr->ipr_name == name);
if (IP_VALID(IPR_SOR_PORT(ipr->ipr_soright))) {
type |= MACH_PORT_TYPE_DNREQUEST;
if (IPR_SOR_SPREQ(ipr->ipr_soright)) {
type |= MACH_PORT_TYPE_SPREQUEST;
if (!IPR_SOR_SPARMED(ipr->ipr_soright)) {
type |= MACH_PORT_TYPE_SPREQUEST_DELAYED;
}
}
}
return type;
}

搞清楚 mach_port_names 原理

原理就是 通过获取 目标进程的 task_t 对象, 然后获取 ipc_space 对象,从 ipc_space 的 is_table 遍历所有 entry, 根据 ie_bits 找到已经分配的 entry.

计算 port name ==> name = MACH_PORT_MAKE(index, IE_BITS_GEN(bits));
获取 type的逻辑 需要注意:

  1. 获取 ipc_prot 对象(port), entry->ie_object
  2. 获取请求的数组索引,entry->ie_request
  3. 获取请求数组 ,port->ip_requests
  4. 根据 ipr->ipr_soright 赋予 type 值