Как пассивно захватывать из доменных сокетов Unix (мониторинг сокетов AF_UNIX)?

13

Перехват TCP / IP и UDP может быть сделан с использованием tcpdump/ dumpcapи создает файл pcap / pcapng, который может быть передан в Wireshark для дальнейшего анализа. Существует ли подобный инструмент для именованных доменных сокетов Unix? (Общее решение, которое работает для абстрактных сокетов, тоже было бы неплохо.)

strace«как есть» недостаточно, фильтровать ввод-вывод для доменных сокетов Unix непросто. Использование прокси socatили подобное также не подходит, так как целью является пассивный анализ для существующих открытых программ.

Как я могу получить захват пакета, который я могу использовать в Wireshark для анализа? Примерами протокольных приложений являются X11 (Xorg, мое текущее приложение) и cURL / PHP (HTTP). Я видел CONFIG_UNIX_DIAGвариант в ядре Linux, это какой-то пользы?

Lekensteyn
источник
1
См. Захват трафика протокола X11
Стефан Шазелас
@ StéphaneChazelas Спасибо, но поскольку Xorg был запущен с -nolisten tcp, нет TCP-сокета. Если все не получится, я, вероятно, вернусь к использованию xscope или вашего хитрого трюка strace + text2pcap. Я все еще был бы заинтересован в общем захвате сокетов Unix (только для данных, а не данных побочных каналов).
Лекенштейн
Помимо strace, вы также можете посмотреть на audd и systemtap.
Стефан Шазелас
systemtap выглядит почти как взлом GDB, но на уровне ядра. Не знаю насчет аудита, я нашел только хук LSM, который проверял, разрешено ли вам чтение / запись. (Я сейчас
копаюсь в

Ответы:

12

Начиная с ядра Linux v4.2-rc5, захват напрямую невозможен с использованием интерфейсов, используемых libpcap. libpcap использует специфичный для Linux домен AF_PACKET(псевдоним PF_PACKET), который позволяет собирать данные только для данных, проходящих через « сетевое устройство » (например, интерфейсы Ethernet).

Нет интерфейса ядра для захвата из AF_UNIXсокетов. Стандартные захваты Ethernet имеют заголовок Ethernet с источником / назначением и т. Д. У сокетов Unix такого поддельного заголовка нет, а реестр типов заголовков канального уровня не содержит ничего связанного с этим.

Основные точки входа для данных - unix_stream_recvmsgи unix_stream_sendmsgдля SOCK_STREAM( SOCK_DGRAMи SOCK_SEQPACKETимеют функции с одинаковыми именами). Данные буферизируются внутри sk->sk_receive_queueи внутри unix_stream_sendmsgфункции , нет кода, который в конечном итоге приводит к вызову tpacket_rcvфункции для захвата пакетов. Посмотрите этот анализ с помощью osgx для SO для получения более подробной информации о внутренностях захвата пакетов в целом.

Возвращаясь к первоначальному вопросу о AF_UNIXмониторинге сокетов, если вы в основном заинтересованы в данных приложения, у вас есть несколько вариантов:

  • Пассивный (также работает для уже запущенных процессов):
    • Использование straceи захват возможных системных вызовов, которые выполняют ввод / вывод. Есть много из них, read, pread64, readv, preadv, recvmsgи многих других ... См @ Stéphane Chazelas примера для 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 , manual ).

Предложенная CONFIG_UNIX_DIAGопция, к сожалению, здесь также бесполезна, ее можно использовать только для сбора статистики, а не для получения данных в реальном времени по мере их прохождения (см. Linux / unix_diag.h ).

К сожалению, в настоящее время нет идеальных трассировщиков для доменных сокетов Unix, которые производят pcaps (насколько мне известно). В идеале должен быть формат libpcap, в котором есть заголовок, содержащий PID source / dest (если он доступен), за которым следуют необязательные дополнительные данные (учетные данные, дескрипторы файлов) и, наконец, данные. Не имея этого, лучшее, что можно сделать, - это отслеживание системных вызовов.


Дополнительная информация (для заинтересованных читателей), вот некоторые следы (полученные при взломе GDB unix_stream_*и rbreak packet.c:.Linux в QEMU и socat в основной Linux 4.2-rc5):

# 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
Lekensteyn
источник
Кстати, если вы прочитали kristrev.github.io/2013/07/26/… и увидели инструкции по отслеживанию уведомлений о соединении через netlink и задались вопросом, может ли диагностика обеспечить анализ пакетов, ответ все равно - нет . Эта диагностика предоставляет статистику через опрос, а не в реальном времени.
Лекенштейн
9

Я написал инструмент для захвата и сброса трафика сокетов доменов 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
...
mechpen
источник