Как гласит документация, функцию
ObRegisterCallbacks
можно вызывать только для фильтрации создания описателей процесса и нити (поле
ObjectType в структуре OB_OPERATION_REGISTRATION):
A pointer to the object type (process or thread) that triggers the callback
routine. Specify either PsProcessType for process handle operations, or
PsThreadType for thread handle operations.
Если не верить написанному и запустить тест, который попытается
зарегистрировать фильтр, например, для nt!LpcPortObjectType, то путем недолгой
трассировки мы увидим следующий код:
test byte ptr [rcx+42h],40h
je nt!ObRegisterCallbacks+0x1a8 (fffff800`01ad2ce8)
; nt!ObRegisterCallbacks+0x1a8 (fffff800`01ad2ce8):
mov esi,0C000000Dh
А при выходе функция вернет STATUS_INVALID_PARAMETER(0xC000000D). По листингу
nt!ObRegisterCallbacks нетрудно догадаться, что на момент проверки в rcx
хранится указатель на целевой тип объекта. Структура типа объекта есть в
символах:
kd> dt nt!_OBJECT_TYPE -b
+0x000 TypeList : _LIST_ENTRY
+0x000 Flink : Ptr64
+0x008 Blink : Ptr64
+0x010 Name : _UNICODE_STRING
+0x000 Length : Uint2B
+0x002 MaximumLength : Uint2B
+0x008 Buffer : Ptr64
+0x020 DefaultObject : Ptr64
+0x028 Index : UChar
+0x02c TotalNumberOfObjects : Uint4B
+0x030 TotalNumberOfHandles : Uint4B
+0x034 HighWaterNumberOfObjects : Uint4B
+0x038 HighWaterNumberOfHandles : Uint4B
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x000 Length : Uint2B
+0x002 ObjectTypeFlags : UChar
+0x002 CaseInsensitive : Pos 0, 1 Bit
+0x002 UnnamedObjectsOnly : Pos 1, 1 Bit
+0x002 UseDefaultObject : Pos 2, 1 Bit
+0x002 SecurityRequired : Pos 3, 1 Bit
+0x002 MaintainHandleCount : Pos 4, 1 Bit
+0x002 MaintainTypeList : Pos 5, 1 Bit
+0x002 SupportsObjectCallbacks : Pos 6, 1 Bit
+0x004 ObjectTypeCode : Uint4B
...
Как видно из результата печати структуры, по смещению 0x42 расположены флаги. А
маска 0x40 (6-ой бит) соответствует флагу с очень говорящим названием
SupportsObjectCallbacks. Ради продолжения эксперимента можно написать
простенький скрипт печати флагов для всех типов (py-скрипт с использованием
PyKd версии 0.1.x):
from pykd import *
import re
nt = loadModule( "nt" )
cmdRes = dbgCommand( "!object \\ObjectTypes" )
for strRes in cmdRes.split( "\n" ):
strRes = strRes.strip("\r\n")
mObj = re.search( "[ ]+[0-9]*[0-9]*[ ]+([0-9|a-f]+)", strRes )
if mObj:
typeObj = nt.typedVar( "_OBJECT_TYPE", int(mObj.group(1), 16 ))
dprint( "%024s" % loadUnicodeString(typeObj.Name.getAddress()) )
if typeObj.TypeInfo.CaseInsensitive:
dprint( " [CaseInsensitive]")
if typeObj.TypeInfo.UnnamedObjectsOnly:
dprint( " [UnnamedObjectsOnly]")
if typeObj.TypeInfo.UseDefaultObject:
dprint( " [UseDefaultObject]")
if typeObj.TypeInfo.SecurityRequired:
dprint( " [SecurityRequired]")
if typeObj.TypeInfo.MaintainHandleCount:
dprint( " [MaintainHandleCount]")
if typeObj.TypeInfo.MaintainTypeList:
dprint( " [MaintainTypeList]")
if typeObj.TypeInfo.SupportsObjectCallbacks:
dprint( " <col fg=\"changed\">[SupportsObjectCallbacks]</col>", True)
if hasattr(typeObj.TypeInfo, "CacheAligned"):
if typeObj.TypeInfo.CacheAligned:
dprint( " [CacheAligned]")
dprintln( "")
Результат работы скрипта:
TmTm [UnnamedObjectsOnly] [SecurityRequired]
Desktop [SecurityRequired] [MaintainHandleCount]
Process [UnnamedObjectsOnly] [SecurityRequired] [SupportsObjectCallbacks]
DebugObject [SecurityRequired]
TpWorkerFactory
Adapter [UseDefaultObject]
Token [UseDefaultObject] [SecurityRequired]
EventPair [UseDefaultObject]
PcwObject [UnnamedObjectsOnly] [UseDefaultObject]
WmiGuid [SecurityRequired]
EtwRegistration [SecurityRequired] [MaintainHandleCount]
Session [UseDefaultObject] [SecurityRequired]
Timer
Mutant
IoCompletion [CaseInsensitive] [UseDefaultObject]
WindowStation [SecurityRequired] [MaintainHandleCount]
Profile
File [CaseInsensitive] [MaintainHandleCount]
Semaphore
EtwConsumer [SecurityRequired] [MaintainHandleCount]
TmTx [SecurityRequired]
SymbolicLink [CaseInsensitive] [UseDefaultObject]
FilterConnectionPort [SecurityRequired]
Key [UseDefaultObject] [SecurityRequired]
KeyedEvent [UseDefaultObject]
Callback
UserApcReserve [UnnamedObjectsOnly]
Job [SecurityRequired]
Controller [UseDefaultObject]
IoCompletionReserve [UnnamedObjectsOnly]
Device [CaseInsensitive] [UseDefaultObject]
Directory [CaseInsensitive] [UseDefaultObject] [SecurityRequired]
Section [UseDefaultObject]
TmEn [SecurityRequired]
Thread [UnnamedObjectsOnly] [SecurityRequired] [SupportsObjectCallbacks]
Type [UseDefaultObject] [MaintainTypeList]
FilterCommunicationPort
PowerRequest [MaintainHandleCount]
TmRm [SecurityRequired] [MaintainHandleCount]
Event
ALPC Port [MaintainHandleCount]
Driver [CaseInsensitive] [UseDefaultObject]
Результат полностью соответствует документации: флаг SupportsObjectCallbacks
установлен только для объектов типов Process и Thread. Но можно
продолжить экспериментировать: напишем небольшой тестовый драйвер, который
будет следить за описателями ALPC-портов, собственноручно взводя флаг
SupportsObjectCallbacks в nt!LpcPortObjectType. Смещение до этого флага
одинаково на всех линейке ОС, начиная с Vista: 0x2a для i386 и 0x42 для AMD64.
Тест имеет больше академический интерес, так как за такие манипуляции бьет по
рукам
KPP aka PatchGuard.
Далеко не секрет, что все Win32-процессы при инициализации устанавливают
LPC-соединение с csrss.exe. Полезной нагрузкой драйвера-теста будет запрет
установки такого соединения для процессов calc.exe. Стек вызовов должен быть
примерно такой:
fffff880`0baf14c0 fffff800`01a6facf nt!ObpCallPreOperationCallbacks+0x196
fffff880`0baf1540 fffff800`019f1bbd nt!ObpPreInterceptHandleCreate+0xaf
fffff880`0baf15c0 fffff800`019bb93e nt! ?? ::NNGAKEGL::`string'+0x30c3f
fffff880`0baf16d0 fffff800`019adaa3 nt!ObInsertObjectEx+0xde
fffff880`0baf1920 fffff800`0197d812 nt!AlpcpCreateClientPort+0x2e3
fffff880`0baf19d0 fffff800`0197f0e1 nt!NtSecureConnectPort+0x2ec
fffff880`0baf1b50 fffff800`016d0f93 nt!NtConnectPort+0x41
fffff880`0baf1bb0 00000000`771e1c3a nt!KiSystemServiceCopyEnd+0x13
00000000`0015e8f8 00000000`771d462e ntdll!ZwConnectPort+0xa
00000000`0015e900 00000000`771d43b5 ntdll!CsrpConnectToServer+0x25e
00000000`0015eac0 000007fe`fd6b041a ntdll!CsrClientConnectToServer+0x230
00000000`0015ecd0 00000000`771cb0d8 KernelBase!KernelBaseDllInitialize+0x148
00000000`0015ef60 00000000`771d42ed ntdll!LdrpRunInitializeRoutines+0x1fe
00000000`0015f130 00000000`771d1dc8 ntdll!LdrGetProcedureAddressEx+0x2aa
00000000`0015f2a0 00000000`771d1a17 ntdll!LdrpInitializeProcess+0x1a0c
00000000`0015f790 00000000`771bc32e ntdll! ?? ::FNODOBFM::`string'+0x29220
00000000`0015f800 00000000`00000000 ntdll!LdrInitializeThunk+0xe
Запрет заключается в том, что для создаваемого описателя сбрасываем маску
доступа в 0. Процесс не сможет выполнить инициализацию, не имея связи с
csrss.exe. В результате вместо запущенного калькулятора на экран выводится
диалог:
[calc.exe - Application Error]
The application was unable to start correctly (0xc0000142).
Click OK to close the application.
Так что тест свою задачу выполнил. Исходный код теста приводить не буду, так
как он тривиален. А все шаги достаточно подробно описаны.
ΞρεΤΙκ