Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Учебное пособие 1977

.pdf
Скачиваний:
15
Добавлен:
30.04.2022
Размер:
3.44 Mб
Скачать

push с push Ь push a call Function

Если вызываемая функция является методом некоторого объекта, ссылка this обычно передается в функцию через регистр есх. Компиляторы Borland часто оптимизируют код, передавая один-два параметра функции через регистры, обычно еах и edx;

каждый параметр функции всегда занимает в стеке целое машин ное слово, даже если реальная длина параметра меньше;

обращения к параметрам функции выполняются по адресам вида [ebp + N * 4 + 4], где N – номер параметра функции (нумерация начинается с единицы). Регистр ebp инициализируется первыми командами функции:

push ebp mov ebp, esp

Некоторые компиляторы оптимизируют код несложных функций, адресуя параметры функции через регистр esp. В этом случае вышеприведенные команды в начале функции не нужны;

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

sub esp, N

выполняемой в начале функции. Обращения к локальным переменным выполняются по адресам вида [ebp-K]. Некоторые компиляторы оптимизируют код несложных функций, адресуя локальные переменные функции через регистр esp;

возвращаемое значение функции помещается перед выходом в регистр еах или, если функция возвращает 64разрядное значение, в пару регистров edx:eax.

В качестве примера дизассемблера рассмотрим «умный» дизассемблер IDA, являющийся де-факто стандартом начиная примерно с 2005 г. Другие программы «умного» дизассемблирования в настоящее время практически не применяются и потому в данном пособии не рассматриваются. Примерный вид основного окна программы представлен на рис. 1.3. Начиная с версии 5.0 IDA поддерживает режим

21

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

Для дизассемблера IDA существует множество модулей расширения (плагинов), особенно полезен плагин Hex-Rays, позволяющий декомпилировать машинный код в формат, близкий к его исходному тексту на языке С. Пример декомпиляции кода с помощью Hex-Rays приведен на рис. 1.5. Конечно, полученный псевдокод весьма далек от оригинального кода на языке С, но очевидно, что он воспринимается аналитиком гораздо лучше, чем ассемблеровский вариант.

Рис. 1.3. Обычный режим отображения кода в окне «умного» дизассемблера IDA5.5.0.925t

22

Рис. 1.4. Режим отображения кода в виде графа в окне «умного» дизассемблера IDA 5.5.0.925t

Рис. 1.5. Результат декомпиляции машинного кода плагином Hex-Rays

Для того чтобы дизассемблировать файл с помощью дизассемблера IDA, следует открыть файл через пункт меню File / Open, выбрать параметры дизассемблирования (обычно

23

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

Если для дизассемблированного файла доступна отладочная информация в формате PDB, полезно загрузить ее в дизассемблер. Для этого надо поместить PDB-файл в ту же директорию, что и дизассемблированный файл, и в меню дизассемблера выбрать пункт File / Load File /PDB File.

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

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

В ходе дизассемблирования IDA расставляет по тексту листинга комментарии, содержащие следующую информацию:

прототипы функций, полученные из файла с отладочной информацией;

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

(пункт меню Options / General, вкладка Cross-references);

имена параметров для документированных системных функций;

значения строковых констант, к которым обращаются машинные команды;

24

«подозрительные» фрагменты кода, где вероятны ошибки дизассемблирования.

Пользователь может модифицировать эти комментарии или вставлять свои с помощью клавиш [:] (двоеточие) – комментарий в конце текущей строки, [Ins] – комментарий перед текущей строкой, [Shift-Ins] – комментарий после текущей строки.

Вбольшинстве случаев IDA правильно отличает код от данных. В тех редких случаях, когда дизассемблер ошибается, представление информации можно изменить вручную с помощью клавиш [С] (преобразовать в код) или [D] (преобразовать в данные).

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

Большая часть информации, необходимой аналитику, сосредоточена в основном окне дизассемблера – окне IDA View. Иногда для быстрого поиска нужного места в программном модуле бывает полезно воспользоваться окнами Exports (экспортируемые функции), Imports (статически импортируемые функции), Functions (все функции) и Names (нее функции и переменные). Списки, представленные в этих окнах, можно сортировать различными способами, нужный элемент списка можно быстро найти, введя первые символы его имени. Окно Function calls позволяет пользователю просмотреть еще два списка: список вызовов текущей функции

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

25

Если необходимо просмотреть псевдокод на языке С для текущей функции, следует нажать [F5] и IDA откроет новое окно, в котором будет отображен псевдокод, сгенерированный плагином Hex-Rays (естественно, этот плагин должен быть предварительно установлен). Нажав [Ctrl-F5], можно декомпилировать весь программный модуль в отдельный текстовый файл.

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

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

Пусть, например, мы хотим узнать, каким образом в Windows осуществляется извлечение звуков системного динамика (системный вызов Веер). Дизассемблируем библиотеку kerneI32.dll, декомпилируем функцию Веер и немедленно обнаруживаем, что это делается с помощью отправки на устройство VDeviceVBeep кода управления 65536, при этом входной буфер имеет длину 8 байт и содержит последовательно записанные друг за другом частоту звука в герцах и длину звука в миллисекундах:

RtllnitUnicodeString(есхО, &v9, L"\\Device\\Beep"); v10 = &v9; v11 = 24; v12

=0; v13 = 0; vl4 = 0; vl5 = 0; v7 =

NtCreateFile (&vl6, 3, &vll, &vl7, 0, 0, 3, 3, 0, 0, 0); vl8 = dwFreq; vl9 = dwDuration; v20 = NtDeviceloControlFile(vl6, 0, 0, 0, &vl7, 65536, &vl8, 8, 0, 0);

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

26

область применимости статического метода, вероятно, будет расширяться.

1.4.Динамический метод

1.4.1.Программные отладочные средства

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

Существует достаточно много отладчиков. Среди наиболее распространенных можно назвать Oily Debugger, WinDbg, Syser. Большинство современных компиляторов имеют встроенные отладчики, многие из них (например, отладчик, встроенный в Microsoft Visual Studio) пригодны не только для отладки только что написанных программ, но и для анализа стороннего программного обеспечения. Выбор отладчика зависит от цели его применения. Из перечисленных отладчиков наиболее удобны в использовании Oily Debugger и встроенный отладчик Microsoft Visual Studio, имеющие удобный многооконный интерфейс. Однако наблюдается следующая закономерность: чем более отладчик удобен, тем менее он эффективен. Под эффективностью здесь понимается способность «проникать» в системные области памяти и противостоять защите от отладчика.

Механизм работы любого отладчика основан на использовании специальных средств, аппаратно реализованных в процессорах ЭВМ. Для процессоров семейства Intel х86 к этим средствам относятся: флаг трассировки, точка останова и отладочные регистры.

Флаг трассировки (Trace flag или Trap flag, TF). Это бит

8 регистра флагов. Когда этот бит равен единице, процессор после выполнения каждой машинной команды вызывает прерывание 1. Флаг трассировки используется отладчиками

27

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

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

mov ss, А mov esp, В

как одну машинную команду;

- в ходе выполнения команд int и sysenter/syscall содержимое реги стра флагов копируется в стек, а в самом регистре флаг TF сбра сывается. По завершении обработки прерывания флаговый ре гистр восстанавливается из стека и флаг TF восстанавливается (если, конечно, обработчик прерывания не модифицировал со храненные в стеке флаги). В результате отладчик при пошаговом проходе команды int/sysenter/syscall не «проваливается» внутрь обработчика прерывания (который, как правило, размещается в области памяти, недоступной отладчику пользовательского режима), а воспринимает текущую команду не как команду перехода, а как «атомарную» машинную команду. Аппаратные прерывания, возникающие в ходе отладки программы, отладчик, как правило, не замечает вообще.

Точка останова (Breakpoint). Машинный код команды вызова прерывания 3 (int 3), в отличие от других прерываний, занимает всего один байт. Поэтому эта команда может быть вставлена в любое место отлаживаемой программы. Отладчик перехватывает прерывание 3, и после установки точки останова (замены первого байта машинной команды на байт CCh – код

28

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

Отладочные регистры (Debug registers). Начиная с модели 80386 процессоры семейства Intel имеют восемь отладочных регистров. Эти регистры позволяют разместить в памяти ЭВМ четыре аппаратные точки останова. В отличие от обычных точек останова, аппаратные обладают более широкими возможностями. Отлаживаемая программа может быть остановлена не только при выполнении определенной команды, но и при обращении к определенному участку памяти. При останове программы аппаратной точкой останова вызывается прерывание 1.

Отладчики обычно реализуют следующие возможности: пошаговый проход программы (трассировка); пошаговый проход программы, при котором вызов

функции об рабатывается как одна команда; пошаговый проход программы с «провалом» в

прерывания; установка/снятие точки останова с заданной команды,

многие отладчики допускают установку условных точек останова, сраба тывающих лишь в случае выполнения некоторого дополнитель ного условия (например, еах = 0). Условные точки останова реа лизуются как программное расширение обычных (программных или аппаратных) точек останова – точка останова срабатывает вне зависимости от выполнения заданного условия, а затем от ладчик принимает решение, следует ли приостановить отлажи ваемую программу или продолжить ее выполнение;

установка/удаление аппаратной точки останова; установка/удаление точки останова с порта/портов; выполнение участка программы до ближайшей точки

останова;

29

выполнение участка программы до команды, подсвеченной курсором, или до ближайшей точки останова;

перезагрузка программы; просмотр кода/памяти/регистров/флагов/стека; поиск строки в памяти;

поиск машинной команды в памяти; изменение содержимого регистров/флагов/памяти/кода.

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

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

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

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

По сравнению со статическим методом анализа динамический метод позволяет проводить поиск алгоритмов

30