2.  Задача исследования модуля защиты и понимания принципов его действия. Злоумышленник должен понять, каким образом построена защита, где она хранит (если хранит) эталонные характеристики среды, где сохраняет (если сохраняет) свои метки и ключи, на каком этапе принимается решение о регистрации программы либо об отклонении регистрации. При этом злоумышленник сталкивается с проблемой анализа машинного кода, что приводит к аналогичным трудностям, представленным выше.

Однако необходимо отметить, что на самом деле сложности, перечисленные выше, для подготовленного злоумышленника в большинстве случаев таковыми не являются. Существует множество программных продуктов, облегчающих ему решение задач 1 и 2: их реализация, в отдельных случаях доводится до автоматизма.

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

Под обратным проектированием (reverse engineering) понимают процесс исследования и анализа машинного кода, нацеленный на понимание общих механизмов функционирования программы, а также на его перевод на более высокий уровень абстракции (более высокий уровень языка программирования) вплоть до восстановления текста программы на исходном языке программирования.

Основными методами обратного проектирования являются отладка и дизассемблирование программ. При этом используются следующие средства (инструменты).

Отладчики – программные средства, позволяющие выполнять программу в пошаговом режиме, контролировать ее выполнение, вносить изменения в ход выполнения. Данные средства позволяют проследить весь механизм работы программы и являются средствами динамического исследования работы программ. В качестве примера одного из наиболее мощных отладочных средств можно привести отладчик SoftIce.

Дизассемблеры – программные средства, позволяющие получить листинг программы на языке ассемблера, с целью его дальнейшего статического изучения. Дизассемблеры являются средствами статического исследования.

По способу реализации интерфейса взаимодействия с пользователем, существующие дизассемблеры можно разделить на две категории — автономные и интерактивные.

Автономные дизассемблеры требуют от пользователя задания всех указаний до начала дизассемблирования и не позволяют вмешиваться непосредственно в сам процесс. Если же конечный результат окажется неудовлетворительным, пользователь либо вручную правит полученный листинг, либо указывает дизассемблеру на его ошибки и повторяет всю процедуру вновь и вновь, порой десятки раз. Такой способ общения человека с дизассемблером непроизводителен и неудобен, но его легче запрограммировать.

Интерактивные дизассемблеры обладают развитым пользовательским интерфейсом, благодаря которому приобретают значительную гибкость, позволяя человеку "вручную" управлять разбором программы, помогая автоматическому анализатору там, где ему самому не справиться — отличать адреса от констант, определять границы инструкций и т. д.

Примером автономного дизассемблера является SOURCER, а интерактивного — IDA Pro.

Мониторы событий – программные средства, позволяющие отслеживать определенные типы событий, происходящие в системе.

Редакторы кода занимают отдельное место среди средств обратного проектирования. Данные средства, как правило, включают функции дизассемблирования, но позволяют также вносить изменения в код программы. Данные средства предназначены, в основном, для исследования программ, занимающих небольшой объем.

Классификацию средств обратного проектирования ПО можно представить в виде следующей схемы (рис 9.3).

 

Рис. 9.3. Классификация средств обратного проектирования

Таким образом, использование техники обратного проектирования позволяет злоумышленнику детально разобраться с принципами функционирования программы, в том числе и модулей защиты. Это может ему позволить в дальнейшем атаковать отдельные модули системы защиты. Производитель ПО должен стремиться не допустить этого, он должен противодействовать внутреннему исследованию кода своей программы злоумышленником. Таким образом, защита своих программ от внутренного исследования (в особенности критических модулей, ответственных за безопасность), является одной из актуальных задач для производителя ПО. Производитель должен уметь противостоять всем средствам обратного проектирования, облегчающих злоумышленнику задачу взлома.

9.4.3.  Локализация кода модуля защиты

Задача локализации кода модуля защиты ПО достаточно легко может быть решена злоумышленником с помощью современных средств отладки путем отслеживания вызовов WinAPI функций, используемых разработчиком. Использование стандартных WinAPI функций в настоящее время является неотъемлемым стилем программирования Windows-приложений. Практически любое из них для реализации стандартных действий (вывод на экран сообщений, доступ к тексту редактируемого окна, доступ к файлам и т. д.) использует вызовы данных функций. Основная проблема для разработчиков средств защиты заключается в том, что современные отладчики позволяют без существенных проблем установить точки прерывания по условию вызова известных им функций. Используя данный факт, злоумышленник может получить доступ к программному коду, расположенному вблизи места данного вызова, после чего продолжить изучение кода с помощью средств отладки.

