3.1 Windows驱动开发:内核远程堆分配与销毁
在开始学习内核内存读写篇之前,我们先来实现一个简单的内存分配销毁堆的功能,在内核空间内用户依然可以动态的申请与销毁一段可控的堆空间,一般而言内核中提供了ZwAllocateVirtualMemory
这个函数用于专门分配虚拟空间,而与之相对应的则是ZwFreeVirtualMemory
此函数则用于销毁堆内存,当我们需要分配内核空间时往往需要切换到对端进程栈上再进行操作,接下来LyShark
将从API开始介绍如何运用这两个函数实现内存分配与使用,并以此来作为驱动读写篇的入门知识。
首先以内存分配为例ZwAllocateVirtualMemory()
函数,该系列函数在ntifs.h
头文件内,且如果需要使用则最好提前在程序头部进行声明,该函数的微软官方定义如下所示;
NTSYSAPI NTSTATUS ZwAllocateVirtualMemory([in] HANDLE ProcessHandle, // 进程句柄[in, out] PVOID *BaseAddress, // 指向将接收已分配页面区域基址的变量的指针[in] ULONG_PTR ZeroBits, // 节视图基址中必须为零的高顺序地址位数[in, out] PSIZE_T RegionSize, // 指向将接收已分配页面区域的实际大小[in] ULONG AllocationType, // 包含指定要执行的分配类型的标志的位掩码[in] ULONG Protect // 包含页面保护标志的位掩码
);
参数ProcessHandle
用于传入一个进程句柄此处我们可以通过NtCurrentProcess()
获取到当前自身进程的句柄。
参数BaseAddress
则用于接收分配堆地址的首地址,此处指向将接收已分配页面区域基址的变量的指针。
参数RegionSize
则用于指定需要分配的内存空间大小,此参数的初始值指定区域的大小(以字节为单位)并向上舍入到下一个主机页大小边界。
参数AllocationType
用于指定分配内存的属性,通常我们会用到的只有两种MEM_COMMIT
指定为提交类型,MEM_PHYSICAL
则用于分配物理内存,此标志仅用于地址窗口扩展AWE内存。 如果设置了MEM_PHYSICAL
则还必须设置MEM_RESERVE
不能设置其他标志,并且必须将保护设置为PAGE_READWRITE
。
参数Protect
用于设置当前分批堆的保护属性,通常当我们需要分配一段可执行指令的内存空间时会使用PAGE_EXECUTE_READWRITE
,如果无执行需求则推荐使用PAGE_READWRITE
属性。
在对特定进程分配堆时第一步就是要切入到该进程的进程栈中,此时可通过KeStackAttachProcess()
切换到进程栈,于此对应的是KeUnstackDetachProcess()
脱离进程栈,这两个函数的具体定义如下;
// 附加到进程栈
void KeStackAttachProcess(PRKPROCESS PROCESS, // 传入EProcess结构[out] PRKAPC_STATE ApcState // 指向KAPC_STATE结构的不透明指针
);
// 接触附加
void KeUnstackDetachProcess([in] PRKAPC_STATE ApcState // 指向KAPC_STATE结构的不透明指针
);
此处如果需要附加进程栈则必须提供该进程的PRKPROCESS
也就是EProcess
结构,此结构可通过PsLookupProcessByProcessId()
获取到,该函数接收一个进程PID并将此PID转为EProcess
结构,函数定义如下;
NTSTATUS PsLookupProcessByProcessId([in] HANDLE ProcessId, // 进程PID[out] PEPROCESS *Process // 输出EP结构
);
基本的函数介绍完了,那么这段代码应该不难理解了,如下代码中需要注意一点,参数OUT PVOID Buffer
用于输出堆地址而不是输入地址。
#include <ntifs.h>
#include <windef.h>// 定义声明
NTSTATUS ZwAllocateVirtualMemory(__in HANDLE ProcessHandle,__inout PVOID *BaseAddress,__in ULONG_PTR ZeroBits,__inout PSIZE_T RegionSize,__in ULONG AllocationType,__in ULONG Protect
);// 分配内存空间
NTSTATUS AllocMemory(IN ULONG ProcessPid, IN SIZE_T Length, OUT PVOID Buffer)
{NTSTATUS Status = STATUS_SUCCESS;PEPROCESS pEProcess = NULL;KAPC_STATE ApcState = { 0 };PVOID BaseAddress = NULL;// 通过进程PID得到进程EProcessStatus = PsLookupProcessByProcessId((HANDLE)ProcessPid, &pEProcess);if (!NT_SUCCESS(Status) && !MmIsAddressValid(pEProcess)){return STATUS_UNSUCCESSFUL;}// 验证内存可读if (!MmIsAddressValid(pEProcess)){return STATUS_UNSUCCESSFUL;}__try{// 附加到进程栈KeStackAttachProcess(pEProcess, &ApcState);// 分配内存Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &BaseAddress, 0, &Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);RtlZeroMemory(BaseAddress, Length);// 返回内存地址*(PVOID*)Buffer = BaseAddress;// 脱离进程栈KeUnstackDetachProcess(&ApcState);}__except (EXCEPTION_EXECUTE_HANDLER){KeUnstackDetachProcess(&ApcState);Status = STATUS_UNSUCCESSFUL;}ObDereferenceObject(pEProcess);return Status;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("Uninstall Driver Is OK \n"));
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint(("hello lyshark \n"));DWORD process_id = 4160;DWORD create_size = 1024;DWORD64 ref_address = 0;NTSTATUS Status = AllocMemory(process_id, create_size, &ref_address);DbgPrint("对端进程: %d \n", process_id);DbgPrint("分配长度: %d \n", create_size);DbgPrint("分配的内核堆基址: %p \n", ref_address);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
运行如上代码片段,则会在进程PID=4160
中开辟一段堆空间,输出效果如下;
与创建堆相对ZwFreeVirtualMemory()
则用于销毁一个堆,其微软定义如下所示;
NTSYSAPI NTSTATUS ZwFreeVirtualMemory([in] HANDLE ProcessHandle, // 进程句柄[in, out] PVOID *BaseAddress, // 堆基址[in, out] PSIZE_T RegionSize, // 销毁长度[in] ULONG FreeType // 释放类型
);
相对于创建来说,销毁堆则必须传入堆空间BaseAddress
基址,以及堆空间的RegionSize
长度,需要格外注意FreeType
参数,这是一个位掩码,其中包含描述ZwFreeVirtualMemory
将为页面指定区域执行的任意操作类型。如果传入MEM_DECOMMIT
则将取消提交页面的指定区域,页面进入保留状态。而如果设置为MEM_RELEASE
则堆地址将被立即释放。
销毁堆空间FreeMemory()
的完整代码如下所示,销毁是我们使用MEM_RELEASE
参数即立即销毁。
#include <ntifs.h>
#include <windef.h>// 申请堆
NTSTATUS ZwAllocateVirtualMemory(__in HANDLE ProcessHandle,__inout PVOID *BaseAddress,__in ULONG_PTR ZeroBits,__inout PSIZE_T RegionSize,__in ULONG AllocationType,__in ULONG Protect
);// 销毁堆
NTSYSAPI NTSTATUS ZwFreeVirtualMemory(__in HANDLE ProcessHandle,__inout PVOID *BaseAddress,__inout PSIZE_T RegionSize,__in ULONG FreeType
);// 分配内存空间
NTSTATUS AllocMemory(IN ULONG ProcessPid, IN SIZE_T Length, OUT PVOID Buffer)
{NTSTATUS Status = STATUS_SUCCESS;PEPROCESS pEProcess = NULL;KAPC_STATE ApcState = { 0 };PVOID BaseAddress = NULL;// 通过进程PID得到进程EProcessStatus = PsLookupProcessByProcessId((HANDLE)ProcessPid, &pEProcess);if (!NT_SUCCESS(Status) && !MmIsAddressValid(pEProcess)){return STATUS_UNSUCCESSFUL;}// 验证内存可读if (!MmIsAddressValid(pEProcess)){return STATUS_UNSUCCESSFUL;}__try{// 附加到进程栈KeStackAttachProcess(pEProcess, &ApcState);// 分配内存Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &BaseAddress, 0, &Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);RtlZeroMemory(BaseAddress, Length);// 返回内存地址*(PVOID*)Buffer = BaseAddress;// 脱离进程栈KeUnstackDetachProcess(&ApcState);}__except (EXCEPTION_EXECUTE_HANDLER){KeUnstackDetachProcess(&ApcState);Status = STATUS_UNSUCCESSFUL;}ObDereferenceObject(pEProcess);return Status;
}// 注销内存空间
NTSTATUS FreeMemory(IN ULONG ProcessPid, IN SIZE_T Length, IN PVOID BaseAddress)
{NTSTATUS Status = STATUS_SUCCESS;PEPROCESS pEProcess = NULL;KAPC_STATE ApcState = { 0 };Status = PsLookupProcessByProcessId((HANDLE)ProcessPid, &pEProcess);if (!NT_SUCCESS(Status) && !MmIsAddressValid(pEProcess)){return STATUS_UNSUCCESSFUL;}if (!MmIsAddressValid(pEProcess)){return STATUS_UNSUCCESSFUL;}__try{// 附加到进程栈KeStackAttachProcess(pEProcess, &ApcState);// 释放内存Status = ZwFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &Length, MEM_RELEASE);// 脱离进程栈KeUnstackDetachProcess(&ApcState);}__except (EXCEPTION_EXECUTE_HANDLER){KeUnstackDetachProcess(&ApcState);Status = STATUS_UNSUCCESSFUL;}ObDereferenceObject(pEProcess);return Status;
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("Uninstall Driver Is OK \n"));
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint(("hello lyshark \n"));DWORD process_id = 4160;DWORD create_size = 1024;DWORD64 ref_address = 0;NTSTATUS Status = AllocMemory(process_id, create_size, &ref_address);DbgPrint("对端进程: %d \n", process_id);DbgPrint("分配长度: %d \n", create_size);DbgPrint("分配的内核堆基址: %p \n", ref_address);Status = FreeMemory(process_id, create_size, ref_address);DbgPrint("销毁堆地址: %p \n", ref_address);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}
编译并运行如上这段代码,代码会首先调用AllocMemory()
函数实现分配堆,然后调用FreeMemory()
函数销毁堆,并输出销毁地址,如下图所示;
相关文章:

3.1 Windows驱动开发:内核远程堆分配与销毁
在开始学习内核内存读写篇之前,我们先来实现一个简单的内存分配销毁堆的功能,在内核空间内用户依然可以动态的申请与销毁一段可控的堆空间,一般而言内核中提供了ZwAllocateVirtualMemory这个函数用于专门分配虚拟空间,而与之相对应…...

C++: 模板初阶
文章目录 一. 泛型编程二. 函数模板函数模板的原理函数模板的实例化隐式实例化: 让编译器根据实参推演模板参数的实际类型显示实例化: 在函数名后的<>中制定模板参数的世纪类型 模板参数的匹配原则 三. 类模板类模板的定义格式类模板的实例化 一. 泛型编程 如何实现一个…...

人工智能基础_机器学习036_多项式回归升维实战3_使用线性回归模型_对天猫双十一销量数据进行预测_拟合---人工智能工作笔记0076
首先我们拿到双十一从2009年到2018年的数据 可以看到上面是代码,我们自己去写一下 首先导包,和准备数据 from sklearn.linear_model import SGDRegressor import numpy as np import matplotlib.pyplot as plt X=np.arange(2009.2020)#左闭右开,2009到2019 获取从2009到202…...

【算法挨揍日记】day29——139. 单词拆分、467. 环绕字符串中唯一的子字符串
139. 单词拆分 139. 单词拆分 题目描述: 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 解题思路&am…...
YOLOv8-Seg改进:轻量级Backbone改进 | VanillaNet极简神经网络模型 | 华为诺亚2023
🚀🚀🚀本文改进:一种极简的神经网络模型 VanillaNet,支持vanillanet_5, vanillanet_6, vanillanet_7, vanillanet_8, vanillanet_9, vanillanet_10, vanillanet_11等版本,相比较yolov8-seg各个版本如下: layersparametersgradientsGFLOPsvanillanet_521230017523...

解决Requests中使用httpbin服务器问题:自定义URL的实现与验证
问题背景 在使用Python的Requests模块进行单元测试时,可能会遇到无法使用本地运行的httpbin服务器进行测试的问题。这是因为测试脚本允许通过环境变量HTTPBIN_URL指定用于测试的本地httpbin实例,但在某些测试用例中,URL是硬编码为httpbin.or…...

软考-高级-系统架构设计师教程(清华第2版)【第17章 通信系统架构设计理论与实践(P614~646)-思维导图】
软考-高级-系统架构设计师教程(清华第2版)【第17章 通信系统架构设计理论与实践(P614~646)-思维导图】 课本里章节里所有蓝色字体的思维导图...

【MATLAB源码-第82期】基于matlab的OFDM系统载波频移偏差(CFO)估计,对比三种不同的方法。
操作环境: MATLAB 2013b 1、算法描述 正交频分复用(OFDM)系统中的载波频率偏移(CFO)估计是一项关键技术,用于确保数据传输的准确性和效率。CFO通常由于振荡器频率不匹配和多普勒频移引起。不同的CFO估计…...

Docker Swarm: 容器编排的力量和优势深度解析
文章目录 Docker Swarm的核心概念1. 节点(Node)2. 服务(Service)3. 栈(Stack) 使用Docker Swarm1. 初始化Swarm2. 加入节点3. 创建服务4. 扩展和缩减服务5. 管理栈6. 管理服务更新 Docker Swarm的优势深度解…...

调整Windows键盘上只能看到拼音而无法看到实际的文本以及关闭输入法悬浮窗方法
一、输入法设置 如果您在键盘上只能看到拼音而无法看到实际的文本,这可能是因为您的输入法设置为中文拼音输入法或其他仅显示拼音的输入法。 要解决这个问题,您可以尝试以下方法: 1. 切换输入法:按下 Shift Alt 组合键或 Wind…...
【微软技术栈】C#.NET 中的管道操作
C#.NET 管道为进程间通信提供了平台。 管道分为两种类型: 匿名管道。 匿名管道在本地计算机上提供进程间通信。 与命名管道相比,虽然匿名管道需要的开销更少,但提供的服务有限。 匿名管道是单向的,不能通过网络使用。 仅支持一个服…...
Python学习笔记--进程
进程 Python 中的多线程其实并不是真正的多线程,如果想要充分地使用多核 CPU 的资源,在 Python 中大部分情况需要使用多进程。 Python 提供了非常好用的多进程包 multiprocessing,只需要定义一个函数,Python 会完成其他所有事情。 借助这个包,可以轻松完成从单进程到并…...

比亚迪刀片电池与特斯拉4680电池比较
1 电池材料 比亚迪刀片电池采用的磷酸铁锂LFP(LiFePO4),特斯拉的4680电池采用的三元锂。 磷酸铁锂:循环寿命长,安全性能好,价格低廉,但是能量密度低,导电性能差,低温表现…...
在写windows C++代码的时候,从代码安全角度考虑,我们应该注意什么?
在写windows C代码的时候,从代码安全角度考虑,我们应该注意什么?分别是:输入验证、内存管理、错误处理、并发和线程安全、使用安全的API、避免使用不安全的函数、最小权限原则。 一、输入验证 1. 用户输入验证 #include <io…...

【草料】uni-app ts vue 小程序 如何如何通过草料生成对应的模块化二维码
一、查看uni-app项目 1、找到路径 可以看到项目从 src-race-pages-group 这个使我们目标的查询页面 下面我们将这个路径copy到草料内 2、找到进入页面入参 一般我们都会选择 onload() 函数下的入参 这里我们参数的是 id 二、草料 建议看完这里的教程文档 十分清晰!…...
CMS与FullGC
JVM中的CMS(Concurrent Mark Sweep)GC和Full GC(Full Garbage Collection)是两种不同的垃圾回收算法。 CMS GC:CMS GC是一种并发的垃圾回收算法,它在运行期间与应用程序线程并发工作,尽可能减少…...

一款.NET开源的小巧、智能、免费的Windows内存清理工具 - WinMemoryCleaner
前言 我们在使用Windows系统的时候经常会遇到一些程序不会释放已分配的内存,从而导致电脑变得缓慢。今天给大家推荐一款.NET开源的小巧、智能、免费的Windows内存清理工具:WinMemoryCleaner。 使用Windows内存清理工具来优化内存,这样不必浪…...

iptables详解:链、表、表链关系、规则的基本使用
目录 防火墙基本概念 什么是防火墙? Netfilter与iptables的关系 链的概念 表的概念 表链关系 规则的概念 查询规则 添加规则 删除iptables中的记录 修改规则 更详细的命令(5链4表) 防火墙基本概念 什么是防火墙? 在…...
安全管理中心(设备和技术注解)
网络安全等级保护相关标准参考《GB/T 22239-2019 网络安全等级保护基本要求》和《GB/T 28448-2019 网络安全等级保护测评要求》 密码应用安全性相关标准参考《GB/T 39786-2021 信息系统密码应用基本要求》和《GM/T 0115-2021 信息系统密码应用测评要求》 1系统管理 1.1对系统管…...

Failed to execute org.scala-tools:maven-scala-plugin:2.15.2解决
原因也不是很清楚,查看一个博主文章(net.alchim31.maven:scala-maven-plugin:maven依赖无法下载或无法编译)得到的解决方案: 在idea的terminal执行以下语句即可实现maven对scala代码的编译: mvn clean scala:compile compile pac…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...

华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...

9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...