Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
книги хакеры / Майкл_Сикорски,_Эндрю_Хониг_Вскрытие_покажет!_Практический_анализ.pdf
Скачиваний:
18
Добавлен:
19.04.2024
Размер:
17.17 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

.

 

 

 

 

21

 

 

 

 

 

 

 

.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-битными, но бывают и такие, которые написаны под 64-битную архитектуру с расчетом на взаимодействие с 64-битной ОС. Популярность последних растет по мере распространения 64-битных операционных систем.

Существует несколько 64-битных архитектур. Одна из них, Itanium, стала первой поддерживаться в Windows; она создана для производительных вычислений и несовместима с x86. Позже компания AMD представила 64-битную архитектуру под названием AMD64, которая способна выполнять код формата x86. Компания Intel переняла эту технологию и назвала свою реализацию EM64T. Теперь эта архитектура известна как x64 или x86-64, и на сегодняшний день это самая популярная реализация 64-битного кода в Windows. Все современные версии Windows имеют 64-битный вариант с поддержкой как 64-, так и 32-битных приложений.

Архитектура x64 разрабатывалась в качестве надстройки над x86, поэтому наборы их инструкций не сильно различаются. Если вы откроете 64-битный исполняемый файл в IDA Pro, вам должно быть знакомо большинство инструкций. Одна из трудностей, связанных с анализом 64-битного вредоносного ПО, заключается в том, что не все инструменты поддерживают ассемблер в формате x64. Например, на момент написания этой книги OllyDbg (в отличие от WinDbg) не поддерживал 64-битные приложения. В IDA Pro такая поддержка имеется, но для этого вам потребуется версия Advanced.

Данная глава посвящена различиям между 32- и 64-битными системами. Здесь вы также найдете несколько советов относительно анализа 64-битного кода.

Какой смысл в 64-битном вредоносном ПО?

Тридцатидвухбитное вредоносное ПО может атаковать как 32-, так и 64-битные системы. Зачем же тогда тратить время на написание отдельной 64-битной версии?

Одна и та же система поддерживает приложения с любой разрядностью, однако вы не можете выполнять 32-битный код внутри 64-битного процесса. Когда процессор выполняет 32-битные инструкции, он находится в 32-битном режиме и не может обрабатывать 64-битный код. Поэтому, если вредоносу нужно работать в рамках адресного пространства 64-битного процесса, он должен сам быть 64-битным.

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

w

 

 

to

 

 

472  Часть 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

 

 

 

 

Ниже приводится несколько ситуаций, в которых вредоносную программу нужно компилировать под архитектуру x64.

Режим ядра. Весь код ядра операционной системы находится в одном и том же адресном пространстве, и его разрядность соответствует разрядности ОС. Руткиты часто выполняются внутри ядра, поэтому, если они атакуют 64-битные ОС, они должны быть скомпилированы в 64-битный машинный код. Кроме того, антивирусы и локальные системы безопасности часто содержат модули ядра, и, если вредоносное ПО противодействует таким приложениям, оно тоже должно быть 64-битным или как минимум иметь 64-битные компоненты. В 64-бит- ных версиях Windows есть механизм обнаружения неавторизованных изменений ядра, что усложняет его заражение и исключает загрузку драйверов без цифровой подписи (эти нововведения были подробно рассмотрены в конце главы 10).

Плагины и внедрение кода. Чтобы корректно выполняться в 64-битном процессе, плагины и внедряемый код тоже должны быть 64-битными. Например, 64-битная версия Internet Explorer поддерживает только 64-битные плагины и компоненты ActiveX. Код, который внедряется так, как это было описано в главе 12, тоже работает в рамках другого процесса. И, если этот процесс 64-битный, код должен иметь ту же разрядность.

Код командной оболочки. Код командной оболочки обычно является частью эксплойта внутри зараженного процесса. Например, чтобы воспользоваться уязвимостью в 64-битной версии Internet Explorer, злоумышленнику придется написать 64-битный shell-код. Чем больше пользователей запускает одновременно 64- и 32-битные приложения, тем чаще авторам вредоносного ПО приходится писать отдельный код командной оболочки для каждой из архитектур.

Особенности архитектуры x64

Ниже перечислены наиболее важные отличия архитектуры x64 от x32 в контексте Windows.

Все адреса и указатели x64 являются 64-битными.

Все регистры общего назначения (включая RAX, RBX, RCX и т. д.) имеют увеличенный размер, хотя при этом доступны их 32-битные версии. Например, RAX является 64-битным вариантом регистра EAX.

Некоторые регистры общего назначения (RDI, RSI, RBP и RSP) были расширены для поддержки побайтового доступа путем добавления суффикса L к 16-битной версии. Например, BP обычно содержит 16 младших бит регистра RBP, а BPL — 8 младших бит регистра RBP.

Специальные регистры являются 64-битными и имеют другие названия. Например, RIP — это 64-битный указатель на инструкцию.

 

 

 

 

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

 

 

Глава 21. Шестидесятичетырехбитные вредоносные программы  473

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Регистров общего назначения стало в два раза больше. Новые регистры получили имена R8–R15. Их версии типа DWORD (32-битные) доступны как R8D, R9D и т. д. Для доступа к версиям типа WORD (16-битным) используется суффикс W (R8W, R9W и т. д.), а для байтовых версий предусмотрен суффикс L (R8L, R9L и т. д.).

Архитектура x64 также поддерживает относительную адресацию данных с помощью указателя на инструкции. Это важное отличие от x86 в контексте контроллера прерываний и кода командной оболочки. В частности, чтобы получить в ассемблере формата x86 доступ к данным по адресу, который не является сдвигом относительно регистра, приходится хранить в инструкции весь адрес целиком. Это называется абсолютной адресацией. Но в архитектуре x64 ассемблер позволяет обращаться к данным со сдвигом относительно указателя на текущую инструкцию. В литературе, посвященной технологии x64, это называется адресацией относительно регистра RIP. В листинге 21.1 показана простая программа на языке C, которая обращается к адресу в памяти.

Листинг 21.1. Простая программа на языке C с доступом к данным

int x;

void foo() { int y = x;

...

}

Если перевести листинг 21.1 в ассемблерный код формата x86, он будет обращаться к глобальным данным (то есть к переменной x). Для этого инструкция mov кодирует 4 байта, представляющих собой адрес данных. Эта инструкция зависит от размещения, поскольку она всегда получает доступ к адресу 0x00403374, но, если загрузить исполняемый файл в другом месте, ее придется изменить, чтобы она обращалась к корректному участку памяти (листинг 21.2).

Листинг 21.2. Ассемблер формата x86 для программы из листинга 21.1

00401004 A1

74

33

40

00 mov

eax, dword_403374

Можно заметить, что байты адреса хранятся вместе с инструкцией в позициях , , и . Как вы помните, порядок их размещения направлен от младшего байта к старшему. Байты 74, 33, 40 и 00 соответствуют адресу 0x00403374.

После перекомпиляции кода для платформы x64 мы получим ту же инструкцию mov, что и в листинге 21.2.

Листинг 21.3. Ассемблер формата x64 для листинга 21.1

0000000140001058 8B 05

A2

D3

00

00 mov

eax, dword_14000E400

На ассемблерном уровне никаких изменений вроде бы нет. Инструкция попрежнему имеет вид mov eax, dword_адрес, и IDA Pro автоматически вычисляет ее адрес. Но благодаря различию на уровне опкодов данный код на архитектуре x64 является позиционно-независимым (чего нельзя сказать в случае с x86).

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

w

 

 

to

 

 

474  Часть 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

 

 

 

 

