驅動通信:通過PIPE管道與內核層通信

在本人前一篇博文《驅動開發:通過ReadFile與內核層通信》詳細介紹了如何使用應用層ReadFile系列函數實現內核通信 , 本篇將繼續延申這個知識點,介紹利用PIPE命名管道實現應用層與內核層之間的多次通信方法 。

  • 什么是PIPE管道?
在Windows編程中 , 數據重定向需要用到管道PIPE,管道是一種用于在進程間共享數據的機制,通常由兩端組成,數據從一端流入則必須從令一端流出,也就是一讀一寫 , 利用這種機制即可實現進程間直接通信 。管道的本質其實是一段共享內存區域 , 多數情況下管道是用于應用層之間的數據交換的,其實驅動中依然可以使用命名管道實現應用層與內核層的直接通信 。
那么如何在內核中創建一個管道?請看以下代碼片段,以及MSDN針對函數的解析 。
  • InitializeObjectAttributes
    • 初始化一個OBJECT_ATTRIBUTES結構,它設置將被打開的對象句柄的屬性 。然后調用方可以將一個指向該結構的指針傳遞給實際打開句柄的例程 。
  • ZwCreateFile
    • 該函數的作用時創建或打開一個已經存在的文件,在這里其實是打開objAttr這個文件 。
  • KeInitializeEvent
    • 將事件對象初始化為同步 (單個服務) 或通知類型事件,并將其設置為已發出信號或未發出信號的狀態 。
HANDLE g_hClient;IO_STATUS_BLOCK g_ioStatusBlock;KEVENT g_event;VOID NdisMSleep(IN ULONGMicrosecondsToSleep);// 初始化管道void init(){ UNICODE_STRING uniName; OBJECT_ATTRIBUTES objAttr; RtlInitUnicodeString(&uniName, L"\\DosDevices\\Pipe\\LySharkPipeConn"); InitializeObjectAttributes(&objAttr, &uniName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); ZwCreateFile(&g_hClient, GENERIC_READ | GENERIC_WRITE, &objAttr, &g_ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!g_hClient) {return; } KeInitializeEvent(&g_event, SynchronizationEvent, TRUE);}原理就是打開\\DosDevices\\Pipe\\LySharkPipeConn文件 , 然后將事件對象初始化為同步狀態 。
接下來就是如何將數據發送給應用層的問題,發送問題可以調用ZwWriteFile這個內核函數,如下我們實現的效果是將一個char類型的字符串傳輸給應用層 。
// 將數據傳到R3應用層// LySharkVOID ReportToR3(char* m_parameter, int lent){ if (!NT_SUCCESS(ZwWriteFile(g_hClient, NULL, NULL, NULL, &g_ioStatusBlock, (void*)m_parameter, lent, NULL, NULL))) {DbgPrint("寫出錯誤"); }}內核層的核心代碼就是如上這些,將這些整合在一起完整代碼如下所示:
#include <ntifs.h>#include <ndis.h>#include <stdio.h>HANDLE g_hClient;IO_STATUS_BLOCK g_ioStatusBlock;KEVENT g_event;VOID NdisMSleep(IN ULONGMicrosecondsToSleep);// 初始化管道void init(){ UNICODE_STRING uniName; OBJECT_ATTRIBUTES objAttr; RtlInitUnicodeString(&uniName, L"\\DosDevices\\Pipe\\LySharkPipeConn"); InitializeObjectAttributes(&objAttr, &uniName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); ZwCreateFile(&g_hClient, GENERIC_READ | GENERIC_WRITE, &objAttr, &g_ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); if (!g_hClient) {return; } KeInitializeEvent(&g_event, SynchronizationEvent, TRUE);}// 將數據傳到R3應用層// LySharkVOID ReportToR3(char* m_parameter, int lent){ if (!NT_SUCCESS(ZwWriteFile(g_hClient, NULL, NULL, NULL, &g_ioStatusBlock, (void*)m_parameter, lent, NULL, NULL))) {DbgPrint("寫出錯誤"); }}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint("驅動卸載成功 \n");}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ init(); // 延時3秒 NdisMSleep(3000000); DbgPrint("hello lyshark \n"); for (int x = 0; x < 10; x++) {// 分配空間char *report = (char*)ExAllocatePoolWithTag(NonPagedPool, 4096, 'lysh');if (report){RtlZeroMemory(report, 4096);RtlCopyMemory(report, "hello lyshark", 13);// 發送到應用層ReportToR3(report, 4096);ExFreePool(report);} } DbgPrint("驅動加載成功 \n"); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}內核中創建了命名管道,客戶端就需要創建一個相同名稱的管道,并通過ReadFile函數讀取管道中的數據 , 應用層核心代碼如下所示:
#include <iostream>#include <windows.h>int main(int argc, char *argv[]){ HANDLE hPipe = CreateNamedPipe(TEXT("\\\\.\\Pipe\\LySharkPipeConn"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_WAIT_FOREVER, NULL); if (INVALID_HANDLE_VALUE =https://www.huyubaike.com/biancheng/= hPipe) {return false; } const int size = 1024 * 10; char buf[size]; DWORD rlen = 0; while (true) {//if (ConnectNamedPipe(hPipe, NULL) != NULL)// PowerBy: LyShark.comif (1){if (ReadFile(hPipe, buf, size, &rlen, NULL) == FALSE){continue;}else{//接收信息char* buffer_tmp = (char*)&buf;// 拷貝前半部分,不包括 buffer_datachar* buffer = (char*)malloc(size);memcpy(buffer, buffer_tmp, size);printf("內核層數據: %s \n", buffer);free(buffer_tmp);free(buffer);}} } system("pause"); return 0;}

推薦閱讀