[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]