Методы затруднения дизассемблирования предполагают что
Перейти к содержимому

Методы затруднения дизассемблирования предполагают что

  • автор:

20. Защита программ от изучения. Цели, методы, средства изучения программ.

Защита программ от изучения. Цели изучения программ. Задача анализа программного обеспечения возникает в самых различных ситуациях. Например, что компьютерную сеть нашей организации поразил неизвестный компьютерный вирус. Для того, чтобы создать способный с ним бороться антивирус, необходимо понять принципы функционирования этого вируса, а для этого необходимо провести анализ машинного кода вируса. Другой пример. Систему защиты информации нельзя считать надежной до тех пор, пока не проведена экспертиза, которая показала, что известные методы преодоления систем защиты данного класса не работают для тестируемой системы. Группа экспертов становится в позицию злоумышленника и пытаются преодолеть защиту, реализуемую тестируемой системой. Если это не удаётся, систему можно рекомендовать для использования. Экспертиза должна включать в себя апробирование всех известных методов преодоления защиты, в том числе методов, связанных с анализом программного обеспечения. Таким образом, при проведении экспертизы системы защиты информации возникает задача анализа программного обеспечения этой системы. Однако чаще всего программы изучаются взломщиками, пытающимися создать пиратские копии лицензионных программных средств. Задачу изучения программы можно сформулировать следующим образом. Мы имеем бинарный код программы и минимальную информацию о том, что эта программа делает. Нам нужно получить детальную информацию о функционировании этой программы. Под программой подразумевается не только exe– или com–файл, но и библиотеку функций, драйвер устройства и т. д. Hа каком бы языке Вы не писали, какие бы способы защиты не использовали, все, пpевpатится в команды пpоцессоpа. В нашем слyчае — семейства intel x86. Так что, полyчив листинг пpогpаммы, взломщик видит знакомые ассемблеpные команды, пpичем для анализа алгоpитма pаботы пpогpаммы в большинстве слyчаев не важно, с помощью чего она была создана. Достаточно лишь хорошо pазбиpаться в работе пpоцессоpа и в аpхитектypе компьютеpа. 6.3.2 Методы изучения программ В настоящее время сформировались три подхода к восстановлению алгоритмов, реализуемых программой: • метод экспериментов; • статический метод; • динамический метод. Метод экспериментов заключается в проведении многократных экспериментов с изучаемой программой и сравнительном анализе полученных результатов. Изучаемая программа рассматривается как «черный ящик», для которого известны входные и выходные данные, но неизвестно внутреннее устройство. Задача аналитика заключается в том, чтобы, подбирая входные данные, восстановить алгоритмы функционирования «черного ящика». Эффективность метода экспериментов слабо зависит от программной реализации системы защиты (на нее, в частности, не влияет применение средств защиты программного обеспечения от изучения) и определяется, в первую очередь, сложностью анализируемых алгоритмов. Метод экспериментов редко применяется в чистом виде. Чаще этот метод применяется как дополнение к динамическому или статическому методу. Метод экспериментов эффективен при анализе программ, реализующих относительно простые алгоритмы. Такие программы встречаются довольно редко. Сущность статического режима заключается в изучении исходного текста программы. Для получения листингов исходного текста выполняемый программный модуль дизассемблируют, то есть получают из программы на машинном языке программу на языке Ассемблер. В настоящее время статический метод используется чаще всего как вспомогательный инструмент для проверки предположений о восстанавливаемых алгоритмах защиты. Динамический режим изучения алгоритма программы предполагает выполнение трассировки программы. Под трассировкой программы понимается выполнение программы на ЭВМ с использованием специальных средств, позволяющих выполнять программу в пошаговом режиме, получать доступ к регистрам, областям памяти, производить остановку программы по определенным адресам и т. д. В динамическом режиме изучение алгоритма работы программы осуществляется либо в процессе трассировки, либо по данным трассировки, которые записаны в запоминающем устройстве. Средства противодействия дизассемблированию не могут защитить от трассировки и наоборот: программы, защищенные только от трассировки, могут быть дизассемблированы. Поэтому для защиты программ от изучения необходимо иметь средства противодействия как дизассемблированию, так и трассировке, так как дизассемблирование и отладка обычно используются вместе. Средства изучения программ Средства анализа исполняемого кода Первое средство – декомпилятор. Процесс перевода из двоичного вида в символьный, на языке команд какого-нибудь языка. Например, дизассемблеры, деклиппер, obj2asm и многие другие. Эти вещи появились раньше отладчиков, т.к. в начале не было архитектуры со встроенными средствами отлаживания программ. В чем их неудобство: 1. Неверное определение размеров данных. Например, вы дизассемблировали программу закрытую HASP ключом. Чтобы ее взломать, вам нужно найти точку входа в HASP API. Она находится сразу за строкой HASPDOSDRV. Найдете ее после дизассемблирования очень сложно. 2. Отсутствие динамики. 3. Статичный анализ (если данные зашифрованы, то декомпилятор их не расшифрует). 4. Огромное количество незначимых для Вас команд. 5. Невозможность посмотреть регистры, стек и память. В чем преимущество: 1. Возможность изменения исходного кода программы. 2. Невозможность обнаружения. Существует несколько проблем при работе дизассемблеров. Проблема восстановления символических имен. В программе, написанной на языке ассемблера все переменные, метки, процедуры и сегменты имеют символические имена. При компиляции программы эти имена заменяются физическими адресами. В скомпилированном машинном коде не остается информации о символических именах, если, конечно, она специально не помещена туда для отладки или для взаимодействия с другими программами (например, динамически подгружаемые библиотеки Windows содержат таблицу имен экспортируемых функций, необходимую для их импорта программами). Обычно эта проблема решается путем «придумывания» дизассемблером символических имен типа var1, label2, proc3 и т.д. Проблема различения команд и данных. В скомпилированной программе машинный код и данные отличаются друг от друга только по контексту использования. Например, машинная команда может непосредственно следовать за глобальной переменной. Если дизассемблер принимает код за данные или наоборот, текст, выдаваемый дизассемблером, становится бессмысленным. Проблема определения границы машинной команды. Команды машинного кода следуют друг за другом подряд непосредственно, без каких-либо разделителей. При выполнении кода процессор начинает считывать очередную команду с байта, непосредственно следующего за только что выполненной командой. Если дизассемблер неправильно определяет границу команды, он неправильно восстанавливает эту команду и несколько команд, следующих за нейТаким образом, дизассемблеры могут допускать серьезные ошибки. Дизассемблеры условно можно разделить на «глупые» и «умные». «Глупые» дизассемблеры даже не пытаются решить описанные проблемы, в результате чего результат работы дизассемблера напоминает ассемблеровский текст весьма отдаленно. В то же время «глупые» дизассемблеры преобразуют машинный код очень быстро (почти мгновенно). К «глупым» дизассемблерам относятся встроенные дизассемблеры отладчиков и антивирусных утилит, предназначенные для интерактивного просмотра небольших участков машинного кода. «Умные» дизассемблеры преобразуют машинный код в ассемблеровский настолько точно, что в отдельных случаях повторное ассемблирование приводит к тому же самому машинному коду. «Умные» дизассемблеры различают команды и данные, практически всегда правильно определяют границы машинных команд, выделяют в машинном коде отдельные функции, отслеживают перекрестные ссылки. Время дизассемблирования программы «умным» дизассемблером может составлять десятки минут. Редко, но бывает необходимым внесения крупных изменений в код программы. Прямая вставка двоичных кодов не помогает, т.к. нарушается расположения меток перехода и процедур. Повторная перекомпиляция вписывает новые смещения для джампов и колов. ЭТО ОЧЕНЬ РЕДКИЙ СЛУЧАЙ. Но разработчики дизассемблеров давно учли сложности использования своих программ. И появились такие программы, как Хакер-VIEW (HIEW) и IDA (Интерактивный Дизассемблер). HACKERVIEW выпускается как внешний просмотрщик для Нортона. Вы можете просмотреть любой исполняемый файл по любому смещению, а также «выполнить» какую-то часть программы или собственную программу, написанную на ассемблере. Это позволяет расшифровывать программы и обходить защиту от дизассемблирования. Он понимает, как старые форматы исполняемых файлов DOS-COM и DOS-EXE, так и форматы исполняемых файлов Windows и OS/2. IDA очень мощное средство работы с ассемблерными текстами программ. Обладает такими же возможностями, как и HACKERVIEW, но имеет более удобный интерфейс. Также очень хорошо предусмотрена архитектура работы программ в Windows. Т.е. такие вещи, как DLL, расширенный режим работы с памятью и т.д. В своей практике я ни разу не использовал IDA для ломки, но для анализа вирусов приходилось. Очень хорошее средство. Интерактивные декомпиляторы программ занимают свою нишу в инструментарии кракера. В основном это совместное использование с отладчиками, где основную работу делают отладчики. Дело в том, что программирование, благодаря Windows, в основном стало событийным, а не линейным как это было в ДОСе. Поэтому проще в отладчике поставить брейк-точку на нужное нам событие, анализируем, что готовит программа. И уже после, если того требует необходимость, лезем HEIW в нужную часть программы. Но многие задачи не требуют такого совместного использования. Пошаговые трассировщики. Пpактически любая компьютеpная аpхитектypа должна пpедyсматpивать наличие каких-либо аппаpатных или хотя бы пpогpаммно-аппаpатных сpедств отладки. Изначально сама по себе концепция отладки в PC была пpогpаммно-аппаpатной, с pасчетом на возможность пpедоставления пользователю пpогpаммного интеpфейса общения с механизмами отладки. Все, что было заложено фиpмой intel с самого начала, — это два «особенных» аппаpатных пpеpывания, пpедназначенных для отладочных целей: int 1, с помощью котоpого выполнялось пошаговое исполнение пpогpаммы, и int 3, пpедназначенного для вставок точек останова пpоцессоpа (break points) в код отлаживаемой пpогpаммы. Хоть эти пpеpывания и генеpиpовались самим пpоцессоpом, но контpоль над их генеpацией и обpаботка этих пpеpываний возлагалась на отладчик пpогpаммного ypовня. С выходом в свет пpоцессоpа i80386, появились новые возможности отладки, связанные с особенностями 32-битного защищенного pежима пpоцессоpа, а точнее, с возможностью полностью изолиpовать выполняемые пpогpаммы дpyг от дpyга (напpимеp, отладчик от отлаживаемой пpогpаммы). Были введены специальные pегистpы DR0-DR7, пpедназначенные для отладочных целей (таких как yстановка break points на обpащение к опpеделенным адpесам памяти и поpтам). Тепеpь отладчик, запyщенный с нyлевым CPL (Code Privelege Level), полностью защищен от возможных воздействий со стоpоны отлаживаемой пpогpаммы, котоpyю он запyскает с более низким ypовнем пpивелегий. И взламываемая пpогpамма, пpи пpавильной pеализации отладчика (не без исключений, конечно), yже не может отpавить жизнь отладчикy, Пеpеходя от отладчиков в частности к reverse engineering’y вообще, стоит yпомянyть о пpинципиально дpyгом подходе к отладке: об эмyляции исполняемого кода, когда дешифpацию и выполнение инстpyкций пpоизводит не pеальный пpоцессоp, а пpогpамма-эмyлятоp. Сyть эмyлятоpов состоит в том, что выполняемая пpогpамма не должна иметьвозможность полyчить достyп к pеальным pесypсам компьютеpной системы. Эмyлятоp как бы «находится» междy pесypсами системы и отлаживаемой пpогpаммой, виpтyализиpyя все компоненты системы, необходимые ей для pаботы (такие, как память, поpты). Пpогpамма исполняется медленней, но это компенсиpyется тем, что она чyвствyет себя совеpшенно по-дpyгомy: ведь никаких отладчиков и пpочих пpогpамм в ее адpесном пpостpанстве нет.

