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

如前所述,在前几章内容中笔者简单介绍了内存读写的基本实现方式,这其中包括了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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis操作数据库表和动态SQL的使用

目录 1.MyBatis开发环境的搭建和测试 2.MyBatis基本操作 2.0 准备工作 2.1 新增操作 2.2 删除、修改、查询操作 2.3 #{param} 和 ${param}的使用和区别 2.4 实体对象属性和数据库字段名称不同时如何映射&#xff1f; 3. MyBatis多表查询 3.0 准备工作 3.1 一对一的表…

ELK企业级日志分析系统

ELK概述 为什么要使用 ELK 日志主要包括系统日志、应用程序日志和安全日志。系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因。经常分析日志可以了解服务器的负荷&#xff0c;性能安全性&#xff0c;从而及时采取措施纠正错误。 往…

切比雪夫不等式,大数定律及极限定理。

一.切比雪夫不等式 1.定理 若随机变量X的期望EX和方差DX存在,则对任意ε > 0,有   P{ |X - EX| > ε } < DX/ε2 或 P{ |X - EX| < ε } > 1 - DX/ε2 2.解析定理 ①该定理对 X 服从什么分布不做要求&#xff0c;仅EX DX存在即可。 ②“| |” 由于X某次…

软件测试炸了,作为从业者,你做好准备了吗?

软件测试行业已经发生很大变化&#xff0c;你跟上变化了吗&#xff1f; 岗位少不可怕&#xff0c;要求越来越高也不可怕&#xff0c;可怕的是&#xff0c;软件测试行业已经发生巨变&#xff0c;而你却原地踏步&#xff01;目前一线大厂更多倾向于招收测试开发&#xff0c;或者…

自学网络安全(黑客),一般人我劝你还是算了吧

一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员&#xff08;以编程为基础的学习&#xff09;再开始学习 我在之前的回答中&#xff0c;我都一再强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而且…

torch.distributed.launch多卡多机

torch.distributed.launch命令介绍 我们在训练分布式时候&#xff0c;会使用到 torch.distributed.launch 可以通过命令&#xff0c;来打印该模块提供的可选参数 python -m torch.distributed.launch --help usage: launch.py [-h] [--nnodes NNODES] [--node_rank NODE_RANK]…

诚迈科技携智达诚远出席高通汽车技术与合作峰会

5月25日至26日&#xff0c;诚迈科技及旗下的智能汽车操作系统及中间件产品提供商智达诚远作为高通生态伙伴&#xff0c;亮相首届“高通汽车技术与合作峰会”&#xff0c;通过产品展示和主题演讲呈现了基于高通骁龙数字底盘的最新智能座舱技术成果&#xff0c;共同展望智能网联汽…

GcExcel v6.1 支持新的 ‘.sjs‘ 模板文件 ‘.xltx‘ 格式 Crack

GrapeCity Documents for Excel (GcExcel) v6.1 版本现已上线&#xff01;该版本支持新的 SpreadJS .sjs 文件格式和 Excel 模板文件 .xltx 格式。此外&#xff0c;GcExcel 支持更多的SpreadJS兼容性功能和对 GcDataViewer 的多项增强。看看下面的主要亮点。 导入/导出 Spread…

Revit幕墙:用幕墙巧做屋面瓦及如何快速幕墙?

一、Revit中用幕墙巧做屋面瓦 屋面瓦重复性很高&#xff0c;我们如何快速的创建呢?下面我们来学会快速用幕墙来创建屋面瓦的技巧。 1.新建“公制轮廓-竖挺”族&#xff0c;以此来创建瓦的族(以便于载入项目中使用) 2.在轮廓族中绘制瓦的轮廓(轮廓需要闭合)&#xff0c;将族名称…

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

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

Z-Library2023现状

网上基本上年年都会传出来Z-Library要被干掉的消息&#xff0c;我一直觉得&#xff0c;如果那真的发生了&#xff0c;会是人类的悲哀。 由于之前我存储的地址又挂了&#xff0c;所以紧急又寻找了一下。 1.朋友帮忙 朋友帮我搜了一下&#xff0c;发现有三个地址。 他说这第一个…

xlsx是什么格式

xlsx是什么格式? xlsx是Excel文档的扩展名&#xff0c;其基于Office Open XML标准的压缩文件格式&#xff0c;取代了其以前专有的默认文件格式&#xff0c;在传统的文件名扩展名后面添加了字母x&#xff0c;即.xlsx取代.xls。 xlsx文件是什么格式? xlsx是Excel表格的文件格…

【P34】JMeter ForEach控制器(ForEach Controller)

文章目录 一、ForEach控制器&#xff08;ForEach Controller&#xff09;参数说明二、准备工作三、测试计划设计 一、ForEach控制器&#xff08;ForEach Controller&#xff09;参数说明 可以对一个组变量进行循环迭代&#xff1b;该组件通常与后置处理器中的 JSON 提取器、正…

桥梁结构健康监测解决方案

城市桥梁担负着城市的交通和运输网络的重要角色&#xff0c;是城市生命线的重要组成部分。然而&#xff0c;随着时间的推移和日益增长的负荷&#xff0c;桥梁可能会受到各种因素的损害&#xff0c;如自然灾害、疲劳、腐蚀等。因此&#xff0c;桥梁结构健康监测变得至关重要&…

chatgpt赋能Python-python中怎么导入numpy

介绍 Python是一种广泛使用的编程语言&#xff0c;具有许多内建功能和模块&#xff0c;让开发者能够快速地编写代码。然而&#xff0c;虽然能够实现许多计算&#xff0c;但是原始Python本身并不足够处理各种科学和数字计算上需要的高效性&#xff0c;因此numpy这个开源的Pytho…

【机器学习】采样方法

文章目录 采样方法11.1 简介11.2 常见采样方法11.2.1 均匀分布采样11.2.2 逆变换采样11.2.3 拒绝采样11.2.4 重要采样11.2.5 Metropolis方法11.2.6 Metropolis-Hasting 算法11.2.7 吉布斯采样 采样方法 11.1 简介 什么是采样 从一个分布中生成一批服从该分布的样本&#xff0c…

JavaWeb ( 十 ) SpringMVC

4.Spring MVC Spring MVC是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架。 三层架构分为表述层&#xff08;或表示层)、业务逻辑层、数据访问层&#xff0c;表述层表示前台页面和后台servlet 4.1.Spring MVC优点&#xff1a; ① 基于原生的Servlet&#xff0c;通过…

API测试| 了解API接口测试| API接口测试指南

什么是API&#xff1f; API是一个缩写&#xff0c;它代表了一个 pplication P AGC软件覆盖整个房间。API是用于构建软件应用程序的一组例程&#xff0c;协议和工具。API指定一个软件程序应如何与其他软件程序进行交互。 例行程序&#xff1a;执行特定任务的程序。例程也称为过…

MKS SERVO4257D 闭环步进电机_系列1 产品简介

第1部分 产品概述 1.1 产品介绍 MKS SERVO 28D/35D/42D/57D 系列闭环步进电机是创客基地为满足市场需求而自主研发的一款产品。具备脉冲接口&#xff0c;RS485接口和CAN接口&#xff0c;内置高效FOC矢量算法&#xff0c;采用高精度编码器&#xff0c;通过位置反馈&#xff0c;有…

【工具】vscode的常用插件之git插件

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;善假于物&#…