1.windows驱动分类
由于windows版本发展,出现过几种不同驱动模型:vxd、NT、WDM、WDF。前面的vxd主要同之前的win95/98系统相匹配,后面的NT、WDM、WDF则是同后面的NT内核的windows系统匹配。
ps:个人猜测,其实应该只有2种类型的驱动模型vxd和NT,这2种驱动模型分别对应的是windows发展过程中使用到的2种不同内核:win95/98的wince内核(暂且叫做wince内核,wince反而是由它们发展而来的)和后面windows2000后的NT内核。WDM驱动在我看来是在NT模型下添加了对即插即用驱动支持(WDM内部也是由DRIVER_OBJECT和DEVICE_OBJECT基本结构组成,基本概念没有超出NT模型范围),最后的WDF驱动没有进一步接触,但是据说是在WDM上面进行一层封装,为了提高开发者效率(WDF之于WDM约等于MFC之于win32)。WDM、WDF都是以NT为基础。
2.windows驱动开发环境搭建
环境搭建其实比较简单的,网上有很多讲解,基本都差不多,选择其一即可。我选择的软件包是:vs2005、wdk7600、ddkwizard。按照这个顺序安装即可。具体文档可以上本人资源上下载,是一篇blog的复制。
ps:最好是在vm下创建一虚拟机试验驱动,否则,哈哈。。。。
3.最简单的NT驱动
#include <ntddk.h>
VOID TestNTUnload(PDRIVER_OBJECT pDriverObject)
{
KdPrint(("TestNT Unload Ok!"));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegisterPath)
{
pDriverObject->DriverUnload = TestNTUnload;
KdPrint(("Hi, Windows NT Driver!"));
return STATUS_SUCCESS;
}
ntddk.h是nt驱动必须添加的,KdPrint实现打印输出,DriverEntry是驱动入口,pDriverObject是驱动在内核中的总代理表示,其他基本上差不多了。TestNTUnload函数在驱动被卸载时被调用。
4.编译
使用2步中的环境创建一个EntryDriver,添加c源码,然后在Sources中设置TARGETTYPE=DRIVER,在SOURCES=后面加上刚添加的c源码,编译,即可以得到一个sys的文件,这个就是我们最终的目标驱动了。
5.驱动加载卸载工具及调试打印工具
首先是上面KdPrint函数中打印信息的查看,这个需要一个工具DbgView.exe。ms出品,坚如磐石。。。。可以在我的资源中下载。
然后是sys驱动加载与卸载了。NT式驱动在windows中被当做是一个服务处理,具体是任务管理器中的system服务进行管理。有关驱动加载有如下几种处理方式进行:
1.在注册表[HLM\SYSTEM\CurrentControlSet\Services]中添加
[HLM\SYSTEM\CurrentControlSet\Services\TestNT]
DisplayName=TestNT
ErrorControl=1
ImagePath=\??\C:\TestNT.sys
Start=3
Type=1
上面的c:\TestNT.sys就是上面编译出来的驱动文件完整路径名称。
然后在命令行下面:
net start TestNT 表示加载该驱动
net stop TestNT 表示卸载该驱动
ps:这里简单说一下注册表中[HLM\\SYSTEM]相关内容含义。
[HLM\SYSTEM]中保存的是当前计算机中有关驱动和服务进程的相关信息,其下主要子项有:
ControlSet001
ControlSet002
CurrentControlSet
其中的CurrentControlSet表示的是当前系统中有关驱动和服务进程的相关信息,启动时候是从ControlSet001中复制而来,修改CurrentControlSet也会对ControlSet001中相关进行修改,ControlSet002中保存的则是上次成功启动后的ControlSet的保存(在非正常断电条件下,常会出现是否恢复上次成功记录,这时候进行选择,CurrentControlSet中将会是ControlSet002中信息复制,这个本人没有经过验证)。
同时子项Select中相关内容也可以看出上述关系。
2.使用SCM管理器之类的程序
加载驱动:
hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hScm == NULL)
{
CString tmp = TEXT("OpenScm Failed!");
Msg(this, tmp);
goto DONE;
}
hService = CreateService(hScm,
m_DriverName.GetBuffer(),
m_DriverName.GetBuffer(),
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE,
m_SysFilePath.GetBuffer(),
NULL, NULL, NULL, NULL, NULL);
if (hService == NULL)
{
if (GetLastError() == ERROR_IO_PENDING || GetLastError() == ERROR_SERVICE_EXISTS)
{
hService = OpenService(hScm, m_DriverName.GetBuffer(), SERVICE_ALL_ACCESS);
if (hService == NULL)
{
CString tmp = TEXT("OpenService Failed!");
Msg(this, tmp);
goto DONE;
}
}
else
{
CString tmp = TEXT("CreateService Failed!");
Msg(this, tmp);
goto DONE;
}
}
主要函数是OpenSCManager(打开SCM管理器,服务创建需要通过其控制)、CreateService(创建服务)。
卸载驱动:
if (!DeleteService(hService))
{
CString tmp = TEXT("Unload Service Failed!");
Msg(this, tmp);
goto DONE;
}
if (hService != NULL)
CloseServiceHandle(hService);
if (hScm != NULL)
CloseServiceHandle(hScm);
主要函数是DeleteService(删除服务)、CloseServiceHandle(关闭服务句柄)。
启动服务:
if (!StartService(hService, NULL, NULL))
{
bRet = GetLastError();
if (bRet == ERROR_SERVICE_ALREADY_RUNNING)
{
CString tmp = TEXT("Service Alread Running!");
Msg(this, tmp);
}
else
{
CString tmp = TEXT("Start Service Failed!");
Msg(this, tmp);
}
goto DONE;
}
停止服务:
if (!ControlService(hService, SERVICE_CONTROL_STOP, &status))
{
CString tmp = TEXT("Stop Service Failed!");
Msg(this, tmp);
goto DONE;
}
ps:为求简单,制作了个小的mfc程序用于NT驱动的加载与卸载。参考csdn资源。
3.工具
SRVINSTW.EXE,在加载过程中要自己对驱动文件路径填入。大致原理应该也是同上。
ps:
其实发现NT驱动同wince流驱动大体上也是差不太多的,wince中的xxx_Init相对于DriverEntry,xxx_DeInit对应上面的TestNTUnload,中间则是各个文件接口调用,如:
pDriverObject->MajorFunction[IRP_MJ_CREATE] = TestNTIrpHandle;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = TestNTIrpHandle;
pDriverObject->MajorFunction[IRP_MJ_READ] = TestNTIrpHandle;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = TestNTIrpHandle;
对应流驱动的xxx_Open、xxx_Close、xxx_Read、xxx_Write。在底层函数调用接口统一来说,NT驱动反而更加简单些。