tiechui_lesson03_缓冲读写与自定义控制

news2024/10/6 12:34:57

学习了与应用层通过缓冲区方式的交互,包括读写,自定义控制等。小坑比较多,大部分是是头文件和设置上的错误,跟着视频敲想快进就跳过了一些细节。包括:

  1. <windef.h> 头文件的引用    //使用DWORD等类型
  2. switch语句中break的设置           //这个总是会容易忽略
  3. 应用层自定义码的设置需要引入头文件winioctl.h 

内核模块代码:

#include <ntifs.h>
#include <windef.h>	//使用DWORD等类型

#define DEVICE_NAME L"\\Device\\MyFirstDevicePlus"		//设备名称
#define SYM_NAME	L"\\??\\MyFirstDevicePlus"			//符号链接

//定义自定义控制码 (做一个减法)
#define IOCTL_MUL	CTL_CODE(FILE_DEVICE_UNKNOWN,0x855,METHOD_BUFFERED, FILE_ANY_ACCESS)

//分发函数
NTSTATUS MyCreate(PDEVICE_OBJECT pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device has be opened!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyClearUp(PDEVICE_OBJECT pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyClearUp!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyClose(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyClose!");
	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 0;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}
NTSTATUS MyRead(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyRead!");

	//获取当前IRP堆栈信息
	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);

	//对应用户层的读请求
	ULONG readsize = pstack->Parameters.Read.Length;
	DbgPrint("用户请求读大小为%u\n", readsize);
	//对应用户层所分配的缓冲区的内存位置
	PCHAR readbuffer = pirp->AssociatedIrp.SystemBuffer;	

	// readbuffer的赋值实际上是对用户缓冲区的改变
	RtlCopyMemory(readbuffer,
		"This Message Come From Kernel.",
		strlen("This Message Come From Kernel."));

	pirp->IoStatus.Status = status;
	//对于Information的赋值是返回给用户程序实际读取的长度。
	pirp->IoStatus.Information = strlen("This Message Come From Kernel.");

	//输出下字符串的长度
	DbgPrint("Really Read Info Len is %lld\n", 
		strlen("This Message Come From Kernel."));
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}

NTSTATUS MyWrite(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyWrite!");

	//获取当前IRP堆栈信息
	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);

	//对应用户层的写请求
	ULONG writesize = pstack->Parameters.Write.Length;
	DbgPrint("用户请求写大小为%u\n", writesize);
	//对应用户层所分配的缓冲区的内存位置
	PCHAR writebuffer = pirp->AssociatedIrp.SystemBuffer;

	// 写入扩展设备之前先进行清0操作
	RtlZeroMemory(pdevice->DeviceExtension, 200);
	// writebuffer的数据写入到设备扩展里边
	RtlCopyMemory(pdevice->DeviceExtension,writebuffer,writesize);

	DbgPrint("写缓冲区内存地址:%p,设备扩展内容%s\n", writebuffer,
		(PCHAR)pdevice->DeviceExtension);

	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = 13;

	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}

NTSTATUS MyControl(PDEVICE_OBJECT	pdevice, PIRP pirp)
{
	UNREFERENCED_PARAMETER(pdevice);
	NTSTATUS status = STATUS_SUCCESS;
	DbgPrint("My Device MyControl!");

	PIO_STACK_LOCATION pstack = IoGetCurrentIrpStackLocation(pirp);
	ULONG	iocode = pstack->Parameters.DeviceIoControl.IoControlCode;	// 获取控制码

	ULONG inlen = pstack->Parameters.DeviceIoControl.InputBufferLength;
	ULONG outlen = pstack->Parameters.DeviceIoControl.OutputBufferLength;
	ULONG ioinfo = 0;

	DbgPrint("InputBufferLength is %u\n", inlen);
	DbgPrint("OutputBufferLength is %u\n", outlen);

	switch (iocode)
	{
	case IOCTL_MUL:
	{
		//做一个减法

		DWORD indata = *(PDWORD)pirp->AssociatedIrp.SystemBuffer;
		DbgPrint("--Kernel Indata %d \n", indata);
		indata = indata * 5;
		*(PDWORD)pirp->AssociatedIrp.SystemBuffer = indata;z
		ioinfo = 50;

		//别忘记break!
		break;
	}
	default:
		status = STATUS_UNSUCCESSFUL;
		ioinfo = 0;
		break;
	}

	pirp->IoStatus.Status = status;
	pirp->IoStatus.Information = ioinfo;
	IoCompleteRequest(pirp, IO_NO_INCREMENT);

	return status;
}

//卸载函数
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
	DbgPrint("basedriver 卸载驱动\n");
	if (DriverObject->DeviceObject)
	{
		IoDeleteDevice(DriverObject->DeviceObject);
	}
	UNICODE_STRING symLink = RTL_CONSTANT_STRING(SYM_NAME);
	IoDeleteSymbolicLink(&symLink);
}

//入口函数
NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT DriverObject, 
	IN PUNICODE_STRING RegistryPath
)
{
	UNREFERENCED_PARAMETER(RegistryPath);
	//注册卸载函数
	DriverObject->DriverUnload = DriverUnload;

	NTSTATUS status = STATUS_SUCCESS;
	PDEVICE_OBJECT pdevice;		// 用来接收创建的设备对象
	UNICODE_STRING devicename = { 0 };
	RtlInitUnicodeString(&devicename, DEVICE_NAME);

	//创建设备对象
	status = IoCreateDevice(DriverObject, 
							200,	// 定义设备扩展的大小,用来存放写入的数据
							&devicename, 
							FILE_DEVICE_UNKNOWN, 
							0, 
							TRUE, 
							&pdevice);

	if (!NT_SUCCESS(status))
	{
		KdPrint(("IoCreateDevice 虚拟设备打开失败 状态码 (0x%08X)\n",status));
		DbgPrint("IoCreateDevice 虚拟设备打开失败 状态码 (0x%08X)\n", status);
	}

	if (pdevice->Flags)
	{
		//不要忘了设备对象的读写方式,否则会蓝屏
		pdevice->Flags |= DO_BUFFERED_IO;	// 缓冲区方式的读写
	}



	//
	//创建成功,创建符号链接
	//
	UNICODE_STRING symname = { 0 };
	RtlInitUnicodeString(&symname, SYM_NAME);
	status = IoCreateSymbolicLink(&symname, &devicename);	// 符号链接名 设备名
	if (!NT_SUCCESS(status))
	{
		KdPrint(("IoCreateSymbolicLink 符号链接创建失败 状态码 (0x%08X)", status));
		DbgPrint("IoCreateSymbolicLink 符号链接创建失败 状态码 (0x%08X)", status);
	}

	//设置分发例程
	DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;
	DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MyClearUp;
	DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;
	DriverObject->MajorFunction[IRP_MJ_READ] = MyRead;
	DriverObject->MajorFunction[IRP_MJ_WRITE] = MyWrite;
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyControl;

	return 0;
}

 应用层代码:

#include <stdio.h>
#include <Windows.h>
#include <winioctl.h>// 使用自定义控制码
#include <stdlib.h>

//定义自定义控制码 (做一个乘法)
#define IOCTL_MUL	CTL_CODE(FILE_DEVICE_UNKNOWN,0x855,METHOD_BUFFERED,	FILE_ANY_ACCESS)

int main()
{
	HANDLE hdevice = NULL;
	CHAR readBuffer[50] = { 0 };
	DWORD bread = 0;


	hdevice = CreateFile(L"\\\\.\\MyFirstDevicePlus", 
		GENERIC_WRITE | GENERIC_READ, 0
		, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if (hdevice != INVALID_HANDLE_VALUE)
	{
		printf("打开设备SUCCESS!\n");
	}
	else {
		printf("打开设备失败!\n");

		system("pause");
	}
	system("pause");


	//读文件
	if (ReadFile(hdevice, (PVOID)readBuffer, 50, &bread, NULL))
	{
		printf("readBuffer --地址 %p --内容 %s\n", readBuffer, readBuffer);
		printf("真正读取长度 bread is %d\n", bread);
	}
	else {
		printf("ReadFile failed !\n");
	}
	system("pause");


	//写文件
	if (WriteFile(hdevice, "This message come from R3.",
		strlen("This message come from R3."), &bread, NULL))
	{

		printf("返回写入长度 bread is %d\n", bread);	// 写固定了 
	}
	else {
		printf("WriteFile failed !\n");
	}
	system("pause");


	//自定义控制
	printf("自定义控制 !\n");
	DWORD a = 8888, b = 0;
	DeviceIoControl(hdevice, IOCTL_MUL, &a, 4, &b, 4, &bread, NULL);

	printf("in %d  out %d   really info %d\n", a, b, bread);



	//关闭句柄
	if (hdevice != nullptr)
	{
		CloseHandle(hdevice);
		printf("关闭句柄!\n");
	}

	system("pause");

	return 0;
}

运行效果:

 将a乘了5并返回给应用层的b变量。

小结:

视频代码写的很粗糙,不过很感谢锤总能带着敲代码找错误,很有帮助,能让我对与内核开发不再那么畏惧。

收获是:

  1. 用户层读数据的时候,在内核层可以将一些数据放到pirp->AssociatedIrp.SystemBuffer缓冲区中,那么用户层程序就可以获取到这个缓冲区中的数据了。
  2. 用户层写数据的时候,在内核层可以将用户层的写入缓冲区的数据先拷贝到设备扩展的区域中。
  3. 自定义控制的写法是设置好控制码,然后在分发例程中对该控制码进行处理,通过之前定义好的缓冲区的方式,与应用层进行交互。
  4. pirp->IoStatus.Information 这个字段是返回给发出请求的函数一些信息,类似下边这样设置后,函数就能获取到这个数值。
    pirp->IoStatus.Information = 13;
  5. 视频中只是简单介绍,想要写好驱动程序,还需要继续练习,加深理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/496420.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iOS开发多target

场景 背景:设想一下有一个场景,一个业务分为多种身份,他们大部分功能是相同的,但是也有自己的差异性。这种情况,想要构建出不同身份的APP。你会怎么做??? 当然,你可以拷贝一份代码出来,给项目重新命名。这样做的好处是,他们互相不会冲突,不用去关心是否有逻辑的冲…

Python中变量赋值过程的理解

Python中变量赋值过程的理解 在Python中对变量赋值过程的理解&#xff0c;有助于学习者对Python的变量和所指向的对象之间的指向关系深刻理解&#xff0c;避免编程中多个变量赋值后&#xff0c;对变量结果的不确定&#xff0c;减少赋值过程中疑问和困惑。 1.赋值过程基本过程 …

全文检索-Elasticsearch-进阶检索

文章目录 前言一、SearchAPI1.1 URL 后接参数检索1.2 URL 加请求体检索 二、Query DSL2.1 基本语法格式2.2 匹配查询 match2.3 短语匹配 match_phase2.4 多字段匹配 multi_match2.5 复合查询 bool2.6 过滤 filter2.7 查询 term2.8 聚合 aggregations 三、Mapping3.1 待完成3.2 …

Mybatis动态SQL用法

动态SQL是Mybatis的一大重要特性&#xff0c;它可以完成不同条件下的SQL拼接&#xff0c;降低了因为SQL语句书写中的小错误而造成程序报错的概率&#xff0c;例如拼接时要确保不能忘记添加必要的空格&#xff0c;还要注意去掉列表最后一个列名的逗号&#xff0c;利用动态SQL就可…

Vue项目dialog组件数据项清空

目录 一、重置myParams 二、this.$refs["name"].resetFields() 1、使用v-if 然后渲染, 无效 2、使用nextTick, 数据显示慢一拍, 重新打开dialog才会显示上次输入的内容 三、复盘-最终方法 一、重置myParams <el-form:model"myParams":rules&quo…

ssh登录出现Permission denied, please try again可能的解决方案

问题描述 环境&#xff1a;ubuntu 20.04问题描述&#xff1a;在已经设置免密登录后&#xff0c;ssh登录时&#xff08;例如ssh localhost&#xff09;还需要输入密码&#xff0c;并且输入密码后出现下面一系列的报错&#xff1a; xxx(用户名)localhosts password: Permissio…

Makefile教程(入门介绍)

文章目录 前言一、Makefile介绍二、make和Makefile的关系三、编写一个简单的Makefile总结 前言 本篇文章将带大家学习Makefile&#xff0c;Makefile在文件的编译中起到重要作用&#xff0c;在Linux中我们也是经常使用到Makefile&#xff0c;下面我将会带大家学习什么是Makefil…

GNSS定位原理--理解笔记

1、利用“后方交会”原理进行定位。 由已知3个点的坐标以及3个已知点到未知点的距离&#xff0c;就可以计算出未知点的坐标。测量出3个已知点到位置点的距离后&#xff0c;根据三球交会定位&#xff0c;利用方程计算出未知点坐标。 两球交会得到一个圆&#xff0c;三球交会得…

【自学网络安全】从零开始学习网络渗透的核心知识点,助你入门宝典

前言 上周旁听了一个大学学长组织的线上网络安全交流会&#xff0c;里边不乏充斥着各位行业大牛&#xff0c;讲的内容确实精彩&#xff0c;可能对于网络安全经验5年的人来说&#xff0c;是受益匪浅&#xff0c;欢迎程度极高&#xff0c;恨不得跳出屏幕来表示赞同&#xff0c;毕…

初识Spring:如何在Maven工程上搭建Spring框架?

编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 Maven版本&#xff1a;apache-maven-3.6.3 Mybatis版本&#xff1a;3.5.6 spring版本&#xff1a;5.3.1 文章目录 Spring系列专栏文章目录一. 什么是Spring&#xff1f;二. 如何搭建S…

基于海鸥算法改进的随机森林分类算法-附代码

基于海鸥算法改进的随机森林分类算法 文章目录 基于海鸥算法改进的随机森林分类算法1.数据集2.RF模型3.基于海鸥算法优化的RF4.测试结果5.Matlab代码6.Python代码 摘要&#xff1a;为了提高随机森林数据的分类预测准确率&#xff0c;对随机森林中的树木个数和最小叶子点数参数利…

Java阶段二Day13

Java阶段二Day13 文章目录 Java阶段二Day13网站SpringSpringFramework核心模块特点使用Spring创建项目对象存储创建对象的方式 IoC容器控制反转依赖注入IoC容器的实现 基于XML管理bean获取bean方式xml配置文件中的标签特殊值处理注入 今天开始是学习后端框架SSM&#xff08;Spr…

元气森林讲出新的“可乐味”故事?

这个五一&#xff0c;一场接一场的音乐节不断拔高假日的声浪。仅成都一市&#xff0c;就开展了298场演出&#xff0c;累计吸引10万余游客市民观看。人潮涌动之间&#xff0c;节日的氛围被拉满。 值得一提的是&#xff0c;音乐节不仅是游客的狂欢&#xff0c;也是消费品牌的盛宴…

【Java|golang】1419. 数青蛙

给你一个字符串 croakOfFrogs&#xff0c;它表示不同青蛙发出的蛙鸣声&#xff08;字符串 “croak” &#xff09;的组合。由于同一时间可以有多只青蛙呱呱作响&#xff0c;所以 croakOfFrogs 中会混合多个 “croak” 。 请你返回模拟字符串中所有蛙鸣所需不同青蛙的最少数目。…

Spring Boot集成ShardingSphere实现读写分离(四) | Spring Cloud 43

一、读写分离 1.1 背景 面对日益增加的系统访问量&#xff0c;数据库的吞吐量面临着巨大瓶颈。 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说&#xff0c;将数据库拆分为主库和从库&#xff0c;主库负责处理事务性的增删改操作&#xff0c;从库负责处理查询操…

Qt的Model/View结构

Model/View结构 将界面组件与所编辑的数据分离开来&#xff0c;又通过数据源的方式连接起来&#xff0c;是处理界面与数据的一种较好的方式。Qt使用Model/View结构来处理这种关系&#xff0c;Model/View的基本结构如图5-1所示。其中各部分的功能如下。 图1 Model/View基本结构…

ps磨皮插件专用智能磨皮插件 AI算法美颜 提高P图效率

ps的功能这么强大&#xff0c;其美白磨皮方法当然不止一种。本文就给大家细数一下ps美白磨皮常用的几种方法。在各种方法中&#xff0c;插件法的操作更为简单&#xff0c;效果也更好&#xff0c;因此我们还会介绍ps磨皮美白插件哪个好。事不宜迟&#xff0c;一起来看看吧。 一…

Throwable、Error、Exception 运行时异常、非运行时异常

一、Throwable Throwable类&#xff08;可抛出&#xff09;是一个超类&#xff0c;是所有错误和异常根。只有继承于Throwable的类或者其子类才能够被抛出。 有两个子类为Error和Exception&#xff0c;其他的异常类都继承于这两个子类。 三、Error和Exception Error类及其子类…

C++知识点 -- 智能指针

C知识点 – 智能指针 文章目录 C知识点 -- 智能指针一、智能指针的使用及原理1.使用场景2.RAII3.智能指针的设计思想4.智能指针的拷贝问题 二、auto_ptr三、unique_ptr四、shared_ptr1.模拟实现2.shared_ptr的循环引用 五、weak_ptr六、定制删除器七、内存泄漏1.什么是内存泄漏…

PDF转Word免费的软件有哪些?(完整教程版!)

这五种免费PDF转Word的方式&#xff0c;不仅免费&#xff0c;还能准确恢复PDF文件内容&#xff0c;前方高能&#xff0c;快速学习&#xff01; 1.Office直接打开 大厂软件&#xff0c;既能读文档又能转换格式&#xff0c;总让人放心。 转换流程1&#xff1a;在Word的最新版本…