4 《Undocumented Windows 2000 Secrets》翻译 --- 第四章( 二 )


NTSTATUS SpyOutputBool (BOOL fValue,
PVOID pOutput,
DWORD dOutput,
PDWORD pdInfo)
{
return SpyOutputBinary (&fValue, BOOL_,
pOutput, dOutput, pdInfo);
}
// -----------------------------------------------------------------
NTSTATUS SpyOutputPointer (PVOID pValue,
PVOID pOutput,
DWORD dOutput,
PDWORD pdInfo)
{
return SpyOutputBinary (&pValue, PVOID_,
pOutput, dOutput, pdInfo);
}
列表 4-11. 向 IOCTL 的缓冲区中写入数据
你可能注意到 列表 4-7 中的 SpyDispatcher() 还引用了其他的 SpyInput*() 和 SpyOutput*() 函数 。尽管这些函数最终还是调用 SpyInputBinary() 和 SpyOutputBinary(),但它们还是比 列表 4-10 和 4-11 中的基本函数要复杂些,因此,稍后我们在讨论它们 。现在,让我们从 SpyDispatcher() 开始,一步步的分析它的 switch/case 语句 。
IOCTL 函数 SPY_IO_VERSION_INFO
IOCTL 的 SPY_IO_VERSION_INFO 函数用有关 Spy 驱动自身的数据填充调用者提供的 SPY_VERSION_INFO 结构 。该功能不需要输入参数,需要使用 SpyOutputVersionInfo() 帮助函数 。列表 4-12 给出了该函数和 SPY_VERSION_INFO 结构,该函数很简单,它将 dVersion 成员设置为 SPY_VERSION 常量(当前是 100,表示 V1.00 ),该常量定义于 w2k_spy.h 中 。然后复制驱动程序的符号化名称,即字符串常量 DRV_NAME (“ SBS Windows 2000 Spy Device ”)到 awName 成员 。通过整除 dVersion 可获取主版本号,剩下的是次版本号 。
typedef struct _SPY_VERSION_INFO
{
DWORD dVersion;
WORD awName [SPY_NAME];
}
SPY_VERSION_INFO, *PSPY_VERSION_INFO, **PPSPY_VERSION_INFO;
#define SPY_VERSION_INFO_ sizeof (SPY_VERSION_INFO)
NTSTATUS SpyOutputVersionInfo (PVOID pOutput,
DWORD dOutput,
PDWORD pdInfo)
{
SPY_VERSION_INFO svi;
svi.dVersion = SPY_VERSION;
wcscpyn (svi.awName, USTRING (CSTRING (DRV_NAME)), SPY_NAME);
return SpyOutputBinary (&svi, SPY_VERSION_INFO_,
pOutput, dOutput, pdInfo);
}
列表 4-12. 获取 Spy 驱动程序的版本信息
IOCTL 函数 SPY_IO_OS_INFO
该函数比上一个有趣的多 。它是另一个只有输出的函数,不需要输入参数,使用几个操作系统的内部参数来填充调用者提供的 SPY_OS_INFO 结构 。列表 4-13 列出了该结构的定义,和 Dispatcher 调用的 SpyOutputOsInfo() 帮助函数 。有些结构体成员只是被简单的设为定义于 DDK 头文件和 w2k_spy.h 中的常量;其他的将被设为从几个内部的内核变量和结构体中读取的当前值 。在第二章中,你已经了解了变量 NtBuildNumber 和 NtGlobalFlag (由 ntoskrnl.exe 导出,参见 附录 B 中的 表 B-1 ) 。和其他的 Nt* 符号不同,这两个符号不指向 API 函数,而是指向位于内核的 .data section 中的变量 。在 Win32 世界里,导出变量是十分罕见的 。不过,Windows 2000 的几个内核模块都使用了这一技术 。Ntoskrnl.exe 导出了至少 55 个变量,ntdll.dll 提供了 4 个,hal.dll 提供了 1 个 。SpyOutputOsInfo() 将从 ntoskrnl.exe 导出的变量中复制 MmHighestUserAddress 、 MmUserProbeAddress 、 MmSystemRangeStart 、 NtGlobalFlag 、 KeI386MachineType 、 KeNumberProcessors 和 NtBuildNumber 到输出缓冲区中 。
当一个模块从另一个模块中导入数据时,它需要使用 extern 关键字来通知编译器和链接器 。这会使链接器生成一个进入模块导出节的入口,并会解析符号名以确定其地址 。有些 extern 声明已经包含在 ntddk.h。列表 4-13 给出了缺失的那些 extern 声明 。
extern PWORD NlsAnsiCodePage;
extern PWORD NlsOemCodePage;
extern PWORD NtBuildNumber;
extern PDWORD NtGlobalFlag;
extern PDWORD KeI386MachineType;
typedef struct _SPY_OS_INFO
{
DWORD dPageSize;

推荐阅读