Средства противодействия крэкеру
|
Автор: Семьянов П.В., Зегжда Д.П.
|
|
Доктор ставит хакеру диагноз:
- Итак, дорогой, вам осталось жить 30 дней.
- Извините, доктор, а где можно скачать crack?
|
Это документ является теоретическим и обозревает общий подход к противодействию исследования защиты ПО и обходу этого противодействия.
До недавнего времени исследование функционирования программы, задаваемой ее исполняемым кодом (и ничем более) называлось "вскрытием" (cracking) и являлось уделом хакеров, и, естественно, не могло не быть связано с нарушением авторских прав или каким-либо другим нанесением ущерба компьютерной системе. С появлением в последнее время вредного программного обеспечения (badware) ситуация, очевидно, изменилась. Многие вирусологи теперь только и заняты тем, что изучают алгоритмы все новых и новых вирусов - и эта работа для них из безусловно творческой превратилась в рутинную. Известно, что при обезвреживании знаменитого вируса Морриса самая важная часть работ легла на плечи специалистов по дизассемблированию, причем выполнить они ее должны были в кратчайшее время - когда вирус еще распространялся, и не было известно, что он не содержит функции разрушения. Именно дизассемблирование могло дать тогда ответ на три главных вопроса: что это такое, чем это грозит и как с этим бороться [2].
Таким образом, одна из задач теории компьютерной безопасности определение, содержит ли данная программа функцию разрушения (нанесения ущерба) - сводится, по сути дела, к задаче исследования программы, задаваемой ее объектным или исполняемым кодом. При постановке последней должны разделяться два вопроса:
- собственно построение алгоритма (или какой-либо интересующей его части) на языке, удобном для последующего анализа (обычно это ассемблер или язык более высокого уровня);
- семантический анализ полученного алгоритма для ответа на интересующие вопросы (например, о правильности программы или о степени ее надежности).
Хотя обе эти задачи принципиально разрешимы, можно высказать сомнение в получении сколь-нибудь достоверных результатов при использовании автоматических методов. Более того, даже имея стопроцентную уверенность в отсутствии ненадежного (untrusted) кода в исходном тексте программы, нельзя гарантировать отсутствие ошибок или троянских коней в программе, которая будет его интерпретировать (для исполняемого кода это происходит на уровне микрокода) или во внешних вызываемых функциях (в частности, операционной системы). Этот аспект рассматривался К.Томпсоном в [5]: "Никакой уровень верификации или исследований исходного текста не защитит вас от исполнения ненадежного кода... По мере того, как уровень языка программирования снижается, находить такие ошибки становится все труднее и труднее. Хорошо продуманную "ошибку" в микрокоде найти почти невозможно".
В дальнейшем, если не будет специально оговорено, под исследованием программы будем понимать именно процесс получения ее алгоритма.
Все средства исследования ПО можно разбить на два класса: статические и динамические. Первые оперируют исходным кодом программы как данными и строят ее алгоритм без исполнения, вторые же изучают программу, интерпретируя ее в реальной или виртуальной вычислительной среде. Отсюда сразу следует, что первые являются более универсальными в том смысле, что теоретически могут получить алгоритм всей программы, в т.ч. и тех блоков, которые никогда не получат управления, вторые же могут строить этот алгоритм только на основании конкретной трассы (trace) программы, полученной при определенных входных данных. Поэтому задача получения полного алгоритма программы в этом случае эквивалентна построению исчерпывающего набора тестов для подтверждения правильности программы, что практически невозможно, и вообще при динамическом исследовании можно говорить только о построении некоторой части алгоритма.
Два наиболее известных типа программ, предназначенных для исследования ПО, как раз и относятся к разным классам: это отладчик - динамическое средство и дизассемблер - средство статического исследования. Если первый широко применяется пользователем для отладки собственных программ и задачи посторения алгоритма для него вторичны и реализуются самим пользователем, то второй предназначен исключительно для их решения и формирует на выходе ассемблерный текст алгоритма. Таким образом, если принять во внимание то, о чем говорилось в начале статьи, создание дизассемблеров можно объяснить только научными и "хакерными" целями, и остается поблагодарить пытливых ученых и неутомимых хакеров, что для них (или они сами) создали средство статического исследования программ задолго до того, как появились реально нуждающиеся в этом программы.
Помимо этих двух основных программных продуктов можно предложить:
- "дискомпиляторы", генерирующие из исполняемого кода программу на языке высокого уровня;
- "трассировщики", сначала запоминающие каждую инструкцию, проходящую через процессор, а затем переводящие набор их в форму, удобную для статического исследования, автоматически выделяя циклы, подпрограммы и т.п.;
- "следящие системы", запоминающие и анализирующие трассу уже не инструкций, а других характеристик, например, вызванных программой прерываний.
Естественно, после создания средств исследования не могли не появиться программы, им противодействующие, так что задача получения алгоритма может быть столь же трудноразрешимой, как и задача его анализа. Будем называть такие программы противодействующими исследованию или защищенными, а алгоритмы, реализующее это противодействие - системой защиты.
В этом случае задача исследования защищенного ПО сводится в первую очередь к исследованию самой системы защиты и ее анализу. Естественно, что система защиты сама может быть защищена на более высоком уровне и так далее, но в любом случае можно начать исследование одним из методов, описанных выше, с самого верхнего уровня. Более того, динамические методы в этом случае оказываются по крайней мере не хуже статических (как будет показано ниже, на самом деле они являются основными), т.к. система защиты должна получать управление при любом наборе входных данных и ее трасса может быть получена всегда.
Как ни странно, программами, противодействующими их исследованию, являются как средства, обеспечивающие безопасность компьютерной системы (системы разделения доступа, защиты от несанкционированного копирования и т.п.), так и программы, направленные на ее нарушение (компьютерные вирусы, троянские кони и т.п.). (Кстати, и необходимость в их исследовании испытывают люди, стоящие по разные стороны баррикад первые вскрывают хакеры, а вторые - специалисты по защите компьютерных систем). Средства противодействия оказываются краеугольным камнем в таких системах, т.к. при их отсутствии квалифицированый специалист сможет очень быстро разобратся в их логике.
Методы, используемые программами для собственной защиты, также хорошо известны, и большинство из них базируется на использовании того принципа фон Неймана, что программы и данные выглядят и хранятся одинаково, в результате чего программа может модифицировать саму себя. Этого бывает достаточно для подавления средств статического анализа (поэтому они обычно и непригодны для исследования защищенных программ). В случае защиты от динамических средств может быть использован тот факт, что изучаемая программа запускается в возмущенной самим средством операционной среде и может это распознать.
Для системы MS-DOS описаны [3, 4] конкретные приемы защиты от исследования, базирующиеся на описанных принципах :
1) от дизассемблера и дискомпилятора
а) модификация кода программы - приводит к тому, что дизас семблер не может достоверно распознать инструкции и/или данные:
- зашифровка критичного кода и расшифровка его самой систе мой защиты перед передачей управления на него;
- модификация кода непосредственно самой программой;
б) скрытие команд передачи управления - приводит к тому, что дизассемблер не может построить граф передачи управления:
- косвенная передача управления;
- использование нестандартных способов передачи управления (JMP через RET, RET и CALL через JMP);
- модификация адреса перехода в коде программы;
2) от отладчика
а) выявление изменений операционной среды - приводит к тому, что программа отказывается правильно работать:
- проверка количества свободной памяти, векторов прерываний и т.п.;
- проверка временных характеристик программы;
б) подавление изменения операционной среды - программа либо са ма еще раз перенастраивает среду, либо вообще не может рабо тать в возмущенной среде:
- расшифровка кода в зависимости от эталонного состояния среды;
- использование отладочных прерываний (INT 1 и INT 3) для собственных нужд;
- использование абсолютной адресации;
- назначение стека программы непосредственно в область ис польняемого кода;
- самомодификация программы в зависимости от эталонных вре менных характеристик;
в) противодействие установке контрольных точек - отладчик или не может установить контрольную точку, или программа рас познает ее:
- подсчет контрольных сумм участков кода программы;
- чередование команд запрета и разрешения прерываний;
г) нарушение интерфейса с пользователем - приводит к тому, что пользователь не может пронаблюдать за ходом выполнения программы:
- блокировка клавиатуры;
- искажения при выводе на терминал;
3) от следящей системы за прерываниями (помимо аналогичных 2а, 2б методов):
- вызов нужных функций через прерывания более низкого уров ня, нестандартные прерывания или точки входа в них, ис пользования аппаратуры напрямую;
4) от семантического анализатора:
- использование функций, внешне похожих на критичные, со вершенно бесполезных на самом деле;
К счастью для исследователя, истинными оказывается утверждение, что любую систему защиты можно вскрыть за конечное время - это следует из того, что ее команды однозначно интерпретируются процессором. При этом время, необходимое для вскрытия хорошей системы защиты, оказывается сравнимым с написанием защищенной программы заново. Однако не все программы (особенно badware) пишут профессионалы, поэтому часто можно вскрыть или обойти защиту, найдя ее слабейшее звено, сократив время вскрытия на несколько порядков. Для этого, в частности, можно рекомендовать следующие достаточно универсальные методы:
- если программа зашищена только от средств статического анализа, она легко изучается динамически, и наоборот;
- "метод изменения одного байта" - в момент, когда система защиты сравнивает контрольную информацию (состояние операционной среды, котрольную сумму) с эталонной, простым изменением команды перехода она направляется по правильному пути;
- аналогично, результат работы функции, возвращающей текущую контрольную информацию, может быть подменен на эталонное (ожидаемое) значение (например, с помощью перехвата соотвествующего прерывания);
- когда система защиты расшифровала критичный код, он может быть скопирован в другое место памяти или на диск в момент или вскоре после передачи управления на него. Частный случай - после окончания работы программы весь ее код расшифрован и доступен.
Естественно, что в профессиональных системах защиты [4] все эти приемы не проходят, и потребуются многие дни или месяцы кропотливой работы в отладчике, чтобы разобраться в их функционировании. Для исследования таких систем необходимы специальные средства (естественно, их с успехом можно применять и для любых других программ). Можно предложить следующие перспективные направления:
- интерпретация программы в полностью виртуальной среде, где эмулируются процессор, память, внешние устройства, операционнная среда и т.д. В этом случае все описанные приемы противодействия оказываются неэффективными; однако, какой бы прозрачной не была эмуляция среды, все равно она вносит возмущения в истинную, и программа может это распознать со всеми вытекающими отсюда последствиями. Самым трудным и слабым местом здесь будет точная эмуляция временных характеристик аппаратуры, недокументированных прерываний и т.п.
- объединение трассировщика и дизассемблера в единое статическо-динамическое средство исследования. Трассировщик в динамике проходит программу до того момента, когда можно применить статическое исследование, попутно запоминая все команды переходов и строя таким образом граф передачи управления. После прохождения по определенному пути графа надо вернуться к ближайшему узлу, сэмулировать ситуацию, при которой возможно прохождение по другой ветви и пойти по ней. Хотя теоретически для построения полного алгоритма здесь тоже необходим полный набор входных данных, их реальное количество будет гораздо меньше, чем для стандартных динамических средств.
Оба этих перспективных средства исследования должны иметь очень сложную логику, которую невозможно реализовать без интеграции со средствами искусственного интеллекта.
Именно такие средства в сочетании с автоматизированной системой семантического анализа предполагается использовать для исследования программ в рамках антивирусной лаборатории (АВЛ) [1].
Литература.
- Зегжда Д.П., Матвеев В.А., Молотков С.В., Тихомиров Ю.В., под редакцией Шмакова Э. М. Защита информации в компьютерных системах. Теоретические аспекты защиты от вирусов. - СПб, СПбГТУ, 1993.
- Моисеенко И. Суета вокруг Роберта или Моррис-сын и все, все, все. // КомпьютерПресс, 1991, N 8-9.
- Расторгуев С.П., Дмитриевский Н.Н. Искусство защиты и "раздевания" программ. - М., 1991.
- Спесивцев А.В. и др. Защита информации в персональных ЭВМ. М., Радио и связь, 1992.
- Thompson K. Reflection on trusting trust. // CACM, 1984, v. 27, N 8.
|