电脑技术学习

《Undocumented Windows 2000 Secrets》翻译 --- 第五章(1)

dn001

第五章 监控Native API调用

翻译:Kendiv( fcczj@263.net )

更新:Tuesday, February 22, 2005

声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。

拦截系统调用在任何时候都是程序员们的最爱。这种大众化爱好的动机也是多种多样的:代码性能测试(Code Profiling)和优化,逆向工程,用户活动记录等等。所有这些都有一个共同的目的:将控制传递给一块特殊的代码,这样无论一个应用程序何时调用系统服务,都可以发现哪个服务被调用了,接收了什么参数,返回的结果是什么以及执行它花费了多少时间。根据最初由Mark RussinovichBryce Cogswell提出的技巧,本章将介绍一个可以hook任意Native API函数的通用框架。这里使用的方法完全是数据驱动(data-driven)的,因此,它可以很容易被扩展,并能适应其他Windows NT/2000版本。所有进程的API调用产生的数据都被写入一个环状缓冲区中,客户端程序可以通过设备I/O控制来读取该缓冲区。采用的数据协议(protocol data)的格式是以行为导向的ANSI文本流,它符合严格的格式化规则,应用程序可以很容易的再次处理它们(postprocessing)。为了示范此种客户端程序的基本框架,本章还提供了一个示例性的数据协议察看器,该程序运行于控制台窗口中。

译注:

profiling 一般是指对程序做性能方面的测试, 主要是速度上的。 在翻译时,可译为:剖析,最好是根据上文环境进行翻译。

修改服务描述符表

对比“原始”的操作系统,如DosWindows 3.x,它们对程序员在API中加入hook的限制很少,而Win32系统,如Windows 2000/NTWindows 9.x则很难驾驭,因为它们使用了巧妙的保护机制把不相关的代码分离出来。在Win32 API上设置一个系统范围的hook绝不是一个小任务。幸运的是,我们有像Matt PIEtrekJeffery Richter这样的Win32向导,他们做了大量的工作来向我们展示如何完成这一任务,尽管事实上,并没有简单和优雅的解决方案。在1997年,RussinovichCogswell介绍了一种可在Windows NT上实现系统范围hook的完全不同的方法,可在更低一层上拦截系统调用(RussinovichCogswell 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的调用者通过EAXEDX寄存器传入。EAX包含从0开始的索引,该索引可用于一个API函数指针的数组,EDX指向调用者的参数堆栈。KiSystemService()通过读取ServiceTable的一个成员的值(该成员是ntoskrnl.exe的一个公开数据结构:KeServiceDescriptorTable5-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中的第二个项被系统使用了,用来指向内部的W32pServiceTablew32pArgumentTable结构,Win32的内核模式组件Win32K.sys使用这两个结构来分派自己的API调用,如5-2所示。KiSystemService()通过检查EAX中索引值的第1213位来确认是不是应该由Win32K.sys处理API调用。如果这两个位都是0,则是由ntoskrnl.exe处理的Native API调用,因此KiSystemService()使用第一个SDT。如果第12位为1并且第13位为0KiSystemService()使用第二个SDT,这个SDT并没有被当前系统使用。这意味着Native API调用的索引值的潜在范围是:0x0000 --- 0x0FFFFWin32K.sys调用使用的索引范围是:0x1000 --- 0x1FFF。因此,0x2000 --- 0x2FFF0x3000 --- 0x3FFF保留给剩下的两个SDT。在Windows 2000中,Native API服务表包含248个项,Win32K.sys表包含639个项。

5-2. KeServiceDescriptorTableShadow的结构图

RussinovichCogswell的独具特色的方法是:通过简单的向KiServiceTable数组中放入一个不同的处理例程来hook所有API调用。这个处理例程最终会调用位于ntoskrnl.exe中的原始处理例程,但它有机会察看一下被调用函数的输入/输出参数。这个方法非常强大却又如此简单。因为所有用户模式的线程必须经过这个“针眼”才能获得Native API的服务,安装一个全局hook来简单的替换一个函数指针的方法,在启动一个新的进程和线程的情况下,也能很稳定的工作。这并不需要一种通讯机制来通知新加入或将要移除的进程/线程。

不幸的是,系统服务表在不同Windows NT版本上不相同。5-1比较了Windows NT/2000KiServiceTable。很显然,不仅是处理例程的号码从211增加到了248,而且新的处理例程并不是直接添加到列表的末尾,而是被插入到了各个地方!因此,一个服务函数索引,如0x20Windows 2000中指向NtCreateFile(),而在Windows NT中却指向NtCreateProfile()。所以,通过操作服务函数表进行hookAPI调用监控器必须小心的检查它所在的Windows NT的版本。这可以通过如下几个方式来完成:

l 一种可能性是,检查由ntoskrnl.exe导出的公开变量:NtBuildNumber,就像RussinovichCogswell在他们的原文中所作的那样。Windows NT 4.0为所有Service Pack提供的Build Number是:1381Windows 2000的当前Build Number是:2195。看来有希望,这个版本号在Windows NT的早期版本中很稳定。

l 另一个可能性是,检查SharedUserData结构中的NtMajorVersionNtMinorVersion成员,该结构定义与Windows 2000头文件ntddk.h中。Windows NT 4.0的所有Service Pack都将SharedUserData->NtMajorVersion设为4,将SharedUserData->NtMinorVersion设为0Windows 2000的当前版本为Windows NT Version 5.0

l 本章给出的代码采用了另一中替代方法:它测试SDTServiceLimit成员是否和它的预期值相匹配,该预期值是2110xD3)针对Windows NT 4.02480xF8)针对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

RussinoichCogewell采用的最重要的一步是:编写一个内核模式的设备驱动程序来安装和维护Native API Hook。因为,用户模式下的模块没有修改系统服务描述符表的权限。就像第四章中的Spy驱动程序,这是一种多少有些特殊的驱动程序,因为它不处理通常的I/O请求。它只是导出一个简单的设备I/O控制(IOCTL)接口,以让用户模式下的代码访问它收集到的数据。该驱动程序的主要任务是修改KiServiceTable、拦截并记录所选的Windows 2000 Native API调用。尽管这种方法很简单而且优雅,它还是有些让人担心。它的简单使我想起了在DOS时代,hook一个系统服务只需要简单的修改处理器的中断向量表中的指针。任何知道如何编写基本的Windows 2000内核驱动程序的人都可以hook任意的NT系统服务而不需要而外的努力。

RussinovichCogswell使用他们的技术开发了一个非常有用的Windows NT注册表监视器。当使用他们的技术来完成其他“间谍”任务时,我很快就变得烦躁起来,这是因为我需要为我要监控的每个API函数都编写一个独立的hook API函数。为了避免编写大量的代码,我打算找出一种方法来强迫所有我感兴趣的API函数进入同一个hook函数中。这个任务花费了我大量的时间,并给我展示了多种多样的蓝屏。不过,最终的结果是我得到了一个通用的解决方案,只需花费很少的努力,我就能hook不同的API函数集合。

标签: