当前位置: 首页 > news >正文

windows内核研究(内存管理-线性地址的管理)

内存管理


线性地址的管理

进程空间的地址划分

分区x86 32位Windows
空指针赋值区0x00000000 - 0x0000FFFF
用户模式区0x00010000 - 0x7FFEFFFF
64KB禁入区0x7FFF0000 - 0x7FFFFFFF
内核0x80000000 - 0xFFFFFFFF

线性地址有4GB,但是并不是所有的地方都能访问(这里的不能访问只是默认情况下,一但给这些区域挂上物理页还是可以访问的),所以需要记录哪些地方分配了

在内核空间是通过一个链表把所有未分配的空间链在一起
但是在用户空间,这样管理的效率太低,而是通过收索二叉树来管理

在_EPROCESS结构体当中有一个成员VadRoot,这个成员就是这个二叉树的入口点

在这里插入图片描述
由于我是用64位windbg分析32位的系统,版本等原因导致VadRoot的地址未被正常解析出来,所以我们直接加上偏移来解析这个地址

dt _RTL_AVL_TREE (ac110040 + 310)  // 进程地址ac110040 + 偏移310 VadRoot处

在这里插入图片描述
得到地址:0xbb925678

VadRoot通常每一个节点都是_MMVAD结构,但是现在的windows对VadRoot进行了优化,并不直接指向_MMVAD,而是通过AVL 树/红黑树的结构来进行优化访问和存储可以使用以下命令直接遍历VadRoot

!vad 地址 // 遍历vad

在这里插入图片描述

字段示例值含义
VAD 节点地址bb923260该 VAD 节点在内核中的内存地址(_MMVAD 结构地址)
Level8该节点在 VAD 树中的深度(层级)
Start580内存区域的起始页号(需转换为虚拟地址:Start << PAGE_SHIFT,32位系统 PAGE_SHIFT=12,即 0x580000
End5a7内存区域的结束页号(0x5A7000
Commit5已提交的物理页数量(单位:页,每页通常 4KB)
TypeMapped内存类型:
Private(私有内存,如堆/栈)
Mapped(映射文件或共享内存)
SubtypeExe子类型(仅适用于 Mapped 类型):
Exe(可执行文件映射)
Image(镜像文件)
• 其他(如 Pagefile
ProtectionEXECUTE_WRITECOPY内存保护标志:
READONLY/READWRITE
EXECUTE/EXECUTE_WRITECOPY
PAGE_GUARD(保护页)
File/Desc\Users\...\x32dbg.exe如果是文件映射,显示文件路径;如果是共享内存,显示描述信息(如 Pagefile section

Private Memory

申请内存的两种方式:

  1. 通过VirtualAlloc/VirtualAllocEx申请的:Private Memory(当前的进程独享内存)
  2. 通过CreateFileMapping映射的:Mapped Memory

我们来通过代码来看一下VirtualAlloc在没有分配和分配后的线性地址

#include<iostream>
#include<windows.h>LPVOID lpAddr;int main() {printf("当前内存还未申请!");getchar();lpAddr = VirtualAlloc(NULL, 0x1000 * 2, MEM_COMMIT, PAGE_READWRITE);printf("申请的内存地址:0x%x", lpAddr);system("pasue");return 0;
}

在这里插入图片描述
此时内存还未申请,我们用windbg查看一下当前进程的线程地址

在这里插入图片描述
回到程序让程序申请内存后我们再来看下
在这里插入图片描述

在这里插入图片描述
可以看到在我们没有分配内存时,0xbc0位置是没有分配的,可以看上面对应的属性和我们申请时填写的一致

堆与栈

那这个VirtualAlloc和我们在写c/c++程序时,用到的molloc/new关键字有什么区别呢,c/c++使用的申请是在当中申请的它们的低层实现是HeapAlloc,它是由操作系统提前通过VirtualAlloc申请好的一块内存空间,当使用molloc/new时,就会把申请好的地址给挂过去

代码测试

#include<iostream>
#include<Windows.h>int num = 0x789;int main() {printf("申请内存之前!");getchar();// 在栈上分配内存int stack = 0x123;// 在堆上分配内存int* heap = new int(0x456);printf("栈空间的地址:0x%x\n",&stack);printf("堆空间的地址:0x%x\n",heap);printf("全局变量的地址:0x%x\n", &num);system("pause");return 0;
}

在这里插入图片描述

在这里插入图片描述

可以发现在我们程序中无论是全局变量,还是堆空间,栈空间中的内存在程序运行时就已经存在了

可以发现全局变量是在我们的程序中的一个位置写死的


Mapped Memory

上面讲到Private Memory是推私有的,而Mapped Memory是共享的,可以是文件共享或者是物理页共享

在这里插入图片描述
在上图中,Mapped后面有对应文件路径的就是文件共享,反之就是物理页共享

代码测试

#include<iostream>
#include<windows.h>int main(){// 第一个参数如果提供一个文件的句柄,那么创建出来的就是文件映射,否则就是内存映射。HANDLE g_hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,BUFSIZ,L"共享内存");// 将物理页与线性地址进行关联LPTSTR g_lpBuff = (LPTSTR)MapViewOfFile(g_hMapFile,FILE_MAP_ALL_ACCESS,0,0,BUFSIZ);*(PDWORD)g_lpBuff = 0x12345678;printf("A进程写入地址内容:%p - %x",g_lpBuff,*(PDWORD)g_lpBuff);system("pause");return 0;
}

在这里插入图片描述
我们再到windbg中遍历一下

在这里插入图片描述
可以看到B30的位置已经分配好了物理页,然后我们就可以在其他进程获取到这个创建好的内存空间

代码测试

#include <iostream>
#include <windows.h>int main() {HANDLE g_hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"共享内存");// 将物理页与线性地址进行映射LPTSTR g_lpBuff = (LPTSTR)MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFSIZ);printf("B进程读取%x", *(PDWORD)g_lpBuff);system("pause");return 0;
}

在这里插入图片描述
可以看到我们成功的读取到了内容

共享文件

#include<iostream>
#include<windows.h>int main(){HANDLE g_hFile = CreateFile(L"newMemory.exe",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL);HANDLE g_hMapFile = CreateFileMapping(g_hFile,NULL,PAGE_READWRITE,0,BUFSIZ,NULL);LPTSTR g_lpBuff = (LPTSTR)MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFSIZ);printf("地址:0x%x",g_lpBuff);system("pause");return 0;
}

在这里插入图片描述

windbg中查看

在这里插入图片描述

可以看到已经成功的映射到了我们的文件上


写拷贝

可以看到这里的有一部分它的类型是EXECUTE_WRITECOPY,Mapped的后面还有一个Exe,这又是什么呢?
在这里插入图片描述

代码测试

#include<iostream>
#include<windows.h>int main(){LoadLibrary(L"C:\\Users\\win10x32\\Desktop\\gxnc.exe");system("pause");return 0;
}

在这里插入图片描述

可以看到当我们以LoadLibrary载入一个PE文件时,它的属性会被设置为EXECUTE_WRITECOPOY,所以我们看到的kernel32.dll,KernelBase.dll,其实都是操作系统用LoadLibrary一个个加载的,本质上没有任何区别,设置为EXECUTE_WRITECOPOY是因为当前系统环境有很多进程都在使用,也都可以对该文件进行修改,那这样以来,一但某一个进程修改了系统dll,那其他使用这个dll的进程就会出问题

http://www.dtcms.com/a/319712.html

相关文章:

  • 【PHP 中的 `use` 关键字完全指南】
  • Linux图文理解进程
  • fiddler实用用法,抓包内容导入到apipos
  • 数据库管理系统:入门需要了解的内容
  • Modbus核心参数,调试工具,接线注意事项
  • Mongodb常用命令简介
  • C++线程库的学习
  • 从Centos 9 Stream 版本切换到 Rocky Linux 9
  • MongoDB数据存储界的瑞士军刀:cpolar内网穿透实验室第513号挑战
  • IDEA-Research推出的一系列检测、分割模型:从DINO(改进版DETR)、Grounding Dino、DINO-X到Grounded SAM2
  • 串联所有单词的子串-leetcode
  • 计算机基础·linux系统
  • Linux线程学习
  • pytorch学习笔记-最大池化maxpooling的使用、搭建多层网络并验证、sequential的使用
  • golang的面向对象编程,struct的使用
  • 2.8 逻辑符号
  • Linux怎么查看时区信息?(Linux时区)(tzselect)
  • Java中接口与抽象类
  • 处理失败: module ‘fitz‘ has no attribute ‘open‘
  • 传统防火墙与下一代防火墙
  • 华为 2025 校招目标院校
  • 【2025最新】在 macOS 上构建 Flutter iOS 应用
  • 嵌入式学习---在 Linux 下的 C 语言学习 Day10
  • 可执行文件的生成与加载执行
  • 超高车辆如何影响城市立交隧道安全?预警系统如何应对?
  • [论文阅读] 软件工程 | 软件工程中的同理心:表现、动机与影响因素解析
  • oracle 11G安装大概率遇到问题
  • 大文件断点续传(vue+springboot+mysql)
  • Failed to restart docker.service: Unit docker.service is masked.
  • PostgreSQL 数据库 设置90天密码过期时间的完整方案