Пример 9.1. Пусть реализация модуля защиты ПО предусматривает вывод на экран сообщения (MessageBox) о неудачной регистрации в случае неверного ввода серийного номера. В данном случае злоумышленник может установить в отладчике точку останова (breakpoint) на вызов функции MessageBox непосредственно перед вводом серийного номера, ввести произвольный серийный номер (однозначно неверный, чтобы сработала функция выдачи сообщения о неудачной регистрации) и тем самым выйти на программный код, осуществляющий вызов данной функции, а значит и на модуль защиты. Дальнейший взлом является делом техники.

Наиболее распространенными Win32 API функциями, используемыми производителями для выполнения стандартных действий, являются следующие.

1. MessageBoxA(hWnd, lpText, LpCaption, uType) – вывод на экран стандартного окна сообщения (например, сообщения об ошибочной регистрации).

2. MessageBoxIndirectA(lpMsgBoxParams) – вывод вариантов окна, с различными кнопками (retry, abort, ignore).

3. GetDlgItemTextA(hDlg, nIDDlgItem, lpString, nMaxCount) – чтение информации, введенной пользователем в окне TEdit.

4. GetWindowTextA(hWnd, lpString, nMaxCount) – чтение информации, введенной пользователем в окне TEdit.

Функции GetDlgItemTextA или GetWindowTextA могут использоваться разработчиком в модуле защиты для чтения ключевой информации, введенной пользователем в окне ввода. Многие модули защиты вслед за этим производят сравнение введенной информации с эталонной.

В качестве менее распространенной функции, выполняющей чтение информации из окна TEdit, можно привести следующую.

5. hmemcpy(lpMemTarget,lpMemSource,nBytes) используется Windows для копирования участков памяти, а значит, может использоваться и при чтении ключевой информации из введенного окна. Отлавливать и исследовать программу по вызову данной функции будет сложнее для начинающих крэкеров, но возможно, если иметь в этом навык. Сложность состоит в том, что, как правило, точка останова будет находиться очень глубоко в программе и потребуется потратить определенные усилия, прежде чем злоумышленник доберется собственно до места вызова процедуры проверки корректности ключевой информации. Прежде чем дойти до этой точки, придется выходить из очень многих вложенных процедур.

ЗАМЕЧАНИЯ. В некоторых случаях, пока пользователь не введет верную ключевую информацию, кнопка подтверждения ввода неактивна. Тогда использование прерываний по функциям MessageBoxA, MessageBoxIndirectA, GetDlgItemTextA, GetWindowTextA бесполезно. В данном случае злоумышленник вынужден пользоваться hmemcpy.

6. GetTickCount() – возвращает число миллисекунд со времени запуска системы. Данная функция часто используется разработчиками для реализации защиты, заключающейся в том, что в незарегистрированной версии программы через определенные промежутки времени появляется информация, убрать которую невозможно в течение некоторого время (nag screen). Отловив вызов данной функции, злоумышленник выходит на процедуру реализации данной защиты и отключает ее.

7. GetDriveTypeA(lpRootPathName) – возвращает тип диска (например, 3-HDD, 5-CD). Разработчик ПО может использовать данную функцию для привязки своего ПО к CD (для предотвращения запуска с винчестера). Отловив вызов данной функции, злоумышленник может проставить тип диска вручную либо отключить механизм проверки.

10. RegCreateKeyEx(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition) – создание ключа в системном реестре. Достаточно часто используется производителями ПО для сохранения в реестре служебной информации (пароля, даты установки программы, количества запусков и т. д.). Отловив вызов данной функции, злоумышленник может раскрыть и получить доступ к конфиденциальной информации либо отключить механизм проверки ключа.

11. Функция SetTimer() и сообщения WM_Timer. Их отлавливание может использоваться злоумышленником для взлома программ, защищенных выводом nag-screen.

12. FindFirstFile(lpFileName, lpFindFileData) – осуществляет поиск в директории файла с заданным именем. Может использоваться злоумышленником для взлома программ, сохраняющих конфиденциальную информацию в служебных, системных, скрытых файлах, файлах инициализации и т. д. Для этих же целей может использоваться отлов функции FindNextFile(hFindFile, lpFindFileData). Злоумышленник, отловив данные функции, может выяснить информацию о файлах, где хранится ключевая информация.

