Windows内核情景分析学习笔记(三)

内存管理

用户空间内存

在Windows中 对于用户空间的内存采用AVL树进行管理,在Reatos中 EPROCESS中的对象Vadroot指向MADDRESS_SPACE结构

Struct MADDRESS_SPACE //地址空间描述符
{
MEMORY_AREA* MemoryRoot;//本地址空间的已分配区段表(一个AVL树的根)
VOID* LowestAddress;//本地址空间的最低地址(用户空间是0,内核空间是0x80000000)
EPROCESS* Process;//本地址空间的所属进程
USHORT* PageTableRefCountTable;
ULONG PageTableRefCountTableSize;//上面那个表的大小
}

该结构的MemoryRoot所指向的正式AVL树的根 其对应的结构如下所示

Struct MEMORY_AREA //区段描述符
{
Void* StartingAddress; //开始地址,普通区段对齐64KB,其它类型区段对齐4KB
Void* EndAddress;//结尾地址,EndAddress – StartingAddress就是该区段的大小
MEMORY_AREA* Parent;//AVL树中的父节点
MEMORY_AREA* LeftChild;//左边的子节点
MEMORY_AREA* RightChild;//右边的子节点
//常见的区段类型有:普通型区段、视图型区段、缓冲型区段(后面文件系统中会讲到)等
ULONG type;//本区段的类型
ULONG protect;//本区段的保护权限,可读、可写、可执行的组合
ULONG flags;//当初分配本区段时的分配标志
BOOLEAN DeleteInProgress;//本区段是否标记为了‘已删除’
ULONG PageOpCount;
Union
{
Struct //这个Struct专用于视图型区段
{
//凡是含有ROS字样的函数与结构体都表示是ReactOS与Windows中不同的实现细节
ROS_SECTION_OBJECT* section;
ULONG ViewOffest;//指本视图型区段在所在Segment内部的偏移
MM_SECTION_SEGMENT* Segment;//所属Segment
BOOLEAN WriteCopyView;//本视图区段是不是一个写复制区段
}SectionData;
LIST_ENTRY RegionListHead;//本区段内部的所有Region区块,放在一个链表中
}Data;
}//end

从根节点出发便可以遍历到已注册的所有区间

//通过参数AVL树的根节点,和所需定位的虚拟地址,找到对应的区间结点
MEMORY_AREA*  MmLocateMemoryAreaByAddress(MADDRESS_SPACE* as, void* addr);

因为这棵树是一颗排好序的树,只要对比如果addr小于当前结点的StartingAddress就从左子树继续查找,如果大于EndAddress就从右子树查找,否则要不就是已经找到所在区间结点要不就是直到找到叶子节点还未找到,找到便返回对应的区间节点,反之则返回NULL

//找到符合长度和对其标准的 未使用区间 根据所给TopDown来寻找是从高地址找起还是低地址找起
Void* MmFindGap(MADDRESS_SPACE* as, ULONG len, ULONG AlignGranularity,  BOOL TopDown)

这里介绍从低地址找起的具体流程
先通过MmIterateFirstNode 一路遍历左子树直到找到叶子节点 也就是区间地址最小结点 然后通过MmIterateNextNode 找到区间结点第二小的结点,然后通过相减得到两个区间内 还存在多大的空隙,比较是否跟所给的长度和对其符合,如果符合则返回相应的虚拟地址,否则继续重复上面的操作 直到找到或者已遍历完整个用户空间依旧不存在 合适的位置,返回NULL

//根据所给的地址和长度 找到重合的区间节点
MEMORY_AREA* MmLocateMemoryAreaByRegion(MADDRESS_SPACE* as, void* addr, ULONG len)

与MmFindGap类似的方法 只不过比较的时候是拿地址和长度进行比较

//根据所给的BaseAddr Len protect等参数分配一个区间
NTSTATUS MmCreateMemoryArea(MADDRESS_SPACE* as, type,  void** BaseAddr,  Len,  protect, bFixedAddr, AllocFlags,   MEMORY_AREA**  Result)

当所给BaseAddr为0的时候 意味着并没有指定确定的区间的StartingAddress
只需通过MmFindGap找到一块符合长度需求的空袭,得到虚拟内存地址,然后对as的各个对象进行初始化操作即可
当所给BaseAddr不为0的时候,需要先对BaseAddr进行对齐操作,然后通过MmLocateMemoryAreaByAddress判断BaseAddr是否已经被包含在已分配的区间之内,如果已包含则返回失败,如果没有包含则将已对齐的BaseAddr及其他参数 将as进行初始化操作

接下来进行区块的介绍

在Windows内存管理中,已经分配的区间可以划分为多个区块,当然一个刚分配好的区间只有一个区块 区块的结构如下所示

Struct MM_REGION //区块描述符
{
ULONG type;//指本区块的分配类型(预定型分配、提交型分配),又叫映射状态(已映射、尚未映射)
ULONG protect;//本区块的访问保护权限,可读、可写、可执行的组合
ULONG length;//区块长度,对齐页面大小(4KB)
LIST_ENTRY RegionListEntry;//用来挂入所在区段的区块链表
}

属于同一个区间的区块是通过ListEntry进行连接的,并且会存在区块类型,我们通过VirtualAllocEx指定的MEM_COMMIT或者MEM_RESERVE都会体现在这个类型里面

接下来进行几个区块相关函数的介绍

//根据所给虚拟内存地址找到对应的区块 RegionListHead为对应的区间的首区块
MM_REGION* MmFindRegion(void* AreaBaseAddr,  LIST_ENTRY*  RegionListHead,  void* TgtAddr, Void** RegionBaseAddr)

根据RegionListHead遍历队列 进行和AreaBaseAddr的对比,找到对应的区块

MM_REGION* MmSplitRegion(MM_REGION* rgn, BaseAddr,   StartAddr,Len,  NewType,NewProtect AlterFunc)

这个函数将指定区块内部的指定区域(StartAddr,Len)修改为新的分配类型、保护属性,使原区块分裂,一分为三(特殊情况一分为二),然后调用AlterFunc跟着修改二级页表中,新区块的那些PTE,最后再跟着修改物理页面分配情况。函数返回新分出来的那个中间区块。这是一个内部辅助函数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值