понедельник, 28 ноября 2011 г.

Ищем код Patch-Guard

Стало интересно, можно ли найти код Patch Guard используя лишь факт того, что он каким-то образом считает хеши секций.
В итоге родился скрипт для IDA, который ищет во всех ф-циях инструкции используемые для подсчета хешей, т.е. xor/rol и прочие.

Получился такой список:

RtlpValidatePeHeaderHash2 xor count = 2 <=== данная ф-ция не интересна
RtlpValidatePeHeaderHash2 shr count = 1
RtlpValidatePeHeaderHash2 rol count = 1

sub_565580 xor count = 2
sub_565580 shr count = 1
sub_565580 rol count = 2

sub_565730 xor count = 24
sub_565730 rdtsc count = 6
sub_565730 shl count = 6
sub_565730 shr count = 4
sub_565730 ror count = 6

sub_565950 xor count = 3
sub_565950 shr count = 4
sub_565950 rol count = 2

sub_565B10 xor count = 3
sub_565B10 shr count = 5
sub_565B10 rol count = 2

sub_566090 xor count = 6
sub_566090 rdtsc count = 1
sub_566090 shl count = 1
sub_566090 shr count = 1
sub_566090 ror count = 1

sub_566140 xor count = 3
sub_566140 shr count = 3
sub_566140 rol count = 2

sub_5667D0 xor count = 4
sub_5667D0 shr count = 5
sub_5667D0 rol count = 2

sub_566A50 xor count = 3
sub_566A50 shl count = 1
sub_566A50 shr count = 2
sub_566A50 rol count = 2

sub_566BC0 xor count = 3
sub_566BC0 shl count = 1
sub_566BC0 shr count = 3
sub_566BC0 rol count = 2

sub_566D30 xor count = 13
sub_566D30 rdtsc count = 3
sub_566D30 shl count = 5
sub_566D30 shr count = 1
sub_566D30 ror count = 3

FsRtlMdlReadCompleteDevEx xor count = 12
FsRtlMdlReadCompleteDevEx rdtsc count = 2
FsRtlMdlReadCompleteDevEx shl count = 2
FsRtlMdlReadCompleteDevEx shr count = 4
FsRtlMdlReadCompleteDevEx ror count = 2

ObLogSecurityDescriptor xor count = 2 <=== данная ф-ция не интересна
ObLogSecurityDescriptor shr count = 1
ObLogSecurityDescriptor rol count = 2

KiSystemStartup xor count = 1 <=== данная ф-ция не интересна, т.к. данные инструкции используются только для инициализации security_cookie
KiSystemStartup rdtsc count = 1
KiSystemStartup shl count = 1
KiSystemStartup shr count = 2
KiSystemStartup rol count = 1
KiSystemStartup ror count = 2

SEH хендлеры IDA не нашла, поэтому используя скрипт из предыдущего поста, добавив в него вывод BeginAddress/EndAddress в RUNTIME_FUNCTION.

После отработки скрипта, запустим скрипт нахождения xor/ror/rol инструкций, выкинув лишнее(ObLogSecurityDescriptor, KiSystemStartup) получим:

sub_410820 xor count = 1 <=== Pg Seh handler
sub_410820 rol count = 1
sub_410820 ror count = 1

sub_5010D0 xor count = 1 <=== Pg Seh handler
sub_5010D0 rol count = 1
sub_5010D0 ror count = 1

sub_502360 xor count = 1 <=== Pg Seh handler
sub_502360 rol count = 1
sub_502360 ror count = 1

sub_565580 xor count = 2
sub_565580 shr count = 1
sub_565580 rol count = 2

sub_565730 xor count = 24
sub_565730 rdtsc count = 6
sub_565730 shl count = 6
sub_565730 shr count = 4
sub_565730 ror count = 6

sub_565950 xor count = 3
sub_565950 shr count = 4
sub_565950 rol count = 2

sub_565B10 xor count = 3
sub_565B10 shr count = 5
sub_565B10 rol count = 2

