驱动开发文章二

来源:互联网 发布:cs软件下载 编辑:程序博客网 时间:2024/06/07 05:37

目录(?)[+]

转载请注明来源: 
enjoy5512的博客 : http://blog.csdn.net/enjoy5512 
GitHub : https://github.com/whu-enjoy


1. 在驱动中使用文件

  在Windows执行体中,通过文件对象来代表文件,该文件对象是一种由对象管理器管理的执行体对象。例如:目录也是由文件对象代表的。 
内核组件通过对象名来引用文件,即在文件的全路径前面加\DosDevices。(在Windows 2000及后续操作系统中,\??等同于\DosDevices)。例如,文件C:\WINDOWS\example.txt的对象名为\DosDevices\C:\WINDOWS\example.txt。你需要用对象名来打开文件以获取句柄。

使用文件步骤: 
 1. 打开文件返回文件句柄。 
 2. 调用合适的ZwXxxFile 函数以完成对文件的操作。 
 3. 调用ZwClose函数关闭打开的文件句柄。 
  
  当打开一个指向文件的文件句柄时,Windows执行体就创建了一个文件对象来代表该文件,同时返回一个代表该对象的文件句柄。因此,对于单个文件来说,会存在多个文件对象的情况。同样,由于用户模式的应用程序可能会复制文件句柄,因此,对于同一个文件对象,也会存在多个文件句柄。只有当所有指向一个文件对象的文件句柄都关闭后,Windows执行体才会删除该文件对象。


1.1 打开文件句柄

  1. 定义一个OBJECT_ATTRIBUTES结构体变量,然后调用InitializeObjectAttributes函数初始化该变量。关键是设置改变量的ObjectName字段为文件对象名。
InitializeObjectAttributes宏,初始化一个OBJECT_ATTRIBUTES结构体,它指定对象句柄的属性,供打开句柄的例程使用。VOID InitializeObjectAttributes([out] POBJECT_ATTRIBUTES InitializedAttributes,[in] PUNICODE_STRING ObjectName,[in] ULONG Attributes,[in] HANDLE RootDirectory,[in, optional] PSECURITY_DESCRIPTOR SecurityDescriptor);参数:InitializedAttributes [out]指定要初始化的OBJECT_ATTRIBUTES结构体指针。ObjectName [in]一个指向UNICODE字符串对象的指针,它包含将要打开句柄的对象名称。它必须是一个完整的对象名称或者是相对于RootDirectory参数指定目录的相对路径。Attributes [in]指定一个或多个以下列标志:OBJ_INHERIT这个句柄可以被当前进程的子进程继承。OBJ_PERMANENT此标志仅应用于对象管理器命名的对象。默认情况下,这样的对象会在关闭所有它们打开的句柄时删除。如果指定了此标志,对象不会在所有打开的句柄关闭时删除,驱动可以使用ZwMakeTemporaryObject删除固定的对象。OBJ_EXCLUSIVE这个对象只可以有一个打开的句柄。意味着仅有一个进程可以访问此对象。OBJ_CASE_INSENSITIVE如果指定了此标志,在对ObjectName参数与已存在的对象名称进行匹配时,会不区分大小写。OBJ_OPENIF如果在创建对象的例程中指定此标志,当对象已经存在时,例程将会打开些对象。否则,创建对象的例程会返回值为STATUS_OBJECT_NAME_COLLISION的NTSTATUS代码。OBJ_KERNEL_HANDLE指定句柄仅能在内核模式访问。OBJ_FORCE_ACCESS_CHECK打开句柄的例程,应执行所有的访问检查,即使是在内核模式下也如此。RootDirectory [in]root目录对象句柄,用于ObjectName参数指定的相对路径时。如果ObjectName参数是一个完整的对象名,RootDirectory是NULL。使用ZwCreateDirectoryObject获取对象目录的句柄。SecurityDescriptor [in, optional]指定对象创建时应用的一个安全描述符。此参数是可选的。对象如果接受默认的安全设置,驱动程序可以指定NULL。更多信息,参见下面的备注节。返回值:无备注:InitializeObjectAttributes初始化一个OBJECT_ATTRIBUTES结构体,它设置将被打开的对象句柄的属性。调用者可以传递一个此结构的指针到实际打开句柄的例程。驱动程序运行进程上下文中,若要运行在系统进程,需要设置OBJ_KERNEL_HANDLE标志到Attributes参数。这个标志限制,使用此打开的句柄的进程仅能运行在内核模式。否则句柄可以在驱动运行的进程上下文中访问。注意,InitializeObjectAttributes始终设置OBJCECT_ATTRIBUTES的SecurityQualityOfService成员为NULL。如果驱动程序需要一个非空的值,可以直接设置SecurityQualityOfService成员。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

2 调用IoCreateFile, ZwCreateFile, 或者 ZwOpenFile,传递上面定义的结构体变量,成功就会返回执行该文件的句柄。

