关闭

Windows驱动开发入门

804人阅读 评论(0) 收藏 举报
分类:

对于初学者,DbgView.exe和SRVINSTW.EXE是非常简单有用的两个工具,一定要装上。前者用于查看日志信息,后者用于加载驱动。

一、驱动程序设计的必要性
    在传统DOS系统下,每个应用程序都有权利读写硬件,读写I/O端口,控制系统中断,然而到了Windows系统中,为了保持良好的系统安全性,对应用程序的权限作出了限制,因为不适当的硬件读写会引发整个系统的崩溃。在Windows系统中,将整个程序设计为分层结构,其中,应用程序位于ring3,驱动程序位于ring0,应用程序不能读写底层硬件,对于硬件操作必须借助于底层驱动程序,所以,只要是与硬件系统打交道的Windows程序,必然会涉及到驱动程序的开发和设计。

二、驱动程序的分类和设计工具
    驱动程序是Windows系统的内核,驱动程序的分类与Windows相关,在Windows 9X下,驱动程序的类型为VXD(虚拟设备驱动程序),在Windows 2000/XP,驱动程序的类型为WDM(Windows驱动程序设计模型),生成的驱动程序设计文件为.sys格式。
    在Windows9X下,设计驱动程序的工具称为VTOOLSD,而在Windows 2000/xp下,设计驱动程序的工具为DriverStudio中的DriverWorks,另外的设计驱动程序的工具还有WinDriver,微软提供的开发工具为Windows DDK。由于所有的驱动设计工具均以DDK作为基本的类或者参照,加上DDK是一个免费软件,所以在下面主要以DDK为例进行讲解,掌握了DDK工具,其他工具也就变得简单了。

三、Window DDK软件的安装与环境设置
    每个Windows系统都有各自的DDK开发工具,在安装DDK前,请先根据Windows系统的不同,安装不同的DDK工具。在安装DDK之前,请先安装相应的编译器如Visual C++6.0,本文以Windows 2000下的DDK为例,说明安装过程,安装DDK完成后,选择菜单“开始”->“Development Kit”->“Check Build environment”将自动进行各项环境的设置,当然用户也可选择“Free Build environment”,二者的区别在于“check”带有调试信息,“Free”则不带有调试信息,一般情况下,在软件开发阶段,选用“check”,而在发布阶段,选用“Free”。
    选择上述命令后,将进入编译环境,该环境为“DOS”界面,进入驱动程序所在的目录,一般情况下,该目录包含了以下文件:
makefile 编译文件,一般不作更改
sources 规定了其中的源文件,驱动程序的类型
源文件 为.c或者.h文件
    为了编写方便,我们可以进入DDK提供的例子下面,将makefile和sources拷贝到我们程序所在的目录下,将sources进行简单的修改,编译时,进入相应的目录,在该目录下输入“Build”,系统编译完成后,将在objchk\i386目录下生成相应的.sys文件。

四、驱动程序的编写
    寻找一个合适的编译器如EditPlus,当然也可以用VC,只不过需要手动编译,第一步,找到驱动程序的入口函数DriverEntry(),相当于Main()或者WinMain(),所有的驱动函数入口均从DriverEntry()开始,下面以端口驱动程序为例,说明驱动的编写。该文件位于NTDDK\src\general\portio下。
1.创建设备
    对于设备驱动程序,首先要创建该设备,这段代码可以放在DriverEntry中,也可放在AddDevice中。
首先,调用IoCreateDevice()创建自己的设备,该函数用法可以参考DDK或者相关示例。在端口操作中,可以将函数写为:
status = IoCreateDevice (DriverObject,
                    sizeof (LOCAL_DEVICE_INFO),
                    &7,
                    GPD_TYPE,
                    0,
                    FALSE,
                  &deviceObject);
    其次,调用函数IoCreateSymbolicLink()创建两个设备之间的连接
    status = IoCreateSymbolicLink( &win32DeviceName, &ntDeviceName );

NTSTATUS
DiskFilterAddDevice(
	IN PDRIVER_OBJECT DriverObject,
	IN PDEVICE_OBJECT PhysicalDeviceObject
	)
{

	NTSTATUS                status;
	IO_STATUS_BLOCK         ioStatus;
	PDEVICE_OBJECT          filterDeviceObject;
	PDEVICE_EXTENSION       deviceExtension;
	PIRP                    irp;
	HANDLE              thread_handle;
	static  UCHAR firstdisk = 0;
	ULONG					diskOrderFalg = 0;
	PAGED_CODE();
	DbgPrint("ntdisk:add device.\n");
	DbgPrint("DriverName = %ws\n", DriverObject->HardwareDatabase->Buffer);
	if (PhysicalDeviceObject->DeviceType == FILE_DEVICE_MASS_STORAGE && PhysicalDeviceObject->Characteristics == 0x181)//U盘
	{
		return STATUS_SUCCESS;
	}
	status = IoCreateDevice(DriverObject,
		DEVICE_EXTENSION_SIZE,
		NULL,
		FILE_DEVICE_DISK,
		0,
		FALSE,
		&filterDeviceObject);

	if (!NT_SUCCESS(status))
	{
		return status;
	}

	filterDeviceObject->Flags |= DO_DIRECT_IO;

	deviceExtension = (PDEVICE_EXTENSION)filterDeviceObject->DeviceExtension;

	RtlZeroMemory(deviceExtension, DEVICE_EXTENSION_SIZE);

	deviceExtension->TargetPhysicalDeviceObject = PhysicalDeviceObject;
	deviceExtension->TargetDeviceObject = IoAttachDeviceToDeviceStack(filterDeviceObject, PhysicalDeviceObject);
	if (deviceExtension->TargetDeviceObject == NULL)
	{
		IoDeleteDevice(filterDeviceObject);
		return STATUS_NO_SUCH_DEVICE;
	}

	deviceExtension->DiskNumber = firstdisk++;

	DbgPrint("ntdisk: Attach to device %x,my device %x,return %x,disknumber %d \n", PhysicalDeviceObject, filterDeviceObject, deviceExtension->TargetDeviceObject, deviceExtension->DiskNumber);
	deviceExtension->DeviceObject = filterDeviceObject;
	KeInitializeEvent(&deviceExtension->PagingPathCountEvent, NotificationEvent, TRUE);

	filterDeviceObject->Flags |= DO_POWER_PAGABLE;

	filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;


	g_pDeviceExten[deviceExtension->DiskNumber] = deviceExtension;

	//向scsi发信号,打开开关。
	MakeSynchronusIoctl(deviceExtension->TargetDeviceObject, IOCTL_FROM_NTDISK_RW_SWITCH_ON, NULL, 0, NULL, 0);
	return STATUS_SUCCESS;
}


2.初始化所用的资源
    在驱动程序中,总需要访问I/O端口、系统中断、内存地址以及DMA,使用这些资源之前,需要获取资源并且初始化,一种简单的方法是直接指定,如中断10,DMA3等,这种方法虽然简单,但灵活性差,任何硬件资源的改变均需在驱动程序中作出修改;另一种较为科学的方法就是让驱动程序访问注册表,从注册表中访问硬件资源,然后进行初始化。
在驱动程序中,访问注册表的函数为RtlQueryRegistryValues(),该函数的用法较为复杂,可参考相关资料,建议在驱动程序开始开发时,直接给资源赋值,等驱动程序调试成功后再加入访问注册表代码。
3.注册驱动程序的各个处理函数
    DriverObject->MajorFunction[IRP_MJ_CREATE]          = GpdDispatch;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = GpdDispatch;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = GpdDispatch;
    DriverObject->DriverUnload                          = GpdUnload;
    DriverObject->MajorFunction[IRP_MJ_PNP]            = GpdDispatchPnp;
    DriverObject->MajorFunction[IRP_MJ_POWER]          = GpdDispatchPower;
    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]= SystemControl;
    DriverObject->DriverExtension->AddDevice          = GpdAddDevice;
    处理函数的注册方法有点类似Windows下应用程序设计的消息处理函数,注册完成后,当处理相应的IRP时,自动调用相应的函数模块。

NTSTATUS
DriverEntry(
	IN PDRIVER_OBJECT DriverObject,
	IN PUNICODE_STRING RegistryPath
	)
{


	ULONG i;
	HANDLE hThread;
	NTSTATUS ntStatus;

	for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
		//if (i != IRP_MJ_POWER)
		{
			DriverObject->MajorFunction[i] =
				DiskFilterPassThrough;
		}
	}
	DriverObject->MajorFunction[IRP_MJ_CREATE] = DiskFilterCreate;
	DriverObject->MajorFunction[IRP_MJ_READ] = DiskFilterReadWrite4;
	DriverObject->MajorFunction[IRP_MJ_WRITE] = DiskFilterReadWrite4;
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DiskFilterDeviceControl;
	DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = DiskFilterShutdownFlush;

	//DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] =  DiskFilterShutdownFlush;
	//DriverObject->MajorFunction[IRP_MJ_POWER] =          DiskFilterDispatchPower;
	//DriverObject->MajorFunction[IRP_MJ_PNP]             = DiskFilterDispatchPnp;

	DriverObject->DriverExtension->AddDevice = DiskFilterAddDevice;
	DriverObject->DriverUnload = DiskFilterUnload;

	return(STATUS_SUCCESS);

} // DriverEntry


五、驱动程序与应用程序间的信息交互
    驱动程序用以访问底层硬件,应用程序实现人机交互,驱动程序和应用程序之间需要实现相应的信息交互,一方面,应用程序通过对驱动程序发送相应的指令,实现硬件控制的动作指令,另一方面,驱动程序将硬件读写的状态、从硬件上获得的数据传送给驱动程序,实现应用程序与驱动程序间的交互函数包括以下API函数;相应的API函数能够激发驱动程序的消息。
接口API函数          驱动程序的中IRP
CreateFile            IRP_MJ_CREATE
CloseHandle         IRP_MJ_CLOSE
ReadFile              IRP_MJ_READ
WriteFile              IRP_MJ_WRITE
DeviceIoControl    IRP_MJ_DEVICE_CONTROL
    在应用程序中,用户可以调用上述函数操作驱动程序,其中CreateFile( )用于打开驱动程序,在使用完驱动程序之后,可以用CloseHandle()关闭驱动程序,ReadFile( )用于从驱动程序中读取数据,WriteFile()用以往驱动程序中写入数据,在函数中,最重要的是DeviceIoControl(),通过定义各种ITL_CODE来实现应用程序与驱动程序间的通讯函数,并可以传递各种参数和数据。

六、驱动程序的安装
使用SRVINSTW.EXE--->

1.手动安装方法
    生成的驱动程序为sys后缀,一般放于Windows\System32\Drivers目录下,如果进行手动安装,可以将生成好的驱动程序拷贝到该目录中,然后修改注册表,对于注册表的修改,可以进入注册表修改程序进行修改,也可编写注册表程序进行修改,以下为一注册驱动程序的注册表文件示例。
REGEDIT4
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Device]
"Type"=dword:00000001
"Start"=dword:00000001
"ErrorControl"=dword:00000001
"DisplayName"="Device"
"Group"="port"
"Tag"=dword:00000001

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Device\Parameters]
"IRQ Line"=dword:00000003
    直接在资源管理器下双击reg文件,在弹出的窗口上选择“是”将直接修改注册表,完成后,重新启动Windows系统,将调用驱动程序。
2.编写安装文件INF
    INF文件含有安装一个WDM设备驱动程序需要的所有必需的信息,包括赋值的文件列表,要创建的注册表项等,Windows为大多数类型的设备提供了一个标准的安装程序。INF文件是一个文本文件,由节组成,每一节从括在方括号中的节的名称开始,后面是节的内容,每一行可以是简单的一项,或者设置一个一个值。具体的INF文件编写可以参考现成的示例。
DDK安装完成后,其中存在工具GenINF,可以按照该向导进行INF文件的编写。
3.利用API函数编程实现驱动程序的安装
    利用API函数实现注册表的安装,其实是利用访问注册表的API函数访问修改注册表,实现驱动程序的安装。这种方法完全可以嵌入到我们的应用程序中,以下提供了安装驱动程序的API代码。主要的API函数包括RegCreateKeyEx(),RegSetValueEx(),RegQueryValueEx(),RegCloseKey() 。

七、驱动程序的调试
由于驱动程序的所有信息不能直接输出到屏幕上,所以驱动程序的调试较一般应用程序要难得多,在调试时,可以利用应用程序中的DeviceIoControl()获取驱动程序的状态,也可借助调试工具SoftIce,比较方便的工具是SysInternals公司的DebugView,如果驱动程序中带有调试语句信息DbgPrint(),可以直接将该函数提供的信息显示到屏幕上。

使用windbg+虚拟机调试 || windbg+真机   通过串口调试。
1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:101960次
    • 积分:1951
    • 等级:
    • 排名:千里之外
    • 原创:91篇
    • 转载:52篇
    • 译文:0篇
    • 评论:10条
    最新评论