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

news/2024/11/22 20:47:43/

如前所述,在前几章内容中笔者简单介绍了内存读写的基本实现方式,这其中包括了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);// 让内核对象引用数减1ObDereferenceObject(Process);// DbgPrint("读取进程 %d 的地址 %x 出错", ptr->Pid, ptr->Address);return FALSE;}// 解除绑定KeUnstackDetachProcess(&state);// 让内核对象引用数减1ObDereferenceObject(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);// 让内核对象引用数减1ObDereferenceObject(Process);return FALSE;}// 写入数据到指定位置RtlCopyMemory(ChangeData, OpCode, Size);DbgPrint("[内核写字节] # 写入地址: 0x%x 写入数据: %x \n", Address, OpCode);// 让内核对象引用数减1ObDereferenceObject(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指针指向字节数组arrpx = 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指针指向字节数组arrpx = 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.ppmy.cn/news/105409.html

相关文章

Linux:虚拟网卡技术tun/tap

一、介绍 Linux中的TAP网络接口详解是Linux网络管理中的很重要的一部分&#xff0c;它可以用来建立虚拟网络&#xff0c;模拟网络&#xff0c;管理网络流量以及实现安全网络功能等。本文将介绍TAP网络接口的基本原理、如何使用它进行网络管理、与以太网的区别&#x…

docker 镜像/容器的打包、导出、导入

目录 一、将变动过的容器打包生成新的镜像 二、对镜像进行导出导入 1、将镜像导出为一个镜像img文件 2、将img镜像文件导入&#xff0c;复制出一个完全一样镜像 三、对容器进行导入导出 1、将容器导出为一个镜像tar文件 2、将镜像tar文件导入&#xff0c;生成一个新镜像…

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

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

【观察】浪潮信息:自研液环式真空CDU技术,将被动应对变为主动防御

毫无疑问&#xff0c;在“双碳”战略的大环境下&#xff0c;数据中心走向绿色低碳和可持续发展已成为“不可逆”的大趋势&#xff0c;特别是随着全国一体化大数据中心、新型数据中心等政策文件的出台、“东数西算”工程的正式启动&#xff0c;数据中心的建设规模和数量呈现出快…

我用低代码结合ChatGPT开发,每天多出1小时摸鱼

&#x1f449;腾小云导读 GPT 出现之后&#xff0c;很多人推测大量的软件都会因为其出现而重写。本文主要是低代码平台与 ChatGPT 结合的一些思考以及实践。期望与各位读者一起搭上 AI 这列快车&#xff0c;为开发提提速&#xff5e; &#x1f449;目录 1 背景 2 Demo 演示 3 思…

权限维持-SSP-DLL 加载

前言 继续学习中&#xff0c;今天是权限维持的东西&#xff0c;大家永远不要忘记初心&#xff0c;要一起奋斗哦&#xff01; 注&#xff1a;单机环境和域环境都可以使用 复现 一.进程注入lsass.exe 使用mimikatz将伪造的SSP注入内存&#xff0c;这样用户在注销重新登录的时候就…

知识点滴 - POSIX vs SUS vs LSB

SUS扩展了POSIX&#xff1b;LSB扩展了POSIX和SUS&#xff0c;但有一些冲突。 只有经过SUS认证的操作系统才能被称为 "Unix"&#xff08;因为SUS的所有者Open Group拥有Unix商标&#xff09;&#xff1b;并非所有的Linux发行版都符合LSB&#xff0c;例如Debian和Ubunt…

ThreadLocal精讲

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…