sub_566090 xor count = 6
sub_566090 rdtsc count = 1
sub_566090 shl count = 1
sub_566090 shr count = 1
sub_566090 ror count = 1

sub_566140 xor count = 3
sub_566140 shr count = 3
sub_566140 rol count = 2

sub_5667D0 xor count = 4
sub_5667D0 shr count = 5
sub_5667D0 rol count = 2

sub_566A50 xor count = 3
sub_566A50 shl count = 1
sub_566A50 shr count = 2
sub_566A50 rol count = 2

sub_566BC0 xor count = 3
sub_566BC0 shl count = 1
sub_566BC0 shr count = 3
sub_566BC0 rol count = 2

sub_566D30 xor count = 13
sub_566D30 rdtsc count = 3
sub_566D30 shl count = 5
sub_566D30 shr count = 1
sub_566D30 ror count = 3

FsRtlMdlReadCompleteDevEx xor count = 12
FsRtlMdlReadCompleteDevEx rdtsc count = 2
FsRtlMdlReadCompleteDevEx shl count = 2
FsRtlMdlReadCompleteDevEx shr count = 4
FsRtlMdlReadCompleteDevEx ror count = 2

Видно, что операции для всех трех SEH обработчиков одинаковые - xor, rol, ror.
Осталось определить к какой ф-ции они относятся. Для этого смотрим лог:

RuntimeFunctionAddress = 5a5bc4, beginAddress = 4107f0, endAddress = 410811, UnwindData = 531044, ExceptionHandler = 429830
Scope Handler = 410820
Scope Handler = 410820

RuntimeFunctionAddress = 5c9a8c, beginAddress = 501090, endAddress = 5010bd, UnwindData = 53101c, ExceptionHandler = 429830
Scope Handler = 5010d0

RuntimeFunctionAddress = 5b0e20, beginAddress = 43f130, endAddress = 43f1b1, UnwindData = 5312b4, ExceptionHandler = 429830
Scope Handler = 502360

Получаем DPC ф-ции используемые патчгвардом:

.text:00000000004107F0 ExpTimeZoneDpcRoutine proc near
.text:0000000000501090 ExpTimeRefreshDpcRoutine proc near
.text:000000000043F130 ExpTimerDpcRoutine proc near

Далее по гиперссылкам восстанавливаем целостную картину, то есть сперва получаем цепочку ф-ций которая вызывает данную, и в конце концов приходим к родительской ф-ции.

Получим две основные цепочки, пример:

403AA2(KiFilterFiberContext) -> 403aa2 -> 8212f0 -> 804810 -> 565950

и

567450(FsRtlUninitializeSmallMcb) -> 566f00 -> 5667d0 -> 566260 -> 565ed0 -> 565b10 -> 565950 -> 565730

Очевидно, что одна из них - инициализация PG, а вторая - ф-ция проверки хешей.

Далее, построив дерево вызовов от этих 2х родительских ф-ций получим все ф-ции используемые патч гвардом.

Таким образом, статическим анализом будет найдено 95% кода патч гварда, на долю динамического анализа придется небольшая часть,
без которой полностью логику восстановить будет сложно.

p.s. Речь шла о PG первой версии.

четверг, 24 ноября 2011 г.

Определение SEH обработчиков в х64 коде

IDA плохо распознает код, если на него нет перекрестных ссылок. Наглядный пример - SEH обработчики для x64 кода.
Как известно, модель исключений в х64 изменилась, в ней используется data-driven подход, в котором роль метаданных играет содержимое секции PE файла. Данная секция содержит все необходимые для раскрутки данные, а именно, массив ф-ций RUNTIME_FUNCTION:

