Linux. Глубокое понимание работы системы. Часть 2 — трассировка стандартных и сетевых служб

UPD: Запись перенесена из старого блога, опубликована в 2015 году.

Предисловие

В дополнение к предыдущей статье, хотел бы выложить вот эту ссылочку на статью в журнале «Хакер» по схожей тематике. Почитайте на досуге.
Итак, мы уже посмотрели как работает обычная программа, от чего она зависит, как работает с системными и библиотечными вызовами, какие у нас есть инструменты анализа и поиска информации. Теперь пришло время копнуть дальше.

Мы продолжим в том же ключе и введем еще некое колличество средств наблюдения, чтобыо пнимать не только из каких компонент состоит ОС  и как они взаимодействуют между собой, но и в каких целях, а так же как они взаимодействуют с другими частями ос- демонами, процессами и пр.
Какими каналами связи пользуются программы, например при обращении с ядром, ФС или соседями?
Они пользуются такими объектами как например файл, каталог, сокет, именованый канал и т.д.

Одной из основных сущностей в Linux и Unix системах, предоставляющих различные сервисы и виды доступа является такое понятие как файл. В nix системах действует парадигма, смысл которой состоит в следующем- все есть файл или может быть отображено с помощью файлов. Отсюда следует логичный вывод что важными инструментами для исследования у нас будут является инструменты работы с файлами.

Инструменты

1. lsof — (от англ. LiSt of Open Files) — утилита, служащая для вывода информации о том, какие файлы используются теми или иными процессами.

Эта программа будучи запущенной показывает открытые в системе файлы, более того -показывает какие процессы, в каких целях, от лица каких пользователей, какие файлы открывают.

Самое простое — получить список процессов в системе, взять PID какого-нибудь процесса и посмотреть для него, какие файлы он открыл. Для примера просто введем ps, получив список програм, запущенных в данном сеансе

Видим что у нас запущен процесс bash с идентификатором 7594. Посмотрим командой lsof -p *PID* список файлов, которые он открыл

Видим, что процесс 7594 запущен от лица текущего пользователя (akella — т.е. меня)

Что же мы видим? Смотрим на столбец FD, означающий File Descriptor и столбец TYPE, означающий тип открытого объекта.
Видим что bash открыл набор регулярных ( обычных — REG) и символьных (CHR) файлов и каталогов (DIR), находящихся на устройстве 8.5 и 136,5 ( столбец DEVICE), с определенными размерами ( столбец SIZE), с такими то индексными дистрикпторами (столбец NODE) и именем в пределах своей файловой системы (NAME). И что мы видим в итоге то?

Рассмотрим последние строчки. У любого уважающего себя процесса в Unix всегда открыты три файловых дескриптора — 0,1 и 2. В данном случае с буковкой u что означает что они открыты и на запись и на чтение. Это стандартные потоки ввода, вывода и сообщений об ошибках.
Мы видим, что они у нас связаны с псевдотерминалом /dev/pts/5, эмулирующий из себя личный терминал с которым мы якобы работаем ( на самом деле работаем с псевдотерминалом в графической оболочке)
Тут стоило бы упомянуть про то что это псевдотерминал (pt) slave, который соединяется с матер-мультиплексором /dev/ptmx, но лучше о том что это и зачем — прочитайте вот тут.

Итого, простое наблюдение за открытыми файлами нам уже позволяет узнать много нового и открывать для себя факты, связанные с работой ос.

Вернемся к выводу lsof. Итак, кроме дескрипторов потока ввода вывода и устройств псевдотерминалов, мы видим в столбце FD признак mem — файлы, отображенные в память

обычные регулярные файлы библиотек просто отображены в память + еще файл с регулярными данными ( не библиотеки) — т.е. их просто считали с диска и загрузили в память процесса. Но на  самом деле это не так- их просто отобразили, а попытка процесса считать или записать что то в это отображение на самом деле приведет к операциям чтения или записи на диске.
Что мы видим еще? Запись txt в колонке FD говорит о том, что сегмент текста  , т.е. имя исполняемого файла был взят из /bin/bash
Что корневой каталок rtd является на самом деле корневым /, и что текущий каталог cwd у нас /home/akella
.
Все то же самое можно делать для других различных процессов. Давайте поставим интересный эксперимент и пронаблюдаем за одним из главных процессов в системе- процессом init

