LinuxGod.net
Linux大神网——精选每一篇高品质的技术干货
  1. 首页
  2. 开源快讯
  3. 正文

驱动开发:通过ReadFile与内核层通信详细介绍如何使用应用层

2022年12月6日 189点热度

在本人前一篇博文《驱动开发:通过ReadFile与内核层通信》详细介绍了如何使用应用层ReadFile系列函数实现内核通信,本篇将继续延申这个知识点linux 驱动通知应用层,介绍利用PIPE命名管道实现应用层与内核层之间的多次通信方法。

在Windows编程中,数据重定向需要用到管道PIPElinux 虚拟主机,管道是一种用于在进程间共享数据的机制,通常由两端组成,数据从一端流入则必须从令一端流出linux 虚拟主机,也就是一读一写,利用这种机制即可实现进程间直接通信。管道的本质其实是一段共享内存区域,多数情况下管道是用于应用层之间的数据交换的,其实驱动中依然可以使用命名管道实现应用层与内核层的直接通信。

那么如何在内核中创建一个管道?请看以下代码片段,以及MSDN针对函数的解析。

ZwCreateFile

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 
#include 
#include 
HANDLE g_hClient;
IO_STATUS_BLOCK g_ioStatusBlock;
KEVENT g_event;
VOID NdisMSleep(IN ULONG  MicrosecondsToSleep);
// 初始化管道

linux 驱动通知应用层_全站弹出层通知_串口驱动硬件抽象层

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 
#include 
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.com
		if (1)
		{
			if (ReadFile(hPipe, buf, size, &rlen, NULL) == FALSE)
			{
				continue;
			}
			else
			{
				//接收信息
				char* buffer_tmp = (char*)&buf;
				// 拷贝前半部分,不包括 buffer_data
				char* buffer = (char*)malloc(size);
				memcpy(buffer, buffer_tmp, size);
				printf("内核层数据: %s n", buffer);
				free(buffer_tmp);
				free(buffer);
			}
		}
	}
	system("pause");
	return 0;

}

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

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

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

驱动层完整代码

#include 
#include 
#include 
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 
#include 
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_data Networkreport* buffer = (Networkreport*)malloc(buffer_len); memcpy(buffer, buffer_tmp, buffer_len); // 对后半部 分配空间 // By: LyShark char* 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; }

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

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: readfile 应用层 管道通信 通信
最后更新:2022年12月6日

Linux大神网

每日更新,欢迎收藏♥ 不积跬步无以至千里,加油,共勉。

点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论

Linux大神网

每日更新,欢迎收藏♥
不积跬步无以至千里,加油,共勉。

最新 热点 随机
最新 热点 随机
Linux系统用户系统上的三种类型的帐户的介绍 Linux下的开机启动设置方法是什么?脚本或服务 谷歌更新Linux内核构建的公共内核库:添加对kokoro作业的支持 linux下mysql中可以使用REVOKE语句来删除某个用户的权限 内核的角度来看,调用hotplug和通常的hotplug环境 Linux系统在开机的时候自动加载某些脚本或系统服务 如何在Linux上安装Diablo3操作系统Diablo3 RedHatLinux软件安装包软件后缀为.rpm最初 1.1.4Linux的内核版本和发行版本是什么意思?(图) Linux系统的安装-Ubuntu开启虚拟机安装过程默认注意事项 hello_exit函数代码存放在__init段中(一) 英伟达将LinuxGPU内核模块作为开放源码/MIT双重许可证(图) virtualboxlinux官方版免费、小巧,同时支持Windows、Linux和Mac系统主机 Linux平台下的嵌入式软件要具备说明技能吗? 图解linuxlinux内核结构框图对内核框图有个总体的把握 linux虚拟机的安装步骤是什么?虚拟机和服务器的区别 精通Linux程序设计的高级程序员并非一件可望不可及的事情 linux虚拟机软件 Linux在安装的时候要注意什么?Linux安装注意事项 Python都有哪些应用,学习完成Python后能的工作方向 微软考虑用Rust开发内核的Linux内核-Hartman
Linux5.12的推送请求不断涌入新开放的合并窗口预计4月底看到它的稳定版本go语言被称作互联网时代的c语言,用来开发嵌入式linux的理由腾讯云服务器上也搭建一套环境,安装成功自动启动个人笔记本安装Ubuntu20.04LTS下载地址启动第一步--加载BIOS当你打开计算机电源(组图)虚拟机安装Ubuntu操作系统-Ubuntu空间20G镜像下载指令中各个make-C~/linuxM=`pwd编译Linux启动过程中的几个部分内核的引导(图)如何在Linux上安装虚拟机的结果大多都是怎么安装的一个免费软件时间跟进的小白鼠是什么鬼?专题计算机是如何启动的?、内核操作系统的启动流程学习Linux最简单、最实用的环境就是虚拟机环境(上)基于命令修改文件的权限命令-ld1.Linux磁盘分区和目录Linux发行版本之间的差别很少?国内性价比很高的Linux虚拟主机系统安装的流程是什么?Linux下修改文件权限的权限与所有权的实现就显得很有必要linux到底难不难学呢?推荐可以查看Linux命令大全Linux中修改文件权限的命令、创建者所在组、所有人Linux的内核放在了哪里?/boot的启动目录一览阿里云>社区>主题地图S>查看存储推荐
为什么要给Linux二进制文件设置这种权限?(上) Linux线程的操作、多线程的同步和互斥的基本单位 MBR方式主引导记录(MBR)代码的扇区方式引导 web应用漏洞/心脏滴血8069zabbix服务远程命令执行/注入8161activemq弱口令 1.1.4Linux的内核版本和发行版本是什么意思?(图) Linux系统中常用的有两个账户和普通账户的区别 SteelEyeLifeKeeper集群软件关键特性企业数据中心 udpsocket编程1.UDP编程框架常用函数小插曲 Linux下强大的shell,模式下配置 linux下mysql中可以使用REVOKE语句来删除某个用户的权限 -max=999999net.2 Ubuntu18验证dhcp服务器配置是否成功?.04的配置 Linux系统的安装-Ubuntu开启虚拟机安装过程默认注意事项 红帽Linux零基础入门不难 老男孩教育怎么样有哪些课程?-八维教育 更为丰富多彩快手直播伴侣app是一款爱看什么视频就能将视頻 :2管理大部分命令的组成 学习嵌入式linux编程开发应该要必备什么条件?(一) 1.给ens33网卡配置静态IP、网关、DNS,检查是否生效 PS教程:如何用gif文件来形容文件的意思?
标签聚合
文件目录 linux服务器 软件 shell 命令模式 unix sudo linux系统 虚拟机 电脑
书籍
课程
技术群
技术干货大合集↓
  • 2023年2月 / 12篇
  • 2023年1月 / 161篇
  • 2022年12月 / 187篇
  • 2022年11月 / 76篇

COPYRIGHT © 2023 linuxgod.net ALL RIGHTS RESERVED.