如何从Unix域套接字被动捕获(AF_UNIX套接字监控)?

如何从Unix域套接字被动捕获(AF_UNIX套接字监控)?

TCP/IP 和 UDP 捕获可以使用tcpdump/进行dumpcap,并生成 pcap/pcapng 文件,该文件可以馈送到 Wireshark 进行进一步分析。是否存在用于命名 Unix 域套接字的类似工具? (适用于抽象套接字的通用解决方案也很好。)

strace按原样还不够,过滤 Unix 域套接字 I/O 并不简单。 A代理使用socat或类似也不适合,因为目标是对现有开放程序进行被动分析。

如何获取可在 Wireshark 中用于分析的数据包捕获?示例协议应用程序是 X11(Xorg,我当前的应用程序)和 cURL/PHP (HTTP)。我见过一个CONFIG_UNIX_DIAGLinux内核中的选项,这个有什么用吗?

答案1

从 Linux 内核 v4.2-rc5 开始,无法直接使用 libpcap 使用的接口进行捕获。 libpcap 使用 Linux 特定的AF_PACKET(别名PF_PACKET)域,仅允许您捕获通过“的数据”网络设备“(例如以太网接口)。

没有用于从AF_UNIX套接字捕获的内核接口。标准以太网捕获具有带有源/目标等的以太网标头。Unix 套接字没有这样的假标头,并且链路层标头类型注册表没有列出与此相关的任何内容。

数据的基本入口点是unix_stream_recvmsgunix_stream_sendmsgfor SOCK_STREAMSOCK_DGRAM并且SOCK_SEQPACKET具有类似命名的函数)。数据被缓冲在sk->sk_receive_queueunix_stream_sendmsg功能,没有任何代码最终导致调用tpacket_rcv功能用于数据包捕获。看osgx 对 SO 的分析有关一般数据包捕获内部结构的更多详细信息。

回到关于AF_UNIX套接字监控的原始问题,如果您主要对应用程序数据感兴趣,您有一些选择:

  • 被动(也适用于已经运行的进程):
    • 使用strace并捕获执行 I/O 的可能系统调用。有很多read,,,,,,pread64还有readv更多preadv......recvmsg看看@斯特凡·查泽拉斯的示例xterm。这种方法的缺点是您首先必须找到文件描述符,然后仍然可能会错过系统调用。通过 strace,您可以使用-e trace=file其中的大多数(pread仅由 介绍-e trace=desc,但大多数程序可能不将其用于 Unix 套接字)。
    • 中断/修改unix_stream_recvmsg, unix_stream_sendmsg(或unix_dgram_*unix_seqpacket_*)核心并在某处输出数据。您可以使用 SystemTap 来设置此类跟踪点,这里是一个例子监视传出消息。需要内核支持和调试符号的可用性
  • 活动(仅适用于新进程):

    • 使用也可以写入文件的代理。您可以自己编写一个快速多路复用器,或者破解类似的东西,也输出 pcap (注意限制,例如AF_UNIX可以传递文件描述符,AF_INET不能传递文件描述符):

      # fake TCP server connects to real Unix socket
      socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CONNECT:some.sock
      # start packet capture on said port
      tcpdump -i lo -f 'tcp port 6000'
      # clients connect to this Unix socket
      socat UNIX-LISTEN:fake.sock,fork TCP-CONNECT:127.0.0.1:6000
      
    • 使用专用应用程序代理。对于 X11,有 xscope (git,手动的)。

不幸的是,建议的CONFIG_UNIX_DIAG选项在这里也没有帮助,它只能用于收集统计数据,而不能获取实时数据(请参阅linux/unix_diag.h)。

不幸的是,目前对于生成 pcap 的 Unix 域套接字还没有完美的跟踪器(据我所知)。理想情况下,libpcap 格式的标头包含源/目标 PID(如果可用),后跟可选的附加数据(凭据、文件描述符),最后是数据。如果缺乏这一点,最好的办法就是系统调用跟踪。


其他信息(对于感兴趣的读者),这里有一些回溯(通过 GDB 中断unix_stream_*rbreak packet.c:.、QEMU 中的 Linux 和主线 Linux 4.2-rc5 上的 socat 获得):

# echo foo | socat - UNIX-LISTEN:/foo &
# echo bar | socat - UNIX-CONNECT:/foo
unix_stream_sendmsg at net/unix/af_unix.c:1638
sock_sendmsg_nosec at net/socket.c:610
sock_sendmsg at net/socket.c:620
sock_write_iter at net/socket.c:819
new_sync_write at fs/read_write.c:478
__vfs_write at fs/read_write.c:491
vfs_write at fs/read_write.c:538
SYSC_write at fs/read_write.c:585
SyS_write at fs/read_write.c:577
entry_SYSCALL_64_fastpath at arch/x86/entry/entry_64.S:186

unix_stream_recvmsg at net/unix/af_unix.c:2210
sock_recvmsg_nosec at net/socket.c:712
sock_recvmsg at net/socket.c:720
sock_read_iter at net/socket.c:797
new_sync_read at fs/read_write.c:422
__vfs_read at fs/read_write.c:434
vfs_read at fs/read_write.c:454
SYSC_read at fs/read_write.c:569
SyS_read at fs/read_write.c:562

