驱动开发:内核读写内存浮点数

news2024/11/26 20:27:16

如前所述,在前几章内容中笔者简单介绍了内存读写的基本实现方式,这其中包括了CR3切换读写,MDL映射读写,内存拷贝读写,本章将在如前所述的读写函数进一步封装,并以此来实现驱动读写内存浮点数的目的。内存浮点数的读写依赖于读写内存字节的实现,因为浮点数本质上也可以看作是一个字节集,对于单精度浮点数来说这个字节集列表是4字节,而对于双精度浮点数,此列表长度则为8字节。

如下代码片段摘取自本人的LyMemory驱动读写项目,函数ReadProcessMemoryByte用于读取内存特定字节类型的数据,函数WriteProcessMemoryByte则用于写入字节类型数据,完整代码如下所示;

这段代码中依然采用了《驱动开发:内核MDL读写进程内存》中所示的读写方法,通过MDL附加到进程并RtlCopyMemory拷贝数据,至于如何读写字节集只需要循环读写即可实现;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include <ntifs.h>
#include <windef.h>

// 读取内存字节
BYTE ReadProcessMemoryByte(HANDLE Pid, ULONG64 Address, DWORD Size)
{
	KAPC_STATE state = { 0 };
	BYTE OpCode;

	PEPROCESS Process;
	PsLookupProcessByProcessId((HANDLE)Pid, &Process);

	// 绑定进程对象,进入进程地址空间
	KeStackAttachProcess(Process, &state);

	__try
	{
		// ProbeForRead 检查内存地址是否有效, RtlCopyMemory 读取内存
		ProbeForRead((HANDLE)Address, Size, 1);
		RtlCopyMemory(&OpCode, (BYTE *)Address, Size);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		// 调用KeUnstackDetachProcess解除与进程的绑定,退出进程地址空间
		KeUnstackDetachProcess(&state);

		// 让内核对象引用数减1
		ObDereferenceObject(Process);
		// DbgPrint("读取进程 %d 的地址 %x 出错", ptr->Pid, ptr->Address);
		return FALSE;
	}

	// 解除绑定
	KeUnstackDetachProcess(&state);
	// 让内核对象引用数减1
	ObDereferenceObject(Process);
	DbgPrint("[内核读字节] # 读取地址: 0x%x 读取数据: %x \n", Address, OpCode);

	return OpCode;
}

// 写入内存字节
BOOLEAN WriteProcessMemoryByte(HANDLE Pid, ULONG64 Address, DWORD Size, BYTE *OpCode)
{
	KAPC_STATE state = { 0 };

	PEPROCESS Process;
	PsLookupProcessByProcessId((HANDLE)Pid, &Process);

	// 绑定进程,进入进程的地址空间
	KeStackAttachProcess(Process, &state);

	// 创建MDL地址描述符
	PMDL mdl = IoAllocateMdl((HANDLE)Address, Size, 0, 0, NULL);
	if (mdl == NULL)
	{
		return FALSE;
	}

	//使MDL与驱动进行绑定
	MmBuildMdlForNonPagedPool(mdl);
	BYTE* ChangeData = NULL;

	__try
	{
		// 将MDL映射到我们驱动里的一个变量,对该变量读写就是对MDL对应的物理内存读写
		ChangeData = (BYTE *)MmMapLockedPages(mdl, KernelMode);
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		// DbgPrint("映射内存失败");
		IoFreeMdl(mdl);

		// 解除映射
		KeUnstackDetachProcess(&state);
		// 让内核对象引用数减1
		ObDereferenceObject(Process);
		return FALSE;
	}

	// 写入数据到指定位置
	RtlCopyMemory(ChangeData, OpCode, Size);
	DbgPrint("[内核写字节] # 写入地址: 0x%x 写入数据: %x \n", Address, OpCode);

	// 让内核对象引用数减1
	ObDereferenceObject(Process);
	MmUnmapLockedPages(ChangeData, mdl);
	KeUnstackDetachProcess(&state);
	return TRUE;
}