19.05.2015 146.94 Кб 91 Ощущение.doc

14.11.2019 144.9 Кб 2 П-2Оценка концентрации пыли в воздухе рабочей з. doc

13.11.2019 1.09 Mб 4 П-№1 Исследование микроклимата производственной. doc

13.11.2019 199.68 Кб 2 П-№5 Исследование вибрации рабочих мест.doc

25.08.2019 47.17 Кб 3 Павленко О.С ПСК-41 Социология конфликта..docx

25.04.2019 147.65 Кб 13 ПАЗИ.docx

24.11.2018 226.3 Кб 3 Панов.doc

19.03.2016 100.66 Кб 24 Пантеон скандинавских богов.rtf

07.09.2019 1.69 Mб 19 ПАРКЕТ.doc

14.11.2019 257.54 Кб 9 Паровая завеса В РИЦ.doc

28.09.2019 1.18 Mб 7 пбс.docx

Ограничение

Для продолжения скачивания необходимо пройти капчу:

Глава 3. Общие методы защиты программ от отладки и дизассемблирования

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

Один из методов, используемый для затруднения отладки и дизассемблировании программ, заключается в привлечении редко используемых инструкций процессора, недокументированных инструкций, или инструкций имеющих скрытый результат. В данном случае, не все злоумышленники хорошо знакомы с такого рода командами и скрытыми возможностями процессора. Недостаток данных методов – жесткая привязка к процессору. Кроме этого, не гарантируется поддержка недокументированных инструкций в будущих модулях процессорах, а значит и совместимость с ними разработанных защит.В качестве примеров «сокрытия» реальных инструкций можно привести примеры, приведенные в таблице 2.1.Рассмотрим особенности записи кодов инструкций в процессоре INTEL.

Реализация процессора INTEL предполагает следующий формат инструкций для него:

Префикс | Опкод | ModR / M | SIB | Смещение | Непосредственный операнд

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

Префиксы блокировки и повторения – говорят о том, что код инструкции относится к действиям блокировки или повторения.

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

Таблица 2.2. Соответствия префиксов и кодируемых ими сегментов

3. Префиксы переопределения размеров операндов (префикс 66h). Данный префикс используется в 16-разрядном режиме для манипулирования с 32-битными операндами и наоборот.

4. Префиксы переопределения размеров адреса (префикс 67h).

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

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

1. В качестве одной из недокументированных возможностей процессора INTEL можно использовать недокументированную возможность использования префиксов, например, префикса переопределения размеров операндов. Согласно стандарту, он используется только при наличии в команде каких-либо операндов. Однако, на практике для реального процессора, данный префикс может быть поставлен совершенно перед любой командой, и это будет работать на реальном процессоре. Например, как это ни странно, реальный процессор воспримет инструкцию 0x66 CLI (использование префикса перед оператором запрещения прерываний). В связи с тем, что данная особенность не документирована (предполагается, что никому в голову писать подобные вещи), , то как правило, отладчики и дизассемблеры не воспринимают подобные инструкции и отказываются корректно интерпретировать данный код.

