Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #48, НОЯБРЬ 2004 г.

Немой укор за компьютерный хардкор

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

Спецвыпуск: Хакер, номер #048, стр. 048-058-4


Таким образом, все, что нам нужно, – это скопировать самомодифицирующийся код в стек (кучу), где он сможет харакирить себя как захочет. Рассмотрим пример:

//определяем размер самомодифицирующейся функции

#define SELF_SIZE ((int) x_self_mod_end - (int) x_self_mod_code)

//начало самомодифицирующейся функции

//спецификатор naked, поддерживаемый компилятором MS VC,

//указывает компилятору на необходимость создания чистой

//ассемблерной функции, то есть такой функции, куда компилятор

//не внедряет никакой отсебятины

__declspec( naked ) int x_self_mod_code(int a, int b )

{

__asm{

begin_sm: ; начало самомодифицирующегося кода

mov eax, [esp+4] ; получаем первый аргумент

call get_eip ; определяем свое текущее положение в памяти

get_eip:

add eax, [esp + 8 + 4] ; складываем/вычитаем из первого аргумента второй

pop edx ; в edx адрес начала инструкции add eax, …

xor byte ptr [edx],28h ; меняем add на sub и наоборот

ret ; возвращаемся в материнскую функцию

}

} x_self_mod_end(){/* конец самомодифицирующейся функции */ }

main()

{

int a;

int (__cdecl *self_mod_code)(int a, int b);

// раскомментируй следующую строку, чтобы убедиться, что непосредственная

// самомодификация под Windows невозможна (система выплюнет исключение)

// self_mod_code(4,2);

// выделяем память из кучи (в куче модификация кода разрешена)

// с таким же успехом мы могли бы выделить память из стека:

// self_mod_code[SELF_SIZE];

self_mod_code = (int (__cdecl*)(int, int)) malloc(SELF_SIZE);

// копируем самомодифицирующийся код в стек/кучу

memcpy(self_mod_code, x_self_mod_code, SELF_SIZE);

// вызываем самомодифицирующуюся процедуру 10 раз

for (a = 1;a< 10;a++) printf("%02X ", self_mod_code(4,2)); printf("\n");

}

Самомодифицирующийся код заменяет машинную команду "ADD" на "SUB", а "SUB" на "ADD", и потому циклический вызов функции self_mod_code возвращает следующую последовательность чисел: "06 02 06 02…", подтверждая тем самым успешное завершение акта самомодификации.

Некоторые находят предложенную технологию слишком громоздкой. Некоторых возмущает то, что копируемый код должен быть полностью перемещаемым, то есть сохраняющим свою работоспособность независимо от текущего местоположения в памяти. Код, сгенерированный компилятором, в общем случае таковым не является, что вынуждает нас спускаться на чисто ассемблерный уровень. Каменный век! Неужели до сих пор программисты не додумались до более прогрессивных методик?! А как же!

Давай для разнообразия попробуем создать простейшую зашифрованную процедуру, написанную полностью на языке высокого уровня (например, Си, хотя те же самые приемы пригодны и для Паскаля с его уродливым родственником Delphi). При этом мы будем исходить из следующих предположений: а) порядок размещения функций в памяти совпадает с очередностью их объявления в программе (практически все компиляторы так и поступают); б) шифруемая функция не содержит перемещаемых элементов, также называемых fixup'ами или релокациями (это справедливо для большинства исполняемых файлов, но динамическим библиотекам без релокаций никуда).

Назад на стр. 048-058-3  Содержание  Вперед на стр. 048-058-5