Есть одна замечательная програмка, широко известная в узких кругах, под названием 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
Баг сидит в драйвере 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
Комментариев нет:
Отправить комментарий