2014年9月11日星期四

内核中的APC

/*
文件名:KeInsertQueueApc.c

应用层有相应的公开的APC函数,如:QueueUserAPC。
但是内核中却没有公开的函数,在微软的网站上搜索下,只搜索到一篇文章:
http://www.microsoft.com/msj/0799/nerd/nerd0799.aspx
很早的了,上个世纪的。而且还有隐含的声明。
APC作为一个操作系统的基本的机制,岂能没有?除非你把应用层的也删除了等原因。
其实这些APC函数在内核中已经导出了,且在很早的版本都有了。
所以可以放心使用这些函数。
内核驱动开发的功能和性能在于开发人员的认识和胆量。
胆量从何而来,在于认识的熟练程度。

顺便说一下:
内核的DPC函数倒是公开了,如:
KeInitializeDpc
KeInsertQueueDpc
KeRemoveQueueDpc

本文的一些数据来自WRK和上面的链接。

本文只是一个简单的示例:
在内核中调用应用程序事先准备好的函数。
更多的功能请看网上:如杀进程,注入DLL等。

made by correy
made at 2014.09.11
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/

#include "ntddk.h"

#define FILE_DEVICE_UNKNOWN             0x00000022
#define IOCTL_UNKNOWN_BASE              FILE_DEVICE_UNKNOWN
#define IOCTL_NOTIFY_REGISTER_APC CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0803, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

typedef
VOID
(*PKNORMAL_ROUTINE) (
    IN PVOID NormalContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    );

typedef
VOID
(*PKKERNEL_ROUTINE) (
    IN struct _KAPC *Apc,
    IN OUT PKNORMAL_ROUTINE *NormalRoutine,
    IN OUT PVOID *NormalContext,
    IN OUT PVOID *SystemArgument1,
    IN OUT PVOID *SystemArgument2
    );

typedef
VOID
(*PKRUNDOWN_ROUTINE) (
    IN struct _KAPC *Apc
    );

typedef enum _KAPC_ENVIRONMENT {
    OriginalApcEnvironment,
    AttachedApcEnvironment,
    CurrentApcEnvironment,
    InsertApcEnvironment
} KAPC_ENVIRONMENT;

NTKERNELAPI
VOID
KeInitializeApc (
    __out PRKAPC Apc,
    __in PRKTHREAD Thread,
    __in KAPC_ENVIRONMENT Environment,
    __in PKKERNEL_ROUTINE KernelRoutine,
    __in_opt PKRUNDOWN_ROUTINE RundownRoutine,
    __in_opt PKNORMAL_ROUTINE NormalRoutine,
    __in_opt KPROCESSOR_MODE ProcessorMode,
    __in_opt PVOID NormalContext
    );

PLIST_ENTRY
KeFlushQueueApc (
    __inout PKTHREAD Thread,
    __in KPROCESSOR_MODE ProcessorMode
    );

NTKERNELAPI
BOOLEAN
KeInsertQueueApc (
    __inout PRKAPC Apc,
    __in_opt PVOID SystemArgument1,
    __in_opt PVOID SystemArgument2,
    __in KPRIORITY Increment
    );

BOOLEAN
KeRemoveQueueApc (
    __inout PKAPC Apc
    );

void     KnotifyUnloadDriver(PDRIVER_OBJECT DriverObject);
NTSTATUS KnotifyDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS KnotifyDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS        ntStatus;
    UNICODE_STRING  uszDriverString;
    UNICODE_STRING  uszDeviceString;
    UNICODE_STRING  uszNotifyEventString;

    PDEVICE_OBJECT  pDeviceObject;
         
    __debugbreak();
   
    RtlInitUnicodeString(&uszDriverString, L"\\Device\\test");// Point uszDriverString at the driver name  
    ntStatus = IoCreateDevice(DriverObject, 0, &uszDriverString, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);// Create and initialize device object
    if(ntStatus != STATUS_SUCCESS)
        return ntStatus;  
 
    RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\test");// Point uszDeviceString at the device name  
    ntStatus = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);// Create symbolic link to the user-visible name
    if(ntStatus != STATUS_SUCCESS)
    {      
        IoDeleteDevice(pDeviceObject);// Delete device object if not successful
        return ntStatus;
    }

    // Load structure to point to IRP handlers...
    DriverObject->DriverUnload                         = KnotifyUnloadDriver;
    DriverObject->MajorFunction[IRP_MJ_CREATE]         = KnotifyDispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = KnotifyDispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KnotifyDispatchIoctl;
     
    return ntStatus;// Return success
}


NTSTATUS KnotifyDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information=0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return(STATUS_SUCCESS);
}


void KMApcCallback(PKAPC Apc, PKNORMAL_ROUTINE NormalRoutine, PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2)
    /*
    0: kd> k
    ChildEBP RetAddr
    b1a6fcfc 8050099b KNotify!KMApcCallback [d:\temp\sources\msj\column\0799\kernel\knotify.c @ 164]
    b1a6fd4c 80542853 nt!KiDeliverApc+0x1af
    b1a6fd4c 7c92e514 nt!KiServiceExit+0x59
    0012fef8 7c92d21a ntdll!KiFastSystemCallRet
    0012fefc 7c8023f1 ntdll!ZwDelayExecution+0xc
    0012ff54 0040110b kernel32!SleepEx+0x61
    0012ff78 00401228 ConsoleApplication1!main+0xab [e:\code\consoleapplication1\consoleapplication1.cpp @ 51]
    0012ffc0 7c816037 ConsoleApplication1!__tmainCRTStartup+0xfe [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 241]
    0012fff0 00000000 kernel32!BaseProcessStart+0x23
    */
{
    ExFreePool(Apc);
    return;
}


NTSTATUS KnotifyDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    NTSTATUS           ntStatus;
    PIO_STACK_LOCATION irpStack  = IoGetCurrentIrpStackLocation(Irp);

    switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
    {
        case IOCTL_NOTIFY_REGISTER_APC:
        {
            PKAPC Apc;
            ULONG *UserRoutine = (ULONG *)Irp->AssociatedIrp.SystemBuffer;

            Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
            KeInitializeApc(Apc,
                            KeGetCurrentThread(),
                            OriginalApcEnvironment,
                            (PKKERNEL_ROUTINE)&KMApcCallback, // kernel-mode routine
                            0,                                // rundown routine
                            (PKNORMAL_ROUTINE)*UserRoutine,   // user-mode routine
                            UserMode, (PVOID)(ULONG)1);
            KeInsertQueueApc(Apc, (PVOID)(ULONG)2, (PVOID)(ULONG)3, 0);
            ntStatus = STATUS_SUCCESS;
            break;
        }    
        default:
            break;
    }

    Irp->IoStatus.Status = ntStatus;
   
    // Set # of bytes to copy back to user-mode...
    if(ntStatus == STATUS_SUCCESS)
        Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    else
        Irp->IoStatus.Information = 0;

    if(ntStatus != STATUS_PENDING)
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return ntStatus;
}


void KnotifyUnloadDriver(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING  uszDeviceString;

    IoDeleteDevice(DriverObject->DeviceObject);

    RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\test");
    IoDeleteSymbolicLink(&uszDeviceString);
}
----------------------------------------------------------------------------------------------------------------------------------------------
#include "stdafx.h"

#include <Windows.h>

#define FILE_DEVICE_UNKNOWN             0x00000022
#define IOCTL_UNKNOWN_BASE              FILE_DEVICE_UNKNOWN
#define IOCTL_NOTIFY_REGISTER_APC CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0803, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)


