在本人前一篇博文《驱动开发:通过ReadFile与内核层通信》
详细介绍了如何使用应用层ReadFile
系列函数实现内核通信 , 本篇将继续延申这个知识点,介绍利用PIPE
命名管道实现应用层与内核层之间的多次通信方法 。
- 什么是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;}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 51单片机下实现软件模拟IIC通信
- mac通过docker一键部署Jenkins
- 如何通过执行SQL为低代码项目提速?
- 驱动开发:通过ReadFile与内核层通信
- 通过Thread Pool Executor类解析线程池执行任务的核心流程
- 【Python+C#】手把手搭建基于Hugging Face模型的离线翻译系统,并通过C#代码进行访问
- 如何通过 Java 代码隐藏 Word 文档中的指定段落
- day44-反射03
- 信念的力量演讲稿600字 信念的力量演讲稿
- 怎么控制摄影光比