VX Heaven

Dark Angel
40hex [11]
Июнь 1993

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

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

Прежде чем пытаться написать полиморфную программу, необходимо получить руководство с подробным описанием набора инструкций 80x86.
набор инструкций 80x86. Без манипуляций с опкодами на уровне битов любая полиморфная программа
будет иметь ограниченную область применения. Хорошая жесткая структура набора инструкций 80x86 станет очевидна
легко заметна после простого ознакомления с опкодами. Использование этого структурированного
набора инструкций позволяет использовать компактные процедуры генерации кода, которые лежат в основе каждой
значимых полиморфных процедур.

После изучения структуры операционных кодов, следует изложить базовую организацию полиморфной
должна быть изложена базовая организация полиморфной подпрограммы. Здесь необходимо понимание основ, лежащих в основе таких подпрограмм.
необходимо. Традиционный подход рассматривает процедуру дешифрования как простую исполняемую
строку, например, "BB1301B900022E8137123483C302E2F6". Истинная (продвинутая) полиморфная
напротив, рассматривает процедуру расшифровки как концептуальный алгоритм, например, "Установить
регистр 'указатель', то есть регистр, содержимое которого содержит указатель на память, подлежащую
расшифровывается. Установите регистр счетчика. Используйте регистр-указатель для расшифровки одного байта. Обновить
регистр указателя. Уменьшить регистр счетчика, зацикливаясь, если он не равен нулю". Две программы, которые
соответствуют этому алгоритму:

Образец шифрования 1

          mov bx,offset startencrypt    ; here, bx is the 'pointer' register
          mov cx,viruslength / 2     ; and cx holds the # of iterations
decrypt_loop:
          xor word ptr [bx],12h      ; decrypt one word at a time
          inc bx              ; update the pointer register to
          inc bx              ; point to the next word
          loop decrypt_loop          ; and continue the decryption
startencrypt:

Образец шифрования 2


start:
         mov bx,viruslength ; now bx holds the decryption length
         mov bp,offset start ; bp is the 'pointer' register
decrypt_loop:
         add byte ptr [bp+0Ch],33h ; bp+0Ch -> memory location to be
                          ; decrypted at each iteration
         inc bp             ; update the pointer register
         dec bx             ; and the count register
         jnz decrypt_loop        ; loop if still more to decrypt

Число возможностей, по сути, бесконечно. Естественно, рассмотрение расшифровки как
алгоритм, а не как исполняемую строку, значительно повышает гибкость при создании
фактической процедуры. Различные части алгоритма расшифровки могут быть изменены, что дает возможность
для дальнейших вариаций. Используя приведенный выше пример, одним из возможных вариантов является изменение порядка
порядок установки регистров, т.е.


    mov cx,viruslength
    mov bx,offset startencrypt

вместо

    mov bx,offset startencrypt
    mov cx,viruslength

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

Первым шагом, который проходит большинство полиморфных программ, является определение точной
вариации, которая должна быть закодирована. Например, полиморфная программа может решить, что для
процедура расшифровки должна использовать шифрование xor длиной слова с bx в качестве регистра указателя, dx в качестве
контейнер для значения шифра, а cx - регистр счетчика. Как только эта информация будет
эта информация известна, программа должна быть в состоянии вычислить начальное значение каждой переменной. Например,
если cx - это регистр счетчика для шифрования длиной в байт, то в нем должна храниться длина вируса.
Для повышения вариативности длина шифра может быть увеличена на небольшую, случайную
величину. Обратите внимание, что некоторые переменные, в частности регистр указателя, могут быть неизвестны
до кодирования остальной части программы. Эта деталь обсуждается ниже.

Разумеется, выбор переменных и регистров сам по себе не приведет к корректной расшифровке.
полиморфная программа должна также кодировать фактические инструкции для выполнения работы!
Самые дешевые полиморфные программы кодируют единственную инструкцию "mov" для присвоения значения регистру.
значения в регистр. Более сложные программы кодируют серию инструкций, которые
функционально эквивалентны простой трехбайтовой инструкции "mov", но значительно отличаются по форме. Например,
например,

        mov ax, 808h

можно заменить на

        mov ax, 303h        ; ax = 303h
        mov bx, 101h       ; bx = 101h
        add ax, bx       ; ax = 404h
        shl ax, 1      ; ax = 808h

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

После того как регистры закодированы, следует закодировать собственно цикл расшифровки. Цикл
может выполнять ряд действий, наиболее значимым из которых должно быть манипулирование участком памяти
ячейкой памяти, т.е. фактической инструкцией расшифровки, и обновление регистра указателя, если это необходимо.
при необходимости. Наконец, сама инструкция цикла должна быть закодирована. Она может принимать различные формы,
включая "loop", "loopnz", "jnz" и т.д. Возможные вариации включают изменение значения расшифровки
регистра и регистра счетчика во время каждой итерации.

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

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

Расшифровщики переменной длины имеют существенный компромисс; точное начало расшифровки
не может быть известно до кодирования дешифратора. Существует два подхода к работе
обойти это ограничение. Первый заключается в кодировании регистра указателя в одной инструкции, т.е.
mov bx,185h и изменить начальное значение, как только оно станет известно. Это упрощенный, хотя и
нежелательно, поскольку снижает вариативность процедуры. Альтернативный подход заключается в том, чтобы закодировать
инструкцию шифрования в виде xor word ptr [bx+185h], cx (как в примере шифрования 2,
выше) вместо xor word ptr [bx], cx (как в примере шифрования 1). Это повышает
гибкость программы, так как начальное значение регистра указателя не обязательно должно быть каким-либо фиксированным
значение; правильность расшифровки может быть обеспечена регулировкой смещения в инструкции расшифровки
инструкции. Затем можно кодировать регистр-указатель несколькими инструкциями,
повышая гибкость. Однако использование любого из этих методов повышает предсказуемость
сгенерированного кода. Лучшим подходом будет включение обоих методов в одну
полиморфную процедуру и случайный выбор одного из них во время каждого запуска.

В качестве примера полиморфной программы я привожу DAME, Dark Angel's Multiple Encryptor
и простой вирус, который его использует. Они представлены в следующей статье. DAME использует целый ряд
мощных техник для достижения полного полиморфизма. Кроме того, его легко усовершенствовать; обе
процедуры кодирования и гарблеров могут быть расширены алгоритмически с минимальными усилиями.
В следующем выпуске я подробно прокомментирую и объясню различные части DAME.