void ApcCallback(PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2)
    /*
    0: kd> k
    ChildEBP RetAddr
    0012fc1c 7c92e457 ConsoleApplication1!ApcCallback [e:\code\consoleapplication1\consoleapplication1.cpp @ 20]
    0012ff54 0040110b ntdll!KiUserApcDispatcher+0x7
    0012ff78 00401228 ConsoleApplication1!main+0xab [e:\code\consoleapplication1\consoleapplication1.cpp @ 51]
    0012ffc0 7c816037 ConsoleApplication1!__tmainCRTStartup+0xfe [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 241]
    0012fff0 00000000 kernel32!BaseProcessStart+0x23
    */
{
    wchar_t Message[80];
    wsprintf(Message,L"APC Callback!\n P1 == %lx\n P2 == %lx\n P3 == %lx", NormalContext,  SystemArgument1, SystemArgument2);
    MessageBox(NULL, Message, L"Unotify", MB_OK);
}


 int __cdecl main ()
{
    __debugbreak();

    HANDLE hDevice = CreateFile(L"\\\\.\\test", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
    if(hDevice == INVALID_HANDLE_VALUE)
    {
        MessageBox(0,L"请加载驱动!",0,0);
        return 0;
    }

    DWORD dwBytesReturned;
    DWORD pfnApc = (DWORD)&ApcCallback;    
    BOOL bReturnCode = DeviceIoControl(hDevice, IOCTL_NOTIFY_REGISTER_APC, &pfnApc, sizeof(DWORD), 0, 0, &dwBytesReturned, 0);
    if(bReturnCode == FALSE)
    {
        MessageBox(0,L"DeviceIoControl失败!",0,0);
        return 0;
    }
   
    MessageBox(NULL,L"APC Dispatched!",L"Unotify",MB_OK);//先显示这个,再显示ApcCallback的消息框。不过这也可以想办法改变的。
       
    SleepEx(INFINITE, TRUE);//这一行是所有实现的关键。

    return 0;
}

2014年9月4日星期四

驱动创建和访问用户堆

#include <ntifs.h>
#include <windef.h>


/*
文件名:RtlCreateHeap.c 
其实最主要的还是RtlCreateHeap的说明,注意这个可以有回调函数。
RtlAllocateHeap这个函数也不能在指定的地址上申请内存。

made by correy
made at 2014.09.04
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;   
    PVOID HeapHandle = 0;
    PVOID p = 0;
    BOOLEAN B = FALSE ;
    SIZE_T  Size = 9;
    ULONG  Alignment = 1;

    KdBreakPoint();

    HeapHandle = RtlCreateHeap(0, 0, 0, 0, 0, 0);
    if (HeapHandle == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }    

    KdPrint(("HeapHandle == %p!\n", HeapHandle));

    p = RtlAllocateHeap(HeapHandle, HEAP_ZERO_MEMORY, Size);
    if (p == NULL) {
        HeapHandle = RtlDestroyHeap(HeapHandle);
        if (HeapHandle != NULL) {
            KdPrint(("RtlDestroyHeap fails!\n"));
        }
        return STATUS_INSUFFICIENT_RESOURCES;
    }  

    KdPrint(("p == %p!\n", p));

    __try {
        ProbeForWrite(p, Size, Alignment);
        *(char *)p = 9;
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        KdPrint(("ExceptionCode == 0x%x!\n", GetExceptionCode()));
    }

    B = RtlFreeHeap(HeapHandle, HEAP_NO_SERIALIZE, p);
    if (B == FALSE )
    {
        KdPrint(("RtlFreeHeap fails!\n"));
    }

    HeapHandle = RtlDestroyHeap(HeapHandle);
    if (HeapHandle != NULL) {
        KdPrint(("RtlDestroyHeap fails!\n"));
    }

    return status;    
} 

/*
运行结果如下:
HeapHandle == 00180000!
p == 00180688!

这些内存在哪个进程呢?
函数没有指明,但是函数的说明指明了。
*/

InstallHinfSection安装驱动

RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection DefaultInstall 132 C:\Documents and Settings\Administrator\桌面\test-minifilter\test.inf

rem 带空格的路径也不要加分号;路径最好用全路径,不要用相对路径或仅仅一个文件名。
rem minifilter的INF文件建议使用WDK7600的,不建议使用VS2012生成的。因为VS2012生成的能加载成功,但是有的回调不会被调用。
rem 这是文件过滤驱动的标准的加载办法,具体的可见WDK的Using an INF File to Install a File System Filter Driver。
rem 但是minifilter里面没有说,只说了INF文件及加载的顺序。
rem 更多信息请查看InstallHinfSection函数的用法:http://msdn.microsoft.com/en-us/library/aa376957(v=vs.85).aspx。
rem 最好配有CAT文件。
rem 卸载的就不用说了。
rem 安装的办法有很多种,正确的还是微软建议的FilterLoad/FltLoadFilter函数,命令行的还有fltmc load /sc start /net start. 
rem 函数是没有返回值的,编写代码和调用命令是差不多的,就怕RUNDLL32.EXE 不存在的情况。

/*
#include <Windows.h>

#include <Setupapi.h>
#pragma comment (lib,"Setupapi.lib")

 int __cdecl main ()
{
    InstallHinfSection(NULL,NULL,TEXT("DefaultInstall 132 C:\\test-minifilter\\test.inf"),0); //只是安装,没有启动。
    return 0;
}
*/

made by correy
made at 2014.09.04
email:kouleguan at hotmail dot com




2014年9月2日星期二

内核中GUID/UUID/LUID

#include <ntifs.h>
#include <windef.h>
#include <initguid.h> //静态定义UUID用的,否则:error LNK2001。


/*
文件名:ExUuidCreate.C 
功能和目的:示例内核中GUID/UUID/LUID的用法。
起因:Windows系统的有些上述的ID是固定不变的。最好能记住。COM中也可能如此。

ExUuidCreate 

RtlGUIDFromString
RtlStringFromGUID

RtlConvertLongToLuid 有实现代码的函数
RtlConvertUlongToLuid 有实现代码的函数
RtlEqualLuid 这是个宏
RtlIsZeroLuid 这是个宏

ConvertInterfaceGuidToLuid 
ConvertInterfaceLuidToGuid
不过这两个都是:Available in Windows Vista and later versions of the Windows operating systems.

ZwAllocateLocallyUniqueId
这个函数在WDK中没有说:Available in Windows Vista and later versions of Windows.
在下面的连接中都说了:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff566415(v=vs.85).aspx
http://technet.microsoft.com/en-ca/ff566415(v=vs.90).aspx
http://technet.microsoft.com/it-it/subscriptions/ff566415.aspx
经查看,这个函数XP-32的内核下没有导出。
而且WDK只有函数原型,没有实现的代码,LIB里面啥样没有看。

The locally unique identifier (LUID) is a 64-bit value guaranteed to be unique only on the system on which it was generated.
The LUID structure is an opaque structure that specifies an identifier that is guaranteed to be unique on the local machine. 
WDK里面的这两句话矛盾不?

made by correy
made at 2014.08.15
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


#define tag  'tset' //test


/*
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426(v=vs.85).aspx
Disk Drives
Class = DiskDrive
ClassGuid = {4d36e967-e325-11ce-bfc1-08002be10318}
This class includes hard disk drives. See also the HDC and SCSIAdapter classes.
在整个WinDDK\7600.16385.1目录中只有两个INF文件包含4d36e967。WRK中也没有搜索到。
所以这个还得自己定义,系统没有定义。

更多的信息:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff547786(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff542998(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553428(v=vs.85).aspx
*/
DEFINE_GUID (DiskClassGuid, 0x4d36e967, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);


BOOL RtlEqualUuid(IN UUID * pu1, IN UUID * pu2)
{
    return RtlEqualMemory(pu1, pu2, sizeof(UUID));  //RtlCompareMemory 
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;   
    UUID  Uuid = {0};//typedef GUID UUID;
    UUID  Uuid2 = DiskClassGuid;
    UNICODE_STRING  GuidString = {0};
    BOOL B = FALSE ;
    LUID Luid = {0};//查看结构用的.

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;    

    status = ExUuidCreate(&Uuid);//注意这个是没有前后的大括号的,但是调试器显示的时候可能会显示上.
    if (!NT_SUCCESS(status)) {
        return status;
    }

    GuidString.MaximumLength = MAX_PATH;
    GuidString.Buffer = ExAllocatePoolWithTag( NonPagedPool, GuidString.MaximumLength, tag );
    if (GuidString.Buffer == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }    
    RtlZeroMemory(GuidString.Buffer, GuidString.MaximumLength);

    status = RtlStringFromGUID(&Uuid, &GuidString);//#define REFGUID const GUID &
    if (!NT_SUCCESS(status)) {
        ExFreePool(GuidString.Buffer);
        return status;
    }

    KdPrint(("GuidString %wZ\n", &GuidString));  //这个是有前后的大括号的.

    status = RtlGUIDFromString(&GuidString, &Uuid);//各个变量/参数保持不变.
    if (!NT_SUCCESS(status)) {
        ExFreePool(GuidString.Buffer);
        return status;
    }

    //B = RtlEqualLuid((LUID *)&Uuid, (LUID *)&DiskClassGuid);
    B = RtlEqualUuid(&Uuid, &Uuid2);//肯定不相等.B == 0.

    ExFreePool(GuidString.Buffer);

    return status;    
} 

2014年8月22日星期五

编程获取某个CPU的PCR/PRCB

#include <ntifs.h>
#include <windef.h>
//#include <wdbgexts.h> //明明有这个文件为啥说找不到呢?这里包含DBGKD_GET_VERSION64和DBGKD_DEBUG_DATA_HEADER64,KDDEBUGGER_DATA64等结构。


/*
文件名:KeGetPcr.c
功能:获取某个CPU的PCR/PRCB。

以前感觉eprocess/kprocess和_ethread/_kthread就很厉害了。
其实还有比他们更加底层的,这就是_kpcr和_kprcb。
更多的还有blos,PCI等硬件信息。

以前觉得这太底层,不敢触及。
其实这是必须接触,接触到有很多的好处。
本文没有用汇编语法实现的X86和X64的编程。其实也是变相的汇编,由编译器实现而已。
看来搞系统,不但知道系统的知识,还要知道编译环境的信息,会更好。

软件再智能,再高级也是借助于硬件实现的。
没用硬件,连空虚的灵魂都没有。

本文参考:WDK和MSDN。

made by correy
made at 2014.08.22
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


//这是X64的定义。
//这里用代码实现,这个函数肯定没有导出。
//__forceinline
//PKPCR
//KeGetPcr (
//    VOID
//    )
//
//{
//    return (PKPCR)(ULONG_PTR)KIPCR();
//}


//这是X64的定义。
//__forceinline
//PKPCR
//KeGetPcr (
//    VOID
//    )
//
//{
//    return (PKPCR)__readgsqword(FIELD_OFFSET(KPCR, Self));
//}


/*
仿照系统的写的64位的KeGetPcr,
自己写一个X86的。
*/
#ifdef _X86_
__forceinline
PKPCR
KeGetPcr (
    VOID
    )
{
    return (PKPCR)__readfsdword(FIELD_OFFSET(KPCR, SelfPcr));
}
#endif
//另一种思路是:直接汇编。
//__asm {  
//    movzx eax, _PCR
//        mov pkpcr,eax
//}
//注意:这一行.
//#define _PCR   fs:[0]  


struct _KPRCB * KeGetPrcb (PKPCR pkpcr)
    /*
    _KPRCB结构没有公开。
    WRK和WINDBG肯定有。
    而且有32和64之分。
    wrk\WindowsResearchKernel-WRK\WRK-v1.2\base\ntos\inc\i386.h
    wrk\WindowsResearchKernel-WRK\WRK-v1.2\base\ntos\inc\amd64.h
    */
{
#ifdef _X86_
    return pkpcr->Prcb;
#endif

#if !defined(MIDL_PASS) && defined(_M_AMD64)
    return pkpcr->CurrentPrcb;
#endif
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{   
    
}


#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PKPCR pkpcr;
    struct _KPRCB *Prcb;
    //DBGKD_GET_VERSION64 * pdgv;
    //PKDDEBUGGER_DATA64 pkdd;
    //char * p;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload; 

    KeSetSystemAffinityThread(1);
    pkpcr = KeGetPcr ();
    KeRevertToUserAffinityThread();

    Prcb = KeGetPrcb(pkpcr);

    ////下面打印获取一些没有导出的重要的信息,如诸多变量。
    //参考了:http://www.cnblogs.com/unixstudio/archive/2012/11/05/2755356.html。
    //pdgv = pkpcr->KdVersionBlock;//在X64下这个等于零。

    ////pkdd = (PKDDEBUGGER_DATA64)((char *)pkpcr + sizeof(DBGKD_GET_VERSION64));
    //p = (char *)pdgv;
    //p += sizeof(DBGKD_GET_VERSION64);
    //pkdd = (PKDDEBUGGER_DATA64)p;     

    return Status;
} 


/*
X86的验证:
0: kd> !pcr 
KPCR for Processor 0 at ffdff000:
    Major 1 Minor 1
	NtTib.ExceptionList: f88f6578
	    NtTib.StackBase: f88f6df0
	   NtTib.StackLimit: f88f4000
	 NtTib.SubSystemTib: 00000000
	      NtTib.Version: 00000000
	  NtTib.UserPointer: 00000000
	      NtTib.SelfTib: 00000000

	            SelfPcr: ffdff000
	               Prcb: ffdff120
	               Irql: 00000000
	                IRR: 00000000
	                IDR: ffffffff
	      InterruptMode: 00000000
	                IDT: 8003f400
	                GDT: 8003f000
	                TSS: 80042000

	      CurrentThread: 8234c8a0
	         NextThread: 00000000
	         IdleThread: 8055ce60

	          DpcQueue: 
0: kd> !pcr 0
KPCR for Processor 0 at ffdff000:
    Major 1 Minor 1
	NtTib.ExceptionList: f88f6578
	    NtTib.StackBase: f88f6df0
	   NtTib.StackLimit: f88f4000
	 NtTib.SubSystemTib: 00000000
	      NtTib.Version: 00000000
	  NtTib.UserPointer: 00000000
	      NtTib.SelfTib: 00000000

	            SelfPcr: ffdff000
	               Prcb: ffdff120
	               Irql: 00000000
	                IRR: 00000000
	                IDR: ffffffff
	      InterruptMode: 00000000
	                IDT: 8003f400
	                GDT: 8003f000
	                TSS: 80042000

	      CurrentThread: 8234c8a0
	         NextThread: 00000000
	         IdleThread: 8055ce60

	          DpcQueue: 
0: kd> dt nt!_kpcr ffdff000
   +0x000 NtTib            : _NT_TIB
   +0x01c SelfPcr          : 0xffdff000 _KPCR
   +0x020 Prcb             : 0xffdff120 _KPRCB
   +0x024 Irql             : 0 ''
   +0x028 IRR              : 0
   +0x02c IrrActive        : 0
   +0x030 IDR              : 0xffffffff
   +0x034 KdVersionBlock   : 0x8054e2b8 Void
   +0x038 IDT              : 0x8003f400 _KIDTENTRY
   +0x03c GDT              : 0x8003f000 _KGDTENTRY
   +0x040 TSS              : 0x80042000 _KTSS
   +0x044 MajorVersion     : 1
   +0x046 MinorVersion     : 1
   +0x048 SetMember        : 1
   +0x04c StallScaleFactor : 0xd40
   +0x050 DebugActive      : 0 ''
   +0x051 Number           : 0 ''
   +0x052 Spare0           : 0 ''
   +0x053 SecondLevelCacheAssociativity : 0 ''
   +0x054 VdmAlert         : 0
   +0x058 KernelReserved   : [14] 0
   +0x090 SecondLevelCacheSize : 0
   +0x094 HalReserved      : [16] 0
   +0x0d4 InterruptMode    : 0      注意:从这里开始这里的在结构的定义里面是没有的。也就是说WINDBG命令显示的比结构定义的成员多四个。
   +0x0d8 Spare1           : 0 ''
   +0x0dc KernelReserved2  : [17] 0
   +0x120 PrcbData         : _KPRCB
0: kd> dt nt!_kpcr ffdff000 -b
   ...
   这命令太长就不显示了,有几千行。
0: kd> dt nt!_kprcb 0xffdff120 
   +0x000 MinorVersion     : 1
   +0x002 MajorVersion     : 1
   +0x004 CurrentThread    : 0x8234c8a0 _KTHREAD
   +0x008 NextThread       : (null) 
   +0x00c IdleThread       : 0x8055ce60 _KTHREAD
   +0x010 Number           : 0 ''
   +0x011 Reserved         : 0 ''
   +0x012 BuildType        : 0
   +0x014 SetMember        : 1
   +0x018 CpuType          : 6 ''
   +0x019 CpuID            : 1 ''
   +0x01a CpuStep          : 0x3a09
   +0x01c ProcessorState   : _KPROCESSOR_STATE
   +0x33c KernelReserved   : [16] 0
   +0x37c HalReserved      : [16] 0
   +0x3bc PrcbPad0         : [92]  ""
   +0x418 LockQueue        : [16] _KSPIN_LOCK_QUEUE
   +0x498 PrcbPad1         : [8]  ""
   +0x4a0 NpxThread        : (null) 
   +0x4a4 InterruptCount   : 0x87e0
   +0x4a8 KernelTime       : 0x50b7
   +0x4ac UserTime         : 0x89
   +0x4b0 DpcTime          : 0x47
   +0x4b4 DebugDpcTime     : 0
   +0x4b8 InterruptTime    : 0x33b
   +0x4bc AdjustDpcThreshold : 0x14
   +0x4c0 PageColor        : 0x14a14
   +0x4c4 SkipTick         : 1
   +0x4c8 MultiThreadSetBusy : 0x1 ''
   +0x4c9 Spare2           : [3]  ""
   +0x4cc ParentNode       : 0x8055d560 _KNODE
   +0x4d0 MultiThreadProcessorSet : 3
   +0x4d4 MultiThreadSetMaster : 0xffdff120 _KPRCB
   +0x4d8 ThreadStartCount : [2] 0
   +0x4e0 CcFastReadNoWait : 0
   +0x4e4 CcFastReadWait   : 0x27f
   +0x4e8 CcFastReadNotPossible : 0
   +0x4ec CcCopyReadNoWait : 6
   +0x4f0 CcCopyReadWait   : 0x344
   +0x4f4 CcCopyReadNoWaitMiss : 1
   +0x4f8 KeAlignmentFixupCount : 0
   +0x4fc KeContextSwitches : 0x2c332
   +0x500 KeDcacheFlushCount : 0
   +0x504 KeExceptionDispatchCount : 0xc4
   +0x508 KeFirstLevelTbFills : 0
   +0x50c KeFloatingEmulationCount : 0
   +0x510 KeIcacheFlushCount : 0
   +0x514 KeSecondLevelTbFills : 0
   +0x518 KeSystemCalls    : 0xd1c9d
   +0x51c SpareCounter0    : [1] 0
   +0x520 PPLookasideList  : [16] _PP_LOOKASIDE_LIST
   +0x5a0 PPNPagedLookasideList : [32] _PP_LOOKASIDE_LIST
   +0x6a0 PPPagedLookasideList : [32] _PP_LOOKASIDE_LIST
   +0x7a0 PacketBarrier    : 0
   +0x7a4 ReverseStall     : 0xce
   +0x7a8 IpiFrame         : 0xf871ea9c Void
   +0x7ac PrcbPad2         : [52]  ""
   +0x7e0 CurrentPacket    : [3] 0x00000001 Void
   +0x7ec TargetSet        : 0
   +0x7f0 WorkerRoutine    : 0x804fc26a     void  nt!KiFlushTargetMultipleTb+0
   +0x7f4 IpiFrozen        : 0x24
   +0x7f8 PrcbPad3         : [40]  ""
   +0x820 RequestSummary   : 0
   +0x824 SignalDone       : (null) 
   +0x828 PrcbPad4         : [56]  ""
   +0x860 DpcListHead      : _LIST_ENTRY [ 0xffdff980 - 0xffdff980 ]
   +0x868 DpcStack         : 0xf88c7000 Void
   +0x86c DpcCount         : 0x29e6
   +0x870 DpcQueueDepth    : 0
   +0x874 DpcRoutineActive : 0
   +0x878 DpcInterruptRequested : 0
   +0x87c DpcLastCount     : 0x29e6
   +0x880 DpcRequestRate   : 0
   +0x884 MaximumDpcQueueDepth : 1
   +0x888 MinimumDpcRate   : 3
   +0x88c QuantumEnd       : 0
   +0x890 PrcbPad5         : [16]  ""
   +0x8a0 DpcLock          : 0
   +0x8a4 PrcbPad6         : [28]  ""
   +0x8c0 CallDpc          : _KDPC
   +0x8e0 ChainedInterruptList : (null) 
   +0x8e4 LookasideIrpFloat : 0n768
   +0x8e8 SpareFields0     : [6] 0
   +0x900 VendorString     : [13]  "GenuineIntel"
   +0x90d InitialApicId    : 0 ''
   +0x90e LogicalProcessorsPerPhysicalProcessor : 0x2 ''
   +0x910 MHz              : 0xd40
   +0x914 FeatureBits      : 0xa0033fff
   +0x918 UpdateSignature  : _LARGE_INTEGER 0x00000017`00000000
   +0x920 NpxSaveArea      : _FX_SAVE_AREA
   +0xb30 PowerState       : _PROCESSOR_POWER_STATE
0: kd> dt nt!_kpcr poi(pkpcr) 这是编程获取的,可以和前面的对比。
   +0x000 NtTib            : _NT_TIB
   +0x01c SelfPcr          : 0xffdff000 _KPCR
   +0x020 Prcb             : 0xffdff120 _KPRCB
   +0x024 Irql             : 0 ''
   +0x028 IRR              : 0
   +0x02c IrrActive        : 0
   +0x030 IDR              : 0xffffffff
   +0x034 KdVersionBlock   : 0x8054e2b8 Void
   +0x038 IDT              : 0x8003f400 _KIDTENTRY
   +0x03c GDT              : 0x8003f000 _KGDTENTRY
   +0x040 TSS              : 0x80042000 _KTSS
   +0x044 MajorVersion     : 1
   +0x046 MinorVersion     : 1
   +0x048 SetMember        : 1
   +0x04c StallScaleFactor : 0xd40
   +0x050 DebugActive      : 0 ''
   +0x051 Number           : 0 ''
   +0x052 Spare0           : 0 ''
   +0x053 SecondLevelCacheAssociativity : 0 ''
   +0x054 VdmAlert         : 0
   +0x058 KernelReserved   : [14] 0
   +0x090 SecondLevelCacheSize : 0
   +0x094 HalReserved      : [16] 0
   +0x0d4 InterruptMode    : 0
   +0x0d8 Spare1           : 0 ''
   +0x0dc KernelReserved2  : [17] 0
   +0x120 PrcbData         : _KPRCB
*/


/*
X64的验证:
0: kd> !pcr 
KPCR for Processor 0 at fffff800019f9d00:
    Major 1 Minor 1
	NtTib.ExceptionList: fffff80001753000
	    NtTib.StackBase: fffff80001754080
	   NtTib.StackLimit: 000000000008e2e8
	 NtTib.SubSystemTib: fffff800019f9d00
	      NtTib.Version: 00000000019f9e80
	  NtTib.UserPointer: fffff800019fa4f0
	      NtTib.SelfTib: 000007fffff9e000

	            SelfPcr: 0000000000000000
	               Prcb: fffff800019f9e80
	               Irql: 0000000000000000
	                IRR: 0000000000000000
	                IDR: 0000000000000000
	      InterruptMode: 0000000000000000
	                IDT: 0000000000000000
	                GDT: 0000000000000000
	                TSS: 0000000000000000

	      CurrentThread: fffffa8018df6680
	         NextThread: 0000000000000000
	         IdleThread: fffff80001a07cc0

	          DpcQueue: 
0: kd> !pcr 0
KPCR for Processor 0 at fffff800019f9d00:
    Major 1 Minor 1
	NtTib.ExceptionList: fffff80001753000
	    NtTib.StackBase: fffff80001754080
	   NtTib.StackLimit: 000000000008e2e8
	 NtTib.SubSystemTib: fffff800019f9d00
	      NtTib.Version: 00000000019f9e80
	  NtTib.UserPointer: fffff800019fa4f0
	      NtTib.SelfTib: 000007fffff9e000

	            SelfPcr: 0000000000000000
	               Prcb: fffff800019f9e80
	               Irql: 0000000000000000
	                IRR: 0000000000000000
	                IDR: 0000000000000000
	      InterruptMode: 0000000000000000
	                IDT: 0000000000000000
	                GDT: 0000000000000000
	                TSS: 0000000000000000

	      CurrentThread: fffffa8018df6680
	         NextThread: 0000000000000000
	         IdleThread: fffff80001a07cc0

	          DpcQueue: 
0: kd> dt nt!_kpcr fffff800019f9d00
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : 0xfffff800`01753000 _KGDTENTRY64
   +0x008 TssBase          : 0xfffff800`01754080 _KTSS64
   +0x010 UserRsp          : 0x8e2e8
   +0x018 Self             : 0xfffff800`019f9d00 _KPCR
   +0x020 CurrentPrcb      : 0xfffff800`019f9e80 _KPRCB
   +0x028 LockArray        : 0xfffff800`019fa4f0 _KSPIN_LOCK_QUEUE
   +0x030 Used_Self        : 0x000007ff`fff9e000 Void
   +0x038 IdtBase          : 0xfffff800`01753080 _KIDTENTRY64
   +0x040 Unused           : [2] 0
   +0x050 Irql             : 0 ''
   +0x051 SecondLevelCacheAssociativity : 0xc ''
   +0x052 ObsoleteNumber   : 0 ''
   +0x053 Fill0            : 0 ''
   +0x054 Unused0          : [3] 0
   +0x060 MajorVersion     : 1
   +0x062 MinorVersion     : 1
   +0x064 StallScaleFactor : 0xd40
   +0x068 Unused1          : [3] (null) 
   +0x080 KernelReserved   : [15] 0
   +0x0bc SecondLevelCacheSize : 0x300000
   +0x0c0 HalReserved      : [16] 0xca332730
   +0x100 Unused2          : 0
   +0x108 KdVersionBlock   : (null) 
   +0x110 Unused3          : (null) 
   +0x118 PcrAlign1        : [24] 0
   +0x180 Prcb             : _KPRCB 注意:从这里开始这里的在结构的定义里面是没有的。也就是说WINDBG命令显示的比结构定义的成员多四个。
0: kd> dt nt!_kprcb fffff800019f9e80
   +0x000 MxCsr            : 0x1f80
   +0x004 LegacyNumber     : 0 ''
   +0x005 ReservedMustBeZero : 0 ''
   +0x006 InterruptRequest : 0 ''
   +0x007 IdleHalt         : 0 ''
   +0x008 CurrentThread    : 0xfffffa80`18df6680 _KTHREAD
   +0x010 NextThread       : (null) 
   +0x018 IdleThread       : 0xfffff800`01a07cc0 _KTHREAD
   +0x020 NestingLevel     : 0 ''
   +0x021 PrcbPad00        : [3]  ""
   +0x024 Number           : 0
   +0x028 RspBase          : 0xfffff880`0231dc70
   +0x030 PrcbLock         : 0
   +0x038 PrcbPad01        : 0
   +0x040 ProcessorState   : _KPROCESSOR_STATE
   +0x5f0 CpuType          : 6 ''
   +0x5f1 CpuID            : 1 ''
   +0x5f2 CpuStep          : 0x3a09
   +0x5f2 CpuStepping      : 0x9 ''
   +0x5f3 CpuModel         : 0x3a ':'
   +0x5f4 MHz              : 0xd40
   +0x5f8 HalReserved      : [8] 0
   +0x638 MinorVersion     : 1
   +0x63a MajorVersion     : 1
   +0x63c BuildType        : 0 ''
   +0x63d CpuVendor        : 0x2 ''
   +0x63e CoresPerPhysicalProcessor : 0x2 ''
   +0x63f LogicalProcessorsPerCore : 0x1 ''
   +0x640 ApicMask         : 0xfffffffe
   +0x644 CFlushSize       : 0x40
   +0x648 AcpiReserved     : (null) 
   +0x650 InitialApicId    : 0
   +0x654 Stride           : 2
   +0x658 Group            : 0
   +0x660 GroupSetMember   : 1
   +0x668 GroupIndex       : 0 ''
   +0x670 LockQueue        : [17] _KSPIN_LOCK_QUEUE
   +0x780 PPLookasideList  : [16] _PP_LOOKASIDE_LIST
   +0x880 PPNPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
   +0x1480 PPPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
   +0x2080 PacketBarrier    : 0n0
   +0x2088 DeferredReadyListHead : _SINGLE_LIST_ENTRY
   +0x2090 MmPageFaultCount : 0n152708
   +0x2094 MmCopyOnWriteCount : 0n3321
   +0x2098 MmTransitionCount : 0n64413
   +0x209c MmDemandZeroCount : 0n64284
   +0x20a0 MmPageReadCount  : 0n46114
   +0x20a4 MmPageReadIoCount : 0n8302
   +0x20a8 MmDirtyPagesWriteCount : 0n0
   +0x20ac MmDirtyWriteIoCount : 0n0
   +0x20b0 MmMappedPagesWriteCount : 0n1
   +0x20b4 MmMappedWriteIoCount : 0n1
   +0x20b8 KeSystemCalls    : 0x1fd8c5
   +0x20bc KeContextSwitches : 0x219d2
   +0x20c0 CcFastReadNoWait : 0
   +0x20c4 CcFastReadWait   : 0x1648
   +0x20c8 CcFastReadNotPossible : 0
   +0x20cc CcCopyReadNoWait : 0
   +0x20d0 CcCopyReadWait   : 0x1865
   +0x20d4 CcCopyReadNoWaitMiss : 0
   +0x20d8 LookasideIrpFloat : 0n2147483647
   +0x20dc IoReadOperationCount : 0n7516
   +0x20e0 IoWriteOperationCount : 0n1741
   +0x20e4 IoOtherOperationCount : 0n70676
   +0x20e8 IoReadTransferCount : _LARGE_INTEGER 0x51033c0
   +0x20f0 IoWriteTransferCount : _LARGE_INTEGER 0x1786f65
   +0x20f8 IoOtherTransferCount : _LARGE_INTEGER 0x1feec4
   +0x2100 TargetCount      : 0n0
   +0x2104 IpiFrozen        : 0x24
   +0x2180 DpcData          : [2] _KDPC_DATA
   +0x21c0 DpcStack         : 0xfffff800`01760fb0 Void
   +0x21c8 MaximumDpcQueueDepth : 0n4
   +0x21cc DpcRequestRate   : 0
   +0x21d0 MinimumDpcRate   : 3
   +0x21d4 DpcLastCount     : 0x55dc
   +0x21d8 ThreadDpcEnable  : 0x1 ''
   +0x21d9 QuantumEnd       : 0 ''
   +0x21da DpcRoutineActive : 0 ''
   +0x21db IdleSchedule     : 0 ''
   +0x21dc DpcRequestSummary : 0n0
   +0x21dc DpcRequestSlot   : [2] 0n0
   +0x21dc NormalDpcState   : 0n0
   +0x21de DpcThreadActive  : 0y0
   +0x21de ThreadDpcState   : 0n0
   +0x21e0 TimerHand        : 0x2eac
   +0x21e4 MasterOffset     : 0n8675
   +0x21e8 LastTick         : 0x2eac
   +0x21ec UnusedPad        : 0
   +0x21f0 PrcbPad50        : [2] 0
   +0x2200 TimerTable       : _KTIMER_TABLE
   +0x4400 DpcGate          : _KGATE
   +0x4418 PrcbPad52        : (null) 
   +0x4420 CallDpc          : _KDPC
   +0x4460 ClockKeepAlive   : 0n1
   +0x4464 ClockCheckSlot   : 0x1 ''
   +0x4465 ClockPollCycle   : 0x53 'S'
   +0x4466 NmiActive        : 0
   +0x4468 DpcWatchdogPeriod : 0n1924
   +0x446c DpcWatchdogCount : 0n1741
   +0x4470 TickOffset       : 0x23f7e
   +0x4478 KeSpinLockOrdering : 0n0
   +0x447c PrcbPad70        : 0
   +0x4480 WaitListHead     : _LIST_ENTRY [ 0xfffffa80`1a996c00 - 0xfffffa80`18df70e0 ]
   +0x4490 WaitLock         : 0
   +0x4498 ReadySummary     : 0x1500
   +0x449c QueueIndex       : 1
   +0x44a0 TimerExpirationDpc : _KDPC
   +0x44e0 PrcbPad72        : [4] 0
   +0x4500 DispatcherReadyListHead : [32] _LIST_ENTRY [ 0xfffff800`019fe380 - 0xfffff800`019fe380 ]
   +0x4700 InterruptCount   : 0x11edc
   +0x4704 KernelTime       : 0x2d4b
   +0x4708 UserTime         : 0x161
   +0x470c DpcTime          : 0x55
   +0x4710 InterruptTime    : 0x213
   +0x4714 AdjustDpcThreshold : 0xc
   +0x4718 DebuggerSavedIRQL : 0 ''
   +0x4719 PrcbPad80        : [7]  ""
   +0x4720 DpcTimeCount     : 0
   +0x4724 DpcTimeLimit     : 0x282
   +0x4728 PeriodicCount    : 0
   +0x472c PeriodicBias     : 0
   +0x4730 AvailableTime    : 0x73
   +0x4734 KeExceptionDispatchCount : 0x58c0
   +0x4738 ParentNode       : 0xfffff800`01a07c00 _KNODE
   +0x4740 StartCycles      : 0x00000096`d20559b7
   +0x4748 PrcbPad82        : [3] 0
   +0x4760 MmSpinLockOrdering : 0n0
   +0x4764 PageColor        : 0x3abb
   +0x4768 NodeColor        : 0
   +0x476c NodeShiftedColor : 0
   +0x4770 SecondaryColorMask : 0x3f
   +0x4774 PrcbPad83        : 0
   +0x4778 CycleTime        : 0x00000015`c0c9fe04
   +0x4780 CcFastMdlReadNoWait : 0
   +0x4784 CcFastMdlReadWait : 0
   +0x4788 CcFastMdlReadNotPossible : 0
   +0x478c CcMapDataNoWait  : 0
   +0x4790 CcMapDataWait    : 0x5ce8
   +0x4794 CcPinMappedDataCount : 0x573
   +0x4798 CcPinReadNoWait  : 0
   +0x479c CcPinReadWait    : 0x160
   +0x47a0 CcMdlReadNoWait  : 0
   +0x47a4 CcMdlReadWait    : 0
   +0x47a8 CcLazyWriteHotSpots : 0x18
   +0x47ac CcLazyWriteIos   : 0xdb
   +0x47b0 CcLazyWritePages : 0x1cb3
   +0x47b4 CcDataFlushes    : 0x1e3
   +0x47b8 CcDataPages      : 0x1ad7
   +0x47bc CcLostDelayedWrites : 0
   +0x47c0 CcFastReadResourceMiss : 0
   +0x47c4 CcCopyReadWaitMiss : 0xc4b
   +0x47c8 CcFastMdlReadResourceMiss : 0
   +0x47cc CcMapDataNoWaitMiss : 0
   +0x47d0 CcMapDataWaitMiss : 0x63e
   +0x47d4 CcPinReadNoWaitMiss : 0
   +0x47d8 CcPinReadWaitMiss : 0x1a
   +0x47dc CcMdlReadNoWaitMiss : 0
   +0x47e0 CcMdlReadWaitMiss : 0
   +0x47e4 CcReadAheadIos   : 0x1ea3
   +0x47e8 MmCacheTransitionCount : 0n0
   +0x47ec MmCacheReadCount : 0n0
   +0x47f0 MmCacheIoCount   : 0n0
   +0x47f4 PrcbPad91        : [1] 0
   +0x47f8 RuntimeAccumulation : 0x6f1b1aaa
   +0x4800 PowerState       : _PROCESSOR_POWER_STATE
   +0x4900 PrcbPad92        : [16]  ""
   +0x4910 KeAlignmentFixupCount : 0
   +0x4918 DpcWatchdogDpc   : _KDPC
   +0x4958 DpcWatchdogTimer : _KTIMER
   +0x4998 Cache            : [5] _CACHE_DESCRIPTOR
   +0x49d4 CacheCount       : 4
   +0x49d8 CachedCommit     : 0x86
   +0x49dc CachedResidentAvailable : 0xf6
   +0x49e0 HyperPte         : 0xfffff880`00800006 Void
   +0x49e8 WheaInfo         : 0xfffffa80`18e31960 Void
   +0x49f0 EtwSupport       : 0xfffffa80`18e4c010 Void
   +0x4a00 InterruptObjectPool : _SLIST_HEADER
   +0x4a10 HypercallPageList : _SLIST_HEADER
   +0x4a20 HypercallPageVirtual : 0xfffff880`02d53000 Void
   +0x4a28 VirtualApicAssist : 0xfffff880`02d51000 Void
   +0x4a30 StatisticsPage   : 0xfffff880`02d52000  -> 0x00900001`00000002
   +0x4a38 RateControl      : (null) 
   +0x4a40 CacheProcessorMask : [5] 3
   +0x4a68 PackageProcessorSet : _KAFFINITY_EX
   +0x4a90 CoreProcessorSet : 1
   +0x4a98 PebsIndexAddress : 0xfffff800`019fe918 Void
   +0x4aa0 PrcbPad93        : [12] 0
   +0x4b00 SpinLockAcquireCount : 0x5310d6
   +0x4b04 SpinLockContentionCount : 0xa1e
   +0x4b08 SpinLockSpinCount : 0x392b847a
   +0x4b0c IpiSendRequestBroadcastCount : 0x21e2
   +0x4b10 IpiSendRequestRoutineCount : 0x21f
   +0x4b14 IpiSendSoftwareInterruptCount : 0x5f77
   +0x4b18 ExInitializeResourceCount : 0x3497
   +0x4b1c ExReInitializeResourceCount : 0x47c
   +0x4b20 ExDeleteResourceCount : 0x2843
   +0x4b24 ExecutiveResourceAcquiresCount : 0x1861cb
   +0x4b28 ExecutiveResourceContentionsCount : 0x66e
   +0x4b2c ExecutiveResourceReleaseExclusiveCount : 0x367ca
   +0x4b30 ExecutiveResourceReleaseSharedCount : 0x14fa15
   +0x4b34 ExecutiveResourceConvertsCount : 0x305
   +0x4b38 ExAcqResExclusiveAttempts : 0x35d51
   +0x4b3c ExAcqResExclusiveAcquiresExclusive : 0x30a5d
   +0x4b40 ExAcqResExclusiveAcquiresExclusiveRecursive : 0x52d2
   +0x4b44 ExAcqResExclusiveWaits : 0x5b6
   +0x4b48 ExAcqResExclusiveNotAcquires : 0x22
   +0x4b4c ExAcqResSharedAttempts : 0x143531
   +0x4b50 ExAcqResSharedAcquiresExclusive : 0x1008
   +0x4b54 ExAcqResSharedAcquiresShared : 0x13f9db
   +0x4b58 ExAcqResSharedAcquiresSharedRecursive : 0x2b4b
   +0x4b5c ExAcqResSharedWaits : 0xb8
   +0x4b60 ExAcqResSharedNotAcquires : 3
   +0x4b64 ExAcqResSharedStarveExclusiveAttempts : 0xcf6f
   +0x4b68 ExAcqResSharedStarveExclusiveAcquiresExclusive : 1
   +0x4b6c ExAcqResSharedStarveExclusiveAcquiresShared : 0xceff
   +0x4b70 ExAcqResSharedStarveExclusiveAcquiresSharedRecursive : 0x6f
   +0x4b74 ExAcqResSharedStarveExclusiveWaits : 0
   +0x4b78 ExAcqResSharedStarveExclusiveNotAcquires : 0
   +0x4b7c ExAcqResSharedWaitForExclusiveAttempts : 0
   +0x4b80 ExAcqResSharedWaitForExclusiveAcquiresExclusive : 0
   +0x4b84 ExAcqResSharedWaitForExclusiveAcquiresShared : 0
   +0x4b88 ExAcqResSharedWaitForExclusiveAcquiresSharedRecursive : 0
   +0x4b8c ExAcqResSharedWaitForExclusiveWaits : 0
   +0x4b90 ExAcqResSharedWaitForExclusiveNotAcquires : 0
   +0x4b94 ExSetResOwnerPointerExclusive : 0
   +0x4b98 ExSetResOwnerPointerSharedNew : 0x298
   +0x4b9c ExSetResOwnerPointerSharedOld : 0xba
   +0x4ba0 ExTryToAcqExclusiveAttempts : 0
   +0x4ba4 ExTryToAcqExclusiveAcquires : 0
   +0x4ba8 ExBoostExclusiveOwner : 0xe
   +0x4bac ExBoostSharedOwners : 0
   +0x4bb0 ExEtwSynchTrackingNotificationsCount : 0
   +0x4bb4 ExEtwSynchTrackingNotificationsAccountedCount : 0
   +0x4bb8 VendorString     : [13]  "GenuineIntel"
   +0x4bc5 PrcbPad10        : [3]  ""
   +0x4bc8 FeatureBits      : 0x21193dfe
   +0x4bd0 UpdateSignature  : _LARGE_INTEGER 0x00000017`00000000
   +0x4bd8 Context          : 0xfffff880`009c6340 _CONTEXT
   +0x4be0 ContextFlags     : 0x10004b
   +0x4be8 ExtendedState    : 0xfffff880`009c6000 _XSAVE_AREA
   +0x4c00 Mailbox          : (null) 
   +0x4c80 RequestMailbox   : [1] _REQUEST_MAILBOX
0: kd> dt nt!_kpcr poi(pkpcr) 这是编程获取的,可以和前面的对比。
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : 0xfffff800`01753000 _KGDTENTRY64
   +0x008 TssBase          : 0xfffff800`01754080 _KTSS64
   +0x010 UserRsp          : 0x8e2e8
   +0x018 Self             : 0xfffff800`019f9d00 _KPCR
   +0x020 CurrentPrcb      : 0xfffff800`019f9e80 _KPRCB
   +0x028 LockArray        : 0xfffff800`019fa4f0 _KSPIN_LOCK_QUEUE
   +0x030 Used_Self        : 0x000007ff`fff9e000 Void
   +0x038 IdtBase          : 0xfffff800`01753080 _KIDTENTRY64
   +0x040 Unused           : [2] 0
   +0x050 Irql             : 0 ''
   +0x051 SecondLevelCacheAssociativity : 0xc ''
   +0x052 ObsoleteNumber   : 0 ''
   +0x053 Fill0            : 0 ''
   +0x054 Unused0          : [3] 0
   +0x060 MajorVersion     : 1
   +0x062 MinorVersion     : 1
   +0x064 StallScaleFactor : 0xd40
   +0x068 Unused1          : [3] (null) 
   +0x080 KernelReserved   : [15] 0
   +0x0bc SecondLevelCacheSize : 0x300000
   +0x0c0 HalReserved      : [16] 0xca332730
   +0x100 Unused2          : 0
   +0x108 KdVersionBlock   : (null) 
   +0x110 Unused3          : (null) 
   +0x118 PcrAlign1        : [24] 0
   +0x180 Prcb             : _KPRCB
*/

2014年8月20日星期三

内核中的CPUID

#include <ntifs.h>
#include <windef.h>
#include <intrin.h> //VS2012编译。
//#include <mmintrin.h> //WDK 编译。
//#include <emmintrin.h>//WDK 编译。
//#include <xmmintrin.h>//WDK 编译。


/*
标题:内核中的CPUID。

CPUID这个指令很有用,如查看:CPU微码,虚拟化等。
注意很旧的CPU不支持:http://correy.webs.com/articles/computer/asm/cpu_microcode.asm.txt
如此重要的指令,微软岂能不支持。
尽管X64不支持内联汇编,但也有替代的办法对付此特别有用的指令。
如:http://msdn.microsoft.com/en-us/library/26td21ds.aspx

本文参考:http://msdn.microsoft.com/zh-cn/library/hskdteyh(v=vs.110).aspx
在X86和X64的Windows系统上测试通过。

made by correy
made at 2014.08.20
homepage:http://correy.webs.com
*/


//自己添加。
#define bool BOOL 
#define false FALSE 
#define printf_s DbgPrint


const char* szFeatures[] =
{
    "x87 FPU On Chip",
    "Virtual-8086 Mode Enhancement",
    "Debugging Extensions",
    "Page Size Extensions",
    "Time Stamp Counter",
    "RDMSR and WRMSR Support",
    "Physical Address Extensions",
    "Machine Check Exception",
    "CMPXCHG8B Instruction",
    "APIC On Chip",
    "Unknown1",
    "SYSENTER and SYSEXIT",
    "Memory Type Range Registers",
    "PTE Global Bit",
    "Machine Check Architecture",
    "Conditional Move/Compare Instruction",
    "Page Attribute Table",
    "36-bit Page Size Extension",
    "Processor Serial Number",
    "CFLUSH Extension",
    "Unknown2",
    "Debug Store",
    "Thermal Monitor and Clock Ctrl",
    "MMX Technology",
    "FXSAVE/FXRSTOR",
    "SSE Extensions",
    "SSE2 Extensions",
    "Self Snoop",
    "Multithreading Technology",
    "Thermal Monitor",
    "Unknown4",
    "Pending Break Enable"
};


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;  

    char CPUString[0x20];
    char CPUBrandString[0x40];
    int CPUInfo[4] = {-1};
    int nSteppingID = 0;
    int nModel = 0;
    int nFamily = 0;
    int nProcessorType = 0;
    int nExtendedmodel = 0;
    int nExtendedfamily = 0;
    int nBrandIndex = 0;
    int nCLFLUSHcachelinesize = 0;
    int nLogicalProcessors = 0;
    int nAPICPhysicalID = 0;
    int nFeatureInfo = 0;
    int nCacheLineSize = 0;
    int nL2Associativity = 0;
    int nCacheSizeK = 0;
    int nPhysicalAddress = 0;
    int nVirtualAddress = 0;
    int nRet = 0;

    int nCores = 0;
    int nCacheType = 0;
    int nCacheLevel = 0;
    int nMaxThread = 0;
    int nSysLineSize = 0;
    int nPhysicalLinePartitions = 0;
    int nWaysAssociativity = 0;
    int nNumberSets = 0;

    unsigned    nIds, nExIds, i;

    bool    bSSE3Instructions = false;
    bool    bMONITOR_MWAIT = false;
    bool    bCPLQualifiedDebugStore = false;
    bool    bVirtualMachineExtensions = false;
    bool    bEnhancedIntelSpeedStepTechnology = false;
    bool    bThermalMonitor2 = false;
    bool    bSupplementalSSE3 = false;
    bool    bL1ContextID = false;
    bool    bCMPXCHG16B = false;
    bool    bxTPRUpdateControl = false;
    bool    bPerfDebugCapabilityMSR = false;
    bool    bSSE41Extensions = false;
    bool    bSSE42Extensions = false;
    bool    bPOPCNT = false;

    bool    bMultithreading = false;

    bool    bLAHF_SAHFAvailable = false;
    bool    bCmpLegacy = false;
    bool    bSVM = false;
    bool    bExtApicSpace = false;
    bool    bAltMovCr8 = false;
    bool    bLZCNT = false;
    bool    bSSE4A = false;
    bool    bMisalignedSSE = false;
    bool    bPREFETCH = false;
    bool    bSKINITandDEV = false;
    bool    bSYSCALL_SYSRETAvailable = false;
    bool    bExecuteDisableBitAvailable = false;
    bool    bMMXExtensions = false;
    bool    bFFXSR = false;
    bool    b1GBSupport = false;
    bool    bRDTSCP = false;
    bool    b64Available = false;
    bool    b3DNowExt = false;
    bool    b3DNow = false;
    bool    bNestedPaging = false;
    bool    bLBRVisualization = false;
    bool    bFP128 = false;
    bool    bMOVOptimization = false;

    bool    bSelfInit = false;
    bool    bFullyAssociative = false;

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;

    // __cpuid with an InfoType argument of 0 returns the number of valid Ids in CPUInfo[0] and the CPU identification string in the other three array elements.
    // The CPU identification string is not in linear order. The code below arranges the information in a human readable form.

    /*
    这行指令对应的汇编代码为:
    X86:
    180 test.c               f88902e5 8d758c          lea     esi,[ebp-74h]
    180 test.c               f88902e8 33c0            xor     eax,eax
    180 test.c               f88902ea 33c9            xor     ecx,ecx
    180 test.c               f88902ec 0fa2            cpuid
    180 test.c               f88902ee 8906            mov     dword ptr [esi],eax
    180 test.c               f88902f0 895e04          mov     dword ptr [esi+4],ebx
    180 test.c               f88902f3 894e08          mov     dword ptr [esi+8],ecx
    180 test.c               f88902f6 89560c          mov     dword ptr [esi+0Ch],edx

    X64:
    167 test.c               fffff880`06b79300 33c0            xor     eax,eax
    167 test.c               fffff880`06b79302 33c9            xor     ecx,ecx
    167 test.c               fffff880`06b79304 0fa2            cpuid
    167 test.c               fffff880`06b79306 488dbc24d0010000 lea     rdi,[rsp+1D0h]
    167 test.c               fffff880`06b7930e 8907            mov     dword ptr [rdi],eax
    167 test.c               fffff880`06b79310 895f04          mov     dword ptr [rdi+4],ebx
    167 test.c               fffff880`06b79313 894f08          mov     dword ptr [rdi+8],ecx
    167 test.c               fffff880`06b79316 89570c          mov     dword ptr [rdi+0Ch],edx
    */
    __cpuid(CPUInfo, 0);
    nIds = CPUInfo[0];
    memset(CPUString, 0, sizeof(CPUString));
    *((int*)CPUString) = CPUInfo[1];
    *((int*)(CPUString+4)) = CPUInfo[3];
    *((int*)(CPUString+8)) = CPUInfo[2];

    // Get the information associated with each valid Id
    for (i=0; i<=nIds; ++i)
    {
        __cpuid(CPUInfo, i);
        printf_s("\nFor InfoType %d\n", i); 
        printf_s("CPUInfo[0] = 0x%x\n", CPUInfo[0]);
        printf_s("CPUInfo[1] = 0x%x\n", CPUInfo[1]);
        printf_s("CPUInfo[2] = 0x%x\n", CPUInfo[2]);
        printf_s("CPUInfo[3] = 0x%x\n", CPUInfo[3]);

        // Interpret CPU feature information.
        if  (i == 1)
        {
            nSteppingID = CPUInfo[0] & 0xf;
            nModel = (CPUInfo[0] >> 4) & 0xf;
            nFamily = (CPUInfo[0] >> 8) & 0xf;
            nProcessorType = (CPUInfo[0] >> 12) & 0x3;
            nExtendedmodel = (CPUInfo[0] >> 16) & 0xf;
            nExtendedfamily = (CPUInfo[0] >> 20) & 0xff;
            nBrandIndex = CPUInfo[1] & 0xff;
            nCLFLUSHcachelinesize = ((CPUInfo[1] >> 8) & 0xff) * 8;
            nLogicalProcessors = ((CPUInfo[1] >> 16) & 0xff);
            nAPICPhysicalID = (CPUInfo[1] >> 24) & 0xff;
            bSSE3Instructions = (CPUInfo[2] & 0x1) || false;
            bMONITOR_MWAIT = (CPUInfo[2] & 0x8) || false;
            bCPLQualifiedDebugStore = (CPUInfo[2] & 0x10) || false;
            bVirtualMachineExtensions = (CPUInfo[2] & 0x20) || false;
            bEnhancedIntelSpeedStepTechnology = (CPUInfo[2] & 0x80) || false;
            bThermalMonitor2 = (CPUInfo[2] & 0x100) || false;
            bSupplementalSSE3 = (CPUInfo[2] & 0x200) || false;
            bL1ContextID = (CPUInfo[2] & 0x300) || false;
            bCMPXCHG16B= (CPUInfo[2] & 0x2000) || false;
            bxTPRUpdateControl = (CPUInfo[2] & 0x4000) || false;
            bPerfDebugCapabilityMSR = (CPUInfo[2] & 0x8000) || false;
            bSSE41Extensions = (CPUInfo[2] & 0x80000) || false;
            bSSE42Extensions = (CPUInfo[2] & 0x100000) || false;
            bPOPCNT= (CPUInfo[2] & 0x800000) || false;
            nFeatureInfo = CPUInfo[3];
            bMultithreading = (nFeatureInfo & (1 << 28)) || false;
        }
    }

    // Calling __cpuid with 0x80000000 as the InfoType argument
    // gets the number of valid extended IDs.
    __cpuid(CPUInfo, 0x80000000);
    nExIds = CPUInfo[0];
    memset(CPUBrandString, 0, sizeof(CPUBrandString));

    // Get the information associated with each extended ID.
    for (i=0x80000000; i<=nExIds; ++i)
    {
        __cpuid(CPUInfo, i);
        printf_s("\nFor InfoType %x\n", i); 
        printf_s("CPUInfo[0] = 0x%x\n", CPUInfo[0]);
        printf_s("CPUInfo[1] = 0x%x\n", CPUInfo[1]);
        printf_s("CPUInfo[2] = 0x%x\n", CPUInfo[2]);
        printf_s("CPUInfo[3] = 0x%x\n", CPUInfo[3]);

        if  (i == 0x80000001)
        {
            bLAHF_SAHFAvailable = (CPUInfo[2] & 0x1) || false;
            bCmpLegacy = (CPUInfo[2] & 0x2) || false;
            bSVM = (CPUInfo[2] & 0x4) || false;
            bExtApicSpace = (CPUInfo[2] & 0x8) || false;
            bAltMovCr8 = (CPUInfo[2] & 0x10) || false;
            bLZCNT = (CPUInfo[2] & 0x20) || false;
            bSSE4A = (CPUInfo[2] & 0x40) || false;
            bMisalignedSSE = (CPUInfo[2] & 0x80) || false;
            bPREFETCH = (CPUInfo[2] & 0x100) || false;
            bSKINITandDEV = (CPUInfo[2] & 0x1000) || false;
            bSYSCALL_SYSRETAvailable = (CPUInfo[3] & 0x800) || false;
            bExecuteDisableBitAvailable = (CPUInfo[3] & 0x10000) || false;
            bMMXExtensions = (CPUInfo[3] & 0x40000) || false;
            bFFXSR = (CPUInfo[3] & 0x200000) || false;
            b1GBSupport = (CPUInfo[3] & 0x400000) || false;
            bRDTSCP = (CPUInfo[3] & 0x8000000) || false;
            b64Available = (CPUInfo[3] & 0x20000000) || false;
            b3DNowExt = (CPUInfo[3] & 0x40000000) || false;
            b3DNow = (CPUInfo[3] & 0x80000000) || false;
        }

        // Interpret CPU brand string and cache information.
        if  (i == 0x80000002)
            memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
        else if  (i == 0x80000003)
            memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
        else if  (i == 0x80000004)
            memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
        else if  (i == 0x80000006)
        {
            nCacheLineSize = CPUInfo[2] & 0xff;
            nL2Associativity = (CPUInfo[2] >> 12) & 0xf;
            nCacheSizeK = (CPUInfo[2] >> 16) & 0xffff;
        }
        else if  (i == 0x80000008)
        {
           nPhysicalAddress = CPUInfo[0] & 0xff;
           nVirtualAddress = (CPUInfo[0] >> 8) & 0xff;
        }
        else if  (i == 0x8000000A)
        {
            bNestedPaging = (CPUInfo[3] & 0x1) || false;
            bLBRVisualization = (CPUInfo[3] & 0x2) || false;
        }
        else if  (i == 0x8000001A)
        {
            bFP128 = (CPUInfo[0] & 0x1) || false;
            bMOVOptimization = (CPUInfo[0] & 0x2) || false;
        }
    }

    // Display all the information in user-friendly format.

    printf_s("\n\nCPU String: %s\n", CPUString);

    if  (nIds >= 1)
    {
        if  (nSteppingID)
            printf_s("Stepping ID = %d\n", nSteppingID);
        if  (nModel)
            printf_s("Model = %d\n", nModel);
        if  (nFamily)
            printf_s("Family = %d\n", nFamily);
        if  (nProcessorType)
            printf_s("Processor Type = %d\n", nProcessorType);
        if  (nExtendedmodel)
            printf_s("Extended model = %d\n", nExtendedmodel);
        if  (nExtendedfamily)
            printf_s("Extended family = %d\n", nExtendedfamily);
        if  (nBrandIndex)
            printf_s("Brand Index = %d\n", nBrandIndex);
        if  (nCLFLUSHcachelinesize)
            printf_s("CLFLUSH cache line size = %d\n", nCLFLUSHcachelinesize);
        if (bMultithreading && (nLogicalProcessors > 0))
           printf_s("Logical Processor Count = %d\n", nLogicalProcessors);
        if  (nAPICPhysicalID)
            printf_s("APIC Physical ID = %d\n", nAPICPhysicalID);

        if  (nFeatureInfo || bSSE3Instructions ||
             bMONITOR_MWAIT || bCPLQualifiedDebugStore ||
             bVirtualMachineExtensions || bEnhancedIntelSpeedStepTechnology ||
             bThermalMonitor2 || bSupplementalSSE3 || bL1ContextID || 
             bCMPXCHG16B || bxTPRUpdateControl || bPerfDebugCapabilityMSR || 
             bSSE41Extensions || bSSE42Extensions || bPOPCNT || 
             bLAHF_SAHFAvailable || bCmpLegacy || bSVM ||
             bExtApicSpace || bAltMovCr8 ||
             bLZCNT || bSSE4A || bMisalignedSSE ||
             bPREFETCH || bSKINITandDEV || bSYSCALL_SYSRETAvailable || 
             bExecuteDisableBitAvailable || bMMXExtensions || bFFXSR || b1GBSupport ||
             bRDTSCP || b64Available || b3DNowExt || b3DNow || bNestedPaging || 
             bLBRVisualization || bFP128 || bMOVOptimization )
        {
            printf_s("\nThe following features are supported:\n");

            if  (bSSE3Instructions)
                printf_s("\tSSE3\n");
            if  (bMONITOR_MWAIT)
                printf_s("\tMONITOR/MWAIT\n");
            if  (bCPLQualifiedDebugStore)
                printf_s("\tCPL Qualified Debug Store\n");
            if  (bVirtualMachineExtensions)
                printf_s("\tVirtual Machine Extensions\n");
            if  (bEnhancedIntelSpeedStepTechnology)
                printf_s("\tEnhanced Intel SpeedStep Technology\n");
            if  (bThermalMonitor2)
                printf_s("\tThermal Monitor 2\n");
            if  (bSupplementalSSE3)
                printf_s("\tSupplemental Streaming SIMD Extensions 3\n");
            if  (bL1ContextID)
                printf_s("\tL1 Context ID\n");
            if  (bCMPXCHG16B)
                printf_s("\tCMPXCHG16B Instruction\n");
            if  (bxTPRUpdateControl)
                printf_s("\txTPR Update Control\n");
            if  (bPerfDebugCapabilityMSR)
                printf_s("\tPerf\\Debug Capability MSR\n");
            if  (bSSE41Extensions)
                printf_s("\tSSE4.1 Extensions\n");
            if  (bSSE42Extensions)
                printf_s("\tSSE4.2 Extensions\n");
            if  (bPOPCNT)
                printf_s("\tPPOPCNT Instruction\n");

            i = 0;
            nIds = 1;
            while (i < (sizeof(szFeatures)/sizeof(const char*)))
            {
                if  (nFeatureInfo & nIds)
                {
                    printf_s("\t");
                    printf_s(szFeatures[i]);
                    printf_s("\n");
                }

                nIds <<= 1;
                ++i;
            }
            if (bLAHF_SAHFAvailable)
                printf_s("\tLAHF/SAHF in 64-bit mode\n");
            if (bCmpLegacy)
                printf_s("\tCore multi-processing legacy mode\n");
            if (bSVM)
                printf_s("\tSecure Virtual Machine\n");
            if (bExtApicSpace)
                printf_s("\tExtended APIC Register Space\n");
            if (bAltMovCr8)
                printf_s("\tAltMovCr8\n");
            if (bLZCNT)
                printf_s("\tLZCNT instruction\n");
            if (bSSE4A)
                printf_s("\tSSE4A (EXTRQ, INSERTQ, MOVNTSD, MOVNTSS)\n");
            if (bMisalignedSSE)
                printf_s("\tMisaligned SSE mode\n");
            if (bPREFETCH)
                printf_s("\tPREFETCH and PREFETCHW Instructions\n");
            if (bSKINITandDEV)
                printf_s("\tSKINIT and DEV support\n");
            if (bSYSCALL_SYSRETAvailable)
                printf_s("\tSYSCALL/SYSRET in 64-bit mode\n");
            if (bExecuteDisableBitAvailable)
                printf_s("\tExecute Disable Bit\n");
            if (bMMXExtensions)
                printf_s("\tExtensions to MMX Instructions\n");
            if (bFFXSR)
                printf_s("\tFFXSR\n");
            if (b1GBSupport)
                printf_s("\t1GB page support\n");
            if (bRDTSCP)
                printf_s("\tRDTSCP instruction\n");
            if (b64Available)
                printf_s("\t64 bit Technology\n");
            if (b3DNowExt)
                printf_s("\t3Dnow Ext\n");
            if (b3DNow)
                printf_s("\t3Dnow! instructions\n");
            if (bNestedPaging)
                printf_s("\tNested Paging\n");
            if (bLBRVisualization)
                printf_s("\tLBR Visualization\n");
            if (bFP128)
                printf_s("\tFP128 optimization\n");
            if (bMOVOptimization)
                printf_s("\tMOVU Optimization\n");
        }
    }

    if  (nExIds >= 0x80000004)
        printf_s("\nCPU Brand String: %s\n", CPUBrandString);

    if  (nExIds >= 0x80000006)
    {
        printf_s("Cache Line Size = %d\n", nCacheLineSize);
        printf_s("L2 Associativity = %d\n", nL2Associativity);
        printf_s("Cache Size = %dK\n", nCacheSizeK);
    }


    for (i=0;;i++)
    {
        __cpuidex(CPUInfo, 0x4, i);
        if(!(CPUInfo[0] & 0xf0)) break;

        if(i == 0)
        {
            nCores = CPUInfo[0] >> 26;
            printf_s("\n\nNumber of Cores = %d\n", nCores + 1);
        }

        nCacheType = (CPUInfo[0] & 0x1f);
        nCacheLevel = (CPUInfo[0] & 0xe0) >> 5;
        bSelfInit = (CPUInfo[0] & 0x100) >> 8;
        bFullyAssociative = (CPUInfo[0] & 0x200) >> 9;
        nMaxThread = (CPUInfo[0] & 0x03ffc000) >> 14;
        nSysLineSize = (CPUInfo[1] & 0x0fff);
        nPhysicalLinePartitions = (CPUInfo[1] & 0x03ff000) >> 12;
        nWaysAssociativity = (CPUInfo[1]) >> 22;
        nNumberSets = CPUInfo[2];

        printf_s("\n");

        printf_s("ECX Index %d\n", i);
        switch (nCacheType)
        {
            case 0:
                printf_s("   Type: Null\n");
                break;
            case 1:
                printf_s("   Type: Data Cache\n");
                break;
            case 2:
                printf_s("   Type: Instruction Cache\n");
                break;
            case 3:
                printf_s("   Type: Unified Cache\n");
                break;
            default:
                 printf_s("   Type: Unknown\n");
        }

        printf_s("   Level = %d\n", nCacheLevel + 1); 
        if (bSelfInit)
        {
            printf_s("   Self Initializing\n");
        }
        else
        {
            printf_s("   Not Self Initializing\n");
        }
        if (bFullyAssociative)
        {
            printf_s("   Is Fully Associatve\n");
        }
        else
        {
            printf_s("   Is Not Fully Associatve\n");
        }
        printf_s("   Max Threads = %d\n", nMaxThread+1);
        printf_s("   System Line Size = %d\n", nSysLineSize+1);
        printf_s("   Physical Line Partions = %d\n", nPhysicalLinePartitions+1);
        printf_s("   Ways of Associativity = %d\n", nWaysAssociativity+1);
        printf_s("   Number of Sets = %d\n", nNumberSets+1);
    }    

    return status;    
} 