typedef struct _RUNTIME_FUNCTION
{
     ULONG BeginAddress;
     ULONG EndAddress;
     ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;

UnwindData представлена следующей структурой переменной длины:

typedef struct _UNWIND_INFO
{
    UCHAR Version : 3;
    UCHAR Flags : 5;
    UCHAR SizeOfProlog;
    UCHAR CountOfCodes;
    UCHAR FrameRegister : 4;
    UCHAR FrameOffset : 4;
    UNWIND_CODE UnwindCode[1];

//
// The unwind codes are followed by an optional DWORD aligned field that
// contains the exception handler address or a function table entry if
// chained unwind information is specified. If an exception handler address
// is specified, then it is followed by the language specified exception
// handler data.
//
//  union
// {
//      struct
//      {
//          ULONG ExceptionHandler;
//          ULONG ExceptionData[];
//      };
//
//      RUNTIME_FUNCTION FunctionEntry;
//  };
//

} UNWIND_INFO, *PUNWIND_INFO;

Соответственно, если установлен флаг UNW_FLAG_EHANDLER, то в данной структуре присутствует SEH обработчик, сразу за которым следует структура SCOPE_TABLE:

typedef struct _SCOPE_TABLE
{
     ULONG Count;
     struct
     {
         ULONG BeginAddress;
         ULONG EndAddress;
         ULONG HandlerAddress;
         ULONG JumpTarget;
     } ScopeRecord[1];
 } SCOPE_TABLE, *PSCOPE_TABLE;

Именно массив записей ScopeRecord и содержат SEH обработчики для ф-ции.

В ExceptionHandler структуры UNWIND_INFO чаще всего сидит _C_specific_handler, который извлекает все HandlerAddress из ScopeRecord,
проверяет принадлежность инструкции вызвавшей исключение на принадлежность к RUNTIME_FUNCTION (т.е. попадает ли она в диапазон BeginAddress - EndAddress), и если хендлер != 1 и попадает в диапазон - он вызывается.

Обладая данными знаниями несложно написать скрипт, который распознает все seh обработчики в файле.
Зачем это может понадобиться будет рассмотрено в следующей заметке.

До обработки скриптом:

PAGE:00000000007A1290 NtAllocateUuids endp
PAGE:00000000007A1290
PAGE:00000000007A1290 ; ---------------------------------------------------------------------------
PAGE:00000000007A1291                 db 0Fh dup(90h)
PAGE:00000000007A12A0                 db 8Bh, 0C0h
PAGE:00000000007A12A2                 dw 5553h, 8348h, 28ECh
PAGE:00000000007A12A8                 dq 25048B4865EA8B48h, 4845894800000188h, 983848458B48DB33h
PAGE:00000000007A12A8                 dq 8BC3950F00000153h, 5D28C48348C38BC3h, 909090909090C35Bh
PAGE:00000000007A12A8                 dq 9090909090909090h, 28EC83485553C08Bh, 25048B4865EA8B48h
PAGE:00000000007A12A8                 dq 4045894800000188h, 9938404D8B48DB33h, 8BC3950F00000153h
PAGE:00000000007A12A8                 dq 5D28C48348C38BC3h, 909090909090C35Bh, 9090909090909090h
PAGE:00000000007A12A8                 dq 4820EC834855C08Bh, 8825048B4865EA8Bh, 3320458948000001h
PAGE:00000000007A12A8                 dq 538138204D8B48C0h, 8348C0950F000001h, 90909090C35D20C4h
PAGE:00000000007A12A8                 dq 2 dup(9090909090909090h)
PAGE:00000000007A1360
PAGE:00000000007A1360 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A1360
PAGE:00000000007A1360
PAGE:00000000007A1360 ; int __fastcall XIPLocatePages(PVOID Object, __int64, __int64, __int64, __int64)

После обработки скриптом:

PAGE:00000000007A1290 NtAllocateUuids endp
PAGE:00000000007A1290
PAGE:00000000007A1290 ; ---------------------------------------------------------------------------
PAGE:00000000007A1291                 db 0Fh dup(90h)
PAGE:00000000007A12A0
PAGE:00000000007A12A0 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A12A0
PAGE:00000000007A12A0
PAGE:00000000007A12A0 sub_7A12A0      proc near
PAGE:00000000007A12A0                 mov     eax, eax
PAGE:00000000007A12A2                 push    rbx
PAGE:00000000007A12A3                 push    rbp
PAGE:00000000007A12A4                 sub     rsp, 28h
PAGE:00000000007A12A8                 mov     rbp, rdx
PAGE:00000000007A12AB                 mov     rax, gs:188h
PAGE:00000000007A12B4                 mov     [rbp+48h], rax
PAGE:00000000007A12B8                 xor     ebx, ebx
PAGE:00000000007A12BA                 mov     rax, [rbp+48h]
PAGE:00000000007A12BE                 cmp     [rax+153h], bl
PAGE:00000000007A12C4                 setnz   bl
PAGE:00000000007A12C7                 mov     eax, ebx
PAGE:00000000007A12C9                 mov     eax, ebx
PAGE:00000000007A12CB                 add     rsp, 28h
PAGE:00000000007A12CF                 pop     rbp
PAGE:00000000007A12D0                 pop     rbx
PAGE:00000000007A12D1                 retn
PAGE:00000000007A12D1 sub_7A12A0      endp
PAGE:00000000007A12D1
PAGE:00000000007A12D1 ; ---------------------------------------------------------------------------
PAGE:00000000007A12D2                 align 20h
PAGE:00000000007A12E0
PAGE:00000000007A12E0 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A12E0
PAGE:00000000007A12E0
PAGE:00000000007A12E0 sub_7A12E0      proc near
PAGE:00000000007A12E0                 mov     eax, eax
PAGE:00000000007A12E2                 push    rbx
PAGE:00000000007A12E3                 push    rbp
PAGE:00000000007A12E4                 sub     rsp, 28h
PAGE:00000000007A12E8                 mov     rbp, rdx
PAGE:00000000007A12EB                 mov     rax, gs:188h
PAGE:00000000007A12F4                 mov     [rbp+40h], rax
PAGE:00000000007A12F8                 xor     ebx, ebx
PAGE:00000000007A12FA                 mov     rcx, [rbp+40h]
PAGE:00000000007A12FE                 cmp     [rcx+153h], bl
PAGE:00000000007A1304                 setnz   bl
PAGE:00000000007A1307                 mov     eax, ebx
PAGE:00000000007A1309                 mov     eax, ebx
PAGE:00000000007A130B                 add     rsp, 28h
PAGE:00000000007A130F                 pop     rbp
PAGE:00000000007A1310                 pop     rbx
PAGE:00000000007A1311                 retn
PAGE:00000000007A1311 sub_7A12E0      endp
PAGE:00000000007A1311
PAGE:00000000007A1311 ; ---------------------------------------------------------------------------
PAGE:00000000007A1312                 align 20h
PAGE:00000000007A1320
PAGE:00000000007A1320 ; =============== S U B R O U T I N E =======================================
PAGE:00000000007A1320
PAGE:00000000007A1320
PAGE:00000000007A1320 sub_7A1320      proc near
PAGE:00000000007A1320                 mov     eax, eax
PAGE:00000000007A1322                 push    rbp
PAGE:00000000007A1323                 sub     rsp, 20h
PAGE:00000000007A1327                 mov     rbp, rdx
PAGE:00000000007A132A                 mov     rax, gs:188h
PAGE:00000000007A1333                 mov     [rbp+20h], rax
PAGE:00000000007A1337                 xor     eax, eax
PAGE:00000000007A1339                 mov     rcx, [rbp+20h]
PAGE:00000000007A133D                 cmp     [rcx+153h], al
PAGE:00000000007A1343                 setnz   al
PAGE:00000000007A1346                 add     rsp, 20h
PAGE:00000000007A134A                 pop     rbp
PAGE:00000000007A134B                 retn
PAGE:00000000007A134B sub_7A1320      endp
PAGE:00000000007A134B
PAGE:00000000007A134B ; ---------------------------------------------------------------------------
PAGE:00000000007A134C                 db  90h

Собственно, сам скрипт:

import idaapi
import idautils
import idc

class ExceptionDetector():
    def __init__( self ):
        self.imageBase = self.GetModuleBase()

    def GetModuleBase( self ):
        defaultHeaderSize = 0x1000
        for segEa in Segments():
            imageBase = segEa - defaultHeaderSize
            return imageBase

    def GetExceptionSectionAddress( self ):
        for ea in Segments():
            if SegName( ea ) == ".pdata":
                return ea
        return 0

    def GetHandlerAddressFromUnwindData( self, unwindDataAddress ):
        versionFlags = Byte( unwindDataAddress )
        countOfCodes = Byte( unwindDataAddress + 2 )
        # only even count allowed
        if countOfCodes % 2:
            countOfCodes = countOfCodes + 1

        # have flag UNW_FLAG_EHANDLER?
        if versionFlags & 8:
            handlerAddress = unwindDataAddress + 4 + countOfCodes * 2
            return handlerAddress

        return 0

    def GetScopeHandlers( self, scopeTableAddress, unwindDataAddress ):
        scopeHandlers = []
        sizeOfScopeRecord = 16
        scopeTableEntriesCount = Dword( scopeTableAddress )

        # FIXME: how many scope entries is possible?
        if scopeTableEntriesCount > 10:
            return scopeHandlers

        # read scope table and fill list with HandlerAddress values
        for i in range( 0, scopeTableEntriesCount ):
            scopeRecord = struct.unpack( "4I", idaapi.get_many_bytes( scopeTableAddress, sizeOfScopeRecord ) )
            scopeHandler = scopeRecord[3]

            if scopeHandler != 1:
                scopeHandlers.append( self.imageBase + scopeHandler )           

            scopeTableAddress = scopeTableAddress + sizeOfScopeRecord

        return scopeHandlers

    # WARN: FindFuncEnd failed at times, so desirable run script twice for better results ( on second time IDA analyser works much better )
    def MakeFunctionForHandler( self, scopeHandler ):
        MakeUnkn( scopeHandler, DOUNK_SIMPLE )
        MakeCode( scopeHandler )
        functionStart = scopeHandler
        functionEnd = FindFuncEnd( functionStart )

        if functionEnd == BADADDR:
            functionEnd = functionStart + 1

        MakeFunction( functionStart, functionEnd )

    def CreateFunctionsForHandlers( self ):
        sizeOfRuntimeFunction = 12
        ea = self.GetExceptionSectionAddress()
        if ea == 0:
            print "CreateFunctionsForHandlers: can't find exception section"
            return
        else:
            print "-------------------------------------------"
            print "exception section finded at address %x" % ea
            print "-------------------------------------------"

        while True:
            runtimeFunction = struct.unpack( "3I", idaapi.get_many_bytes( ea, sizeOfRuntimeFunction ) )
            unwindDataRel = runtimeFunction[2]

            # end of exception section - zero padding bytes
            if unwindDataRel == 0:
                break

            handlerAddress = self.GetHandlerAddressFromUnwindData( self.imageBase + unwindDataRel )

            if handlerAddress:
                handler = Dword( handlerAddress )
                scopeTableAddress = handlerAddress + 4
                scopeHandlers = self.GetScopeHandlers( scopeTableAddress, self.imageBase + unwindDataRel )
                scopeHandlersLength = len( scopeHandlers )

                if scopeHandlersLength > 0:
                    print "RuntimeFunction address = %x UnwindData = %x, ExceptionHandler = %x" % ( ea, self.imageBase + unwindDataRel, self.imageBase + handler )

                    for i in range( 0, scopeHandlersLength ):
                        self.MakeFunctionForHandler( scopeHandlers[i] )
                        print "Scope Handler = %x" % ( scopeHandlers[i] )

                    print "-----------------------------------------"

            ea = ea + sizeOfRuntimeFunction

#      
# entry point
#

print "Script Start...\n"

detector = ExceptionDetector()
detector.CreateFunctionsForHandlers()

print "\nScript End..."

p.s. Из-за особенности анализатора IDA, скрипт лучше всего запускать дважды.
После первого запуска IDA похоже обновляет свои внутренние данные и на второй запуск определяется практически все хендлеры.

среда, 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