Windows内核开发-4-内核编程基础

Windows内核开发-4-内核编程基础

这里会构建一个简单可是完整的驱动程序和一个客户端,部署内核执行一些平时user下没法执行的操做。html

将经过如下内容进行讲解:ios

1 介绍编程

2 驱动初始化数组

3 Create和Close操做调度实例数据结构

4 DeviceIoControl操做调度实例app

5 安装和测试驱动程序函数

整个完整源代码最后面测试

1 介绍

该驱动将解决Windows API设置线程优先级的不灵活性。spa

在User模式下,线程的优先级由其进程优先级类和基于每一个线程的偏移量组合来肯定,偏移量具备有限的级别数。更改进程的优先级类别能够采用SetPriorityClass函数来实现。命令行

每一个优先级类对应着一个优先级,这个对应的优先级也是在进程中建立线程时默认的优先级。可使用SetThreadPriority函数来修改特定线程的优先级。

基于进程优先级类和线程的优先级偏移量的可用线程优先级图 :

Priority Class -Sat -2 -1 0(default) +1 +2 +Sat Comments
Idle(Low) 1 2 3 4 5 6 15  
Below Normal 1 4 5 6 7 8 15  
Normal 1 6 7 8 9 10 15  
Above Normal 1 8 9 10 11 12 15  
High 1 11 12 13 14 15 15 只有6个级别能够选,不是7个。
Real-time 16 22 23 24 25 26 31 16-31全部级别均可以选

SetThreadPriority函数能够接受指定偏移量的值,五个普通级别分别对应的偏移量是从-2到2:THREAD_PRIORITY_LOWEST (-2), THREAD_PRIORITY_BELOW_NORMAL (-1), THREAD_PRIORITY_NORMAL (0), THREAD_PRIORITY_ABOVE_NORMAL (+1), THREAD_PRIORITY_HIGHEST (+2)。另外两个级别被称为饱和级别,将优先级设置为支持的两个极端:THREAD_PRIORITY_IDLE (-Sat) 和 THREAD_PRIORITY_TIME_CRITICAL (+Sat)。

//修改优先级的例子
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
//将进程优先级类修改成ABOVE NORAML
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
//将线程优先级修改成Above_normal

上面的基于进程优先级类和线程的优先级偏移量的可用线程优先级图表示了咱们要解决的问题,这里只有一小部分的线程优先级能够设置,咱们此次准备写的驱动就是为了来绕过这些限制,容许将线程的优先级设置为任意数字而且不用考虑进程优先级类。

小结:设计驱动的目的就是在User态下设计的线程优先级不太行,东西比较少,并且麻烦,采用内核来处理后直接将线程的优先级设置为任何数字并且不用考虑进行的优先级类。

 

2 Driver Initialization初始化驱动

建立WDM项目,删除inf文件,再建立C++源文件,而后添加WDK头文件建立一个空的DriverEntry()函数。

#include<ntddk.h>

extern"C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{




return STATUS_SUCCESS;
}

大部分通常的驱动须要实现如下内容:

1 设置Unload卸载函数。

2 设置驱动程序支持的调度实例。

3 建立设备对象。

4 建立对设备对象的符号连接。

实现完以上的内容后,一个驱动程序就能够进行交互了。

2.1 Unload

第一步:建立一个Unload实例函数,而且将DriverEntry中的驱动对象指针指向Unload:

#include<ntddk.h>

void PriorityBoosterUnload(_In_ PDRIVER_OBJECT DriverObject);


extern"C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = PriorityBoosterUnload;
return STATUS_SUCCESS;
}

对于Unload函数,咱们须要根据DriverEntry函数来具体实现里面的逻辑结构,由于该函数的主要目的仍是释放资源。

2.2 Dispatch routines调度实例

其实也能够理解为交互。其实全部的驱动都应该支持IRP_MJ_CREATE和IRP_MJ_CLOSE操做,否则是没法打开和关闭驱动对象的。

在DriverEntry中添加如下代码:

#include<ntddk.h>

void PriorityBoosterUnload(_In_ PDRIVER_OBJECT DriverObject);
NTSTATUS PriorityBoosterCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp);//新增


extern"C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = PriorityBoosterUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = PriorityBoosterCreateClose;//新增
DriverObject->MajorFunction[IRP_MJ_CLOSE] = PriorityBoosterCreateClose;//新增
return STATUS_SUCCESS;
}

这里咱们注意到Create和CLOSE都指向的是同一个实例函数,这是由于它们两个执行的代码逻辑差很少,若是有比较复杂的状况,能够将其分开写。其实全部的驱动对象的majorfunction函数指针数组都有一个相同的原型(由于他们都是函数指针数组的一部分),因此这里的新增的函数申明就是major function对于的函数原型:

NTSTATUS PriorityBoosterCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp);
//固然函数的名字是能够改的。

该函数必须返回一个NTSTATUS类型变量,而后接受一个设备对象的指针,和一个指向I/O Request Packet(IRP)的指针,对于全部类型的请求,IRP是存储请求信息的主要对象。

2.3 将信息传给驱动程序

光有Create和Close确定不够的,由于咱们的需求里面咱们还须要告诉驱动给那一个线程设置成为何优先级。用User Client的角度来看有三个基本API能够用WriteFile,ReadFile,DeviceIoControl,Read是一个读,不能写数据进去,因此从驱动程序来看就能够不用这个函数了,由于咱们要把信息传给驱动。对于Write和DeviceIoControl的选择这个就全看你们喜欢了。通常来讲若是真的是一个写的操做就用Write,可是对于其它任何东西DeviceIoControl确定是首选,由于它是将数据传入和传出驱动程序的通用机制。

更改线程的优先级并非纯粹的Write,因此这里咱们采用DeviceIoControl:

BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice,
_In_ DWORD dwIoControlCode,
_In_reads_bytes_opt_(nInBufferSize) LPVOID lpInBuffer,
_In_ DWORD nInBufferSize,
_Out_writes_bytes_to_opt_(nOutBufferSize,*lpBytesReturned) LPVOID lpOutBuffer,
_In_ DWORD nOutBufferSize,
_Out_opt_ LPDWORD lpBytesReturned,
_Inout_opt_ LPOVERLAPPED lpOverlapped);

对DeviceIoControl来讲有三个东西很是重要:

1:可控制的代码

2:一个输入缓冲区

3:一个输出缓冲区

DeviceIoControl:比较灵活,能够支持多种控制代码。

在驱动端,DeviceIoControl对应IRP_MJ_DEVICE_CONTROL的MajorFunction函数指针数组的内容。因此添加已下代码到DriverEntry中:

 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PriorityBoosterDeviceControl;

2.4 客户端和驱动的通讯协议

为了让Client和kernel能够交换数据,咱们得实现刚刚申明的PriorityBoosterDeviceControl函数,咱们须要控制的代码逻辑以及输出输入的缓冲区,缓冲区应该包含咱们须要的线程ID和要设置的优先级,这些信息由客户端提供驱动对其采起行动。要两个交互就意味着Client/kernel须要有一个单独的文件来传输信息。

因此这里咱们新建一个PriorityBoosterCommon.h 头文件来做为信息传输的介质,该文件也会被Client使用。

该文件咱们须要两个数据一个是须要的结构体,另外一个是更改线程优先级的控制代码。

先看看结构体:

struct ThreadData {
ULONG ThreadId;
int Priority;
};

须要线程的惟一ID和目标优先级,TID(Thread ID)是一个32位无符号整数,用ULONG不用DWORD是由于ntddk里面没有DWORK只有ULONG,而ULONG比较通用。

优先级应该是1-31之间的数字,因此采用一个简单的int就行了。

接下来还须要一个控制代码,该控制代码必须采用CTL_CODE宏来定义,该宏接受构成最终控制代码的四个参数,CTL_CODE宏的定义:

#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))

对这段宏定义的讲解

