您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何通過Async反向與內核通信”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何通過Async反向與內核通信”吧!
在開始學習Async反向通信之前先來研究一個Sync正向通信案例,不論是正向反向通信其在通信模式上與通過ReadFile與內核層通信
的通信模式基本一致,都是通過ReadFile
觸發驅動中的IRP_MJ_READ
讀取派遣,唯一的區別是在傳輸數據時使用了MmGetSystemAddressForMdl
方式,它將給定MDL
描述的物理頁面映射到系統空間,并調用RtlCopyMemory()
將全局字符串復制到這個空間內,這樣客戶端就可以循環讀取內核傳出的數據。
我們來看驅動端代碼是如何實現的這個功能,代碼并沒有什么特殊的無法理解的點,只是需要注意我們在驅動入口調用IoCreateDevice()
時傳入了第二個參數FILE_DEVICE_EXTENSION
,該參數的作用是,創建設備時,指定設備擴展內存的大小,傳一個值進去,就會給設備分配一塊非頁面內存。
#include <ntddk.h> #include <stdio.h> // 保存一段非分頁內存,用于給全局變量使用 #define FILE_DEVICE_EXTENSION 4096 // 定義全局字符串 static int global_count = 0; static char global_char[5][128] = { 0 }; // 驅動綁定默認派遣函數 NTSTATUS _DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp) { _pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; _pIrp->IoStatus.Information = 0; IoCompleteRequest(_pIrp, IO_NO_INCREMENT); return _pIrp->IoStatus.Status; } // 驅動創建后觸發 NTSTATUS _SyncCreateCloseDispatch(PDEVICE_OBJECT _pDevcieObject, PIRP _pIrp) { _pIrp->IoStatus.Status = STATUS_SUCCESS; _pIrp->IoStatus.Information = 0; IoCompleteRequest(_pIrp, IO_NO_INCREMENT); return _pIrp->IoStatus.Status; } // 應用層讀數據后觸發 NTSTATUS _SyncReadDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp) { NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(_pIrp); PVOID pBuffer = NULL; ULONG uBufferLen = 0; do { // 讀寫請求使用的是直接I/O方式 pBuffer = MmGetSystemAddressForMdl(_pIrp->MdlAddress); if (pBuffer == NULL) { status = STATUS_UNSUCCESSFUL; break; } uBufferLen = pIrpStack->Parameters.Read.Length; DbgPrint("讀字節長度: %d \n", uBufferLen); // 最大支持20字節讀請求 uBufferLen = uBufferLen >= 20 ? 20 : uBufferLen; // 輸出五次字符串 if (global_count < 5) { RtlCopyMemory(pBuffer, global_char[global_count], uBufferLen); global_count = global_count + 1; } } while (FALSE); // 填寫返回狀態及返回大小 _pIrp->IoStatus.Status = status; _pIrp->IoStatus.Information = uBufferLen; // 完成IRP IoCompleteRequest(_pIrp, IO_NO_INCREMENT); return status; } // 卸載驅動 VOID _UnloadDispatch(PDRIVER_OBJECT _pDriverObject) { // 刪除創建的設備 UNICODE_STRING Win32DeviceName; RtlInitUnicodeString(&Win32DeviceName, L"\\DosDevices\\LySharkSync"); IoDeleteDevice(_pDriverObject->DeviceObject); } // 驅動入口 NTSTATUS DriverEntry(PDRIVER_OBJECT _pDriverObject, PUNICODE_STRING _pRegistryPath) { UNICODE_STRING DeviceName, Win32DeivceName; PDEVICE_OBJECT pDeviceObject = NULL; NTSTATUS status; HANDLE hThread; OBJECT_ATTRIBUTES ObjectAttributes; // 設置符號名 RtlInitUnicodeString(&DeviceName, L"\\Device\\LySharkSync"); RtlInitUnicodeString(&Win32DeivceName, L"\\DosDevices\\LySharkSync"); // 循環初始化IRP函數 for (ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { _pDriverObject->MajorFunction[i] = _DefaultDispatch; } // 再次覆蓋派遣函數 _pDriverObject->MajorFunction[IRP_MJ_CREATE] = _SyncCreateCloseDispatch; _pDriverObject->MajorFunction[IRP_MJ_CLOSE] = _SyncCreateCloseDispatch; _pDriverObject->MajorFunction[IRP_MJ_READ] = _SyncReadDispatch; _pDriverObject->DriverUnload = _UnloadDispatch; // 分配一個自定義擴展 大小為sizeof(DEVEXT) // By: LyShark.com status = IoCreateDevice(_pDriverObject, sizeof(FILE_DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); if (!NT_SUCCESS(status)) return status; if (!pDeviceObject) return STATUS_UNEXPECTED_IO_ERROR; // 為全局變量賦值 strcpy(global_char[0], "hi,lyshark A"); strcpy(global_char[1], "hi,lyshark B"); strcpy(global_char[2], "hi,lyshark C"); strcpy(global_char[3], "hi,lyshark D"); strcpy(global_char[4], "hi,lyshark E"); // 指定讀寫方式為 直接I/O MDL模式 pDeviceObject->Flags |= DO_DIRECT_IO; // 數據傳輸時地址校驗大小 pDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; status = IoCreateSymbolicLink(&Win32DeivceName, &DeviceName); pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; }
對于應用層來說并沒有什么特別的,同樣調用ReadFile
讀取內核中的參數,同樣for循環讀取五次,代碼如下:
#include <stdio.h> #include <Windows.h> int main(int argc, char *argv[]) { HANDLE hFile; char Buffer[10] = { 0 }; DWORD dwRet = 0; BOOL bRet; hFile = CreateFileA("\\\\.\\LySharkSync", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; for (int x = 0; x < 5; x++) { bRet = ReadFile(hFile, Buffer, 20, &dwRet, NULL); if (!bRet) { CloseHandle(hFile); return 0; } printf("讀入數據: %s -> 讀取長度: %d \n", Buffer, dwRet); } return 0; }
這段代碼運行效果如下:
與同步模式不同,異步模式
雖然同樣使用ReadFile
實現通信,但在通信中引入了Event
事件通知機制,這也是異步與同步最大的區別所在,用戶層可以分別創建多個Event事件,等待內核依次做出相應并最終一并返回。
首先驅動內定義了_DeviceExtension
自定義接口,該接口用于保存此次事件所對應的Irp
以及其所對應的DPC時間等。
異步分發函數_AsyncReadDispatch
同樣是被IRP_MJ_READ
派遣函數觸發的,觸發后其內部會首先IoGetCurrentIrpStackLocation
得到當前IRP的堆棧信息,然后設置IoMarkIrpPending()
并最終將該IRP通過InsertTailList()
插入到IRP鏈表內等待被處理。
IoMarkIrpPending
用于標記指定的IRP,標志著某個驅動的分發例程(分發函數)因需要被其他的驅動程序進一步處理最終返回STATUS_PENDING狀態。
函數_CustomDpc
則是定時器內部要執行的具體操作,在DriverEntry
驅動入口處做了如下初始化,初始化了鏈表,并初始化了一個定時器,最后啟動這個定時器每隔1秒都會執行一次_CustomDpc
如果我們的IRP鏈表內IsListEmpty() 檢測
存在數據,則會主動拷貝內存RtlCopyMemory
并推送到應用層。
// 初始化IRP鏈表 InitializeListHead(&pDevExt->IrpList); // 初始化定時器 KeInitializeTimer(&(pDevExt->timer)); // 初始化DPC pDevExt是傳給_CustomDpc函數的參數 KeInitializeDpc(&pDevExt->dpc, (PKDEFERRED_ROUTINE)_CustomDpc, pDevExt); // 設置定時時間位1s pDevExt->liDueTime = RtlConvertLongToLargeInteger(-10000000); // 啟動定時器 KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);
驅動層完成代碼如下所示:
#include <ntddk.h> // 自定義接口擴展 typedef struct _DeviceExtension { LIST_ENTRY IrpList; KTIMER timer; LARGE_INTEGER liDueTime; KDPC dpc; }DEV_EXT, *PDEV_EXT; // 默認派遣函數 NTSTATUS _DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp) { _pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; _pIrp->IoStatus.Information = 0; IoCompleteRequest(_pIrp, IO_NO_INCREMENT); return _pIrp->IoStatus.Status; } // 創建派遣函數 NTSTATUS _AsyncCreateCloseDispatch(PDEVICE_OBJECT _pDevcieObject, PIRP _pIrp) { _pIrp->IoStatus.Status = STATUS_SUCCESS; _pIrp->IoStatus.Information = 0; IoCompleteRequest(_pIrp, IO_NO_INCREMENT); return _pIrp->IoStatus.Status; } // 讀取派遣函數 NTSTATUS _AsyncReadDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp) { NTSTATUS status; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(_pIrp); PDEV_EXT pDevExt = (PDEV_EXT)_pDeviceObject->DeviceExtension; IoMarkIrpPending(_pIrp); // 將IRP插入自定義鏈表中插入的是ListEntry InsertTailList(&pDevExt->IrpList, &_pIrp->Tail.Overlay.ListEntry); // 返回pending 主要返回給I/O管理器的值必須和IRP的Pending標志位一致 // By: LyShark.com // 即調用iomarkirppending和返回值要一致 return STATUS_PENDING; } // DPC線程 VOID _CustomDpc(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) { PIRP pIrp; PDEV_EXT pDevExt = (PDEV_EXT)DeferredContext; PVOID pBuffer = NULL; ULONG uBufferLen = 0; PIO_STACK_LOCATION pIrpStack = NULL; do { if (!pDevExt) { break; } // 檢查尾端IRP鏈表是否為空 為空則跳出 if (IsListEmpty(&pDevExt->IrpList)) { break; } // 從IRP鏈表中取出一個IRP并完成該IRP 取出的是ListEntry的地址 PLIST_ENTRY pListEntry = (PLIST_ENTRY)RemoveHeadList(&pDevExt->IrpList); if (!pListEntry) break; pIrp = (PIRP)CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry); pIrpStack = IoGetCurrentIrpStackLocation(pIrp); DbgPrint("當前DPC Irp: 0x%x\n", pIrp); // 驅動程序的讀寫方式位直接I/O pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress); if (pBuffer == NULL) { pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); break; } uBufferLen = pIrpStack->Parameters.Read.Length; DbgPrint("讀取DPC長度: %d\n", uBufferLen); // 支持5字節以下的讀請求 uBufferLen = uBufferLen > 13 ? 13 : uBufferLen; // 復制請求內容 RtlCopyMemory(pBuffer, "hello lyshark", uBufferLen); pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = uBufferLen; // 完成該IRP IoCompleteRequest(pIrp, IO_NO_INCREMENT); } while (FALSE); // 重新設置定時器 KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc); } // 卸載驅動 VOID _UnloadDispatch(PDRIVER_OBJECT _pDriverObject) { UNICODE_STRING Win32DeviceName; PDEV_EXT pDevExt = (PDEV_EXT)_pDriverObject->DeviceObject->DeviceExtension; RtlInitUnicodeString(&Win32DeviceName, L"\\DosDevices\\LySharkAsync"); // 刪除定時器 // LyShark KeCancelTimer(&pDevExt->timer); // 刪除創建的設備 IoDeleteDevice(_pDriverObject->DeviceObject); } // 驅動入口 NTSTATUS DriverEntry(PDRIVER_OBJECT _pDriverObject, PUNICODE_STRING _pRegistryPath) { UNICODE_STRING DeviceName, Win32DeivceName; PDEVICE_OBJECT pDeviceObject = NULL; NTSTATUS status; PDEV_EXT pDevExt = NULL; HANDLE hThread; OBJECT_ATTRIBUTES ObjectAttributes; CLIENT_ID CID; RtlInitUnicodeString(&DeviceName, L"\\Device\\LySharkAsync"); RtlInitUnicodeString(&Win32DeivceName, L"\\DosDevices\\LySharkAsync"); for (ULONG i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { _pDriverObject->MajorFunction[i] = _DefaultDispatch; } _pDriverObject->MajorFunction[IRP_MJ_CREATE] = _AsyncCreateCloseDispatch; _pDriverObject->MajorFunction[IRP_MJ_CLOSE] = _AsyncCreateCloseDispatch; _pDriverObject->MajorFunction[IRP_MJ_READ] = _AsyncReadDispatch; _pDriverObject->DriverUnload = _UnloadDispatch; // 分配自定義擴展 status = IoCreateDevice(_pDriverObject, sizeof(DEV_EXT), &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); if (!NT_SUCCESS(status)) return status; if (!pDeviceObject) return STATUS_UNEXPECTED_IO_ERROR; pDeviceObject->Flags |= DO_DIRECT_IO; pDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; status = IoCreateSymbolicLink(&Win32DeivceName, &DeviceName); pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; pDevExt = (PDEV_EXT)pDeviceObject->DeviceExtension; // 初始化IRP鏈表 InitializeListHead(&pDevExt->IrpList); // 初始化定時器 KeInitializeTimer(&(pDevExt->timer)); // 初始化DPC pDevExt是傳給_CustomDpc函數的參數 KeInitializeDpc(&pDevExt->dpc, (PKDEFERRED_ROUTINE)_CustomDpc, pDevExt); // 設置定時時間位1s pDevExt->liDueTime = RtlConvertLongToLargeInteger(-10000000); // 啟動定時器 KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc); return STATUS_SUCCESS; }
驅動層說完了,接下來是應用層,對于應用層來說,需要使用CreateEvent
打開通知事件,或者叫做事件對象,然后當有通知時,則直接使用ReadFile
讀取對應的緩沖區,當所有讀取全部結束WaitForMultipleObjects
等待結束即輸出結果。
#include <stdio.h> #include <Windows.h> int main(int argc, char *argv[]) { HANDLE hFile; char Buffer[3][32] = { 0 }; DWORD dwRet[3] = { 0 }; OVERLAPPED ol[3] = { 0 }; HANDLE hEvent[3] = { 0 }; // By:LyShark hFile = CreateFileA("\\\\.\\LySharkAsync", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); if (INVALID_HANDLE_VALUE == hFile) return 0; // event用來通知請求完成 hEvent[0] = CreateEvent(NULL, TRUE, FALSE, NULL); ol[0].hEvent = hEvent[0]; hEvent[1] = CreateEvent(NULL, TRUE, FALSE, NULL); ol[1].hEvent = hEvent[1]; hEvent[2] = CreateEvent(NULL, TRUE, FALSE, NULL); ol[2].hEvent = hEvent[2]; // 讀取事件內容到緩存 ReadFile(hFile, Buffer[0], 13, &dwRet[0], &ol[0]); ReadFile(hFile, Buffer[1], 13, &dwRet[1], &ol[1]); ReadFile(hFile, Buffer[2], 13, &dwRet[2], &ol[2]); // 等待三個事件執行完畢 WaitForMultipleObjects(3, hEvent, TRUE, INFINITE); // 輸出結果 printf("緩存LyShark A: %s \n", Buffer[0]); printf("緩存LyShark B: %s \n", Buffer[1]); printf("緩存LyShark C: %s \n", Buffer[2]); CloseHandle(hFile); return 0; }
這段代碼最終運行效果如下:
感謝各位的閱讀,以上就是“如何通過Async反向與內核通信”的內容了,經過本文的學習后,相信大家對如何通過Async反向與內核通信這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。