1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 __author__ = (
20 "Michael Cohen <scudette@google.com>",
21 "Adam Sindelar <adam.sindelar@gmail.com>")
22
23 from rekall import obj
24 from rekall import plugin
25 from rekall_lib import registry
26
27 from rekall.plugins.darwin import common
31 """Walks the global list of sockets in uipc_usrreq."""
32
33 name = "unp_sockets"
34 type_name = "socket"
35
44
47 """Looks up handles that point to a socket and collects the socket."""
48
49 name = "open_sockets"
50 type_name = "socket"
51
56
59 """Prints all open sockets we know about, from any source.
60
61 Netstat will display even connections that lsof doesn't know about, because
62 they were either recovered from an allocation zone, or found through a
63 secondary mechanism (like system call handler cache).
64
65 On the other hand, netstat doesn't know the file descriptor or, really, the
66 process that owns the connection (although it does know the PID of the last
67 process to access the socket.)
68
69 Netstat will also tell you, in the style of psxview, if a socket was only
70 found using some of the methods available.
71 """
72
73 name = "netstat"
74
75 @classmethod
90
91 @registry.classproperty
92 @registry.memoize
94 header = [dict(name="socket", type="socket", width=60)]
95 for method in cls.methods():
96 header.append(dict(name=method, width=12))
97
98 return plugin.PluginHeader(*header)
99
110
113 """
114
115 One version of arp_init looks like this:
116
117 void
118 arp_init(void)
119 {
120 VERIFY(!arpinit_done);
121
122 LIST_INIT(&llinfo_arp); // <-- This is the global we want.
123
124 llinfo_arp_zone = zinit(sizeof (struct llinfo_arp),
125 LLINFO_ARP_ZONE_MAX * sizeof (struct llinfo_arp), 0,
126 LLINFO_ARP_ZONE_NAME);
127 if (llinfo_arp_zone == NULL)
128 panic("%s: failed allocating llinfo_arp_zone", __func__);
129
130 zone_change(llinfo_arp_zone, Z_EXPAND, TRUE);
131 zone_change(llinfo_arp_zone, Z_CALLERACCT, FALSE);
132
133 arpinit_done = 1;
134 }
135
136 Disassembled, the first few instructions look like this:
137 0x0 55 PUSH RBP
138 0x1 4889e5 MOV RBP, RSP
139 0x4 803d65e9400001 CMP BYTE [RIP+0x40e965], 0x1
140 0xb 7518 JNZ 0xff80090a7f95
141 0xd 488d3dee802900 LEA RDI, [RIP+0x2980ee]
142 0x14 488d35f5802900 LEA RSI, [RIP+0x2980f5]
143 0x1b baf3000000 MOV EDX, 0xf3
144
145 # This is a call to kernel!panic (later kernel!assfail):
146 0x20 e80b6c1400 CALL 0xff80091eeba0
147
148 # This is where it starts initializing the linked list:
149 0x25 48c70548e94000000000 MOV QWORD [RIP+0x40e948], 0x0
150 00
151 0x30 488d0d0e812900 LEA RCX, [RIP+0x29810e]
152 """
153 name = "disassembled_llinfo_arp"
154
155 PANIC_FUNCTIONS = (u"__kernel__!_panic", u"__kernel__!_assfail")
156
201
202
203 -class DarwinArp(common.AbstractDarwinProducer):
204 """Show information about arp tables."""
205
206 name = "arp"
207 type_name = "rtentry"
208
229
232 """Show routing table."""
233
234 __name = "route"
235
236 RNF_ROOT = 2
237
239 """Walks the radix tree starting from the header h.
240
241 This function is taken from
242 xnu-2422.1.72/bsd/net/radix.c: rn_walk_tree()
243
244 Which is why it does not conform to the style guide.
245
246 Note too that the darwin source code abuses C macros:
247
248 #define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
249 #define rn_key rn_u.rn_leaf.rn_Key
250 #define rn_mask rn_u.rn_leaf.rn_Mask
251 #define rn_offset rn_u.rn_node.rn_Off
252 #define rn_left rn_u.rn_node.rn_L
253 #define rn_right rn_u.rn_node.rn_R
254
255 And then the original code does:
256 rn = rn.rn_left
257
258 So we replace these below.
259 """
260 rn = h.rnh_treetop
261
262 seen = set()
263
264
265 while rn.rn_bit >= 0:
266 rn = rn.rn_u.rn_node.rn_L
267
268 while rn and rn not in seen:
269 base = rn
270
271 seen.add(rn)
272
273
274 while (rn.rn_parent.rn_u.rn_node.rn_R == rn and
275 not rn.rn_flags & self.RNF_ROOT):
276 rn = rn.rn_parent
277
278
279 rn = rn.rn_parent.rn_u.rn_node.rn_R
280 while rn.rn_bit >= 0:
281 rn = rn.rn_u.rn_node.rn_L
282
283 next = rn
284
285
286 while True:
287 rn = base
288 if not rn:
289 break
290
291 base = rn.rn_u.rn_leaf.rn_Dupedkey
292 if not rn.rn_flags & self.RNF_ROOT:
293 yield rn
294
295 rn = next
296 if rn.rn_flags & self.RNF_ROOT:
297 return
298
300 renderer.table_header(
301 [("Source IP", "source", "20"),
302 ("Dest IP", "dest", "20"),
303 ("Interface", "interface", "9"),
304 ("Sent", "sent", "8"),
305 ("Recv", "recv", "8"),
306 ("Time", "timestamp", "24"),
307 ("Expires", "expires", "8"),
308 ("Delta", "delta", "8")])
309
310 route_tables = self.profile.get_constant_object(
311 "_rt_tables",
312 target="Array",
313 target_args=dict(
314 count=32,
315 target="Pointer",
316 target_args=dict(
317 target="radix_node_head")))
318
319 for node in self.rn_walk_tree(route_tables[2]):
320 rentry = node.dereference_as("rtentry")
321
322 renderer.table_row(
323 rentry.source_ip,
324 rentry.dest_ip,
325 rentry.name,
326 rentry.sent, rentry.rx,
327 rentry.base_calendartime,
328 rentry.rt_expire,
329 rentry.delta)
330
333 """Walks the global list of interfaces.
334
335 The head of the list of network interfaces is a kernel global [1].
336 The struct we use [2] is just the public part of the data [3]. Addresses
337 are related to an interface in a N:1 relationship [4]. AF-specific data
338 is a normal sockaddr struct.
339
340 References:
341 1:
342 https://github.com/opensource-apple/xnu/blob/10.9/bsd/net/dlil.c#L254
343 2:
344 https://github.com/opensource-apple/xnu/blob/10.9/bsd/net/if_var.h#L528
345 3:
346 https://github.com/opensource-apple/xnu/blob/10.9/bsd/net/dlil.c#L188
347 4:
348 https://github.com/opensource-apple/xnu/blob/10.9/bsd/net/if_var.h#L816
349 """
350
351 name = "ifconfig"
352
353
354
355
356 IFNET_HEAD_NAME = ("_ifnet_head", "_dlil_ifnet_head")
357
371
376
379 """Check IP Filters for hooks."""
380
381 __name = "ip_filters"
382
384 renderer.table_header([
385 ("Context", "context", "10"),
386 ("Filter", "filter", "16"),
387 ("Handler", "handler", "[addrpad]"),
388 ("Symbol", "symbol", "20")])
389
390 resolver = self.session.address_resolver
391 for list_name in ["_ipv4_filters", "_ipv6_filters"]:
392 filter_list = self.profile.get_constant_object(
393 list_name, target="ipfilter_list")
394
395 for item in filter_list.tqh_first.walk_list("ipf_link.tqe_next"):
396 filter = item.ipf_filter
397 name = filter.name.deref()
398 handler = filter.ipf_input.deref()
399 renderer.table_row("INPUT", name, handler,
400 resolver.format_address(handler))
401
402 handler = filter.ipf_output.deref()
403 renderer.table_row("OUTPUT", name, handler,
404 resolver.format_address(handler))
405
406 handler = filter.ipf_detach.deref()
407 renderer.table_row("DETACH", name, handler,
408 resolver.format_address(handler))
409