Итак, вышел очередной апдейт оп каспера - коий умеет детектить два пермутирующих вируса.
Один вирус (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 ==