# tcpdump -i lo &
# echo foo | socat - TCP-LISTEN:1337 &
# echo bar | socat - TCP-CONNECT:127.0.0.1:1337
tpacket_rcv at net/packet/af_packet.c:1962
dev_queue_xmit_nit at net/core/dev.c:1862
xmit_one at net/core/dev.c:2679
dev_hard_start_xmit at net/core/dev.c:2699
__dev_queue_xmit at net/core/dev.c:3104
dev_queue_xmit_sk at net/core/dev.c:3138
dev_queue_xmit at netdevice.h:2190
neigh_hh_output at include/net/neighbour.h:467
dst_neigh_output at include/net/dst.h:401
ip_finish_output2 at net/ipv4/ip_output.c:210
ip_finish_output at net/ipv4/ip_output.c:284
ip_output at net/ipv4/ip_output.c:356
dst_output_sk at include/net/dst.h:440
ip_local_out_sk at net/ipv4/ip_output.c:119
ip_local_out at include/net/ip.h:119
ip_queue_xmit at net/ipv4/ip_output.c:454
tcp_transmit_skb at net/ipv4/tcp_output.c:1039
tcp_write_xmit at net/ipv4/tcp_output.c:2128
__tcp_push_pending_frames at net/ipv4/tcp_output.c:2303
tcp_push at net/ipv4/tcp.c:689
tcp_sendmsg at net/ipv4/tcp.c:1276
inet_sendmsg at net/ipv4/af_inet.c:733
sock_sendmsg_nosec at net/socket.c:610
sock_sendmsg at net/socket.c:620
sock_write_iter at net/socket.c:819
new_sync_write at fs/read_write.c:478
__vfs_write at fs/read_write.c:491
vfs_write at fs/read_write.c:538
SYSC_write at fs/read_write.c:585
SyS_write at fs/read_write.c:577
entry_SYSCALL_64_fastpath at arch/x86/entry/entry_64.S:186

tpacket_rcv at net/packet/af_packet.c:1962
dev_queue_xmit_nit at net/core/dev.c:1862
xmit_one at net/core/dev.c:2679
dev_hard_start_xmit at net/core/dev.c:2699
__dev_queue_xmit at net/core/dev.c:3104
dev_queue_xmit_sk at net/core/dev.c:3138
dev_queue_xmit at netdevice.h:2190
neigh_hh_output at include/net/neighbour.h:467
dst_neigh_output at include/net/dst.h:401
ip_finish_output2 at net/ipv4/ip_output.c:210
ip_finish_output at net/ipv4/ip_output.c:284
ip_output at net/ipv4/ip_output.c:356
dst_output_sk at include/net/dst.h:440
ip_local_out_sk at net/ipv4/ip_output.c:119
ip_local_out at include/net/ip.h:119
ip_queue_xmit at net/ipv4/ip_output.c:454
tcp_transmit_skb at net/ipv4/tcp_output.c:1039
tcp_send_ack at net/ipv4/tcp_output.c:3375
__tcp_ack_snd_check at net/ipv4/tcp_input.c:4901
tcp_ack_snd_check at net/ipv4/tcp_input.c:4914
tcp_rcv_state_process at net/ipv4/tcp_input.c:5937
tcp_v4_do_rcv at net/ipv4/tcp_ipv4.c:1423
tcp_v4_rcv at net/ipv4/tcp_ipv4.c:1633
ip_local_deliver_finish at net/ipv4/ip_input.c:216
ip_local_deliver at net/ipv4/ip_input.c:256
dst_input at include/net/dst.h:450
ip_rcv_finish at net/ipv4/ip_input.c:367
ip_rcv at net/ipv4/ip_input.c:455
__netif_receive_skb_core at net/core/dev.c:3892
__netif_receive_skb at net/core/dev.c:3927
process_backlog at net/core/dev.c:4504
napi_poll at net/core/dev.c:4743
net_rx_action at net/core/dev.c:4808
__do_softirq at kernel/softirq.c:273
do_softirq_own_stack at arch/x86/entry/entry_64.S:970

答案2

我写了一个工具捕获并转储 UNIX 域套接字流量。它用于bpf/kprobe探测内核功能unix_stream_sendmsg并将流量转储到用户空间。

该工具取决于bcc,所以你需要bcc先安装。

运行示例:

$ sudo ./sockdump.py /var/run/docker.sock # run "docker ps" in another terminal
>>> docker[3412] len 83
GET /_ping HTTP/1.1
Host: docker
User-Agent: Docker-Client/18.06.1-ce (linux)

>>> dockerd[370] len 215
HTTP/1.1 200 OK
Api-Version: 1.38
Docker-Experimental: false
Ostype: linux
Server: Docker/18.06.1-ce (linux)
Date: Tue, 25 Sep 2018 07:05:03 GMT
Content-Length: 2
Content-Type: text/plain; charset=utf-8

OK
...

相关内容