Windows驱动开发入门
對(duì)于初學(xué)者,DbgView.exe和SRVINSTW.EXE是非常簡(jiǎn)單有用的兩個(gè)工具,一定要裝上。前者用于查看日志信息,后者用于加載驅(qū)動(dòng)。
一、驅(qū)動(dòng)程序設(shè)計(jì)的必要性
??? 在傳統(tǒng)DOS系統(tǒng)下,每個(gè)應(yīng)用程序都有權(quán)利讀寫硬件,讀寫I/O端口,控制系統(tǒng)中斷,然而到了Windows系統(tǒng)中,為了保持良好的系統(tǒng)安全性,對(duì)應(yīng)用程序的權(quán)限作出了限制,因?yàn)椴贿m當(dāng)?shù)挠布x寫會(huì)引發(fā)整個(gè)系統(tǒng)的崩潰。在Windows系統(tǒng)中,將整個(gè)程序設(shè)計(jì)為分層結(jié)構(gòu),其中,應(yīng)用程序位于ring3,驅(qū)動(dòng)程序位于ring0,應(yīng)用程序不能讀寫底層硬件,對(duì)于硬件操作必須借助于底層驅(qū)動(dòng)程序,所以,只要是與硬件系統(tǒng)打交道的Windows程序,必然會(huì)涉及到驅(qū)動(dòng)程序的開發(fā)和設(shè)計(jì)。
二、驅(qū)動(dòng)程序的分類和設(shè)計(jì)工具
??? 驅(qū)動(dòng)程序是Windows系統(tǒng)的內(nèi)核,驅(qū)動(dòng)程序的分類與Windows相關(guān),在Windows 9X下,驅(qū)動(dòng)程序的類型為VXD(虛擬設(shè)備驅(qū)動(dòng)程序),在Windows 2000/XP,驅(qū)動(dòng)程序的類型為WDM(Windows驅(qū)動(dòng)程序設(shè)計(jì)模型),生成的驅(qū)動(dòng)程序設(shè)計(jì)文件為.sys格式。
??? 在Windows9X下,設(shè)計(jì)驅(qū)動(dòng)程序的工具稱為VTOOLSD,而在Windows 2000/xp下,設(shè)計(jì)驅(qū)動(dòng)程序的工具為DriverStudio中的DriverWorks,另外的設(shè)計(jì)驅(qū)動(dòng)程序的工具還有WinDriver,微軟提供的開發(fā)工具為Windows DDK。由于所有的驅(qū)動(dòng)設(shè)計(jì)工具均以DDK作為基本的類或者參照,加上DDK是一個(gè)免費(fèi)軟件,所以在下面主要以DDK為例進(jìn)行講解,掌握了DDK工具,其他工具也就變得簡(jiǎn)單了。
三、Window DDK軟件的安裝與環(huán)境設(shè)置
??? 每個(gè)Windows系統(tǒng)都有各自的DDK開發(fā)工具,在安裝DDK前,請(qǐng)先根據(jù)Windows系統(tǒng)的不同,安裝不同的DDK工具。在安裝DDK之前,請(qǐng)先安裝相應(yīng)的編譯器如Visual C++6.0,本文以Windows 2000下的DDK為例,說明安裝過程,安裝DDK完成后,選擇菜單“開始”->“Development Kit”->“Check Build environment”將自動(dòng)進(jìn)行各項(xiàng)環(huán)境的設(shè)置,當(dāng)然用戶也可選擇“Free Build environment”,二者的區(qū)別在于“check”帶有調(diào)試信息,“Free”則不帶有調(diào)試信息,一般情況下,在軟件開發(fā)階段,選用“check”,而在發(fā)布階段,選用“Free”。
??? 選擇上述命令后,將進(jìn)入編譯環(huán)境,該環(huán)境為“DOS”界面,進(jìn)入驅(qū)動(dòng)程序所在的目錄,一般情況下,該目錄包含了以下文件:
makefile 編譯文件,一般不作更改
sources 規(guī)定了其中的源文件,驅(qū)動(dòng)程序的類型
源文件 為.c或者.h文件
??? 為了編寫方便,我們可以進(jìn)入DDK提供的例子下面,將makefile和sources拷貝到我們程序所在的目錄下,將sources進(jìn)行簡(jiǎn)單的修改,編譯時(shí),進(jìn)入相應(yīng)的目錄,在該目錄下輸入“Build”,系統(tǒng)編譯完成后,將在objchk\i386目錄下生成相應(yīng)的.sys文件。
四、驅(qū)動(dòng)程序的編寫
??? 尋找一個(gè)合適的編譯器如EditPlus,當(dāng)然也可以用VC,只不過需要手動(dòng)編譯,第一步,找到驅(qū)動(dòng)程序的入口函數(shù)DriverEntry(),相當(dāng)于Main()或者WinMain(),所有的驅(qū)動(dòng)函數(shù)入口均從DriverEntry()開始,下面以端口驅(qū)動(dòng)程序?yàn)槔?#xff0c;說明驅(qū)動(dòng)的編寫。該文件位于NTDDK\src\general\portio下。
1.創(chuàng)建設(shè)備
??? 對(duì)于設(shè)備驅(qū)動(dòng)程序,首先要?jiǎng)?chuàng)建該設(shè)備,這段代碼可以放在DriverEntry中,也可放在AddDevice中。
首先,調(diào)用IoCreateDevice()創(chuàng)建自己的設(shè)備,該函數(shù)用法可以參考DDK或者相關(guān)示例。在端口操作中,可以將函數(shù)寫為:
status = IoCreateDevice (DriverObject,
??????????????????? sizeof (LOCAL_DEVICE_INFO),
??????????????????? &7,
??????????????????? GPD_TYPE,
??????????????????? 0,
??????????????????? FALSE,
????????????????? &deviceObject);
??? 其次,調(diào)用函數(shù)IoCreateSymbolicLink()創(chuàng)建兩個(gè)設(shè)備之間的連接
??? 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發(fā)信號(hào),打開開關(guān)。MakeSynchronusIoctl(deviceExtension->TargetDeviceObject, IOCTL_FROM_NTDISK_RW_SWITCH_ON, NULL, 0, NULL, 0);return STATUS_SUCCESS; }
2.初始化所用的資源
??? 在驅(qū)動(dòng)程序中,總需要訪問I/O端口、系統(tǒng)中斷、內(nèi)存地址以及DMA,使用這些資源之前,需要獲取資源并且初始化,一種簡(jiǎn)單的方法是直接指定,如中斷10,DMA3等,這種方法雖然簡(jiǎn)單,但靈活性差,任何硬件資源的改變均需在驅(qū)動(dòng)程序中作出修改;另一種較為科學(xué)的方法就是讓驅(qū)動(dòng)程序訪問注冊(cè)表,從注冊(cè)表中訪問硬件資源,然后進(jìn)行初始化。
在驅(qū)動(dòng)程序中,訪問注冊(cè)表的函數(shù)為RtlQueryRegistryValues(),該函數(shù)的用法較為復(fù)雜,可參考相關(guān)資料,建議在驅(qū)動(dòng)程序開始開發(fā)時(shí),直接給資源賦值,等驅(qū)動(dòng)程序調(diào)試成功后再加入訪問注冊(cè)表代碼。
3.注冊(cè)驅(qū)動(dòng)程序的各個(gè)處理函數(shù)
??? 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;
??? 處理函數(shù)的注冊(cè)方法有點(diǎn)類似Windows下應(yīng)用程序設(shè)計(jì)的消息處理函數(shù),注冊(cè)完成后,當(dāng)處理相應(yīng)的IRP時(shí),自動(dòng)調(diào)用相應(yīng)的函數(shù)模塊。
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
五、驅(qū)動(dòng)程序與應(yīng)用程序間的信息交互
??? 驅(qū)動(dòng)程序用以訪問底層硬件,應(yīng)用程序?qū)崿F(xiàn)人機(jī)交互,驅(qū)動(dòng)程序和應(yīng)用程序之間需要實(shí)現(xiàn)相應(yīng)的信息交互,一方面,應(yīng)用程序通過對(duì)驅(qū)動(dòng)程序發(fā)送相應(yīng)的指令,實(shí)現(xiàn)硬件控制的動(dòng)作指令,另一方面,驅(qū)動(dòng)程序?qū)⒂布x寫的狀態(tài)、從硬件上獲得的數(shù)據(jù)傳送給驅(qū)動(dòng)程序,實(shí)現(xiàn)應(yīng)用程序與驅(qū)動(dòng)程序間的交互函數(shù)包括以下API函數(shù);相應(yīng)的API函數(shù)能夠激發(fā)驅(qū)動(dòng)程序的消息。
接口API函數(shù)????????? 驅(qū)動(dòng)程序的中IRP
CreateFile??????????? IRP_MJ_CREATE
CloseHandle???????? IRP_MJ_CLOSE
ReadFile????????????? IRP_MJ_READ
WriteFile????????????? IRP_MJ_WRITE
DeviceIoControl??? IRP_MJ_DEVICE_CONTROL
??? 在應(yīng)用程序中,用戶可以調(diào)用上述函數(shù)操作驅(qū)動(dòng)程序,其中CreateFile( )用于打開驅(qū)動(dòng)程序,在使用完驅(qū)動(dòng)程序之后,可以用CloseHandle()關(guān)閉驅(qū)動(dòng)程序,ReadFile( )用于從驅(qū)動(dòng)程序中讀取數(shù)據(jù),WriteFile()用以往驅(qū)動(dòng)程序中寫入數(shù)據(jù),在函數(shù)中,最重要的是DeviceIoControl(),通過定義各種ITL_CODE來實(shí)現(xiàn)應(yīng)用程序與驅(qū)動(dòng)程序間的通訊函數(shù),并可以傳遞各種參數(shù)和數(shù)據(jù)。
六、驅(qū)動(dòng)程序的安裝
使用SRVINSTW.EXE--->
1.手動(dòng)安裝方法
??? 生成的驅(qū)動(dòng)程序?yàn)閟ys后綴,一般放于Windows\System32\Drivers目錄下,如果進(jìn)行手動(dòng)安裝,可以將生成好的驅(qū)動(dòng)程序拷貝到該目錄中,然后修改注冊(cè)表,對(duì)于注冊(cè)表的修改,可以進(jìn)入注冊(cè)表修改程序進(jìn)行修改,也可編寫注冊(cè)表程序進(jìn)行修改,以下為一注冊(cè)驅(qū)動(dòng)程序的注冊(cè)表文件示例。
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文件,在彈出的窗口上選擇“是”將直接修改注冊(cè)表,完成后,重新啟動(dòng)Windows系統(tǒng),將調(diào)用驅(qū)動(dòng)程序。
2.編寫安裝文件INF
??? INF文件含有安裝一個(gè)WDM設(shè)備驅(qū)動(dòng)程序需要的所有必需的信息,包括賦值的文件列表,要?jiǎng)?chuàng)建的注冊(cè)表項(xiàng)等,Windows為大多數(shù)類型的設(shè)備提供了一個(gè)標(biāo)準(zhǔn)的安裝程序。INF文件是一個(gè)文本文件,由節(jié)組成,每一節(jié)從括在方括號(hào)中的節(jié)的名稱開始,后面是節(jié)的內(nèi)容,每一行可以是簡(jiǎn)單的一項(xiàng),或者設(shè)置一個(gè)一個(gè)值。具體的INF文件編寫可以參考現(xiàn)成的示例。
DDK安裝完成后,其中存在工具GenINF,可以按照該向?qū)нM(jìn)行INF文件的編寫。
3.利用API函數(shù)編程實(shí)現(xiàn)驅(qū)動(dòng)程序的安裝
??? 利用API函數(shù)實(shí)現(xiàn)注冊(cè)表的安裝,其實(shí)是利用訪問注冊(cè)表的API函數(shù)訪問修改注冊(cè)表,實(shí)現(xiàn)驅(qū)動(dòng)程序的安裝。這種方法完全可以嵌入到我們的應(yīng)用程序中,以下提供了安裝驅(qū)動(dòng)程序的API代碼。主要的API函數(shù)包括RegCreateKeyEx(),RegSetValueEx(),RegQueryValueEx(),RegCloseKey() 。
七、驅(qū)動(dòng)程序的調(diào)試
由于驅(qū)動(dòng)程序的所有信息不能直接輸出到屏幕上,所以驅(qū)動(dòng)程序的調(diào)試較一般應(yīng)用程序要難得多,在調(diào)試時(shí),可以利用應(yīng)用程序中的DeviceIoControl()獲取驅(qū)動(dòng)程序的狀態(tài),也可借助調(diào)試工具SoftIce,比較方便的工具是SysInternals公司的DebugView,如果驅(qū)動(dòng)程序中帶有調(diào)試語(yǔ)句信息DbgPrint(),可以直接將該函數(shù)提供的信息顯示到屏幕上。
使用windbg+虛擬機(jī)調(diào)試 || windbg+真機(jī) ? 通過串口調(diào)試。
總結(jié)
以上是生活随笔為你收集整理的Windows驱动开发入门的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux安装PHPwind
- 下一篇: 生产企业智能制造执行系统解决方案(MES