В 64-битной версии байты инструкции не содержат фиксированного адреса данных. Адрес равен 14000E400, но байты выглядят как A2 , D3 , 00 и 00 , что соответствует значению 0x0000D3A2.

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

Адресация относительно указателя на инструкцию — это важная особенность платформы x64, которая существенно уменьшает количество адресов, нуждающихся в перемещении при загрузке DLL. Она также упрощает написание shell-кода, поскольку для обращения к данным больше не нужно получать указатель на EIP. Эта возможность устраняет необходимость в инструкциях call/pop, чем затрудняет распознавание кода командной оболочки (см. раздел «Позиционно-независимый код» в главе 19). При работе с вредоносным ПО, написанным для архитектуры х64, многие методики скрытия shell-кода становятся излишними или теряют свою актуальность.

Особенности вызова кода и использования стека на платформе х64

Формат вызова кода в 64-битных версиях Windows больше всего напоминает использование 32-битного соглашения fastcall, рассмотренного в главе 6. Первые четыре параметра вызова загружаются в регистры RCX, RDX, R8 и R9, а еще один попадает в стек.

ПРИМЕЧАНИЕ

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

При работе с 32-битным кодом пространство стека можно выделять и освобо­ждать посреди функции, используя инструкции push и pop. Но в архитектуре x64 функция не может выделять какое-либо пространство в процессе выполнения, даже если она использует инструкцию push или другую операцию для изменения стека.

На рис. 21.1 сравнивается управление стеком в 32- и 64-битном коде. Обратите внимание на то, что на левом графике размер стека растет по мере записи в него

 

 

 

 

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

 

 

Глава 21. Шестидесятичетырехбитные вредоносные программы  475

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

аргументов и уменьшается при очистке. Пространство стека выделяется в начале функции, меняя свой объем по ходу ее работы. Когда функция вызывается, стек расширяется; когда функция завершается, стек возвращается к обычному размеру. Сравните это с 64-битной версией, в которой стек растет в начале функции, но остается неизменным, пока та не закончит свою работу.

Рис. 21.1. Размер стека одной и той же функции, скомпилированной для архитектур x86 и x64

Иногда 32-битный компилятор может сгенерировать код, который не меняет размер стека посреди функции, но для 64-битного кода это обязательное требование. И хотя данное ограничение не требуется на уровне процессора, от него зависит корректная работа модели обработки исключений, используемой в Windows x64. Столкнувшись с исключением, функции, которые не соблюдают это соглашение, могут привести к сбою программы или вызвать другие проблемы.

Из-за отсутствия инструкций push и pop посреди функции аналитику может быть сложнее выяснить, сколько аргументов она принимает: нет простого способа определить, для чего используется адрес в памяти — для локальной переменной или входящего параметра. Мы также не можем узнать, хранится ли параметр в регистре. Например, если прямо перед вызовом функции загрузить в ECX какоенибудь значение, вы не сможете сказать, является ли оно параметром или чем-то другим.

В листинге 21.4 показан пример дизассемблированного вызова функции, который был скомпилирован для 32-битного процессора.

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

w

 

 

to

 

 

476  Часть VI  •  Специальные темы

w Click

 

 

 

 

 

 

 

 

 

 

 

o

m

 

w

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

Листинг 21.4. Вызов функции printf, скомпилированный для 32-битного процессора

004113C0

mov

eax, [ebp+arg_0]

004113C3

push

eax

004113C4

mov

ecx, [ebp+arg_C]

004113C7

push

ecx

004113C8

mov

edx, [ebp+arg_8]

004113CB

push

edx

004113CC

mov

eax, [ebp+arg_4]

004113CF

push

eax

004113D0

push

offset aDDDD_

004113D5

call

printf

004113DB

add

esp, 14h

 

 

 

 

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

 

 

 

 

Прежде чем вызвать printf, 32-битный ассемблерный код выполняет пять инструкций push. Сразу после вызова стек очищается путем добавления в него значения 0x14. Это явно говорит о том, что функции printf передается пять параметров.

