日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Windows驱动—Windows应用程序和Windows驱动通信编程

發布時間:2025/3/15 windows 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Windows驱动—Windows应用程序和Windows驱动通信编程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 介紹
  • 知識前奏
    • 內核方面編程
      • 設備對象和符號鏈接
      • 分發函數
    • 應用方面編程
      • 打開設備
      • 設備控制請求
  • 代碼
    • 應用層代碼
    • 內核層代碼
    • 完整工程代碼
  • 測試效果

介紹

Windows應用程序(Ring3層)和內核驅動(Ring0層)是運行在Windows權限的不同級別,簡單來說各有優勢。內核層權限較大 能做很多 應用程序辦不到的事情 不直接面向程序使用的用戶,Windows應用程序在Ring3層 直接面向用戶,界面友好。當應用層辦不到的時候就需要借助內核層了,所以 win32應用程序和Windows內核驅動通信是有必要的。Windows應用程序和Windows內核驅動程序直接是可以進行雙向通信的,相互都是可以發送信息的

本篇博客,將使用一個最簡單的例子來講解 Win32程序和內核驅動程序通信編程。這里把內核驅動當做一個Server,應用程序當做一個客戶端。客戶端向內核驅動發生一個 小寫字符串,內核驅動將小寫字符串轉成大寫 然后回射回來。

知識前奏

內核方面編程

設備對象和符號鏈接

如果驅動需要和應用程序通信,首先必須要生成一個設備對象(Device Object)。設備對象和分發函數構成了整個內核體系的基本框架設備對象用來暴露給應用層,應用層可以像操作文件一樣操作它。用于和應用程序通信的設備往往用來"控制"這個內核驅動,所以往往稱之為**“控制設備對象”**(Control Device Object,CDO)。生成設備對象使用IoCreateDevice函數,原型如下:

/*return STATUS_SUCCESS成功 */ NTSTATUS IoCreateDevice(// 可直接從DriverEntry參數中獲得IN PDRIVER_OBJECT DriverObject,// 表示設備擴展大小(應用設備擴展)會專門講述IN ULONG DeviceExtensionSize,// 設備名IN PUNICODE_STRING DeviceName OPTIONAL,// 設備類型,Windows已經規定了一系列設備類型IN DEVICE_TYPE DeviceType,// 表示一組設備屬性IN ULONG DeviceCharacteristics,// 表示是否一個獨占設備,設置獨占,這個設備將在同一個時刻只能被打開一個句柄。一般都不會設置為獨占設備IN BOOLEAN Exclusive,// 返回結果OUT PDEVICE_OBJECT *DeviceObject);

控制設備需要有一個名字,這樣才會被暴露出來,供其他程序打開與之通信。設備的名字可以在IoCreateDevice或IoCreateDeviceSecure時指定。但是,應用層是無法直接通過設備的名字來打開對象的,必須建立一個暴露給應用層的符號鏈接。符號鏈接是記錄一個字符串對應到另一個字符串的簡單結構。函數原型IoCreateSymbolicLink如下:

NTSTATUS IoCreateSymbolicLink(// 符號鏈接名,如果該符號鏈接名存在 則創建不成功IN PUNICODE_STRING SymbolicLinkName,// 設備名IN PUNICODE_STRING DeviceName);

控制設備和符號鏈接的刪除很簡單,一一對應,IoDeleteDevice、IoDeleteSymbolicLink

分發函數

分發函數是一組用來處理發送給設備對象(當然包括控制設備)的請求的函數。這些函數當然由內核驅動的開發者編寫,以便處理這些請求并返回給Windows。分發函數是設置在驅動對象上的。Windows的IO管理器在收到請求時,會根據請求發送的目標,也就是一個設備對象,來調用這個設備對象所從屬的驅動對象上的分發函數。最簡單的3種請求:

  • 打開(Create):在試圖訪問一個設備對象之前,必須先用打開請求打開它。只有得到成功的返回,才可以發送其他的請求。
  • 關閉(Close):在結束訪問一個設備對象之后,發送關閉請求將它關閉。關閉之后,就必須再次打開才能訪問。
  • 設備控制(Device Control):設備控制請求是一種即可以用來輸入(應用到內核),又可以用來輸出(從內核到應用)的請求。

分發函數原型:

NTSTATUS MyDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp) {// 在分發函數內部進行 請求的處理。一般有如下幾個步驟// 第1步,判斷請求是否發送給該驅動的設備對象// 第2步,獲取請求的當前棧空間(空間中含有請求相關的信息)// 第3步,獲取請求的功能號,不同的功能號做不同的處理(這里就可以對輸入輸出做操作了)。// 第4步,結束請求 }

應用方面編程

打開設備

在應用程序中 打開驅動中創建的設備。和打開文件沒什么區別,使用 CreateFile即可,要注意文件的路徑。

/* 文件的路徑就是符號鏈接的路徑,但是符號鏈接的路徑在應用看來,是以"\\.\"開頭的。注意,這些"\"在C語言中要使用"\\"來轉義 */ #define MY_DEVOBJ_SYB_NAME (L"\\\\.\\lcx10000")HANDLE deviceHandle = CreateFile(MY_DEVOBJ_SYB_NAME,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,0);

設備控制請求

設備控制請求即可以進行輸入也可進行輸出。每個設備控制請求都會有一個功能號,用來區分不同的設備控制請求。這個功能號體現在CTL_CODE中。

CTL_CODE是一個宏,是SDK里頭文件提供的。我們要做的是直接利用這個宏來生成一個自己的設備控制請求功能號。CTL_CODE有4個參數。

  • 參數1,設備類型,這里的控制設備與任何硬件都沒有關系,所以直接定義為未知類型FILE_DEVICE_UNKNOWN。
  • 參數2,生成這個功能號的核心數字,這個數字直接用來和其他參數“合成”功能號?0x0~0x7ff被微軟預留了,同時也不能超過0xfff。如果要定義超過一個的功能號,那么不同的功能號就靠這個數字進行區分。
  • 參數3,METHOD_BUFFERED是說用緩存方式。用緩存方式,輸入輸出緩存會在用戶和內核之間拷貝。這是比較簡單和安全的一種方式
  • 參數4,是這個操作需要的權限。當需要將數據發送到設備時,相當于往設備寫入數據,所以標志為擁有寫數據權限(FILE_WRITE_DATA)。

設備控制請求函數原型:

BOOL DeviceIoControl(// 設備句柄HANDLE hDevice,// Control codeDWORD dwIoControlCode,// 輸入緩沖區LPVOID lpInBuffer,// 輸入緩沖區長度DWORD nInBufferSize,// 輸出緩沖區LPVOID lpOutBuffer,// 輸出緩沖區長度DWORD nOutBufferSize,// 接受到的有效數據長度LPDWORD lpBytesReturned,// 指向OVERLAPPED結構體的指針LPOVERLAPPED lpOverlapped );#define CTL_CODE( DeviceType, Function, Method, Access )

代碼

代碼中有很詳細的注釋。

應用層代碼

這里使用簡單MFC界面操作代碼

// 設備名對應的符號鏈接名,用于暴露給應用層。符號鏈接在應用看來是在\\.\ 的 #define MY_DEVOBJ_SYB_NAME (L"\\\\.\\lcx10000")//CTL_CODE創建控制碼 #define IOCTL_SEND_AND_REC_STR\CTL_CODE(FILE_DEVICE_UNKNOWN\, 0x801, METHOD_BUFFERED,\FILE_READ_DATA | FILE_WRITE_DATA)void CMy02_Win32ToDriverDlg::OnBnClickedSendtodriver() {UpdateData(TRUE);int len = m_uiSendToDriverString.GetLength();char* pInStr = new char[len+1];char *pOutStr = new char[len+1];memset(pInStr,0,len+1);memset(pOutStr,0,len+1);for(int i = 0; i < len ; i++){pInStr[i] = m_uiSendToDriverString.GetAt(i);}//char* pStr = (char*)m_uiSendToDriverString.GetBuffer(0);int ret_len = 0;// 1.打開驅動設備HANDLE deviceHandle = CreateFile(MY_DEVOBJ_SYB_NAME,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM,0);if(deviceHandle == INVALID_HANDLE_VALUE){DWORD errCode = ::GetLastError();m_uiDriverResponse = _T("CreateFile 失敗!驅動未加載");m_uiDriverResponse.AppendFormat(_T(" errCode=%d"),errCode);}else{// 2.向驅動設備發送設備控制請求if( DeviceIoControl(deviceHandle,IOCTL_SEND_AND_REC_STR,pInStr,len,pOutStr,len+1,(LPDWORD)&ret_len,NULL)){m_uiDriverResponse = _T("suc.");m_uiDriverResponse.AppendFormat(_T("retLen=%d,response=%s"),ret_len,CString(pOutStr));}}UpdateData(FALSE); }

內核層代碼

#include <Wdm.h> #include <wdmsec.h>// 全局設備對象 PDEVICE_OBJECT g_devObj = NULL;// 設備名對應的符號鏈接名,用于暴露給應用層。符號鏈接一般都是在\??\路徑下 #define MY_DEVOBJ_SYB_NAME (L"\\??\\lcx10000")// 設備一般都是位于 \Device\這個路徑下的 #define MY_DEVOBJ_NAME (L"\\Device\\lcx10000")// 可用VS自帶的GUID生成器生成 {D1AC1F58-AAC4-45DD-AEC4-A9670DC47B29} static const GUID g_devGUID = { 0xd1ac1f58, 0xaac4, 0x45dd, { 0xae, 0xc4, 0xa9, 0x67, 0xd, 0xc4, 0x7b, 0x29 } };//CTL_CODE創建控制碼 #define IOCTL_SEND_AND_REC_STR\CTL_CODE(FILE_DEVICE_UNKNOWN\, 0x801, METHOD_BUFFERED,\FILE_READ_DATA | FILE_WRITE_DATA)NTSTATUS MyDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp) {NTSTATUS status = STATUS_SUCCESS;ULONG ret_len = 0,i = 0,temp = 0;// 第1步,判斷請求是否發送給該驅動的設備對象,如果不是 簡單返回成功if(DeviceObject == g_devObj){// 第2步,獲取請求的當前棧空間(空間中含有請求相關的信息)PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);// 第3步,獲取請求的功能號,不同的功能號做不同的處理// 打開請求的主功能號是 IRP_MJ_CREATE// 關閉請求的主功能號是 IRP_MJ_CLOSE// 設備控制請求的主功能號是IRP_MJ_DEVICE_CONTROL// 處理打開和關閉IRP,可以簡單返回成功即可if( irpStack->MajorFunction == IRP_MJ_CREATE ||irpStack->MajorFunction == IRP_MJ_CLOSE ){status = STATUS_SUCCESS;}// 處理設備控制請求DeviceIoControlelse if( irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL){// 當前是一個緩存方式的設備控制請求,直接從IRP請求參數中獲取緩沖區buffer// 同時 這里輸入緩沖區、輸出緩沖區是共享的PVOID buffer = Irp->AssociatedIrp.SystemBuffer;ULONG inLen = irpStack->Parameters.DeviceIoControl.InputBufferLength;ULONG outLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;//KdBreakPoint();// 斷點設置,使用windbg調試時可以打開//控制碼由這個宏函數CTL_CODE創建if(irpStack->Parameters.DeviceIoControl.IoControlCode== IOCTL_SEND_AND_REC_STR){if(inLen > 0){// 做一個簡單打印DbgPrint("str=%s",(char*)buffer);// 要求輸出緩存要多于輸入緩存if(outLen >= inLen){// 這里轉大寫后返回//_strupr((char*)buffer);ret_len = inLen;for(; i <= inLen ; i++){temp = (ULONG)((char*)buffer)[i];if( temp >= 97 && temp <= 122){((char*)buffer)[i] -= 32;}}}else{status = STATUS_INVALID_PARAMETER;}}else{status = STATUS_INVALID_PARAMETER;}}else{// 其他控制碼請求,一律返回非法參數錯誤。status = STATUS_INVALID_PARAMETER;}}}// KdBreakPoint(); // 斷點設置,使用windbg調試時可以打開// 第4步,結束請求// 這個Informatica用來記錄這次返回到底使用了多少輸出空間Irp->IoStatus.Information = ret_len;// 用于記錄這個請求的完成狀態Irp->IoStatus.Status = status;// 用于結束這個請求IoCompleteRequest(Irp,IO_NO_INCREMENT);return status; }VOID DriverUnload(__in struct _DRIVER_OBJECT *DriverObject) { UNICODE_STRING DeviceLinkName = RTL_CONSTANT_STRING(MY_DEVOBJ_SYB_NAME);DbgPrint("DriverUnload enter \n");// 刪除符號鏈接IoDeleteSymbolicLink(&DeviceLinkName);// 刪除設備對象IoDeleteDevice(g_devObj); }NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING reg_path) {int i;NTSTATUS status;// 設備名UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(MY_DEVOBJ_NAME);UNICODE_STRING SDDLString = RTL_CONSTANT_STRING(L"D:P(A;;GA;;;WD)");UNICODE_STRING DeviceLinkName = RTL_CONSTANT_STRING(MY_DEVOBJ_SYB_NAME);DbgPrint("DriverEntry enter \n");// 如果驅動需要和應用程序通信,首先必須要生成一個設備對象status = IoCreateDevice(driver,0,&DeviceName,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&g_devObj);// 由于IoCreateDevice函數生成的設備具有默認的安全,那么必須具有管理員權限的進程才能打開它// 可用如下函數替換,不過下面的函數在 WinXP中無法使用//status = IoCreateDeviceSecure(driver,0,// &DeviceName,FILE_DEVICE_UNKNOWN,// FILE_DEVICE_SECURE_OPEN,FALSE,// &SDDLString,// 設備對象安全設置// &g_devGUID, // 設備guid// &g_devObj);if(!NT_SUCCESS(status)){return status;}// 創建符號鏈接status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceName);if(!NT_SUCCESS(status)){// 一旦失敗,之前生成的設備對象也要刪掉,防止內存泄漏IoDeleteDevice(g_devObj);return status;}// 設置驅動對象的分發函數,分發函數是一組用來處理發送給設備對象的請求的函數// 這里driver->MajorFunction是一個數組,不同的請求可以設置不同的處理函數// 這里為了方便所有的請求都用一個處理函數,在函數內部去區分請求,再做不同的邏輯處理//int i; // 變量定義要放在最前面,放這里不行for(i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++){//driver->MajorFunction[i] = NULL;driver->MajorFunction[i] = MyDispatch;}// 支持動態卸載。driver->DriverUnload = DriverUnload;return STATUS_SUCCESS; }

完整工程代碼

筆者用vs2010進行的編譯 win32應用程序和驅動程序,在Win XP下測試正常。完整項目工程可以在這里下載。

測試效果

總結

以上是生活随笔為你收集整理的Windows驱动—Windows应用程序和Windows驱动通信编程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。