本帖最后由 YFSafe 于 2022-8-15 14:17 编辑
这是驱动开发教程的最后一个章节,也是最关键的一点.目前为止所有的驱动程序代码都导向这个分发例程.这个分发例程师完成实际工作的例程,它将为给定的线程设置请求的优先级.
首先我们需要检查控制代码.通常驱动程序会支持多个控制代码,一旦发现了为识别的控制代码,我们需要立刻让这个请求失败.
[C++] 纯文本查看 复制代码 NTSTATUS SetPriorityDeviceControl(PDEVICE_OBJECT,PIRP Irp)
{
auto stack = IoGetCurrentIrpStackLocation(Irp);
auto status = STATUS_SUCCESS;
switch(stack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_SET_PRIORITY:
//TO DO:修改优先级
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
对于任意的IRP,获取信息的关键在于检查与当前的设备层相连的IO_STACK_LOCATION.调用IoGetCurrentIrpStackLocation函数会返回一个指向正确的IO_STACK_LOCATION的指针.在我们当前的情况下,只有一个IO_STACK_LOCATION.但是无论怎么样,调用IoGetCurrentIrpStackLocation都是正确的做法.
IO_STACK_LOCATION的主要部分是一个巨大的联合体成员,叫做Patameters.它包含了多个结构体.每一类IRP都有一个结构体.对于IRP_MJ_DEVICE_CONTROL,需要观察的结构是DeviceIoControl.在哪里我们能够找到客户程序传递过来的信息,比如控制代码;缓冲区以及它的长度.
上面的switch语句使用IoControlCode成员来确定是否是能处理的控制代码.如果不是,我们就将状态设置为不成功的某个值并跳出switch语句.
我们所需的最后一部分常见代码是在switch语句之后完成IRP的代码,无论成功与否.否则,客户程序将得不到请求完成的响应.
[C++] 纯文本查看 复制代码 Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return status;
以上代码使用当前的状态值完成了Irp.如果不能识别控制代码,那就是一个失败状态.反之,在能识别控制代码的情况下,结果状态就取决于实际的工作状态.
最后一块的代码最重要:执行真正改变线程优先级的工作.第一步是检查接收到的缓冲区是否足够大,大到能够容纳一个ThreadData对象.用户提供的输入缓冲区的指针可以从Type3InputBuffer成员中得到,输入缓冲区的大小由InputBufferLength指出:
[C++] 纯文本查看 复制代码 if(stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ThreadData))
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}
下一步,假设缓冲区足够大,我们就把它当作ThreadData来处理.
[C++] 纯文本查看 复制代码 auto data = (ThreadData*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
如果缓冲区的指针是NULL,那我们就需要终止.
[C++] 纯文本查看 复制代码 if(data == NULL)
{
status = STATUS_INVALID_PARAMETER;
break;
}
然后,检查优先级是否在1~31的合法范围之内,如果不是的话就终止.
[C++] 纯文本查看 复制代码 if(data->Priority < 1 || data->Priority > 31)
{
status = STATUS_INVALID_PARAMETER;
break;
}
我们离目标越来越近了!!!我们需要用的API是KeSetPriorityThread.函数原型如下:[C++] 纯文本查看 复制代码 KPRIORITY KeSetPriorityThread(
PKTHREAD Thread,
KPRIORITY Priority);
1.Thread————被修改优先级的线程对象指针.
2.Priority————要修改的优先级值.
我们现在拥有一个线程ID,该如何把它转变成我们需要的Thread参数呢?
在这里,我们就需要用到一个函数:PsLookupThreadByThreadId.为了得到这个函数的定义,我们需要加入另一个头文件:
[C++] 纯文本查看 复制代码 #include <ntifs.h>
注意必须讲这个头文件加在#include <ntddk.h>之前,否则会报很多错.
现在我们可以将线程ID转换成对象指针了.
[C++] 纯文本查看 复制代码 PETHREAD Thread;
status = PsLookupProcessByProcessId(ULongToHandle(data->ThreadId),&Thread);
if(!NT_SUCCESS(status))
break;
现在我们已经准备好修改优先级了.下面是进行线程优先级修改的代码.
[C++] 纯文本查看 复制代码 KeSetPriorityThread((PKTHREAD)Thread,data->Priority);
接下来,我们需要减少这个线程对象的引用计数,完成这个任务的函数是ObDerefere
[C++] 纯文本查看 复制代码 ObDereferenceObject(Thread); nceObject;
下面是完整的IRP_MJ_DEVICE_CONTROL分发例程代码,给大家参考:
[C++] 纯文本查看 复制代码 NTSTATUS SetPriorityDeviceControl(PDEVICE_OBJECT,PIRP Irp)
{
auto stack = IoGetCurrentIrpStackLocation(Irp);
auto status = STATUS_SUCCESS;
switch(stack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_SET_PRIORITY:
//TO DO:修改优先级
if(stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ThreadData))
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}
auto data = (ThreadData*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
if(data == NULL)
{
status = STATUS_INVALID_PARAMETER;
break;
}
if(data->Priority < 1 || data->Priority > 31)
{
status = STATUS_INVALID_PARAMETER;
break;
}
PETHREAD Thread;
status = PsLookupProcessByProcessId(ULongToHandle(data->ThreadId),&Thread);
if(!NT_SUCCESS(status))
break;
KeSetPriorityThread((PKTHREAD)Thread,data->Priority);
ObDereferenceObject(Thread);
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return status;
}
|