Тоже самое следует сказать и про префиксы переопределения размеров адреса. Данные префиксы согласно документации используются только в командах, оперирующих с адресами памяти. Их использование в других командах не документировано, но процессор будет эти команды выполнять. В качестве примера такой инструкции можно привести инструкцию 0x67 STI.

Приведенные методы позволяют противостоять, также, и виртуальным эмуляторам процессоров (см. п. 4).

2. Использование префикса переопределения размеров операндов совместно с инструкцией RETN

Достаточно мощным приемом противодействия отладчикам и дизассемблерам, основанным на использовании недокументированных возможностей, является использование префикса переопределения размеров операндов совместно с командой RETN. Казалось бы, раз команда RETN не имеет операндов, то префикс 66 процессор игнорирует, но это не так. Дело в том, что RETN работает с неявным операндом-регистром ip/eip. Именно этот операнд и изменяет префикс. В реальном и 16-разрядном режиме указатель команд всегда обрезается до 16 бит и на первый взгляд все сработает корректно, однако, при записи префикса, стек окажется несбалансированным. Из него вместо одного слова возьмется два. Как правило, это приводит к возникновению исключительной ситуации 0Ch – исчерпание стека. Это приводит к зависанию большинства отладчиков, а дизассемблеры на смогут отследить стек. Однако, данный пример будет работать только в реальном режиме. Под Windows перехватить прерывание 0Ch не представляется возможным.

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

