当前位置:早雪网网络学院编程文档其他语言 → 《Undocumented Windows 2000 Secrets》翻译 --- 第四章(4)

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

减小字体 增大字体 作者:未知  来源:supcode.com收集整理  发布时间:2005-7-1 14:38:01

第四章  探索Windows 2000的内存管理机制

翻译:Kendiv (fcczj@263.net )

更新:Sunday, February 17, 2005

 

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

 

尽管Spy设备使用可缓冲的I/O,但它还是会检查输入/输出缓冲区的有效性。因为客户端程序传入的数据可能比所需的少或者提供的缓冲区不够容纳输出数据。系统不能捕获这些语意错误,因为它不知道在一次IOCTL传输中所传输的数据的类型。因此,SpyDispatcher()调用帮助函数SpyInput*()SpyOutput*()来从I/O缓冲区中复制或写入数据。这些函数仅在缓冲区大小与操作的需求相匹配时才执行。列表4-10给出了基本的输入函数,列表4-11给出了基本的输出函数。SpyInputBinary()SpyOutputBinary()被广泛的使用,它们测试缓冲区的大小,如果OK,则使用Windows 2000运行时库函数RtlCopyMemory()复制被请求的数据。剩余的函数只是上述两个基本函数的简单外包,用来操作常见的数据类型DWORDBOOLPVOIDHANDLE等。SpyOutputBlock()复制由调用者在SPY_MEMORY_BLOCK结构中指定的数据块,当然这需要首先验证请求范围内的字节都是可读的。如果传入的输入缓冲区的大小不正确,SpyInput*()函数将返回STATUS_INVALID_BUFFER_SIZE,如果输出缓冲区比需要的小,SpyOutput*()函数将返回STATUS_BUFFER_TOO_SMALL

 

 

NTSTATUS SpyInputBinary (PVOID  pData,

                         DWORD  dData,

                         PVOID  pInput,

                         DWORD  dInput)

    {

    NTSTATUS ns = STATUS_INVALID_BUFFER_SIZE;

 

    if (dData <= dInput)

        {

        RtlCopyMemory (pData, pInput, dData);

        ns = STATUS_SUCCESS;

        }

    return ns;

    }

 

// -----------------------------------------------------------------

 

NTSTATUS SpyInputDword (PDWORD pdValue,

                        PVOID  pInput,

                        DWORD  dInput)

    {

    return SpyInputBinary (pdValue, DWORD_, pInput, dInput);

    }

 

// -----------------------------------------------------------------

 

NTSTATUS SpyInputBool (PBOOL  pfValue,

                       PVOID  pInput,

                       DWORD  dInput)

    {

    return SpyInputBinary (pfValue, BOOL_, pInput, dInput);

    }

 

// -----------------------------------------------------------------

 

NTSTATUS SpyInputPointer (PPVOID ppAddress,

                          PVOID  pInput,

                          DWORD  dInput)

    {

    return SpyInputBinary (ppAddress, PVOID_, pInput, dInput);

    }

 

// -----------------------------------------------------------------

 

NTSTATUS SpyInputHandle (PHANDLE phObject,

                         PVOID   pInput,

                         DWORD   dInput)

    {

    return SpyInputBinary (phObject, HANDLE_, pInput, dInput);

    }

列表4-10.  IOCTL缓冲区中读取输入数据

 

 

NTSTATUS SpyOutputBinary (PVOID  pData,

                          DWORD  dData,

                          PVOID  pOutput,

                          DWORD  dOutput,

                          PDWORD pdInfo)

    {

    NTSTATUS ns = STATUS_BUFFER_TOO_SMALL;

 

    *pdInfo = 0;

 

    if (dData <= dOutput)

        {

        RtlCopyMemory (pOutput, pData, *pdInfo = dData);

        ns = STATUS_SUCCESS;

        }

    return ns;

    }

 

// -----------------------------------------------------------------

 

NTSTATUS SpyOutputBlock (PSPY_MEMORY_BLOCK psmb,

                         PVOID             pOutput,

                         DWORD             dOutput,

                         PDWORD            pdInfo)

    {

    NTSTATUS ns = STATUS_INVALID_PARAMETER;

 

    if (SpyMemoryTestBlock (psmb->pAddress, psmb->dBytes))

        {

        ns = SpyOutputBinary (psmb->pAddress, psmb->dBytes,

                              pOutput, dOutput, pdInfo);

        }

    return ns;

    }

 