2014年8月18日星期一

内核中的GetProcAddress

#include <ntifs.h>
#include <windef.h>


/*
文件名:MiFindExportedRoutineByName.c

写作目的:
MmGetSystemRoutineAddress这个函数有如下的限制:
It can only be used for routines exported by the kernel or HAL, not for any driver-defined routine. 

有时候获取别的内核模块的函数的地址是一个解决问题的办法,如:WINHV.sys。
有人为此还专门写了函数,当然是解析PE32/PE32+了。

其实系统已经提供了一些函数,只不过导出而没有公开而已。

看WRK知道:MmGetSystemRoutineAddress是通过MiFindExportedRoutineByName实现的。
可是:MiFindExportedRoutineByName没有导出,定位又没有好的稳定的办法。
所以自己实现,还好RtlImageDirectoryEntryToData(RtlImageNtHeader)已经导出。

本文的一些信息摘自:WRK。
不过这也是源码,加入驱动也是可以使用的。

made by correy
made at 2014.08.18
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory


/*
这个应该兼容PE32+也就是64位的系统。
有待看PE32规范。
*/
typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


PVOID RtlImageDirectoryEntryToData (IN PVOID Base, IN BOOLEAN MappedAsImage, IN USHORT DirectoryEntry, OUT PULONG Size);
/*++
Routine Description:
    This function locates a Directory Entry within the image header and returns either the virtual address or seek address of the data the Directory describes.
Arguments:
    Base - Supplies the base of the image or data file.
    MappedAsImage - FALSE if the file is mapped as a data file.
                  - TRUE if the file is mapped as an image.
    DirectoryEntry - Supplies the directory entry to locate.
    Size - Return the size of the directory.
Return Value:
    NULL - The file does not contain data for the specified directory entry.
    NON-NULL - Returns the address of the raw data the directory describes.
--*/
//{
//    PIMAGE_NT_HEADERS NtHeaders;
//
//    if (LDR_IS_DATAFILE(Base)) {
//        Base = LDR_DATAFILE_TO_VIEW(Base);
//        MappedAsImage = FALSE;
//    }
//
//    NtHeaders = RtlImageNtHeader(Base);
//
//    if (!NtHeaders)
//        return NULL;
//
//    if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//        return (RtlpImageDirectoryEntryToData32(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS32)NtHeaders));
//    } else if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//        return (RtlpImageDirectoryEntryToData64(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS64)NtHeaders));
//    } else {
//        return (NULL);
//    }
//}