sudo lsof -p 1

sudo нам нужен для того, чтобы посмотреть подробно информацию о чужом процессе. Ведь init запустили не мы и чтобы посмотреть детали его работы нам потребуются права root.

Что можем сразу отметить:

  • корневой и текущий каталог совпадают — это корень фс /
  • сегмент текста- кто и откуда запущен — /sbin/init
  • куча необходимых библиотек
  • дескрипторы потоков ввода, вывода и ошибок (0,1,2) прикреплены к «великому ничто» — устройству /dev/null, т.е.  процесс не общается с пользователем, а значит является демоном,
  • 3-им потоком на чтение и 4 на запись открыт некий  канал (pipe) — возможно кстати и не именованный, у него вроде нет имени
  • кроме того, видим что 7 — открыт сокет в пространстве unix, т.е. на самом деле это своего рода файл.
  • 10 открыт  обычный файл dbus.log  ( и еще лог файлы можно увидеть. Т.е. видим что это регулярные файлы — скорее всего текстовые, куда пишутся логи)

 

  • видим, что открыт псевдотерминал мультиплексор /dev/ptmx, а значит он запускает какие-то программы, требующие работы с терминалами или в среде с терминалами. Какие- мы можем судить по файлам логов
  • видим что мультиплексор у нас открыт не один и если посмотрим число открытых слейвов и псевдотерминалов tty, то поймем, кто скорее всего их наплодил ( мы бы обычным входом в систему столько не сделали)

До сих пор, мы запускали lsof с ключом -p, чтобы посмотреть, какие файлы открыл конкретный процесс. Однако, в большинстве случаев ее используют по другому. Она используется lfos * имя файла* — увидим, какими процессами он открыт. Например посмотрим, кто у нас открыл /dev/ptmx

В конце мы видим наш gnome-terminal — графическая псевдотерминальная программа, через которую я и демонстрирую все эти опыты.

Рассмотрим другой пример — посмотрим на вывод lsof относительно какого-нибудь псевдотерминала.

Когда мы в командном интерпретаторе выполняли команду sudo, она наследовала все его свойства — в том числе текущий каталог и список открытых файлов, потоки ввода вывода, идентификатор пользователя и пр.
что мы видим — команда bash запускает sudo с эскалацией полномочий до root, а та в свою очередь запускается lsof с эскалированными полномочиями и все они унаследовали стандартные потоки вода вывода 0,1,2 друг от друга и открытый файл pts/5.  А изначально все это было запущено процессом gnome-terminal, который нам все это и показывает.

А как мы узнали что нам надо посмотреть именно pts/5? Просто спросив у системы на каком псевдотерминале мы работаем сейчас

Посмотрим теперь, кто у нас использует другие псевдотерминалы

Мы видим PID идентификатор некоего процесса 1609 и можем явно взглянуть на него

Увидеть, откуда он запущен, возможно даже узнать его родителя (PPID -Parent PID):

Возможно будет удобнее вывести все это в виде дерева:

ps uxf | less

и с помощью ключа / организовать поиск по PID 1609

Увидим наш процесс, увидим что им запущено и если подняться немного вверх по дереву, поймем что родителем является init ( собственно по PPID = 1 это и так понятно).

Итого, программа lsof показывает нам информацию об использовании файлов процессами и соответственно мы узнаем о важных системных файлах и способах взаимодействия с ними.

Для дальнейшей демонстрации нам необходимо осознанно выбрать из списка, приведенного на предыдущем шаге процесс, который использует не просто файлы или сокеты, являющиеся файлами 9имею ввиду файловые сокеты), а задействует сетевые сокеты ( которые тоже являются на поверку файлами н ов силу своих свойств), которые работают в совершенно другом пространстве — пространстве IP адресов и портов.

Выберем процесс, являющийся сетевой ( клиентской либо серверной) службой, либо ее частью. Для этого обратимся за помощью к еще одной простой программе / утилите.