9.4.4.  Базовые методы противодействия отладчикам

Противодействие отладке затрудняет злоумышленнику трассировку ПО, а значит локализацию и исследование ПО и модуля защиты.

Методы борьбы с отладчиками можно подразделить на следующие группы [Error! Reference source not found.].

1.  Использование триков (ловушек) для отладчиков, с помощью которых возможно выявить наличие последнего в оперативной памяти, и прекратить работу программы, либо затруднить процесс отладки.

2.  Выявление наличия отладчика в оперативной памяти и последующая его нейтрализация, используя различные «дыры», допущенные при реализации отладчиков, либо внедренные разработчиком отладчика сознательно.

3.  Использование недокументированных команд и недокументированных возможностей процессора.

Защита от отладчиков реального режима

Работа данных отладчиков построена на использовании двух аппаратных прерываний:

int 1, с помощью которого выполняется пошаговое исполнение программы, и

int 3, с помощью которого в код отлаживаемой программы внедряются точки останова (breakpoints).

При трассировке программы отладчиком задействуется регистр флагов, а именно флаг трассировки TF (восьмой бит регистра флагов при нумерации с нуля), однако, непосредственная его проверка внутри программы для обнаружения отладчика может оказаться безрезультатной, так как большинство отладчиков эмулируют «чистый» регистр отладки.

Основываясь на данных фактах, приведем примеры нескольких триков для отладчиков реального режима:

Трик 1

Данная ловушка помогает выяснить реальное содержимое трассировочного флага TF в условиях эмуляции его отладчиком. Ловушка основана на том, что вследствие аппаратной особенности реализации процессора INTEL после исполнения инструкции pop ss прерывание int 1 не может быть вызвано, и отладчик «не замечает» и пропускает следующую за данной командой инструкцию. Таким образом, следующая за pop ss инструкция исполняется на реальном процессоре, а не под отладкой. Выяснить действительное содержание трассировочного флага в данном случае можно следующим образом:

……

Push ss;

Pop ss;

Защита

Pushf;

Кладем в стек значение флагового регистра. Данную команду отладчик как бы «не замечает», она исполняется на реальном процессоре.

Pop ax;

Восстанавливаем ax из стека.

Test ax,100h;

Проверка флага TF на трассировку.

Jnz DebuggerDetected;

Переход на процедуру завершения работы

……

Трик 2

Для противодействия отладчикам реального режима можно перехватывать в защищаемой программе прерывание int 1 и использовать его для собственных нужд, либо для ссылки на механизм защиты. Тогда при попытке отладки программы она будет либо некорректно работать, либо будет передавать управление модулю защиты, который, например, будет выводить на экран сообщение о невозможности продолжения работы.

Механизм защиты может выглядеть в данном случае следующим образом.

……

Xor dx, dx;

Обнуляем регистр dx

Mov ds, dx;

Обнуляем регистр ds

Mov ax, 2501h;

Int 21h;

Функция 25, подфункция 1. Вектор обработчика первого прерывания устанавливается на адрес 0000:0000

……

Точки останова реализуются отладчиками реального режима следующим образом.

1.  В код программы вставляется вместо точки останова код 0xCC, а значение памяти, которое находилось по его адресу, запоминается отладчиком.

2.  Когда программа встречает команду с кодом 0xCC, она вызывает исключительную ситуацию 0x3h (int 3). При этом в стеке запоминается регистр флагов, указатель текущего кодового сегмента (CS), указатель команд IP, запрещаются прерывания (очищается флаг FI).

3.  После выполнения действий п.2 программа поступает в монопольное распоряжение отладчика.

4.  Обычно, при входе в отладчик последний сохраняет текущие значения всех регистров программы в стеке либо присваивает своим локальным переменным.

Таким образом, установка точки останова требует непосредственной модификации кода программы (0xCC), вынуждая отладчик изменить соответствующий байт программы. Система защиты может попытаться обнаружить факт изменения кода программы и прекратить ее исполнение.

Используя данный факт, для защиты от установки точек останова можно использовать следующие подходы.

1. Зашифровать код программы на некотором ключе, зависящем от контрольной суммы исполняемого кода. При изменении кода программы расшифровка будет неверна и исполнение программы нарушится.

2. Установить из программы вершину стека в нуль. Так как стек ОС растет сверху вниз (к нижним адресам памяти), то когда процессор встретит точку прерывания, установленную отладчиком, то попытается в первую очередь сохранить регистр флагов в стеке, однако сделать этого не сможет, так как стек исчерпан. В данном случае ОС завершит некорректно работающее приложение с ошибкой.

