驱动版Hello World - CSDNBlog

来源:互联网 发布:xp怎样连接网络打印机 编辑:程序博客网 时间:2024/04/30 02:21
导读: 
  我们学习程序设计,都是从“Hello World”开始的,驱动程序也不例外,今天我就写一个驱动版的“Hello World”来热热身,目的希望大家能对驱动程序的基本框架有所了解。 
  驱动程序分为2类,一个是Kernel模式驱动,另一个是Windows模式驱动,2种模式本质是相同,但细节不同,本文介绍的是内核模式驱动和驱动程序的安装、使用。 
  驱动程序同普通的EXE,DLL一样,都属于PE文件,而且都有一个入口函数。但EXE中,入口函数是main()/WinMain()和Unicode的wmain()/wWinmain(),DLL的入口函数则可有可无,它是DllMain()。驱动程序也有入口函数,而且是必须的,它是DriverEntry(),再次提示,它是必须的,因为I/O管理器会首先调用驱动程序的DriverEntry(),它的作用就像DllMain()--完成一些初始化工作。DriverEntry()一共有2个参数:1PDRIVER_OBJECT DriverObject,指向驱动程序对象的指针,我们操作驱动程序,全靠它,它是由I/O管理器传递进来的;2)PUNICODE_STRING RegistryPath,驱动程序的服务主键,这个参数的使用并不多,但要注意,在DriverEntry()返回后,它可能会消失,所以如果需要使用,记住先要保存下来。DriverEntry()的返回一个NTSTATUS值,它是一个ULONG值,具体的定义,请参见DDK中的NTSTATUS.H头文件,里边有详细的定义。 
  既然要写驱动版的“Hello World”,就需要确定如何来与驱动程序通信,常用的共享内存,共享事件,IOCTL宏,或者直接用ReadFile()或WriteFile()进行读写,在本文里我就采用一种简单的、但又很常用的IOCTL宏,它依赖的IRP派遣例程是IRP_MJ_DEVICE_CONTROL,Win32程序使用DeviceIoControl()与驱动进行通信,根据不同的IOCTL宏,输出不同的调试信息。为了简便,我并没有使用ReadFile()将信息读出来,而是直接用DbgPrint()输出,所以需要使用DbgView查看,其他调试工具也可以。PS:偷懒! 
  驱动程序与I/O管理器通信,使用的是IRP,即I/O请求包。IRP分为2部分:1)IRP首部;2)IRP堆栈。IRP首部信息如下: 
  IRP首部: 
  IO_STATUS_BLOCK IoStatus 包含I/O请求的状态 
  PVOID AssociatedIrp.SystemBuffer 如果执行缓冲区I/O,这个指针指向系统缓冲区 
  PMDL MdlAddress 如果直接I/O,这个指针指向用户缓冲区的存储器描述符表 
  PVOID UserBuffer I/O缓冲区的用户空间地址 
  IRP堆栈: 
  UCHAR MajorFunction 指示IRP_MJ_XXX派遣例程 
  UCHAR MinorFunction 同上,一般文件系统和SCSI驱动程序使用它 
   
  union Parameters MajorFunction的联合类型 
  { 
  struct Read IRP_MJ_READ的参数 
  ULONG Length 
  ULONG Key 
  LARGE_INTEGER ByteOffset 
   
  struct Write IRP_MJ_WRITE的参数 
  ULONG Length 
  ULONG Key 
  LARGE_INTEGER ByteOffset 
   
  struct DeviceIoControl IRP_MJ_DEVICE_CONTROL参数 
  ULONG OutputBufferLength 
  ULONG InputBufferLength 
  ULONG IoControlCode 
  PVOID Type3InputBuffer 
  } 
  PDEVICE_OBJECT DeviceObject 请求的目标设备对象的指针 
  PFILE_OBJECT FileObject 请求的目标文件对象的指针,如果有的话 
  操作IRP。对于不同的IRP函数,操作也是不同的:有的只操作IRP首部;有的只操作IRP堆栈;还有操作IRP整体,下面是一些常用的函数: 
  IRP整体: 
  名称 描述 调用者 
  IoStartPacket 发送IRP到Start I/O例程 Dispatch 
  IoCompleteRequest 表示所有的处理完成 DpcForIsr 
  IoStartNextPacket 发送下一个IRP到Start I/O例程 DpcForIsr 
  IoCallDriver 发送IRP请求 Dispatch 
  IoAllocateIrp 请求另外的IRP Dispatch 
  IoFreeIrp 释放驱动程序分配的IRP I/O Completion 
  IRP堆栈: 
  名称 描述 调用者 
  IoGetCurrentIrpStackLocation 得到调用者堆栈的指针 Dispatch 
  IoMarkIrpPending 为进一步的处理标记调用者I/O堆栈 Dispatch 
  IoGetNextIrpStackLocation 得到下一个驱动程序的I/O堆栈的指针 Dispatch 
  IoSetNextIrpStackLocation 将I/O堆栈指针压入堆栈 Dispatc 
  在驱动程序,IRP派遣例程起着很重要的作用,每个IRP派遣例程,几乎都有对应的Win32函数,下面是几个常用的: 
  IRP派遣例程: 
  名称 描述 调用者 
  IRP_MJ_CREATE 请求一个句柄 CreateFile 
  IRP_MJ_CLEANUP 在关闭句柄时取消悬挂的IRP CloseHandle 
  IRP_MJ_CLOSE 关闭句柄 CloseHandle 
  IRP_MJ_READ 从设备得到数据 ReadFile 
  IRP_MJ_WRITE 传送数据到设备 WriteFile 
  IRP_MJ_DEVICE_CONTROL 控制操作(利用IOCTL宏) DeviceIoControl 
  IRP_MJ_INTERNAL_DEVICE_CONTROL 控制操作(只能被内核调用) N/A 
  IRP_MJ_QUERY_INFORMATION 得到文件的长度 GetFileSize 
  IRP_MJ_SET_INFORMATION 设置文件的长度 SetFileSize 
  IRP_MJ_FLUSH_BUFFERS 写输出缓冲区或者丢弃输入缓冲区 FlushFileBuffers FlushConsoleInputBuffer PurgeComm 
  IRP_MJ_SHUTDOWN 系统关闭 InitiateSystemShutdown 
  ====================================================================================================
  下面开始写我们的驱动版的“Hello World”,程序很简单,先介绍一下流程: 
  1,调用IoCreateDevice()创建一个设备,并返回一个设备对象。 
  2,调用IoCreateSynbolicLink()创建一个符号连接,使Win32程序可以使用驱动程序 
  3,设置IRP_MJ_DEVICE_CONTROL派遣例程HelloWorldDispatch()和卸载例程HelloWorldUnLoad()。 
  如果Win32程序使用DeviceIoControl(),则执行HelloWorldDispatch()函数 
  4,调用IoGetCurrentIrpStackLocation()得到当前调用者的IRP指针 
  5,取得IO控制代码,完成后使用IoCompleteRequest()完成IRP操作 
  如果使用ControlService()停止驱动程序,则执行HelloWorldUnLoad()函数 
  4,调用IoDeleteSymbolicLink()删除符号连接 
  5,调用IoDeleteDevice()删除已建立的设备 
  驱动入口DriverEntry() 
  //创建设备 
  IoCreateDevice(DriverObject, //驱动程序对象 
  0, //扩展设备的大小,由于不需要,所以置0 
  &DeviceNameString, //设备名称 
  FILE_DEVICE_UNKNOWN, //设备类型 
  0, //指示设备允许的操作 
  FALSE, //如果为TRUE,表示只能有一个线程使用该设备,为FALSE,则没有限制 
  &lpDeviceObject); //返回的设备对象 
  //创建符号连接 
  IoCreateSymbolicLink(&DeviceLinkString, //存放符号连接的UNICODE_STRING 
  &DeviceNameString); //设备名称 
  //派遣例程和卸载例程 
  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;吧 
  DriverObject->DriverUnload=HelloWorldUnLoad; 
  IRP派遣例程HelloWorldDispatch() 
  IrpStack=IoGetCurrentIrpStackLocation(pIrp); //得到当前调用者的IRP堆栈 
  //获取IO控制代码,并执行指定操作,这里只是DbgPrint() 
  IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode; 
  switch (IoControlCodes) { 
  ...... 
  IoCompleteRequest(pIrp,IO_NO_INCREMENT); //完成IRP操作 
  卸载例程HelloWorldUnLoad() 
  //删除符号连接和设备 
  IoDeleteSymbolicLink(&DeviceLinkString); 
  IoDeleteDevice(DriverObject->DeviceObject); 
  ====================================================================================================完整代码: 
  #ifndef __HELLOWORLD_C__ 
  #define __HELLOWORLD_C__ 
  #define DEBUGMSG 
  #include "HelloWorld.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); 
  //使用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]=//HelloWorldDispatch; 
  DriverObject->MajorFunction[IRP_MJ_CLOSE]=//HelloWorldDispatch; 
  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch; 
  DriverObject->DriverUnload=HelloWorldUnLoad; 
  return ntStatus; 
  Error: 
  #ifdef DEBUGMSG 
  DbgPrint("Error DriverEntry()/n"); 
  #endif 
  return ntStatus; 
  } 
  NTSTATUS HelloWorldDispatch (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 HelloWorldDispatch()/n"); 
  #endif 
  IrpStack=IoGetCurrentIrpStackLocation(pIrp); //得到当前调用者的IRP 
  switch (IrpStack->MajorFunction) 
  { 
  case IRP_MJ_CREATE: 
  #ifdef DEBUGMSG 
  DbgPrint("IRP_MJ_CREATE/n"); 
  #endif 
  break; 
  case IRP_MJ_CLOSE: 
  #ifdef DEBUGMSG 
  DbgPrint("IRP_MJ_CLOSE/n"); 
  #endif 
  break; 
  case IRP_MJ_DEVICE_CONTROL: 
  #ifdef DEBUGMSG 
  DbgPrint("IRP_MJ_DEVICE_CONTROL/n"); 
  #endif 
  //取得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; 
  default: 
  pIrp->IoStatus.Status=STATUS_INVALID_PARAMETER; 
  break; 
  } 
  break; 
  default: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 
  ====================================================================================================
  驱动程序的编译需要使用DDK中的build实用程序,它是一个命令行程序,使用不是很方便。VC知识库有一篇在VC++ 6.0中编译驱动的文章,有兴趣可以去看看。 
  1,makefile 
  编译驱动程序,首先应该准备一个makefile,这个文件很简单,只有一句代码: 
  # 
  # DO NOT EDIT THIS FILE!!! Edit ./sources. if you want to add a new source 
  # file to this component. This file merely indirects to the real make file 
  # that is shared by all the driver components of the Windows NT DDK 
  # 
  !INCLUDE $(NTMAKEENV)/makefile.def 
  正如描述的那样,不要修改这个文件---它是通用的! 
  2,sources 
  准备的第二个文件就是sources,它描述了一些编译的细节。针对本文的程序,sources文件的内容是这样的: 
  TARGETNAME=HelloWorld //驱动名称 
  TARGETPATH=. //编译后SYS的路径 
  TARGETTYPE=DRIVER //类型为驱动程序 
  SOURCES= HelloWorld.c //只有一个源文件 
  有了这2个文件后,就可以使用build进行编译了。进入「开始」菜单/程序/Development Kits/Windows 2000 DDK, 
  分别有3个CMD程序:1)Checked 64 Bit Build Environment,“Debug”的64位版本;2)Checked Build Environment 
  “Debug”的32位版本;3)Free Build Environment,“Release”的32位版本。不用说,肯定是使用Free Build Environment。 
  New or updated MSVC detected. Updating DDK environment.... 
  Setting environment for using Microsoft Visual C++ tools. 
  Starting dirs creation...Completed. 
  C:/NTDDK>cd/ 
  C:/>cd HelloWorld 
  C:/HelloWorld>build 
  BUILD: Object root set to: ==> objfre 
  BUILD: /i switch ignored 
  BUILD: Compile and Link for i386 
  BUILD: Loading c:/NTDDK/build.dat... 
  BUILD: Computing Include file dependencies: 
  BUILD: Examining c:/helloworld directory for files to compile. 
  c:/helloworld - 1 source files (127 lines) 
  BUILD: Saving c:/NTDDK/build.dat... 
  BUILD: Compiling c:/helloworld directory 
  Compiling - helloworld.c for i386 
  BUILD: Linking c:/helloworld directory 
  Linking Executable - i386/helloworld.sys for i386 
  BUILD: Done 
  1 file compiled 
  1 executable built 
  C:/HelloWorld> 
  现在C:/HelloWorld/i386目录下,就有了HelloWorld.sys。 
  ====================================================================================================
  驱动程序的安装如同安装服务一样,唯一不同的是,创建服务时,类型是内核驱动,其他跟操作服务没什么区别。 
  安装驱动程序流程: 
  1,调用OpenSCManager()打开服务控制管理器 
  2,调用CreateService()创建一个服务,服务类型为内核驱动 
  3,调用OpenService()取得服务句柄 
  启动服务 
  4,调用StartService()启动服务 
  停止服务 
  4,调用ControlService()停止服务 
  删除服务 
  4,调用DeleteService()删除服务 
  5,调用CloseServiceHandle()关闭服务句柄 
  操作驱动程序流程: 
  1,调用CreateFile()取得设备句柄 
  2,调用DeviceIoControl()传递I/O控制代码 
  3,调用CloseHandle()关闭设备句柄 
  http://www.xfocus.net/tools/200411/882.html 
  这里有一个完整的驱动安装程序,所以我就不写了,只给出操作驱动程序的代码 
  完整代码: 
  #define DEBUGMSG 
  #include 
  #include 
  #include 
  #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 erron GetLastError() 
  #define MY_DEVICE_NAME "////.//HelloWorld" 
  #define MY_DEVICE_START "-start" 
  #define MY_DEVICE_STOP "-stop" 
  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) 
  DriverControl(argv[1]); 
  else 
  { 
  Usage(argv[0]); 
  return 0; 
  } 
  return 0; 
  } 
  BOOL DriverControl (TCHAR *Maik) 
  { 
  HANDLE hDevice=NULL; //设备句柄 
  DWORD RetBytes=0; 
  //获得设备句柄 
  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 (hDevice) 
  CloseHandle(hDevice); //关闭句柄 
  return TRUE; 
  } 
  void Usage (TCHAR *Paramerter) 
  { 
  fprintf(stderr,"============================================================================/n"
  " 驱动版Hello World/n" 
  "作者:dahubaobao[EST]/n" 
  "主页:[url]www.eviloctal.com[/url] or [url]www.ringz.org[/url]/n" 
  "邮件:[email]dahubaobao@eviloctal.com[/email]/n" 
  "OICQ:382690/n/n" 
  "%s -start/t启动/n" 
  "%s -stop /t停止/n/n" 
  "本程序只是用做代码交流,如有错误,还请多多包含!/n" 
  "============================================================================/n" 
  ,Paramerter,Paramerter); 
  } 
  头文件HelloWorld.h代码: 
  #ifndef __HELLOWORLD_H__ 
  #define __HELLOWORLD_H__ 
  #include 
  #define DEVICE_HELLO_INDEX 0x860 
  //2个IOCTL宏 
  #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 NT_DEVICE_NAME L"//Device//HelloWorld" //设备名称 
  #define DOS_DEVICE_NAME L"//DosDevices//HelloWorld" //符号连接 
  NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp); 
  VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject); 
  #endif 
   
  Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=768601 

本文转自 
http://blog.csdn.net/zhangjie0072/archive/2006/06/01/768601.aspx
原创粉丝点击