2. netstat — команда unix и Windows, которая показывает содержимое различных структур данных, связанных с сетью, в различных форматах в зависимости от указанных опций.

Сетевая статистика, демонстрируемая этой утилитой, запущенной без опций нам показывается как

активные соединения с интернетом, т.е. сокетов интернет ( средств взаимодействия интернет) и соединений  Unix ( надо пролистать чуть ниже)

Unix сокеты файловые, нужны для общения разных процессов сетевым или точнее псевдосетевым образом на одном узле, а вот интернет нужны для общения процессов на разных узлах. Причем у них абсолютно одинаковая природа.Более того, природа файловая, т.е. в Unix запись информации в файл на диске, передача информации через сокет являющийся файлом на этом узле и передача информации процессу на другом узле через сетевой сокет, который тоже является файлом однотипна.

Но нам пока файловые не интересны, поэтому остановимся на сокетах интернет. Конкретно нас интересуют TCP и UDP сокеты. И мы попросим их показать.  По умолчанию нам покажут активные сетевые сокеты — инициатором которых мы являемся. Или клиентские ( безсерверный)

netstat -tu

Если мы хотим увидеть  серверные, то необходимо добавить ключ -l (Listening), тогда нам покажут слушающие сокеты инициатором соединения которых мы не являемся. Добавив ключ -p мы увидим процессы, их инициирующие. Но для корректного отображения инфомрации о процессах, нам необходимо иметь права администратора. Тогда команда принимает вид

sudo netstat -tulp

 

 

Видим, какие процессы понаоткрывали TCP/TCPv6 или UDP сокеты. В TCP сокетах на порту Ipp ( если хотите узнать номер, добавьте флаг -n к запуску netstat), у нас работает демон cups.

Обратившись к документации ( man cupsd), мы уже увидим что это такое.
Мы можем спросить

which cupsd

Из какого файла он запущен. Используя pgerp мы можем узнать PID в котором он выполняется ( но мы итак это видели в netstat) и с помощью lsof -l узнать какие файлы он открыл

Среди прочих ( библиотеки и файлы журналов), мы увидим сокеты в слушающем состоянии по IP протоколу 4 и 6 версии. Т.е. то же что мы видели в команде netstat но просматривая открытые файлы. Почему так? потому, что повторюсь — сетевые сокеты это файлы.

Тут все делается через файлы=)
Далее в своем исследовании вспоминаем предыдущую статью и используя dpkg с ключами -s и -S находим пакет из которого была установлена программа и описание пакета, где мы можем найти ссылку на страничку разработчика.

А теперь на секунду остановимся и подумаем- взяв первый попавшийся открытый (серверный) порт, мы дошли до страницы разработчика сервиса, открывшего этот порт.
Что нам еще как администратору дает? Очень просто- увидев открытые порты на свежеустановленном сервере, мы выясняем что же там работает, решаем — нужно нам это или не нужно и узнав пакеты, из которых ставилось это ПЛ, удаляем ненужные сервисы, тем самым, повышая безопасность и отказоустойчивость системы.

Я на своем десктопе выпиливать cups не буду, он мне нужен. Но на тестовой машине Вы можете попробовать а потом проверить- действительно ли после удаления порт закрылся и если не закрылся то почему и кто его по прежнему слушает.

Теперь давайте посмотрим, что же у нас по этим самым сокетам происходит. Сделать это можно разным способом. Для начала выберем себе следующую жертву. Воспользуемся netstat еще раз:

sudo netstat -tulpn

Жертвой станет dnsmasq. Забегая вперед, скажу что в данном случае он выполняет у нас роль локального DNS прокси.

Итак, разберем его. Для начала, посмотрим откуда он загрузился:

lsof -p 1783 | grep txt

Видим, что из программы /usr/bin/dnsmasq. Обратимся к пакетному менеджеру (привожу на скриншоте сразу всю последовательность действий, т.к. их описание уже есть в предыдущей статье по теме):

