- •Внимание!
- •Об авторах
- •О техническом редакторе
- •О соавторах
- •Предисловие
- •Благодарности
- •Отдельное спасибо
- •Введение
- •Необходимая квалификация
- •Изучение на примерах
- •Структура книги
- •Глава 0. Анализ вредоносных программ для начинающих
- •Цель анализа вредоносных программ
- •Методики анализа вредоносного ПО
- •Общие правила анализа вредоносного ПО
- •Глава 1. Основные статические методики
- •Сканирование антивирусом: первый шаг
- •Хеширование: отпечатки пальцев злоумышленника
- •Поиск строк
- •Упакованное и обфусцированное вредоносное ПО
- •Формат переносимых исполняемых файлов
- •Компонуемые библиотеки и функции
- •Статический анализ на практике
- •Заголовки и разделы PE-файла
- •Итоги главы
- •Глава 2. Анализ вредоносных программ в виртуальных машинах
- •Структура виртуальной машины
- •Запуск виртуальной машины для анализа вредоносного ПО
- •Использование виртуальной машины для анализа безопасности
- •Риски при использовании VMware для анализа безопасности
- •Запись/воспроизведение работы компьютера
- •Итоги главы
- •Глава 3. Основы динамического анализа
- •Песочницы: решение на скорую руку
- •Запуск вредоносных программ
- •Мониторинг с помощью Process Monitor
- •Сравнение снимков реестра с помощью Regshot
- •Симуляция сети
- •Перехват пакетов с помощью Wireshark
- •Использование INetSim
- •Применение основных инструментов для динамического анализа
- •Итоги главы
- •Уровни абстракции
- •Архитектура x86
- •Итоги главы
- •Глава 5. IDA Pro
- •Загрузка исполняемого файла
- •Интерфейс IDA Pro
- •Использование перекрестных ссылок
- •Анализ функций
- •Схематическое представление
- •Повышение эффективности дизассемблирования
- •Плагины к IDA Pro
- •Итоги главы
- •Глава 6. Распознавание конструкций языка C в ассемблере
- •Переменные: локальные и глобальные
- •Дизассемблирование арифметических операций
- •Распознавание выражений if
- •Распознавание циклов
- •Соглашения, касающиеся вызова функций
- •Анализ выражений switch
- •Дизассемблирование массивов
- •Распознавание структур
- •Анализ обхода связного списка
- •Итоги главы
- •Глава 7. Анализ вредоносных программ для Windows
- •Windows API
- •Реестр Windows
- •API для работы с сетью
- •Отслеживание запущенной вредоносной программы
- •Сравнение режимов ядра и пользователя
- •Native API
- •Итоги главы
- •Глава 8. Отладка
- •Сравнение отладки на уровне исходного и дизассемблированного кода
- •Отладка на уровне ядра и пользователя
- •Использование отладчика
- •Исключения
- •Управление выполнением с помощью отладчика
- •Изменение хода выполнения программы на практике
- •Итоги главы
- •Глава 9. OllyDbg
- •Загрузка вредоносного ПО
- •Пользовательский интерфейс OllyDbg
- •Карта памяти
- •Просмотр потоков и стеков
- •Выполнение кода
- •Точки останова
- •Трассировка
- •Обработка исключений
- •Редактирование кода
- •Анализ кода командной оболочки
- •Вспомогательные возможности
- •Подключаемые модули
- •Отладка с использованием скриптов
- •Итоги главы
- •Драйверы и код ядра
- •Подготовка к отладке ядра
- •Использование WinDbg
- •Отладочные символы Microsoft
- •Отладка ядра на практике
- •Руткиты
- •Загрузка драйверов
- •Итоги главы
- •Глава 11. Поведение вредоносных программ
- •Программы для загрузки и запуска ПО
- •Бэкдоры
- •Похищение учетных данных
- •Механизм постоянного присутствия
- •Повышение привилегий
- •Заметая следы: руткиты, работающие в пользовательском режиме
- •Итоги главы
- •Глава 12. Скрытый запуск вредоносного ПО
- •Загрузчики
- •Внедрение в процесс
- •Подмена процесса
- •Внедрение перехватчиков
- •Detours
- •Внедрение асинхронных процедур
- •Итоги главы
- •Глава 13. Кодирование данных
- •Простые шифры
- •Распространенные криптографические алгоритмы
- •Нестандартное кодирование
- •Декодирование
- •Итоги главы
- •Глава 14. Сетевые сигнатуры, нацеленные на вредоносное ПО
- •Сетевые контрмеры
- •Безопасное расследование вредоносной деятельности в Интернете
- •Контрмеры, основанные на сетевом трафике
- •Углубленный анализ
- •Сочетание динамических и статических методик анализа
- •Понимание психологии злоумышленника
- •Итоги главы
- •Искажение алгоритмов дизассемблирования
- •Срыв анализа слоя стека
- •Итоги главы
- •Глава 16. Антиотладка
- •Обнаружение отладчика в Windows
- •Распознавание поведения отладчика
- •Искажение работы отладчика
- •Уязвимости отладчиков
- •Итоги главы
- •Глава 17. Методы противодействия виртуальным машинам
- •Признаки присутствия VMware
- •Уязвимые инструкции
- •Изменение настроек
- •Побег из виртуальной машины
- •Итоги главы
- •Глава 18. Упаковщики и распаковка
- •Анатомия упаковщика
- •Распознавание упакованных программ
- •Способы распаковки
- •Автоматизированная распаковка
- •Ручная распаковка
- •Советы и приемы для работы с распространенными упаковщиками
- •Анализ без полной распаковки
- •Итоги главы
- •Глава 19. Анализ кода командной оболочки
- •Загрузка кода командной оболочки для анализа
- •Позиционно-независимый код
- •Определение адреса выполнения
- •Поиск символов вручную
- •Окончательная версия программы Hello World
- •Кодировки кода командной оболочки
- •NOP-цепочки
- •Поиск кода командной оболочки
- •Итоги главы
- •Глава 20. Анализ кода на C++
- •Объектно-ориентированное программирование
- •Обычные и виртуальные функции
- •Создание и уничтожение объектов
- •Итоги главы
- •Какой смысл в 64-битном вредоносном ПО?
- •Особенности архитектуры x64
- •Признаки вредоносного кода на платформе x64
- •Итоги главы
- •Приложения
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
8 |
|||
|
|
|
|
|
|
.c |
|
|||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
Отладка
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Отладчик — это программное или аппаратное средство, которое используется для проверки или изучения работы программ. Отладчики помогают при написании ПО, так как ранние версии кода обычно содержат ошибки. В ходе разработки вы даете программе задачу и получаете результат, но вы не видите, каким путем было достигнуто решение. Отладчики позволяют понять, чем именно занимается программа во время выполнения. Они предназначены для того, чтобы программист мог оценивать и контролировать внутреннее состояние и поведение программы.
Отладчик предоставляет информацию об исполняемом коде, которую сложно (а часто невозможно) было бы получить путем дизассемблирования. Дизассемблер предлагает статическую версию того, как программа выглядит непосредственно перед выполнением первой инструкции. Отладчик создает динамическое представление кода во время его работы. Например, вы можете узнать, какие значения хранятся в памяти по тому или иному адресу и как они меняются в ходе выполнения.
Возможность оценивать и контролировать выполнение программы является определяющей при анализе безопасности. Отладчики позволяют просматривать значения на любом участке памяти и регистре, а также аргументы любой функции. Вы всегда можете изменить любой аспект выполнения программы. Например, можно поменять значение какой угодно переменной, когда вам это удобно, — для этого нужно лишь иметь достаточно информации об этой переменной, включая ее адрес.
В следующих двух главах мы рассмотрим два отладчика: OllyDbg и WinDbg. Здесь же мы сосредоточимся на концепциях и возможностях, характерных для отладчиков любого вида.
Сравнение отладки на уровне исходного и дизассемблированного кода
Большинство разработчиков знакомы с отладчиками, которые работают на уровне исходного кода и позволяют производить отладку во время написания программы. Такие отладчики обычно встроены в среду разработки. Они дают возможность указывать в исходном коде точки останова, чтобы вы могли проанализировать внутрен-
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
Глава 8. Отладка 193 |
to |
|
|
|
|
|
||||
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
нее состояние переменных в определенный момент и затем продолжать выполнение по одной строке за раз (мы подробно обсудим точки останова в этой главе).
В отладке на уровне ассемблера (ее еще называют низкоуровневой) используется не исходный, а дизассемблированный код. Отладчики этого типа, как и любые другие, позволяют выполнять отдельно каждую инструкцию, указывать точки останова и просматривать участки памяти.
Аналитики безопасности активно применяют отладчики уровня ассемблера, поскольку они не требуют доступа к исходному коду.
Отладка на уровне ядра и пользователя
В главе 7 мы обсудили некоторые различия между режимами ядра и пользователя в Windows. Отладка ядра является более сложной по сравнению с обычными приложениями, так как для ее выполнения, как правило, необходимо использовать сразу две системы. В пользовательском режиме отладчик работает в той же системе, что и анализируемый код. При этом отлаживается лишь один исполняемый файл, который изолируется от других процессов самой ОС.
Отладка ядра происходит на двух системах, поскольку ядро существует в единственном экземпляре — если оно дойдет до точки останова, вместе с ним остановятся все остальные приложения. Одна система выполняет отлаживаемый код, а другая — отладчик. Кроме того, конфигурация системы должна позволять отладку, при этом вам нужно соединить два компьютера.
ПРИМЕЧАНИЕ
Отладчик ядра можно запускать и в отлаживаемой системе, однако это крайне непопулярный подход. Эту возможность предоставляла программа под названием SoftICE, но с 2007 года она не поддерживается. На сегодняшний день никто не предлагает продукты с подобной функциональностью.
Для отладки в режиме пользователя и ядра существуют разные программные пакеты. Единственным популярным отладчиком с поддержкой ядра является WinDbg. OllyDbg считается самым популярным продуктом для отладки вредоносного кода, однако он не поддерживает режим ядра. WinDbg подходит и для пользовательских приложений, а IDA Pro имеет встроенный отладчик, но ни один из этих инструментов не сравнится по своим возможностям и простоте с OllyDbg.
Использование отладчика
Существует два подхода к отладке программ. Во-первых, вы можете запустить программу с помощью отладчика. Загрузившись в память, она немедленно остановит свою работу, не дойдя до точки входа. В этот момент вы получаете полный контроль над ее выполнением.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
w |
|
|
to |
|
|
194 Часть III • Продвинутый динамический анализ |
||||
w Click |
|
|
|
|
|
|
||||
|
|
|
|
|
o |
m |
||||
|
w |
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Вы можете также подключить отладчик к программе во время ее работы. Все потоки программы остановятся, и вы сможете их отлаживать. Это хороший вариант для отладки уже запущенных программ или процессов, затронутых вредоносным ПО.
Пошаговое выполнение
Самое простое, что вы можете сделать с помощью отладчика, — это пошагово изучить программу. В этом случае после выполнения каждой инструкции управление возвращается к отладчику. Это позволяет увидеть все, что происходит внутри программы.
Вы можете пошагово выполнить всю программу целиком, но это может занять довольно много времени. Этот инструмент хорошо подходит для изучения отдельных участков кода, но вы должны осторожно подходить к выбору того, что именно нужно проанализировать. Старайтесь сосредоточиться на общей картине, чтобы не потеряться в мелких деталях.
На примере листинга 8.1 показано, каким образом отладчик помогает понять участок дизассемблированного кода.
Листинг 8.1. Пошаговое выполнение кода
mov |
edi, DWORD_00406904 |
|
mov |
ecx, 0x0d |
|
LOC_040106B2 |
|
|
xor |
[edi], 0x9C |
|
inc |
edi |
|
loopw |
LOC_040106B2 |
|
... |
|
|
DWORD:00406904: |
F8FDF3D0 |
В листинге вы видите адрес данных, к которым обращаются и которые изменяют внутри цикла. Значение, находящееся в конце строки , не похоже ни на текст в формате ASCII, ни на любой другой знакомый нам тип данных, но с помощью отладчика вы можете пройтись по этому циклу и узнать, чем занимается этот код.
Если бы мы пошагово выполнили этот цикл, используя WinDbg или OllyDbg, мы бы увидели, что данные в нем изменяются. Например, в листинге 8.2 показано, как представленная выше функция модифицирует 13 байт, изменяя их при каждой итерации цикла (рядом с адресами приводятся как сами байты, так и их представление в формате ASCII).
Листинг 8.2. Пошагово проходим по участку кода, чтобы увидеть, как он изменяет память
D0F3FDF8 D0F5FEEE FDEEE5DD 9C (............. |
) |
|
4CF3FDF8 D0F5FEEE FDEEE5DD 9C (L............ |
) |
|
4C6FFDF8 D0F5FEEE FDEEE5DD 9C |
(Lo........... |
) |
4C6F61F8 D0F5FEEE FDEEE5DD 9C |
(Loa.......... |
) |
. . . SNIP . . . |
|
|
4C6F6164 4C696272 61727941 00 |
(LoadLibraryA.) |
Если подключить отладчик, становится очевидно, что эта функция использует однобайтную операцию XOR для декодирования строки LoadLibraryA. Определить эту строку лишь с помощью статического анализа было бы сложнее.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
Шаг с обходом и шаг со входом
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
Глава 8. Отладка 195 |
to |
|
|
|
|
|
||||
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Во время пошагового обхода кода отладчик останавливается после каждой инструкции. И хотя вас интересует принцип работы программы, назначение каждого вызова может быть не таким существенным. Например, если ваша программа вызывает функцию LoadLibrary, вам, вероятно, не захочется перебирать каждую ее инструкцию.
Чтобы выбрать, какие инструкции вы хотите видеть в отладчике, вы можете пройтись по ним с обходом и со входом. При обходе инструкции вы ее минуете. Например, если обойти вызов, следующей инструкцией в отладчике будет та, что идет после возвращения этого вызова. Но если вы войдете в вызов, отладчик перей дет к его первой инструкции.
Шаг с обходом позволяет существенно уменьшить количество инструкций, которые вам нужно анализировать, но при этом вы рискуете перешагнуть через важную функцию. Кроме того, некоторые вызовы никогда не возвращаются — перешагнув через них, отладчик не сможет вернуть себе управление. Если (а скорее, когда) это случится, перезапустите программу и перейдите в то же место, но на этот раз сделайте шаг со входом.
ПРИМЕЧАНИЕ
Это хороший повод воспользоваться функциями записи/воспроизведения в VMware. Перешагнув функцию, которая никогда не возвращается, вы сможете воспроизвести сеанс отладки и исправить свою ошибку. Начните запись в самом начале процедуры отладки. Затем, сделав шаг с обходом через функцию, которая не возвращается, остановите запись. Воспроизведите процедуру, остановившись прямо перед шагом с обходом, и на этот раз войдите в функцию.
При входе в функцию можно и не заметить, что инструкции, которые вы пошагово выполняете, не имеют никакого отношения к анализируемому коду. Например, функция, в которую вы вошли, может вызвать другую функцию, а та — еще одну. В итоге вы максимально удалитесь от предмета анализа. К счастью, большинство отладчиков позволяют возвращаться к вызывающей функции, а некоторые умеют выходить наружу. Иногда отладчики предоставляют возможность переходить к инструкции возврата непосредственно перед завершением функции.
Приостановка выполнения с помощью точек останова
Точки останова используются для приостановки выполнения, позволяя исследовать состояние программы. Остановленную таким образом программу называют прерванной. Необходимость в точках останова объясняется тем, что вы не можете получить доступ к регистрам или адресам памяти, пока программа работает, поскольку хранящиеся в них значения постоянно меняются.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
w |
|
|
to |
|
|
196 Часть III • Продвинутый динамический анализ |
||||
w Click |
|
|
|
|
|
|
||||
|
|
|
|
|
o |
m |
||||
|
w |
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
В листинге 8.3 продемонстрирована ситуация, в которой могут пригодиться точки останова. В этом примере происходит обращение к регистру EAX. Дизассемблер не может вам сказать, какая функция при этом вызывается, но вы можете сами это выяснить, указав точку останова для этой инструкции. Дойдя до этой точки, программа остановится, а отладчик покажет вам содержимое EAX, которое соответствует местоположению вызываемой функции.
Листинг 8.3. Обращение к EAX
00401008 |
mov |
ecx, [ebp+arg_0] |
0040100B |
mov |
eax, [edx] |
0040100D |
call |
eax |
В листинге 8.4 показано начало функции с вызовом CreateFile, который открывает файловый дескриптор. В ассемблерном коде сложно определить имя файла, хотя оно частично передается в виде аргумента функции. Вы можете воспользоваться IDA Pro, чтобы найти все вызовы этой функции и посмотреть, какие аргументы ей передаются, но эти значения сами могут оказаться аргументами, передаваемыми извне, или результатом выполнения других функций. Очень быстро эта задача может стать слишком сложной. Упростить ее можно с помощью отладчика.
Листинг 8.4. Использование отладчика для определения имени файла
0040100B |
xor |
eax, esp |
|
0040100D |
mov |
[esp+0D0h+var_4], eax |
|
00401014 |
mov |
eax, edx |
|
00401016 |
mov |
[esp+0D0h+NumberOfBytesWritten], 0 |
|
0040101D |
add |
eax, 0FFFFFFFEh |
|
00401020 |
mov |
cx, [eax+2] |
|
00401024 |
add |
eax, 2 |
|
00401027 |
test |
cx, cx |
|
0040102A |
jnz |
short loc_401020 |
|
0040102C |
mov |
ecx, dword ptr ds:a_txt ; ".txt" |
|
00401032 |
push |
0 |
; hTemplateFile |
00401034 |
push |
0 |
; dwFlagsAndAttributes |
00401036 |
push |
2 |
; dwCreationDisposition |
00401038 |
mov |
[eax], ecx |
|
0040103A |
mov |
ecx, dword ptr ds:a_txt+4 |
|
00401040 |
push |
0 |
; lpSecurityAttributes |
00401042 |
push |
0 |
; dwShareMode |
00401044 |
mov |
[eax+4], ecx |
|
00401047 |
mov |
cx, word ptr ds:a_txt+8 |
|
0040104E |
push |
0 |
; dwDesiredAccess |
00401050 |
push |
edx |
; lpFileName |
00401051 |
mov |
[eax+8], cx |
|
00401055 |
call |
CreateFileW ; CreateFileW(x,x,x,x,x,x,x) |
Укажем точку останова для вызова CreateFileW и посмотрим, какие значения хранятся в стеке при ее срабатывании. На рис. 8.1 показан снимок окна отладчика WinDbg с инструкцией в точке останова. После этой точки первый аргумент функции выводится в виде строки в формате ASCII (вы научитесь это делать в главе 10, посвященной WinDbg).
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
Глава 8. Отладка 197 |
to |
|
|
|
|
|
||||
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Рис. 8.1. Использование точки останова для просмотра аргументов вызова. Мы указали точку останова для функции CreateFileW, а затем изучили первый параметр стека
В этом случае очевидно, что создаваемый файл называется LogFile.txt. Мы мо гли бы определить это и с помощью IDA Pro, но отладчик позволил нам упростить и ускорить данную процедуру.
Теперь представьте, что у нас есть фрагмент зараженного кода и перехваченные пакеты, в которых мы видим зашифрованные данные. Мы можем найти вызов отправки и код шифрования, однако расшифровать сами данные будет довольно сложно, поскольку нам неизвестен ни алгоритм, ни ключ. К счастью, мы можем воспользоваться отладчиком, чтобы упростить эту задачу, поскольку алгоритмы шифрования часто представляют собой отдельные функции для преобразования данных.
Если найти момент вызова алгоритма шифрования, можно будет указать точку останова в момент, когда отправляемые данные еще не зашифрованы, и затем их просмотреть. Ассемблерный код этой функции показан в листинге 8.5.
Листинг 8.5. Использование точки останова для просмотра данных до того, как они будут зашифрованы
004010D0 |
sub |
esp, 0CCh |
004010D6 |
mov |
eax, dword_403000 |
004010DB |
xor |
eax, esp |
004010DD |
mov |
[esp+0CCh+var_4], eax |
004010E4 |
lea |
eax, [esp+0CCh+buf] |
004010E7 |
call |
GetData |
004010EC |
lea |
eax, [esp+0CCh+buf] |
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|
|
|
|||
|
|
X |
|
|
|
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
|
|
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
|
r |
|
|
|
||
P |
|
|
|
|
|
NOW! |
o |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|
|
|
|||
w |
|
|
to |
|
|
198 |
Часть III |
• Продвинутый динамический анализ |
|||||
w Click |
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
o |
m |
|
|
|
||||
|
w |
|
|
|
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
.c |
|
|
|
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
|
|
|
df |
|
|
n |
e |
|
|
|
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
004010EF |
call |
EncryptData |
|
||
|
|
|
|
|
|
|
|
004010F4 |
mov |
ecx, s |
|
||
|
|
|
|
|
|
|
|
004010FA |
push |
0 |
; flags |
||
|
|
|
|
|
|
|
|
004010FC |
push |
0C8h |
; len |
||
|
|
|
|
|
|
|
|
00401101 |
lea |
eax, [esp+0D4h+buf] |
|||
|
|
|
|
|
|
|
|
00401105 |
push |
eax |
; buf |
||
|
|
|
|
|
|
|
|
00401106 |
push |
ecx |
; s |
||
|
|
|
|
|
|
|
|
00401107 |
call |
ds:Send |
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
На рис. 8.2 показано окно отладчика OllyDbg, в котором выводится буфер памяти до того, как он передается в функцию шифрования. На верхней панели можно видеть инструкцию с точкой останова, а внизу — сообщение. В нашем случае отправляемые данные являются строкой Secret Message (см. столбец ASCII справа).
Рис. 8.2. Просмотр данных программы до того, как они попадают в функцию шифрования
Вы можете использовать несколько разных типов точек останова, включая программное и аппаратное выполнение, а также остановку с условием. И хотя все они служат одной цели, некоторые из них помогут там, где другие не справятся. Посмотрим, как работает каждый из этих типов.
Программные точки останова
Все точки останова, которые обсуждались выше, были программными — они заставляли программу остановиться при выполнении определенной инструкции. В большинстве популярных отладчиков этот тип точек останова используется по умолчанию, если не указано никаких параметров.
Чтобы создать программную точку останова, отладчик перезаписывает первый байт инструкции с помощью значения 0xCC, которое соответствует прерыванию INT 3, предназначенному специально для отладчиков. При выполнении инструкции 0xCC ОС генерирует исключение и передает управление отладчику.
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
Глава 8. Отладка 199 |
to |
|
|
|
|
|
||||
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Втабл. 8.1 рядом показаны дамп памяти и дизассемблированный код функции
сзаданной точкой останова.
Таблица 8.1. Дизассемблированный код и дамп памяти функции с заданной точкой останова
Дизассемблированное представление |
|
Дамп памяти |
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
00401130 |
55 |
|
|
|
|
push |
ebp |
00401130 |
CC |
8B |
EC |
83 |
00401131 |
8B |
EC |
|
|
|
mov |
ebp, esp |
00401134 |
E4 |
F8 |
81 |
EC |
00401133 |
83 |
E4 |
F8 |
|
|
and |
esp, 0FFFFFFF8h |
00401138 |
A4 |
03 |
00 |
00 |
00401136 |
81 |
EC |
A4 |
03 |
00 |
00 sub |
esp, 3A4h |
0040113C |
A1 |
00 |
30 |
40 |
0040113C |
A1 |
00 |
30 |
40 |
00 |
mov |
eax, dword_403000 |
00401140 |
00 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Функция начинается с инструкции push ebp , которая соответствует опкоду 0x55, но в начале ее дампа памяти содержатся байты 0xCC , представляющие собой точку останова.
Вокне дизассемблирования отладчик выводит оригинальную инструкцию, но
вдампе памяти, сгенерированном вне отладчика, можно видеть байты, которые на самом деле хранятся по соответствующему адресу. В дампе памяти находится значение 0x55, но, если эта или внешняя программа попытается прочитать эти байты, результатом будет значение 0xCC.
Если эти байты поменяются во время выполнения программы, точка останова не сработает. Например, если точка останова указана для кода, который модифицирует сам себя или редактируется извне, она попросту исчезнет. Если прочитать содержимое памяти функции вне отладчика, вместо оригинального значения получится 0xCC. Кроме того, данное несоответствие будет замечено любой программой, которая проверяет целостность этой функции.
Вы можете создавать любое количество программных точек останова в пользовательском режиме, однако в режиме ядра могут существовать ограничения. Изменения кода, которые при этом происходят, являются незначительными и требуют небольшого количества памяти для ведения учета внутри отладчика.
Аппаратные точки останова
Архитектура x86 предусматривает отдельные регистры для поддержки аппаратных точек останова. Каждый раз, когда процессор выполняет инструкцию, на аппаратном уровне происходит проверка тождественности указателя инструкции и адреса точки останова. В отличие от программных, аппаратные точки останова не зависят от того, какие значения хранятся по заданному адресу. Например, если вы укажете точку останова для адреса 0x00401234, процессор прервет выполнение в этом месте вне зависимости от того, что там находится. Это может оказаться существенным преимуществом при отладке кода, который сам себя модифицирует.
Аппаратные точки останова имеют еще один плюс по сравнению с программными: их можно назначать на этапе обращения, а не вызова. Например, вы можете сделать так, чтобы программа останавливалась при чтении или записи какого-то
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
w |
|
|
to |
|
|
200 Часть III • Продвинутый динамический анализ |
||||
w Click |
|
|
|
|
|
|
||||
|
|
|
|
|
o |
m |
||||
|
w |
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
to |
|
|
|
|
|
|
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
участка памяти. Это может помочь в попытках выяснить, чему соответствует значение, хранящееся по определенному адресу. В этом случае отладчик остановит выполнение при записи в этот участок, независимо от того, вызывается ли в этот момент какая-либо инструкция (такие точки останова могут срабатывать для запи си и/или чтения).
У аппаратных точек останова есть один существенный недостаток: для хранения их адресов отводится всего четыре физических регистра.
Еще одним минусом точек останова этого типа является то, что запускаемая программа может их легко модифицировать. Центральный процессор имеет восемь отладочных регистров, но используется только шесть из них. Первые четыре, DR0-DR3, хранят адрес точки останова. Управляющий отладочный регистр, DR7, определяет, активизирована ли точка останова и с какой операцией она связана — с чтением, записью или выполнением. Вредоносная программа может отредактировать эти регистры, пытаясь помешать процедуре отладки. К счастью, процессоры архитектуры x86 позволяют это предотвратить. Если установить флаг GD в регистр DR7, точка останова сработает до выполнения любой инструкции mov, которая может попытаться получить доступ к отладочному регистру. Таким образом вы сможете обнаружить изменение отладочных регистров. И хотя этот подход неидеален (он позволяет определить лишь инструкции mov, которые обращаются к отладочным регистрам), он все равно очень полезен.
Условные точки останова
Программные точки останова, которые срабатывают только при выполнении определенного условия, называются условными. Представьте, к примеру, что у вас есть точка останова для функции GetProcAddress. Она будет прерывать работу при каждом вызове GetProcAddress. Но что, если вы хотите, чтобы она срабатывала только в случае передачи аргумента RegSetValue? Этого можно добиться с помощью условной точки останова. В данном случае условием выступает значение в стеке, которое соответствует первому аргументу.
Условные точки останова, как и программные, всегда передаются отладчику, а тот уже проверяет условие и решает, нужно ли продолжать выполнение, не вовлекая в этот процесс пользователя. Разные отладчики поддерживают различные условия.
Точки останова выполняются намного дольше, чем обычные инструкции, и, если назначить их для часто вызываемой инструкции, это может существенно замедлить вашу программу. В некоторых случаях такое замедление может быть настолько сильным, что программа попросту не сможет завершиться. Но это касается лишь условных точек останова, поскольку снижение производительности является незначительным по сравнению со временем, которое тратится на исследование состояния программы. Тем не менее, несмотря на этот недостаток, условные точки останова могут оказаться по-настоящему полезными при анализе небольшого участка кода.