Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Под хакерством обычно понимают изучение дизассемблированного кода приложений с целью их взлома. Сегодня мы займем противоположную сторону баррикад — изучим приемы защиты. Настала пора основательно разобраться в устройстве используемых хакерами инструментов, функциональной поддержке со стороны операционной системы и процессора, чтобы противостоять взломщику, скрыть свои секреты и не стать легкой наживой для злоумышленника!
Фундаментальные основы хакерства
Пятнадцать лет назад эпический труд Криса Касперски «Фундаментальные основы хакерства» был настольной книгой каждого начинающего исследователя в области компьютерной безопасности. Однако время идет, и знания, опубликованные Крисом, теряют актуальность. Редакторы «Хакера» попытались обновить этот объемный труд и перенести его из времен Windows 2000 и Visual Studio 6.0 во времена Windows 10 и Visual Studio 2019.
Ссылки на другие статьи из этого цикла ищи на странице автора.
Три основных этапа взлома защитных механизмов — это локализация кода защиты в коде приложения, анализ алгоритма его работы и собственно сам взлом. Все этапы одинаково важны: если, например, не будет пройден второй из них — за взлом нечего браться.
Можно классифицировать защиты по типу «этапа преткновения». Например, шифры и криптозащиты опираются на третий этап — алгоритм их работы обычно общедоступен, хорошо документирован и в общем случае известен хакеру, но это не сильно облегчает взлом (разве что упрощает написание переборщика). Механизмы с использованием регистрационных номеров, напротив, делают упор на засекречивании алгоритма генерации, а также затруднении его поиска и анализа в коде программы (еще бы, зная алгоритм, можно легко написать кейген).
Однако даже если защита построена с применением криптографических методов, скажем шифрует тело критически важных функций криптостойким методом по непомерно длинному ключу, она может быть «отвязана» от ключа, например копированием дампа программы после расшифровки. Еще проще — распространять программу вместе с ключом (обычная тактика пиратов). Один из способов воспрепятствовать такому беспределу — заложить в ключ зашифрованную привязку к компьютеру или проверять «чистоту» копии через интернет (можно даже и втихомолку — скрыто от пользователя, хотя это считается дурным тоном). Но что помешает хакеру, владеющему лицензионной копией программы, расшифровать ее своим ключом и выкусить все проверки чего бы там ни было?
Таким образом, любая защита должна уметь эффективно препятствовать своему обнаружению, анализу, попутно отравляя жизнь дизассемблеру и отладчику — основным инструментам взломщика. Без этого защита не защита.
В эпоху царствования MS-DOS в основном использовались программы реального режима, которые монопольно распоряжаются процессором, памятью и аппаратурой, в любой момент беспрепятственно переходят в защищенный режим и возвращаются обратно. Отладчики в то время (еще хлипкие, немощные, нежизнеспособные) легко обманывались, срубались и завешивались тривиальными приемами программирования, активно используемыми защитами. Другими словами, дизассемблеры тогда были очень глупыми и впадали в ступор от одного только вида зашифрованного или самомодифицирующегося кода. Словом, настоящий рай для разработчиков защит.
Сегодня все изменилось. Прежде всего, прикладной программе под Windows особо выделываться никто не даст. С защищенным режимом теперь слишком не разгонишься — используй прозаические непривилегированные инструкции, а о разных хитростях не помышляй. Та же небольшая часть защитных приемов, что может функционировать даже в такой ориентированной на пользователя среде, наталкивается на сильно поумневшие отладчики и дизассемблеры.
Аппаратная поддержка отладки в процессорах 386+ в совокупности с виртуальным режимом работы, привилегированными инструкциями и виртуальной памятью позволяет создавать отладчики, которые практически не могут быть обнаружены прикладной программой, и уж тем более она не в состоянии получить над ними контроль.
Существуют и отладчики‑эмуляторы, фактически настоящие виртуальные машины, самостоятельно исполняющие код, вместо того чтобы пустить его на «живой» процессор. При этом эмулятор всегда исполняется в режиме супервизора даже по отношению к отлаживаемому коду нулевого кольца. У защиты очень мало шансов обнаружить отладчик или помешать его работе (да и то если эмулятор реализован с ошибками).
Появились и интерактивные дизассемблеры (та же IDA), которые в силу тесного взаимодействия с пользователем (в смысле хакером) могут обходить любые мыслимые и немыслимые ловушки, оставленные разработчиком.
Даже на уровне нулевого кольца в Windows очень трудно что‑либо скрыть. Чтобы обеспечить совместимость со всем парком Windows-подобных операционных систем, приходится использовать только документированные возможности. Строить в «окнах» защиту — все равно что пытаться заблудиться в парке. Будь там хоть миллион деревьев, все они геометрически правильно расположены и обильно увешаны табличками «Выход — там».
Таким образом, надежно противостоять изучению программы очень трудно, если вообще возможно. Однако многие приемы против отладчиков и дизассемблеров просто интересны сами по себе и достойны того, чтобы рассмотреть их в этой статье.
Раньше всех появился debug.com — пародия, которая отдаленно напоминает отладчик, зато входит в штатную поставку MS-DOS. Сегодня этот инструмент годится разве что для забавы и изучения ассемблера. Впрочем, и тогда от него мало кто был в восторге, и новые отладчики росли как грибы после дождя. Правда, большинство из них недалеко ушло от своего прототипа, отличаясь от оригинала разве что интерфейсом.
Это было золотое время для разработчиков защит. Стоило лишь «запереть» клавиатуру, запретить прерывания, сбросить флаг трассировки, и отладка программы становилась невозможной.
Первые мало‑мальски пригодные для взлома отладчики появились только после оснащения компьютеров процессором 80286. В памяти хакеров навсегда останутся AFD PRO, написанный в 1987 году AdTec GmbH, знаменитый Turbo Debugger, созданный годом позже братьями Крисом и Ричем Вильямсами, первый эмулирующий отладчик Сергея Пачковки, написанный, правда, с большим опозданием — в 1991 году. Разработчики защит крякнули, но выдержали — эти отладчики по‑прежнему позволяли отлаживаемой программе захватить над собой контроль и очень плохо переносили «извращения» со стеком, экраном, клавиатурой…
Ситуация изменилась с выходом процессора 80386 — резкое усложнение программного обеспечения и, как следствие, огромные сложности с его отладкой диктовали необходимость наличия развитых отладочных средств в самом процессоре. И в 386 они появились! С этого момента разработчикам защит стали наступать на пятки.
Масла в огонь подлила NuMega, выпустившая в конце восьмидесятых годов свой замечательный SoftICE, пользовавшийся у хакеров огромной популярностью, а позже портированный на Windows 9x и Windows NT. Он долгое время оставался бесспорным фаворитом (хотя не без конкуренции). Впрочем, неверно было бы считать, что NuMega — криминальная фирма, а SoftICE исключительно хакерский продукт. Этот отладчик предназначен в первую очередь для разработчиков драйверов и для легальных исследователей операционной системы (не разбираясь во внутренностях ОС, с драйверами особо не разгонишься).
Но так или иначе, SoftICE задал копоти всем защитам и их разработчикам. Пускай он не был полностью невидимым для отлаживаемых программ Stealth-отладчиком, имел ряд ошибок, позволяющих себя обнаружить, и давал защите возможность вырваться из‑под контроля, но в умелых руках отладчик справлялся со всеми этими ограничениями и обходил заботливо расставленные «капканы». И с каждой версией SoftICE противостоять ему становилось все труднее и труднее (старые ошибки устранялись быстрее, чем вносились новые).
Постепенно мода на антиотладочные приемы сошла на нет и уж совсем заглохла под победное шествие Windows XP SP3. Поскольку одновременно с ее выходом SoftICE перестал корректно работать, полностью завешивая систему без возможности дальнейшего функционирования. Все указывает на то, что такой же эффект был в Windows Vista, вышедшей годом ранее, но на ней я самолично SoftICE не проверял.
К тому моменту NuMega уже была приобретена компанией Compuware, и SoftICE распространялся как часть пакета для разработки драйверов DriverStudio. Последняя версия SoftICE была выпущена в апреле 2006 года. Service Pack 3 для Windows XP появился двумя годами позднее.
Так как SoftICE использовал недокументированные возможности Windows, с выходом новой версии винды он переставал работать. Как было сказано ранее, после 2006 года SoftICE перестал получать обновления, затем был продан компании Micro Focus и ею же окончательно похоронен. Вероятно, главную роль в смерти SoftICE сыграли деловые разногласия создателей отладчика с Microsoft, а не технические проблемы, как принято считать. Похоже, SoftICE использовал какие‑то особые механизмы операционной системы, которые Microsoft посчитала «неудобными».
При срабатывании поставленной точки останова SoftICE завешивал систему, позволяя пользователю (в данном случае хакеру) продолжать взаимодействовать с компьютером через свою прослойку между аппаратным обеспечением и ОС. Поэтому после завешивания системы взломщик мог спокойно возобновить ее работу, а она, в свою очередь, о зависании даже не подозревала. У нее даже отставало системное время на тот период, пока она находилась в «отключке» — в зависшем состоянии.
Свято место пусто не бывает. В качестве отладчика пользовательского уровня популярным стал OllyDbg. Однако сейчас по данным с официального сайта разработка отладчика заморожена.
Предприимчивые китайцы выпустили отладчик уровня ядра под названием Syser, по своим возможностям во многом схожий с испустившим дух SoftICE. Только энтузиазма хватило ненадолго, и уже совсем скоро разработка была прекращена. Сейчас его не найти даже на файловых помойках.
Зато Microsoft с течением времени превратила свой захудалый отладчик WinDbg в действительно мощный и полезный для системных программистов и хакеров инструмент. Грубо говоря, WinDbg представляет собой оболочку для отладки с помощью движка dbgeng.dll
, который включен непосредственно в операционную систему. WinDbg может использоваться как отладчик либо пользовательского режима, либо режима ядра, но не одновременно.
Вместе с гибелью SoftICE распространилось совершенно нелепое убеждение, что под Windows на прикладном уровне дернуть хвост человеку с отладчиком невозможно. Это вызывает ухмылку профессионалов, эпизодически встраивающих разные ловушки в свои программы — так, больше для разминки (дабы мозги жиром не заплыли), чем для серьезной борьбы с хакерами.
Бороться с хакерами при современном уровне средств анализа приложений несколько наивно — те и от тигра хвост оторвут. Но сегодня, кроме хакеров, серьезную угрозу представляют и вчерашние желторотые пользователи, начитавшиеся справочных руководств о том, «как ломать программы» (благо сейчас они доступны всем и каждому), и теперь только и ищущие, на чем бы испытать свою силушку богатырскую.
Бороться с отладчиком, не представляя себе, как он работает, было бы по меньшей мере некультурно. Поэтому ниже мы рассмотрим базовые принципы, лежащие в его основе. Это изложение не является всеобъемлющим, тем не менее оно позволяет составить общее представление о вопросе. Технические подробности исчерпывающе изложены в 17-й главе Debug, Branch Profile, Tsc, And Resource Monitoring Features технического руководства Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3B: System Programming Guide, Part 2 (PDF), которое бесплатно распространяет фирма Intel.
Все отладчики можно разделить на две категории: первые используют отладочные средства процессора, а вторые самостоятельно эмулируют процессор, полностью контролируя выполнение «подопытной» программы.
Качественный эмулирующий отладчик отлаживаемый код ни обнаружить, ни обойти не может, но полноценных эмуляторов современных Core-процессоров на сегодняшний день нет, и вряд ли они появятся в обозримом будущем.
Да и есть ли смысл их создавать? Микропроцессоры линейки Core предоставляют в распоряжение разработчика богатейшие отладочные возможности, позволяющие контролировать даже привилегированный код! Они поддерживают пошаговое исполнение программы, отслеживают выполнение инструкции по заданному адресу, контролируют обращения к нужным ячейкам памяти (или портам ввода‑вывода), сигнализируют о переключениях задач и так далее.
В общей сложности микропроцессоры серии x86, начиная с модели x386, содержат восемь отладочных 32-битных регистров. В обновленной архитектуре x86-64 количество отладочных регистров не изменилось, однако они увеличились в размере до 64 бит.
Четыре отладочных регистра DR0 — DR3
хранят линейные адреса четырех контрольных точек, а управляющий регистр DR7
содержит для каждой из них условие, при выполнении которого процессор генерирует исключение INT 0x1
, передавая управление отладчику. Всего существует четыре различных условия: прерывание при выполнении команды, прерывание при модификации ячейки памяти, прерывание при чтении или модификации, но не исполнении ячейки памяти и прерывание при обращении к порту ввода‑вывода.
Отладочные регистры для линейных адресов точек останова в зависимости от архитектуры процессора имеют размеры 32 или 64 бита.
Регистры DR4
и DR5
зарезервированы для использования в будущем. Флаги регистра DR6
устанавливаются в зависимости от происходящих исключений. Например, первые четыре флага устанавливаются в зависимости от срабатывания соответствующих точек останова в регистрах DR0 — DR3
. Флаги регистра DR7
позволяют включать или отключать установленные в регистрах DR0 — DR3
точки останова, а также определять условия срабатывания прерываний.
Регистры отладки в процессорах на архитектуре IA-32
Регистры отладки в процессорах на архитектуре AMD64
Если бит трассировки регистра флагов установлен, то после выполнения каждой машинной инструкции автоматически генерируется отладочное исключение INT 1
и управление передается отладчику. Отлаживаемый код может обнаружить трассировку анализом регистра флагов, поэтому для обеспечения собственной невидимости отладчик должен распознавать команды чтения регистра флагов и эмулировать их выполнение, возвращая нулевое значение флага трассировки.
Следует обратить внимание на одно важное обстоятельство: после выполнения команды, модифицирующей значение регистра SS
, отладочное исключение не генерируется! Отладчик должен уметь распознавать такую ситуацию и самостоятельно устанавливать точку останова на следующую инструкцию. В противном случае войти в процедуру, предваренную инструкцией POP SS
, автоматический трассировщик не сможет:
PUSH SSPOP SSCALL MySecretProc
Не все современные отладчики учитывают эту тонкость, и такой прием, несмотря на свою архаичность, может оказаться далеко не бесполезным.
Установкой специального бита можно добиться генерации отладочного исключения при всяком обращении к отладочным регистрам, которое возникает даже в том случае, если их пытается прочесть (модифицировать) привилегированный код. Грамотно спроектированный отладчик может скрыть факт своего присутствия, не позволяя отлаживаемому коду себя обнаружить, какие бы ни были у него привилегии. Правда, если «подопытный» код отлаживает сам себя, задействовав все четыре контрольные точки, отладчик не сможет работать.
Если бит Т
в TSS
отлаживаемой задачи установлен, то при каждом переключении на нее будет генерироваться отладочное исключение до выполнения первой команды задачи. Чтобы предотвратить собственное обнаружение, отладчик может отслеживать всякие обращения к TSS
и возвращать программе подложные данные. Необходимо заметить: Windows NT по соображениям производительности не использует TSS
(точнее, использует, но всего один) и эта отладочная возможность для нее совершенно бесполезна.
Программная точка останова — единственное, что нельзя замаскировать, не прибегая к написанию полноценного эмулятора процессора. Она представляет собой однобайтовый код 0xCC
, который, если его поместить в начало инструкции, вызывает исключение INT 0x3
при попытке ее выполнения. Отлаживаемой программе достаточно подсчитать свою контрольную сумму, чтобы выяснить, была ли установлена хоть одна точка останова или нет. Для достижения этой цели она может воспользоваться командами MOV
, MOVS
, LODS
, POP
, CMP
, CMPS
или любыми другими, ни один отладчик не в состоянии их все отследить и эмулировать.
Настоятельно рекомендую использовать программные точки останова в тех и только в тех случаях, когда аппаратных уже не хватает. Как мы увидели выше, в процессорах x86-64 имеется четыре аппаратные точки останова. Много это или мало — зависит от случая и отлаживаемой программы. Однако практически все современные отладчики всегда устанавливают программные, а не аппаратные точки останова. Это обстоятельство может быть с успехом использовано в защитных механизмах, примеры реализаций которых приведены в разделе «Как противостоять трассировке».
Когда возникает отладочное исключение (как, впрочем, и любое другое), процессор заносит в стек регистр флагов, адрес следующей (или текущей — в зависимости от рода исключения) выполняемой инструкции и лишь затем передает управление отладчику.
В реальном режиме флаги с адресом возврата заносятся в стек отлаживаемой программы, поэтому факт отладки обнаружить очень просто — достаточно контролировать целостность содержимого, лежащего выше указателя стека. Или, как вариант, установить указатель на его вершину, тогда добавление новых данных в стек окажется невозможным и отладчик не сможет функционировать.
Иная ситуация складывается при работе в защищенном режиме — обработчик исключения может находиться в своем собственном адресном пространстве и не использовать никаких ресурсов отлаживаемого приложения, в том числе и стека. Грамотно спроектированный отладчик защищенного режима ни обнаружить, ни блокировать принципиально не может даже привилегированный код, исполняющийся в нулевом кольце. Сказанное справедливо для всех версий Windows семейства NT.
Вскрыть защитный механизм в общем случае не составляет проблемы. Куда сложнее найти этот самый механизм в коде ломаемого приложения. Сегодня мало кто использует для этой цели автоматическую трассировку — на смену ей пришли аппаратные контрольные точки.
Пусть некая защита запрашивает пароль, затем каким‑то образом удостоверяется в его подлинности (например, сравнивает с оригиналом) и в зависимости от результатов проверки передает управление соответствующей ветке программы. Вскрыть такую защиту взломщик может, даже не вникая в алгоритм аутентификации! Он просто введет первый пришедший ему на ум пароль (необязательно совпадающий с правильным), найдет его в памяти, установит контрольную точку на первый символ строки своего пароля, дождется «всплытия» отладчика, отследившего обращение к паролю, выйдет из сравнивающей процедуры и «подправит» условие перехода так, чтобы управление всегда получала нужная ветвь программы.
Время снятия подобных защит измеряется секундами, и обычно такие программы ломаются раньше, чем успевают дойти до легального потребителя. К счастью, этому можно противостоять!
Откуда бы ни бралась ключевая информация — из реестра, файла или с клавиатуры, взломщик может практически мгновенно локализовать ее местоположение в памяти и установить контрольную точку. Помешать этому нельзя, но не составит труда подложить хакеру сюрприз — пусть ключевая информация анализируется не сразу же после получения, а передается в качестве аргумента множеству функций, которые что‑то с ней делают и затем передают другим функциям, а те, в свою очередь, следующим.
Источник: xakep.ru