查看: 481|回复: 0

[原创] [驱动开发教程]2.6.DeviceIoControl分发例程

[复制链接]

4

技术

17

魅力

6

原创

版主

禁止发言

Rank: 7Rank: 7Rank: 7

积分
5370
人气
208
分享
36

最佳新人活跃会员

发表于 2022-8-15 13:59:30 | 显示全部楼层 |阅读模式
本帖最后由 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;
}


评分

参与人数 2经验 +22 人气 +3 收起 理由
剑指巅峰 + 22 赞一个!
explore + 3 膜拜

查看全部评分

本帖被以下淘专辑推荐:

YF工作室驻x64论坛分部
工作室曾开发的软件:YFSafe安全软件,YFChat在线聊天软件,MBRTools等。
欢迎有能力的你加入我们一起共同进步。请发邮件至yfstudio2021@outlook.com
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表