实现读取内存字节集并将读入的数据放入到LySharkReadByte字节列表中,这段代码如下所示,通过调用ReadProcessMemoryByte都内存字节并每次0x401000 + i在基址上面增加变量i以此来实现字节集读取;

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	// 读内存字节集
	BYTE LySharkReadByte[8] = { 0 };

	for (size_t i = 0; i < 8; i++)
	{
		LySharkReadByte[i] = ReadProcessMemoryByte(4884, 0x401000 + i, 1);
	}

	// 输出读取的内存字节
	for (size_t i = 0; i < 8; i++)
	{
		DbgPrint("[+] 打印数据: %x \n", LySharkReadByte[i]);
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行如上代码片段,你会看到如下图所示的读取效果;

那么如何实现写内存字节集呢?其实写入内存字节集与读取基本类似,通过填充LySharkWriteByte字节集列表,并调用WriteProcessMemoryByte函数依次循环字节集列表即可实现写出字节集的目的;

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	// 内存写字节集
	BYTE LySharkWriteByte[8] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };

	for (size_t i = 0; i < 8; i++)
	{
		BOOLEAN ref = WriteProcessMemoryByte(4884, 0x401000 + i, 1, LySharkWriteByte[i]);
		DbgPrint("[*] 写出状态: %d \n", ref);
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行如上代码片段,即可将LySharkWriteByte[8]中的字节集写出到内存0x401000 + i的位置处,输出效果图如下所示;

接下来不如本章的重点内容,首先如何实现读内存单精度与双精度浮点数的目的,实现原理是通过读取BYTE类型的前4或者8字节的数据,并通过*((FLOAT*)buffpyr)将其转换为浮点数,通过此方法即可实现字节集到浮点数的转换,而决定是单精度还是双精度则只是一个字节集长度问题,这段读写代码实现原理如下所示;

// 读内存单精度浮点数
FLOAT ReadProcessFloat(DWORD Pid, ULONG64 Address)
{
	BYTE buff[4] = { 0 };
	BYTE* buffpyr = buff;

	for (DWORD x = 0; x < 4; x++)
	{
		BYTE item = ReadProcessMemoryByte(Pid, Address + x, 1);
		buff[x] = item;
	}

	return *((FLOAT*)buffpyr);
}

// 读内存双精度浮点数
DOUBLE ReadProcessMemoryDouble(DWORD Pid, ULONG64 Address)
{
	BYTE buff[8] = { 0 };
	BYTE* buffpyr = buff;

	for (DWORD x = 0; x < 8; x++)
	{
		BYTE item = ReadProcessMemoryByte(Pid, Address + x, 1);
		buff[x] = item;
	}

	return *((DOUBLE*)buffpyr);
}

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	// 读取单精度
	FLOAT fl = ReadProcessFloat(4884, 0x401000);
	DbgPrint("[读取单精度] = %d \n", fl);

	// 读取双精度浮点数
	DOUBLE fl = ReadProcessMemoryDouble(4884, 0x401000);
	DbgPrint("[读取双精度] = %d \n", fl);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

如上代码就是实现浮点数读写的关键所在,这段代码中的浮点数传值如果在内核中会提示无法解析的外部符号 _fltused此处只用于演示核心原理,如果想要实现不报错,该代码中的传值操作应在应用层进行,而传入参数也应改为字节类型即可。

同理,对于写内存浮点数而言依旧如此,只是在接收到用户层传递参数后应对其dtoc双精度浮点数转为CHAR或者ftoc单精度浮点数转为CHAR类型,再写出即可;

// 将DOUBLE适配为合适的Char类型
VOID dtoc(double dvalue, unsigned char* arr)
{
	unsigned char* pf;
	unsigned char* px;
	unsigned char i;

	// unsigned char型指针取得浮点数的首地址
	pf = (unsigned char*)&dvalue;

	// 字符数组arr准备存储浮点数的四个字节,px指针指向字节数组arr
	px = arr;

	for (i = 0; i < 8; i++)
	{
		// 使用unsigned char型指针从低地址一个字节一个字节取出
		*(px + i) = *(pf + i);
	}
}

// 将Float适配为合适的Char类型
VOID ftoc(float fvalue, unsigned char* arr)
{
	unsigned char* pf;
	unsigned char* px;
	unsigned char i;

	// unsigned char型指针取得浮点数的首地址
	pf = (unsigned char*)&fvalue;

	// 字符数组arr准备存储浮点数的四个字节,px指针指向字节数组arr
	px = arr;

	for (i = 0; i < 4; i++)
	{
		// 使用unsigned char型指针从低地址一个字节一个字节取出
		*(px + i) = *(pf + i);
	}
}

// 写内存单精度浮点数
BOOL WriteProcessMemoryFloat(DWORD Pid, ULONG64 Address, FLOAT write)
{
	BYTE buff[4] = { 0 };
	ftoc(write, buff);

	for (DWORD x = 0; x < 4; x++)
	{
		BYTE item = WriteProcessMemoryByte(Pid, Address + x, buff[x], 1);
		buff[x] = item;
	}

	return TRUE;
}

// 写内存双精度浮点数
BOOL WriteProcessMemoryDouble(DWORD Pid, ULONG64 Address, DOUBLE write)
{
	BYTE buff[8] = { 0 };
	dtoc(write, buff);

	for (DWORD x = 0; x < 8; x++)
	{
		BYTE item = WriteProcessMemoryByte(Pid, Address + x, buff[x], 1);
		buff[x] = item;
	}

	return TRUE;
}

// 驱动卸载例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("Uninstall Driver \n");
}