参数 做用
DeviceType 标识一种设备,能够是WDK中定义的FILE_DEVICE_XXX里面常量之一,可是这个主要用于硬件,和咱们这个软件的驱动来讲这个值不重要,可是微软指定第三方的驱动程序的这个值应该以0x8000开头
Function 一个升序数字用来表示特定操做,通常状况下,这个数字在同一驱动的不一样控制代码下必须不一样,一样,任何数字均可以,可是官方文档规定 第三方驱动程序该值应该以0x800开头
Method 最重要的部分,表示客户端提供的输入和输出缓冲区如何传递给驱动程序,对于咱们的驱动程序这里采用最简单的值METHOD_NEITHER
Access 指示此操做是针对驱动程序 (FILE_WRITE_ACCESS)、来自驱动程序 (FILE_READ_ACCESS) 仍是双向 (FILE_ANY_ACCESS)。

这里咱们采用下面这种宏定义:

#define PRIORITY_BOOSTER_DEVICE 0x8000
#define IOCTL_PRIORITY_BOOSTER_SET_PRIORITY CTL_CODE(PRIORITY_BOOSTER_DEVICE, \
0x800, METHOD_NEITHER, FILE_ANY_ACCESS)

2.3 建立设备对象

在DriverEntry中还须要设备对象,以便咱们能够打开句柄来到达驱动程序。

典型的软件驱动程序只须要一个设备对象,并带有指向它的符号连接(能够理解为文件的快捷方式)来方便User Client获取它的句柄。

建立一个设备对象须要使用IoCreateDevice API:

NTSTATUS IoCreateDevice(
_In_ PDRIVER_OBJECT DriverObject,
_In_ ULONG DeviceExtensionSize,
_In_opt_ PUNICODE_STRING DeviceName,
_In_ DEVICE_TYPE DeviceType,
_In_ ULONG DeviceCharacteristics,
_In_ BOOLEAN Exclusive,
_Outptr_ PDEVICE_OBJECT *DeviceObject);

IoCreateDevice API参数解析

参数 说明
DriverObject 设备对象所属的驱动程序对象,通常只用来传递给DriverEntry函数的驱动程序对象
DeviceExtensionSize 除了sizeof(DEVICE-OBJECT)还有额外字节,用于将某些数据结构与设备相关联。 对于仅建立单个设备对象的软件驱动程序而言,它不太有用,由于设备所需的状态能够简单地由全局变量管理。
DeviceName 内部设备名称,一般在设备对象管理器目录下建立
DeviceType 与某种类型的基于硬件的驱动程序相关。对于软件驱动采用FILE_DEVICE_UNKNOWN值
DeviceCharacteristics 一组标志和某些特定驱动程序相关(软件驱动程序不多用它),若是软件驱动程序支持真正的命名空间则该值指定0或者FILE_DEVICE_SECURE_OPEN
Exclusive 是否容许多个文件对象打开同一设备。FALSE赞成,TRUE不一样意
DeviceObject 返回的设备对象指针,若是成功函数会从Non paged Pool非分页内存池分配结构并将结果指针存储在引用参数中

在建立设备对象前,先要建立一个UNICODE_STRING字符串来保存该内部设备名字:

UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\PriorityBooster");
// 或者RtlInitUnicodeString(&devName, L"\\Device\\ThreadBoost");

设备对象的名字能够是任意的,可是必须在Device目录下。有两种使用常量字符串来初始化UNICODE_STRING的办法,第一种是使用RtlInitUnicodeString 这个很好用,可是RtlInitUnicodeString必须计算字符串中的字符数才能很好的初始化。

还有一种更快的办法是采用RTL_CONSTANT_STRING宏,它在编译时静态计算字符串的长度,这意味着它只能与常量字符串一块儿工做。

而后在DriverEntry中写入咱们的代码:

PDEVICE_OBJECT DeviceObject;
NTSTATUS status = IoCreateDevice(
DriverObject // our driver object,
0 // no need for extra bytes,
&devName // the device name,
FILE_DEVICE_UNKNOWN // device type,
0 // characteristics flags,
FALSE // not exclusive,
&DeviceObject // the resulting pointer
);
if (!NT_SUCCESS(status)) {
KdPrint(("Failed to create device object (0x%08X)\n", status));
return status;
}

2.4 建立符号连接

如今咱们有一个指向咱们的设备对象的指针,下一步须要提供符号连接来使User态的调用者能够访问该设备对象,如下几行代码建立一个符号连接并将其链接到咱们的设备对象:

UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\PriorityBooster");
status = IoCreateSymbolicLink(&symLink, &devName);
if (!NT_SUCCESS(status)) {
KdPrint(("Failed to create symbolic link (0x%08X)\n", status));
IoDeleteDevice(DeviceObject);
return status;
}

一样名字随你取,可是目录必须是 \??目录下

IoCreateSymbolicLink 经过接受符号连接和连接的目标来完成工做。可是务必注意,若是建立失败,须要调用IoDeleteDevice来销毁建立的内容。通常状况下,若是DriverEntry返回的是失败状态,则不会调用Unload函数,若是咱们有不少初始化要作,那么记得若是失败记得销毁掉。

一旦咱们前面的都成功了,那么必定不要忘记在Unload函数里面撤销在DriverEntry中所作的任何事情。

2.5 unload撤销

咱们前面建立了设备对象,已经符号连接,是先有的设备对象后有的符号连接。因此咱们销毁的时候得反着来,先销毁符号连接,再销毁设备对象。这里有点像C++的析构函数了。

void PriorityBoosterUnload(_In_ PDRIVER_OBJECT DriverObject) {
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\PriorityBooster");
// delete symbolic link
IoDeleteSymbolicLink(&symLink);
// delete device object
IoDeleteDevice(DriverObject->DeviceObject);
}

2.6 初始化驱动小结:

对于驱动通常有几个板块,1 开始 2 中间交互 3 结束

中间交互呢通常是User和Kernel经过I/O设备对象来进行交互,经常是采起开辟设备对象,而后User采用符号连接来使用该对象设备对象能够理解为一个用来交互的东西。而后结束须要删除掉中间用了的东西。

3 Client Code 编写客户端代码

再在该解决方案下添加一个空项目,而后新建一个.cpp文件来编写客户端代码:

image-20210723144511398

添加如下头文件:

#include"../PriorityBooster/PriorityBoosterCommon.h"
#include<Windows.h>
#include<stdio.h>
#include<iostream>
//PriorityBoosterCommon.h是咱们用来给Client和Driver进行交互的文件。

修改main函数来接受命令行参数,咱们须要接受线程id,和优先级的value值。

int main(int argc, const char* argv[])
{
if (argc < 3)
{
std::cout << "Usage: Booster <threadid> <priority>" << endl;
return 0;
}
}

而后须要打开设备的句柄来获取设备传输的数据,CreateFile 的第一个参数“filename”应该是前缀为“\\.\”的符号连接:

HANDLE hDevice = CreateFile(L"\\\\.\\PriorityBooster", GENERIC_WRITE,
FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (hDevice == INVALID_HANDLE_VALUE)
return Error("Failed to open device");
//再定义了一个error函数来打印错误的文本。
int Error(const char* message) {
printf("%s (error=%d)\n", message, GetLastError());
return 1;
}

CreateFile会经过IRP_MJ_CREATE调度实例来写入驱动,若是驱动没有加载就会报错说没有符号连接。就会收到错误2(File not found)。

如今经过符号连接拿到了设备的句柄,如今开始能够调用DeviceIoControl和设备对象进行交互了。在交互前先定义结构体和给给结构体赋值。

 ThreadData TempData;
TempData.ThreadId = atoi(argv[1]);//命令行的第一个参数,atoi字符串int
TempData.Priority = atoi(argv[2]);//命令行的第一个参数

调用DeviceIoControl传递数据,而后关闭Device句柄:

 DWORD returned;
BOOL success = DeviceIoControl(
hDevice,//设备句柄
IOCTL_PRIORITY_BOOSTER_SET_PRIORITY,//控制代码
&TempData, sizeof(TempData),//输入buffer和length
nullptr, 0, //输出buffer和length
&returned, nullptr
);
CloseHandle(hDevice);

DeviceControl经过IRP_MJ_DEVICE_CONTROL majorfunction的实例函数来和driver交互。

这样客户端的代码就搞定了。

4 打开和关闭的调度函数实例

如今咱们须要添加的就是驱动代码里面的调度函数,由于以前咱们只是申明了而已,并无实现这个函数。

4.1 Create/Close调度函数

Create和Close的调度函数是最好实现的,只须要返回成功就好。

_Use_decl_annotations_//函数的注释和参数的注释同样,无关紧要
NTSTATUS PriorityBoosterCreateClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

每一个调度实例函数都接受设备对象和一个I/O对象IRP,设备对象不用处理,由于咱们这里只有一个设备对象只能是咱们在DriverEntry中建立的那个,IRP很是重要,在下下章讲。

IRP是一个表示请求(交互)的半文档结构,一般来自执行中的管理器:I/O管理器、即插即用管理器或电源管理器。对于一个简单的软件驱动程序极可能就是一个I/O管理器,IRP如何建立不用考虑,driver的目的都是用来处理IRP,请求(交互)的细节须要完成它才行。

驱动的各类请求都是包含在IRP中,经过查看IRP的成员能够找到请求的类型和详细数据。

须要注意的是IRP不会单独运行,它伴随着一个或多个IO_STACK_LOCATION类型的结构。咱们这个简单驱动就只有一个IO_STACK_LOCATION。

简单来讲就是咱们须要的一些信息在基础的IRP结构里,还有一些在咱们设备堆栈的IO_STACK_LOCATION中。

在建立和关闭的状况下,咱们不须要查看任何成员。 咱们只须要在其 IoStatus 成员(类型为 IO_STATUS_BLOCK)中设置 IRP 的状态,该成员有两个成员:

 

   
Status 代表此请求将完成的状态
Information 一个多态成员,在不一样的请求中意味着不一样的东西。 在建立和关闭的状况下,零值就能够了。

为了真正完成IRP,还在最后调用了IoCompleteRequest函数,这个函数主要是将IRP传播回给它的调用者通知客户端操做已经完成。第二个参数是驱动程序能够提供给其客户端的临时优先级提高至,大多数状况下0值是比较好的IO_NO_INCREMENT被定义为0,由于这样请求就是同步的了,就你们优先级都同样。该函数还要作的最后一个操做是返回与放入 IRP 的操做相同的状态。

4.2 DeviceIoControl调度函数

这是最重要的地方了。首先须要检查的是控制代码。 典型的驱动程序可能支持不少控制码,因此若是控制码不被识别,咱们当即返回请求失败 :

_Use_decl_annotations_
NTSTATUS PriorityBoosterDeviceControl(PDEVICE_OBJECT, PIRP Irp) {
// 获取IO_STACK_LOCATION
auto stack = IoGetCurrentIrpStackLocation(Irp); // IO_STACK_LOCATION*
auto status = STATUS_SUCCESS;
switch (stack->Parameters.DeviceIoControl.IoControlCode) {
//获取控制代码
case IOCTL_PRIORITY_BOOSTER_SET_PRIORITY:
// do the work
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}

任何获取IRP信息的关键是查看和当前设备管理的IO_STACK_LOCATION中的内容,调用IoGetCurrentIrpStackLocation会返回一个指向正确IO_STACK_LOCATION的指针。

IO_STACK_LOCATION中的主要成分是一个名为Parameters 的union成员,它包含一组结构体和每种IRP一一对应。

在IRP_MJ_DEVICE_CONTROL 状况下,咱们要查看它的DeviceControl成员,在该结构体中咱们能够找到传递给client的信息,如:控制代码、缓冲区和缓冲区长度等等。

无论前面怎么判断,最后必须有一段代码来肯定执行,来返回status:

Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;

这段代码一般是放到最后,若是前面的都判断正确时才真正实现DeviceControl。

 

开头和结尾咱们写好了,最后是最好玩和最重要的部分了,就是修改线程优先级。

首先咱们要检测咱们收到的缓冲区是否足够大能够包含一个ThreadData,为何要检测?由于Kernel和User下的栈是不同的,不属于一个东西,因此必须检查,特别是对于kernel的东西检查是很是重要的。

指向User提供的输入缓冲区指针在Type3InputBuff中,输入缓冲区长度在InputBufferLength中:

 if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ThreadData)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}

而后假设缓冲区够大,那么咱们就获取获得缓冲区指针:

auto data = (ThreadData*)stack->Parameters.DeviceIoControl.Type3InputBuffer;

若是指针为空须要终止:

 if (data == nullptr) {
status = STATUS_INVALID_PARAMETER;
break;
}

而后检测线程的优先级number是否在1-31之间:

 if (data->Priority < 1 || data->Priority > 31) {
status = STATUS_INVALID_PARAMETER;
break;
}

接下来调用设置线程优先级API :

KPRIORITY KeSetPriorityThread(
_Inout_ PKTHREAD Thread,
_In_ KPRIORITY Priority);

PKTHREAD是一个8位整数,线程自己是由一个指向KTHREAD对象的指针来标识的,KTHREAD是内核管理线程的方式之一,KTHREAD没有文档来记录只能由API来经过线程ID获取Kernel中指向真是线程对象的指针。该API叫作PsLookupThreadByThreadId,使用它须要添加头文件<ntifs.h>。

如今能够把线程ID变成一个指针了:

PETHREAD Thread;
status = PsLookupThreadByThreadId(ULongToHandle(data->ThreadId), &Thread);
if (!NT_SUCCESS(status))
break;

找到以后就能够调用优先级函数来改动优先级了:

KeSetPriorityThread((PKTHREAD)Thread, data->Priority);

可是在使用完以后还须要释放线程句柄防止资源滥用:

ObDereferenceObject(Thread);

最终函数代码:

_Use_decl_annotations_
NTSTATUS PriorityBoosterDeviceControl(PDEVICE_OBJECT, PIRP Irp) {
// get our IO_STACK_LOCATION
auto stack = IoGetCurrentIrpStackLocation(Irp);
auto status = STATUS_SUCCESS;

switch (stack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_PRIORITY_BOOSTER_SET_PRIORITY:
{
// do the work
if (stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ThreadData)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}

auto data = (ThreadData*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
if (data == nullptr) {
status = STATUS_INVALID_PARAMETER;
break;
}

if (data->Priority < 1 || data->Priority > 31) {
status = STATUS_INVALID_PARAMETER;
break;
}

PETHREAD Thread;
status = PsLookupThreadByThreadId(ULongToHandle(data->ThreadId), &Thread);
if (!NT_SUCCESS(status))
break;

KeSetPriorityThread((PKTHREAD)Thread, data->Priority);
ObDereferenceObject(Thread);
KdPrint(("Thread Priority change for %d to %d succeeded!\n",
data->ThreadId, data->Priority));
break;
}

default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}

Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}

最终的彻底代码:

Windows内核驱动--实现修改线程优先级demo - Sna1lGo - 博客园 (cnblogs.com)

5 安装和测试

终于到了这里了,千万不要把东西直接安装到本机上,说不定就蓝屏了。最好是用虚拟机来操做。

在虚拟机中用sc.exe来加载,不清楚的能够看一看前面的博客:

Windows内核开发-2-开始内核开发-2-内核开发入门 - Sna1lGo - 博客园 (cnblogs.com)

 

 

 

添加并加载该驱动,采用WinObj来查看加载的数据:

符号连接没问题

 

 

 

而后使用一下:

这里咱们经过process Explorer看到cmd进程由一个线程的级别是8,咱们给它改一下

 

 

booster 768 25

 

 

搞定

总结

这里从头至尾写了一个简单可是完整的驱动,还写了一个客户端交互。挺不错了。加油!

相关文章
相关标签/搜索