PVOID MiFindExportedRoutineByName (IN PVOID DllBase, IN PANSI_STRING AnsiImageRoutineName)
/*++
Routine Description:
    This function searches the argument module looking for the requested exported function name.
Arguments:
    DllBase - Supplies the base address of the requested module.
    AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
Return Value:
    The virtual address of the requested routine or NULL if not found.
--*/
{
    USHORT OrdinalNumber;
    PULONG NameTableBase;
    PUSHORT NameOrdinalTableBase;
    PULONG Addr;
    LONG High;
    LONG Low;
    LONG Middle;
    LONG Result;
    ULONG ExportSize;
    PVOID FunctionAddress;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;

    PAGED_CODE();

    ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize);
    if (ExportDirectory == NULL) {
        return NULL;
    }
    
    NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);// Initialize the pointer to the array of RVA-based ansi export strings.    
    NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);// Initialize the pointer to the array of USHORT ordinal numbers.    
    Low = 0;
    Middle = 0;
    High = ExportDirectory->NumberOfNames - 1;

    while (High >= Low) // Lookup the desired name in the name table using a binary search.
    {        
        Middle = (Low + High) >> 1;// Compute the next probe index and compare the import name with the export name entry.
        Result = strcmp (AnsiImageRoutineName->Buffer, (PCHAR)DllBase + NameTableBase[Middle]);
        if (Result < 0) {
            High = Middle - 1;
        } else if (Result > 0) {
            Low = Middle + 1;
        } else {
            break;
        }
    }

    // If the high index is less than the low index, then a matching table entry was not found.
    // Otherwise, get the ordinal number from the ordinal table.
    if (High < Low) {
        return NULL;
    }

    OrdinalNumber = NameOrdinalTableBase[Middle];

    // If the OrdinalNumber is not within the Export Address Table,then this image does not implement the function.
    // Return not found.
    if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
        return NULL;
    }

    // Index into the array of RVA export addresses by ordinal number.
    Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
    FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);

    // Forwarders are not used by the kernel and HAL to each other.
    ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) || (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));

    return FunctionAddress;
}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;    

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;    

    //测试的代码就不写了。

    return status;    
} 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