В листинге 21.5 показан ассемблерный код того же вызова, скомпилированного для 64-битного процессора.

Листинг 21.5. Вызов функции printf, скомпилированный для 64-битного процессора

0000000140002C96

mov

ecx, [rsp+38h+arg_0]

0000000140002C9A

mov

eax, [rsp+38h+arg_0]

0000000140002C9E

mov

[rsp+38h+var_18], eax

0000000140002CA2

mov

r9d, [rsp+38h+arg_18]

0000000140002CA7

mov

r8d, [rsp+38h+arg_10]

0000000140002CAC

mov

edx, [rsp+38h+arg_8]

0000000140002CB0

lea

rcx, aDDDD_

0000000140002CB7

call

cs:printf

В 64-битном варианте количество параметров, передаваемых в printf, является менее очевидным. Загрузка инструкций в регистры RCX, RDX, R8 и R9 указывает на перемещение аргументов функции, но инструкция mov в строке менее очевидна. IDA Pro маркирует это значение как локальную переменную, но у нас нет четкого способа отличить его от аргумента вызываемой функции. В данном случае мы можем просто проверить, сколько параметров передается в строку форматирования, но в других ситуациях все может оказаться гораздо сложнее.

Листовые и нелистовые функции

Соглашение об использовании 64-битного стека делит функции на листовые и нелистовые. Нелистовая функция содержит внутри себя другие вызовы, а листовая — нет.

Нелистовые функции иногда называют многослойными, поскольку для каждого вызова требуется слой стека. При любом вызове в стеке должно выделяться 0x20 байт. В это пространство вызываемая функция может при необходимости сохранить параметры, загруженные в регистры (RCX, RDX, R8 и R9).

Листовые и нелистовые функции модифицируют стек только в начале и в конце своей работы. Эти участки, отвечающие за изменение стека, рассматриваются далее.

 

 

 

 

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

 

 

Глава 21. Шестидесятичетырехбитные вредоносные программы  477

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Пролог и эпилог в 64-битном коде

В Windows 64-битный ассемблерный код имеет четкие разделы в начале и конце функций. Они называются прологом и эпилогом и могут содержать полезную информацию. Любая инструкция mov в прологе всегда используется для сохранения аргументов, переданных в функцию (компилятор не может вставить в пролог инструкцию mov, которая занимается чем-то другим). Пример пролога в небольшой функции показан в листинге 21.6.

Листинг 21.6. Код пролога в небольшой функции

00000001400010A0

mov

[rsp+arg_8], rdx

00000001400010A5

mov

[rsp+arg_0], ecx

00000001400010A9

push

rdi

00000001400010AA

sub

rsp, 20h

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

Обработка исключений в 64-битном коде

В отличие от 32-битных систем, архитектура x64 не использует стек для структурированной обработки исключений. В 32-битном коде fs:[0] выступает в роли указателя на текущий слой обработки, который хранится в стеке; это позволяет каждой функции определить свой собственный обработчик исключений. Как следствие,

вначале функции часто можно увидеть инструкции, изменяющие значение fs:[0]. Существуют также эксплойты, которые модифицируют эту информацию в стеке, чтобы получить контроль над кодом во время исключительной ситуации.

На платформе x64 в структурированной обработке исключений используется статическая таблица, которая хранится в PE-файле. В стек не сохраняется никакая информация, связанная с этим процессом. Для каждой функции в исполняемом файле предусмотрена структура IMAGE_RUNTIME_FUNCTION_ENTRY. Она находится

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

WOW64

Компания Microsoft разработала подсистему WOW64 (Windows 32-bit on Windows 64-bit), которая позволяет корректно выполнять 32-битные приложения на 64-бит- ном компьютере. У этой подсистемы есть несколько особенностей, которыми могут воспользоваться злоумышленники.