4. Использование дублирующих префиксов, то есть записи префиксов вида 0x66,0x66 непосредственно перед командой. Хотя фирма INTEL не гарантирует корректную работу своих процессоров при обнаружении такого рода инструкций, но фактически все процессоры правильно интерпретируют данные ситуации. Иное дело – отладчики и дизассемблеры, которые спотыкаются и начинают некорректно вести себя.

Следует отметить, также, что процессором INTEL корректно выполняются и инструкции вида DS:FS:CS:Mov ax, [100] (последний префикс перекрывает все остальные), а отладчики и дизассемблеры сбиваются при их анализе. Данный пример хорошо работает под Windows и другими операционными системами.

5. Обращение к недокументированным регистрам. В процессорах INTEL регистры в настоящее время кодируются 3-мя битами следующим образом (таблица 2.3).

Таблица 2.3. Кодирование регистров в инструкциях

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

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

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

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Алейников С. И., Богатов А. О.

О методах деобфускации программ
Комплексная кольцевая система защиты программного обеспечения от нелицензионного использования
Методика обнаружения следов вредоносного программного обеспечения в дампах оперативной памяти
Методика применения языка Ассемблер для стеговложения информации в исполняемые файлы
Определение границ подпрограмм при статическом анализе бинарных образов
i Не можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Текст научной работы на тему «Защита программ от дизассемблирования»

Защита программ от дизассемблирования

С. II. Алейников. А. О. Богатов

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

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

Трудность задачи дизассемблирования для архитектуры х86 известна давно. Она обусловлена следующими причинами:

1. отсутствует чёткое разделение данных и кода, в результате чего код можно принять за данные и наоборот:

2. не фиксирована длина инструкций, вследствие чего иногда нельзя определить, где начинается та или иная инструкция:

3. имеется косвенная адресация, поэтому мы можем даже примерно не знать адрес, на который осуществится переход в результате исполнения некоторой команды передачи управления:

4. возможна модификация кода во время исполнения:

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

Возникает вопрос: как использовать данные особенности при создании кода. трудного для дизассемблирования, и каковы шансы успешно восстановить код. подвергнутый подобным преобразованиям?

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

2. Разновидности и цели дизассемблирования

Прежде всего следует поставить границы рассматриваемой задачи. Анализировать программы можно двумя способами.

Во-нервых. статически (дизассемблирование) — анализируя программный код. записанный в исполняемом файле, физически этот файл не исполняя.

Во-вторых, динамически (отладка) — анализируется программный код во время исполнения (в пошаговом режиме или при помощи статического анализа снятого «дамна» программы).

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

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

3. Простейшие методы дизассемблирования

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

Как только алгоритм видит следующий необработанный байт, он пытается рассмотреть этот байт как начало инструкции. Если не получилось восстановить инструкцию, алгоритм переходит к следующему байту. Основной недостаток этого способа — очень часто данные трактуются как инструкции.

При использовании рекурсивного обхода одновременно с дизассемблированием инструкций строится граф потока управления. После декодирования инструкции передачи управления (ИПУ) начинается декодирование но всем возможным адресам перехода. Главный недостаток такого подхода — в том. что возникают значительные затруднения в случае динамического вычисления адреса перехода (косвенной адресации).

Приведём простой пример (взятый из |2|) работы этих двух методов на одном и том же коде. Исходный (ассемблируемый) код таков:

