Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #96, ДЕКАБРЬ 2006 г.

Ассемблерные извращения

Крис Касперски

Хакер, номер #096, стр. 096-140-3


К тому же кадры некоторых функций достаточно схожи между собой, что позволяет объединить несколько кадров в один! Достаточно сказать, что каждая функция нуждается в переменных, инициализированных нулями. Чтобы не делать много раз один и тот же MOV [EBP+XXh],0 лучше (и быстрее) выполнить REP STOS!

Вот в чем истинная сила ассемблера! Вот извращения, недоступные языкам высокого уровня, но… самые зверские издевательства еще впереди!!!

Защита адреса возврата от переполнения

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

Ассемблер предоставляет по меньшей мере 2 надежных механизма, до которых компиляторы еще не «додумались». Первый и самый простой — это 2 стека: один для хранения адресов возврата, другой - для передачи аргументов и локальных переменных. Кстати говоря, существуют процессорные архитектуры, в которых этот механизм реализован изначально. Но x86-семейство к ним, увы, не относится, поэтому приходится брать в лапы напильник и точить.

Для организации двух раздельных стеков нам требуется всего лишь 1 дополнительный регистр (который можно выделить из пула регистров общего назначения). Пусть это будет регистр EBP, указывающий на стек с локальными переменными. Собственно говоря, неправильно называть его стеком, поскольку в операционных системах семейства Windows стек представляет собой особый регион памяти, подпираемый сверху сторожевой страницей page-guard. Мы же разместим свой стек в памяти, полученной функцией VirtualAlloc или, если хочется оптимизации, в .BSS-секции PE-файла, выделение которой обходится очень дешевого (в плане машинного времени). Но это все детали реализации. Будем считать, что ESP указывает на нормальный стек, а EBP — на рукотворный. Как тогда будет происходить вызов функций и передача аргументов?

А вот так:

; // подготовительные операции

MOV EBP, [XXX] ; XXX - указатель на рукотворный стек

MOV ESP, ESP ; ;-)

…

; // передача аргументов функции

MOV [EBP+00h], arg_a

MOV [EBP+04h], arg_b

MOV [EBP+08h], arg_c

// вызов самой функции

CALL func

…

// ================================================================================

; // реализация самой функции

func:

ADD EBP, local_var_size ; резервируем память под локальные переменные

MOV ECX, [EBP-local_var_size+04h] ; загрузка аргумента arg_b в регистр ECX

MOV ESI, [EBP-local_var_size+08h] ; загрузка аргумента arg_c в регистр ESI

MOV EDI, EBP ; грузим в EDI указатель на конец области локальных переменных

SUB EDI, local_var_size ; вычисляем указатель на локальный буфер

; (в данном случае он расположен по смещению 00h

; относительно фрейма)

REP MOVSB ; копируем arg_b байтов из arg_c в локальный буфер

; // делаем еще что-то полезное

RET ; выходим из функции

Назад на стр. 096-140-2  Содержание  Вперед на стр. 096-140-4