#include <ntifs.h>
#include <windef.h>
#include <Aux_klib.h> //source里面要有TARGETLIBS=$(DDK_LIB_PATH)\Aux_klib.lib


/*
made by correy
made at 2014.08.19
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


#define TAG  'tset' //test

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


PVOID RtlImageDirectoryEntryToData (IN PVOID Base, IN BOOLEAN MappedAsImage, IN USHORT DirectoryEntry, OUT PULONG Size);
/*++
Routine Description:
    This function locates a Directory Entry within the image header and returns either the virtual address or seek address of the data the Directory describes.
Arguments:
    Base - Supplies the base of the image or data file.
    MappedAsImage - FALSE if the file is mapped as a data file.
                  - TRUE if the file is mapped as an image.
    DirectoryEntry - Supplies the directory entry to locate.
    Size - Return the size of the directory.
Return Value:
    NULL - The file does not contain data for the specified directory entry.
    NON-NULL - Returns the address of the raw data the directory describes.
--*/
//{
//    PIMAGE_NT_HEADERS NtHeaders;
//
//    if (LDR_IS_DATAFILE(Base)) {
//        Base = LDR_DATAFILE_TO_VIEW(Base);
//        MappedAsImage = FALSE;
//    }
//
//    NtHeaders = RtlImageNtHeader(Base);
//
//    if (!NtHeaders)
//        return NULL;
//
//    if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//        return (RtlpImageDirectoryEntryToData32(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS32)NtHeaders));
//    } else if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//        return (RtlpImageDirectoryEntryToData64(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS64)NtHeaders));
//    } else {
//        return (NULL);
//    }
//}


PVOID MiFindExportedRoutineByName (IN PVOID DllBase, IN PANSI_STRING AnsiImageRoutineName)
/*++
Routine Description:
    This function searches the argument module looking for the requested exported function name.
Arguments:
    DllBase - Supplies the base address of the requested module.
    AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
Return Value:
    The virtual address of the requested routine or NULL if not found.
--*/
{
    USHORT OrdinalNumber;
    PULONG NameTableBase;
    PUSHORT NameOrdinalTableBase;
    PULONG Addr;
    LONG High;
    LONG Low;
    LONG Middle;
    LONG Result;
    ULONG ExportSize;
    PVOID FunctionAddress = 0;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;

    PAGED_CODE();

    __try
    {
        FunctionAddress = *(PVOID *)DllBase;
        FunctionAddress = 0;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return FunctionAddress;
    }  

    //确保DllBase可以访问。否则蓝屏。
    ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize);
    if (ExportDirectory == NULL) {
        return NULL;
    }
    
    NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);// Initialize the pointer to the array of RVA-based ansi export strings.    
    NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);// Initialize the pointer to the array of USHORT ordinal numbers.    
    Low = 0;
    Middle = 0;
    High = ExportDirectory->NumberOfNames - 1;

    while (High >= Low) // Lookup the desired name in the name table using a binary search.
    {        
        Middle = (Low + High) >> 1;// Compute the next probe index and compare the import name with the export name entry.
        Result = strcmp (AnsiImageRoutineName->Buffer, (PCHAR)DllBase + NameTableBase[Middle]);
        if (Result < 0) {
            High = Middle - 1;
        } else if (Result > 0) {
            Low = Middle + 1;
        } else {
            break;
        }
    }

    // If the high index is less than the low index, then a matching table entry was not found.
    // Otherwise, get the ordinal number from the ordinal table.
    if (High < Low) {
        return NULL;
    }

    OrdinalNumber = NameOrdinalTableBase[Middle];

    // If the OrdinalNumber is not within the Export Address Table,then this image does not implement the function.
    // Return not found.
    if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
        return NULL;
    }

    // Index into the array of RVA export addresses by ordinal number.
    Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
    FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);

    // Forwarders are not used by the kernel and HAL to each other.
    ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) || (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));

    return FunctionAddress;
}


PVOID get_module_image_base(__in UCHAR * name)
{
    NTSTATUS status = 0;
    ULONG  modulesSize;
    AUX_MODULE_EXTENDED_INFO * modules;
    ULONG  numberOfModules;
    ULONG i;
    PIMAGE_EXPORT_DIRECTORY pied = 0;
    PVOID ImageBase = 0;

    status = AuxKlibInitialize();
    if (!NT_SUCCESS( status )) 
    {
        KdPrint(( "AuxKlibInitialize fail %d\n", status));
        return ImageBase;
    }    
    
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), NULL);// Get the required array size.
    if (!NT_SUCCESS(status) || modulesSize == 0) {
        return ImageBase;
    }
    
    numberOfModules = modulesSize / sizeof(AUX_MODULE_EXTENDED_INFO);// Calculate the number of modules.
        
    modules = (AUX_MODULE_EXTENDED_INFO*) ExAllocatePoolWithTag(PagedPool, modulesSize, TAG);// Allocate memory to receive data.
    if (modules == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        return ImageBase;
    }
    RtlZeroMemory(modules, modulesSize);
    
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), modules);// Obtain the module information.
    if (!NT_SUCCESS(status)) {
        ExFreePoolWithTag(modules,TAG);
        return ImageBase;
    }

    for (i = 0;i<numberOfModules;i++)
    {
        UCHAR * pfilename = modules[i].FullPathName + modules[i].FileNameOffset;

        if (_stricmp(pfilename, name) == 0)
        {
            ImageBase = modules[i].BasicInfo.ImageBase;
            break;
        }
    }

    ExFreePoolWithTag(modules,TAG);

    return ImageBase;
} 


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;  
    PVOID ImageBase = 0;
    PVOID FunctionAddress;
    //ANSI_STRING EngCreateBitmap  = RTL_CONSTANT_STRING("EngCreateBitmap");//NT开头的函数竟然一个也没有导出。
    ANSI_STRING EngCreateBitmap  = RTL_CONSTANT_STRING("ArcFilterDprIndicateReceive");

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;    

    //ImageBase = get_module_image_base("WIN32k.sys");//注意这个的环境:有的线程不可以访问。不然地址不可访问。
    ImageBase = get_module_image_base("ndis.sys");
    if (ImageBase == 0)
    {
        return status; 
    }

    FunctionAddress = MiFindExportedRoutineByName (ImageBase, &EngCreateBitmap);
    if (FunctionAddress == 0)
    {
        return status; 
    }

    /*
    验证下:
    1: kd> dd FunctionAddress L1
    f88f6c78  ba5552cd
    1: kd> u ba5552cd
    NDIS!ArcFilterDprIndicateReceive:
    ba5552cd 8bff            mov     edi,edi
    ba5552cf 55              push    ebp
    ba5552d0 8bec            mov     ebp,esp
    ba5552d2 83ec24          sub     esp,24h
    ba5552d5 a11c6353ba      mov     eax,dword ptr [NDIS!__security_cookie (ba53631c)]
    ba5552da 53              push    ebx
    ba5552db 8b5d10          mov     ebx,dword ptr [ebp+10h]
    ba5552de 56              push    esi
    */

    return status;    
} 

2014年8月15日星期五

WINDBG条件断点

Setting a Conditional Breakpoint

Conditional breakpoints can be very useful when you are trying to find bugs in your code. 
They cause a break to occur only if a specific condition is satisfied.

正如文章所说:很有用。
同时,这也是通向WINDBG脚本的好途径。

本文收集本人常用的条件断点:


1.不是条件断点的高级断点:发生操作时不断下来而是打印个信息。
bu nt!zwcreatefile ".echo zwcreatefile is called;g"
bu ntdll!ntcreatefile ".echo ntcreatefile is called;g"
bu kernel32!CreateFileA "poi(eso + 4)"
bu kernel32!CreateFileW "poi(eso + 4)"


2.不算是条件断点的基于进程/线程的断点,主要用于内核模式的调试:
bu /p 81cf3540 test!PreOPeration
bu /p 81cf3540 test!PreOPeration+0x1af
bu1 /p 81e7f368 @@masm(`test!e:\code\driver\test0\test.c:422+`);
线程的不再说了,因为CID都是全局的。
这也省了编写这些识别条件的代码了。

2014年8月9日星期六

编写内核DLL

#include <ntifs.h>
#include <windef.h>


/*
汇编也可以写内核的DLL,包括64位的。
这里也就不说了。

VS2012和VS2013应该也可以,好像应该有示例的工程。

这里是WDK的内核的DLL的示例。

参考:
http://msdn.microsoft.com/zh-cn/library/windows/hardware/ff542891(v=vs.85).aspx
http://msdn.microsoft.com/zh-cn/library/windows/hardware/dn613893(v=vs.85).aspx
http://www.wd-3.com/archive/KernelDlls.htm

注意事项:
1.Unlike a standard driver, however, an export driver does not receive IRPs or occupy a place in the driver stack, nor is it considered to be a system service.
2.(Even if you use DECLSPEC_EXPORT, your .def file must contain at least DllInitialize and DllUnload so you can mark these functions as PRIVATE.)
3.You can build any standard driver as an export driver—it will operate as a standard driver when loaded in the usual way, and also export functions that other drivers can call. 

made by correy
made at 2014.08.09
homepage:http://correy.webs.com
*/


//DECLSPEC_IMPORT 
//DECLSPEC_EXPORT
 __declspec(dllexport) int test ()
{
    int Status = STATUS_SUCCESS;

    KdPrint(("内核DLL的test\n"));

    return Status;
}


NTSTATUS DllUnload(void)
{
    NTSTATUS Status = STATUS_SUCCESS;

    KdPrint(("内核DLL的DllUnload\n"));

    return Status;
}


NTSTATUS DllInitialize(_In_  PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status = STATUS_SUCCESS;

    KdPrint(("内核DLL的DllInitialize\n"));

    return Status;
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{   
    KdPrint(("内核DLL的Unload\n"));
}


/*
 At a minimum, an export driver must have a DriverEntry routine; 
 this can be an empty stub to satisfy build scripts--the export driver's DriverEntry is never called by Plug and Play.
*/
#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status = STATUS_SUCCESS;
    KdBreakPoint();

    DriverObject->DriverUnload = Unload; 

    KdPrint(("内核DLL的入口\n"));

    return Status;
} 

source文件如下:

TARGETNAME=KernelDLL #The .sys file is your kernel-mode DLL.

TARGETTYPE=EXPORT_DRIVER #这也可能决定了别的程序对这个文件的调用方式(静态链接和只有函数的信息)。DRIVER_LIBRARY可能就是静态链接的。以及生成的文件大小等。

SOURCES=KernelDLL.C 

DLLDEF=def.def #这个文件没有使用。这是另一个办法。

TARGETPATH=obj

/////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <ntifs.h>
#include <windef.h>


DECLSPEC_IMPORT int test (); 


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{   
    
}


#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status = STATUS_SUCCESS;
    KdBreakPoint();

    DriverObject->DriverUnload = Unload; 

    test();

    return Status;
} 

source文件如下:

TARGETNAME=test

TARGETTYPE=DRIVER

SOURCES=test.C 

TARGETPATH=obj 

TARGETLIBS=KernelDLL.lib #要把这个文件复制到相应的位置。好像还有静态链接和动态链接之别。

/////////////////////////////////////////////////////////////////////////////////////////////////////////

测试:
1.Export drivers must be installed in the %Windir%\System32\Drivers directory.
2.Install the export driver .sys file in the %windir%\system32\drivers directory. 
  It will be loaded the first time any other driver calls into it.
  先加载KernelDLL.sys.
  不然test.sys加载失败。
  开始觉得这不可思议,不像DLL。
  又一想系统的SYS也都是这样的。如ntos*.exe NDIS.SYS。
  所以此功能用处不大。
3.加载test.sys.
  效果就不说了。
  

2014年8月5日星期二

打印栈帧的一点信息

#include <ntifs.h>

LARGE_INTEGER  Cookie;


/*
功能:打印到现在为止的函数调用的栈帧的信息(栈回溯),而不是最终的函数调用栈帧的信息。

记得以前有个函数,可以打印函数调用堆栈的信息,如果有符号文件还可以显示更多的信息。
当时觉得没有用,一心想解析符号文件的信息。

现在想来,即使没有符号文件及相关的信息,有这些调用堆栈的信息,对开发这和解决问题也是有用的。
不能光想着符号文件。没有符号文件分析程序更加锻炼人的能力。

此文收集并简单测试一些信息。

更多的API如下:
IoGetStackLimits
IoGetInitialStack 
IoGetRemainingStackSize 
IoWithinStackLimits
KeExpandKernelStackAndCallout 
KeSetKernelStackSwapEnable

http://www.cnblogs.com/welfear/archive/2010/11/16/1878503.html
http://blog.csdn.net/cosmoslife/article/details/7818841
http://blogs.msdn.com/b/vcblog/archive/2014/01/23/examining-stack-traces-of-objects-using-visual-studio-2013.aspx
CaptureStackBackTrace 
RtlCaptureStackBackTrace 
RtlCaptureStackContext
RtlGetCallerAddress XP 已经导出。
RtlWalkFrameChain http://msdn.microsoft.com/zh-cn/office/ff563638(v=vs.100).aspx
KeExpandKernelStackAndCallout

made by correy
made at 2014.08.05
homepage:http://correy.webs.com
*/


void get_open_information_from_stack_in_32()
    /*
    相信也可以处理64位系统的,尽管它的参数的调用方式特殊,但参数也是存在栈上的。
    */
{
    /*
    The RtlCaptureStackBackTrace routine captures a stack back trace by walking up the stack and recording the information for each frame.
    Important  This is an exported function that MUST probe the ability to take page faults.
    所以:IRQL: <= DISPATCH_LEVEL

    Requirements
    Versions: Available in Windows XP and later versions of the Windows operating systems.
    */

    /*
    此时栈的信息类似如下:
    3: kd> k
    ChildEBP RetAddr  
    efaf6964 f8870132 test!get_open_information_from_stack_in_32+0x8 [e:\code\driver\test\test.c @ 20]
    efaf6970 f887005d test!post_open_key+0x12 [e:\code\driver\test\test.c @ 58]
    efaf6980 8056bdd4 test!RegistryCallback+0x1d [e:\code\driver\test\test.c @ 74]
    efaf69b4 806312bd nt!CmpCallCallBacks+0x50
    efaf6b88 805c0099 nt!CmpParseKey+0x7b9
    efaf6c00 805bca48 nt!ObpLookupObjectName+0x119
    efaf6c54 80626810 nt!ObOpenObjectByName+0xea
    efaf6d50 805427e8 nt!NtOpenKey+0x1c8
    efaf6d50 7c92e514 nt!KiSystemServicePostCall
    0168f700 7c92d5da ntdll!KiFastSystemCallRet
    0168f7e8 7c93019b ntdll!ZwOpenKey+0xc
    0168fa14 00000000 ntdll!RtlAllocateHeap+0x1c2
    在这种情况下是一定能找到:nt!NtOpenKey函数的地址的。
    */

    PVOID pbacts = IoGetInitialStack();//returns the base address of the current thread's stack.
    /*
    上面一行运行的结果如下:
    3: kd> r
    eax=efaf7000 ebx=8055b3a0 ecx=efaf6b44 edx=e1a02d90 esi=e1a02d88 edi=e1cf5508
    eip=f88700b1 esp=efaf6948 ebp=efaf6964 iopl=0         nv up ei ng nz ac pe nc
    cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00000296
    test!get_open_information_from_stack_in_32+0x11:
    f88700b1 33c0            xor     eax,eax
    3: kd> dd efaf6948
    efaf6948  efaf7000 00000001 805f2501 80630424
    efaf6958  00000000 e16d2518 e1d1ab98 efaf6970
    efaf6968  f8870132 00000000 efaf6980 f887005d
    efaf6978  efaf6b44 0000000d efaf69b4 8056bdd4
    efaf6988  00000000 0000000d efaf6b44 00000000
    efaf6998  e16d2518 e1d1ab98 e1a02d90 e1a02d90
    efaf69a8  81ed9da0 8055b214 00000000 efaf6b88
    efaf69b8  806312bd 0000000d efaf6b44 e1bb5558
    3: kd> dd pbacts
    efaf6948  efaf7000 00000001 805f2501 80630424
    efaf6958  00000000 e16d2518 e1d1ab98 efaf6970
    efaf6968  f8870132 00000000 efaf6980 f887005d
    efaf6978  efaf6b44 0000000d efaf69b4 8056bdd4
    efaf6988  00000000 0000000d efaf6b44 00000000
    efaf6998  e16d2518 e1d1ab98 e1a02d90 e1a02d90
    efaf69a8  81ed9da0 8055b214 00000000 efaf6b88
    efaf69b8  806312bd 0000000d efaf6b44 e1bb5558
    可见IoGetInitialStack的内容和栈指针(这里是ESP)的内容的内容是一样的。
    */
    
    USHORT ncf = 0;//The number of captured frames
    USHORT i = 0;

    //In Windows XP and Windows Server 2003, the sum of the FramesToSkip and FramesToCapture parameters must be less than 63.
    ULONG FramesToSkip = 0;//The number of frames to skip from the start of the back trace. 
    ULONG FramesToCapture = 62;//The number of frames to be captured. 
    
    PVOID  * BackTrace = 0;//An array of pointers captured from the current stack trace. 
    PVOID  * pBackTrace = 0;

    /*An optional value that can be used to organize hash tables. 
    If this parameter is NULL, no hash value is computed.
    This value is calculated based on the values of the pointers returned in the BackTrace array.
    Two identical stack traces will generate identical hash values.
    */
    ULONG BackTraceHash = 0;

    /*
    http://msdn.microsoft.com/zh-cn/office/ff563638(v=vs.100).aspx
    RtlGetCallersAddress. Use the intrinsic _ReturnAddress instead.
    就是本函数的返回地址,也就是调用本函数的下一个指令的地址。
    在调试器里面就是Retaddr。
    */
    PVOID CallersAddress = _ReturnAddress();

    //http://msdn.microsoft.com/zh-cn/library/windows/hardware/Dn613940(v=vs.85).aspx
    int stacklength = 32 * 1024;//取X86/X64/IA64的栈的最大值。

    UNICODE_STRING NtOpenKey = RTL_CONSTANT_STRING(L"NtOpenKey");
    PVOID p_NtOpenKey = MmGetSystemRoutineAddress(&NtOpenKey);//此函数没有导出。获取的值恒为0.

    ULONG_PTR  LowLimit = 0;//Pointer to a caller-supplied variable in which this routine returns the lower offset of the current thread's stack frame. 
    ULONG_PTR  HighLimit = 0;//Pointer to a caller-supplied variable in which this routine returns the higher offset of the current thread's stack frame.
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //下面开始代码。

    IoGetStackLimits(&LowLimit, &HighLimit);

    BackTrace = (PVOID  *)ExAllocatePool(NonPagedPool, stacklength); 
    if (BackTrace == NULL)
    {
        return ;
    }
    RtlZeroMemory(BackTrace, stacklength);
    /*
    BackTrace的地址一定在LowLimit和HighLimit之间,这是废话。
    */

    ncf = RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture, BackTrace, &BackTraceHash);     
    /*
    这行代码运行后的效果类似为:
    3: kd> dps esp
    efaf6948  efaf7000
    efaf694c  00000000
    efaf6950  0000003e
    efaf6954  ebbd2e00
    efaf6958  00009000
    efaf695c  82161000
    efaf6960  e1d10009
    efaf6964  efaf6970
    efaf6968  f8870132 test!post_open_key+0x12 [e:\code\driver\test\test.c @ 58]
    efaf696c  00000000
    efaf6970  efaf6980
    efaf6974  f887005d test!RegistryCallback+0x1d [e:\code\driver\test\test.c @ 74]
    efaf6978  efaf6b44
    efaf697c  0000000d
    efaf6980  efaf69b4
    efaf6984  8056bdd4 nt!CmpCallCallBacks+0x50
    efaf6988  00000000
    efaf698c  0000000d
    efaf6990  efaf6b44
    efaf6994  00000000
    efaf6998  e16d2518
    efaf699c  e1d1ab98
    efaf69a0  e1a02d90
    efaf69a4  e1a02d90
    efaf69a8  81ed9da0
    efaf69ac  8055b214 nt!CmpRegistryLock+0x34
    efaf69b0  00000000
    efaf69b4  efaf6b88
    efaf69b8  806312bd nt!CmpParseKey+0x7b9
    efaf69bc  0000000d
    efaf69c0  efaf6b44
    efaf69c4  e1bb5558
    3: kd> dps BackTrace
    efaf695c  82161000
    efaf6960  e1d10009
    efaf6964  efaf6970
    efaf6968  f8870132 test!post_open_key+0x12 [e:\code\driver\test\test.c @ 58]
    efaf696c  00000000
    efaf6970  efaf6980
    efaf6974  f887005d test!RegistryCallback+0x1d [e:\code\driver\test\test.c @ 74]
    efaf6978  efaf6b44
    efaf697c  0000000d
    efaf6980  efaf69b4
    efaf6984  8056bdd4 nt!CmpCallCallBacks+0x50
    efaf6988  00000000
    efaf698c  0000000d
    efaf6990  efaf6b44
    efaf6994  00000000
    efaf6998  e16d2518
    efaf699c  e1d1ab98
    efaf69a0  e1a02d90
    efaf69a4  e1a02d90
    efaf69a8  81ed9da0
    efaf69ac  8055b214 nt!CmpRegistryLock+0x34
    efaf69b0  00000000
    efaf69b4  efaf6b88
    efaf69b8  806312bd nt!CmpParseKey+0x7b9
    efaf69bc  0000000d
    efaf69c0  efaf6b44
    efaf69c4  e1bb5558
    efaf69c8  00000000
    efaf69cc  efaf6c40
    efaf69d0  e63561d1
    efaf69d4  81220010
    efaf69d8  e1b9fb18
    可见这个函数的栈顶少几个,但是不影响查找以前的函数和参数。
    多几个的原因可能是又调用函数了或者局部变量的初始化。

    BackTrace的这段内容就是函数的调用关系。
    */

    /*
    这里打印的信息类似于WINDBG的K命令,但是信息不多。
    */
    for (pBackTrace = BackTrace;i < ncf;pBackTrace++,i++ )
    {
        //if (*pBackTrace == p_NtOpenKey)//ZwOpenKey可以直接使用。
        //{
        //    break;
        //}
        KdPrint(("%d: %p \n", i, *pBackTrace));
    }

    /*
    要想获取某个函数的参数信息
    还得用IoGetStackLimits(&LowLimit, &HighLimit)的两个参数的值。
    在那个范围内搜索。
    */

    /*
    RtlWalkFrameChain和RtlCaptureStackBackTrace功能相似。
    RtlCaptureStackBackTrace封装了RtlWalkFrameChain(见WDK)。
    只不过:
    1.RtlWalkFrameChain导出(且头文件有函数申明)但是没有公开。
    2.RtlWalkFrameChain还可以获取包括(加上)用户态栈帧的数目。
    3.#pragma optimize( "y", off ) // disable FPO
    */
    //ncf = (USHORT)RtlWalkFrameChain (BackTrace, FramesToCapture, 0);  
    //ncf = (USHORT)RtlWalkFrameChain (BackTrace, FramesToCapture, 1); 

    ExFreePool(BackTrace);
}


NTSTATUS post_open_key(__in_opt PVOID  Argument2)
{
    unsigned int status = STATUS_SUCCESS;

    get_open_information_from_stack_in_32();

    return status;
}