Вот теперь можно без отступлений повторить что этот пакет для нас выполняет роль DNS кеширующего прокси ( см строку описания).
В выводе netstat мы видели, что он открыл 53 порт для внутреннего интерфейса с адресом 127.0.0.1

С помощью lsof посмотрим теперь какие файлы- сокеты он открыл. Видим что открыты два — TCP и UDP сокет — два файловых дескриптора.

И теперь, если мы протрассируем системные вызовы, а обмен и получение данных через эти файловые дескрипторы идет через ядро — когда приходит пакет, он поступает на сетевую карту, в драйвер, потом в стек TCP/IP, потом маршрутизируется — направляется в этот порт (  в нашем случае правда про сетевую карту речь не идет т.к. все происходит на локалхосте), мы опять все это можем увидеть просто наблюдая за системными вызовами нашего процесса с PID 1783.

Правда нам надо прицепиться к уже исполняющемуся процессу. Для этого запустим:

sudo strace -p 1783

 

  • Мы видим системный вызов select, который ожидает, прослушивая дескрипторы 3-7 — не поступят ли данные.
  • Далее из дескриптора 4 (UDP сокет по адресу 127.0.0.1) он получает сообщение запрос  — исходя из строки «…\0\0\4imv4\2vk\3com\0\0…» — речь идет явно про Вконтакте. Клиентский порт с которого пришел запрос 50696. Пришло 42 байта.
  • Далее он открывает новый сокет, привязало к нему клиентский порт(вызов bind, порт 20592, дескриптор 10)
  • и перенаправляет запрос реальному dns серверу в локалке на адрес 172.30.1.1 на порт 53 через 10 сокет.
  • вновь ждет прослушивая дескрипторы select-ом
  • получает информацию от DNS сервера и передает ее локальному процессу, закрывает дескриптор 10 ( порт через который он отправлял-получал информацию от внешнего DNS) и снова уходит в ожидание.

Если Вы захотите подробно узнать что делается в системных вызовах bind, select, sendto и т.д. — вводите man 2 *имя вызова* и получите подробную информацию, почти из первых рук.

Отлично — мы видим запросы, но нам бы хотелось узнать более подробно, что же происходит там, внутри, а вернее подробней взглянуть на само сетевое взаимодействие. Можно взять спецификацию DNS и посмотреть что означает тот или иной байт запроса (наш трассировщик уже показал нам в виде строк все что смог сам расшифровать т.к. по сути DNS это бинарный протокол). Однако для нас уже существуют инструменты, позволяющие все это расшифровывать и заглядывать внутрь этих самых пакетов. Например простейшее средство — tcpdump.

3. tcpdump  — утилита UNIX (есть клон   для Windows), позволяющая перехватывать и анализировать сетевой трафик, проходящий через компьютер, на котором запущена данная программа. Шпаргалка для нуждающихся.

Рассмотирм как мы будем взаимодействовать:

  • клиентский запрос прилетает на адрес 127.0.0.1 интерфейс lo
  • его ретранслируют через интерфейс eth0 на DNS сервер локальной сети 172.30.1.1
  • от сервера на eth0 пришел ответ
  • он был ретранслирован на lo

 

  • tcpdump запущенный на интерфейсе lo будет наблюдать за программой ретранслятором, отслеживая клиентский запрос и ответ на него.
  • На eth0 увидим как ретранслятор передает все на сервер и получит ответ на запрос от него.
Пробуем захватывать пакеты на нашем lo интерфейсе. Параллельно во второй вкладке терминала, я с помощью утилиты dig попросил предоставить мне А записи для доменного имени yandex.ru, чтобы несколько разнообразить эксперимент.

Итак, мы видим запрос к ретранслятору об адресах для А записи imv4.vk.com (да, яндекс не пригодился- вконтакте везде вылезет) и ответ от него, несущий два Ip адреса:

23:40:24.880567 IP localhost.56011 > workstation.domain: 51898+ A? imv4.vk.com. (29)
23:40:24.881695 IP workstation.domain > localhost.56011: 51898 2/0/0 A 95.213.4.211, A 95.213.4.212 (61)

