- •Внимание!
- •Об авторах
- •О техническом редакторе
- •О соавторах
- •Предисловие
- •Благодарности
- •Отдельное спасибо
- •Введение
- •Необходимая квалификация
- •Изучение на примерах
- •Структура книги
- •Глава 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 |
|
|
. |
|
|
|
|
6 |
|||||
|
|
|
|
|
|
|
.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 |
|
|
|
|
Распознавание конструкций языка C в ассемблере
В главе 4 мы познакомились с архитектурой x86 и ее самыми распространенными инструкциями. Но в успешном обратном проектировании отдельные инструкции просто так не рассматриваются. Это слишком утомительный процесс, так как дизассемблированная программа может состоять из тысяч и миллионов элементов. Аналитик безопасности должен уметь получать общее представление о назначении кода, анализируя целые его участки, и останавливаться на отдельных инструкциях только при необходимости. Развитие этого навыка требует времени.
Чтобы разобраться, как именно нужно группировать инструкции, для начала подумаем о том, как автор вредоноса разрабатывает код. Вредоносные программы обычно пишутся на языках высокого уровня, чаще всего на C. Конструкции кода — это уровень абстракции, который определяет функциональные свойства, но не подробности их реализации. Примером таких конструкций могут служить циклы, операторы if, связные списки, оператор switch и т. д. Программа состоит из отдельных конструкций, которые вместе составляют ее общую функциональность.
Эта глава содержит описание более чем десяти разных конструкций языка C и должна послужить для вас отправной точкой. Мы рассмотрим каждую конструкцию с точки зрения ассемблера, хотя данная глава предназначена для того, чтобы научить вас делать обратное: как аналитику безопасности вам придется переводить дизассемблированный код на более высокий уровень. Изучение этого процесса в обратном направлении часто оказывается более простым, так как программисты привыкли к чтению и пониманию исходного кода.
В этой главе мы сосредоточимся на том, как компилируются самые распространенные и сложные конструкции, такие как циклы и условные выражения. Усвоив эти базовые принципы, вы научитесь быстро составлять общее представление о том, как работает код.
Помимо обсуждения конструкций мы также рассмотрим различия между компиляторами, разные версии и параметры которых могут влиять на то, как именно та или иная конструкция будет представлена в ассемблере. В качестве примера мы возьмем два способа компиляции выражения switch. Эта глава содержит довольно глубокий материал по конструкциям языка C, поэтому чем лучше вы понимаете этот язык и программирование в целом, тем больше пользы вы сможете из нее извлечь. Если вам нужна помощь с языком C, обратите внимание на классическую книгу
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
w |
|
|
to |
|
|
136 Часть II • Продвинутый статический анализ |
||||
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 |
|
|
|
|
Брайана Кернигана и Денниса Ритчи «Язык программирования C» (The C Pro gramming Language) (Prentice-Hall, 1988). Большая часть вредоносного ПО написана именно на этом языке, хотя в некоторых случаях используются Delphi и C++. C является простым языком, имеющим непосредственное отношение к ассемблеру, так что это самая логичная отправная точка для начинающего аналитика безопасности.
Во время чтения данной главы не забывайте, что ваша цель заключается в понимании общей функциональности программы, а не в анализе каждой отдельной инструкции. Не позволяйте мелочам скрыть от вас общую картину. Сосредоточивайтесь на работе программ в целом, а не на их отдельных возможностях.
Переменные: локальные и глобальные
Доступ к глобальным переменным имеет любая функция в программе. Локальные переменные доступны только в той функции, в которой они были объявлены. В языке C объявление переменных этих двух видов мало чем различается, но в ассемблере они выглядят совсем по-разному.
Ниже представлены два примера кода на языке C: один для глобальных переменных, а другой — для локальных. Обратите внимание на небольшое различие между ними. В листинге 6.1 переменные x и y объявляются глобально, за пределами функции, а в листинге 6.2 — внутри (то есть локально).
Листинг 6.1. Простая программа с двумя глобальными переменными
int x = 1; int y = 2;
void main()
{
x = x+y;
printf("total = %d\n", x);
}
Листинг 6.2. Простая программа с двумя локальными переменными
void main()
{
int x = 1; int y = 2;
x = x+y;
printf("total = %d\n", x);
}
Вязыке C разница между глобальными и локальными переменными невелика,
ив данном случае обе программы дают один и тот же результат. Однако дизассемблированные варианты, представленные в листингах 6.3 и 6.4, довольно сильно разнятся. К глобальным переменным обращаются по адресу в памяти, а к локаль-
ным — по адресу в стеке.
|
|
|
|
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 |
|
|
|||
Глава 6. Распознавание конструкций языка C в ассемблере 137 |
to |
|
|
|
|
|
||||
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Влистинге 6.3 глобальная переменная x обозначена как dword_40CF60, с адресом
впамяти 0x40CF60. Заметьте, что при перемещении eax в dword_40CF60 перемен-
ная x меняет свой адрес . Это повлияет на все последующие функции, которые используют эту переменную.
Листинг 6.3. Пример глобальной переменной из листинга 6.1, представленный в ассемблере
00401003 |
mov |
eax, dword_40CF60 |
00401008 |
add |
eax, dword_40C000 |
0040100E |
mov |
dword_40CF60, eax |
00401013 |
mov |
ecx, dword_40CF60 |
00401019 |
push |
ecx |
0040101A |
push |
offset aTotalD ;"total = %d\n" |
0040101F |
call |
printf |
В листингах 6.4 и 6.5 локальная переменная x находится в стеке с постоянным сдвигом относительно ebp. В листинге 6.4 адрес [ebp-4] используется функцией
внеизменном виде для адресации локальной переменной x. Это означает, что адрес ebp-4 находится в стеке и что обратиться к нему можно только из той функции,
вкоторой была объявлена соответствующая переменная.
Листинг 6.4. Пример локальной переменной из листинга 6.2, представленный в ассемблере
00401006 |
mov |
dword ptr [ebp-4], 0 |
0040100D |
mov |
dword ptr [ebp-8], 1 |
00401014 |
mov |
eax, [ebp-4] |
00401017 |
add |
eax, [ebp-8] |
0040101A |
mov |
[ebp-4], eax |
0040101D |
mov |
ecx, [ebp-4] |
00401020 |
push |
ecx |
00401021 |
push |
offset aTotalD ; "total = %d\n" |
00401026 |
call |
printf |
В листинге 6.5 программа IDA Pro любезно дала нашей переменной x фиктивное имя var_4. Как упоминалось в главе 5, фиктивные имена можно поменять на более осмысленные, которые отражают их назначение. Замена -4 на var_4 упрощает анализ, поскольку после переименования переменной в x вам не придется искать в функции сдвиг -4.
Листинг 6.5. Пример локальной переменной из листинга 6.2, представленный в ассемблере и промаркированный
00401006 |
mov |
[ebp+var_4], 0 |
0040100D |
mov |
[ebp+var_8], 1 |
00401014 |
mov |
eax, [ebp+var_4] |
00401017 |
add |
eax, [ebp+var_8] |
0040101A |
mov |
[ebp+var_4], eax |
0040101D |
mov |
ecx, [ebp+var_4] |
00401020 |
push |
ecx |
00401021 |
push |
offset aTotalD ; "total = %d\n" |
00401026 |
call |
printf |
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
w |
|
|
to |
|
|
138 Часть II • Продвинутый статический анализ |
||||
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 |
|
|
|
|
В программе на языке C может выполняться множество разных математических операций. В этом разделе мы рассмотрим то, как они представлены в ассемблере.
В листинге 6.6 показан код на C с двумя переменными и несколькими арифметическими выражениями. Операции -- и ++ используются для декрементирования и соответственно инкрементирования значения на 1. Операция % применяется для получения остатка после деления двух переменных.
Листинг 6.6. Код на C с двумя переменными и набором арифметических операций
int a = 0; int b = 1; a = a + 11; a = a - b; a--;
b++;
b = a % 3;
Ниже показано, как листинг 6.6 будет выглядеть в ассемблере. Этот код можно разбить на части и транслировать обратно в C.
Листинг 6.7. Пример арифметических операций из листинга 6.6, переведенный на ассемблер
00401006 |
mov |
[ebp+var_4], 0 |
0040100D |
mov |
[ebp+var_8], 1 |
00401014 |
mov |
eax, [ebp+var_4] |
00401017 |
add |
eax, 0Bh |
0040101A |
mov |
[ebp+var_4], eax |
0040101D |
mov |
ecx, [ebp+var_4] |
00401020 |
sub |
ecx, [ebp+var_8] |
00401023 |
mov |
[ebp+var_4], ecx |
00401026 |
mov |
edx, [ebp+var_4] |
00401029 |
sub |
edx, 1 |
0040102C |
mov |
[ebp+var_4], edx |
0040102F |
mov |
eax, [ebp+var_8] |
00401032 |
add |
eax, 1 |
00401035 |
mov |
[ebp+var_8], eax |
00401038 |
mov |
eax, [ebp+var_4] |
0040103B |
cdq |
|
0040103C |
mov |
ecx, 3 |
00401041 |
idiv |
ecx |
00401043 |
mov |
[ebp+var_8], edx |
В этом примере а и b являются локальными переменными, поскольку они адресуются в рамках стека. Программа IDA Pro пометила a как var_4, а b — как var_8. Изначально переменным var_4 и var_8 присваиваются значения соответственно 0 и 1. Затем a перемещается в регистр eax , после чего туда добавляется 0x0b, то есть происходит инкремент на 11. После этого из a вычитается b . Компилятор решил использовать инструкции sub и add вместо функций inc и dec.