Анимация
JavaScript
|
Главная Библионтека Дизассемблер не понимает, что условие заведомо истинно, а "дальний перо сфабрикованный нами, никогда управления не получит. Поэтому он добросове дизассемблирует несуществующую цепь команд. Интерактивные дизассемблеры формируют исходный текст/листинг по выполнимо1у коду программы так же, как это делают автоматические дизассемблеры. Однако интерац тивные дизассемблеры отличаются от автоматических наличием мощного пользователь, ского интерфейса, который сильно облегчает анализ дизассемблированной профаммы. Интерактивные дизассемблеры, как правило, позволяют: менять имена переменных, меток, подпрофамм и т. д., вводить имена для новы) адресов, удалять имеющиеся метки/имена; а искать последовательности символов в результирующем тексте и последовательносп байт в исполнимом коде; повторно дизассемблировать участки кода в последовательность ассемблерны команд или директив DB; задавать комментарии к подпрофаммам, прерываниям и т. д., которые автоматическ расставляются около всех соответствующих вызовов; просматривать перечень сегментов профаммы; редактировать дизассемблированный текст с автоматической модификацией испо.: нимого кода или без таковой. Различные интерактивные дизассемблеры предоставляют также и иные возможност: Самые совершенные интерактивные дизассемблеры (IDA) позволяют не только меня! уже дизассемблированный код, но и вмешиваться в сам процесс дизассемблирования. Вышеописанные способы хороши против автоматических дизассемблеров. Однако запутать IDA (или любой другой интерактивный дизассемблер) только с их помощью не удастся. Точнее, удастся, но лишь до того момента, пока наш противник не сообразит принудительно обозначить засоряющие байты как Undefined, а все после них - как код. Сразу же после этого он получит возможность анализа защищаемой профаммы в среде дизассемблера. Против этого существует сложный, но эффективный прием, получивший на хакер-ском жаргоне название "динамический фуфель". Суть приема заключается в том, что засоряющие байты никак не обходятся командами передачи управления. Они замещаются безобидными командами (NOP, STI и пр.) уже в ходе выполнения профаммы, н" заведомо до первого запуска подпрофамм, содержащих эти "фуфели". Другими словами-защищаемый от дизассемблирования фрагмент профаммы действительно не может быть запущен в том виде, в каком профамма находится на диске - скорее всего, это приведем к зависанию компьютера. Однако, запустившись, профамма считывает откуда-то Д" ные, необходимые для устранения засоряющих байтов, и замещает их на команды, никз" не влияющие на ход выполнения профаммы. Взлом профаммы, которую защитили таким способом - длительный и трудоемкий оцесс, даже если данные для "дефуфелизации" содержатся в коде самой профаммы. \ожно и еще усложнить взлом, сохраняя эти данные на нулевой дорожке или в умыш-.,енно оставляемом "зазоре" между разделами дисковой подсистемы. В данном случае само по себе копирование секретной профаммы ничего противнику не даст - он потеряет данные, необходимые для ее преобразования к нормально работающему состоянию. 3 Х.4. Защита от отладчиков реального режима Защититься от исследования под отладчиком можно двумя путями: g тем или иным способом обнаружить отладчик и передать управление на некоторую ветку реакции на отладчик; g "зафязнить" профамму фрагментами кода, которые нормально выполняются без отладчика, но под отладчиком приводят к аварийному завершению, зависанию компьютера или искажению хода выполнения профаммы. 3.1.4.1. Обнаружение отладчика Отладчики реального режима достаточно просто обнаружить. Можно выделить две основные фуппы методов их обнаружения: I использование аппаратных особенностей процессора, в частности наличие очереди команд, а также потеря трассировочного прерывания после выполнения некоторых инструкций, например инструкций изменения содержимого сегментных регистров по командам MOV или POP); выявление изменений операционной среды путем проверки векторов прерываний, проверки времени выполнения отдельных участков профаммы, проверки начальных состояний регистров при запуске профаммы и т. п. Отладчики используют такие ресурсы компьютера, как отладочные прерывания INT 1 (трассировочное прерывание или прерывание пошаговой работы), INT 3 (прерывание контрольной точки) и флаг трассировки TF. Все это может применяться для обнаружения исследования под таким отладчиком защищаемой профаммы. Дело в том, что процессоры Intel 80x86 "теряют" трассировку одной команды, если предыдущая команда изменяла значение сегментного регистра. Поэтому можно обнаружить установку флага трассировки TF в процессе отладки, например так. Пример 3.5. ==================================== mov ах, SS push ах pop SS pushf pop ax pushf pop sub raov raov ax, bx bx, OFFSET BeingDebugged [bx], ax Вариацией этого метода является "ловля" отладчика на командах (префиксах) переу тановки сегмента. Пример 3.6.
При пошаговой трассировке данных фрагментов, например, в среде TD переменн( BeingDebugged присвоится значение lOOh. Однако этот способ не гарантирует 100%-й вер ятности обнаружения отладчика, так как фрагмент может быть пройден не по шагам, а сра; (команда Go to cursor / F4 или Step over / F8). Кроме того, хорошие отладчики реального \ жима (например, InSight 1.01) прерьгеание ШТ 1 не используют - т. е. способ не работас а с некоторыми очень плохими отладчиками (например, IVIMD 1.00) он тоже не проходит, т. как они используют INT 1, но не сбрасывают флаг TF вообще! Поэтому нужно примеш и другие способы обнаружения отладчиков реального режима. Самый очевидный из них - проверка байта, находящегося по вектору прерывания IN1 (можно и INT 1, но это менее надежно, так как хорошие отладчики реального режима, г пример, InSight, INT 1 вообще не перехватывают). Вектор лучше получать непосредстве но из таблицы векторов прерываний, а не вызовом функции 35h прерывания INT 21h. ;===== Пример 3.7. ==================================== хог ах, ах mov es, ах mov bx, OCh dh, dh xor mov mov mov sub add raov dl, BYTE PTR es:[bx] bx, OFFSET BeingDebugged ax, [bx] dl, OCFh ax, dx [bx], ax Код команды iret После выполнения этого фрагмента под отладчиком реального режима (даже такИ* хорошим, как InSight!) переменная BeingDebugged будет иметь ненулевое значение f еперь в любом месте программы можно сравнить значение с нулем и, если не равно, оеагировать на отладчик. Следующий способ обнаружения отладчиков реального режима основан на реализации в последних механизма точек останова по 1NT 3. Когда в таком отладчике ставят точку останова, байт по этому адресу замещается на однобайтовую команду ШТ 3 (КОП OCCh). Соответственно профамма может обнаружить эти команды, взвести флаг обнаружения отладчика (в наших примерах - BeingDebugged) и среагировать на отладчик по проверке этого флага. Способов обнаружения этих команд (и вообще искажения кода, так как есть отладчики, ставящие вместо ШТ 3 вызов другого прерывания - например, Meffistofel) можно придумать много - начиная с поиска байта OCCh командой SCASB и кончая вычислением различных контрольных кодов целостности с защищаемым куском кода в качестве аргумента. На процессорах семейства 486 возможно обнаружить отладчик, используя буфер предвыборки команд. Изменение кода команды, которая уже выбрана и находится в этой очереди, никак не повлияет на ход выполнения профаммы. Под отладчиком же очередь предвыборки постоянно сбрасывается, и выполнится измененная команда. ;===== Пример 3.8. ==================================== Critical: Normal; raov clc jnc raov mov BYTE PTR Crirical, 0F9h ; Код операции stc Normal bx, OFFSET BeingDebugged [bx], 1 ; Нормальное выполнение программы Первая команда этого примера никак не повлияет на нормальное выполнение профаммы, так как пересьшка выполняется в оперативную память, а команды уже находятся в буфере предвыборки. Под отладчиком же выполнится измененный код, и флаг отладки будет взведен. Однако нужно помнить, что этот способ годен лишь на вспомогательные роли, так как даже если профамма будет выполняться на встраиваемой машине с аналогом 486-го процессора, исследовать ее, по всей вероятности, будут на Pentium. А там конвейеризация реализована иначе, и буфера предвыборки нет. Следующий способ обнаружения отладчика применим только против некачественных отладчиков, таких, как CodeView или Turbo Debugger. Он основан на том, что при загрузке профаммы определенным образом инициализуются регистры. При этом в регистр СХ заносится ненулевое значение - в СОМ-профаммах это длина СОМ-файла, а в ЕХЕ-профаммах - размер кода в оперативной памяти. Регистр DI устанавливается равным причем значение SP не равно нулю. Исследование же программы под отладчиком требует неоднократных прогонов. CodeView и TD при первом прогоне профаммы обнуляют регистры АХ, ВХ, СХ, DX, S1, DI, BP. При повторном прогоне программы Пример 3.9. === call MyProc; push crap MyProc bx bx [bx], OCCh Debug Проверка после вызова MyProc 3.1.4.2. Искажение работы программы под отладчиком реального режима Все способы, описанные в 3.1.3.1, вполне применимы для реальных защит, но у них есть существенный недостаток: можно вообще не разбираться, как профамма определяет факт работы под отладчиком! Противнику достаточно найти команду перехода на ветку реакции на отладчик - и все наши ухищрения становятся бесполезными. Поэтому в реальных защитах их нужно дополнять трюками, искажающими работу профаммы без явных проверок. Можно выделить следующие методы искажения хода выполнения профаммы под отладчиком: противодействие установке контрольных точек и изменению кода профаммы, например периодической проверкой контрольных сумм различных участков программы, чередованием команд запрета и разрешения прерываний и т. п. ; в нарушение интерфейса с пользователем, например путем блокировки клавиатура-искажения вывода на экран и т. п.; использование отладочных прерываний (а иногда и не только отладочных!) для реализации таких ответственных действий, как генерация участков кода, шифрование, вызов других подпрофамм системы защиты; определение стека в области исполняемого кода и неоднократная его смена. Если отладчик не обнаружен (BeingDebugged = 0), выполняется возврат на следующую за RETN команду. Если же BeingDebugged о О, то выполняется переход на команду по смещению CS: АХ, где АХ - это преобразованное значение BeingDebugged. При этом 1Ы, как правило, попадаем в середину некоторой последовательности команд или даже в середину многобайтной команды, что приводит обычно к повисанию DOS и невозможности дальнейшей работы. Следующий способ исказить выполнение профаммы под отладчиком - прибавлять значение переменной BeingDebugged к смещению в регистре при косвенных вызовах профамм.
Как видим, если отладчик не обнаружен, засекреченная подпрофамма нормальнс вызовется. Если же его засекли, то смещение будет искажено, и это, по всей вероятности, приведет к повисанию DOS. Можно подменять вектора отладочных прерываний (INT 1, INT 3). Здесь открывается широкий простор для фантазии разработчика - можно поменять их местами, заместить на вектор INT 19h, INT 20h или любой другой вектор, сдвигать сегмент или смещение в Этих векторах, модифицировать код обработчика и т. д. Попытка трассировки профаммы при таких перестановках опять же приводит к повисанию отладчика или DOS. Следующий способ - блокировать видеоподсистему. -=== Пример 3.12. =================================== Последовательность ассемблерных команд, ======== "==== приводящая к повисанию любого отладчика. ======= mov ах, 1201h mov Ы, 32h int lOh
Можно использовать запрещение работы с клавиатурой. CodeView опять обнуляет эти регистры, а Turbo Debugger вообще не трогает мус. оставшийся после предыдущего прогона. Сравнивая в начале профаммы значен регистров с требуемыми, можно обнаружить отладчик (например, по условию СХ = или DI о SP) и взвести флаг обнаружения или сразу же перейти на ветку реакции на отладчик. Против отладчиков высокого качества исполнения (InSight, DeGlucker и пр j данный прием бесполезен. Наконец, рассмотрим метод обнаружения отладчика, основанный на поиске точкц останова. Начнем с использования флага обнаружения отладчика (точнее говоря, переменнот туса этого обнаружения - она вовсе не обязана содержать лишь 2 значения - О и Г искажения выполнения профаммы. ,,,г== Пример 3.10. =================================== mov ах, BeingDebugged shl ах, 3 push ах retn 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 [ 37 ] 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |