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…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
第22节 Node.js JXcore 打包
Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 本文主要介绍JXcore的打包功能。 JXcore 安装 下载JXcore安装包&a…...