EX_CALLBACK_FUNCTION RegistryCallback;
NTSTATUS RegistryCallback(__in PVOID  CallbackContext, __in_opt PVOID  Argument1, __in_opt PVOID  Argument2)
{
    switch((int)Argument1)
    {  
    case RegNtPostOpenKey:// == 13 仅仅XP收到此消息.
        post_open_key(Argument2);
        break;
    default:
        break;
    }

    return STATUS_SUCCESS;
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{   
    CmUnRegisterCallback(Cookie);
}


#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    KdBreakPoint();

    DriverObject->DriverUnload = Unload; 

    return CmRegisterCallback(RegistryCallback, NULL, &Cookie);
} 

2014年7月24日星期四

内核中的注册表操作

#include <ntifs.h>
#include <windef.h>

/*
Zw层次的注册表操作很简单。
但是做与不做还是有点区别的。

made by correy
made at 2014.07.24
homepage:http://correy.webs.com
*/

NTSTATUS ZwEnumerateKeyEx(IN UNICODE_STRING * Name)
    /*
    显示一个注册表的键下的:子键,名字,类型,数据。
    注意:没有递归显示。
    */
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES        ObjectAttributes;
    HANDLE  KeyHandle;
    PKEY_FULL_INFORMATION pfi;    
    ULONG  ResultLength;
    ULONG i = 0;

    InitializeObjectAttributes(&ObjectAttributes, Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
    if( !NT_SUCCESS( Status ) )
    {
        return Status;
    }

    /*
    注意ZwQueryKey的第一个参数。
    The KeyHandle passed to ZwQueryKey must have been opened with KEY_QUERY_VALUE access. 
    This is accomplished by passing KEY_QUERY_VALUE, KEY_READ, or KEY_ALL_ACCESS as the DesiredAccess parameter to ZwCreateKey or ZwOpenKey.
    */

    // 第一次调用是为了获取需要的长度
    Status = ZwQueryKey(KeyHandle, KeyFullInformation, NULL, 0, &ResultLength);
    if( !NT_SUCCESS( Status ) )
    {
        if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
        {
            //在下面申请内存。
        }
        else
        {
            ZwClose(KeyHandle);
            return Status;
        }
    }

    //ResultLength += MAX_PATH ;
    //ResultLength *= 2;//多申请一半。
    pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength);
    if (pfi == NULL)
    {
        //If ExAllocatePool returns NULL, the caller should return the NTSTATUS value STATUS_INSUFFICIENT_RESOURCES or should delay processing to another point in time.
        Status = STATUS_INSUFFICIENT_RESOURCES;
        ZwClose(KeyHandle);
        return Status;
    }

    // 第二次调用是为了获取数据
    Status = ZwQueryKey(KeyHandle, KeyFullInformation, pfi, ResultLength, &ResultLength);//少了赋值。这等低级的错误。
    if( !NT_SUCCESS( Status ) )
    {
        ExFreePool(pfi);
        ZwClose(KeyHandle);
        return Status;
    }

    //枚举子键。
    for (i = 0; i < pfi->SubKeys; i++)
    {     
        PKEY_BASIC_INFORMATION pbi;
        UNICODE_STRING us;

        // 获取第i个子项的长度
        Status = ZwEnumerateKey(KeyHandle, i, KeyBasicInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                break;
            }
        }

        pbi = (PKEY_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pbi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        // 获取第i个子项的数据
        Status = ZwEnumerateKey(KeyHandle, i, KeyBasicInformation, pbi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pbi);
            break;
        }

        us.Buffer = pbi->Name;
        us.Length = (USHORT)pbi->NameLength;
        us.MaximumLength = us.Length;
        
        DbgPrint("subkey:%wZ\n", &us); 

        /*
        在这里组合字符串,可以考虑递归枚举。
        */

        ExFreePool(pbi);// 释放内存
    }

    //枚举名字,类型,数据。
    for (i = 0; i < pfi->Values; i++) //可以考虑用ZwQueryValueKey获取数量。MSDN关于这个成员的解释是:The number of value entries for this key.
    {     
        PKEY_VALUE_BASIC_INFORMATION pkvbi;
        UNICODE_STRING us;
        PKEY_VALUE_PARTIAL_INFORMATION pkvpi;
        UNICODE_STRING data;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        // 获取名字及类型。
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueBasicInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                break;
            }
        }
        pkvbi = (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pkvbi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueBasicInformation, pkvbi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pkvbi);
            break;
        }

        us.Buffer = pkvbi->Name;
        us.Length = (USHORT)pkvbi->NameLength;
        us.MaximumLength = us.Length;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // 获取数据
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValuePartialInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                ExFreePool(pkvbi);
                break;
            }
        }
        pkvpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pkvpi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            ExFreePool(pkvbi);
            break;
        }
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValuePartialInformation, pkvpi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pkvpi);
            ExFreePool(pkvbi);
            break;
        }

        data.Buffer = (PWCH)pkvpi->Data;//有的数据可能无法显示。
        data.Length = (USHORT)pkvpi->DataLength;
        data.MaximumLength = data.Length;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        DbgPrint("name:%wZ,type:%d,data:%wZ\n", &us, pkvbi->Type, &data); 

        ExFreePool(pkvbi);// 释放内存
        ExFreePool(pkvpi);
    }

    ExFreePool(pfi);
    ZwClose(KeyHandle);

    return Status;
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL; 
    UNICODE_STRING test = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control");//\\Session Manager
    
    KdBreakPoint();

    DriverObject->DriverUnload = Unload;  

    status = ZwEnumerateKeyEx(&test);
    if( !NT_SUCCESS( status ) )
    {
        DbgPrint("ZwEnumerateKeyEx fail with 0x%x\n", status); 
    }   

    return status;//STATUS_SUCCESS
} 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <ntifs.h>
#include <windef.h>

#define TAG 'tset' //test


NTSTATUS ZwCopyKey(IN UNICODE_STRING * Name, IN UNICODE_STRING * Name2)
    /*
    复制一个注册表的键下的:子键,名字,类型,数据。
    注意:
    1.没有递归复制。
    2.没有复制(安全)属性。
    3.没有对参数的有效性进行检查。字符串的末尾不要带L'\\'.
    4.确认使用前这两个路径是存在的。
    5.更多的缺陷,请你补充纠正。更多的功能等待你的发挥。
    */
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    OBJECT_ATTRIBUTES        ObjectAttributes;
    HANDLE  KeyHandle;
    HANDLE  KeyHandle3;
    PKEY_FULL_INFORMATION pfi;    
    ULONG  ResultLength;
    ULONG i = 0;

    InitializeObjectAttributes(&ObjectAttributes, Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    Status = ZwOpenKey(&KeyHandle, KEY_ALL_ACCESS, &ObjectAttributes);
    if( !NT_SUCCESS( Status ) )
    {
        return Status;
    }

    /*
    注意ZwQueryKey的第一个参数。
    The KeyHandle passed to ZwQueryKey must have been opened with KEY_QUERY_VALUE access. 
    This is accomplished by passing KEY_QUERY_VALUE, KEY_READ, or KEY_ALL_ACCESS as the DesiredAccess parameter to ZwCreateKey or ZwOpenKey.
    */

    // 第一次调用是为了获取需要的长度
    Status = ZwQueryKey(KeyHandle, KeyFullInformation, NULL, 0, &ResultLength);
    if( !NT_SUCCESS( Status ) )
    {
        if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
        {
            //在下面申请内存。
        }
        else
        {
            ZwClose(KeyHandle);
            return Status;
        }
    }

    //ResultLength += MAX_PATH ;
    //ResultLength *= 2;//多申请一半。
    pfi = (PKEY_FULL_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength);
    if (pfi == NULL)
    {
        //If ExAllocatePool returns NULL, the caller should return the NTSTATUS value STATUS_INSUFFICIENT_RESOURCES or should delay processing to another point in time.
        Status = STATUS_INSUFFICIENT_RESOURCES;
        ZwClose(KeyHandle);
        return Status;
    }

    // 第二次调用是为了获取数据
    Status = ZwQueryKey(KeyHandle, KeyFullInformation, pfi, ResultLength, &ResultLength);//少了赋值。这等低级的错误。
    if( !NT_SUCCESS( Status ) )
    {
        ExFreePool(pfi);
        ZwClose(KeyHandle);
        return Status;
    }

    //枚举子键。
    for (i = 0; i < pfi->SubKeys; i++)
    {     
        PKEY_BASIC_INFORMATION pbi;
        UNICODE_STRING us;
        UNICODE_STRING new_key ;
        OBJECT_ATTRIBUTES ob;
        HANDLE KeyHandle2 = 0;
        PUNICODE_STRING Class = NULL;
        ULONG  Disposition;

        // 获取第i个子项的长度
        Status = ZwEnumerateKey(KeyHandle, i, KeyBasicInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                break;
            }
        }

        pbi = (PKEY_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pbi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        // 获取第i个子项的数据
        Status = ZwEnumerateKey(KeyHandle, i, KeyBasicInformation, pbi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pbi);
            break;
        }

        us.Buffer = pbi->Name;
        us.Length = (USHORT)pbi->NameLength;
        us.MaximumLength = us.Length;
        
        DbgPrint("subkey:%wZ\n", &us); 

        //开始新建。

        new_key.Buffer = (wchar_t *)ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG);
        if (new_key.Buffer == NULL) { 
            ExFreePool(pbi);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        RtlZeroMemory(new_key.Buffer, MAX_PATH);
        RtlInitEmptyUnicodeString(&new_key, new_key.Buffer,MAX_PATH);

        RtlCopyUnicodeString(&new_key,Name2); 

        Status = RtlAppendUnicodeToString(&new_key, L"\\");
        if (!NT_SUCCESS (Status)) {
            ExFreePool(new_key.Buffer);
            ExFreePool(pbi);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }         

        Status = RtlAppendUnicodeStringToString(&new_key, &us);
        if (!NT_SUCCESS (Status)) {
            ExFreePool(new_key.Buffer);
            ExFreePool(pbi);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        } 

        InitializeObjectAttributes(&ob, &new_key, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, 0);
        Status = ZwCreateKey(&KeyHandle2, KEY_WRITE, &ob, 0, Class, REG_OPTION_NON_VOLATILE, &Disposition);//KEY_ALL_ACCESS KEY_READ
        if (!NT_SUCCESS (Status)) 
        {
            //如果子键已经存在,返回正确。
            ExFreePool(new_key.Buffer);
            ExFreePool(pbi);
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        else
        {
            if (KeyHandle2)//断言FileHandle不等于0也不是无效的句柄。
            {
                Status = ZwClose(KeyHandle2);
                if (!NT_SUCCESS (Status)) 
                {
                    KdPrint(("ZwClose fail with 0x%x.\n", Status));
                }
            }
        }

        /*
        在这里组合字符串,可以考虑递归。
        */

        ExFreePool(pbi);// 释放内存
        ExFreePool(new_key.Buffer);
    }

    //处理上面失败的情况。主要是for 循环。
    if( !NT_SUCCESS( Status ) )
    {
        ExFreePool(pfi);
        ZwClose(KeyHandle);
        return Status;
    }

    InitializeObjectAttributes(&ObjectAttributes, Name2, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    Status = ZwOpenKey(&KeyHandle3, KEY_ALL_ACCESS, &ObjectAttributes);
    if( !NT_SUCCESS( Status ) )
    {
        ExFreePool(pfi);
        ZwClose(KeyHandle);
        return Status;
    }

    //枚举名字,类型,数据。
    for (i = 0; i < pfi->Values; i++) //可以考虑用ZwQueryValueKey获取数量。MSDN关于这个成员的解释是:The number of value entries for this key.
    {     
        PKEY_VALUE_BASIC_INFORMATION pkvbi;
        UNICODE_STRING us;
        PKEY_VALUE_PARTIAL_INFORMATION pkvpi;
        UNICODE_STRING data;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        // 获取名字及类型。
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueBasicInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                break;
            }
        }
        pkvbi = (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pkvbi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueBasicInformation, pkvbi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pkvbi);
            break;
        }

        us.Buffer = pkvbi->Name;
        us.Length = (USHORT)pkvbi->NameLength;
        us.MaximumLength = us.Length;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // 获取数据
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValuePartialInformation, NULL, 0, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            if (Status == STATUS_BUFFER_TOO_SMALL  || Status == STATUS_BUFFER_OVERFLOW) //STATUS_BUFFER_OVERFLOW这个情况应该不会发生在这种情况下。
            {
                //在下面申请内存。
            }
            else
            {
                ExFreePool(pkvbi);
                break;
            }
        }
        pkvpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(NonPagedPool, ResultLength); 
        if (pkvpi == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            ExFreePool(pkvbi);
            break;
        }
        Status = ZwEnumerateValueKey(KeyHandle, i, KeyValuePartialInformation, pkvpi, ResultLength, &ResultLength);
        if( !NT_SUCCESS( Status ) )
        {
            ExFreePool(pkvpi);
            ExFreePool(pkvbi);
            break;
        }

        data.Buffer = (PWCH)pkvpi->Data;//有的数据可能无法显示。
        data.Length = (USHORT)pkvpi->DataLength;
        data.MaximumLength = data.Length;

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        Status = ZwSetValueKey(KeyHandle3, &us, 0, pkvbi->Type, data.Buffer, data.Length);
        if( !NT_SUCCESS( Status ) ) //如果句柄的权限是KEY_READ这里成功,但是实际是没有成功的。
        {
            ExFreePool(pkvpi);
            ExFreePool(pkvbi);
            break;
        }

        DbgPrint("name:%wZ,type:%d,data:%wZ\n", &us, pkvbi->Type, &data); 

        ExFreePool(pkvbi);// 释放内存
        ExFreePool(pkvpi);
    }

    ExFreePool(pfi);
    ZwClose(KeyHandle);
    ZwClose(KeyHandle3);

    return Status;
}


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL; 
    UNICODE_STRING test = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
    UNICODE_STRING test2 = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager2");
    
    KdBreakPoint();

    DriverObject->DriverUnload = Unload;  

    status = ZwCopyKey(&test, &test2);
    if( !NT_SUCCESS( status ) )
    {
        DbgPrint("ZwEnumerateKeyEx fail with 0x%x\n", status); 
    }   

    return status;//STATUS_SUCCESS
}