[Death Virii Crew] Presents
CHAOS A.D. Vmag, Issue 3, Autumn 1996 - Winter 1997


			 Как же работают полиморфики.


 PC>   Слушай, объясни, как работают полиморфики ?
 PC>   Я так понимаю, что берут команды типа
 PC>      mov ax,5
 PC>   и заменяют на что-то типа
 PC>     push cx
 PC>     xor cx,cx
 PC>     inc cx    ;  *!*
 PC>     xhcg ax,cx
 PC>     add ax,4  ;  *!*
 PC>     pop cx
 PC>       Или вроде того. Ессесьно, каждый раз  команды типа *!* меняются
 PC>   на другие.
 PC>    Или я не прав ?

   ну что то в этом роде. в общем классический полиморфик строится так:

    1) декриптор
    2) зашифрованное тело

   Зашифрованное тело всегда разное, то есть криптуют его каждый раз по разному
   и разными ключами. Hа этом и основана его неповторяемость.
   Далее: декриптор. Цель сделать его как можно более различным по коду каждый
   каждый раз при его образовании. то есть - в каждом новом сгенерированном
   декрипторе все по разному. то есть (в максимуме) ни одного постоянного 
   байта.
   Теперь как это сделать:
   допустим имеется некий образец декриптора (примитивный для примера) :

   mov ax,kod
   mov si,offset
   mov cx,len

 @1:
   xor [si],ax
   inc si
   inc si
   loop @1

  как мы можем его изменить так, чтобы по маске его уже было не выловить.

 - Первое. мы видим первые три команды, от перестановки которых местами в
  любом порядке результат не изменится. Следовательно мы можем их менять
  местами.
 - Второе: мы видим постоянные регистры используемые в данном декрипторе,
  как то - ax,cx,si.
  Мы можем их изменить на другие. si например, как регистр индексный мы
  можем поменять на di,bx,bp, то есть другие индексные регистры.
  ax например на любой не задействованный в декрипторе.
  cx не можем изменить, но посредством замены кода мы можем избавиться от
  этой жесткой привязки (см. ниже)
  Так вот, все регистры мы можем менять. все встречающиеся команды с
  использованием регистра, который мы меняем мы меняем тоже соответственно.
- Третье: мы можем добавить в код некоторые дополнительные команды, которые
  по сути не играют роли в исполнении, но присутствуют затем, чтобы как можно
  более размыть маску. допустим однобайтовые: nop,cld,cmc... и такое подобное.
  двухбайтовые: adc dx,bx; sub bx,ax, трехбайтовые: mov xx,[любое 16 битовое
  значение] ну и так далее. В этом контексте надо использовать все регистры
  которые _незадействованы_ в твоем декрипторе. то есть если вдруг ты
  используешь в декрипторе ax,si,cx то в полиморном мусоре ты используешь все
  остальные.
- Четвертое: как ты уже сказал взаимозаменение действующих команд. ты в принципе
  сам ответил на этот вопрос.
  добавлю немного что вот loop @1 ты мог бы заменить на dec cx,jnz @1 или
  cmp si,offset конца декриптуемого кода и jbe @1 или jb @1 , в зависимости если
  ты переставишь местами si и оffset то выйдет уже jae @1 или ja.
  если декриптор большой и не влазит в 128 байт то формируй что-то вроде:

  cmp si,offset_of_end
  jbe @2
  jmp @1 ; всместо jmp можно использовать mov xx,@1 и jmp xx (xx - это        
  неиспользуемый регистр)
  @2:
           Короче - это твоя фантазия как это сделать ;))

- Пятое - частично производное из четвертого: команды раскриптовщика как то -
  xor мы можем заменить на sub,add например.

 соответственно декриптовщик строится в соответствии с криптовщиком.
 соответствие должно проводится по - ключам раскриптовщика и методам
 раскриптовки (add,sub,xor).

- Шестое - мы можем использовать несколько скелетов раскриптовщиков.
  например первый, который раскручивает линейно и просто по одному коду.
  мы можем написать также раскриптовщик который декриптует прыжками по телу
  раскриптовываемого кода. также мы можем (в примитиве) менять ключ
  раскриптовщика динамически. Hапример между командочками inc si и inc si
  мы можем вставить add ax,bx или sub ax,bx. соответственно в начале
  мы должны вставить команду mov bx,kod2.

 Стоит ли говорить, что kod,kos2,offset,len - это всегда разные значения.

- Седьмое - это изобретение чисто мое: использование действующих регистров
  в разных командах накладывает некий отпечаток на код. например:

  mov ax,kod
>     ^^
 .....
  xor [si],ax
>          ^^

  регистры одиннаковы. :( статистически можно выявить их и декриптовать
  основываясь на этом. А это плохо. В противодействие этому между этими
  командами мы можем вставить такой код:

 mov dx,ax или xchg ax,dx или xchg dx,ax

  соответственно код поменяется:

mov ax,kod
...
mov dx,ax
...
xor [si],dx <- и тут уже dx вместо ax.

  если преобразований поставить несколько, например:

mov ax,kod
...
xchg dx,ax
...
mov bp,dx
...
xchg bp,di
...
xor [si],di

  то проследить будет уже не так легко как прежде.

 естественно - поскольку действующие регистры меняются динамически, то
 уже неиспользуемые регистры используются в полиморфном мусоре.

  например вот такой код:

mov dx,kod
...
mov ax,dx
...
mov dx,[что нибудь левое]
...
xchg bp,ax
...
add ax,cx  ; портим ax
...
xchg bx,bp
...
xor bp,ax  ; портим bp
...
...
xor [si],bx


p.S. Hу вот для начального ознакомления вроде бы все. Я не рассказал тебе
     еще построений левых условных переходов, левых процедур и такое прочее. 
     но надеюсь что пока тебе это не понадобится ;))
     в любом случае ты _сам_ можешь многому научиться
     поизучав SMEG,MtE и другие классические вещи.

 И еще запомни - не надо слепо копировать эти приемы, надо придумывать самому
 - так гораздо интереснее, полезнее и приятнее. :)


						   (c) by Reminder [DVC]