Трик 3 Аппаратное запрещение прерываний

Данная ловушка основана на том, что запрещение прерываний от клавиатуры приводит к зависанию отладчиков реального режима. Если войти в режим отладки, то невозможно будет набрать ни одну команду отладчика, в том числе и выйти из него. Запретить прерывания от клавиатуры в реальном режиме MS-DOS можно, например, следующими способами.

Способ 1

……

In al, 21h

Or al, b

Out 21h, al

Способ 2

In al, 61

Or al, b

Out 61h, al

Способ 3

Mov al, 0Adh

Out 64h, al

Трик 4. Перепрограммирование видеоадаптера

Многие отладчики реального режима перестают корректно работать, если запретить видеовывод через прерывание int 10h.

Защита от отладчиков защищенного режима

Особенностью отладчиков защищенного режима является возможность полной их изоляции от выполняемой программы. В связи с этим, задача обнаружения отладчика в памяти стандартными средствами значительно усложняется. Особенностью защищенного режима является введение специализированных регистров DR0 – DR7, предназначенных для отладочных целей (таких, как установка точек останова, в том числе на обращение к определенным адресам памяти и др.).

Один из способов противодействия отладчикам защищенного режима заключается в манипулировании регистрами отладки DR0-DR7. При наличии отладчика в памяти сама программа либо не может получить доступа к регистрам отладки, либо нарушит работу самого отладчика, например,

……

Lea ebx, continue;

Заносим в ebx адрес перехода

Mov dr0,ebx;

Записываем адрес перехода в DR0

Xor eax, ebx;

Исключаем вероятность совпадения eax и ebx

Mov eax, dr0;

Читаем адрес перехода из DR0

Jmp ax;

Переходим по данному адресу. Если доступ к регистру DR0 запрещен, то мы перейдем по неверному адресу и ход выполнения программы будет нарушен

……

Используя данную особенность защищенного режима можно прибегать к разнообразным трикам.

Трик 5. Шифрование кода программы с расшифровкой через отладочный регистр

……

Mov eax,h;

Ключ, которым зашифрован код программы

Mov dr0,eax;

Сохраним ключ в отладочном регистре

Xor eax,0СВСВСВСВh;

Кладем мусор в eax

Mov eax, dr0;

Возвращаем из регистра отладки ключ в eax

Xor dword ptr cs:[hiddencode], eax;

Расшифровываем код

……

hiddencode:

[зашифрованный код]

Для противодействия отладчикам защищенного режима можно через определенные, достаточно малые интервалы времени производить чистку регистров DR0-DR7.

Трик 6

Данный три основан на том, что отладчики защищенного режима теряют одно трассировочное прерывание при установке регистров DRx. Использование данного трика аналогично трику 1 (потеря трассировочного прерывания по команде pop ss).

Достаточно часто разработчики отладчиков допускают ошибки или специально оставляют дырки в своих продуктах. Нередко, это вызовы API функций, которые могут быть доступны отлаживаемой программе. Одной из таких дырок «страдает» наиболее распространенный отладчик защищенного режима SoftIce.

Пример 9.2. Определение наличия SoftIce в оперативной памяти из отлаживаемой программы

……

Mov bx,202h

Mov ax,1684h

Xor di, di

Int 21h;

Если функция не определена, то di останется неизменным

Or di, di

Jnz SoftIceDectected

У SoftIce данная функция занята

……

Cледует отметить, что рассмотренные приемы защиты от отладчиков не являются универсальными. Они являются всего лишь результатом особенностей архитектуры процессора и инструментов отладки. Данные особенности ограничены, а их принципы исчислимы. Трики можно обойти, если злоумышленник знает о них. Если же злоумышленник не знаком с некоторым триком, то какие-то критические моменты, вызванные срабатыванием трика, могут его насторожить, и при повторной прогонке этого участка кода взломщик обойдет эту ловушку.

Преимущества защиты от отладки с помощью перечисленных методов в том, что трики между собой не связаны, их можно очень легко применять. Недостатками данных методов является то, что они предназначены для защиты только от начинающих взломщиков, профессионалы их обойдут. Трики, как правило, включаются в программу по правилу «чем больше, тем лучше» и от их исключения логика работы программы не меняется. В некоторых случаях совокупный размер кодов триков сравним с размером программы.

