《Undocumented Windows 2000 Secrets》翻譯 --- 第五章(1)
第五章 監控Native API調用
翻譯:Kendiv( [email protected] )
更新:Tuesday, February 22, 2005
聲明:轉載請注明出處,并保證文章的完整性,本人保留譯文的所有權利。
攔截系統調用在任何時候都是程序員們的最愛。這種大眾化愛好的動機也是多種多樣的:代碼性能測試(Code Profiling)和優化,逆向工程,用戶活動記錄等等。所有這些都有一個共同的目的:將控制傳遞給一塊特殊的代碼,這樣無論一個應用程序何時調用系統服務,都可以發現哪個服務被調用了,接收了什么參數,返回的結果是什么以及執行它花費了多少時間。根據最初由Mark Russinovich和Bryce Cogswell提出的技巧,本章將介紹一個可以hook任意Native API函數的通用框架。這里使用的方法完全是數據驅動(data-driven)的,因此,它可以很容易被擴展,并能適應其他Windows NT/2000版本。所有進程的API調用產生的數據都被寫入一個環狀緩沖區中,客戶端程序可以通過設備I/O控制來讀取該緩沖區。采用的數據協議(protocol data)的格式是以行為導向的ANSI文本流,它符合嚴格的格式化規則,應用程序可以很容易的再次處理它們(postprocessing)。為了示范此種客戶端程序的基本框架,本章還提供了一個示例性的數據協議察看器,該程序運行于控制臺窗口中。
譯注:
profiling 一般是指對程序做性能方面的測試, 主要是速度上的。 在翻譯時,可譯為:剖析,最好是根據上文環境進行翻譯。
修改服務描述符表
對比“原始”的操作系統,如Dos或Windows 3.x,它們對程序員在API中加入hook的限制很少,而Win32系統,如Windows 2000/NT和Windows 9.x則很難駕馭,因為它們使用了巧妙的保護機制把不相關的代碼分離出來。在Win32 API上設置一個系統范圍的hook絕不是一個小任務。幸運的是,我們有像Matt PIEtrek和Jeffery Richter這樣的Win32向導,他們做了大量的工作來向我們展示如何完成這一任務,盡管事實上,并沒有簡單和優雅的解決方案。在1997年,Russinovich和Cogswell介紹了一種可在Windows NT上實現系統范圍hook的完全不同的方法,可在更低一層上攔截系統調用(Russinovich和Cogswell 1997)。他們提議向Native API Dispatcher中注入日志機制,這僅比用戶模式和內核模式之間的邊界低一些,在這個位置上Windows NT暴露出一個“瓶頸”:所有用戶模式的線程必須通過此處,才能使用操作系統內核提供的服務。
服務和參數表
就像在第二章討論過的,發生在用戶模式下的Native API調用必須通過INT 2eh接口,該接口提供一個i386的中斷門來改變特權級別。你可能還記得所有INT 2eh調用都是由內部函數KiSystemService()在內核模式下處理的,該函數使用系統服務描述符表(SDT)來查找Native API處理例程的入口地址。圖5-1給出了這種分派機制的基本組件之間的相互關系。列表5-1再次給出了SERVICE_DESCRIPTOR_TABLE結構及其子結構的正式定義,在第二章中,已經給出過它們的定義(列表2-1)。
調用KiSystemService()時,需提供兩個參數,由INT 2eh的調用者通過EAX和EDX寄存器傳入。EAX包含從0開始的索引,該索引可用于一個API函數指針的數組,EDX指向調用者的參數堆棧。KiSystemService()通過讀取ServiceTable的一個成員的值(該成員是ntoskrnl.exe的一個公開數據結構:KeServiceDescriptorTable,圖5-1的左面列出了該結構)來獲取函數數組的基地址。實際上,KeServiceDescriptorTable指向一個包含四個服務表的結構,但默認情況下,僅有第一個服務表是有效的。KiSystemService()通過EAX中的索引值進入內部結構KiServiceTable,以查找可處理此API調用的函數的入口地址。在調用目標函數之前,KiSystemServie()以相同的方式查詢KiArgumentTable結構,以找出調用者在參數堆棧中傳入了多少字節,然后使用這個值將參數復制到當前內核堆棧中。此后,就只需要一個簡單的匯編指令:CALL來執行API處理例程了。這里涉及的所有函數都符合__stdcall標準。
圖5-1. KeServiceDescriptorTable的結構圖
typedef NTSTATUS (NTAPI*NTPROC)();
typedef NTPROC* PNTPROC;
#define NTPROC_ sizeof(NTPROC)
typedef struct _SYSTEM_SERVICE_TABLE
{
PNTPROC ;ServiceTable;// array of entry points
PDOWRD; CounterTable;// array of usage counters
DWordServiceLimit;;// number of table entries
PBYTE; ;;ArgumentTable; ;// array of byte counts
}
SYSTEM_SERVICE_TABLE,
*PSYSTEM_SERVICE_TABLE,
**PPSYSTEM_SERVICE_TABLE;
//-----------------------------------------------------------------------------------------------------------
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
SYSTEM_SERVICE_TABLE ntoskrnl// ntoskrnl.exe ( native api )
SYSTEM_SERVICE_TABLE win32k;;// win32k.sys (gdi/user support)
SYSTEM_SERVICE_TABLE Table3;;// not used
SYSTEM_SERVICE_TABLE Table4;;// not used
}
SYSTEM_DESCRIPTOR_TABLE,
*PSYSTEM_DESCRIPTOR_TABLE,
**PPSYSTEM_DESCRIPTOR_TABLE;
列表5-1. SERVICE_DESCRIPTOR_TABLE結構的定義
Windows 2000還提供了另一個服務描述符表參數塊----KeServiceDescriptorTableShadow。不過,KeServiceDescriptorTable已經由ntoskrnl.exe公開的導出了,因此,內核模式的驅動程序可以很容易的訪問它,而KeServiceDescriptorTableShadow則不行。在Windows 2000下,KeServiceDescriptorTableShadow緊隨KeServiceDescriptorTable之后,但是你不能在Windows NT中以這樣的方法找到它,因為,這一規則并不使用于Windows NT。可能在Windows 2000的后續版本的中,這種方法也不行。這兩個參數塊的不同之處在于:KeServiceDescriptorTableShadow中的第二個項被系統使用了,用來指向內部的W32pServiceTable和w32pArgumentTable結構,Win32的內核模式組件Win32K.sys使用這兩個結構來分派自己的API調用,如圖5-2所示。KiSystemService()通過檢查EAX中索引值的第12和13位來確認是不是應該由Win32K.sys處理API調用。如果這兩個位都是0,則是由ntoskrnl.exe處理的Native API調用,因此KiSystemService()使用第一個SDT。如果第12位為1并且第13位為0,KiSystemService()使用第二個SDT,這個SDT并沒有被當前系統使用。這意味著Native API調用的索引值的潛在范圍是:0x0000 --- 0x0FFFF,Win32K.sys調用使用的索引范圍是:0x1000 --- 0x1FFF。因此,0x2000 --- 0x2FFF和0x3000 --- 0x3FFF保留給剩下的兩個SDT。在Windows 2000中,Native API服務表包含248個項,Win32K.sys表包含639個項。
圖5-2. KeServiceDescriptorTableShadow的結構圖
Russinovich和Cogswell的獨具特色的方法是:通過簡單的向KiServiceTable數組中放入一個不同的處理例程來hook所有API調用。這個處理例程最終會調用位于ntoskrnl.exe中的原始處理例程,但它有機會察看一下被調用函數的輸入/輸出參數。這個方法非常強大卻又如此簡單。因為所有用戶模式的線程必須經過這個“針眼”才能獲得Native API的服務,安裝一個全局hook來簡單的替換一個函數指針的方法,在啟動一個新的進程和線程的情況下,也能很穩定的工作。這并不需要一種通訊機制來通知新加入或將要移除的進程/線程。
不幸的是,系統服務表在不同Windows NT版本上不相同。表5-1比較了Windows NT/2000的KiServiceTable。很顯然,不僅是處理例程的號碼從211增加到了248,而且新的處理例程并不是直接添加到列表的末尾,而是被插入到了各個地方!因此,一個服務函數索引,如0x20在Windows 2000中指向NtCreateFile(),而在Windows NT中卻指向NtCreateProfile()。所以,通過操作服務函數表進行hook的API調用監控器必須小心的檢查它所在的Windows NT的版本。這可以通過如下幾個方式來完成:
l 一種可能性是,檢查由ntoskrnl.exe導出的公開變量:NtBuildNumber,就像Russinovich和Cogswell在他們的原文中所作的那樣。Windows NT 4.0為所有Service Pack提供的Build Number是:1381。Windows 2000的當前Build Number是:2195。看來有希望,這個版本號在Windows NT的早期版本中很穩定。
l 另一個可能性是,檢查SharedUserData結構中的NtMajorVersion和NtMinorVersion成員,該結構定義與Windows 2000頭文件ntddk.h中。Windows NT 4.0的所有Service Pack都將SharedUserData->NtMajorVersion設為4,將SharedUserData->NtMinorVersion設為0。Windows 2000的當前版本為Windows NT Version 5.0。
l 本章給出的代碼采用了另一中替代方法:它測試SDT的ServiceLimit成員是否和它的預期值相匹配,該預期值是211(0xD3)針對Windows NT 4.0和248(0xF8)針對Windows 2000。
表5-1. Windows 2000/NT 服務表對比
Windows 2000
索引
Windows NT 4.0
NtAcceptConnectPort
0x00
NtAcceptConnectPort
NtAccessCheck
0x01
NtAccessCheck
NtAccessCheckAndAuditAlarm
0x02
NtAccessCheckAndAuditAlarm
NtAccessCheckByType
0x03
NtAddAtom
NtAccessCheckByTypeAndAuditAlarm
0x04
NtAdjustGroupsToken
NtAccessCheckByTypeResultList
0x05
NtAdjustPrivilegesToken
NtAccessCheckByTypeResultListAndAuditAlarm
0x06
NtAlertResumeThread
NtAccessCheckByTypeResultListAndAuditAlarmByHandle
0x07
NtAlertThread
NtAddAtom
0x08
NtAllocateLocallyUniqueld
NtAdjustGroupsToken
0x09
NtAllocateUuids
NtAdjustPrivilegesToken
0x0A
NtAllocateVirtualMemory
NtAlertResumeThread
0x0B
NtCallbackReturn
NtAlertThread
0x0C
NtCancelloFile
NtAllocateLocallyUniqueld
0x0D
NtCancelTimer
NtAllocateUserPhysicalPages
0x0E
NtClearEvent
NtAllocateUuids
0x0F
NtClose
NtAllocateVirtualMemory
0x10
NtCloseObjectAuditAlarm
NtAreMappedFilesTheSame
0x11
NtCompleteConnectPort
NtAssignProcessToJobObject
0x12
NtConnectPort
NtCallbackReturn
0x13
NtContinue
NtCancelloFile
0x14
NtCreateDirectoryObject
NtCancelTi mer
0x15
NtCreateEvent
NtCancelDeviceWakeupRequest
0x16
NtCreateEventPair
NtClearEvent
0x17
NtCreateFile
NtClose
0x18
NtCreateloCompletion
NtCloseObjectAuditAlarm
0x19
NtCreateKey
NtCompleteConnectPort
0x1A
NtCreateMailslotFile
NtConnectPort
0x1B
NtCreateMutant
NtContinue
0x1C
NtCreateNamedPipeFile
NtCreateDirectoryObject
0x1D
NtCreatePagingFile
NtCreateEvent
0x1E
NtCreatePort
NtCreateEventPair
0x1F
NtCreateProcess
NtCreateFile
0x20
NtCreateProfile
NtCreateloCompletion
0x21
NtCreateSection
NtCreateJobObject
0x22
NtCreateSemaphore
NtCreateKey
0x23
NtCreateSymbolicLinkObject
NtCreateMailslotFile
0x24
NtCreateThread
NtCreateMutant
0x25
NtCreateTimer
NtCreateNamedPipeFile
0x26
NtCreateToken
NtCreatePagingFile
0x27
NtDelayExecution
NtCreatePort
0x28
NtDeleteAtom
NtCreateProcess
0x29
NtDeleteFile
NtCreateProfile
0x2A
NtDeleteKey
NtCreateSection
0x2B
NtDeleteObjectAuditAlarm
NtCreateSemaphore
0x2C
NtDeleteValueKey
NtCreateSymbolicLinkObject
0x2D
NtDeviceloControlFile
NtCreateThread
0x2E
NtDisplayString
NtCreateTimer
0x2F
NtDuplicateObject
NtCreateToken
0x30
NtDuplicateToken
NtCreateWaitablePort
0x31
NtEnumerateKey
NtDelayExecution
0x32
NtEnumerateValueKey
NtDeleteAtom
0x33
NtExtendSection
NtDeleteFile
0x34
NtFindAtom
NtDeleteKey
0x35
NtFlushBuffersFile
NtDeleteObj ectAuditAlarm
0x36
NtFlushlnstructionCache
NtDeleteValueKey
0x37
NtFlushKey
NtDeviceloControlFile
0x38
NtFlushVirtualMemory
NtDisplayString
0x39
NtFlushWriteBuffer
NtDuplicateObject
0x3A
NtFreeVirtualMemory
NtDuplicateToken
0x3B
NtFsControlFile
NtEnumerateKey
0x3C
NtGetContextThread
NtEnumerateValueKey
0x3D
NtGetPlugPlayEvent
NtExtendSection
0x3E
NtGetTickCount
NtFilterToken
0x3F
NtlmpersonateClientOfPort
NtFindAtom
0x40
NtlmpersonateThread
NtFlushBuffersFile
0x41
NtlnitializeRegistry
NtFlushlnstructionCache
0x42
NtListenPort
NtFlushKey
0x43
NtLoadDriver
NtFlushVirtualMemory
0x44
NtLoadKey
NtFlushWriteBuffer
0x45
NtLoadKey2
NtFreeUserPhysicalPages
0x46
NtLockFile
NtFreeVirtualMemory
0x47
NtLockVirtualMemory
NtFsControlFile
0x48
NtMakeTemporaryObject
NtGetContextThread
0x49
NtMapViewOfSection
NtGetDevicePowerState
0x4A
NtNotifyChangeDirectoryFile
NtGetPlugPlayEvent
0x4B
NtNotifyChangeKey
NtGetTickCount
0x4C
NtOpenDirectoryObject
NtGetWriteWatch
0x4D
NtOpenEvent
NtlmpersonateAnonymousToken
0x4E
NtOpenEventPair
NtlmpersonateClientOfPort
0x4F
NtOpenFile
NtlmpersonateThread
0x50
NtOpenloCompletion
NtlnitializeRegistry
0x51
NtOpenKey
NtlnitiatePowerAction
0x52
NtOpenMutant
NtlsSystemResumeAutomatic
0x53
NtOpenObjectAuditAlarm
NtListenPort
0x54
NtOpenProcess
NtLoadDriver
0x55
NtOpenProcessToken
NtLoadKey
0x56
NtOpenSection
NtLoadKey2
0x57
NtOpenSemaphore
NtLockFile
0x58
NtOpenSymbolicLinkObject
NtLockVirtualMemory
0x59
NtOpenThread
NtMakeTemporaryObject
0x5A
NtOpenThreadToken
NtMapUserPhysicalPages
0x5B
NtOpenTimer
NtMapUserPhysicalPagesScatter
0x5C
NtPlugPlayControl
NtMapViewOfSection
0x5D
NtPrivilegeCheck
NtNotifyChangeDirectoryFile
0x5E
NtPrivilegedServiceAuditAlarm
NtNotifyChangeKey
0x5F
NtPrivilegeObjectAuditAlarm
NtNotifyChangeMultipleKeys
0x60
NtProtectVirtualMemory
NtOpenDirectoryObject
0x61
NtPulseEvent
NtOpenEvent
0x62
NtQuerylnformationAtom
NtOpenEventPair
0x63
NtQueryAttributesFile
NtOpenFile
0x64
NtQueryDefaultLocale
NtOpenloCompletion
0x65
NtQueryDirectoryFile
NtOpenJobObject
0x66
NtQueryDirectoryObject
NtOpenKey
0x67
NtQueryEaFile
NtOpenMutant
0x68
NtQueryEvent
NtOpenObjectAuditAlarm
0x69
NtQueryFullAttributesFile
NtOpenProcess
0x6A
NtQuerylnformationFile
NtOpenProcessToken
0x6B
NtQueryloCompletion
NtOpenSection
0x6C
NtQuerylnformationPort
NtOpenSemaphore
0x6D
NtQuerylnformationProcess
NtOpenSymbolicLinkObject
0x6E
NtQuerylnformationThread
NtOpenThread
0x6F
NtQuerylnformationToken
NtOpenThreadToken
0x70
NtQuerylntervalProfile
NtOpenTimer
0x71
NtQueryKey
NtPlugPlayControl
0x72
NtQueryMultipleValueKey
NtPowerlnformation
0x73
NtQueryMutant
NtPrivilegeCheck
0x74
NtQueryObject
NtPrivilegedServiceAuditAlarm
0x75
NtQueryOleDirectoryFile
NtPrivilegeObjectAuditAlarm
0x76
NtQueryPerformanceCounter
NtProtectVirtualMemory
0x77
NtQuerySection
NtPulseEvent
0x78
NtQuerySecurityObject
NtQuerylnformationAtom
0x79
NtQuery Semaphore
NtQueryAttributesFile
0x7A
NtQuerySymbolicLinkObject
NtQueryDefaultLocale
0x7B
NtQuerySystemEnvironmentValue
NtQueryDefaultUILanguage
0x7C
NtQuerySystemlnformation
NtQueryDirectoryFile
0x7D
NtQuerySystemTime
NtQueryDirectoryObject
0x7E
NtQuery Timer
NtQueryEaFile
0x7F
NtQueryTimerResolution
NtQueryEvent
0x80
NtQuery ValueKey
NtQueryFullAttributesFile
0x81
NtQuery VirtualMemory
NtQuerylnformationFile
0x82
NtQuery VolumelnformationFile
NtQuerylnformationJobObject
0x83
NtQueueApcThread
NtQueryloCompletion
0x84
NtRaiseException
NtQuerylnformationPort
0x85
NtRaiseHardError
NtQuerylnformationProcess
0x86
NtReadFile
NtQuerylnformationThread
0x87
NtReadFileScatter
NtQuerylnformationToken
0x88
NtReadRequestData
NtQuerylnstallUILanguage
0x89
NtReadVirtualMemory
NtQuerylntervalProfile
0x8A
NtRegisterThreadTerminatePort
NtQueryKey
0x8B
NtReleaseMutant
NtQueryMultiple ValueKey
0x8C
NtReleaseSemaphore
NtQueryMutant
0x8D
NtRemoveloCompletion
NtQueryObject
0x8E
NtReplaceKey
NtQueryOpenSubKeys
0x8F
NtReplyPort
NtQueryPerformanceCounter
0x90
NtReplyWaitReceivePort
NtQueryQuotalnformationFile
0x91
NtReplyWaitReplyPort
NtQuerySection
0x92
NtRequestPort
NtQuerySecurityObject
0x93
NtRequestWaitReplyPort
NtQuerySemaphore
0x94
NtResetEvent
NtQuerySymbolicLinkObject
0x95
NtRestoreKey
NtQuerySystemEnvironmentValue
0x96
NtResumeThread
NtQuerySystemlnformation
0x97
NtSaveKey
NtQuerySystemTime
0x98
NtSetloCompletion
NtQueryTimer
0x99
NtSetContextThread
NtQueryTimerResolution
0x9A
NtSetDefaultHardErrorPort
NtQueryValueKey
0x9B
NtSetDefaultLocale
NtQueryVirtualMemory
0x9C
NtSetEaFile
NtQueryVolumelnformationFile
0x9D
NtSetEvent
NtQueueApcThread
0x9E
NtSetHighEventPair
NtRaiseException
0x9F
NtSetHighWaitLowEventPair
NtRaiseHardError
0xA0
NtSetHighWaitLowThread
NtReadFile
0xA1
NtSetlnformationFile
NtReadFileScatter
0xA2
NtSetlnformationKey
NtReadRequestData
0xA3
NtSetlnformationObject
NtReadVirtualMemory
0xA4
NtSetlnformationProcess
NtRegisterThreadTerminatePort
0xA5
NtSetlnformationThread
NtReleaseMutant
0xA6
NtSetlnformationToken
NtReleaseSemaphore
0xA7
NtSetlntervalProfile
NtRemoveloCompletion
0xA8
NtSetLdtEntries
NtReplaceKey
0xA9
NtSetLowEventPair
NtReplyPort
0xAA
NtSetLowWaitHighEventPair
NtReplyWaitReceivePort
0xAB
NtSetLowWaitHighThread
NtReplyWaitReceivePortEx
0xAC
NtSetSecurity Object
NtReplyWaitRepiyPort
0xAD
NtSetSystemEnvironmentValue
NtRequestDeviceWakeup
0xAE
NtSetSystemlnformation
NtRequestPort
0xAF
NtSetSystemPowerState
NtRequestWaitReplyPort
0xB0
NtSetSystemTime
NtRequestWakeupLatency
0xB1
NtSetTimer
NtResetEvent
0xB2
NtSetTimerResolution
NtResetWriteWatch
0xB3
NtSetValueKey
NtRestoreKey
0xB4
NtSetVolumelnformationFile
NtResumeThread
0xB5
NtShutdownSystem
NtSaveKey
0xB6
NtSignalAndWaitForSingleObject
NtSaveMergedKeys
0xB7
NtStartProfile
NtSecureConnectPort
0xB8
NtStopProfile
NtSetloCompletion
0xB9
NtSuspendThread
NtSetContextThread
0xBA
NtSystemDebugControl
NtSetDefaultHardErrorPort
0xBB
NtTerminateProcess
NtSetDefaultLocale
0xBC
NtTerminateThread
NtSetDefaultUILanguage
0xBD
NtTestAlert
NtSetEaFile
0xBE
NtUnloadDriver
NtSetEvent
0xBF
NtUnloadKey
NtSetHighEventPair
0xC0
NtUnlockFile
NtSetHighWaitLowEventPair
0xC1
NtUnlockVirtualMemory
NtSetlnformationFile
0xC2
NtUnmapViewOfSection
NtSetlnformationJobObject
0xC3
NtVdmControl
NtSetlnformationKey
0xC4
NtWaitForMultipleObjects
NtSetlnformationObject
0xC5
NtWaitForSingleObject
NtSetlnformationProcess
0xC6
NtWaitHighEventPair
NtSetlnformationThread
0xC7
NtWaitLowEventPair
NtSetlnformationToken
0xC8
NtWriteFile
NtSetlntervalProfile
0xC9
NtWriteFileGather
NtSetLdtEntries
0xCA
NtWriteRequestData
NtSetLowEventPair
0xCB
NtWriteVirtualMemory
NtSetLowWaitHighEventPair
0xCC
NtCreateChannel
NtSetQuotalnformationFile
0xCD
NtListenChannel
NtSetSecurity O b j ect
0xCE
NtOpenChannel
NtSetSystemEnvironment Value
0xCF
NtReplyWaitSendChannel
NtSetSystemlnformation
0xD0
NtSendWaitReplyChannel
NtSetSystemPowerSrate
0xD1
NtSetContextChannel
NtSetSystemTime
0xD2
NtYieldExecution
NtSetThreadExecutionState
0xD3
N/A
NtSetTimer
0xD4
N/A
NtSetTimerResolution
0xD5
N/A
NtSetUuidSeed
0xD6
N/A
NtSetValueKey
0xD7
N/A
NtSetVolumelnformationFile
0xD8
N/A
NtShutdownSystem
0xD9
N/A
NtSignalAndWaitForSingleObject
0xDA
N/A
NtStartProfile
0xDB
N/A
NtStopProfile
0xDC
N/A
NtSuspendThread
0xDD
N/A
NtSystemDebugControl
0xDE
N/A
NtTerminateJobObject
0xDF
N/A
NtTerminateProcess
0xE0
N/A
NtTerminateThread
0xE1
N/A
NtTestAlert
0xE2
N/A
NtUnloadDriver
0xE3
N/A
NtUnloadKey
0xE4
N/A
NtUnlockFile
0xE5
N/A
NtUnlockVirtualMemory
0xE6
N/A
NtUnmapViewOfSection
0xE7
N/A
NtVdmControl
0xE8
N/A
NtWaitForMultipleObjects
0xE9
N/A
NtWaitForSingleObject
0xEA
N/A
NtWaitHighEventPair
0xEB
N/A
NtWaitLowEventPair
0xEC
N/A
NtWriteFile
0xED
N/A
NtWriteFileGather
0xEE
N/A
NtWriteRequestData
0xEF
N/A
NtWriteVirtualMemory
0xF0
N/A
NtCreateChannel
0xF1
N/A
NtListenChannel
0xF2
N/A
NtOpenChannel
0xF3
N/A
NtReplyWaitSendChannel
0xF4
N/A
NtSendWaitReplyChannel
0xF5
N/A
NtSetContextChannel
0xF6
N/A
NtYieldExecution
0xF7
N/A
Russinoich和Cogewell采用的最重要的一步是:編寫一個內核模式的設備驅動程序來安裝和維護Native API Hook。因為,用戶模式下的模塊沒有修改系統服務描述符表的權限。就像第四章中的Spy驅動程序,這是一種多少有些特殊的驅動程序,因為它不處理通常的I/O請求。它只是導出一個簡單的設備I/O控制(IOCTL)接口,以讓用戶模式下的代碼訪問它收集到的數據。該驅動程序的主要任務是修改KiServiceTable、攔截并記錄所選的Windows 2000 Native API調用。盡管這種方法很簡單而且優雅,它還是有些讓人擔心。它的簡單使我想起了在DOS時代,hook一個系統服務只需要簡單的修改處理器的中斷向量表中的指針。任何知道如何編寫基本的Windows 2000內核驅動程序的人都可以hook任意的NT系統服務而不需要而外的努力。
Russinovich和Cogswell使用他們的技術開發了一個非常有用的Windows NT注冊表監視器。當使用他們的技術來完成其他“間諜”任務時,我很快就變得煩躁起來,這是因為我需要為我要監控的每個API函數都編寫一個獨立的hook API函數。為了避免編寫大量的代碼,我打算找出一種方法來強迫所有我感興趣的API函數進入同一個hook函數中。這個任務花費了我大量的時間,并給我展示了多種多樣的藍屏。不過,最終的結果是我得到了一個通用的解決方案,只需花費很少的努力,我就能hook不同的API函數集合。
