ДЕТЕКТИРУЕМ ПЕРМУТИРУЮЩИЙ ВИРУС

by Eugene KaZPERMsky

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

Один вирус (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

==