610 likes | 710 Views
Windows98/2000 驱动程序编写方法. (上). 杨全胜. 1. 驱动程序的开发环境. 对于 VxD 的开发,需要的开发环境是 : Visual C++ 5.0/6.0 Windosw 95DDK 如果想加快开发步骤,建议使用第三方的 VToolsD 开发工具,它将 DDK 的东西全部封装成 C++ 的类,可以直接用 Visual C++ 编写程序,而无须使用汇编。而且它提供的 QuickVxd 能够方便快速地建立 VxD 程序的框架。. 对于 WDM 的开发,又分几种情况 : 对于 Windows 98 系统 Visual C++ 5.0
E N D
Windows98/2000驱动程序编写方法 (上) 杨全胜
1. 驱动程序的开发环境 • 对于VxD的开发,需要的开发环境是: • Visual C++ 5.0/6.0 • Windosw 95DDK • 如果想加快开发步骤,建议使用第三方的VToolsD开发工具,它将DDK的东西全部封装成C++的类,可以直接用Visual C++编写程序,而无须使用汇编。而且它提供的 QuickVxd能够方便快速地建立VxD程序的框架。
对于WDM的开发,又分几种情况: • 对于Windows 98系统 • Visual C++ 5.0 • Windows 98DDK • 2)对于Windows Me/2000 • Visual C++ 6.0 • Windows 2000DDK • 3)对于Windows XP • Visual C++6.0/.net • Windows XPDDK • 同样,为了方便起见,也可以使用第三方的开发工具Driver Works,它也是将DDK的内容封装成类,而且提供一个快速方便地生成驱动框架的工具。
2. 驱动程序开发工具包DriverStudio 2.1 DriverStudio 2.7所包含的工具 • VToolsD VToolsD 是一个用来开发针对Win9X (Windows 95 和Windows 98)操作系统下设备驱动程序(VxD)的工具。VToolsD 中包括生成驱动程序源代码的工具,run-time 和interface 库,以及一些驱动程序样本,可以用来作为各种类型的设备驱动程序的基础部分。 • DriverWorks DriverWorks对于Windows NT下和Windows 98 与Windows 2000共同支持的Win32驱动模型(WDM)设备驱动程序的开发提供完全的支持。DriverWorks中包含一个非常完善的源代码生成工具(DriverWizard) 以及相应的类库和驱动程序样本,它提供了在C++下进行设备驱动程序开发的支持。
DriverNetworks DriverNetworks 是针对Windows网络驱动开发人员的一个模块。在它的核心部分,DriverNetworks 是一个针对NDIS drivers 和TDI clients (DriverSockets)的C++ 的类库。DriverNetworks 中也有Quick Miniport Wizard 用来直接开始一个NDIS Miniport 或Intermediate Driver 工程。它可以让你快速的生成所有采用DriverNetworks C++ 类库编写的NDIS驱动程序的编译,安装和调试所需要的文件 。 • SoftICE SoftICE 是一个功能极其强大的内核模式调试器,它支持在配置一台单独的计算机或两台计算机下进行设备驱动程序的调试。
DriverMonitor DriverMonitor不仅可以显示WDM和VxD在操作系统核心层次输出的调试语句,还可以装载和卸载VxD驱动和NT4系统的驱动程序。DriverMonitor DriverMonitor不仅可以显示WDM和VxD在操作系统核心层次输出的调试语句,还可以装载和卸载VxD驱动和NT4系统的驱动程序。 • EZDriverInstaller EZDriverInstaller是一个无需经过设备管理器或“添加新硬件”功能就能为Windows 2000/XP动态加载和卸载WDM驱动程序的小实用程序。 • SetDDKGo SetDDKGo用来设置设备驱动程序创建的环境。当我们用Visual Studio(VC++)编译驱动程序源程序的时候,需要用SetDDKGo来设置环境变量,之后SetDDKGo会自动启动Visual Studio(VC++)编译环境。
2.2 DriverStudio 2.7的安装 安装需要的软硬件环境 • PC-compatible Intel x86 系统 • Windows 2000, (Optional: 其中一些工具也支持Windows 98 和Windows 95.) • 内存: 最少32 MB, 推荐使用64 MB • 硬盘:完全安装需要72 MB • 针对SoftICE的远程调试: NE2000-兼容网卡或3Com 网卡 • 针对DriverWorks: Microsoft DDK,MS Visual C++
安装步骤: 在安装DriverWorks之前,首先要保证你的计算机上已经安装了Microsoft Visual C++以及相应针对Windows NT 或WDM的DDK的正确版本。所有这些包括DriverStudio的安装都必须以系统管理员身份启动系统。并且要按照下面的顺序安装。 第一步: 安装Microsoft Visual Studio C++ 6.0 (注意Windows 2000DDK暂时对Visual Studio.NET不支持)
第二步: 安装2000DDK(Driver Development Kits)。 注意: 1)如果本地机的操作系统为Windows2000 请确认已经安装了Windows SevicePack1或Windows SevicePack2在安装DDK的时候请选择完全安装。 2)安装中,不需要安装64BIT IA64Binaries 3)安装好后,对于2000DDK不需要手动配置环境变量,只需在开始菜单中点击Checked Build Envirment 则DDK会自动调用setenv配置环境变量,并监测相应的SDK以及Visual Studio IDE
第三步: 安装DriverStudio 2.7(按照安装提示安装)。 注意, DriverStudio 2.7,包括最新的DriverStudio 3.0不能在Windows 2000的SP4和WindowsXP上使用,会引起蓝屏。遇到这种情况,请在安装好后不要重新启动机器,而是立即打上补丁。打补丁的方法是用新的siwvid.sys(补丁的内容)替换掉\%system%\system32\drivers\下的老的siwvid.sys
DriverStudio安装后的设置: 1)使用SetDDKGo工具定义BASEDIR环境变量并启动MSVC 5.0或6.0 ,
3)选择Build|Batch Build(编译|批构件),打开下面的窗口,从中选则需要编译的配置。 注意不要选择IA64的各项,Checked是调试版本,Free是发布版本
4)点击Build编译所选择的库文件。 注意:库文件只需在安装完成后第一次使用前编译一次即可。以后要使用DriverWorks,只需通过SetDDKGo进入MSVC即可。
3. VtoolsD开发VxD简介 生成简单框架
设备名 动态装载 编程语言 生成调试用目标代码
设备类名 虚拟机类名 线程类名
头文件 // MYFIRST.h - include file for VxD MYFIRST #include <vtoolscp.h> #define DEVICE_CLASS MyfirstDevice #define MYFIRST_DeviceID UNDEFINED_DEVICE_ID #define MYFIRST_Init_Order UNDEFINED_INIT_ORDER #define MYFIRST_Major 1 #define MYFIRST_Minor 0 #define DIOC_MY_IO CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_NEITHER, FILE_ANY_ACCESS) // 定义一个句柄用于应用程序与VxD通信
class MyfirstDevice : public VDevice { public: virtual DWORD OnW32DeviceIoControl(PIOCTLPARAMS pDIOCParams); }; class MyfirstVM : public VVirtualMachine { public: MyfirstVM(VMHANDLE hVM); }; class MyfirstThread : public VThread { public: MyfirstThread(THREADHANDLE hThread); };
.cpp文件 // MYFIRST.cpp - main module for VxD MYFIRST #define DEVICE_MAIN #include "myfirst.h" Declare_Virtual_Device(MYFIRST) #undef DEVICE_MAIN MyfirstVM::MyfirstVM(VMHANDLE hVM):VVirtualMachine (hVM) {} MyfirstThread::MyfirstThread(THREADHANDLE hThread): VThread(hThread) {} DWORD MyfirstDevice::OnW32DeviceIoControl (PIOCTLPARAMS pDIOCParams) { char* msg="欢迎进入虚拟机世界!"; char* caption="Hello World!";
switch(pDIOCParams->dioc_IOCtlCode) { case DIOC_OPEN:dout<<"I am Opening!"; break; //调用CreateFile函数时响应该分支代码 case DIOC_MY_IO:dout<<"I am working!"<<endl; SHELL_Message(pDIOCParams->dioc_hvm, MB_OK, msg, caption,0,0,0); break; //调用DeviceIoControl函数时响应该分支下的代码 case DIOC_CLOSEHANDLE:dout<<"I am Closing!";break; //调用CloseHandle函数时响应该分支代码 } return 0; }
.mak文件 # MYFIRST.mak - makefile for VxD MYFIRST DEVICENAME = MYFIRST DYNAMIC = 1 FRAMEWORK = CPP DEBUG = 1 OBJECTS = myfirst.OBJ !include $(VTOOLSD)\include\vtoolsd.mak !include $(VTOOLSD)\include\vxdtarg.mak myfirst.OBJ:myfirst.cpp myfirst.h
Win32环境下的控制台程序 #include <iostream.h> #include <windows.h> #include <winioctl.h> #define DIOC_MY_IO CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_NEITHER, FILE_ANY_ACCESS) //定义一个句柄用于应用程序与VxD通信 void main() { HANDLE hDevice; hDevice=CreateFile ("\\\\.\\myfirst.vxd", 0, 0,0,OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0); // 文件名的路径一定是以\\\\.\\打头 ,默认的VXD的文件的目录是C:\Windows\system,如果VXD在d:\myvxd目录下,则这里应该写成"\\\\.\\d:\\myvxd\\myfirst.vxd"
if(hDevice==INVALID_HANDLE_VALUE) { cout<<"Open VxD error"<<GetLastError()<<endl; exit(1); } DeviceIoControl(hDevice,DIOC_MY_IO,NULL,0,NULL,0, NULL,NULL); //使用句柄DIOC_MY_IO与VxD交互 CloseHandle(hDevice);//关闭设备句柄 }
I/O类 class VIOPort { public: VIOPort(DWORD port);//构造函数 ~VIOPort();//析构函数 BOOL hook();//挂钩一个端口处理程序 BOOL unhook();//摘钩一个端口处理程序 VOID localEnable(VMHANDLE); //允许监控指定虚拟机端口的I/O操作 VOID localDisable(VMHANDLE);//禁止监控指定虚拟机端口的I/O操作 VOID globalEnable();//允许监控所有虚拟机端口的I/O操作 VOID globalDisable();//禁止监控所有虚拟机端口的I/O操作 virtual DWORD handler(VMHANDLE, DWORD port,CLIENT_STRUCT* pRegs, DWORD iotype, DWORD outdata);//端口处理程序 protected: DWORD m_port; BYTE m_thunk[IOPORTTHUNKSIZE]; };
下面我们给出一个例子,该例子捕获端口0xf0,当应用程序要读该端口时,它将上次写入该端口的资料返回给应用程序。用VToolsD生成的时候,基本上和生成Myfirst的方法一样,只是在Windows95控制消息中只选择SYS_DYNAMIC_DEVICE_EXIT和SYS_DYNAMIC_DEVICE_INIT两个消息而不选择W32_DEVICEIOCONTROL。下面我们给出一个例子,该例子捕获端口0xf0,当应用程序要读该端口时,它将上次写入该端口的资料返回给应用程序。用VToolsD生成的时候,基本上和生成Myfirst的方法一样,只是在Windows95控制消息中只选择SYS_DYNAMIC_DEVICE_EXIT和SYS_DYNAMIC_DEVICE_INIT两个消息而不选择W32_DEVICEIOCONTROL。 // IOPORT.h - include file for VxD IOPORT #include <vtoolscp.h> #define DEVICE_CLASS IoportDevice #define IOPORT_DeviceID UNDEFINED_DEVICE_ID #define IOPORT_Init_Order UNDEFINED_INIT_ORDER #define IOPORT_Major 1 #define IOPORT_Minor 0 #define PORT_NUM 0xf0//要捕获的端口地址 ……
………… class IoportPort : public VIOPort { public: IoportPort():VIOPort(PORT_NUM){}; DWORD handler(VMHANDLE hVM, DWORD port,CLIENT_STRUCT* pRegs,DWORD iotype, DWORD outdata); }; …………
// IOPORT.cpp - main module for VxD IOPORT #define DEVICE_MAIN #include "ioport.h" Declare_Virtual_Device(IOPORT) #undef DEVICE_MAIN IoportPort* pio; IoportVM::IoportVM(VMHANDLE hVM) :VVirtualMachine(hVM){} IoportThread::IoportThread(THREADHANDLE hThread) :VThread(hThread){}
BOOL IoportDevice::OnSysDynamicDeviceInit() { // 动态装载初始化的时候调用 pio=new(IoportPort); if(pio) { if(pio->hook()) // 挂上端口映射 { dout<<"hook success"<<endl; } else return FALSE; } else return FALSE; return TRUE; }
BOOL IoportDevice::OnSysDynamicDeviceExit() { // 驱动程序被卸下时调用 if(pio) { if(pio->unhook()) //将I/O映射卸载下来 { dout<<"unhook success"<<endl; } delete pio; } return TRUE; } DWORD IoportPort::handler(VMHANDLE hVM, DWORD port,CLIENT_STRUCT* pRegs,DWORD iotype, DWORD outdata) { static WORD count=0; static DWORD buffer[20]; static BYTE inppt=0; count++; dout<<"entering handler"<<endl; dout<<"entered count"<<count<<endl;
switch(iotype) { case BYTE_INPUT:dout<<"buffer byte"<<(BYTE)buffer[inppt]<<endl;inppt++; return(BYTE)buffer[inppt-1]; case WORD_INPUT:dout<<"buffer word"<<(WORD)buffer[inppt]<<endl;inppt++; return(WORD)buffer[inppt-1]; case DWORD_INPUT:dout<< "buffer dword"<<(WORD)buffer[inppt]<<endl; inppt++; return(WORD)buffer[inppt-1]; case BYTE_OUTPUT:buffer[count-1]=outdata;dout<<"byte output data:"<<(BYTE) outdata<<endl;break; case WORD_OUTPUT:buffer[count-1]=outdata;dout<<"word output data:"<<(WORD) outdata<<endl; break; case DWORD_OUTPUT:buffer[count-1]=outdata;dout<<"dword output data:"<< (DWORD)outdata<< endl; break; default:break; } //对于输入,处理程序将数据输入到端口的缓冲,对于输出,处理程序将缓冲中的资料输出到端口 return 0; }
下面给出调用端口0xf0的Win32控制台程序。 #include "conio.h" #include <iostream.h> #include <windows.h> #include <winioctl.h> #define PORT_NUM 0xf0 void main() { char* srcdata="This is IO Sample!!"; char* desdata=new char(20); HANDLE hDevice; char ch; _cprintf("1=load IOPORT VXD, x=exit, Others=Do not load IOPORT VXD\n"); ch=_getch(); while(ch!='x') { if(ch=='1') { hDevice=CreateFile ("\\\\.\\Ioport.vxd",0,0,0,OPEN_EXISTING,FILE_FLAG_DELETE_ON_CLOSE,0);
if(hDevice==INVALID_HANDLE_VALUE) { cout<<"Open VXD error"<<GetLastError()<<endl; exit(1); } } desdata[0]='\0'; _asm { mov dx,PORT_NUM mov edi,srcdata mov ecx,20 byteoutput:mov al,[edi] out dx,al inc edi loop byteoutput mov esi,desdata mov ecx,20 byteinput:in al,dx mov [esi],al inc esi loop byteinput }
if(ch=='1') { CloseHandle(hDevice);//关闭设备句柄 _cprintf("scrdata and desdata should be the same!\n"); } _cprintf("srcdata:%s\n",srcdata); _cprintf("desdata:%s\n",desdata); _cprintf("1=load IOPORT VXD, x=exit, Others=Do not load IOPORT VXD\n"); ch=_getch(); } }