Windows内核编程基础(3)

news2024/9/27 15:25:55

内存分配

在应用层编程时,系统提供了GlobalAlloc/HeapAlloc/LocalAlloc等函数。C/C++库提供了malloc函数,以及new操作符在堆上分配内存。

在我前面一个关于Windows页交换文件的博客中,介绍了虚拟内存,

虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

堆内存是基于虚拟内存上更小粒度的分割,这个分割由堆管理器管理,根据需求,堆管理器会申请 一页(或多页虚拟内存),然后对这块虚拟内存进行更小粒度的内存分割与管理,以满足开发者对内存的需求。

堆的大小是在应用程序启动时设置,但可以随着空间的需要而增长(分配器从操作系统请求更多内存)。

与应用层的堆概念类似,在内核中有一种称为“池(Pool)"的概念,我们可以从Pool中申请内存

WDK提供了一系列内存分配函数,其中最基本的是ExAllocatePoolWithTag,函数原型如下:

1 NTKERNELAPI
2 PVOID
3 NTAPI
4 ExAllocatePoolWithTag (
5     _In_ __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType,
6     _In_ SIZE_T NumberOfBytes,
7     _In_ ULONG Tag
8     );

PoolType:表示 需要申请哪种类型的内存,PoolTypePOOL_TYPE枚举类型

POOL_TYPE定义如下:

 1 typedef _Enum_is_bitflag_ enum _POOL_TYPE {
 2     NonPagedPool,
 3     NonPagedPoolExecute = NonPagedPool,
 4     PagedPool,
 5     NonPagedPoolMustSucceed = NonPagedPool + 2,
 6     DontUseThisType,
 7     NonPagedPoolCacheAligned = NonPagedPool + 4,
 8     PagedPoolCacheAligned,
 9     NonPagedPoolCacheAlignedMustS = NonPagedPool + 6,
10     MaxPoolType,
11     NonPagedPoolBase = 0,
12     NonPagedPoolBaseMustSucceed = NonPagedPoolBase + 2,
13     NonPagedPoolBaseCacheAligned = NonPagedPoolBase + 4,
14     NonPagedPoolBaseCacheAlignedMustS = NonPagedPoolBase + 6,
15     NonPagedPoolSession = 32,
16     PagedPoolSession = NonPagedPoolSession + 1,
17     NonPagedPoolMustSucceedSession = PagedPoolSession + 1,
18     DontUseThisTypeSession = NonPagedPoolMustSucceedSession + 1,
19     NonPagedPoolCacheAlignedSession = DontUseThisTypeSession + 1,
20     PagedPoolCacheAlignedSession = NonPagedPoolCacheAlignedSession + 1,
21     NonPagedPoolCacheAlignedMustSSession = PagedPoolCacheAlignedSession + 1,
22 
23     NonPagedPoolNx = 512,
24     NonPagedPoolNxCacheAligned = NonPagedPoolNx + 4,
25     NonPagedPoolSessionNx = NonPagedPoolNx + 32,
26 
27 } _Enum_is_bitflag_ POOL_TYPE;

常用的值是:NonPagedPool(非分页内存)与PagedPool(分页内存)。

在前面介绍过分页内存与非分页内存的概念。

非分页内存是指这块内存的内容不会被置换到磁盘上,非分页内存非常宝贵,一般用于高IRQL(>= DISPATCH_LEVEL) 的代码中。

分页内存是指这块内存的内容可以被转换到磁盘上。

除了NonPagedPoolPagedPool类型,我们还需要关心的类型是NonPagedPoolExecuteNonPagedPoolNx

NonPagedPoolExecute类型的内存属性为“可执行”,意味着开发者可以将这块内存写入二进制指令然后执行,这个机制虽然很灵活,但存在一定的安全隐患︰对于一些存在漏洞的代码来说,攻击者可以使用“缓存区溢出攻击”技术,在目标内存(缓冲区)中写入可执行指令,由于这块内存具有“可执行“属性,所以攻击者可以成功实施攻击 。

从Win8开始,推荐使用NonPagedPoolNx类型来替代NonPagedPool类型。

从Windows 10 2004开始,使用POOL_FLAG_NON_PAGED类型

NumberOfTypes:表示 需要申请的内存大小

Tag:一个4个字节的标志,用于标志一块内存的使用者,这个Tag—般用于问题排查,如内存泄露,系统蓝屏等。对于内存泄露的情况,可以通过WindbgPoolMon等一些小工具,查看系统中各Tag标志对应的内存大小,找到最大的或者持续增长的内存块。标记中的每个 ASCII 字符必须是0x7E (平铺) 0x20 空间范围内的值。

如果不需要使用Tag标志,可以传递0,或者调用ExAllocatePool函数。

返回值:成功,返回分配内存的首地址。失败返回NULL。

说明:在Windows 10 2004版本中,ExAllocatePoolWithTag函数已经弃用,替换为ExAllocatePool2函数,详细可以参考以下链接:

更新对 ExAllocatePool2 和 ExAllocatePool3 的已弃用 ExAllocatePool 调用 - Windows drivers | Microsoft Learn

内存使用完毕后需要释放,使用ExFreePoolWithTag函数,声明如下:

1 NTKERNELAPI
2 VOID
3 ExFreePoolWithTag (
4     _Pre_notnull_ __drv_freesMem(Mem) PVOID P,
5     _In_ ULONG Tag
6     );

P:需要释放的内存地址

Tag:内存申请 时的标记,如果分配内存时使用的Tag等于0,释放时也传0即可。

后备列表(Lookaside Lists)

在频繁使用ExAllocatePoolWithTag函数分配内存时,容易 造成”内存碎片“。为了提高性能,系统提供了一种被称为”后备列表(Lookaside Lists)“的内存分配方法。

注意:Lookaside的翻译在不同的地方可能有出入,知道这个概念就行了。在《Windows内核编程》一书中,使用的是”旁视列表“,官方文档上显示的是”后备列表“。官方的中文文档是机翻的,但是我这里还是使用了这个名称。

使用”后备列表“的步骤如下:

1、初始化一个”后备列表“

这里以非分页内存为例,分页内存使用方法基本一样

使用ExInitializeNPagedLookasideList函数初始化”后备列表“对象,声明如下:

 1 NTKERNELAPI
 2 VOID
 3 ExInitializeNPagedLookasideList (
 4     _Out_ PNPAGED_LOOKASIDE_LIST Lookaside,
 5     _In_opt_ PALLOCATE_FUNCTION Allocate,
 6     _In_opt_ PFREE_FUNCTION Free,
 7     _In_ ULONG Flags,
 8     _In_ SIZE_T Size,
 9     _In_ ULONG Tag,
10     _In_ USHORT Depth
11     );

Lookaside:表示 被初始化的”后备列表“对象的指针,在64位系统下,这个指针必须以16字节对齐。ExInitializeNPagedLookasideList执行后,Lookaside会被初始化。

Allocate:一个函数指针(回调函数),当我们从”后备列表“对象分配内存时,系统会调用这个函数。

ALLOCATE_FUNCTION声明如下:

1 PVOID
2 ALLOCATE_FUNCTION (
3     _In_ POOL_TYPE PoolType,
4     _In_ SIZE_T NumberOfBytes,
5     _In_ ULONG Tag
6     );

这个参数可以根据自己实际情况使用,不需要使用时,传NULL,系统会使用默认的内存分配函数。

Free:一个函数指针,当我们从”后备列表”释放申请的内存块时,系统会调用这个函数。

FREE_FUNCTION声明如下:

1 VOID
2 FREE_FUNCTION (
3     _In_ __drv_freesMem(Mem) PVOID Buffer
4     );

这个参数可以根据自己实际情况使用,不需要使用时,传NULL,系统会使用默认的内存释放函数。

Flags:内存分配行为。可选以下值

POOL_NX_ALLOCATION:表示分配的非分页内存的属性为“不可执行”,类似上一节介绍的NonPagedPoolNx标志。
POOL_RAISE_IF_ALLOCATION_FAILURE:表示如果内存失败,将抛出一个异常。
0:如果没有特殊要求,可以把Flags参数设置为0。

Size:每次从“后备列表”对象中申请内存的固定大小,单位是字节,这个值不能小于LOOKASIDE_MINIMUM_BLOCK_SIZE

 LOOKASIDE_MINIMUM_BLOCK_SIZE = ((((LONG)__builtin_offsetof(SLIST_ENTRY, Next)) + (sizeof(((SLIST_ENTRY*)0)->Next))))

在64位系统下,LOOKASIDE_MINIMUM_BLOCK_SIZE的值为8。

Tag:表示 内存分配时所使用的标记,与ExAllocatePoolWithTag中的Tag参数一样。

Depth:保留参数,传0即可。

2、需要内存时,直接从”后备列表“对象申请 内存

 申请内存使用ExAllocateFromNPagedLookasideList函数,该函数声明如下:

1 PVOID
2 ExAllocateFromNPagedLookasideList (
3     _Inout_ PNPAGED_LOOKASIDE_LIST Lookaside
4     )

Lookaside:“后备列表”对象指针

返回值:执行成功,返回相应的内存块首地址,否则 返回NULL

ExAllocateFromNPagedLookasideList分配的内存大小为ExInitializeNPagedLookasideList函数所指定的Size

3、使用完成后,通过”后备列表“回收这些内存

 释放内存使用ExFreeToNPagedLookasideList函数,该函数声明如下:

1 VOID
2 ExFreeToNPagedLookasideList (
3     _Inout_ PNPAGED_LOOKASIDE_LIST Lookaside,
4     _In_ __drv_freesMem(Mem) PVOID Entry
5     )

Lookaside:“后备列表“对象指针

Entry:表示需要释放的内存块

4、当不再需要”后备列表“时,将其对象删除。

 删除”后备列表“时,使用ExDeleteNPagedLookasideList函数。该函数声明如下:

1 NTKERNELAPI
2 VOID
3 ExDeleteNPagedLookasideList (
4     _Inout_ PNPAGED_LOOKASIDE_LIST Lookaside
5     );

Lookaside:表示需要删除的”后备列表“对象指针

 5、完整的示例

下面使用简单的代码演示一下,如何使用”后备列表“

//直接申请内存
PVOID pAllocFromPool = ExAllocatePoolWithTag(NonPagedPoolNx, 10240, 0);

if (pAllocFromPool != NULL)
    ExFreePoolWithTag(pAlloc, 0);


//使用后备列表(Lookaside Lists)
PNPAGED_LOOKASIDE_LIST pLookasideList = 
(PNPAGED_LOOKASIDE_LIST)ExAllocatePoolWithTag(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST), 'urfh');

