驱动通信:通过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;}

推荐阅读