Итак, вышел очередной апдейт оп каспера - коий умеет детектить два пермутирующих вируса.
Один вирус (win32.zperm.a == rpme.z0mbie6a) написан с использованием движка RPME, который позволяет раскидать команды вируса по некоторому буферу и связать их JMP-ами, ну и плюс к этому некоторые другие фичи.
Второй вирус (win32.zperm.b == z0mbie7) это почти то же самое, но вместо RPME там Ply-подобная мутация, то есть каждая команда дополнена до 16 байт NOPами, "плавает" в них, и кроме этого каждый 16-байтный блок может перемещаться в случайную область буфера, и все, опять же, связывается JMP-ами.
Следует сказать, что каспер весьма порадовал тем, что продетектил оба вируса одним и тем же кодом. А особенно мне понравилось название вирусов.
Детектирующий же алгоритм выглядит весьма недальновидно, а уж о каких-нибудь там гениальных идеях и говорить не приходится.
В общем, детектирование проводится так:
(*) Брать команды по одной, удалять среди них JMPы и NOPы, полиморфные команды приводить к одному виду, переменные аргументы заменять на 0, копировать все это в буфер.
(**) Просчитать по буферу контрольную сумму
Как видим, отличие от детектирования обычных вирусов только в (*). Причем, выполняется (*) только при некоторых условиях, например если хотя бы две команды файла совпадают с вирусными - это для того, чтобы не тормозить на всех файлах подряд. Выход из (*) осуществляется при фиксированных количестве или суммарной длине команд, или при нахождении некоторого характерного опкода, в нашем случае CALL EAX, либо же при нахождении опкода отсутствующего в детектируемой области вируса.
Короче говоря, алгоритм писался на конкретный вирус явно без учета всех возможностей пермутирующего движка. Например "обменяные" местами ветви алгоритма (при замене jz <--> jnz) и перемешанные инструкции не обрабатываются никак. Наверное это потому, что такие конструкции не встречаются в самом начале вируса, а именно по его началу и проводится проверка контрольной суммы. Это, в свою очередь, является продолжением старой традиции антивирусников детектировать вирус по длине контрольной суммы не составляющей и десяти процентов от общей длины вируса.
Ниже приведен детектирующий антивирусный код.
=====[begin ZPERM.ASM]======================================================
; source: zperm.c (up000630.avc/_o_a0001.o32)
; 4550 cs1=(0000,04,50155015) cs2=(1400,32,120E7C33) name=Win32.ZPerm.a
; 4B8B cs1=(06BE,10,FC08C1F7) cs2=(06BE,C0,29104DF1) name=Win32.ZPerm.a#
; BD60 cs1=(1400,07,24F92413) cs2=(1400,2A,34D5DC13) name=Win32.ZPerm.b
; FBE8 cs1=(0400,07,D8E48C4A) cs2=(0600,C0,4D20871B) name=Win32.ZPerm.b#
_decode proc near
virus_variant = byte ptr -7 ; 1=a(z6a) 0=b(z7)
opcode = word ptr -6
ibuf_offset = dword ptr -4
ibuf = edi ; ibuf: входной буфер
i = ebx ;
o = esi ; выходной буфер
opcode_count = ebp ; число опкодов в цикле (*)
sub esp, 8
; проверить первую команду файла
cmp opcode_1, 60h ; 60=pusha
je __found1
cmp opcode_1, 0E9h ; E9=jmp
je __found1
cmp opcode_1, 90h ; 90=nop
jne __exit
; проверить вторую команду файла
__found1:
cmp opcode_2, 60h
je __found2
cmp opcode_2, 0E9h
je __found2
cmp opcode_2, 90h
jne __exit
__found2:
xor opcode_count, opcode_count
xor o, o
xor i, i
mov ibuf, offset opcode_2
mov virus_variant, 1 ; 1=a(z6a) 0=b(z7)
mov eax, _EP_Next
mov ibuf_offset, eax
; цикл (*)
__cycle:
inc opcode_count
cmp o, 80h
ja __virus_found
__check_max:
cmp i, 100h
ja __exit
cmp opcode_count, 100h
ja __exit
mov ax, ibuf[i]
mov opcode, ax
xor ecx, ecx
mov cl, al
sub ecx, 0Fh
cmp ecx, 0F0h
ja __exit
xor edx, edx
mov dl, index_table[ecx]
jmp jmp_table[edx*4]
i11__NOP:
mov virus_variant, 0 ; 1=a(z6a) 0=b(z7)
inc i
cmp o, 80h
jbe __check_max
__virus_found:
; BD == mov ebp, <virus-entry-va>
cmp byte ptr obuf+1, 0BDh
jne __exit3
mov dword ptr obuf+2, 0 ; zerofill variable dword
__exit3:
mov ax, 2 ; 2=found???
add esp, 8
retn
; --------------------------------------------------------------------------
; 0F == Jxx (near)
i0__opcode_0F:
mov obuf[o], al
inc o
inc o
mov al, opcode.byte ptr 1
and al, 0F0h
mov (obuf-1)[o], al
xor eax, eax
mov al, opcode.byte ptr 1
cmp eax, 84h ; jz
je __jz_skip
cmp eax, 85h ; jnz
je __jnz_process
__exit:
xor ax, ax ; 0=not found
add esp, 8
retn
; --------------------------------------------------------------------------
__jz_skip:
add i, 6
jmp __cycle
; --------------------------------------------------------------------------
__jnz_process:
mov eax, [ibuf+i+2]
add eax, i
add eax, 6
jmp loc_0_1CF
; --------------------------------------------------------------------------
; 29,2B=sub 31,33=xor
i1234_xorsub:
cmp opcode.byte ptr 1, 0C0h
jne __exit
add o, 2
add i, 2
mov word ptr (obuf-2)[o], 0C031h
jmp __cycle
; --------------------------------------------------------------------------
i7__FS:
xor eax, eax
mov al, opcode.byte ptr 1
cmp eax, 67h
je __copy_6
cmp eax, 89h
je __copy_3
cmp eax, 0FFh
je __copy_3
__exit2:
xor ax, ax ; 0=not found
add esp, 8
retn
; --------------------------------------------------------------------------
__copy_6:
mov al, ibuf[i]
inc i
mov obuf[o], al
inc o
i8121314__copy_5:
mov al, ibuf[i]
inc i
mov obuf[o], al
inc o
i10__copy_4:
mov al, ibuf[i]
inc i
mov obuf[o], al
inc o
__copy_3:
mov al, ibuf[i]
inc i
mov obuf[o], al
inc o
i9__copy_2:
mov al, ibuf[i]
inc i
mov obuf[o], al
inc o
i56__copy_1:
mov al, ibuf[i]
inc i
mov obuf[o], al
inc o
jmp __cycle
; --------------------------------------------------------------------------
i15__E8call:
cmp virus_variant, 0 ; 1=a(z6a) 0=b(z7)
jne loc_0_1C1
mov obuf[o], al
inc o
mov dword ptr obuf[o], 0
add o, 4
add i, 5
jmp __cycle
; --------------------------------------------------------------------------
loc_0_1C1:
mov virus_variant, 0 ; 1=a(z6a) 0=b(z7)
i16__E9jmp:
mov eax, [ibuf+i+1]
add eax, i
add eax, 5
loc_0_1CF:
; подчитываем требуемую область файла
add ibuf_offset, eax
push 100h
mov eax, ibuf_offset
push offset obuf+80h
push eax
mov ibuf, offset obuf+80h
xor i, i
call _Seek_Read
add esp, 0Ch
jmp __cycle
; --------------------------------------------------------------------------
i17__op_FF:
cmp opcode.byte ptr 1, 0D0h ; FF D0 == call eax
jne __exit
mov ax, ibuf[i] ; выход из цикла (*) по опкоду FF D0
mov obuf[o], ax
jmp __virus_found
_decode endp
; --------------------------------------------------------------------------
jmp_table dd offset i0__opcode_0F ; 0
dd offset i1234_xorsub ; 1
dd offset i1234_xorsub ; 2
dd offset i1234_xorsub ; 3
dd offset i1234_xorsub ; 4
dd offset i56__copy_1 ; 5
dd offset i56__copy_1 ; 6
dd offset i7__FS ; 7
dd offset i8121314__copy_5 ; 8
dd offset i9__copy_2 ; 9
dd offset i10__copy_4 ; 10
dd offset i11__NOP ; 11
dd offset i8121314__copy_5 ; 12
dd offset i8121314__copy_5 ; 13
dd offset i8121314__copy_5 ; 14
dd offset i15__E8call ; 15
dd offset i16__E9jmp ; 16
dd offset i17__op_FF ; 17
dd offset __exit ; 18=xx
; 18 = exit
xx = 18
index_table label byte
db 0 ; 0F
db xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx ; 1x
db xx, xx, xx, xx, xx, xx, xx, xx, xx, 1, xx, 2, xx, xx, xx, xx ; 2x
db xx, 3, xx, 4, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx ; 3x
db xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx ; 4x
db xx, xx, xx, xx, xx, xx, xx, xx, 5, xx, xx, xx, xx, xx, xx, xx ; 5x
db 6, 6, xx, xx, 7, xx, xx, xx, 8, xx, 9, xx, xx, xx, xx, xx ; 6x
db xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx ; 7x
db xx, xx, xx, xx, xx, xx, xx, xx, xx, 10, xx, xx, xx, xx, xx, xx ; 8x
db 11, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx ; 9x
db xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx ; Ax
db xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 12, xx, 13, xx, 14 ; Bx
db xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx ; Cx
db xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx ; Dx
db xx, xx, xx, xx, xx, xx, xx, xx, 15, 16, xx, xx, xx, xx, xx, xx ; Ex
db xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 17 ; Fx
==