mov esp, ebp 89 e5

call 1978808 cbranch fnct> e8 00 00 74 11

cmp 0, eax 3c 00

jne 8048014 75 06

mov 0, eax bO 00

jmp 8048019 eb 07

LI: mov (1740000), eax al 00 00 74 01

L2: mov ebp, esp 89 ec

Ъшейный проход даёт такой результат:

mov esp, ebp 89 e5

call 1978808 cbranch fnct> e8 00 00 74 11

or 675003c, al 0a 05 3c 00 75 06

mov 0, eax bO 00

jmp 8048019 eb 07

or 740000al, al 0a 05 al 00 00 74

adc ecx, 90c35dec(ecx) 01 89 ec 5d c3 90

Следующий результат получен методом рекурсивного обхода:

риэЬ еЬр то¥ еэр, еЪр

са11 1978808 сЪгапсЬ. fnct> ог 675003с, а1 то¥ 0, еах ^¡тр 8048019

е8 00 00 74 11 0а 05 Зс 00 75 06 ЪО 00 еЪ 07

0а 05 а1 00 00 74 01

[^ипк] то¥ еЪр, еэр рор еЬр ret пор

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

4. Основные методы затруднения дизассемблирования

Основные методы защиты от статических атак:

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

• встлвка «.мусора» — вставка «лишних» байт в недостижимые участки кода так. чтобы они казались достижимыми и были расшифрованы инструкции, следующие за ними:

• вставка переходов на данные / (текстовых) ссылок на код.

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

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

• динамическое вычисление адреса перехода — переход выполняется командой вида jmp [edi] :

• подмена адреса возврата в стеке в самой функции: при переходе но команде call в стек помешается адрес следующей команды, и. при возврате через ret. переход происходит именно на этот адрес. Командой вида mov [esp-4] , этот адрес можно изменить.

4. Наиболее эффективный метод против статического анализа — это шифрование всего программного кода. Совмещенный с антиотладоч-ными приёмами для дешифратора, он даёт самые эффективные результаты. Основой для этого метода является возможность изменения программного кода во время исполнения. При использовании этого приёма для анализа остаётся доступным только небольшой участок незашифрованного кода — дешифратор, а весь остальной код можно получить либо сняв «дамп» с работающей программы, либо проанализировав дешифратор и написав эмулятор его выполнения.

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

В работе f 1] рассмотрен механизм запутывания бинарного кода с использованием следующих методов: вставка «мусора», «непроницаемых предикатов». подмена адреса возврата. Получены следующие результаты: неверно дизассемблировано около 75% инструкций методом линейного прохода и примерно 40% инструкций — методом рекурсивного обхода. В обоих случаях процент ошибок достаточно велик, чтобы утверждать, что дизассемблер со своей задачей не справился.

5. Обход основных методов защиты