9.4.5.  Базовые методы противодействия дизассемблированию ПО

Дизассемблирование машинного кода злоумышленником проблематично даже в том случае, когда противодействие ему не предусмотрено. Задача автоматической идентификации какого-то участка программы как данных или как кода довольно сложна и неоднозначна: программист может использовать код как данные или наоборот.

Методы защиты ПО от дизассемблирования пытаются «обмануть» дизассемблера, заставить его сбиться, произвести дизассемблирование неверно, перепутать код и данные, выдать на своем выходе «мусор». В этом случае злоумышленник не сможет правильно исследовать логику работы ПО.

Рассмотрим несколько основных подходов к защите ПО от дизассемблирования.

1. Шифрование кода программы. Защищаемый участок кода шифруется каким-либо алгоритмом, а в программу добавляется модуль дешифрования, который в нужный момент дешифрует его и передаст ему управление. Защищаемый участок кода перед дизассемблером предстанет в зашифрованном виде и будет воспринят дизассемблером неверно, на выходе дизассемблера будет сформирован «мусор».

Данный метод при неправильном использовании достаточно уязвим ввиду того, что алгоритм расшифровки доступен взломщику, необходимо лишь найти его и произвести расшифровку. Поэтому шифрование должно производиться на секретном ключе, который доступен только легальному пользователю и никому другому.

2. Сокрытие команд передачи управления. Реализация данного подхода к защите приводит к тому, что дизассемблер не может построить граф передачи управления, и, соответственно, человек должен будет сам вычислять адрес перехода. Например, можно модифицировать адреса переходов при выполнении программы. Если команда модификации и модифицируемая команда достаточно далеко разнесены друг от друга, то велика вероятность того, что человек упустит эту модификацию из виду.

Пример 9.3.

Mov word ptr cs:m[1],1234h;

модификация адреса перехода

……

m: jmp place;

вместо метки place ранее внесен другой адрес

……

Пример 9.4.

mov word ptr cs:m[1],es;

модификация адреса вызываемой подпрограммы

mov word ptr cs:m[3],5678h

……

m: CALL far 0000h;

данный адрес перехода был ранее модифицирован

……

3. Использование косвенной передачи управления.

Данный подход к защите можно проиллюстрировать следующим примером.

Пример 9.5

Mov bx, 1234h

Jmp dword ptr cs:[bx]

4. Использование нестандартных способов передачи управления

Для затруднения изучения дизассемблированного кода могут использоваться нестандартные способы передачи управления, по каким либо адресам. Разработчик может, например, моделировать работу операторов JMP, CALL, INT … средствами других операторов, что затруднит понимание листинга на языке ассемблера. Примеры возможных альтернативных записей команд передачи управления приведены в таблице 9.2.

Табл. 9.2. Примеры возможных альтернативных записей команд передачи управления

Первичный код

Альтернативный код

Jmp m

m:

Mov ax, offset m

Push ax

Ret

m:

Call subr

m:

subr:

Ret

Mov ax, offset m

Push ax

Jmp subr

m:

subr:

Ret

int 13h

pushf; флаги в стек

push cs

mov ax, offset m

push ax; занести в стек адрес возврата

xor ax, ax

mov es, ax

jmp dword ptr es:[13h*4]

m:

ret

pop bx

jmp bx

Iret

Mov bp, sp

Jmp dword ptr [bp]; переход на точку возврата из прерывания

add sp, 4; точка возврата

popf

5. Осуществление короткого перехода вперед, а между командой перехода и адресом перехода добавление «мусора». В данном случае, можно добиться совершенно неверного результата дизассемблирования, в особенности для автоматических дизассемблеров.

Пример 9.6

Seg000:0100 add si, 6; фрагмент кода

Seg000:0103 jmp si; фрагмент кода

Seg000:0105 db 0B9h; фрагмент данных

Seg000:0106 mov si, 114h; фрагмент кода

В приведенном выше примере дизассемблер не сможет в автоматическом режиме определить адрес перехода во второй строке и попытается интерпретировать код с адреса 0000:0005 как инструкции. В этом случае результат дизассемблирования будет совершенно неверен и будет выгладеть следующим образом:

Seg000:0000 add si,6;

Seg000:0003 jmp si;

Seg000:0005 mov cx, 14BEh;

Seg000:0008 add [di+5691h], bp

9.4.6.  Защита от отладки

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

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16