Но согласитесь,  скудновато выглядит? Добавим ключ -v, делающий вывод более подробным, ключ -А, принудительно заставляющий его транслировать все что можно в ASCII и в запросе про яндекс укажем тип записи не А, а ANY. Получим следующий вывод:

Жаль, видим много пропусков. Да, те строки что он смог он выковырял. Посмотрим, что же происходит в виде 16-тиричных дампов. Заменим ключ -А на -х:

Как уже говорил ранее, можно брать в руки спецификации DNS и идти парсить вручную. Удобно если Вы решили подробно изучить его работу. Тут Вы видите полный дамп а не краткие выдержки как в случае с strace.

Придем к компромиссному варианту- постараемся увидеть и настоящие данные, и то что можно вытащить в виде читаемого текста. Заменим -х на -Х:

Вот, видим явный запрос про yandex.ru и явный ответ.
Вот так мы уже можем наблюдать, как работают программы и какие конкретные данные они передают по сети.

Для интереса можете попытаться перехватить icmp трафик (ping) и посмотреть чем на самом деле они заполнены. И чем заполняются при принудительном увеличении пакета icmp.

Зачем нам все это нужно? Во первых, теперь мы можем закапываться в недра системы на столько глубоко, на сколько нам необходимо и разбираться в тонкостях ее устройства и работы.
Во вторых, мы можем наблюдать за нормальной работой системы,  строя для себя эталонную модель поведения, чтобы в будущем облегчить задачу поиска неисправностей.

Теперь познакомимся с чуть более сложным но при этом более удобным средством анализа, которое позволит нам декодировать эти сообщения полностью.

4. tshark — это консольная утилита из состава Wireshark, предназначенная для захвата и анализа сетевых пакетов. С помощью этой утилиты можно выполнять захват пакетов в сети, сохранять пакеты в файле, просматривать ранее сохраненные файлы с информацией о сетевых пакетах. Захват пакетов в утилите TShark выполняется в формате libpcap, который используется в tcpdump и в других утилитах для захвата и анализа сетевых пакетов.

По умолчанию в системе она не стоит, поэтому я добавил ее к себе с помощью aptitude.

Запустим его на интерфейсу lo и сделаем dig-ом запрос про яндекс.

sudo tshark -i lo -V

Его вывод получился на столько подробным, что мне пришлось разбить его на 3 скриншота, зато он расшифровал абсолютно все содержимое и вывел в удобочитаемой форме.

Т.е. для нас он сделал не просто дамп трафика (а tcpdump занимается именно снятием дампа с частичной расшифровкой), а расшифровал все заголовки, команды, флаги и содержимое. Сравните с выводом от tcpdump — тот момент когда возвращается ответ с информацией об записях типа А.
А теперь сравним:

Он сообщил что в запросе, спрашивалось про yandex.ru, тип А — адрес. Ав ответе что есть три записи А и адреса их вот такие вот.

Замечательный инструмент?
Но все бы хорошо, да теперь информации столь много, что разбираться в ней вручную сложно. tshark является консольной версией очень популярной утилиты Wireshark, о коротой стоит сказать пару слов.

5. wireshark — программа-анализатор трафика для компьютерных сетей Ethernet и некоторых других. Имеет графический пользовательский интерфейс.  Незаменимый инструмент в руках сетевиков, админов и хакеров ( в хорошем смысле этого слова).

Описывать так же подробно работу с ним я не вижу смысла. На хабре есть замечательная статья по его использованию, к тому-же, ранее в одной из статей про исследование netbios я уже писал о нем.

Идея в том, что это очень полезный и удобный инструмент. Однако он бесполезен на сервере, не имеющем gui. Однако есть удобный изящный выход из ситуации  -с помощью tshark или tcpdump Вы можете собрать и сохранить дамп трафика, после чего скачать его с сервера себе на машину (например по scp) и открыв Wireshark, проанализировать его в удобном графическом интерфейсе.

На этом вторая часть закончена. Успехов в Ваших изысканиях!

П.С. Данная статья ( и последующие аналогичные) подготовлена на основе конспекта лекций Кетова Дмитрия Владимировича. От себя я добавил редактуру, несколько измененный стиль изложения, свои правки и комментарии.