В статье [2| предложена следующая техника дизассемблирования запутанного (без помощи шифрования) машинного кода.

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

Выделяя среди иолученных инструкций ИПУ. строим первый вариант графа потока управления. Выделяем внутри каждой функции базовые блоки.

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

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

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

Возможны и иные способы преодоления защиты.

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

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

Переходы же по динамически вычисляемым адресам невозможно отследить иначе, как в отладчике во время исполнения.

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

6. Нестандартные приёмы защиты

Предложим не использовавшиеся в известных нам работах приёмы защиты.

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

Главный его недостаток — это одноразовый способ. После однократного обнаружения недокументированной команды она становится известной, заносится в базу команд дизассемблера и становится документированной.

Разновидность данного подхода, пригодная для борьбы с отладкой. — использование известных ошибок известных отладчиков и антивирусных анализаторов. Например, в отладчике TurboDebugger можно вызвать исключение путём следующих операций со стеком: neg esp; neg esp.

Следующий весьма эффективный метод — это использование перекрывающихся инструкций. В статье [1| приведены данные о том. что авторы предпринимали попытку включить этот метод в свой арсенал запутывающих преобразований. Результаты оказались такими: эффективность крайне высока, но велика трудоёмкость (сложно построить но произвольному коду эквивалентный ему и содержащий перекрывающиеся инструкции). Тем не менее, во многих случаях можно искусственно получить перекрывание инструкций. Вот типичный пример:

Последовательность байт, представляющая эти инструкции, выглядит так (вставлен переход но адресу +17):

Ъ8 00 00 еЪ 17 еЪ f6

Выполняется первый переход — передача управления на 3-й переход. а из него мы попадаем в середину 2-й инструкции. Более того. requlred_lnstructlon обязана быть инструкцией перехода — чтобы не произошло ошибки при попытке повторного разбора перехода номер 3 (иначе он будет выполняться тоже с середины инструкции).

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

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

Приём против отладки дешифратора — использовать в качестве кода для расшифровки динамически вычисляемый CRC кода самого дешифратора. Смысл таков:

1. Считаем CRC (Cyclic Redundancy Code) первого дешифратора (можно брать различные функции от кода дешифратора, в простейшем случае — хог всех байт). Цель приёма — в том. чтобы невозможно было установить точку останова на первый дешифратор (на какую-либо его инструкцию) и перехватить управление. Точка останова меняет байт в том месте, где она расположена, и CRC будет посчитан неверно. (Для невозможности установки аппаратной точки останова необходимо при этом использовать аппаратные регистры контроля точек останова.)

2. При помощи получившегося CRC первым дешифратором расшифровываем второй дешифратор.

3. Второй дешифратор расшифровывает основной код (желательно, чтобы до него труднее было добраться, расшифровывать код в динамически выделяемую область памяти).

i Не можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

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

fl] Cullen Linn, Saumya Debra)’. Obfuscation of Executable Code to Improve Resistance to Static Disassembly.

