среда, 9 ноября 2011 г.

Bug в RKU

Есть одна замечательная програмка, широко известная в узких кругах, под названием RootKit Unhooker, которая довольно стабильна (у меня за все время использования падала всего пару раз). Однако идеальных программ не бывает.

Баг сидит в драйвере RKU и заключается в некорректном поиске неэскпортируемой переменной PspCidTable.

RKU ищет её сканируя байты от ф-ции PsLookupProcessByProcessId, причем делается это даже без дизассемблера длин.

PsLookupProcessByProcessId:

805d3152 8bff            mov     edi,edi
805d3154 55              push    ebp
805d3155 8bec            mov     ebp,esp
805d3157 53              push    ebx
805d3158 56              push    esi
805d3159 64a124010000    mov     eax,dword ptr fs:[00000124h]
805d315f ff7508          push    dword ptr [ebp+8]
805d3162 8bf0            mov     esi,eax
805d3164 ff8ed4000000    dec     dword ptr [esi+0D4h]
805d316a ff35c0395680    push    dword ptr [nt!PspCidTable (805639c0)]  <===== нужная переменная, FF35 - нужные байты
805d3170 e861b60300      call    nt!ExMapHandleToPointer (8060e7d6)  <===== дополнительно проверяется байт E8

Собственно код RKU:

.text:00012BF0                 mov     esi, ds:PsLookupProcessByProcessId
...
loc_12C44:                    

.text:00012C44                 cmp     byte ptr [esi+eax], 0FFh
.text:00012C48                 jnz     short loc_12C58
.text:00012C4A                 cmp     byte ptr [esi+eax+1], 35h
.text:00012C4F                 jnz     short loc_12C58
.text:00012C51                 cmp     byte ptr [esi+eax+6], 0E8h
.text:00012C56                 jz      short loc_12C62

loc_12C58:

.text:00012C58                 inc     eax
.text:00012C59                 cmp     eax, 100h
.text:00012C5E                 jl      short loc_12C44
.text:00012C60                 jmp     short loc_12C6B

loc_12C62:                            

.text:00012C62                 mov     [esp+4Ch+var_39], 1
.text:00012C67                 lea     esi, [esi+eax+2] ; esi = указателю на PspCidTable

...

А вот сюда прилетаем независимо от того, нашлись ли искомые байты или нет:

.text:00013031                 cmp     [edi], bx
.text:00013034                 mov     eax, [esi]       ; eax = адресу PspCidTable
.text:00013036                 mov     eax, [eax]       ; достается первый PHANDLE_TABLE
.text:00013038                 mov     dword_1571C, eax ; сохраняется в глобальной переменной

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

При стандартном прологе, новая ф-ция будет выглядеть так:

8bff            mov     edi,edi ; место для хотпатча
55              push    ebp
8bec            mov     ebp,esp

И тогда, вот здесь все эпично рухнет в синий экран:

.text:00013031                 cmp     [edi], bx
.text:00013034                 mov     eax, [esi]       ; esi не сместится на офсет указывающий на PspCidTable, т.к. поиск ничего не найдет и eax будет равен 8b55ff8b, то есть начальным байтам перехваченной ф-ции
.text:00013036                 mov     eax, [eax]       ; естественно адрес 8b55ff8b будет не валиден и все упадет в бсод

kd> !pte 8b55ff8b
                    VA 8b55ff8b
PDE at C06022D0            PTE at C045AAF8
contains 0000000006853963  contains 0000000000000000
pfn 6853      -G-DA--KWEV   not valid

Сценарий выглядит несколько искусственным, однако его реальность можно проверить на коммерческом продукте - китайском антивирусе Jiangmin.

Он как раз патчит IAT загружаемых драйверов, выглядит это так:

kd> dds f7939f98 f7939f98+0x1000
f7939f98  8052576c nt!ObfDereferenceObject
f7939f9c  805c9c46 nt!NtOpenProcess
f7939fa0  8053f960 nt!KeInitializeSpinLock
f7939fa4  804f0218 nt!IoThreadToProcess
f7939fa8  8054a968 nt!ExAllocatePoolWithTag
f7939fac  804f1aa4 nt!IoVolumeDeviceToDosName
f7939fb0  f7491a46                                     <== hooked ( PsLookupProcesByProcessId )
f7939fb4  8052d764 nt!RtlInitUnicodeString
f7939fb8  804f1614 nt!IoDeleteDevice
f7939fbc  804f9f38 nt!KeSetEvent
f7939fc0  f74919ea                                    <== hooked ( MmGetSystemRoutineAddress )
f7939fc4  80575736 nt!IoCreateFile
f7939fc8  804f9eac nt!KeInitializeEvent
f7939fcc  804ffe24 nt!ZwQuerySystemInformation
f7939fd0  804ef2be nt!IoFreeMdl
f7939fd4  804ff974 nt!ZwOpenDirectoryObject
f7939fd8  80506eb8 nt!MmGetVirtualForPhysical
f7939fdc  804f8300 nt!KeDetachProcess
f7939fe0  804fa56e nt!KeDelayExecutionThread
f7939fe4  805c242c nt!ObQueryNameString
f7939fe8  80506e0a nt!MmGetPhysicalAddress
f7939fec  80559d58 nt!IoFileObjectType
f7939ff0  80559d60 nt!IoDriverObjectType
f7939ff4  804ff384 nt!ZwCreateFile
f7939ff8  805cf92a nt!PsCreateSystemThread
f7939ffc  804f0f20 nt!IoBuildAsynchronousFsdRequest
f793a000  80535fec nt!ExAllocatePool
f793a004  80543f24 nt!__KeGetCurrentThread
f793a008  f7491a86                                    <== hooked ( PsTerminateSystemThread )
f793a00c  8054a950 nt!ExFreePool
f793a010  804ef3c8 nt!IoGetCurrentProcess
f793a014  804ff294 nt!ZwClose
f793a018  804eef48 nt!IofCompleteRequest
f793a01c  804fabaa nt!KeWaitForSingleObject
f793a020  80512a96 nt!MmIsAddressValid
f793a024  804ef1b2 nt!IoFreeIrp
f793a028  805628b4 nt!PsInitialSystemProcess
f793a02c  80508998 nt!MmProbeAndLockPages
f793a030  804f8826 nt!KeAttachProcess
f793a034  804ff578 nt!ZwDeleteFile
f793a038  805449a0 nt!KiDispatchInterrupt
f793a03c  805628bc nt!PsThreadType
f793a040  804eedb2 nt!IoAllocateIrp
f793a044  804ffa28 nt!ZwOpenProcess
f793a048  805085ec nt!MmUnlockPages
f793a04c  8052a02e nt!PsGetCurrentThreadId
f793a050  8059ffda nt!KeAddSystemServiceTable
f793a054  804fcf5e nt!KeSetSystemAffinityThread
f793a058  80574702 nt!IoCreateDevice
f793a05c  80559d64 nt!IoDeviceObjectType
f793a060  805004b4 nt!ZwTerminateProcess
f793a064  f7491b7a                                    <== hooked ( ObOpenObjectByPointer )
f793a068  8052a85e nt!DbgPrint
f793a06c  80560e80 nt!MmSectionObjectType
f793a070  f7491bb2                                    <== hooked ( PsLookupThreadByThreadId )
f793a074  805bc890 nt!NtDuplicateObject
f793a078  804f0c6e nt!IoAllocateMdl
f793a07c  804ff9ec nt!ZwOpenKey
f793a080  804fb412 nt!KeSetTargetProcessorDpc
f793a084  8054a2e0 nt!ExFreePoolWithTag
f793a088  805ba2ae nt!ObOpenObjectByName
f793a08c  804ff910 nt!ZwMapViewOfSection
f793a090  805b17c0 nt!MmUnmapViewOfSection
f793a094  804fb3cc nt!KeInitializeDpc
f793a098  804fb4a4 nt!KeInsertQueueDpc
f793a09c  80554a60 nt!KeNumberProcessors
f793a0a0  80500608 nt!ZwWriteFile
f793a0a4  8052a01c nt!PsGetCurrentProcessId
f793a0a8  805b9e5a nt!ObReferenceObjectByHandle
f793a0ac  8054c0e8 nt!NtBuildNumber
f793a0b0  805628b8 nt!PsProcessType
f793a0b4  804f9c1c nt!KeBugCheckEx
f793a0b8  80561118 nt!MmSystemRangeStart
f793a0bc  805300e6 nt!RtlUnwind

Правильно было бы немедленно завершить RKU или попробовать другой вариант поиска данной неэскпортируемой переменной, если требуемая сигнатура для PspCidTable не найдена.

p.s. версия драйвера RKU 3.8.0.6001

Комментариев нет:

Отправить комментарий