x64驅動基礎教程 04
配置好驅動測試環境後,就可以正式編寫驅動了。市面上講解驅動開發的書籍汗牛充棟,但講得較為太複雜,讓初學者不好理解。本文從一個簡單的 hello,world 驅動(驅動模板)講起,力求講解得簡單明瞭,讓大家好理解。
本文主角:
(不得不說,此人長得帥,程式設計技術又牛,讓多少男人羨慕妒忌,讓多少女人一見傾心)。
下載地址: http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
2.KmdMgr。 KmdMgr 是一個由俄國人編寫的驅動載入工具。比起國內那些亂七八糟的驅動載入工具,它的特點是可以與驅動進行通訊(雖然無法設定 I/O 緩衝區)。下載地址:
ofollow,noindex" target="_blank">https://www.assembla.com/code/L2h/subversion/nodes/LowLevel/KmdManager.exe?_for mat=raw&rev=1
3. WIN64AST。作者自行開發的 64 位 ARK 類工具。在本章中用來檢視驅動是否載入成功。在後續章節還有其他的用途。下載地址: www.win64ast.com 。
4.WIN64UDL。作者自行開發的驅動載入工具,能在正常模式下載入沒有簽名的驅動。
編寫驅動: 以下是一個我寫的 WIN64 驅動模板
//【0】包含的標頭檔案,可以加入系統或自己定義的標頭檔案 #include <ntddk.h> #include <windef.h> #include <stdlib.h> //【1】定義符號連結,一般來說修改為驅動的名字即可 #define DEVICE_NAME L"\\Device\\KrnlHW64" #define LINK_NAME L"\\DosDevices\\KrnlHW64" #define LINK_GLOBAL_NAME L"\\DosDevices\\Global\\KrnlHW64" //【2】定義驅動功能號和名字,提供介面給應用程式呼叫 #define IOC/">IOCTL_IO_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_SAY_HELLO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) //【3】驅動解除安裝的處理例程 VOID DriverUnload(PDRIVER_OBJECT pDriverObj) { UNICODE_STRING strLink; DbgPrint("[KrnlHW64]DriverUnload\n"); RtlInitUnicodeString(&strLink, LINK_NAME); IoDeleteSymbolicLink(&strLink); IoDeleteDevice(pDriverObj->DeviceObject); } //【4】 IRP_MJ_CREATE對應的處理例程,一般不用管它 NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) { DbgPrint("[KrnlHW64]DispatchCreate\n"); pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_SUCCESS; } //【5】 IRP_MJ_CLOSE對應的處理例程,一般不用管它 NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp) { DbgPrint("[KrnlHW64]DispatchClose\n"); pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_SUCCESS; } //【6】 IRP_MJ_DEVICE_CONTROL對應的處理例程,驅動最重要的函式之一,一般走正常途徑呼叫驅動功能的程式,都會經過這個函式 NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp) { NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; PIO_STACK_LOCATION pIrpStack; ULONG uIoControlCode; PVOID pIoBuffer; ULONG uInSize; ULONG uOutSize; DbgPrint("[KrnlHW64]DispatchIoctl\n"); pIrpStack = IoGetCurrentIrpStackLocation(pIrp); //控制碼 uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; //輸入輸出緩衝區 pIoBuffer = pIrp->AssociatedIrp.SystemBuffer; //輸入區域大小 uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; //輸出區域大小 uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; switch(uIoControlCode) { //在這裡加入介面 case IOCTL_IO_TEST: { DWORD dw=0; //獲得輸入的內容 memcpy(&dw,pIoBuffer,sizeof(DWORD)); //使用輸入的內容 dw++; //輸出處理的結果 memcpy(pIoBuffer,&dw,sizeof(DWORD)); //處理成功,返回非STATUS_SUCCESS會讓DeviveIoControl返回失敗 status = STATUS_SUCCESS; break; } case IOCTL_SAY_HELLO: { DbgPrint("[KrnlHW64]IOCTL_SAY_HELLO\n"); status = STATUS_SUCCESS; break; } } if(status == STATUS_SUCCESS) pIrp->IoStatus.Information = uOutSize; else pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = status; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return status; } //【7】驅動載入的處理例程,裡面進行了驅動的初始化工作 NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING ustrLinkName; UNICODE_STRING ustrDevName; PDEVICE_OBJECT pDevObj; //初始化驅動例程 pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl; pDriverObj->DriverUnload = DriverUnload; RtlInitUnicodeString(&ustrDevName, DEVICE_NAME); status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj); if(!NT_SUCCESS(status)) return status; if(IoIsWdmVersionAvailable(1, 0x10)) RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME); else RtlInitUnicodeString(&ustrLinkName, LINK_NAME); //建立符號連結 status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName); if(!NT_SUCCESS(status)) { IoDeleteDevice(pDevObj); return status; } //走到這裡驅動實際上已經初始化完成,下面新增的是功能初始化的程式碼 DbgPrint("[KrnlHW64]DriverEntry\n"); return STATUS_SUCCESS; }
編譯驅動:
1.開啟『 x64 Free Build Environment 』:
2.切換到原始碼目錄(假設原始碼目錄是: z:\sys ),並輸入 BUILD 編譯:
3.如果看到『 1 executable built 』字眼,則證明編譯成功。
4.驅動的編譯跟目錄下的 source 檔案有關係,比如本例中,它的內容如下(注意不要手賤把空行去掉了,否則可能會導致無法編譯):
TARGETNAME=KrnlHW64<-驅動的檔案的名稱,一般來說修改這個就行了 TARGETTYPE=DRIVER<-編譯的型別 TARGETPATH=obj INCLUDES=.\ SOURCES = MyDriver.c<-多個C檔案時,把所有C檔案的名稱分成多行寫
測試驅動前的準備:
1.以管理員許可權執行 DBGVIEW 。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session
Manager\Debug
Print Filter的 Default 值修改為 ffffffff
3.開啟 DBGVIEW 並把以下選項全部勾上:
標準的驅動測試方法:
1.開啟虛擬機器,進入雙機除錯的環境(忘記了就參考上節課的內容)。
2.執行 KmdMgr.exe ,把 SYS 拖動到文字框裡。
3.點選 “Register” 和 “Run” 按鈕,看看輸出是否提示成功。如果成功會有類似的輸出:
4.執行 WIN64AST ,點選核心模組,檢視驅動是否已經存在於核心裡了:
“I/O
Control”
按鈕,如果成功會有類似的輸出:
6. 點選 “Unregister” 和 “Stop” 按鈕,如果成功會有類似的輸出:
很顯然,用標準方法測試一個驅動是很麻煩且很耗時的。雙機除錯非常佔用系統資源,雖然我的電腦配置較好( 2600K+16GB 記憶體),但是在操作虛擬機器時,仍然感到了明顯的卡頓。下面介紹一種用特殊工具測試驅動的方法,無需雙機除錯,甚至無需使用虛擬機器。
用 WIN64UDL 測試驅動:
1.執行 WIN64UDL 。
2.把驅動檔案拖進 WIN64UDL 裡,然後按下 ENTER 載入。
3.再按一次 ENTER 解除安裝驅動。