在本人前一篇博文《驱动开发:通过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 ULONG  MicrosecondsToSleep);// 初始化管道
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应用层
// LyShark
VOID 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 ULONG  MicrosecondsToSleep);// 初始化管道
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应用层
// LyShark
VOID 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 == 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;
}

至此将驱动签名后运行,并迅速打开应用层程序等待同步发送事件,即可得到如下返回结果。

此处有必要解释一下为什么会写出错误,很简单这段代码并没有控制何时触发事件,导致两边不同步,因为只是一个案例用于演示管道的应用方法,所以大家不要太较真,如果不想出错误这段代码还有很多需要改进的地方。

管道不仅可以传输字符串完全可以传输结构体数据,如下我们定义一个Networkreport结构体,并通过管道的方式多次传输给应用层,这部分传输模式适合用于驱动中一次性突出多个结构体,例如进程列表的输出,ARK工具中的驱动列表输出等功能的实现。

驱动层完整代码

#include <ntifs.h>
#include <ndis.h>
#include <stdio.h>HANDLE g_hClient;
IO_STATUS_BLOCK g_ioStatusBlock;
KEVENT g_event;typedef struct
{int type;unsigned long address;unsigned long buffer_data_len;char buffer_data[0];
}Networkreport;VOID NdisMSleep(IN ULONG  MicrosecondsToSleep);// 初始化管道
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应用层
// PowerBy: LyShark.com
VOID ReportToR3(Networkreport* 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++){// 分配空间Networkreport *report = (Networkreport*)ExAllocatePoolWithTag(NonPagedPool, 4096, 'lysh');if (report){RtlZeroMemory(report, 4096);report->type = x;report->address = 401000 + x;report->buffer_data_len = 13;// 定位到结构体最后一个元素上unsigned char * tmp = (unsigned char *)report + sizeof(Networkreport);memcpy(tmp, "hello lyshark", 13);// 发送到应用层ReportToR3(report, 4096);ExFreePool(report);}}DbgPrint("驱动加载成功 \n");Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

应用层完整代码

#include <iostream>
#include <windows.h>typedef struct
{int type;unsigned long address;unsigned long buffer_data_len;char *buffer_data;
}Networkreport;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 == hPipe){return false;}const int size = 1024 * 10;char buf[size];DWORD rlen = 0;while (true){//if (ConnectNamedPipe(hPipe, NULL) != NULL)if (1 == 1){if (ReadFile(hPipe, buf, size, &rlen, NULL) == FALSE){continue;}else{//接收信息Networkreport* buffer_tmp = (Networkreport*)&buf;SIZE_T buffer_len = sizeof(Networkreport) + buffer_tmp->buffer_data_len;// 拷贝前半部分,不包括 buffer_dataNetworkreport* buffer = (Networkreport*)malloc(buffer_len);memcpy(buffer, buffer_tmp, buffer_len);// 对后半部 分配空间// By: LySharkchar* data = (char*)malloc(buffer->buffer_data_len);unsigned char* tmp = (unsigned char *)buffer + sizeof(Networkreport) - 4;memcpy(data, tmp, buffer->buffer_data_len);printf("输出数据: %s \n", data);printf("地址: %d \n", buffer_tmp->address);printf("长度: %d \n", buffer_tmp->type);printf("输出长度: %d \n", buffer_tmp->buffer_data_len);free(data);free(buffer);}}}system("pause");return 0;
}

结构体一次性输出多个,效果如下所示:

驱动通信:通过PIPE管道与内核层通信相关推荐

  1. Linux进程通信(一)——pipe管道

    本章内容 采用pipe管道如何进行进程之间的通信 pipe管道进程通信的规则和限制 Linux中pipe管道的实现机制和管理pipe管道的结构体 什么是进程通信 进程通信就是两个进程之间进行数据交换, ...

  2. 内核层与应用层通信详解

    做驱动开发的肯定会遇到应用层与内核层的通信的问题,首先说内核层与应用层的通信可以大概分为两个方面,第一是应用层向内核层主动传递消息,第二是内核层主动与应用层通信.下面我们将分开来谈两个方面. 我们先来 ...

  3. linux 进程和线程或线程和线程之间通过管道通信(pipe)

    linux 进程和线程或线程和线程之间通过管道通信(pipe) 转自:http://blog.csdn.net/robertkun/article/details/8095331 线程间通信: [cp ...

  4. 驱动开发:内核层InlineHook挂钩函数

    在上一章<驱动开发:内核LDE64引擎计算汇编长度>中,LyShark教大家如何通过LDE64引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的InlineHook函 ...

  5. [转载] 关于Win7 x64下过TP保护的一些思路,内核层过保护,驱动过保护

    首先特别感谢梦老大,本人一直没搞懂异常处理机制,看了他的教程之后终于明白了. 在他的教程里我学到了不少东西. 第一次在论坛发帖,就说说Win7 x64位下怎么过TP保护.如果有讲错的地方,还望指出. ...

  6. 安卓RK3399编译驱动MPU6050,实现内核层与HAL层驱动

    新手编译安卓驱动学习 今天我们一起学习一下如何实现对一款有驱动代码的传感器适配安卓系统 开发板:某AR眼镜公司的开发板RK3399 文章目录 新手编译安卓驱动学习 安卓驱动开发常用知识(非新手可以跳过 ...

  7. 【嵌入式总复习】Linux管道详解——管道通信、无名管道、有名管道、具体应用示例

    目录 管道 1. 管道通信 1.1 通信模式 1.2 管道通信中特殊的名词 2. 无名管道(PIPE) 2.1 无名管道的通信原理 2.2 无名管道特点 2.3 如何操作无名管道 示例1 示例2 3. ...

  8. linux进程通信:pipe实现进程同步

    文章目录 通过管道同步进程 实现代码 管道缓冲区 设置缓冲区大小 总结 :pipe的特点 通过管道同步进程 管道自带同步互斥机制: 管道的内核实现:fs/pipe.c ,主要通过内核的锁以及等待队列等 ...

  9. linux内核_Linux驱动编程的本质就是Linux内核编程

    由于Linux驱动编程的本质属于Linux内核编程,因此我们非常有必要熟悉Linux内核以及Linux内核的特点. 这篇文章将会帮助读者打下Linux驱动编程的基础知识. 本篇文章分为如下三个小节进行 ...

最新文章

  1. “编码 5 分钟,命名 2 小时”,这道题究竟怎么解? | 问题征集
  2. linq调用mysql函数_mysql-在LINQ中执行FirstOrDefault的另一种方法
  3. 036_CSS导航条
  4. 环境测试明日最后一天 16万次公交车确保市民出行
  5. 数据结构--链表--约瑟夫环问题(单向循环链表)
  6. 集合的划分(信息学奥赛一本通-T1315)
  7. vuecli3 配置多个代理
  8. idea java mapper.xml文件sql黄色背景解决
  9. 将图片转化为txt文本显示
  10. Nginx命令行控制
  11. 用typedef给结构体一个别名
  12. linux内核移植imx8,iMX8模块Ubuntu移植
  13. word多级标题下一级和上一级没有关联上
  14. 不知道如何做好精益生产管理?可能是你的企业还没有进行工时分析
  15. JMH基准测试,看我怎么用它来测试mongodb的数据加载性能
  16. java robust_Java的鲁棒性(Robust)
  17. php过滤微信表情符号
  18. 2017中兴算法挑战赛(迪杰斯特拉)
  19. cmd bat 执行多条
  20. 【数据科学家】什么是数据科学?

热门文章

  1. 云网融合 — 数据中心
  2. escharts 柱状图 百分比,关于echarts的柱状图如何以百分比显示的问题
  3. Unity MOBA类型游戏的战争迷雾效果
  4. 重磅消息:Elastic 公司即将修改 ElasticSearch 的开源许可证,限制云服务商的使用...
  5. Find a way(bfs专题)
  6. 全基因组重测序基础及高级分析知识汇总
  7. 基于STM32的智能小车(一)
  8. 傻傻分不清楚:裸纤、专线、SDH、MSTP、MSTP+、OTN、PTN、IP-RAN
  9. react二维码qrcode.react以及点击二维码的下载二维码图片
  10. 企业上云服务商之华山论剑