zoukankan      html  css  js  c++  java
  • 自己写的驱动用CreateFile打开时错误码返回1的问题

    就像题目上说的,今天在写一个例子代码时遇到了这个问题,下面是当时驱动层和应用层的代码:

    #include <ntddk.h>
    #define BASE_CODE 0x800
    #define CREATE_THREAD_COMMAND CTL_CODE(FILE_DEVICE_UNKNOWN, BASE_CODE + 1, METHOD_BUFFERED, FILE_ANY_ACCESS)
    #define DEVICE_NAME L"\Device\ThreadDevice"
    #define LINK_NAME L"\??\ControlDevice"
    VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
    {
        UNICODE_STRING uLinkName;
        PDEVICE_OBJECT pDev = pDriverObject->DeviceObject;
        RtlInitUnicodeString(&uLinkName, LINK_NAME);
        IoDeleteSymbolicLink(&uLinkName);
    
        IoDeleteDevice(pDev);
        DbgPrint("Goodbye world!
    ");
    }
    
    VOID MyProcessThread(PVOID pContext)
    {
        //获取当前发送IRP请求的线程名
        PEPROCESS pCurrProcess = IoGetCurrentProcess();
        PTSTR pProcessName = (PTSTR)((CHAR*)pCurrProcess + 0x174);
    //  UNREFERENCED_PARAMETER(pContext);
        DbgPrint("MyProcessThread Current Process %s
    ", pProcessName);
        PsTerminateSystemThread(0);
    }
    
    VOID SystemThread(PVOID pContext)
    {
        //获取系统进程名
        PEPROCESS pCurrProcess = IoGetCurrentProcess();
        PTSTR pProcessName = (PTSTR)((CHAR*)pCurrProcess + 0x174);
    //  UNREFERENCED_PARAMETER(pContext);
        DbgPrint("MyProcessThread Current Process %s
    ", pProcessName);
        PsTerminateSystemThread(0);
    }
    
    VOID CreateThread_Test()
    {
        HANDLE hSysThread = NULL;
        HANDLE hMyProcThread = NULL;
        NTSTATUS status;
        //创建系统进程
        status = PsCreateSystemThread(&hSysThread, 0, NULL, NULL, NULL, SystemThread, NULL);
        //创建用户进程
        status = PsCreateSystemThread(&hMyProcThread, 0, NULL, NtCurrentProcess(), NULL, MyProcessThread, NULL);
    }
    
    NTSTATUS IoControlDispatch(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp
    )
    {
        NTSTATUS status = STATUS_SUCCESS;
        PIO_STACK_LOCATION pIrps = IoGetCurrentIrpStackLocation(Irp);
        if(CREATE_THREAD_COMMAND == pIrps->Parameters.DeviceIoControl.IoControlCode)
        {
            //创建线程
            CreateThread_Test();
        }
        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
    }
    NTSTATUS DriverEntry(
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath)
    {
        UNICODE_STRING uDeviceName;
        UNICODE_STRING uSymbolicName;
        PDEVICE_OBJECT pDevObj;
        NTSTATUS status;
        DbgPrint("Hello, world
    ");
        pDriverObject->DriverUnload = DriverUnload;
    
        //初始化设备名称和链接名称
        RtlInitUnicodeString(&uDeviceName, DEVICE_NAME);
        RtlInitUnicodeString(&uSymbolicName, LINK_NAME);
        //创建设备对像
        status = IoCreateDevice(pDriverObject, 0, &uDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
        if(!NT_SUCCESS(status))
        {
            DbgPrint("Create Device Error!
    ");
            return status;
        }
    
        status = IoCreateSymbolicLink(&uSymbolicName, &uDeviceName);
        if(!NT_SUCCESS(status))
        {
            DbgPrint("Create SymbolicLink Error!
    ");
            IoDeleteDevice(pDevObj);
            return status;
        }
        //设置IRP_MJ_DEVICE_CONTROL分发派遣函数
        pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoControlDispatch;
        return STATUS_SUCCESS;
    }
    #include <stdio.h>
    #include <Windows.h>
    #include <tchar.h>
    #define BASE_CODE 0x800
    #define CREATE_THREAD_COMMAND CTL_CODE(FILE_DEVICE_UNKNOWN, BASE_CODE + 1, METHOD_BUFFERED, FILE_ANY_ACCESS)
    
    int _tmain(int argc, TCHAR *argv[])
    {
        BOOL bRet = FALSE;
        HANDLE hDevice = CreateFile(_T("\\.\ControlDevice"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (INVALID_HANDLE_VALUE == hDevice)
        {
            printf("Create File Error %d 
    ", GetLastError());
            _tsystem(_T("PAUSE"));
            return 0;
        }
    
        printf("Create File Success hDevice = %08x
    ", hDevice);
        bRet = DeviceIoControl(hDevice, CREATE_THREAD_COMMAND, NULL, 0, NULL, 0, NULL, NULL);
        if (!bRet)
        {
            printf("Control Device Error, code : %d
    ", GetLastError());
        }
        _tsystem(_T("PAUSE"));
        return 0;
    }

    这些代码非常简单,就是直接在应用层通过CreateFile打开,然后下发一个控制命令,驱动层接收到这个命令,创建两个线程,一个获取当前下发命令的应用程序的进程名,一个获取系统进程的进程名。
    这段代码当时主要是有两个问题,第一个就是CreateFile打开时错误,并返回错误码1,乍看好像没有什么问题,其实这个问题我估计还是自己对应用层如何调用驱动层不太熟。首先驱动程序跟应用层的窗口程序类似,都是靠事件驱动的,在窗口程序中把这种事件叫做消息,窗口程序的主要功能是处理各种消息,由系统根据消息负责调用消息处理函数,驱动同样是这样。驱动中的设备对象就好像窗口一样,应用层下发的事件都是针对设备对象的。应用层针对不同设备对象下发的请求通过I/O管理器进行封装,变为一个个的IRP,根据不同的设备对象所属的驱动程序的不同,系统会自动调用我们事先准备好的处理程序,在程序中主要做这样几件事:
    1. 对下发的事件进行适当的处理
    2. 决定下发这个IRP或者结束这个IRP
    3. 决定如何向I/O管理器和本层驱动程序返回值
    I/O管理器会根据返回的值来决定如何给上层返回一个值,就拿CreateFile来说,这个API在调用时会经过I/O管理器生成一个IRP_MJ_CREATE类型的IRP,系统根据函数所针对的设备(这个设备可以通过第一个参数知道)找到对应的驱动,然后调用驱动中对应的处理函数,然后将这个处理函数中返回的值返回给I/O管理器,I/O管理器根据这个值决定如何返回值给应用层的API。说道这,这个问题的答案基本上已经出来了,这个问题的原因就是这段代码没有给定IRP_MJ_CREATE的处理函数,I/O管理器并没有收到一个成功的返回,所以它给应用层返回一个错误,我们加上一个Create的处理函数,并在DriverEntry中指定就好了。

    NTSTATUS CreateDispatch(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp
    )
    {
        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
    }
    
    //然后需要在DriverEntry中加上这句话,相当于在系统中注册了一个Create事件的处理函数
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = CreateDispatch;

    这里有两个NTSTATUS的值,通过return返回的是给驱动程序的,而通过Irp->IoStatus.Status返回的是给I/O管理器的,上面说的I/O管理器没有收到成功,说的也是这个值没有给STATUS_SUCCESS
    做完这些工作,这个问题就这样解决了,但是接着执行后面的代码,发现程序崩溃了,会弹出一个内存读写错误的提示框,这个时候可以肯定是应用层的问题,因为如果是内核层出现内存读写错误,系统肯定蓝屏了。当时我推测可能是句柄为NULL,或者DeviceIoControl中哪个缓冲区不能为NULL,为了知道是哪的问题,我在调用DeviceIoControl之前加了一条输出语句,我发现这条语句输出的句柄值是正常的,那就肯定是DeviceIoControl的问题,我先试着吧所有的输入输出缓冲区都给定了一个值,通过排查最后发现是倒数第二个参数不能为NULL,这个参数表示的是驱动层实际返回的缓冲区的大小。最后通过查看驱动层的代码,我终于知道这个值为什么不能为NULL。这就要说到DeviceIoControl与驱动通信的方式,DeviceIoControl的定义如下:

    BOOL DeviceIoControl(
      HANDLE hDevice, 
      DWORD dwIoControlCode, 
      LPVOID lpInBuffer, 
      DWORD nInBufferSize, 
      LPVOID lpOutBuffer, 
      DWORD nOutBufferSize, 
      LPDWORD lpBytesReturned, 
      LPOVERLAPPED lpOverlapped
    );

    DeviceIoControl通过dwIoControlCode向驱动下发控制码,这个控制码在驱动中可以通过IO_STACK_LOCATION 结构中的Parameters.DeviceIoControl.IoControlCode值获得。另外函数根据lpInBuffer和nInBufferSize来给驱动传递数据,通过参数lpOutBuffer和nOutBufferSize来接受来自驱动上传的数据,另外还有一个是驱动真实上传数据的大小,大小都是以字节为单位的。那么这个真实大小是怎么来的呢?答案就是通过Irp->IoStatus.Information这个值,I/O管理器取这个值,将它填充到lpBytesReturned所指向的内存中,既然我们在驱动中指定了这个值为0,自然要给它在应用层分配相应的缓冲区了,前面的由于给的是NULL,I/O管理器不可能将这个值填入NULL缓冲区,所以自然会弹出这个内存读写的错误。
    最后来总结下:
    1. 如果我们要打开对应的驱动中的设备对象,在驱动层需要提供IRP_MJ_CREATE的处理函数,将返回给I/O管理器的值填入到IRP的IoStatus这个结构中。类似的,如果我们通过类似WriteFile、ReadFile的函数发送的相应的IRP,即使我们不需要对这个IRP进行特殊的处理,我们也需要为这些操作提供一个默认的处理函数,至少要向I/0管理器返回一个成功,这样应用层的函数才能调用成功。
    2. DeviceIoControl函数,如果不需要跟驱动层进行交互,那么他的输入输出缓冲区是可以给NULL的,但是由于I/O管理器会像它返回驱动层实际返回的数据的大小,所以这个真实大小的缓冲区一定不能为NULL

  • 相关阅读:
    瀑布流布局(等宽不等高jQuery)
    多组图自动无限循环(swiper轮播)
    $.ajax的标准写法
    走马灯特效
    如何使用js改变HTML中title里面固定的文字
    test
    单例设计模式
    线程的五种状态(线程的生命周期)
    Net基础班第十三天
    装箱、拆箱
  • 原文地址:https://www.cnblogs.com/lanuage/p/7725716.html
Copyright © 2011-2022 走看看