注:驱动一般用ZwCreateFile和ZwOpenFile,IoCreateFile很少使用,当调用ZwCreateFile,ZwOpenFile或IoCreateFile时,Windows执行体创建一个代表该文件的新的文件对象,并返回一个指向该对象的句柄。文件对象一直存在,知道你关闭了所有指向它的文件句柄。
原型:NTSYSAPINTSTATUSNTAPIZwCreateFile(OUT PHANDLE FileHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,OUT PIO_STATUS_BLOCK IoStatusBlock,IN PLARGE_INTEGER AllocationSize OPTIONAL,IN ULONG FileAttributes,IN ULONG ShareAccess,IN ULONG CreateDisposition,IN ULONG CreateOptions,IN PVOID EaBuffer OPTIONAL,IN ULONG EaLength);参数理解:OUT-FileHandle--------这是一个指向一个变量的指针,用来最后存放file object handleIN-DesiredAccess----这个参数指定一个访问权限,大概有以下的权限:FILE_ANY_ACCESS 0x0000 // any typeFILE_READ_ACCESS 0x0001 // file & pipeFILE_READ_DATA 0x0001 // file & pipeFILE_LIST_DIRECTORY 0x0001 // directoryFILE_WRITE_ACCESS 0x0002 // file & pipeFILE_WRITE_DATA 0x0002 // file & pipeFILE_ADD_FILE 0x0002 // directoryFILE_APPEND_DATA 0x0004 // fileFILE_ADD_SUBDIRECTORY 0x0004 // directoryFILE_CREATE_PIPE_INSTANCE 0x0004 // named pipeFILE_READ_EA 0x0008 // file & directoryFILE_WRITE_EA 0x0010 // file & directoryFILE_EXECUTE 0x0020 // fileFILE_TRAVERSE 0x0020 // directoryFILE_DELETE_CHILD 0x0040 // directoryFILE_READ_ATTRIBUTES 0x0080 // all typesFILE_WRITE_ATTRIBUTES 0x0100 // all typesFILE_ALL_ACCESS // All of the preceding +STANDARD_RIGHTS_ALL最后一个权限最大,这里面要注意的是范围问题,有的值只适合目录,有的只适合管道,有的只适合命名管道,有的同时适用,想下这个地方可以做什么文章ObjectAttributes---指向下面这个结构的一个变量,就是来表明文件对象的属性的。typedef struct _OBJECT_ATTRIBUTES {ULONG Length;HANDLE RootDirectory;PUNICODE_STRING ObjectName;ULONG Attributes;PVOID SecurityDescriptor; /* type SECURITY_DESCRIPTOR */PVOID SecurityQualityOfService; /* type SECURITY_QUALITY_OF_SERVICE */} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;这个结构是针对很多对象的,不光是针对文件对象的,所以OBJ_PERMANENT(永久), OBJ_EXCLUSIVE(互斥),and OBJ_OPENLINK这三个实际上对于文件对象来说始终是无效的IoStatusBlock-----这个也是个指针变量,指向一个叫做IO_STATUS_BLOCK的结构体,最后函数返回的时候,这个结构体的成员 里面要填充一些值,具体的呢就是完成状态,请求操作的一些信息,最重要的一个成员就是Information成员,他显示了函数对文件的处理方式,他的值 可能是下面的几个:FILE_SUPERSEDED(替代)FILE_OPENED(打开)FILE_CREATED(创建)FILE_OVERWRITTEN(重写)FILE_EXISTS(存在)FILE_DOES_NOT_EXIST(文件不存在)再看下这个IO_STATUS_BLOCK的具体结构:typedef struct _IO_STATUS_BLOCK {union {NTSTATUS Status;PVOID Pointer;} DUMMYUNIONNAME;ULONG_PTR Information;} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;AllocationSize---这是个可选的参数,他是指定初始化文件需要的内存字节数的,所以可向而知,他只有在真正的涉及到文件创建的时候才有意义,也就是说创建,重写,替换这些操作的时候。指向一个LARGE_INTEGER:typedef union _LARGE_INTEGER {_ANONYMOUS_STRUCT struct{ULONG LowPart;LONG HighPart;} DUMMYSTRUCTNAME;struct{ULONG LowPart;LONG HighPart;} u;#endif //MIDL_PASSLONGLONG QuadPart;} LARGE_INTEGER, *PLARGE_INTEGER;FileAttributes---这个参数指定文件的属性,刚才是文件对象的属性。可以是以下的:FILE_ATTRIBUTE_READONLYFILE_ATTRIBUTE_HIDDENFILE_ATTRIBUTE_SYSTEMFILE_ATTRIBUTE_DIRECTORYFILE_ATTRIBUTE_ARCHIVEFILE_ATTRIBUTE_NORMALFILE_ATTRIBUTE_TEMPORARYFILE_ATTRIBUTE_SPARSE_FILEFILE_ATTRIBUTE_REPARSE_POINTFILE_ATTRIBUTE_COMPRESSEDFILE_ATTRIBUTE_OFFLINEFILE_ATTRIBUTE_NOT_CONTENT_INDEXEDFILE_ATTRIBUTE_ENCRYPTEDShareAccess---指定共享的权限,以下三种的组合:FILE_SHARE_READFILE_SHARE_WRITEFILE_SHARE_DELETECreateDisposition---这个参数指定要对文件干嘛,可以是下面的值:FILE_SUPERSEDEFILE_OPENFILE_CREATEFILE_OPEN_IFFILE_OVERWRITEFILE_OVERWRITE_IFCreateOptions---这个参数指定创建或者打开文件的时候做的一些事情,可以是以下的组合:FILE_DIRECTORY_FILEFILE_WRITE_THROUGHFILE_SEQUENTIAL_ONLYFILE_NO_INTERMEDIATE_BUFFERINGFILE_SYNCHRONOUS_IO_ALERTFILE_SYNCHRONOUS_IO_NONALERTFILE_NON_DIRECTORY_FILEFILE_CREATE_TREE_CONNECTIONFILE_COMPLETE_IF_OPLOCKEDFILE_NO_EA_KNOWLEDGEFILE_OPEN_FOR_RECOVERYFILE_RANDOM_ACCESSFILE_DELETE_ON_CLOSEFILE_OPEN_BY_FILE_IDFILE_OPEN_FOR_BACKUP_INTENTFILE_NO_COMPRESSIONFILE_RESERVE_OPFILTERFILE_OPEN_REPARSE_POINTFILE_OPEN_NO_RECALLFILE_OPEN_FOR_FREE_SPACE_QUERYEaBuffer---这个是个可选的参数,用来存放一些扩展的属性EaLength---存放扩展属性的字节大小返回值理解:如果成功,返回STATUS_SUCCESS如果失败,返回STATUS_ACCESS_DENIED,STATUS_OBJECT_NAME_NOT_FOUND, STATUS_OBJECT_NAME_COLLISION,STATUS_OBJECT_NAME_INVALID, STATUS_SHARING_VIOLATION, STATUS_NOT_A_DIRECTORY, orSTATUS_FILE_IS_A_DIRECTORY.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148

1.2 文件操作

下表列出了驱动中常用的利用文件句柄操作文件的函数

操作函数读文件ZwReadFile写文件ZwWriteFile读文件属性ZwQueryInformationFile设置文件属性ZwSetInformationFile
NTSTATUSZwReadFile(IN HANDLE FileHandle,IN HANDLE Event OPTIONAL,IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,IN PVOID ApcContext OPTIONAL,OUT PIO_STATUS_BLOCK IoStatusBlock,OUT PVOID Buffer,IN ULONG Length,IN PLARGE_INTEGER ByteOffset OPTIONAL,IN PULONG Key OPTIONAL);各参数的简要介绍如下所示:FileHandle:函数ZwCreateFile 返回的句柄。如果它是一个内核句柄,则ZwReadFile 和ZwCreateFile 并不需要在同一个进程中,因为内核句柄是各进程通用的。Event :一个事件,用于异步完成读时;我们忽略这个参数。ApcRoutine Apc:回调例程,用于异步完成读时;我们忽略这个参数。IoStatusBlock:返回结果状态,与ZwCreateFile 中的同名参数相同。Buffer:缓冲区,如果读取文件的内容成功,则内容将被读取到这里。Length:描述缓冲区的长度,即试图读取文件的长度。ByteOffset:要读取的文件的偏移量,也就是要读取的内容在文件中的位置。一般来说,不要将其设置为NULL,文件句柄不一定支持直接读取当前偏移。Key:读取文件时用的一种附加信息,一般不使用。当函数执行成功时返回STATUS_SUCCESS,实际上只要能够读取到任意字节的数据(不管它是否符合参数Length 的要求),都返回成功;但是,如果仅读取文件长度之外的部分,则返回STATUS_END_OF_FILE。ZwWriteFile 的参数与ZwReadFile 基本相同。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
ZwQueryInformationFile与ZwSetInformationFile参数基本相同ZwSetInformationFile 函数:NTSTATUSZwSetInformationFile(IN HANDLE FileHandle,OUT PIO_STATUS_BLOCK IoStatusBlock,IN PVOID FileInformation,IN ULONG Length,IN FILE_INFORMATION_CLASS FileInformationClass);ParametersFileHandle [in]Handle to the file object. This handle is created by a successful call to ZwCreateFile or ZwOpenFile.IoStatusBlock [out]Pointer to an IO_STATUS_BLOCK structure that receives the final completion status and information about the requested operation. The Information member receives the number of bytes set on the file.FileInformation [in]Pointer to a buffer that contains the information to set for the file. The particular structure in this buffer is determined by the FileInformationClass parameter. Setting any member of the structure to zero tells ZwSetInformationFile to leave the current information about the file for that member unchanged.Length [in]The size, in bytes, of the FileInformation buffer.FileInformationClass [in]The type of information, supplied in the buffer pointed to by FileInformation, to set for the file. Device and intermediate drivers can specify any of the following FILE_INFORMATION_CLASS values.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

2. 代码示例

//程序说明开始//==================================================================================//  功能 :  文件操作函数//  参数 :    //  (入口)  //  (出口)  无//  返回 :  VOID//  主要思路 : //  调用举例 : //  日期 : 2016年7月5日 21:07:02 - 2016年7月5日 23:47:35//==================================================================================//程序说明结束VOID FileOption(){    HANDLE SourceFileHandle = NULL;      //源文件句柄    HANDLE TargetFileHandle = NULL;      //目标文件句柄    NTSTATUS Status = STATUS_SUCCESS;    //返回状态    OBJECT_ATTRIBUTES ObjectAttributes;  //OBJECT_ATTRIBUTES结构    UNICODE_STRING SourceFilePath = RTL_CONSTANT_STRING(L"\\??\\c:\\source.txt"); //源文件    UNICODE_STRING TargetFilePath = RTL_CONSTANT_STRING(L"\\??\\c:\\target.txt"); //目标文件    UNICODE_STRING String = {0};           //指向Buffer    IO_STATUS_BLOCK IoStatusBlock;         //返回结果状态结构体    PVOID Buffer = NULL;                   //buffer指针    USHORT Length = 0;                     //要读写的长度    LARGE_INTEGER Offset = {0};            //要读写的偏移    //初始化OBJECT_ATTRIBUTES结构体    InitializeObjectAttributes(        &ObjectAttributes,        &SourceFilePath,        OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,        NULL,        NULL);    //以FILE_OVERWRITE_IF方式打开    Status = ZwCreateFile(        &SourceFileHandle,        GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,        &ObjectAttributes,        &IoStatusBlock,        NULL,        FILE_ATTRIBUTE_NORMAL,        FILE_SHARE_READ,        FILE_OVERWRITE_IF,        FILE_NON_DIRECTORY_FILE|        FILE_RANDOM_ACCESS|        FILE_SYNCHRONOUS_IO_NONALERT,        NULL,        0);    if (!NT_SUCCESS(Status))    {        DbgPrint("Open source file fault !! - %#x\n", Status);        return Status;    }    //字符串操作    //动态申请内存    Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, 1024, 'Tag1');    if (NULL == Buffer)    {        DbgPrint("申请Buffer失败!!\n");        ZwClose(SourceFileHandle);        Status = STATUS_INSUFFICIENT_RESOURCES;        return Status;    }    //初始化字符串指针    RtlInitEmptyUnicodeString(&String, Buffer, 512*sizeof(WCHAR));    //拷贝字符串    RtlCopyUnicodeString(&String, &SourceFilePath);    //追加Unicode变量    RtlAppendUnicodeStringToString(&String, &TargetFilePath);    //追加字符串    RtlAppendUnicodeToString(&String, L"别问我这是啥");    //写入文件    Length = String.Length;    Offset.QuadPart = 0;    Status = ZwWriteFile(        SourceFileHandle,        NULL,        NULL,        NULL,        &IoStatusBlock,        Buffer,        Length,        &Offset,        NULL);    if (!NT_SUCCESS(Status))    {        DbgPrint("写入源文件失败!!\n - %#X", Status);        ZwClose(SourceFileHandle);        ExFreePool(Buffer);        return Status;    }    //初始化OBJECT_ATTRIBUTES结构体    InitializeObjectAttributes(        &ObjectAttributes,        &TargetFilePath,        OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,        NULL,        NULL);    //打开目标文件    Status = ZwCreateFile(        &TargetFileHandle,        GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,        &ObjectAttributes,        &IoStatusBlock,        NULL,        FILE_ATTRIBUTE_NORMAL,        NULL,        FILE_OVERWRITE_IF,        FILE_NON_DIRECTORY_FILE|        FILE_RANDOM_ACCESS|        FILE_SYNCHRONOUS_IO_NONALERT,        NULL,        0);    if (!NT_SUCCESS(Status))    {        DbgPrint("目标文件打开失败!! - %#X", Status);        ZwClose(SourceFileHandle);        ExFreePool(Buffer);        return Status;    }    //初始化文件指针并循环复制文件,每次复制一个字节    Offset.QuadPart = 0;    while(1)    {        //读取源文件        Status = ZwReadFile(            SourceFileHandle,            NULL,            NULL,            NULL,            &IoStatusBlock,            Buffer,            1,            &Offset,            NULL);        if (!NT_SUCCESS(Status))        {            if (STATUS_END_OF_FILE == NULL)            {                Status = STATUS_SUCCESS;            }            break;        }        //写入目标文件        Status = ZwWriteFile(            TargetFileHandle,            NULL,            NULL,            NULL,            &IoStatusBlock,            Buffer,            1,            &Offset,            NULL);        Offset.QuadPart += 1;    }    //释放指针,释放申请的内存    ZwClose(SourceFileHandle);    ZwClose(TargetFileHandle);    ExFreePool(Buffer);

}

目录(?)[+]

转载请注明来源: 
enjoy5512的博客 : http://blog.csdn.net/enjoy5512 
GitHub : https://github.com/whu-enjoy


1. 在驱动中操作注册表

注册表项相当于文件夹,注册表子项子文件夹(类似目录)

1.1 打开注册表键

ZwCreateKey/ZwOpenKey 
代码中测试了ZwOpenKey的使用方式 
先初始化OBJECT_ATTRIBUTES结构体,然后打开注册表键

NTSTATUS ZwCreateKey(  _Out_      PHANDLE            KeyHandle,  _In_       ACCESS_MASK        DesiredAccess,  _In_       POBJECT_ATTRIBUTES ObjectAttributes,  _Reserved_ ULONG              TitleIndex,  _In_opt_   PUNICODE_STRING    Class,  _In_       ULONG              CreateOptions,  _Out_opt_  PULONG             Disposition);ParametersKeyHandle [out]Pointer to a HANDLE variable that receives a handle to the key.DesiredAccess [in]Specifies an ACCESS_MASK value that determines the requested access to the object. In addition to the access rights that are defined for all types of objects (see ACCESS_MASK), the caller can specify one or more of the following access rights, which are specific to object directories:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
DesiredAccess flagAllows caller to do thisKEY_QUERY_VALUERead key values.KEY_SET_VALUEWrite key values.KEY_CREATE_SUB_KEYCreate subkeys for the key.KEY_ENUMERATE_SUB_KEYSRead the key’s subkeys.KEY_CREATE_LINKCreate a symbolic link to the key. This flag is not used by device and intermediate drivers.KEY_NOTIFYAsk to receive notification when the name, value, or attributes of the key change. For more information, see ZwNotifyChangeKey.
The caller can also specify one of the following constants, which combines several ACCESS_MASK flags.
  • 1
  • 1
Constant Consists of theseACCESS_MASK flagsKEY_READSTANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFYKEY_WRITESTANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUBKEYKEY_EXECUTESame as KEY_READ.KEY_ALL_ACCESSSTANDARD_RIGHTS_ALL, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK
ObjectAttributes [in]Pointer to an OBJECT_ATTRIBUTES structure that specifies the object name and other attributes. Use InitializeObjectAttributes to initialize this structure. If the caller is not running in a system thread context, it must set the OBJ_KERNEL_HANDLE attribute when it calls InitializeObjectAttributes.TitleIndexDevice and intermediate drivers set this parameter to zero.Class [in, optional]Pointer to a Unicode string that contains the key's object class. This information is used by the configuration manager.CreateOptions [in]Specifies the options to apply when creating or opening a key, specified as a compatible combination of the following flags.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
CreateOptions flagDescriptionREG_OPTION_VOLATILEKey is not preserved when the system is rebooted.REG_OPTION_NON_VOLATILEKey is preserved when the system is rebooted.REG_OPTION_CREATE_LINKThe newly created key is a symbolic link. This flag is not used by device and intermediate drivers.REG_OPTION_BACKUP_RESTOREKey should be created or opened with special privileges that allow backup and restore operations. This flag is not used by device and intermediate drivers.
Disposition [out, optional]Pointer to a variable that receives a value indicating whether a new key was created or an existing one opened.
  • 1
  • 2
  • 1
  • 2
Disposition valueDescriptionREG_CREATED_NEW_KEYA new key was created.REG_OPENED_EXISTING_KEYAn existing key was opened.
Return valueZwCreateKey returns STATUS_SUCCESS on success, or the appropriate NTSTATUS error code on failure.
  • 1
  • 2
  • 1
  • 2
NTSTATUS ZwOpenKey(  _Out_ PHANDLE            KeyHandle,  _In_  ACCESS_MASK        DesiredAccess,  _In_  POBJECT_ATTRIBUTES ObjectAttributes);ParametersKeyHandle [out]Pointer to the HANDLE variable that receives the handle to the key.DesiredAccess [in]Specifies an ACCESS_MASK value that determines the requested access to the object. For more information, see the DesiredAccess parameter of ZwCreateKey.ObjectAttributes [in]Pointer to an OBJECT_ATTRIBUTES structure that specifies the object name and other attributes. Use InitializeObjectAttributes to initialize this structure. If the caller is not running in a system thread context, it must set the OBJ_KERNEL_HANDLE attribute when it calls InitializeObjectAttributes.Return valueZwOpenKey returns STATUS_SUCCESS if the given key was opened. Otherwise, it can return an error status, including the following:STATUS_INVALID_HANDLESTATUS_ACCESS_DENIED
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

1.2 读注册表键值

1)用ZwQueryValueKey 获取数据结构的长度。 
2)分配如此长度的内存。 
3)再次调用ZwQueryValueKey 查询。 
4)回收内存

NTSTATUS ZwQueryValueKey(  _In_      HANDLE                      KeyHandle,  _In_      PUNICODE_STRING             ValueName,  _In_      KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,  _Out_opt_ PVOID                       KeyValueInformation,  _In_      ULONG                       Length,  _Out_     PULONG                      ResultLength);ParametersKeyHandle [in]Handle to the key to read value entries from. This handle is created by a successful call to ZwCreateKey or ZwOpenKey.ValueName [in]Pointer to the name of the value entry to obtain data for.KeyValueInformationClass [in]A KEY_VALUE_INFORMATION_CLASS value that determines the type of information returned in the KeyValueInformation buffer.KeyValueInformation [out, optional]Pointer to a caller-allocated buffer that receives the requested information.Length [in]Specifies the size, in bytes, of the KeyValueInformation buffer.ResultLength [out]Pointer to a variable that receives the size, in bytes, of the key information. If the ZwQueryValueKey routine returns STATUS_SUCCESS, callers can use the value of this variable to determine the amount of data returned. If the routine returns STATUS_BUFFER_OVERFLOW or STATUS_BUFFER_TOO_SMALL, callers can use the value of this variable to determine the size of buffer required to hold the key information.Return valueZwQueryValueKey returns STATUS_SUCCESS on success, or the appropriate error code on failure. Possible error code values include:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
Return codeDescriptionSTATUS_OBJECT_NAME_NOT_FOUNDThe registry value was not available.STATUS_BUFFER_OVERFLOWThe buffer supplied is too small, and only partial data has been written to the buffer. *ResultLength is set to the minimum size required to hold the requested information.STATUS_BUFFER_TOO_SMALLThe buffer supplied is too small, and no data has been written to the buffer. *ResultLength is set to the minimum size required to hold the requested information.STATUS_INVALID_PARAMETERThe KeyInformationClass parameter is not a valid KEY_VALUE_INFORMATION_CLASS value.Warning If you specify KeyValueBasicInformation for KeyValueInformationClass, Windows 98 and Windows Me return STATUS_SUCCESS even if the registry key or value name does not exist.

1.3 写注册表键值

调用ZwSetValueKey

NTSTATUS ZwSetValueKey(  _In_     HANDLE          KeyHandle,  _In_     PUNICODE_STRING ValueName,  _In_opt_ ULONG           TitleIndex,  _In_     ULONG           Type,  _In_opt_ PVOID           Data,  _In_     ULONG           DataSize);ParametersKeyHandle [in]Handle to the registry key to write a value entry for. This handle is created by a successful call to ZwCreateKey or ZwOpenKey.ValueName [in]Pointer to the name of the value entry for which the data is to be written. This parameter can be a NULL pointer if the value entry has no name. If a name string is specified and the given name is not unique relative to its containing key, the data for an existing value entry is replaced.TitleIndex [in, optional]This parameter is reserved. Device and intermediate drivers should set this parameter to zero.Type [in]One of the following system-defined types of data to write.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
Type ValueMeaningREG_BINARYBinary data in any form.REG_DWORDA 4-byte numerical value.REG_DWORD_LITTLE_ENDIANA 4-byte numerical value with the least significant byte at the lowest address. Identical to REG_DWORD.REG_DWORD_BIG_ENDIANA 4-byte numerical value with the least significant byte at the highest address.REG_EXPAND_SZA null-terminated Unicode string that contains unexpanded references to environment variables, such as “%PATH%”.REG_LINKA Unicode string that names a symbolic link. This type is irrelevant to device and intermediate drivers.REG_MULTI_SZAn array of null-terminated strings, terminated by another zero.REG_NONEData with no particular type.REG_SZA null-terminated Unicode string.REG_RESOURCE_LISTA device driver’s list of hardware resources, used by the driver or one of the physical devices it controls, in the \ResourceMap treeREG_RESOURCE_REQUIREMENTS_LISTA device driver’s list of possible hardware resources it or one of the physical devices it controls can use, from which the system writes a subset into the \ResourceMap treeREG_FULL_RESOURCE_DESCRIPTORA list of hardware resources that a physical device is using, detected and written into the \HardwareDescription tree by the system
Note   Device drivers should not attempt to call ZwSetValueKey to explicitly write value entries in a subkey of the \Registry...\ResourceMap key. Only the system can write value entries to the \Registry...\HardwareDescription tree.Data [in, optional]Pointer to a caller-allocated buffer that contains the data for the value entry.DataSize [in]Specifies the size, in bytes, of the Data buffer. If Type is REG_XXX_SZ, this value must include space for any terminating zeros.Return valueZwSetValueKey returns an NTSTATUS value. Possible return values include:STATUS_SUCCESSSTATUS_ACCESS_DENIEDSTATUS_INVALID_HANDLE
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2. 示例代码

//程序说明开始//==================================================================================//  功能 :  注册表操作函数//  参数 :    //  (入口)  //  (出口)  无//  返回 :  NTSTATUS//  主要思路 : //  调用举例 : //  日期 : 2016年7月6日 16:26:31 - 2016年7月6日 18:13:48//==================================================================================//程序说明结束NTSTATUSRegistryOption(){    HANDLE KeyHandle = NULL;    NTSTATUS Status = STATUS_SUCCESS;    UNICODE_STRING KeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");    OBJECT_ATTRIBUTES ObjectAttributes;    UNICODE_STRING SourceKeyName = RTL_CONSTANT_STRING(L"SystemRoot");    UNICODE_STRING TargetKeyName = RTL_CONSTANT_STRING(L"test");    PKEY_VALUE_PARTIAL_INFORMATION AcKeyInfo = NULL;    KEY_VALUE_PARTIAL_INFORMATION KeyInfo;    ULONG Length = 0;    //初始化OBJECT_ATTRIBUTES结构体    InitializeObjectAttributes(        &ObjectAttributes,        &KeyPath,        OBJ_CASE_INSENSITIVE,        NULL,        NULL);    //打开注册表键    Status = ZwOpenKey(&KeyHandle, KEY_READ|KEY_WRITE, &ObjectAttributes);    if (!NT_SUCCESS(Status))    {        DbgPrint("Open the Key Handle Faild!! -- %#X\n", Status);        return Status;    }    //第一次读取注册表键值探测数据大小    Status = ZwQueryValueKey(        KeyHandle,        &SourceKeyName,        KeyValuePartialInformation,        &KeyInfo,        sizeof(KEY_VALUE_PARTIAL_INFORMATION),        &Length);       if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_BUFFER_TOO_SMALL)    {        DbgPrint("读取SystemRoot键值失败!! - %#X\n", Status);        ZwClose(KeyHandle);        return Status;    }    //根据上面探测的注册表键值大小动态分配内存    AcKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePoolWithTag(NonPagedPool, Length, "tag2");    if (NULL == AcKeyInfo)    {        DbgPrint("在分配保存Key键值的内存空间时失败!!");        ZwClose(KeyHandle);        Status = STATUS_INSUFFICIENT_RESOURCES;        return Status;    }    //再次读取注册表键值    Status = ZwQueryValueKey(    KeyHandle,    &SourceKeyName,    KeyValuePartialInformation,    AcKeyInfo,    Length,    &Length);    if (!NT_SUCCESS(Status))    {        DbgPrint("读取SystemRoot键值失败!! - %#X\n", Status);        ZwClose(KeyHandle);        ExFreePool(AcKeyInfo);        return Status;    }    //写入注册表值    Status = ZwSetValueKey(    KeyHandle,    &TargetKeyName,    0,    AcKeyInfo->Type,    AcKeyInfo->Data,    AcKeyInfo->DataLength);    if (!NT_SUCCESS(Status))    {        DbgPrint("写入test键值失败!! - %#X ", Status);        ZwClose(KeyHandle);        ExFreePool(AcKeyInfo);        return Status;    }       //释放内存与句柄    ZwClose(KeyHandle);    ExFreePool(AcKeyInfo);    return Status;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

找到一篇很不错的博客: 
http://www.cnblogs.com/mydomain/archive/2 
010/10/29/1864013.html

转载请注明来源: 
enjoy5512的博客 : http://blog.csdn.net/enjoy5512 
GitHub : https://github.com/whu-enjoy


1. 获取系统开机时间

  获得当前“滴答数”: 
  获得系统日前和时间往往是为了写日志,获得启动毫秒数则很适合用来做一个随机数的种子。有时也使用时间相关的函数来寻找程序的性能瓶颈。 
  在Win32开发中,我们使用GetTickCount()函数来返回系统自启动之后经历的毫秒数。在驱动开发中,对应的函数是 KeQueryTickCount():

VOID KeQueryTickCount(         OUT PLARGE_INTEGER TickCount         );
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

  需要注意的是,返回到TickCount中的并不是一个简单的毫秒数,而是一个“滴答”数,这个数在不同硬件环境中是不一样的,所以,我们需要结合使用另一个函数:

  ULONG KeQueryTimeIncrement(void);
  • 1
  • 1

2. 获取系统时间

获得当前系统时间: 
使用KeQuerySystemTime()函数得到的当前时间是格林威治时间,接着使用ExSystemTimeToLocalTime()函数转换成当地时间:

VOID KeQuerySystemTime(         OUT PLARGE_INTEGER CurrentTime         );
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
VOID ExSystemTimeToLocalTime(         IN PLARGE_INTEGER SystemTime,         OUT PLARGE_INTEGER LocalTime         );
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

由于这两个函数使用的时间是长长整型,不便于阅读,因此,需通过函数RtlTimeToTimeFields转换成TIME_FIELDS:

VOID RtlTimeToTimeFields(         IN PLARGE_INTEGER Time,         IN PTIME_FIELDS TimeFields         );
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

3. 示例代码

//程序说明开始//==================================================================================//  功能 :  获取系统时间//  参数 :    //  (入口)  //  (出口)  无//  返回 :  VOID//  主要思路 : //  调用举例 : //  日期 : 20167622:58:19 - 20167623:42:41//==================================================================================//程序说明结束VOIDGetTime(){    LARGE_INTEGER TickCount = {0};    LARGE_INTEGER GelinTime = {0};    LARGE_INTEGER LocalTime = {0};    TIME_FIELDS NowFields;    ULONG Inc = 0;    ULONG Day = 0;    ULONG Hour = 0;    ULONG Minute = 0;    ULONG Second = 0;    Inc = KeQueryTimeIncrement();    KeQueryTickCount(&TickCount);    TickCount.QuadPart *= Inc;    TickCount.QuadPart /= 10000;    Day = TickCount.QuadPart / (1000*60*60*24);    Hour = TickCount.QuadPart % (1000*60*60*24) / (1000*60*60);    Minute = TickCount.QuadPart % (1000*60*60*24) % (1000*60*60) / (1000*60);    Second = TickCount.QuadPart % (1000*60*60*24) % (1000*60*60) % (1000*60) / 1000;    KdPrint(("系统启动了%2d%2d小时%2d分钟%2d秒\n", Day, Hour, Minute, Second));    KeQuerySystemTime(&GelinTime);    ExSystemTimeToLocalTime(&GelinTime, &LocalTime);    RtlTimeToTimeFields(&LocalTime, &NowFields);    KdPrint(("系统当前时间 : %4d%2d%2d%2d:%2d:%2d\n",        NowFields.Year,         NowFields.Month,        NowFields.Day,        NowFields.Hour,        NowFields.Minute,        NowFields.Second));}

目录(?)[+]

转载请注明来源: 
enjoy5512的博客 : http://blog.csdn.net/enjoy5512 
GitHub : https://github.com/whu-enjoy


.1. 使用系统线程

PsCreateSystemThread

NTSTATUS PsCreateSystemThread(_Out_PHANDLE ThreadHandle,_In_ULONG DesiredAccess, //所需访问权限_In_opt_POBJECT_ATTRIBUTES ObjectAttributes,_In_opt_HANDLE ProcessHandle,_Out_opt_PCLIENT_ID ClientId,_In_PKSTART_ROUTINE StartRoutine,//线程中要执行的函数_In_opt_PVOID StartContext//传递给上述要执行的函数的参数);ParametersThreadHandle[out] 线程句柄[输出参数]指向一个用于接收此句柄的变量。一旦此句柄 不再使用,驱动必须用ZwClose关闭此句柄。此句柄在WindowsVista 及以后版本的Windows系统中是内核句柄。在较早版本的Windows 里,此句柄不可以是内核句柄。DesiredAccess[in]所需访问权限[输入参数]指定ACCESS_MASK值用于指明对所创建线程的存取权限要求。ObjectAttributes[in, optional]对象属性[输入参数,可选]指向一个结构,它指定对象的属性。OBJ_PERMANENT,OBJ_EXCLUSIVE,和OBJ_OPENIF不是线程对象的有效属性。在Windows XP和更高版本的Windows,如果对方不在系统进程的上下文中运行,它必须为ObjectAttributes设置OBJ_KERNEL_HANDLE属性。对微软的Windows 2000和Windows 98/Me的驱动必须只在系统进程上下文中调用PsCreateSystemThread。对于WindowsVista 及其后版本的WindowsVista,此句柄将是一个内核句柄。ProcessHandle[in, optional] 进程句柄[输入参数,可选]指定“在其地址空间中运行线程的那个进程”的一个打开的句柄。调用者的线程必须对这个进程有process_create_thread访问权限。如果不提供此参数,则将在初始系统进程中创建线程。在为一个驱动程序创建的线程里,这个值应该是空的。可使用定义在ntddk.H中的NtCurrentProcess宏,来指定当前进程。ClientId[out, optional]客户标识[输出参数,可选]指向用于“接收新线程的客户端标识符”的结构。在为一个驱动程序创建的线程里,这个值应该是空的。StartRoutine[in]开始例程[输入参数]新创建的系统线程的入口点。这个参数是一个函数指针,指向能接收一个参数的ThreadStart例程,参数值是由调用者提供的startcontext参数。StartContext[in, optional]开始语境[输入参数,可选]当本函数(PsCreateSystemThread)开始执行时,提供一个单独的参数传递给所创建的线程。返回值PsCreateSystemThread如果成功创建线程则返回STATUS_SUCCESS.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

2. 线程中睡眠

NTSTATUS KeDelayExecutionThread(  _In_ KPROCESSOR_MODE WaitMode,  _In_ BOOLEAN         Alertable,  _In_ PLARGE_INTEGER  Interval);ParametersWaitMode [in]Specifies the processor mode in which the caller is waiting, which can be either KernelMode or UserMode. Lower-level drivers should specify KernelMode.Alertable [in]Specifies TRUE if the wait is alertable. Lower-level drivers should specify FALSE.Interval [in]Specifies the absolute or relative time, in units of 100 nanoseconds, for which the wait is to occur. A negative value indicates relative time. Absolute expiration times track any changes in system time; relative expiration times are not affected by system time changes.Return valueKeDelayExecutionThread returns one of the following values that describes how the delay was completed:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
Return codeDescriptionSTATUS_SUCCESSThe delay completed because the specified interval elapsed.STATUS_ALERTEDThe delay completed because the thread was alerted.STATUS_USER_APCA user-mode APC was delivered before the specified Interval expired.

Note that the NT_SUCCESS macro recognizes all of these status values as “success” values.


3. 使用同步事件

VOIDKeInitializeEvent(IN PRKEVENT  Event,     // 这个参数是初始化事件对象的指针IN EVENT_TYPE  Type,  // 这个参数是事件的类型。一类是“通知事件”对应参数为 NotificationEvent。另一类是“同步事件”,对应参数 SynchronizationEventIN BOOLEAN State         // TRUE:事件对象初始化状态为激发。FALSE:事件对象初始化状态为未激发);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
LONG KeSetEvent(  _Inout_ PRKEVENT  Event,  _In_    KPRIORITY Increment,  _In_    BOOLEAN   Wait);ParametersEvent [in, out]A pointer to an initialized event object for which the caller provides the storage.Increment [in]Specifies the priority increment to be applied if setting the event causes a wait to be satisfied.Wait [in]Specifies whether the call to KeSetEvent is to be followed immediately by a call to one of the KeWaitXxx routines. If TRUE, the KeSetEvent call must be followed by a call to KeWaitForMultipleObjects, KeWaitForMutexObject, or KeWaitForSingleObject. For more information, see the following Remarks section.Return valueIf the previous state of the event object was signaled, a nonzero value is returned.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
NTSTATUS KeWaitForSingleObject(  _In_     PVOID           Object,  _In_     KWAIT_REASON    WaitReason,  _In_     KPROCESSOR_MODE WaitMode,  _In_     BOOLEAN         Alertable,  _In_opt_ PLARGE_INTEGER  Timeout);ParametersObject [in]Pointer to an initialized dispatcher object (event, mutex, semaphore, thread, or timer) for which the caller supplies the storage.WaitReason [in]Specifies the reason for the wait. A driver should set this value to Executive, unless it is doing work on behalf of a user and is running in the context of a user thread, in which case it should set this value to UserRequest.WaitMode [in]Specifies whether the caller waits in KernelMode or UserMode. Lowest-level and intermediate drivers should specify KernelMode. If the given Object is a mutex, the caller must specify KernelMode.Alertable [in]Specifies a Boolean value that is TRUE if the wait is alertable and FALSE otherwise.Timeout [in, optional]Pointer to a time-out value that specifies the absolute or relative time, in 100-nanosecond units, at which the wait is to be completed.A positive value specifies an absolute time, relative to January 1, 1601. A negative value specifies an interval relative to the current time. Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.If *Timeout = 0, the routine returns without waiting. If the caller supplies a NULL pointer, the routine waits indefinitely until the dispatcher object is set to the signaled state. For more information, see the following Remarks section.Return valueKeWaitForSingleObject can return one of the following:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
Return codeDescriptionSTATUS_SUCCESSThe dispatcher object specified by the Object parameter satisfied the wait.STATUS_ALERTEDThe wait was interrupted to deliver an alert to the calling thread.STATUS_USER_APCThe wait was interrupted to deliver a user asynchronous procedure call (APC) to the calling thread.STATUS_TIMEOUTA time-out occurred before the object was set to a signaled state. This value can be returned when the specified set of wait conditions cannot be immediately met and Timeout is set to zero.

Note that the NT_SUCCESS macro recognizes all of these status values as “success” values.


4. 示例代码

static KEVENT s_Event//程序说明开始//==================================================================================//  功能 :  测试线程函数//  参数 :    //  (入口)  //  (出口)  无//  返回 :  VOID//  主要思路 : //  调用举例 : //  日期 : 2016年7月7日 07:24:03 - 2016年7月7日 07:44:41//==================================================================================//程序说明结束NTSTATUSThread(){    static UNICODE_STRING str = RTL_CONSTANT_STRING(L"test thread");    HANDLE ThreadHandle = NULL;    NTSTATUS Status = STATUS_SUCCESS;    KdPrint(("启动线程函数\n"));    //初始化事件    //第二个参数是事件的类型,一类是“通知事件”,对应参数是NotificationEvent。    //另一类是“同步事件”,对应是SynchronizationEvent      //第三个参数如果为真,事件对象的初始化状态为激发状态。    //如果为假,则事件对象的初始化状态为未激发状态      //如果创建的事件对象是“通知事件”,当事件对象变为激发状态时,    //程序员需要手动将其改回未激发      //如果创建的事件对象是“同步事件”,当事件对象为激发状态时,    //如果遇到KeWaitForXXX等内核函数,事件对象则自动变回未激发状态      //  当是TRUE 时初始化事件是有信号状态.,当是FALSE时初始化事件是没信号状态,    //如果此处为TRUE,则为有信号状态,KeWaitForSingleObject会直接通过,    //此时需要调用KeResetEvent来设置为无信号     KeInitializeEvent(&s_Event, SynchronizationEvent, FALSE);    //KeResetEvent(&s_Event);//指定的事件对象设置为无信号状态。    //启动线程    Status = PsCreateSystemThread(        &ThreadHandle,        0,NULL,NULL,NULL,        ThreadProc,        (PVOID)&str);    if (!NT_SUCCESS(Status))    {        DbgPrint("线程启动失败!!-%#X");        return Status;    }    //Do Something    KdPrint(("做一些事!!\n"));    KdPrint(("线程启动函数结束!!\n"));    ZwClose(ThreadHandle);    KeWaitForSingleObject(&s_Event, Executive, KernelMode, 0, 0);    KdPrint(("线程启动函数返回!!\n"));    return Status;}//程序说明开始//==================================================================================//  功能 :  线程函数//  参数 :    //  (入口)  //  (出口)  无//  返回 :  VOID//  主要思路 : //  调用举例 : //  日期 : 2016年7月7日 07:44:29 - 2016年7月7日 08:51:17//==================================================================================//程序说明结束VOIDThreadProc(__in PVOID Context){    PUNICODE_STRING str = (PUNICODE_STRING)Context;    LARGE_INTEGER Interval;    LONG Msec = 3000;    //打印字符串    KdPrint(("进入线程函数 : %wZ\n", str));    //Msec若为1000,则 睡眠的时间为: 1000 * 100 ns * 10 *1000 =1s     Interval.QuadPart = DELAY_ONE_MILLISECOND;    Interval.QuadPart *= Msec;    GetTime();    GetTime();    KeDelayExecutionThread(KernelMode, 0, &Interval);    GetTime();    KdPrint(("线程函数结束\n"));    //设置事件    KeSetEvent(&s_Event, 0, TRUE);    //结束自己    PsTerminateSystemThread(STATUS_SUCCESS);}

原创粉丝点击