Экзекуция по протекции
АрхивПосле такого огромного и сложного проекта, как LaGrande, пара скромных строчек в технической документации по технологии AMD64, посвященных улучшению аппаратной защиты виртуальной памяти, осталась практически незамеченной.
После такого огромного и сложного проекта, как LaGrande, пара скромных строчек в технической документации по технологии AMD64, посвященных улучшению аппаратной защиты виртуальной памяти, осталась практически незамеченной.
Однако несколько недель назад славная компания (в связи с приближающимся выпуском Service Pack 2 для Microsoft Windows XP, который позволит полноценно использовать эту технологию) напомнила о себе очередным пресс-релизом (см., например, www.terralab.ru/news/2004/ 1/15/44430). К сожалению, Intel LaGrande Technology (LT) явно затмевает AMD Execution Protection (EP): в Сети множество обзоров, посвящено первой и практически ни одного — второй. Даже на сайте самой AMD с трудом удалось найти информацию об Execution Protection! А ведь на практике EP зачастую оказывается полезнее LT.
Задачи у этих технологий совершенно разные: Execution Protection защищает от непосредственных атак на приложение (в том числе — любых сетевых атак), а LaGrande — от атак через ядро операционной системы. По известному принципу, гласящему что «любая программа содержит хотя бы одну ошибку», первому типу атак подвержены и Unix, и Windows. Более того — это едва ли не единственный реальный способ атаки по Сети, так что неудивительно, что только такие их типы и становятся «массовыми». Судите сами: практически все выпускаемые патчи устраняют ошибки именно подобного рода. «Переполнение буфера» сегодня является второй по популярности1 уязвимостью, используемой вирусами для своего распространения, — достаточно вспомнить хотя бы эпидемию MSBlast. По личному опыту авторов, незащищенная машина, подключенная к Интернету, «подхватывала инфекцию» в течение получаса! Причем если уровень компьютерной грамотности обычного пользователя потихоньку растет, а доверчивость — пропорционально уменьшается, то от проникающего тихой сапой вируса спасает разве что еженедельное обновление ОС и сетевых программ.
Не стоит забывать и о том, что LT мало помогает в плане защиты Unix-систем, чего не скажешь об Execution Protection. Наконец, EP, будучи неотъемлемой частью архитектуры AMD64, присутствует во всех процессорах Athlon 64 и Opteron и не требует переделки программ — только незначительной доработки операционной системы. То есть пользователю, выбравшему 64-разрядные процессоры от AMD, остается или обновить ОС (установить SP2 для Windows и соответствующие обновления для Unix-систем), или перейти на 64-битные версии ОС (в них EP поддерживается изначально). А LaGrande появится, по самым оптимистическим прогнозам, лишь через год и потребует принципиально новых программ и операционной системы. Все это отнюдь не умаляет достоинств LT — просто технология ориентирована больше на «корпоративного» пользователя, который вовремя ставит заплатки, защищается антивирусами и файрволлами, применяет лишь узкий круг специализированного ПО и страдает скорее от хакеров, нежели от вирусов. Вот ему-то и понадобится защита от копирования, привязка к конкретной машине, механизмы надежной аутентификации, шифрования данных и прочие характерные черты LaGrande (в общем-то, скорее «вредные» для юзера домашнего, поскольку они позволяют организовать надежную защиту копирайта, а то и вовсе ограничить использование «несертифицированных» программ).
EP, правда, тоже не следует переоценивать — она защищает только от одного класса атак (типа «переполнения буфера»), хотя и самого многочисленного — более 80% от общего числа, судя по оценкам специалистов Microsoft. Кроме того, Execution Protection не дает полноценной защиты и от переполнения буфера — вредоносный код по-прежнему будет выводить приложение из строя, просто сделать при этом еще что-либо он не сможет (то есть ставить заплатки к программам все же придется).
Надеемся, вы убедились в безусловной полезности Execution Protection. А чтобы понять, как она работает и для чего применяется, придется немного «покопаться» в устройстве современных процессоров.
1 Первое место по-прежнему держат пользователи, самостоятельно запускающие вредоносные программы.
Бит, перевернувший мир
Что вообще может измениться от добавления в процессор одного-единственного бита? Да, именно так — технология Execution Protection заключается лишь в добавлении специального флага NX к стандартным флагам страниц виртуальной памяти2. Звучит немного заумно, но на самом деле все довольно просто.
Чтобы разобраться в сути EP, начнем с азов и сперва рассмотрим, что такое виртуальная память и для чего она нужна. Пусть у нас есть операционная система и «населяющие» ее программы (приложения). Программы можно условно разделить на служебные, работающие незаметно для пользователя в фоновом режиме, и пользовательские (то есть «заметные» пользователю), причем одна и та же запущенная программа может запускать для себя множество вспомогательных программ. Процессор всего этого «не знает» — он работает с процессами. И служебные, и пользовательские программы, да и сама операционная система — все это процессы. Если открыть (по Ctrl+Alt+Del) Task Manager в операционных системах семейства NT, то можно увидеть разницу между прикладным приложением и процессом: посмотрите на скриншоты — запущено всего три задачи (задачами MS Windows называет приложения, с которыми может непосредственно работать пользователь) и целых 42 процесса. Какие-то из них обеспечивают работу пользовательских задач (winword.exe, far.exe), какие-то — фоновые приложения (ccapp.exe, proxyplus.exe), а какие-то удовлетворяют потребности других программ (rundll32.exe).
Строго говоря, процессор работает даже не с процессами, а с потоками исполнения (threads)3. С точки зрения процессора поток «живет» в оперативной памяти. Вот здесь и начинается самое интересное — в современной системе эта память для каждого процесса индивидуальна. То есть аналогия с офисом глубже, чем может показаться на первый взгляд, — как сотрудники одного подразделения работают в одном помещении и не видят остальные помещения и работающих там людей, так и все потоки одного процесса живут в общей памяти и не видят память других процессов. Получается так, как если бы для каждого процесса у нас был отдельный компьютер, на котором ничего больше не запущено. На самом деле, конечно, и компьютер у нас один, и память одна, просто процессы работают с «ненастоящей» — виртуальной памятью, а процессор на пару с операционной системой при обращении решают, какой адрес в физической памяти соответствует виртуальному. Для этого память «нарезается» страницами — кусочками фиксированного размера (4 Кбайт, 2 Мбайт или 4 Мбайт — в зависимости от выбранного режима), и где-то в память для каждого процесса кладется табличка, в которой задается соответствие между виртуальными и физическими страницами.
Помимо физических адресов виртуальных страниц, туда записывается и кое-какая дополнительная информация — флаги страниц. Эти флаги указывают, что со страницей можно делать, а что нельзя. Можно, например, сделать страницу доступной только для чтения или доступной только операционной системе. Вот мы и добрались до Execution Protection — эта технология просто добавляет в число «стандартных» флагов еще один (NX — Non eXecutive, запрещено исполнение кода из страницы). Зачем он нужен, мы сейчас и обсудим. Пока же проиллюстрируем вышесказанное схемой 1.
Здесь изображено два процесса и их таблицы трансляции виртуальных адресов. Каждый процесс работает со своей виртуальной памятью и ничего не знает о том, где она физически размещается. Например, нулевой странице первого потока соответствует первая страница физической памяти, первой — нулевая, второй — нулевая страница в файле подкачки и т. д. Заметьте, что виртуальная страница не обязательно отображается в страницу физической памяти — принципиально ее можно отобразить куда угодно. Просто если страница отображается в физическую память (во флагах страницы выставлен бит P — Present), то процессор справится с трансляцией самостоятельно и очень быстро. А если такого бита нет, процессору придется звать на помощь операционную систему, которая загрузит нужную страницу в память. Почему-то при упоминании виртуальной памяти часто говорят именно об этой возможности (использовании файла подкачки), однако по значимости ее следовало бы поставить на последнее место.
Без трансляции адресов было бы очень неудобно работать в многозадачной системе. Во-первых, потому, что мы могли бы случайно (или, наоборот, злонамеренно) испортить данные «чужого» процесса, а то и самой операционной системы. То есть любая ошибка в программе приводила бы к краху всей системы. Во-вторых, любому компилятору очень трудно работать, если он не знает заранее, где в памяти будет размещена создаваемая программа. В-третьих… впрочем, не будем забегать вперед. Сейчас нас интересует прежде всего первый пункт, из которого следует, что различные приложения не могут увидеть память друг друга4, и, казалось бы, поэтому хакерская программа не может напрямую, не прибегая к помощи операционной системы, повредить программу пользователя. Однако это не так — программы должны как-то общаться друг с другом, и вот как раз через эти «окна» в системе безопасности и возможна атака.
Execution Protection защищает от наиболее распространенного варианта подобной атаки — «ошибки переполнения буфера». Посмотрим, как организуется такая атака. Для этого нам потребуется чуть подробнее обсудить устройство потока выполнения.
Можно выделить четыре основных «слагаемых» потока. Во-первых, разные вспомогательные данные, используемые операционной системой (номер процесса — PID, указатели на таблицу трансляции виртуальной памяти, на стек потока и многое другое). С создания этой информации и возникает поток. Во-вторых, собственно код потока.
В-третьих, данные, потоком используемые. И в-четвертых — стек потока5. Вот со стеком-то и возникает проблема. В нем обычно сохраняется вспомогательная информация, в том числе довольно «интимного» свойства — точки возврата из вызванных в данный момент подпрограмм6.
Пока все о’кей, но в такой схеме есть одна уязвимость. Поток заполняет стек в обратном порядке — начиная с последней страницы, а данные в оперативную память записываются в порядке прямом. Если процесс запишет в стек слишком много данных, то он сотрет все записанное ранее, в том числе — точку возврата из текущей подпрограммы. Представьте, что наш служащий оставил в стеке место для записи, скажем, срочной информации, поступающей по телефону (буфер данных, на языке программистов). Когда кто-то звонит и что-то сообщает, то сообщение сначала записывается в отведенное место в стеке, а уже потом «переписывается начисто» в данные потока. Но что произойдет, если места для записи поступившей информации не хватает? Человек найдет еще один листик для записи информации, попробует мельче писать, что-то сокращать, на худой конец — сообщит о невозможности записи сообщения и предпримет что-то еще. Примерно так же будут действовать и грамотно написанные программы, однако, к сожалению, велик соблазн написать все «по-простому»7 — и в этом случае поток записывает переданную ему информацию поверх прежних записей.
2 Строго говоря, это даже не отдельная технология, а часть куда более масштабной микроархитектуры AMD64.
3 Представьте себе здание (операционную систему) с множеством офисов, обслуживающих посетителей (пользовательские приложения — «задачи») и внутренние потребности здания. Офис может состоять из нескольких подразделений (процессов) — с одним работает пользователь, остальные обслуживают это подразделение. В нем есть и отдельные подразделения «только для служебного пользования», куда путь пользователю закрыт и с которыми работают только сами офисы. И, наконец, отдельные подразделения состоят из множества сотрудников (потоков исполнения), работающих внутри одного подразделения, но каждый — над своей задачей. Число таких «сотрудников» можно посмотреть в том же диспетчере задач, если включить отображение соответствующего столбца в настройках «Вид/Выбрать столбцы/Счетчик потоков». Мы же пока запомним главное: процессор оперирует потоками, и для каждого потока задано его место работы — процесс.
4 Есть, конечно, специальные средства, позволяющие создать память, «общую» для различных процессов, но на безопасность это не влияет.
5 Это «разделение» легко представить, если продолжить сравнение потока с офисным служащим: вспомогательные данные — это паспорт и пропуск служащего, код — записанные на специальных листочках инструкции о том, что ему нужно сделать, данные — листки с входными и выходными данными, а стек — пустые листы, которые служащий использует для черновиков и пометок о ходе работы.
6 Возвращаясь к нашему примеру: у служащего в инструкциях часто встречаются фразы вроде «см. документ такой-то, страница такая-то». Встретив подобную инструкцию, служащий помечает в стеке, где он остановился в текущем документе, заодно записывает все, о чем в настоящий момент думает, закрывает текущий документ и переходит по приведенной ссылке. Закончив работу там, он закрывает уже этот документ, смотрит в стеке, где он остановился в прошлый раз, и возвращается к исходному (до вызова подпрограммы) состоянию.
7 Этому немало способствуют стандартные функции чтения строк в языке C ,— очень часто используются версии, которые не проверяют, удается ли вместить данные в буфер или нет (они работают быстрее, короче называются, и именно они фигурируют в подавляющем большинстве учебных программ). И это — несмотря на наличие «проверяющих» версий тех же функций. Дело дошло до того, что в руководствах разработчика Unix-приложений крупными буквами подчеркивается опасность использования соответствующих функций и дается ссылка на их безопасные версии. К сожалению, заметного эффекта это пока не оказывает.
Специальным образом сформировав обращение к такой программе, мы добьемся того, что процесс добровольно загрузит в себя вредоносный код и заодно запишет ссылку на него в качестве точки возврата из выполняемой им в этот момент подпрограммы. Как только подпрограмма, в которой осуществлялось чтение данных, закончит работу, процессор этот код сразу же запустит, и атака будет успешно завершена (см. схему 2). Атака, организованная по подобной схеме, и называется атакой переполнением буфера (buffer overflow). Легко видеть, что эта проблема вызвана целым рядом причин — небезопасным соглашением о схеме вызова подпрограмм, неудачной для данного случая организацией стека в x86, но главное — готовностью процессора выполнять код из любого места памяти.
В свое время, когда вершиной микропроцессорной архитектуры многие считали интеловский кристалл 80386, проблема успешно решалась так называемой сегментацией памяти. Не вдаваясь в подробности — оперативная память «нарезалась» небольшими кусочками-сегментами, и процессор работал не со всей памятью, а только с выбранными в данный момент сегментами. Вводилось сегментирование, правда, для решения совсем других проблем, но успешно решала и вопросы разделения памяти на участки кода, данных и стека. Однако с развитием процессоров сегментация использовалась все реже — возникающие проблемы проще решались механизмами виртуальной памяти. Возможность использования сегментов сохранена и по сей день, но обычно прибегают к «вырожденному» варианту — один огромный сегмент охватывает всю возможную память («плоская модель»), а в архитектуре AMD64 (например, в 64-разрядном режиме) сегментацию вообще уже нельзя использовать. В свое время механизмы виртуальной памяти казались «вспомогательными» и не предназначались для полноценного разделения данных, кода и стека. Однако операционная система могла таким образом оперировать флагами страниц, чтобы организовать защиту «своими силами». Например, обычно запрещена запись в страницы, содержащие код программы (программу нельзя изменять в ходе выполнения), а чтобы исключить возможность переполнения стека (ошибка, аналогичная переполнению буфера, только на более высоком уровне — закончилось место в стеке), его с обеих «сторон» обычно закрывают две страницы с максимально строгими ограничениями доступа (делать с этими страницами фактически ничего нельзя — любое обращение приведет к ошибке), надежно «закупоривающими» стек — выйти за его пределы невозможно8. Но вот бита, позволяющего запретить выполнение кода из страницы, до сих пор не было, — его-то и ввела AMD (бит NX). Теперь операционная система может просто-напросто пометить этим битом страницы стека. И хотя переполнить буфер будет по-прежнему возможно (тем самым испортив выполняющуюся программу), запуститься такой код уже не сможет — процессор выдаст ошибку доступа к памяти. В этом и состоит вся защита EP — в добавлении единственного бита, «забытого» в свое время при проектировании и ставшего сегодня одной из самых серьезных угроз компьютерной безопасности. Специалисты AMD и Microsoft полагают, что около половины апдейтов безопасности Windows от Microsoft за последние два года не потребовались бы, будь тогда технология, подобная EP.
Сухой остаток
Итак, Execution Protection не защищает от ошибки в программе, а лишь частично компенсирует ее последствия. Атакованная программа погибнет, но операционная система «устоит» — значит, будет остановлена вирусная эпидемия, на компьютер пользователя не попадет троянская программа; наконец, из-за ошибки в Internet Explorer злоумышленники не смогут взломать куда как более защищенную программу, вначале получив через IE прямой доступ к компьютеру, а затем использовав один из методов непрямой атаки (см. первую статью темы). Кстати, в Microsoft пошли даже дальше защиты стека — в документации по SP2 (там аналогичная EP технология носит имя Data Execution Prevention) написано, что битом NX помечаются вообще все страницы кроме тех, которые явным образом отведены для размещения кода. Потенциально это может создать немало проблем хитрым системам защиты программ от взлома, но судить об этом пока рано — посмотрим, что получится на практике.
Если же вам прямо сейчас нужен компьютер или сервер с повышенной степенью защиты от сетевых атак, а желания часто устанавливать сервис-паки и заплатки, настраивать файрволл и антивирус нет, то имеет смысл присмотреться к AMD Athlon 64 и Opteron. В сочетании с грядущим SP2 для Windows XP они способны эффективно противостоять атакам определенных вирусов и червей (тех, которые вызывают переполнение буфера) и укрепить безопасность домашних и корпоративных компьютеров при работе с электронной почтой и просмотре содержимого Интернет-сайтов, а также усилить защиту сетей и памяти и уменьшить дальнейшее распространение вирусов. Впрочем, на сей счет есть и другие мнения — читайте краткие комментарии Евгения Касперского во врезке. Подчеркнем, что технология AMD EP работает и в обычной 32-битной среде Windows XP (при условии установки SP2).
Бдительный читатель может воскликнуть: «Как же так? Ведь Intel тоже обещала поддержать Execution Protection, а ее новейшие процессоры будут иметь технологию CT9, являющуюся якобы точной копией AMD64, в которую EP входит?» А вот так — подробное изучение технической документации текущих продуктов приводит к неутешительному выводу: поддержки бита NX у Intel в данный момент нет (даже в Prescott, где его ранее обещали), или она надежно заблокирована. И это подтверждается официальными заявлениями. Аналогичная технология давным-давно есть в Intel Itanium, но это совсем другая история.
Что касается будущих процессоров Intel, то бит NX (у Intel он называется точно так же) расположен не там, где у процессоров AMD, и не привязан к регистрам 64-битных расширений. Официально процессоры Intel с поддержкой бита NX появятся во втором полугодии 2004 года одновременно с SP2 для MS Windows XP, причем сразу во всех сегментах рынка — настольном, мобильном и серверном (можно предположить, что ядрами с поддержкой «NX от Intel» станут Prescott нового степпинга, серверная Nocona и «долгострой» Dothan — то есть все 90-нанометровые кристаллы). Скорее всего, для настольных систем Intel функционирование бита NX будет жестко привязано к новому разъему LGA775. Это связано с необходимостью встраивать поддержку NX в BIOS системных плат, а возиться с переделкой армады плат для Socket 478 как-то не с руки.
Фактически обещание Intel выпустить процессоры с работающим битом NX одновременно с выходом Service Pack 2 для Windows XP сводит фору AMD к минимуму — да, NX у AMD64 есть уже сейчас, но реально он заработает тоже только с выпуском SP2, а «бетами» этого сервис-пака или отдельными неофициальными заплатками пользуются лишь немногие владельцы платформы AMD64. С другой стороны, все уже выпущенные системы на базе AMD64 изначально могли поддерживать NX, поэтому SP2 просто позволит им активировать эту возможность и пользоваться повышенной защитой. Впрочем, по некоторым данным, доля проданных процессоров архитектуры AMD64 не так уж велика на общем фоне. Так что выбор делать вам, а время — оно все расставит по своим местам10.
Комментарии Евгения Касперского («Лаборатория Касперского») Как повлияют развиваемые сейчас технологии аппаратной безопасности, включая Intel LaGrande и AMD Execution Protection, на работу компаний, подобных вашей? — Думаю, никак. Насколько я понимаю, речь идет о том, что на этих процессорах будет физически невозможна ошибка переполнения буфера, которая использовалась сетевыми червями Slammer и Lovesan. Однако в первую очередь это было проблемой не существующей архитектуры процессоров, а просчетом Microsoft при написании кода Windows. Да, Slammer и Lovesan были заметными историческими вехами в вирусологии, однако главную угрозу для пользователей (и основную работу для антивирусных фирм) представляют традиционные вирусы, троянцы и черви — не использующие аналогичные уязвимости. — Поживем — увидим. Главное, чтобы процессоры поддерживала операционная система. А вот как это будет реализовано — пока неизвестно. Что вы думаете об эффективности технологии Trusted Platform Module, являющейся одной из основ Intel LaGrande, и о защищенном исполнении программ в разделенных доменах в рамках этой же технологии? Удастся ли с помощью TPM заметно сократить потенциальное количество подходов при написании вирусов? — Исходя только из такого определения TPM, я не вижу какого-либо фактора, благодаря которому вирусов станет меньше. Сама идеология этой системы направлена в первую очередь не на борьбу с вирусными угрозами, а на защиту конфиденциальной информации. Есть и другая сторона проблемы с TPM — эта технология совершенно очевидно защищает интересы крупных производителей программного обеспечения. Вероятнее всего она не позволит запускать на вашем компьютере нелицензионные программные продукты, а также программное обеспечение других разработчиков, не вошедших (или не приглашенных) в альянс Trusted Computing. Об этом свидетельствует крайне негативная реакция по отношению к Trusted Computing практически всех экспертов по инфобезопасности, не связанных с корпорациями. К сожалению, нужно признать, что мы имеем дело с попыткой еще больше упрочить положение ведущих корпораций и даже с попыткой введения тотального контроля за пользователями. Если я не могу запускать на компьютере нужные мне (а не разрешенные «свыше») приложения, зачем мне такая система защиты? |
Выражаем благодарность С@t и Yury_Malich за помощь в подготовке материала.
8 Кстати, легко видеть, что «переполнение буфера» фактически единственно возможный способ атаки, не требующий, чтобы вирус был явным образом запущен самой программой (как, например, в случае макровирусов для MS Word — текстовый редактор прекрасно осведомлен, что запускает какую-то программу, и Execution Protection здесь не спасает).
9 CT, или Clackamas Technology, — под таким именем сейчас фигурируют новомодные 64-битные расширения 32-разрядных процессоров Intel, объявленные на недавнем IDF (см. репортаж в «КТ» #532).
10 Ссылки по теме: www.download.microsoft.com/download/8/7/9/879a7b46-5ddb-4a82-b64d-64e791b3c9ae/WinXPSP2_ Documentation.doc, стр. 43;
www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24593.pdf , раздел по страничной организации памяти.