Chinaunix首页 | 论坛 | 博客

=.=

  • 博客访问: 140386
  • 博文数量: 50
  • 博客积分: 3000
  • 博客等级: 中校
  • 技术积分: 550
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-25 17:22
文章分类

全部博文(50)

文章存档

2010年(7)

2009年(43)

我的朋友

分类: WINDOWS

2009-09-25 17:48:19

一个驱动除去能接收DeviceIoControl传过的操作代码外。还应该能够与用户进程交换数据。为了能交换数据windows设计了三种方式 与用户交互 Buffered,Direct和 Neither。用户通过Createfile打开一个设备时,系统为用户创建一个文件句柄同时也创建一个IRP。这个文件句柄和IRP随同系统的进程一 同管理。也就是说用户的进程消亡了这些对象也就消亡了。用户进程所属的内存空间和驱动的内存空间属于不同的空间。他们不能用指针直接的进行数据交换。这个 就需要系统进行变换和管理。这些原始信息被系统保存到IRP的域中了。我们上面说的三种方式的信息都与IRP有关。具体是那种方式,可以通过指定设备对象 (DeviceObject)中的Flags域来表示。参考代码。
NTSTATUS AddDevice(...)
{
PDEVICE_OBJECT fdo;
IoCreateDevice(..., &fdo);
fdo->Flags |= DO_BUFFERED_IO;
          
fdo->Flags |= DO_DIRECT_IO;
          
fdo->Flags |= 0; // i.e., neither direct nor buffered
}

其中的DO_BUFFERED_IO、DO_DIRECT_IO、0 就是具体的操作类型。
我们来解释这三种方式。
"缓冲"方法(METHOD_BUFFERED)
在 指定了Buffer方式后.系统将分配与用户缓冲区大小相同的非分页内存。系统把缓冲区的地址和长度保存到两个十分不同的地方。I在通讯时,需要将数据在 这两个“缓冲”中拷贝。这种方式是较简单的方式。但是不要直接操作SystemBuffer。这样可能带来灾难性的后果。
"直接"方法(METHOD_IN/OUT_DIRECT)
如 果你在设备对象中指定DO_DIRECT_IO方式,系统将创建一个MDL用来描述包含该用户模式数据缓冲区的锁定内存页。MDL 地址会存储在 IRP 的 MdlAddress 字段中。在读取和写入请求,用户模式缓冲区会被锁定。在锁定后就为数据交换创造了条件。(为什么要锁定?因为用户的进程内存空间地址在物理地址中是变化 的。请参考分页管理的资料。)使用Direct的方式时要锁定“内存”参看下面的代码。
KPROCESSOR_MODE mode;   // either KernelMode or UserMode
PMDL mdl = IoAllocateMdl(uva, length, FALSE, TRUE, Irp); //Allocate a MDL memory
MmProbeAndLockPages(mdl, mode, reading ? IoWriteAccess : IoReadAccess); //lock MDL Pages

MmUnlockPages(mdl); //unlock MDL pages
ExFreePool(mdl); //relase MDL pages
这个锁定后我们就可以操作数据了。这种操作方式一个用处就是可以直接的、连续的与用户进程进行操作。这种方式非常适合带有DMA设备的驱动程序。
"两者都不"方法(METHOD_NEITHER)
这种,方式系统只是把用户模式虚拟地址和字节计数交给你,其余的工作由你去做。这种方式在你不熟悉windows内核的情况下是不推荐的。在内核模式下的操作可能给系统带来危险!
下面我给出一个"缓冲"方法操作的例子。

文件 hello.c


#ifndef __HELLOWORLD_C__
#define __HELLOWORLD_C__

#define DEBUGMSG

#include "Hello.h"

//驱动入口

NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS ntStatus=STATUS_SUCCESS;
    PDEVICE_OBJECT lpDeviceObject=NULL; //指向设备对象的指针

    UNICODE_STRING DeviceNameString; //设备名称

    UNICODE_STRING DeviceLinkString; //符号连接


    //调试信息

    #ifdef DEBUGMSG
           DbgPrint("Starting DriverEntry()\n");
    #endif

    RtlInitUnicodeString(&DeviceNameString,NT_DEVICE_NAME); //初始化Unicode字符串


    //创建设备

    ntStatus=IoCreateDevice(DriverObject,0,&DeviceNameString,FILE_DEVICE_UNKNOWN,
                            0,FALSE,&lpDeviceObject);

    lpDeviceObject->Flags |= DO_BUFFERED_IO; //定义使用缓冲方式


//使用NT_SUCCESS宏检测函数调用是否成功

    if (!NT_SUCCESS(ntStatus))
    {
        #ifdef DEBUGMSG
               DbgPrint("Error IoCreateDevice()\n");
        #endif
        goto Error;
    }

    RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);

    //创建符号连接

    ntStatus=IoCreateSymbolicLink(&DeviceLinkString,&DeviceNameString);

    if (!NT_SUCCESS(ntStatus))
    {
        #ifdef DEBUGMSG
               DbgPrint("Error IoCreateSymbolicLink()\n");
        #endif
        goto Error;
    }

    //设置IRP派遣例程和卸载例程

    DriverObject->MajorFunction[IRP_MJ_CREATE]=HelloCreateDispatch;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]=HelloCloseDispatch;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloControlDispatch;
    DriverObject->DriverUnload=HelloWorldUnLoad;

    return ntStatus;

    Error:
          #ifdef DEBUGMSG
                 DbgPrint("Error DriverEntry()\n");
          #endif

          return ntStatus;
}
//打开设备例程

NTSTATUS HelloCreateDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp)
{
    NTSTATUS ntStatus=STATUS_SUCCESS;
    ULONG IoControlCodes=0; //I/O控制代码

    PIO_STACK_LOCATION IrpStack=NULL; //IRP堆栈


    //设置IRP状态

    pIrp->IoStatus.Status=STATUS_SUCCESS;
    pIrp->IoStatus.Information=0;

    #ifdef DEBUGMSG
           DbgPrint("Starting HelloCreateDispatch()\n");
    #endif

    IrpStack=IoGetCurrentIrpStackLocation(pIrp); //得到当前调用者的IRP

    #ifdef DEBUGMSG
           DbgPrint("IRP_MJ_CREATE\n");
    #endif
ntStatus=pIrp->IoStatus.Status;

    IoCompleteRequest(pIrp,IO_NO_INCREMENT);

    return ntStatus;
}
//关闭例程

NTSTATUS HelloCloseDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp)
{
    NTSTATUS ntStatus=STATUS_SUCCESS;
    ULONG IoControlCodes=0; //I/O控制代码

    PIO_STACK_LOCATION IrpStack=NULL; //IRP堆栈


    //设置IRP状态

    pIrp->IoStatus.Status=STATUS_SUCCESS;
    pIrp->IoStatus.Information=0;

    #ifdef DEBUGMSG
           DbgPrint("Starting HelloCloseDispatch()\n");
    #endif

    IrpStack=IoGetCurrentIrpStackLocation(pIrp); //得到当前调用者的IRP


    #ifdef DEBUGMSG
           DbgPrint("IRP_MJ_CLOSE\n");
    #endif

ntStatus=pIrp->IoStatus.Status;

    IoCompleteRequest(pIrp,IO_NO_INCREMENT);

    return ntStatus;
}
//控制例程

NTSTATUS HelloControlDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp)
{
    NTSTATUS ntStatus=STATUS_SUCCESS;
    ULONG IoControlCodes=0; //I/O控制代码

    PIO_STACK_LOCATION IrpStack=NULL; //IRP堆栈

PVOID pInputBuffer,pOutputBuffer; //缓冲区指针

ULONG inputLength,outputLength; //缓冲区长度

PCHAR pReturnData = "IOCTL - Buffered I/O From Kernel!";
ULONG dwDataSize = sizeof("Example_ReadDirectIO - Hello from the Kernel!");

    //设置IRP状态

    pIrp->IoStatus.Status=STATUS_SUCCESS;
    pIrp->IoStatus.Information=0;

   

#ifdef DEBUGMSG
           DbgPrint("Starting HelloControlDispatch()\n");
    #endif


IrpStack=IoGetCurrentIrpStackLocation(pIrp); //得到当前调用者的IRP堆栈

  
//取得I/O控制代码

    IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;

switch (IoControlCodes)
{
    //启动

       case START_HELLPWORLD:
            DbgPrint("Starting \"Hello World\"\n");

    break;
       //停止

       case STOP_HELLPWORLD:
            DbgPrint("Stoping \"Hello World\"\n");
            break;
   
    case WRITE_HELLPWORLD:
            inputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength; //IRP堆栈中得到长度

      pInputBuffer = pIrp->AssociatedIrp.SystemBuffer;//IRP中得到地址

    
         DbgPrint("Reading a Buffer Data!\n");
    if(pInputBuffer)
    {
     DbgPrint("COMM_BufferedIo UserModeMessage = '%s'\n", pInputBuffer);
    }
    break;
    case READ_HELLPWORLD:
            outputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; //IRP堆栈中得到长度

      pOutputBuffer = pIrp->AssociatedIrp.SystemBuffer;//IRP中得到地址

            DbgPrint("Writing a Buffer Data!\n");
    if(pOutputBuffer)
    {
    
     RtlCopyMemory(pOutputBuffer, pReturnData, dwDataSize);
     pIrp->IoStatus.Information=dwDataSize;
    }
    break;

    default:
       DbgPrint("Invalid Parameter \"Hello World\"\n");
            pIrp->IoStatus.Status=STATUS_INVALID_PARAMETER;
            break;
    }

ntStatus=pIrp->IoStatus.Status;

    IoCompleteRequest(pIrp,IO_NO_INCREMENT);

    return ntStatus;
}

VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject)
{
     UNICODE_STRING DeviceLinkString;
     PDEVICE_OBJECT DeviceObjectTemp1=NULL;
     PDEVICE_OBJECT DeviceObjectTemp2=NULL;

     #ifdef DEBUGMSG
            DbgPrint("Starting HelloWorldUnLoad()\n");
     #endif

     RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);
     IoDeleteSymbolicLink(&DeviceLinkString); //删除符号连接


     if (DriverObject)
     {
         DeviceObjectTemp1=DriverObject->DeviceObject;

         //删除设备

         while (DeviceObjectTemp1)
         {
                DeviceObjectTemp2=DeviceObjectTemp1;
                DeviceObjectTemp1=DeviceObjectTemp1->NextDevice;
                IoDeleteDevice(DeviceObjectTemp2);
         }
     }
}

#endif


文件 hello.h



#ifndef __HELLOWORLD_H__
#define __HELLOWORLD_H__

#include <ntddk.h>

#define DEVICE_HELLO_INDEX 0x860

#define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define STOP_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define WRITE_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+2,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define READ_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+3,METHOD_BUFFERED,FILE_ANY_ACCESS)

#define NT_DEVICE_NAME L"\\Device\\hello" //设备名称

#define DOS_DEVICE_NAME L"\\DosDevices\\hello" //符号连接


NTSTATUS HelloCreateDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp);
NTSTATUS HelloCloseDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp);
NTSTATUS HelloControlDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp);

VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject);

#endif


helloctl.c 文件

#define DEBUGMSG

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

#define DEVICE_HELLO_INDEX 0x860

#define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define STOP_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define WRITE_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+2,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define READ_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+3,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define erron GetLastError()

#define MY_DEVICE_NAME "\\\\.\\hello"

#define MY_DEVICE_START "-start"
#define MY_DEVICE_STOP "-stop"
#define MY_DEVICE_WRITE "-input"
#define MY_DEVICE_READ "-output"

#define MY_INPUT_BUF_SIZE 255
#define MY_OUTPUT_BUF_SIZE 255

BOOL DriverControl (TCHAR *Maik);

void Usage (TCHAR *Paramerter);

int main (int argc,TCHAR *argv[])
{
    if (argc!=2)
    {
        Usage(argv[0]);
        return 0;
    }

    if (strcmpi(argv[1],MY_DEVICE_START)==0 || strcmpi(argv[1],MY_DEVICE_STOP)==0
   || strcmpi(argv[1],MY_DEVICE_WRITE)==0 || strcmpi(argv[1],MY_DEVICE_READ)==0)
        DriverControl(argv[1]);
    else
    {
        Usage(argv[0]);
        return 0;
    }

    return 0;
}

BOOL DriverControl (TCHAR *Maik)
{
    
HANDLE hDevice=NULL; //设备句柄

     DWORD RetBytes=0;
char inputBuffer[MY_INPUT_BUF_SIZE]; //输出用户缓冲区

char outputBuffer[MY_OUTPUT_BUF_SIZE];//输入用户缓冲区

     //获得设备句柄

     hDevice=CreateFile(MY_DEVICE_NAME,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

     if (hDevice==INVALID_HANDLE_VALUE)
     {
         #ifdef DEBUGMSG
                printf("CreateFile() GetLastError reports %d\n",erron);
         #endif
         return FALSE;
     }

     //启动

     if (strcmpi(Maik,MY_DEVICE_START)==0)
     {
         //传递启动的I/O控制代码

         if (!(DeviceIoControl(hDevice,START_HELLPWORLD,NULL,0,NULL,0,&RetBytes,NULL)))
         {
             #ifdef DEBUGMSG
                    printf("DeviceIoControl() GetLastError reports %d\n",erron);
             #endif
             CloseHandle(hDevice);
             return FALSE;
         }
     }

     //停止

     if (strcmpi(Maik,MY_DEVICE_STOP)==0)
     {
   //传递停止的I/O控制代码

         if (!(DeviceIoControl(hDevice,STOP_HELLPWORLD,NULL,0,NULL,0,&RetBytes,NULL)))
         {
             #ifdef DEBUGMSG
                    printf("DeviceIoControl() GetLastError reports %d\n",erron);
             #endif
             CloseHandle(hDevice);
             return FALSE;
         }
     }


//输入数据

     if (strcmpi(Maik,MY_DEVICE_WRITE)==0)
     {
         //输入数据缓冲区

         strcpy(inputBuffer,"Hello,Input Data!");
   if (!(DeviceIoControl(hDevice,WRITE_HELLPWORLD,&inputBuffer,MY_INPUT_BUF_SIZE,NULL,0,&RetBytes,NULL)))
         {
             #ifdef DEBUGMSG
                    printf("DeviceIoControl() GetLastError reports %d\n",erron);
             #endif
             CloseHandle(hDevice);
             return FALSE;
         }
     }

//输出数据

if (strcmpi(Maik,MY_DEVICE_READ)==0)
     {
   if (!(DeviceIoControl(hDevice,READ_HELLPWORLD,NULL,0,&outputBuffer,MY_OUTPUT_BUF_SIZE,&RetBytes,NULL)))
         {
             #ifdef DEBUGMSG
                    printf("DeviceIoControl() GetLastError reports %d\n",erron);
             #endif
             CloseHandle(hDevice);
             return FALSE;
         }else{
  
    printf("DeviceIoControl() output Data! s=%s\n return Bytes=%d%\n",outputBuffer,RetBytes);
  
   }
     }

if (hDevice)
         CloseHandle(hDevice); //关闭句柄


     return TRUE;
}

void Usage (TCHAR *Paramerter)
{
     fprintf(stderr,"============================================================================\n"
             " Hello驱动控制例程\n"
             "%s -start\t启动\n"
             "%s -stop \t停止\n"
             "%s -input\t输入\n"
             "%s -output\t输入\n"
             "本程序只是用做代码交流,如有错误,还请多多包含!\n"
             "============================================================================\n"
             ,Paramerter,Paramerter,Paramerter);
}



客户端的编译在sdk环境下。使用cl helloctl.c编译。安装这个驱动!使用debugview和hellloctl察看结果!
>net start hello
>helloctl -input
>helloctl -output
阅读(1046) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~