1. 前言
随着計算機科學技術的發展,驅動程式的開發悄然成為各個計算機應用領域(特别是編寫與硬體相關程式)的程式員的關注的話題,對于那些迫切希望探究驅動程式開發奧秘的程式員來講,面對鋪天蓋地,五花八門的各種圖書資料,難免出現不知從何入手的問題,本文将帶領你利用微軟成熟的開發設計環境,自己動手開發出幾類最簡單的驅動程式,抛磚引玉,希望大家能夠從中吸取到自己需要的知識,戳破驅動程式開發神秘的面紗,提升自身軟體設計實力,為祖國的軟體事業發展做出更大的貢獻。
本人在學習驅動程式開發伊始,懵懵懂懂中也翻閱了不少前輩們的書籍,也在網際網路上搜集了不少關于驅動開發方面的資料,出處無法一一列舉,本文也将引用或者參考部分内容,在此感謝原著對本人的幫助,對前輩們獻上我最崇高的敬意。
2. 開發環境搭建
2.1 軟體平台搭建:
Microsoft Visual Studio 2008 , WDK7,VMware Workstation6.5.
安裝VS2008及MSDN 。MSDN 可以幫助你更好使用VS2008,在出現問題找不到答案時,可以仔細閱讀一下MSDN ,會提供一些必要的幫助。并且可以通過MSDN免費得到WDK7的下載下傳連接配接。VS2008安裝步驟略。
下載下傳,安裝WDK7,即(Windows Driver Kits 7.0.0)。提示選擇安裝選項時,建議全部選擇安裝,WDK便自動安裝WinDbg(Windows調試工具),用于使用虛拟機對驅動程式代碼進行調試。安裝步驟略。
安裝VMware Workstation.建議選擇安裝6.0以上版本。安裝步驟略。安裝成功以後建立Windows虛拟機,筆者選擇的是WindowsXP系統(其他Windows系統大體相同),并安裝系統映像,使之成為可以正常工作的WindowsXP虛拟系統。
2.2 調試平台搭建:
軟體平台搭建成功以後,調試平台的搭建需要以下幾個步驟。
第一步,修改WindowsXP虛拟機的系統配置。
1. 修改虛拟機配置。在硬體中選擇添加序列槽。在連接配接屬性中選擇“使用命名管道”。保留預設命名管道名稱//./pipe/com_1。在序列槽端屬性中選擇“The end is the server.”,“The other end is an application. ”。勾選I/0模式中的”Yield CPU on Polled”複選框。
2. 啟動虛拟機進入WindowsXP系統,打開“我的電腦”視窗,在“工具”菜單裡面選擇“檔案夾選項”并點選,在檔案夾選項彈出視窗選擇“檢視”頁籤。在“進階選項”中去除“隐藏受保護的作業系統檔案”複選框勾選。并選擇“顯示所有檔案和檔案夾“。确定後系統關閉彈出視窗。
3. 打開系統的安裝分區,筆者電腦預設安裝的C槽。在根目錄下可以找到“boot.ini”配置檔案。輕按兩下打開檔案。修改[boot loader]。Timeouts = 30.修改[Operating systems],複制其中關于WindowsXP 的一行字元(如果是純淨系統隻有一行系統描述,有些系統可能帶有DOS安裝工具的選項,我們隻需要複制關于安裝Windows系統的描述),添加一新行并粘貼複制描述字元串。在系統描述字元串裡面添加“-Debug”以示和前面項目的差別,行末添加“/debug /debugport=COM1 /baudrate=115200” 。儲存關閉檔案。關閉系統。
4. 從開始菜單中選擇“Debugging Tools for Windows(X86)”中的windbg并打開。
在file 菜單下的Symbol Search Path項點選,彈出Symbol Search Path對話框。在Symbol Path編輯框裡面輸入srv*c:/windows/symbols*http://msdl.microsoft.com/download/symbols;cache*c:/windows/symbols。并建立C:/Windows/symbols檔案夾。在file 菜單下選擇Kernal Debug選項,彈出Kernal Debugging對話框,選擇COM頁籤,輸入波特率為115200,端口名//./pipe/com_1 。勾選Pipe複選框。确定後WinDbg即處于等待管道連接配接狀态。
5. 重新啟動WindowsXP虛拟機。在引導清單(即可看到我們在第3步中添加的系統描述)中,選擇帶有“-Debug”選項(前面設定哪項)并回車。正常情況下,在啟動一段時間後WinDbg即顯示連接配接成功。選擇WinDbg中的Debug菜單下break選項,如果虛拟機響應,WinDbg調試菜單和工具欄即變為有效狀态,可以進行單步等其他操作。說明調試平台搭建成功。首次進行連接配接可能需要較長時間。
3. KDM驅動開發示例
3.1 項目屬性配置
1. 打開VS2008,建立一個Visual C++ à Win32 à win32空項目。例如DDKDemo。
2. 打開VS2008 的“生成”菜單中的“配置管理器”選項。在活動解決方案配置中選擇《建立》,建立一個Check空的解決方案配置。
3. 在解決方案管理器中,建立一個DDKDemo.h頭檔案,一個DDKDemo.cpp源檔案。
4. 打開VS2008的“項目”菜單裡面“屬性”選項。即打開Test項目屬性頁。在項目屬性頁選擇“配置屬性”,打開十字圖示。
5. 選擇“C/C++”并展開内部選項。
在“正常”選項中,在“附加包含目錄”中添加wdk 引用頭檔案目錄。并去除“從父級或項目預設設定繼承”複選框的勾選。Wdk頭檔案目錄如下:
D:/WinDDK/7600.16385.0/inc/api
D:/WinDDK/7600.16385.0/inc/crt
D:/WinDDK/7600.16385.0/inc/ddk
注意筆者的WDK安裝目錄在D盤。
在“調試資訊格式”中選擇 “C7 相容(/Z7)”選項。
在“警告等級”中選擇“3級(/W3)”。
在“将警告視為錯誤”中選擇“是(/WX)”。
在“優化”選項中,在“優化”中選擇“禁用(/Od)”。
在“預處理器”選項中,在“預處理器定義”中添加 “WIN32=100;_X86_=1;WINVER=0x501;DBG=1”。并去除“從父級或項目預設屬性繼承”複選框的勾選。
在“進階”選項中,選擇“調用約定”為“__stdcall (/Gz)”。
6. 選擇“連結器”并展開内部選項。
在“正常”選項中,修改“輸出檔案”的檔案擴充名為 .sys 添加“附加庫目錄”
D:/WinDDK/7600.16385.0/lib/Crt/i386
D:/WinDDK/7600.16385.0/lib/wxp/i386
并去除“從父級或項目預設設定繼承”複選框的勾選。
在“輸入”選項中,添加“附加依賴項”ntoskrnl.lib并去除“從父級或項目預設設定繼承”複選框的勾選。
在“清單檔案”選項中,選擇“生成清單”為否。
在“調試”選項中,選擇“生成調試資訊”為“是 (/Debug)”。
在“系統”選項中,選擇“子系統”為“本機 (/SUBSYSTEM:NATIVE)”。選擇“驅動程式”為“驅動程式(/DRIVER)”。
在“進階”中,添加“入口點”為DriverEntry。選擇“随即基址”為“預設值”。選擇“資料執行保護”為預設值。選擇“目标計算機”為“MachineX86(/MACHINE:X86)”。
在“指令行”選項中,添加“/SECTION:INIT,D /IGNORE:4078”。
3.2 示例代碼簡介
#pragma once
//KDM驅動程式頭檔案
extern "C"
{
#include<ntddk.h>
}
//定義幾個函數段屬性宏
#define PAGEDCODE code_seg("PAGED") //分頁記憶體段
#define LOCKEDPAGE code_seg() //非分頁記憶體段
#define INITCODE code_seg("INIT") //初始化記憶體段
//定義裝置擴充結構,用于儲存裝置屬性
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT pDeviceObject; //裝置對象指針
UNICODE_STRING ustrDeviceName; //裝置名
UNICODE_STRING ustrSymbolicName; //符合連接配接名
}DEVICE_EXTENSION,*PDEVICE_EXTENSION;
//輔助建立裝置函數聲明
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject);
//驅動解除安裝回調函數
VOID DDKUnload(IN PDRIVER_OBJECT pDriverObject);
//驅動投遞函數
NTSTATUS DDKDispatchRoutine(IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp);
#include "DDKDemo.h"
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING ustrRegistryPath)
{
NTSTATUS status;
DbgPrint(("Hello,My Windows!Enter DriverEntry!/n"));
//添加一個調試斷點,用于使用WinDbg進行源代碼調試
DbgBreakPoint();
//注冊其他驅動回調函數入口
pDriverObject->DriverUnload=DDKUnload;
pDriverObject->MajorFunction[IRP_MJ_CREATE]=DDKDispatchRoutine;
pDriverObject->MajorFunction[IRP_MJ_CLOSE]=DDKDispatchRoutine;
pDriverObject->MajorFunction[IRP_MJ_WRITE]=DDKDispatchRoutine;
pDriverObject->MajorFunction[IRP_MJ_READ]=DDKDispatchRoutine;
//建立裝置
status=CreateDevice(pDriverObject);
DbgPrint(("DriverEntry end!/n"));
return status;
}
#pragma INITCODE
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject)
{
NTSTATUS status;
PDEVICE_OBJECT pDeviceObject;
PDEVICE_EXTENSION pDeviceExtension;
DbgPrint(("Enter CreateDevice!/n"));
//建立裝置名稱
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName,L"//Device//DDKDevice");
//建立裝置
status=IoCreateDevice(pDriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)DeviceName,
FILE_DEVICE_UNKNOWN,
0,TRUE,
&pDeviceObject);
if(!NT_SUCCESS(status))
{
return status;
}
//添加擴充屬性
pDeviceObject->Flags |= DO_BUFFERED_IO;
//建立符合連接配接
UNICODE_STRING SymbolicLinkName;
RtlInitUnicodeString(&SymbolicLinkName,L"//??//MyDDKDevice");
pDeviceExtension=(PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
pDeviceExtension->pDeviceObject=pDeviceObject;
pDeviceExtension->ustrDeviceName=DeviceName;
pDeviceExtension->ustrSymbolicName=SymbolicLinkName;
status=IoCreateSymbolicLink(&SymbolicLinkName,&DeviceName);
if(!NT_SUCCESS(status))
{
IoDeleteDevice(pDeviceObject);
DbgPrint(("CreateDevice Failed!/n"));
return status;
}
DbgPrint(("CreateDevice end!/n"));
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
VOID DDKUnload(IN PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pNextObject;
DbgPrint(("Enter DDKUnload/n"));
pNextObject=pDriverObject->DeviceObject;
while(pNextObject !=NULL)
{
//周遊所有挂載裝置
PDEVICE_EXTENSION pDevExtension=(PDEVICE_EXTENSION)pNextObject->DeviceExtension;
UNICODE_STRING SymLink=pDevExtension->ustrSymbolicName;
IoDeleteSymbolicLink(&SymLink);
pNextObject=pNextObject->NextDevice;
IoDeleteDevice(pDevExtension->pDeviceObject);
}
}
#pragma PAGEDCODE
NTSTATUS DDKDispatchRoutine(IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp)
{
DbgPrint(("Enter DDKDispatchRoutine!/n"));
NTSTATUS status=STATUS_SUCCESS;
pIrp->IoStatus.Status=status;
pIrp->IoStatus.Information=0;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
DbgPrint(("DDKDispatchRountine end!/n"));
return status;
}
3.3 安裝調試示例
打開DDKDemo所在目錄,進入Check檔案夾,複制DDKDemo.sys檔案到虛拟機中。
驅動的安裝可以有二種選擇,一種使用安裝工具進行安裝,一種自己編寫安裝程式。對于初學者,可以從網上下載下傳DriverMonitor驅動調試安裝工具來安裝自己生成的驅動程式。
打開WinDbg,在file菜單中打開Source Search Path項,在彈出的對話框的Source Path中添加DDKDemo目标檔案夾全路徑,即上述的Check檔案夾的全路徑。在Debug菜單中選擇Source Mode為選擇狀态,啟動Kernal Debug 核心調試。
在WindowsXP虛拟機中安裝加載DDKDemo.sys驅動程式,驅動程式被加載以後,虛拟機即在我們設定的斷點中斷,同時WinDbg處于激活狀态。在WinDbg中選擇單步執行,單步兩下,WinDbg即可彈出附帶源碼的驅動程式調試視窗。現在你已經進入裡系統驅動加載的過程中了,可以随意檢視程式執行過程中的參變量的變化。接下來就是你自己施展才華時候了。呵呵~~