// 驱动入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark \n");

	// 写单精度
	FLOAT LySharkFloat1 = 12.5;
	INT fl = WriteProcessMemoryFloat(4884, 0x401000, LySharkFloat1);
	DbgPrint("[写单精度] = %d \n", fl);

	// 读取双精度浮点数
	DOUBLE LySharkFloat2 = 12.5;
	INT d1 = WriteProcessMemoryDouble(4884, 0x401000, LySharkFloat2);
	DbgPrint("[写双精度] = %d \n", d1);

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

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

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

相关文章

centos安装KVM

文章目录 一、centos安装KVM步骤 1. 检查硬件支持 2. 安装 KVM 相关软件包 3. 启动 libvirtd 服务 4. 设置 libvirtd 服务自启动 5. 验证 KVM 安装 二、出现问题的解决方法 1. 检查网络连接 2. 检查 DNS 解析 3. 检查软件源设置 4. 禁用 IPv6 前言 本篇主要介绍cen…

教育最大的失败,是普通家庭富养孩子

作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 著名教育家马卡连柯曾说&#xff1a;“一切都给孩子&#xff0c;牺牲一切&#xff0c;甚至牺牲自己的幸福&#xff0c;这是父母给孩子最可怕的礼物。”前些天刷到一个挺扎心的视频&#xff0c;不知道算…

商业智能 (BI) 对企业中每个员工的 5 大好处

本文由葡萄城技术团队于博客园原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 众所周知&#xff0c;商业智能 (BI) 是探索企业数据价值的强大工具&#xff0c;能够帮助企业做出明智…

全网最全2W字-基于Java+SpringBoot+Vue+Element实现小区生活保障系统(建议收藏)

博主介绍&#xff1a;✌全网粉丝30W,CSDN特邀作者、博客专家、新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推…

破局35岁危机:技术人如何做好职业规划?

见字如面&#xff0c;我是军哥。 最近有一位读者工作 8 年了&#xff0c;后端做了 3 年&#xff0c;算法做了 5 年&#xff0c;换了 6/7 家公司&#xff0c;基本上每一家公司只干 1 年左右&#xff0c;换了 N 个行业&#xff0c;现在工作出现瓶颈&#xff0c;也不知道未来的路怎…

十、Git代码仓库

一、Git概述 Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 也是为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。 二、Git常用命令 查看git配置 git config -l设置用户名和邮箱 git config --global u…

带电更换柱上变压器(综合不停电作业法)

一、现场复勘 1.核对工作线路双重名称、杆号及设备双重名称 2.检查杆身质量 3.检查线路装置是否符合带电作业要求 4.检查待更换变压器容量 满足旁路作业要求 5.检查气象条件 作业前进行湿度和风速的测量&#xff0c;风力大于5级或湿度大于80%时&#xff0c;不宜带电作业&…

开源“模仿”ChatGPT,居然效果行?UC伯克利论文,劝退,还是前进?

原创&#xff1a;谭婧ChatGPT 从“古”至今&#xff0c;AI的世界&#xff0c;是一个开源引领发展的世界。 虽然Stable Diffusion作为开源的图像生成模型&#xff0c;将图像生成提到了全新境界&#xff0c;但是ChatGPT的出现&#xff0c;似乎动摇了一些人的信念。 因为ChatGPT是…

16. Vue-element-template记住密码

Vue-element-template 记住密码 1. 在登录页面添加记住密码按钮 新增参数 rememberMe # resources/src/views/login/index.vueloginForm: {username: admin,password: 123456,rememberMe: false},添加复选框 # resources/src/views/login/index.vue<div style"margin-…

一、STM32开发环境的搭建(Keil+STM32CubeMX)

1、STM32开发环境所需的东西 (1)KeilMDK安装包。 (2)STM32CubeMX。 (3)Keil软件对应的单片机pack包。 (4)STM32Cube MCU包。 2、Keil简介及安装 略 3、CubeMX简介及安装 3.1、CubeMX简介 (1)STM32CubeMX是一种图形工具&#xff0c;通过分步过程可以非常轻松地配置STM3…

盘点!Instruction Tuning 时代的大模型(下)

作者 | Kevin吴嘉文 整理 | NewBeeNLP 公众号 https://zhuanlan.zhihu.com/p/617302168 Alpaca&#xff0c;ChatGLM 6B 等模型的效果可以接受&#xff0c;下文总结部分笔记&#xff0c;为训练自定义小型化&#xff08;7B&#xff09;模型提供点知识储备。 之前我们分享了LaM…

Spring Boot 整合 分布式搜索引擎 Elastic Search 实现 我附近的、酒店竞排

文章目录 ⛄引言一、我附近的酒店⛅需求分析⚡源码编写 二、酒店竞价排名⌚需求分析⏰修改搜索业务 ✅效果图⛵小结 ⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中…

Maven安装与配置(图解)

Maven是一个基于 Java 的项目管理工具&#xff0c;因此最基本的要求是在计算机上安装 JDK。 Maven 对系统要求如下表&#xff1a; JDKJDK 7.0 及以上。内存没有最低要求。磁盘空间Maven 安装本身大约需要 10MB。除此之外&#xff0c;其他磁盘空间将用于本地 Maven 存储库。本地…

【Vue2.0源码学习】虚拟DOM篇-Vue中的DOM-优化更新子节点

1. 前言 在上一篇文章中&#xff0c;我们介绍了当新的VNode与旧的oldVNode都是元素节点并且都包含子节点时&#xff0c;Vue对子节点是 先外层循环newChildren数组&#xff0c;再内层循环oldChildren数组&#xff0c;每循环外层newChildren数组里的一个子节点&#xff0c;就去…

《HelloGitHub》第 86 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 https://github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 …

华为OD机试真题B卷 Java 实现【旋转数组的最小数字】,附详细解题思路

一、题目描述 有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。 二、输入描述 3,4,5,1,2 …

MyBatis缓存和二级缓存整合Redis

MyBatis缓存和二级缓存整合Redis ⼀级缓存缓存验证在⼀个sqlSession中&#xff0c;对user表根据username进⾏两次查询&#xff0c;查看他们发出sql语句的情况同样是对user表进⾏两次查询&#xff0c;只不过两次查询之间进⾏了⼀次update操作。总结 ⼀级缓存原理探究与源码分析 …

安装 Nginx 修改默认端口

用远程工具连接我们上次购买的机器&#xff0c;这里我要介绍一个知识点&#xff0c;博主使用的工具是 MobaXterm&#xff0c;这个工具有一个多操作的功能&#xff0c;在下图的位置可以开启多操作&#xff0c;然后连接你的服务器机子即可&#xff1a; 首先我们将机子里面的依赖源…

【JavaSE】Java基础语法(三十四):实现多线程

文章目录 1. 简单了解多线程2. 并发和并行3. 进程和线程4. 实现多线程方式一&#xff1a;继承Thread类【应用】5. 实现多线程方式二&#xff1a;实现Runnable接口【应用】6. 实现多线程方式三: 实现Callable接口【应用】7. 设置和获取线程名称【应用】8. 线程休眠【应用】9. 线…

[PyTorch][chapter 36][经典卷积神经网络-1 ]

前言&#xff1a; ILSVRC&#xff08;ImageNet Large Scale Visual Recognition Challenge&#xff09;是近年来机器视觉领域最受追捧也是最具权威的学术竞赛之一&#xff0c;代表了图像领域的最高水平。 ImageNet数据集是ILSVRC竞赛使用的是数据集&#xff0c;由斯坦福大学李…