[TulaAnti&ViralClub] PRESENTS ...
MooN_BuG, Issue 10, Apr 1999 file 004
Great Permutation System
by Tahorg
Привет всем!
Имею честь представить вашему вниманию вирус, использующий
пермутационный алгоритм (такие алгоритмы известны также как
full-morph, полиморфики 6-го класса). На создание данной
программы меня подвигло одно из описаний в энциклопедии
Касперского. Там описывался вирус (для Windows), в котором после
каждой осмысленной команды шел переход в другое место кода, так
что программа получалась крайне запутанной и неудобочитаемой.
Касперский предполагал, что автор воспользовался какой-то
утилитой, которая привела исходный текст на ассемблере в такое
жуткое состояние. "К счастью, - говорилось дальше, - вирус не
имеет перемешивающей (мутационной) процедуры в своем коде,
последовательность команд остается одной и той же в каждом
зараженном файле". Вот я и решил написать программу, в которой
этот недостаток был бы исправлен. Со свойственной мне скромностью
я назвал ее Great Permutation System (GPS). Поскольку главным
образом меня интересовал именно полиморфный механизм, остальные
вирусные составляющие проработаны слабо - вирус нерезидентный,
заражает exe-файлы в текущем каталоге, не проявляется никакими
эффектами. В вирусный комплект входят файлы:
gps.asm - исходный код на ассемблере;
afp.pas / afp.exe - программа для обработки ассемблеровских
файлов;
lfp.pas / lfp.exe - программа для обработки листингов;
(программы на паскале без
комментариев - там идет исключительно
работа со строками);
gps.prc - результат работы afp.exe;
gps.lst - листинг после компиляции gps.prc;
gps.tbl - результат работы lfp.exe;
gps.exe - что в конце концов получилось.
Для того, чтобы вирус мог перемешивать блоки своего кода в
процессе работы, он должен содержать таблицу, в которой было бы
записано, в каком порядке стоят блоки в настоящий момент. Также
необходима информация о длинах блоков кода. Желательно, чтобы вся
подобная информация генерировалась автоматически. Команды
перехода после осмысленных команд также вставляются специальной
программой (afp.exe).
Процесс изготовления вирусного exe-файла состоит из нескольких
стадий. Сначала, понятно, пишется текст на ассемблере. К нему
предъявляются следующие требования:
1) Начало и конец вирусного кода обозначаются комментариями
;#CodeStart и ;#CodeEnd.
2) Код и данные разделены - все данные располагаются после кода.
3) Внутри кода адресация только относительная - нельзя
использовать самомодификацию кода и конструкции типа
lea ax, proc1
call ax.
4) После некоторых команд ставятся специальные комментарии:
а) После безусловных внутрисегментных переходов (jmp near) -
комментарий ;#JMP
б) После условных переходов (включая loop) - ;#Jxx
в) После межсегментных переходов, команд ret и iret - ;#WARP
г) После внутрисегментных вызовов подпрограмм (call near) -
;#CALL
д) После команд proc, endp и прочих команд, не генерирующих
кода - ;#PROC
Собственно говоря, программа для обработки исходника могла бы
и сама определить типы всех команд. Просто для меня было
быстрее вставить в текст комментарии, чем писать полный
интерпретатор ассемблера.
5) Entry point имеет смещение 0.
Затем ассемблеровский файл обрабатывается программой afp.exe:
afp.exe gps.asm gps.prc
Эта программа никак не изменяет текст за пределами блока
;#CodeStart - ;#CodeEnd, а внутри него производит следующие
манипуляции:
1) а) Если после команды не стоит никаких специальных
комментариев, то вслед за ней в текст вставляется команда
jmp near $+5 (точнее, ее код)
mov ax,bx => MOV AX,BX
DB 0E9H, 0, 0
Такая же команда перехода вставляется после вызовов
подпрограмм (;#CALL)
б) Команды безусловного перехода (;#JMP) заменяются на их код
jmp lab1 ;#JMP => DB 0E9H
DW OFFSET LAB1-$-2
Это делается для того, чтобы ассемблер сгенерировал
команду близкого перехода вне зависимости от расстояния до
точки назначения
в) Команды условного перехода (;#Jxx) заменяются на
конструкцию типа
jc lab1 ;#Jxx => JC $+5
JMP $+6
JMP LAB1
(точнее, вместо команд JMP вставляется соответствующий им
код).
г) Команды с комментариями ;#WARP и ;#PROC не изменяются
Команда, после которой стоит 0, 1 или 2 команды перехода,
представляет собой блок, которые в дальнейшем будут
перемешиваться.
2) Перед каждой командой (кроме команд с комментарием ;#PROC,
которым не соответствует код) вставляется метка вида GPS_xxxxx.
3) Каждому блоку (опять же кроме команд с ;#PROC) ставится в
соответствие константа GPSVAR_xxxxx. Т.е., если в исходном
файле была команда
mov ax,bx
то в prc-файле ей будет соответствовать блок типа
GPSVAR_00100 EQU OFFSET GPS_00101-OFFSET GPS_00100 OR 40H
GPS_00100:
MOV AX,BX
DB 0E9H, 0, 0
GPS_00101:
Младшие 6 бит константы GPSVAR_xxxxx содержат просто длину
соответствующего блока кода, а старшие 2 бита - число
настраиваемых адресов в этом блоке. Это число равно:
а) Для команд с комментарием ;#WARP - 0
б) Для команд с комментарием ;#JMP или без комментариев - 1
в) Для команд с комментариями ;#Jxx или ;#CALL - 2
После этого prc-файл ассемблируется с ключом для получения
листинга.
tasm /m /l gps.prc
Я использовал tasm 3.2. Программа lfp.exe рассчитана на
соответствующий ему формат листинга и с другими ассемблерами
могут быть проблемы.
Листинг обрабатывается программой lfp.exe:
lfp.exe gps.lst gps.tbl
В результате tbl-файл содержит следующие данные:
N equ 262
- число блоков в вирусе (или число команд в исходном коде)
BlockPos dw 0
dw 1
dw 2
dw 3
...
dw 261
- таблица, которая показывает порядок расположения блоков в коде.
Вначале блок номер 0 находится на 1-м месте, за ним - блок номер
1 и т.д. При мутациях элементы этой таблицы перемешиваются
случайным образом.
BlockSize db 0044h
db 0045h
db 0048h
db 0048h
...
- таблица, которая содержит информацию о длине блоков кода и
числе настраиваемых адресов. Сюда просто копируются значения
GPSVAR_xxxxx.
И наконец, нужно вставить данные из файла gps.tbl в gps.prc.
Исходный файл содержит текст
N equ 110h
BlockPos dw N dup (?)
BlockSize db N dup (?)
Этот текст надо из gps.prc убрать и подставить правильные
значения N, BlockPos и BlockSize из tbl-файла. После этого
prc-файл снова компилируется и получается готовый к бою вирус.
Теперь о самом алгоритме мутаций. Порядок следования блоков в
коде записан в таблице BlockPos. Эта таблица копируется в
BlockPos2. Затем элементы BlockPos2 перемешиваются - просто много
раз меняются местами элементы со случайными индексами. Допустим,
в конце концов получилось нечто вроде:
12
85
38
...
- это значит, что в начале кода должен стоять блок номер 12, за
ним - номер 85 и т.д.
Для исходного кода и для кода, который должен получиться после
мутации, создаются таблицы BlockOfs и BlockOfs2, так что их
элементы BlockOfs[i] будут равны смещению i-го блока от начала
кода. Для предыдущего случая будет:
BlockOfs[12]=0
BlockOfs[85]=длина блока 12
BlockOfs[38]=длина блока 12+длина блока 85
и т.д.
Алгоритм получения таблицы смещений следующий (пример на паскале):
for i:=0 to N-1 do begin
BlockOfs[i]:=0;
for j:=0 to N-1 do begin
k:=BlockPos[j];
if k=i then
break
else
BlockOfs[i]:=BlockOfs[i]+(BlockSize[k] and $3f)
end
end
И аналогично для BlockOfs2.
Мутировавший код конструируется следующим образом (пример на
смеси ассемблера и паскаля - думаю, будет понятно):
for i:=0 to N-1 do begin
si:=BlockOfs[i]
di:=BlockOfs2[i]+offset Code2 ;Code2 - место для
;нового кода
cx:=BlockSize[i] and 3fh ;cx=длина блока
rep movsb
<настроить адреса>
end
Последняя команда блока - команда перехода (кроме блоков типа
;#WARP). Поэтому после копирования блока в [si-2] находится
относительный адрес перехода - относительно si. Так что настройка
адреса будет выглядеть так:
target:=si+word ptr [si-2]
for i:=0 to N-1 do
if BlockOfs[i]=target then break ;находим блок, куда
;совершается переход
target2:=BlockOfs2[i]+offset Code2 ;абсолютный адрес
;перехода в
;мутировавшем коде
word ptr [di-2]:=target2-di
Если нужно настроить два адреса, то второй относительный адрес
находится в [si-5] - относительно si-3.
При заражении длина файла вначале выравнивается на границу
параграфа. Поэтому начальное значение ip - это просто смещение
нулевого блока BlockOfs2[0] (требование, чтобы entry point имела
смещение 0, связано только с этим).
Таблица BlockSize представляет собой довольно большую сигнатуру,
поэтому при записи на диск таблицы шифруются.
Вот, собственно, и все. Недостатком пока является то, что код
имеет постоянную длину, а значения cs0 и ip0, необходимые для
лечения, находятся по постоянным адресам. Но, в принципе, не так
уж трудно написать программу, где перемешиваются не только блоки
кода, но и блоки данных, а при копировании блоков к ним
дописывается "мусор" (при этом, правда, потребуется еще одна
таблица с настроечной информацией).
Tahorg.
E-mail: [email protected]