[2| Christopher Kruegel, William Robertson, Fredrik Valeur and Giovanni Vigna. Static Disassembly of Obfuscated Binaries.

[3] Shengying Li. A Survey on Tools for Binary Code Analysis. August 2004.

[4] http : //www. wasm. ru/

Дизассемблер

главная программы pe explorer обзорный тур

Дизассемблер

Простота, ясность и удобство навигации

Дизассемблер, входящий в состав PE Explorer, разработан с целью дать разработчикам и исследователям простой и удобный инструмент для выполнения экспресс-анализа исполняемых файлов. Чтобы сделать процесс дизассемблирования быстрым и лёгким, некоторая часть функциональности, встречаемая в других дизассемблерах, была специально убрана. Таким образом, дизассемблер в PE Explorer сфокусирован на простоте, ясности и удобстве навигации, в то же время являясь ничуть не менее мощным, чем более сложные и дорогие конкурирующие продукты.

Главное окно дизассемблера

Мы попытались достичь уровня IDA Pro, при этом не требуя от пользователя специфических знаний и умений, поскольку большинство стадий процесса дизассемблирования в PE Explorer автоматизированы. Во многих случаях использование IDA Pro для быстрого анализа кода файла напоминает стрельбу из очень тяжёлой и дорогой пушки по очень маленьким воробьям. Мы же сделали просто хороший дизассемблер по очень доступной цене. И если ваша повседневная работа включает реверсинг и исследование уязвимостей программного кода, восстановление исходных кодов и поиск параметров, тестирование или изучение поведения неизвестных файлов, PE Explorer сэкономит вам немало часов и во многом упростит вашу работу.

Использование дизассемблера PE Explorer предполагает последующее редактирование полученного листинга вручную. Однако, чтобы избавить пользователя от значительного количества рутинной работы и уменьшить объём ручной правки, дизассемблер использует очень гибкий и интелектуальный алгоритм дизассемблирования, способный реконструировать ассемблерный код изучаемого исполняемого файла с высокой степенью достоверности.

Conrad Herrmann,
Zone Labs, Inc.

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

Gerald Beuchelt,
Sun Microsystems, Inc.

После сравнения PE Explorer с другими продуктами я полагаю, что выбрал самое лучшее предложение на рынке за свои деньги — вместо того, чтобы платить $400 за Ida Pro.

David Burlingame,
Intel Corporation

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

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

Процесс дизассемблирования

Дизассемблер запускается в своём отдельном окне, и вы можете переключаться из окна дизассемблера в основное окно PE Explorer. Дизассемблер поддерживает основные наборы инструкций Intel x86 и расширения MMX, 3D Now!, SSE, SSE2 и SSE3. В начале процесса окно Options предлагает выбрать следующие опции:

Окно опций дизассемблера

Нажатие кнопки Start Now запускает процесс, кнопка Start Later откладывает запуск.

Verify Offsets — При включении этой опции дизассемблер особо тщательно проверяет, является ли анализируемое значение смещением.

Reverse Offset Checking Order — По умолчанию, дизассемблер рассматривает необработанные данные как код, и только потом как смещения к данным. При включении этой опции дизассемблер будет рассматривать необработанные данные как данные.

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

Find ANSI and Unicode Strings — При включении этой опции дизассемблер автоматически обнаруживает текстовые ANSI и Unicode строки длиннее 3-х символов. В отдельных случаях, если результаты анализа позволяют однозначно интерпретировать данные как строку, эта длина может быть и менее 3-х символов.

Find Alignment — Поскольку доступ к выравненным данным происходит быстрее, многие компиляторы выравнивают код, добавляя лишние команды, не влияющие не ход выполнения программы, такие как nop, mov eax,eax и т.п.
При включении этой опции дизассемблер интерпретирует подобный код/данные как выравнивание.

Forcible Find Offsets — При включении этой опции дизассемблер автоматически обнаруживает смещения к данным в коде, который остался нераспознанным после первого прохода по файлу.
Используйте эту опцию с осторожностью, так как правильно определить, являются ли найденные данные смещением или фрагментами кода/данных, довольно сложно. Не рекомендуется использовать эту опцию без особых причин.

Analyze Uprocessed Data — Во время дизассемблирования отдельные фрагменты кода по причине отсутствия явных на них ссылок могут оказаться нераспознанными. При включении данной опции дизассемблер проводит дополнительный анализ таких блоков и пытается определить, являются ли эти фрагменты кодом или данными. Включение этой опции улучшает качество листинга.

Окно Processing Info отображает информацию о ходе процесса дизассемблирования:

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

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

Нахождение всех текстовых строк внутри EXE файла

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

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

Нахождение VCL объектов и методов Delphi

Дизассемблер отображает список всех найденных VCL объектов и методов в отдельных закладках VCL Objects и VCL Methods:

Известные ограничения

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

Дизассемблирование файлов размером более 1 Мб может занять несколько минут. Во многом скрость зависит от возможностей вашего компьютера. В общем случае, для каждого байта дизассемблируемого файла требуется 40 байт памяти. Т.е. для файла размером в 1 Мб требуется 40 Мб памяти, для 2 Мб — 40 Мб памяти, и так далее.

Полученный дизассемблерный листинг не может быть заново скомпилирован «как есть». Мы не ставили перед собой задачи формировать листинг, который мог бы быть рекомпилирован. Это не имеет большого смысла для сколь-либо значимого размера входного файла. Нашей целью было получить продукт, способный БЫСТРО и достаточно качественно дать представление о содержимом исполняемого файла, обнаружить в нём интересующие исследователя места и проанализировать их. Получение же листинга, способного быть откомпилированным, представляется нам задачей мало применимой в реальной жизни в силу внушительных размеров современных исполняемых файлов и, как следствие, КРАЙНЕ ВЫСОКОЙ СЛОЖНОСТИ качественного анализа ВСЕХ данных, содержащихся в программе. Нам кажется, что имеет смысл вести разговор только о возможности извлечь какую-либо процедуру из листинга для использования ее в своих целях (естественно, в рамках действующего законодательства).

Обзорный тур
назад | след.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *