C語(yǔ)言驅(qū)動(dòng)開(kāi)發(fā)之通過(guò)ReadFile與內(nèi)核層通信
驅(qū)動(dòng)與應(yīng)用程序的通信是非常有必要的,內(nèi)核中執(zhí)行代碼后需要將其動(dòng)態(tài)顯示給應(yīng)用層,但驅(qū)動(dòng)程序與應(yīng)用層畢竟不在一個(gè)地址空間內(nèi),為了實(shí)現(xiàn)內(nèi)核與應(yīng)用層數(shù)據(jù)交互則必須有通信的方法,微軟為我們提供了三種通信方式,如下先來(lái)介紹通過(guò)ReadFile系列函數(shù)實(shí)現(xiàn)的通信模式。
長(zhǎng)話(huà)短說(shuō),不說(shuō)沒(méi)用的概念,首先系統(tǒng)中支持的通信模式可以總結(jié)為三種。
- 緩沖區(qū)方式讀寫(xiě)(DO_BUFFERED_IO)
- 直接方式讀寫(xiě)(DO_DIRECT_IO)
- 其他方式讀寫(xiě)
而通過(guò)ReadFile,WriteFile
系列函數(shù)實(shí)現(xiàn)的通信機(jī)制則屬于緩沖區(qū)通信模式,在該模式下操作系統(tǒng)會(huì)將應(yīng)用層中的數(shù)據(jù)復(fù)制到內(nèi)核中,此時(shí)應(yīng)用層調(diào)用ReadFile,WriteFile
函數(shù)進(jìn)行讀寫(xiě)時(shí),在驅(qū)動(dòng)內(nèi)會(huì)自動(dòng)觸發(fā) IRP_MJ_READ
與 IRP_MJ_WRITE
這兩個(gè)派遣函數(shù),在派遣函數(shù)內(nèi)則可以對(duì)收到的數(shù)據(jù)進(jìn)行各類(lèi)處理。
首先需要實(shí)現(xiàn)初始化各類(lèi)派遣函數(shù)這么一個(gè)案例,如下代碼則是通用的一種初始化派遣函數(shù)的基本框架,分別處理了IRP_MJ_CREATE
創(chuàng)建派遣,以及IRP_MJ_CLOSE
關(guān)閉的派遣,此外函數(shù)DriverDefaultHandle
的作用時(shí)初始化其他派遣用的,也就是將除去CREATE/CLOSE
這兩個(gè)派遣之外,其他的全部賦值成初始值的意思,當(dāng)然不增加此段代碼也是無(wú)妨,并不影響代碼的實(shí)際執(zhí)行。
#include <ntifs.h> // 卸載驅(qū)動(dòng)執(zhí)行 VOID UnDriver(PDRIVER_OBJECT pDriver) { PDEVICE_OBJECT pDev; // 用來(lái)取得要?jiǎng)h除設(shè)備對(duì)象 UNICODE_STRING SymLinkName; // 局部變量symLinkName pDev = pDriver->DeviceObject; IoDeleteDevice(pDev); // 調(diào)用IoDeleteDevice用于刪除設(shè)備 RtlInitUnicodeString(&SymLinkName, L"\\??\\LySharkDriver"); // 初始化字符串將symLinkName定義成需要?jiǎng)h除的符號(hào)鏈接名稱(chēng) IoDeleteSymbolicLink(&SymLinkName); // 調(diào)用IoDeleteSymbolicLink刪除符號(hào)鏈接 DbgPrint("驅(qū)動(dòng)卸載完畢..."); } // 創(chuàng)建設(shè)備連接 // LyShark.com NTSTATUS CreateDriverObject(IN PDRIVER_OBJECT pDriver) { NTSTATUS Status; PDEVICE_OBJECT pDevObj; UNICODE_STRING DriverName; UNICODE_STRING SymLinkName; // 創(chuàng)建設(shè)備名稱(chēng)字符串 RtlInitUnicodeString(&DriverName, L"\\Device\\LySharkDriver"); Status = IoCreateDevice(pDriver, 0, &DriverName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj); // 指定通信方式為緩沖區(qū) pDevObj->Flags |= DO_BUFFERED_IO; // 創(chuàng)建符號(hào)鏈接 RtlInitUnicodeString(&SymLinkName, L"\\??\\LySharkDriver"); Status = IoCreateSymbolicLink(&SymLinkName, &DriverName); return STATUS_SUCCESS; } // 創(chuàng)建回調(diào)函數(shù) NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) { pIrp->IoStatus.Status = STATUS_SUCCESS; // 返回成功 DbgPrint("派遣函數(shù) IRP_MJ_CREATE 執(zhí)行 \n"); IoCompleteRequest(pIrp, IO_NO_INCREMENT); // 指示完成此IRP return STATUS_SUCCESS; // 返回成功 } // 關(guān)閉回調(diào)函數(shù) NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp) { pIrp->IoStatus.Status = STATUS_SUCCESS; // 返回成功 DbgPrint("派遣函數(shù) IRP_MJ_CLOSE 執(zhí)行 \n"); IoCompleteRequest(pIrp, IO_NO_INCREMENT); // 指示完成此IRP return STATUS_SUCCESS; // 返回成功 } // 默認(rèn)派遣函數(shù) NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp) { NTSTATUS status = STATUS_SUCCESS; pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return status; } // 入口函數(shù) // By: LyShark NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegistryPath) { DbgPrint("hello lyshark \n"); // 調(diào)用創(chuàng)建設(shè)備 CreateDriverObject(pDriver); pDriver->DriverUnload = UnDriver; // 卸載函數(shù) pDriver->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; // 創(chuàng)建派遣函數(shù) pDriver->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; // 關(guān)閉派遣函數(shù) // 初始化其他派遣 for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { DbgPrint("初始化派遣: %d \n", i); pDriver->MajorFunction[i] = DriverDefaultHandle; } DbgPrint("驅(qū)動(dòng)加載完成..."); return STATUS_SUCCESS; }
代碼運(yùn)行效果如下:
通用框架有了,接下來(lái)就是讓該驅(qū)動(dòng)支持使用ReadWrite
的方式實(shí)現(xiàn)通信,首先我們需要在DriverEntry
處增加兩個(gè)派遣處理函數(shù)的初始化。
// 入口函數(shù) // By: LyShark NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegistryPath) { DbgPrint("hello lyshark \n"); // 調(diào)用創(chuàng)建設(shè)備 CreateDriverObject(pDriver); // 初始化其他派遣 for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { DbgPrint("初始化派遣: %d \n", i); pDriver->MajorFunction[i] = DriverDefaultHandle; } pDriver->DriverUnload = UnDriver; // 卸載函數(shù) pDriver->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; // 創(chuàng)建派遣函數(shù) pDriver->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; // 關(guān)閉派遣函數(shù) // 增加派遣處理 pDriver->MajorFunction[IRP_MJ_READ] = DispatchRead; // 讀取派遣函數(shù) pDriver->MajorFunction[IRP_MJ_WRITE] = DispatchWrite; // 寫(xiě)入派遣函數(shù) DbgPrint("驅(qū)動(dòng)加載完成..."); return STATUS_SUCCESS; }
接著,我們需要分別實(shí)現(xiàn)這兩個(gè)派遣處理函數(shù),如下DispatchRead
負(fù)責(zé)讀取時(shí)觸發(fā),與之對(duì)應(yīng)DispatchWrite
負(fù)責(zé)寫(xiě)入觸發(fā)。
- 引言:
- 對(duì)于讀取請(qǐng)求I/O管理器分配一個(gè)與用戶(hù)模式的緩沖區(qū)大小相同的系統(tǒng)緩沖區(qū)
SystemBuffer
,當(dāng)完成請(qǐng)求時(shí)I/O管理器將驅(qū)動(dòng)程序已經(jīng)提供的數(shù)據(jù)從系統(tǒng)緩沖區(qū)復(fù)制到用戶(hù)緩沖區(qū)。 - 對(duì)于寫(xiě)入請(qǐng)求,會(huì)分配一個(gè)系統(tǒng)緩沖區(qū)并將
SystemBuffer
設(shè)置為地址,用戶(hù)緩沖區(qū)的內(nèi)容會(huì)被復(fù)制到系統(tǒng)緩沖區(qū),但是不設(shè)置UserBuffer
緩沖。
通過(guò)IoGetCurrentIrpStackLocation(pIrp)
接收讀寫(xiě)請(qǐng)求長(zhǎng)度,偏移等基本參數(shù),AssociatedIrp.SystemBuffer
則是讀寫(xiě)緩沖區(qū),IoStatus.Information
是輸出緩沖字節(jié)數(shù),Parameters.Read.Length
是讀取寫(xiě)入的字節(jié)數(shù)。
// 讀取回調(diào)函數(shù) NTSTATUS DispatchRead(PDEVICE_OBJECT pDevObj, PIRP pIrp) { NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(pIrp); ULONG ulReadLength = Stack->Parameters.Read.Length; char szBuf[128] = "hello lyshark"; pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = ulReadLength; DbgPrint("讀取長(zhǎng)度:%d \n", ulReadLength); // 取出字符串前5個(gè)字節(jié)返回給R3層 memcpy(pIrp->AssociatedIrp.SystemBuffer, szBuf, ulReadLength); IoCompleteRequest(pIrp, IO_NO_INCREMENT); return Status; } // 接收傳入回調(diào)函數(shù) // By: LyShark NTSTATUS DispatchWrite(struct _DEVICE_OBJECT *DeviceObject, struct _IRP *Irp) { NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); ULONG ulWriteLength = Stack->Parameters.Write.Length; PVOID ulWriteData = Irp->AssociatedIrp.SystemBuffer; // 輸出傳入字符串 DbgPrint("傳入長(zhǎng)度: %d 傳入數(shù)據(jù): %s \n", ulWriteLength, ulWriteData); IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; }
如上部分都是在講解驅(qū)動(dòng)層面的讀寫(xiě)派遣,應(yīng)用層還沒(méi)有介紹,在應(yīng)用層我們只需要調(diào)用ReadFile
函數(shù)當(dāng)調(diào)用該函數(shù)時(shí)驅(qū)動(dòng)中會(huì)使用DispatchRead
派遣例程來(lái)處理這個(gè)請(qǐng)求,同理調(diào)用WriteFile
函數(shù)則觸發(fā)的是DispatchWrite
派遣例程。
我們首先從內(nèi)核中讀出前五個(gè)字節(jié)并放入緩沖區(qū)內(nèi),輸出該緩沖區(qū)內(nèi)的數(shù)據(jù),然后在調(diào)用寫(xiě)入,將hello lyshark
寫(xiě)回到內(nèi)核里里面,這段代碼可以這樣來(lái)寫(xiě)。
#include <iostream> #include <Windows.h> #include <winioctl.h> int main(int argc, char *argv[]) { HANDLE hDevice = CreateFileA("\\\\.\\LySharkDriver", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == INVALID_HANDLE_VALUE) { CloseHandle(hDevice); return 0; } // 從內(nèi)核讀取數(shù)據(jù)到本地 char buffer[128] = { 0 }; ULONG length; // 讀入到buffer長(zhǎng)度為5 // By:lyshark.com ReadFile(hDevice, buffer, 5, &length, 0); for (int i = 0; i < (int)length; i++) { printf("讀取字節(jié): %c", buffer[i]); } // 寫(xiě)入數(shù)據(jù)到內(nèi)核 char write_buffer[128] = "hello lyshark"; ULONG write_length; WriteFile(hDevice, write_buffer, strlen(write_buffer), &write_length, 0); system("pause"); CloseHandle(hDevice); return 0; }
使用驅(qū)動(dòng)工具安裝我們的驅(qū)動(dòng),然后運(yùn)行該應(yīng)用層程序,實(shí)現(xiàn)通信,效果如下所示:
到此這篇關(guān)于C語(yǔ)言驅(qū)動(dòng)開(kāi)發(fā)之通過(guò)ReadFile與內(nèi)核層通信的文章就介紹到這了,更多相關(guān)C語(yǔ)言 ReadFile內(nèi)核層通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++多線程實(shí)現(xiàn)TCP服務(wù)器端同時(shí)和多個(gè)客戶(hù)端通信
通訊建立后首先由服務(wù)器端發(fā)送消息,客戶(hù)端接收消息;接著客戶(hù)端發(fā)送消息,服務(wù)器端接收消息,實(shí)現(xiàn)交互發(fā)送消息。本文主要介紹了C++多線程實(shí)現(xiàn)TCP服務(wù)器端同時(shí)和多個(gè)客戶(hù)端通信,感興趣的可以了解一下2021-05-05C++中4種強(qiáng)制類(lèi)型轉(zhuǎn)換的區(qū)別詳析
這篇文章主要給大家介紹了關(guān)于C++中4種強(qiáng)制類(lèi)型轉(zhuǎn)換區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03關(guān)于C語(yǔ)言多線程pthread庫(kù)的相關(guān)函數(shù)說(shuō)明
下面小編就為大家?guī)?lái)一篇關(guān)于C語(yǔ)言多線程pthread庫(kù)的相關(guān)函數(shù)說(shuō)明。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05C++順序容器(vector、deque、list)的使用詳解
本文主要介紹了C++順序容器(vector、deque、list)的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06使用C# 判斷給定大數(shù)是否為質(zhì)數(shù)的詳解
本篇文章是對(duì)使用C#判斷給定大數(shù)是否為質(zhì)數(shù)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05你知道如何自定義sort函數(shù)中的比較函數(shù)
這篇文章主要介紹了如何自定義sort函數(shù)中的比較函數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12c++利用stl set_difference對(duì)車(chē)輛進(jìn)出區(qū)域進(jìn)行判定
這篇文章主要介紹了set_difference,用于求兩個(gè)集合的差集,結(jié)果集合中包含所有屬于第一個(gè)集合但不屬于第二個(gè)集合的元素,需要的朋友可以參考下2017-03-03C++ STL入門(mén)教程(1) vector向量容器使用方法
這篇文章主要為大家詳細(xì)介紹了C++ STL入門(mén)教程第一篇,vector向量容器使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08