第五章 监控Native API调用
翻译:Kendiv( fcczj@263.net )
更新: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函数集合。
标签: