Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)
驅動安裝,通訊,Hello?World
開發驅動的簡單流程是這樣,開發驅動安裝程序,開發驅動程序,然后安裝程序(或者其他程序)通過通訊給驅動傳命令,驅動接到之后進行解析并且執行,然后把執行結果返回。
驅動程序Hello?World
之前總結了驅動環境的搭建,這里就直接繼續之前搭建好的環境創建項目,打開vs2015創建一個驅動項目:
寫代碼之前先配置下編譯選項:
然后添加一個項目文件main.c(注意后綴是.c,前面名字無所謂可以不叫main),里面的內容如下(下面模板代碼來源于網絡,作者:胡文亮,我是在看他的資料學習,感謝這位前輩。)
/* WIN64驅動開發模板 作者:Tesla.Angela *///【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 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"); //獲得IRP里的關鍵數據 pIrpStack = IoGetCurrentIrpStackLocation(pIrp); //這個就是傳說中的控制碼 uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; //輸入和輸出的緩沖區(DeviceIoControl的InBuffer和OutBuffer都是它) pIoBuffer = pIrp->AssociatedIrp.SystemBuffer; //EXE發送傳入數據的BUFFER長度(DeviceIoControl的nInBufferSize) uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; //EXE接收傳出數據的BUFFER長度(DeviceIoControl的nOutBufferSize) 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 = STATUS_SUCCESS; break; } case IOCTL_SAY_HELLO: { DbgPrint("[KrnlHW64]IOCTL_SAY_HELLO\n"); status = STATUS_SUCCESS; break; } } //這里設定DeviceIoControl的*lpBytesReturned的值(如果通信失敗則返回0長度) if(status == STATUS_SUCCESS) pIrp->IoStatus.Information = uOutSize; else pIrp->IoStatus.Information = 0; //這里設定DeviceIoControl的返回值是成功還是失敗 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; //判斷支持的WDM版本,其實這個已經不需要了,純屬WIN9X和WINNT并存時代的殘留物 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; }然后右鍵編譯工程,會出現這個錯誤提示:
然后把這個文件刪除:
繼續編譯,還是不會過:繼續改另一個配置文件選項:
然后就可以了:
然后是驅動安裝程序:
目前如果沒有仔細看上面的那個基本模板代碼,需要回去仔細看下。大體了解細節,尤其是里面的注釋。
看完代碼了,接下來是驅動安裝。
驅動安裝和服務安裝,如果之前寫過安裝服務的代碼看驅動安裝代碼會很熟悉,都是采用SCM安裝。具體流程是:
然后這個函數要仔細看定義:
更詳細的細節之前看MSDN吧。
然后還是把資料上的模板代碼直接拿過來,注意目前先用這個模板,因為這個安裝模板是和上面的那個Hello?World對應的,等看懂之后了,在自己定義相關驅動安裝和驅動程序的代碼,也可以自己寫個模板,這里先不自己隨便定義,可能會導致鏈接名字,驅動名字,還有驅動通訊的那個地方的細節自己弄亂了。這個博客看完了再回頭定義自己的就行。
創建C++工程,然后直接添加安裝代碼和安裝測試代碼:
功能代碼:
/*============================ Drvier Control Class (SCM way) ============================*/#pragma comment(lib,"advapi32.lib")class cDrvCtrl { public: cDrvCtrl() { m_pSysPath = NULL; m_pServiceName = NULL; m_pDisplayName = NULL; m_hSCManager = NULL; m_hService = NULL; m_hDriver = INVALID_HANDLE_VALUE; } ~cDrvCtrl() { CloseServiceHandle(m_hService); CloseServiceHandle(m_hSCManager); CloseHandle(m_hDriver); } public: DWORD m_dwLastError; PCHAR m_pSysPath; PCHAR m_pServiceName; PCHAR m_pDisplayName; HANDLE m_hDriver; SC_HANDLE m_hSCManager; SC_HANDLE m_hService; public: BOOL Install(PCHAR pSysPath,PCHAR pServiceName,PCHAR pDisplayName); BOOL Start(); BOOL Stop(); BOOL Remove(); BOOL Open(PCHAR pLinkName); BOOL IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD *RealRetBytes); private: BOOL GetSvcHandle(PCHAR pServiceName); DWORD CTL_CODE_GEN(DWORD lngFunction); protected: //null };BOOL cDrvCtrl::GetSvcHandle(PCHAR pServiceName) { m_pServiceName = pServiceName; m_hSCManager = OpenSCManagerA(NULL,NULL,SC_MANAGER_ALL_ACCESS); if (NULL == m_hSCManager) { m_dwLastError = GetLastError(); return FALSE; } m_hService = OpenServiceA(m_hSCManager,m_pServiceName,SERVICE_ALL_ACCESS); if (NULL == m_hService) { CloseServiceHandle(m_hSCManager); return FALSE; } else { return TRUE; } }BOOL cDrvCtrl::Install(PCHAR pSysPath,PCHAR pServiceName,PCHAR pDisplayName) { m_pSysPath = pSysPath; m_pServiceName = pServiceName; m_pDisplayName = pDisplayName; m_hSCManager = OpenSCManagerA(NULL,NULL,SC_MANAGER_ALL_ACCESS); if (NULL == m_hSCManager) { m_dwLastError = GetLastError(); return FALSE; } m_hService = CreateServiceA(m_hSCManager,m_pServiceName,m_pDisplayName,SERVICE_ALL_ACCESS,SERVICE_KERNEL_DRIVER,SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,m_pSysPath,NULL,NULL,NULL,NULL,NULL); if (NULL == m_hService) { m_dwLastError = GetLastError(); if (ERROR_SERVICE_EXISTS == m_dwLastError) { m_hService = OpenServiceA(m_hSCManager,m_pServiceName,SERVICE_ALL_ACCESS); if (NULL == m_hService) { CloseServiceHandle(m_hSCManager); return FALSE; } } else { CloseServiceHandle(m_hSCManager); return FALSE; } } return TRUE; }BOOL cDrvCtrl::Start() { if (!StartServiceA(m_hService,NULL,NULL)) { m_dwLastError = GetLastError(); return FALSE; } return TRUE; }BOOL cDrvCtrl::Stop() { SERVICE_STATUS ss; GetSvcHandle(m_pServiceName); if (!ControlService(m_hService,SERVICE_CONTROL_STOP,&ss)) { m_dwLastError = GetLastError(); return FALSE; } return TRUE;}BOOL cDrvCtrl::Remove() { GetSvcHandle(m_pServiceName); if (!DeleteService(m_hService)) { m_dwLastError = GetLastError(); return FALSE; } return TRUE; }BOOL cDrvCtrl::Open(PCHAR pLinkName)//example: \\\\.\\xxoo { if (m_hDriver != INVALID_HANDLE_VALUE) return TRUE; m_hDriver = CreateFileA(pLinkName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if(m_hDriver != INVALID_HANDLE_VALUE) return TRUE; else return FALSE; }BOOL cDrvCtrl::IoControl(DWORD dwIoCode, PVOID InBuff, DWORD InBuffLen, PVOID OutBuff, DWORD OutBuffLen, DWORD *RealRetBytes) { DWORD dw; BOOL b=DeviceIoControl(m_hDriver,CTL_CODE_GEN(dwIoCode),InBuff,InBuffLen,OutBuff,OutBuffLen,&dw,NULL); if(RealRetBytes) *RealRetBytes=dw; return b; }DWORD cDrvCtrl::CTL_CODE_GEN(DWORD lngFunction) { return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) | METHOD_BUFFERED; }測試代碼: // DriverInstall.cpp : 定義控制臺應用程序的入口點。 //#include "stdafx.h" #include <string> #include <windows.h> #include "ScmDrvCtrl.h"#pragma warning(disable:4996)#pragma comment(lib,"user32.lib")using namespace std;void GetAppPath(char *szCurFile) //最后帶斜杠 { GetModuleFileNameA(0, szCurFile, MAX_PATH); for (SIZE_T i = strlen(szCurFile) - 1; i >= 0; i--) { if (szCurFile[i] == '\\') { szCurFile[i + 1] = '\0'; break; } } }int main() { BOOL b; cDrvCtrl dc; //設置驅動名稱 char szSysFile[MAX_PATH] = { 0 }; char szSvcLnkName[] = "KrnlHW64";; GetAppPath(szSysFile); strcat(szSysFile, "KrnlHW64.sys"); //安裝并啟動驅動 b = dc.Install(szSysFile, szSvcLnkName, szSvcLnkName); b = dc.Start(); printf("LoadDriver=%d\n", b); //“打開”驅動的符號鏈接 dc.Open("\\\\.\\KrnlHW64"); //使用控制碼控制驅動(0x800:傳入一個數字并返回一個數字) DWORD x = 100, y = 0, z = 0; dc.IoControl(0x800, &x, sizeof(x), &y, sizeof(y), &z); printf("INPUT=%ld\nOUTPUT=%ld\nReturnBytesLength=%ld\n", x, y, z); //使用控制碼控制驅動(0x801:在DBGVIEW里顯示HELLOWORLD) dc.IoControl(0x801, 0, 0, 0, 0, 0); //關閉符號鏈接句柄 CloseHandle(dc.m_hDriver); //停止并卸載驅動 b = dc.Stop(); b = dc.Remove(); printf("UnloadDriver=%d\n", b); getchar(); return 0; }
然后就直接本地嘗試調試安裝一次,結果先是這個:
不用管它,點擊全部允許,但是還是會發現
?
還是裝不上,其實是肯定裝不上的。原因是64位機器需要的強制簽名加載驅動:
注意最后面那句,各種系統的防護已經被攻克,他沒有說win10,不過目前已經親測win10也已經攻克,之后有機會再說這些東西,這里只討論正常安裝。想在win64上安裝無簽名驅動也可以,如果是win7,直接cmd?輸入??bcdedit?/set?testsigning?on?,然后重啟電腦(win7是這樣,win10貌似是前面要多輸入一條命令,如果是要win10的話自己搜下吧,同時還有其他改設置關掉無簽名驅動提示等的設置,加載測試驅動程序,需要的也可以找找,網上很多)。設置后上面的配置之后,重啟電腦:
?
有的時候會有測試模式水印,有的時候沒有,可能提示不是正版的話會沒這個水印,不過這不重要,只要相面的那個cmd命令執行成功,重啟電腦就可以加載無簽名64位驅動了。
接下來測試下我們上面的那一套代碼(所有程序都右鍵管理員啟動):
先打開dbgview全都選上:
然后啟動安裝程序:
OK驅動加載成功,并且通訊測試成功。這里基本模板就算完事了,之后就是一些常用的驅動開發,后續再整理。
總結
以上是生活随笔為你收集整理的Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win64 驱动内核编程-1.环境搭建
- 下一篇: 7.PHP Cookie与Session