- •Внимание!
- •Об авторах
- •О техническом редакторе
- •О соавторах
- •Предисловие
- •Благодарности
- •Отдельное спасибо
- •Введение
- •Необходимая квалификация
- •Изучение на примерах
- •Структура книги
- •Глава 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 |
|
|
|||
w |
|
|
to |
|
|
450 Часть VI • Специальные темы |
||||
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 |
|
|
|
|
Эта функция вычисляет для своего строкового аргумента 32-битный хеш типа DWORD. Регистр EDI хранит значение текущего хеша и после инициализации равен 0. Каждый байт входящей строки загружается с помощью инструкции lodsb . Если байт не равен NULL, текущий хеш поворачивается вправо на 13 (0x0d) в строке , а текущий байт добавляется в хеш. Результат возвращается в регистр EAX, чтобы вызывающий код мог сравнить его с вкомпилированным значением.
ПРИМЕЧАНИЕ
Популярность алгоритма из листинга 19.5 обусловлена тем, что он включен в пакет Metasploit. Вы можете также встретить разные его вариации с другим поворотом или размером хеша.
Окончательная версия программы Hello World
В листинге 19.6 представлена полная реализация функции findSymbolByHash, с помощью которой можно искать экспортные символы в загруженных динамических библиотеках.
Листинг 19.6. Реализация функции findSymbolByHash
; __stdcall DWORD findSymbolByHash(DWORD dllBase, DWORD symHash);
findSymbolByHash: |
|
|
pushad |
|
|
mov |
ebp, [esp + 0x24] |
; загружаем 1-й аргумент dllBase |
mov |
eax, [ebp + 0x3c] |
; получаем сдвиг PE-сигнатуры |
; загружаем массив DataDirectories в edx: рассчитано на PE32 |
||
mov |
edx, [ebp + eax + 4+20+96] |
|
add |
edx, ebp ; edx:= addr IMAGE_EXPORT_DIRECTORY |
|
mov |
ecx, [edx + 0x18] |
; ecx:= NumberOfNames |
mov |
ebx, [edx + 0x20] |
; ebx:= ОВА массива AddressOfNames |
add |
ebx, ebp |
; rva->va |
.search_loop: |
|
|
jecxz |
.error_done |
; если это конец массива, переходим к done |
dec |
ecx |
; dec: счетчик цикла |
; esi:= следующее имя, использует ecx*4, так как каждый указатель занимает 4 байта
mov |
esi, [ebx+ecx*4] |
|
add |
esi, ebp |
; rva->va |
push |
esi |
|
call |
hashString |
; хешируем текущую строку |
; сравниваем результат со вторым аргументом в стеке: symHash |
||
cmp |
eax, [esp + 0x28] |
|
jnz |
.search_loop |
|
; на этом этапе мы нашли строку в AddressOfNames |
||
mov |
ebx, [edx+0x24] |
; ebx:= ОВА таблицы порядковых номеров |
add |
ebx, ebp |
; rva->va |
;переводим cx в порядковый номер по имени-индексу
;используем еcx*2: каждое значение занимает 2 байта
mov |
cx, [ebx+ecx*2] |
|
mov |
ebx, [edx+0x1c] |
; ebx:= ОВА мaссива AddressOfFunctions |
add |
ebx, ebp |
; rva->va |
|
|
|
|
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 |
|
|
|||
Глава 19. Анализ кода командной оболочки 451 |
to |
|
|
|
|
|
||||
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
; eax:= ОВА экспортной функции. Используем ecx*4: каждое значение занимает 4 байта
mov |
eax, [ebx+ecx*4] |
|
add |
eax, ebp |
; rva->va |
jmp |
near .done |
|
.error_done: |
|
|
xor |
eax, eax |
; очищаем eax при ошибке |
.done: |
|
|
mov |
[esp + 0x1c], eax |
; перезаписываем eax, сохраненный в стеке |
popad |
|
|
retn 8 |
|
|
Вкачестве аргументов эта функция принимает указатель на базовый адрес DLL
и32-битный хеш, который соответствует искомому символу. В результате в регистре EAX возвращается указатель на запрашиваемую функцию. Помните, что все виртуальные адреса в PE-файле являются относительными, поэтому, чтобы создать указатели, которые можно использовать, коду приходится добавлять значение dllBase (в этом примере оно хранится в регистре EBP) к каждому полученному ОВА.
Код начинает разбор PE-файла в строке , пытаясь получить указатель на PE-сигнатуру. В строке путем добавления подходящего сдвига создается указатель на IMAGE_EXPORT_DIRECTORY (предполагается, что файл 32-битный). Разбор структуры IMAGE_EXPORT_DIRECTORY начинается в строке ; для этого загружаются значение NumberOfNames и указатель AddressOfNames. Каждый указатель на строку в AddressOfNames передается в функцию hashString , а результат вычисления сравнивается со значением, переданным в качестве аргумента .
Обнаружив подходящий элемент внутри AddressOfNames, код использует его как указатель для массива AddressOfNameOrdinals , чтобы получить соответствующий порядковый номер. Затем этот номер послужит индексом для массива AddressOfFunctions . Это то значение, которое нужно пользователю, поэтому оно сохраняется в стек , перезаписывая содержимое EAX, которое было создано инструкцией pushad. Следующая инструкция popad оставит это значение без изменений.
В листинге 19.7 показана полная версия примера Hello World, которая использует определенные выше функции findKernel32Base и findSymbolByHash, не полагаясь на заранее встроенные адреса API-вызовов.
Листинг 19.7. Пример Hello World, не зависящий от размещения
mov |
ebp, esp |
|
sub |
esp, 24h |
|
call |
sub_A0 |
; вызываем настоящее начало кода |
db 'user32',0 |
|
|
db 'Hello World!!!!',0 |
|
|
sub_A0: |
|
|
pop |
ebx |
; ebx получает указатель на данные |
call |
findKernel32Base |
|
mov |
[ebp-4], eax |
; сохраняем базовый адрес kernel32 |
push |
0EC0E4E8Eh |
; хеш функции LoadLibraryA |
push |
dword ptr [ebp-4] |
|
call |
findSymbolByHash |
|
mov |
[ebp-14h], eax |
; сохраняем адрес LoadLibraryA |
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|
|
|||
|
|
X |
|
|
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
|
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
|
r |
|
|
||
P |
|
|
|
|
|
NOW! |
o |
|
|
|||
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|
|
|||
w |
|
|
to |
|
|
452 |
Часть VI • Специальные темы |
|||||
w Click |
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
o |
m |
|
|
||||
|
w |
|
|
|
|
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
.c |
|
|
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
|
|
df |
|
|
n |
e |
|
|
|
||
|
|
|
|
-xcha |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
lea |
eax, [ebx] |
; eax указывает на "user32" |
|
|
|
|
|
|
|
|
|
|
push |
eax |
|
|
|
|
|
|
|
|
|
|
|
call |
dword ptr [ebp-14h] |
; LoadLibraryA |
|
|
|
|
|
|
|
|
|
|
mov |
[ebp-8], eax |
; сохраняем базовый адрес user32 |
|
|
|
|
|
|
|
|
|
|
push |
0BC4DA2A8h |
; хеш функции MessageBoxA |
|
|
|
|
|
|
|
|
|
|
push |
dword ptr [ebp-8] |
; местоположение библиотеки user32 |
|
|
|
|
|
|
|
|
|
|
call |
findSymbolByHash |
|
|
|
|
|
|
|
|
|
|
|
mov |
[ebp-0Ch], eax |
; сохраняем адрес MessageBoxA |
|
|
|
|
|
|
|
|
|
|
push |
73E2D87Eh |
; хеш функции ExitProcess |
|
|
|
|
|
|
|
|
|
|
push |
dword ptr [ebp-4] |
; местоположение библиотеки kernel32 |
|
|
|
|
|
|
|
|
|
|
call |
findSymbolByHash |
|
|
|
|
|
|
|
|
|
|
|
mov |
[ebp-10h], eax |
; сохраняем адрес ExitProcess |
|
|
|
|
|
|
|
|
|
|
xor |
eax, eax |
|
|
|
|
|
|
|
|
|
|
|
lea |
edi, [ebx+7] |
; edi:= указатель на "Hello World!!!!" |
|
|
|
|
|
|
|
|
|
|
push |
eax |
; uType: MB_OK |
|
|
|
|
|
|
|
|
|
|
push |
edi |
; lpCaption |
|
|
|
|
|
|
|
|
|
|
push |
edi |
; lpText |
|
|
|
|
|
|
|
|
|
|
push |
eax |
; hWnd: NULL |
|
|
|
|
|
|
|
|
|
|
call |
dword ptr [ebp-0Ch] |
; вызываем MessageBoxA |
|
|
|
|
|
|
|
|
|
|
xor |
eax, eax |
|
|
|
|
|
|
|
|
|
|
|
push |
eax |
; uExitCode |
|
|
|
|
|
|
|
|
|
|
call |
dword ptr [ebp-10h] |
; вызываем ExitProcess |
|
|
|
|
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 |
|
|
|
|
Код начинается с использования инструкций call/pop для получения указателя на данные . Затем вызываются функции findKernel32Base и findSymbolByHash , чтобы найти библиотеку kernel32.dll и получить из нее экспортный символ с хешем 0xEC0E4E8E. Этот добавочный хеш с поворотом на 13 соответствует строке LoadLibraryA. Результат, который эта функция записывает в EAX, будет указывать на реальный адрес LoadLibraryA.
Код загружает указатель на строку "user32" и вызывает функцию LoadLibraryA. После этого он находит и вызывает функцию MessageBoxA , чтобы вывести сообщение Hello World!!!!. В конце происходит корректное завершение работы с помощью вызова ExitProcess.
ПРИМЕЧАНИЕ
Разбор PE-файла с помощью встроенных возможностей shell-кoда вместо вызова GetProcAddress имеет еще одно преимущество: это усложняет обратное проектирование. При поверхностном анализе значения хешей скрывают API-вызовы.
Кодировки кода командной оболочки
Чтобы выполнить код командной оболочки, его нужно поместить в адресное пространство программы, из которого он будет вызван. Он должен находиться перед эксплойтом или передаваться вместе с ним. Например, если программа выполняет какую-нибудь простую фильтрацию входящих данных, shell-код должен миновать этот фильтр, иначе он не сможет попасть на уязвимый участок процесса.
|
|
|
|
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 |
|
|
|||
Глава 19. Анализ кода командной оболочки 453 |
to |
|
|
|
|
|
||||
w Click |
|
|
|
|
|
m |
||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
Это означает, что многие уязвимые программы принимают shell-код только в том случае, если он похож на обычные данные.
Вкачестве примера можно привести программу, использующую небезопасные строковые функции strcpy и strcat, которые не устанавливают максимальный размер записываемых ими данных. Если программа считывает или копирует вредоносную информацию в буфер фиксированной длины, используя любую из этих функций, это может легко привести к атаке на основе переполнения буфера. Эти функции работают со строками как с массивами символов, в конце которых находится нулевой байт (0x00). Код командной оболочки, который злоумышленник хочет скопировать в этот буфер, должен выглядеть как обычные данные. Это означает, что посреди него не должно находиться нулевых байтов, иначе операция копирования строки завершится преждевременно.
Влистинге 19.8 показан небольшой фрагмент ассемблерного кода для доступа
креестру. Внутри него вы можете видеть несколько нулевых байтов, поэтому в таком виде он, скорее всего, не подойдет для использования в качестве shell-кода.
Листинг 19.8. Типичный код с выделенными нулевыми байтами
57 |
|
|
|
|
|
push |
edi |
|
50 |
|
|
|
|
|
push |
eax |
; phkResult |
6A |
01 |
|
|
|
|
push |
1 |
; samDesired |
8D |
8B |
D0 |
13 |
00 |
00 |
lea |
ecx, [ebx+13D0h] |
|
6A |
00 |
|
|
|
|
push |
0 |
; ulOptions |
51 |
|
|
|
|
|
push |
ecx |
; lpSubKey |
68 |
02 |
00 |
00 |
80 |
|
push |
80000002h |
; hKey: HKEY_LOCAL_MACHINE |
FF 15 |
20 |
00 |
42 |
00 |
call |
ds:RegOpenKeyExA |
|
Иногда код командной оболочки попадает под дополнительные проверки, которые устраивают программы. Например:
все байты должны быть печатными символами в формате ASCII (быть меньше 0x80);
все байты должны быть буквами или цифрами (от A до Z, от а до z и от 0 до 9).
Чтобы обойти ограничения фильтрации со стороны уязвимой программы, почти любой shell-код шифрует свою основную часть и вставляет декодер, который превращает зашифрованные данные в исполняемые байты. Для удовлетворения строгим условиям фильтров нужно тщательно написать лишь сам декодер, а остальной код пройдет фильтрацию за счет кодирования, которое можно выполнять на этапе компиляции. Процедура, когда код командной оболочки записывает декодированные байты поверх закодированных (что обычно и происходит), называется саморедактированием. Когда она завершится, декодер передаст управление основному вредоносному коду.
Ниже приводятся распространенные методы кодирования.
Применить ко всем байтам исключающее ИЛИ с постоянной байтовой маской. Помните, что, если значения a и b имеют одинаковый размер, для них справедливо уравнение (a XOR b)XOR b == a.