// -----------------------------------------------------------------

 

NTSTATUS SpyOutputDword (DWORD  dValue,

                         PVOID  pOutput,

                         DWORD  dOutput,

                         PDWORD pdInfo)

    {

    return SpyOutputBinary (&dValue, DWORD_,

                            pOutput, dOutput, pdInfo);

    }

 

// -----------------------------------------------------------------

 

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-104-11中的基本函数要复杂些,因此,稍后我们在讨论它们。现在,让我们从SpyDispatcher()开始,一步步的分析它的switch/case语句。

 

IOCTL函数 SPY_IO_VERSION_INFO

IOCTLSPY_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中的常量;其他的将被设为从几个内部的内核变量和结构体中读取的当前值。在第二章中,你已经了解了变量NtBuildNumberNtGlobalFlag(由ntoskrnl.exe导出,参见附录B中的B-1)。和其他的Nt*符号不同,这两个符号不指向API函数,而是指向位于内核的.data section中的变量。在Win32世界里,导出变量是十分罕见的。不过, Windows 2000的几个内核模块都使用了这一技术。Ntoskrnl.exe导出了至少55个变量,ntdll.dll提供了4个,hal.dll提供了1个。SpyOutputOsInfo()将从ntoskrnl.exe导出的变量中复制MmHighestUserAddressMmUserProbeAddressMmSystemRangeStartNtGlobalFlagKeI386MachineTypeKeNumberProcessorsNtBuildNumber到输出缓冲区中。

 

当一个模块从另一个模块中导入数据时,它需要使用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;

    DWORD   dPageShift;

    DWORD   dPtiShift;

    DWORD   dPdiShift;

    DWORD   dPageMask;

    DWORD   dPtiMask;

    DWORD   dPdiMask;

    PX86_PE PteArray;

    PX86_PE PdeArray;

    PVOID   pLowestUserAddress;

    PVOID   pThreadEnvironmentBlock;

    PVOID   pHighestUserAddress;

    PVOID   pUserProbeAddress;

    PVOID   pSystemRangeStart;

    PVOID   pLowestSystemAddress;

    PVOID   pSharedUserData;

    PVOID   pProcessorControlRegion;

    PVOID   pProcessorControlBlock;

    DWORD   dGlobalFlag;

    DWORD   dI386MachineType;

    DWORD   dNumberProcessors;

    DWORD   dProductType;

    DWORD   dBuildNumber;

    DWORD   dNtMajorVersion;

    DWORD   dNtMinorVersion;

    WORD    awNtSystemRoot [MAX_PATH];

    }

    SPY_OS_INFO, *PSPY_OS_INFO, **PPSPY_OS_INFO;

 

#define SPY_OS_INFO_ sizeof (SPY_OS_INFO)

 

NTSTATUS SpyOutputOsInfo (PVOID  pOutput,

                          DWORD  dOutput,

                          PDWORD pdInfo)

    {

    SPY_SEGMENT     ss;

    SPY_OS_INFO     soi;

    NT_PRODUCT_TYPE NtProductType;

    PKPCR           pkpcr;

 

    NtProductType = (SharedUserData->ProductTypeIsValid

                     ? SharedUserData->NtProductType

                     : 0);

 

    SpySegment (X86_SEGMENT_FS, 0, &ss);

    pkpcr = ss.pBase;

 

    soi.dPageSize               =  PAGE_SIZE;

    soi.dPageShift              =  PAGE_SHIFT;

    soi.dPtiShift               =  PTI_SHIFT;

    soi.dPdiShift               =  PDI_SHIFT;

    soi.dPageMask               =  X86_PAGE_MASK;

    soi.dPtiMask                =  X86_PTI_MASK;

    soi.dPdiMask                =  X86_PDI_MASK;

    soi.PteArray                =  X86_PTE_ARRAY;

    soi.PdeArray                =  X86_PDE_ARRAY;

    soi.pLowestUserAddress      =  MM_LOWEST_USER_ADDRESS;

    soi.pThreadEnvironmentBlock =  pkpcr->NtTib.Self;

    soi.pHighestUserAddress     = *MmHighestUserAddress;

    soi.pUserProbeAddress       =  (PVOID) *MmUserProbeAddress;

    soi.pSystemRangeStart       = *MmSystemRangeStart;

    soi.pLowestSystemAddress    =  MM_LOWEST_SYSTEM_ADDRESS;