第五章 监控Native API调用
翻译:Kendiv( fcczj@263.net )
更新: Thursday, February 24, 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
DWORD ServiceLimit; // 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第二个服务表,这两个位的其他组合用于剩下的两个服务表,不过系统当前并未使用这两个服务表。这意味着Native API调用的索引值的潜在范围是:0x0000 --- 0x0FFFF,Win32K.sys调用使用的索引范围是:0x1000 --- 0x1FFF。因此,0x2000 --- 0x2FFF和0x3000 --- 0x3FFF保留给剩下的两个服务表。在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 | NtQuerySemaphore |
NtQueryAttributesFile | 0x7A | NtQuerySymbolicLinkObject |
NtQueryDefaultLocale | 0x7B | NtQuerySystemEnvironmentValue |
NtQueryDefaultUILanguage | 0x7C | NtQuerySystemlnformation |
NtQueryDirectoryFile | 0x7D | NtQuerySystemTime |
NtQueryDirectoryObject | 0x7E | NtQueryTimer |
NtQueryEaFile | 0x7F | NtQueryTimerResolution |
NtQueryEvent | 0x80 | NtQueryValueKey |
NtQueryFullAttributesFile | 0x81 | NtQueryVirtualMemory |
NtQuerylnformationFile | 0x82 | NtQueryVolumelnformationFile |
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 | NtSetSecurityObject |
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函数集合。
……………..待续……………