if (pLookasideList != NULL)
{
    memset(pLookasideList, 0, sizeof(NPAGED_LOOKASIDE_LIST));
    //初始化
    ExInitializeNPagedLookasideList(pLookasideList, NULL, NULL, 0, 1024, 'urfh', 0);

    //分配
    PVOID pAlloc = ExAllocateFromNPagedLookasideList(pLookasideList);

    if (pAlloc != NULL)
    {
        DbgPrint("Memory Allocate First:%p", pAlloc);

        //释放
        ExFreeToNPagedLookasideList(pLookasideList, pAlloc);
    }

    //再次分配
    pAlloc = ExAllocateFromNPagedLookasideList(pLookasideList);

    if (pAlloc != NULL)
    {
        DbgPrint("Memory Allocate Second:%p", pAlloc);

        //释放
        ExFreeToNPagedLookasideList(pLookasideList, pAlloc);
    }

    //删除Lookaside Lists
    ExDeleteNPagedLookasideList(pLookasideList);
    //释放
    ExFreePoolWithTag(pLookasideList, 'urfh');

注意:如果编译报错,请将ExAllocatePoolWithTag替换为ExAllocatePool2函数

1 PVOID pAllocFromPool = ExAllocatePool2(POOL_FLAG_NON_PAGED, 10240, 0);

 运行结果:

参考资料

比较内存分配方法

https://learn.microsoft.com/zh-cn/windows/win32/memory/comparing-memory-allocation-methods

What and where are the stack and heap?

https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap

使用后备列表

使用后备列表 - Windows drivers | Microsoft Learn

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

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

相关文章

古月居全新改版上线:AI 大模型“古月知道”引领 ROS 学习新体验

前言 古月居自成立以来,一直致力于为广大 ROS(机器人操作系统)爱好者和开发者提供优质的学习资源和社区交流平台。经过长期的用户调研和反馈,我们发现旧版古月居在使用过程中存在一些不便之处。 为了更好地服务大家,…

如何生成谷歌临时邮箱?

谷歌的Gmail作为全球最受欢迎的邮件服务之一,不仅因其稳定性和强大的功能而备受青睐,还因为它支持临时邮箱功能,这一功能能够极大地提升用户在各种场景下的使用灵活性。无论是处理一次性事务、注册新账户还是防止垃圾邮件,Gmail的…

通义模型Prompt调优的实用技巧

1. 目录 1. prompt工程简介 2. Prompt设计 2.1 Prompt主要构成要素 2.2 Prompt编写策略 策略一:对较难被准确遵循的复杂规则可拆分为多条规则,有助于提升效果 策略二:适当冗余关键信息 策略三:使用分隔符给Prompt分段 策…

类与对象—python

一、类的含义 1.1类的作用(理解) 收集学生信息时,如果让同学们自主填写,信息的顺序、格式不一,内容混乱。如果发给同学们既定的表格,同学们按照规定的顺序、格式进行填写,那信息就会一目了然&…

回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于SO-SVR蛇群算法优化支持向量机的数据多…

path_provider插件的用法

文章目录 1. 概念介绍2. 实现方法3. 示例代码我们在上一章回中介绍了"如何实现本地存储"相关的内容,本章回中将介绍如何实现文件存储.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在上一章回中介绍的本地存储只能存储dart语言中基本类型的数值,如果遇到…

大数据-147 Apache Kudu 常用 Java API 增删改查

点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…

【学习笔记】 AD24中元器件重叠系统不报错的解决方案(消除报错)

【学习笔记】 AD24中PCB设计元器件重叠后系统不报错的解决方案(如何主动屏蔽报错) 一、Component Clearance未开启使能的解决方案二、最小水平间距设置错误的解决方案三、未开启设计规则检查的解决方案四、设计规则检查中 “在线”和“批量”的含义五、为…

开源的CDN:jsDelivr+Github加速图片加载

文章目录 20240530更新 网站加载的图片耗时,将图片使用jsDelivr进行加速。 每次打开静态网站的时候,都会发现页面的内容已经加载出来了,但是图片还是一片白,就考虑如何让图片能够更快的加载出来。 后面发现可以用jsDelivr加速Gi…

自然场景文本定位系统源码分享

自然场景文本定位检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

南京市副市长吴炜一行至天洑软件参观调研

近日,南京市副市长吴炜、南京市工信局副局长代吉上、南京市科技局副局长王愿华、江宁开发区管委会副主任易骏飞一行至天洑软件参观,调研工业软件重点企业方案,天洑软件副总经理冯克列、总工程师郭阳、研发部部长谢佳雯陪同调研。 Q1&#xff…

南开大学联合同济大学发布最新SOTA Occ OPUS:使用稀疏集进行占据预测,最快实现8帧22FPS

Abstract 占据预测任务旨在预测体素化的 3D 环境中的占据状态,在自动驾驶社区中迅速获得了关注。主流的占据预测工作首先将 3D 环境离散化为体素网格,然后在这些密集网格上执行分类。然而,对样本数据的检查显示,大多数体素是未占…

Linux:编译,调试和Makefile

一丶vim编译器 ### 基本概念 模式:Vim有几种不同的模式,包括: 命令/正常/普通模式:控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insert mode下,或者到 last line mode 插入模…

xpath在爬虫中的应用、xpath插件的安装及使用

安装 1、打开谷歌浏览器进入扩展程序安装页面(右上角会有"开发者模式按钮")默认是关闭的,当安装此插件时需要把开发者模式打开。 2、下载下来的xpath_helper是zip格式的,需要解压缩即可安装。 3、重启浏览器,再次点击扩展程序即…

CAN通信详解

1、CAN介绍 1.1、什么是CAN? CAN(Controller Area Network) 即控制器局域网,是ISO国际标准化的串行通信协议。 开发目的:为了满足汽车产业的“减少线束的数量”、“通过多个LAN,进行大量数据的高速通信”…

9.25 C++继承 多态

手动实现队列 #include <iostream>using namespace std;class My_queue { private:struct Node //队列结构体{int data;Node *next;Node(int value):data(value),next(nullptr){}};Node *front;Node *rear;int size;public:My_queue():front(nullptr),rear(nullptr),siz…

EMQX MQTT 服务器启用 SSL/TLS 安全连接,使用8883端口

1.提前下载安装openssl 2.新建openssl文件夹打开在命令行操作 3.按照下面的操作进行 MQTT 安全 作为基于现代密码学公钥算法的安全协议&#xff0c;TLS/SSL 能在计算机通讯网络上保证传输安全&#xff0c;EMQX 内置对 TLS/SSL 的支持&#xff0c;包括支持单/双向认证、X.509 …

如何使用ssm实现线上旅游体验系统+vue

TOC ssm691线上旅游体验系统vue 绪论 课题背景 身处网络时代&#xff0c;随着网络系统体系发展的不断成熟和完善&#xff0c;人们的生活也随之发生了很大的变化。目前&#xff0c;人们在追求较高物质生活的同时&#xff0c;也在想着如何使自身的精神内涵得到提升&#xff0…

进制数知识(2)—— 浮点数在内存中的存储 和 易混淆的二进制知识总结

目录 1. 浮点数在内存中的存储 1.1 浮点数的大V表示法 1.2 浮点数的存储格式 1.3 浮点数的存入规则 1.4 浮点数的读取规则 1.5 补充&#xff1a;移码与掩码 1.6 题目解析 2. 易错的二进制知识 2.0 符号位到底会不会参与运算&#xff1f; 2.0.1 存储前的编码变化运算 …

【Zynq从零开始】汇总导航

Welcome 大家好&#xff0c;欢迎来到瑾芳玉洁的博客&#xff01; &#x1f611;励志开源分享诗和代码&#xff0c;三餐却无汤&#xff0c;顿顿都被噎。 &#x1f62d;有幸结识那个值得被认真、被珍惜、被捧在手掌心的女孩&#xff0c;不出意外被敷衍、被唾弃、被埋在了垃圾堆。…