1.线性地址的管理

本文深入探讨了进程空间地址划分的细节,包括线性地址的管理和分配,虚拟内存的使用,如通过VirtualAllocEx()申请的内存和文件映射内存的区别,以及如何使用windbg指令查看内存节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程空间的地址划分
在这里插入图片描述
在这里插入图片描述

线性地址有4G 但未必都能访问,所以需要记录,哪些地方分配了

比如你申请了一块内存在0x12345678的位置大小为4字节,这些都要被记录总不能在这申请了下次还在这申请

在_EPROCESS.+ 0x11c VadRoot 这是一颗搜索二叉树,它里面的每一个节点都记录了一块被占用的线性地址空间。

VadRoot的类型为_MMVAD

kd> dt _MMVAD
nt!_MMVAD
   +0x000 StartingVpn      //当前节点线性地址起始位置(以页为单位)
   +0x004 EndingVpn        //当前节点线性地址结束位置(以页为单位)
   +0x008 Parent           //父节点
   +0x00c LeftChild        //左子树
   +0x010 RightChild       //右子树
   +0x014 u                //用于标识内存属性之类的
   +0x018 ControlArea      //控制区
   +0x01c FirstPrototypePte //指向原型PTE第一个 239
   +0x020 LastContiguousPte //指向所映射视图PTE最后一个
   +0x024 u2               
+0x018 ControlArea 控制区:

   +0x000 Segment          //指向段
   +0x004 DereferenceList  
   +0x00c NumberOfSectionReferences 
   +0x010 NumberOfPfnReferences
   +0x014 NumberOfMappedViews //反映关联的内存区对象被映射多少次
   +0x018 NumberOfSubsections //
   +0x01a FlushInProgressCount
   +0x01c NumberOfUserReferences
   +0x020 u
   +0x024 FilePointer             //文件指针
   +0x028 WaitingForDeletion
   +0x02c ModifiedWriteCount
   +0x02e NumberOfSystemCacheViews
 +0x024 FilePointer             文件指针:
 
如果这个值为null,当前MMVAD的这块内存就是一块物理页(文件映射来的),
如果不为null就是我们自己分配的内存
nt!_FILE_OBJECT
   +0x000 Type             
   +0x002 Size             
   +0x004 DeviceObject     
   +0x008 Vpb              
   +0x00c FsContext        
   +0x010 FsContext2       
   +0x014 SectionObjectPointer 
   +0x018 PrivateCacheMap  
   +0x01c FinalStatus      
   +0x020 RelatedFileObject 
   +0x024 LockOperation    
   +0x025 DeletePending    
   +0x026 ReadAccess       
   +0x027 WriteAccess      
   +0x028 DeleteAccess     
   +0x029 SharedRead       
   +0x02a SharedWrite      
   +0x02b SharedDelete     
   +0x02c Flags            
   +0x030 FileName         //这块内存对应的模块名称
   +0x038 CurrentByteOffset 
   +0x040 Waiters          
   +0x044 Busy             
   +0x048 LastLock         
   +0x04c Lock             
   +0x05c Event            
   +0x06c CompletionContext 

所有的内存都可以分成两类,一类为我们用VirtualAllocEx()申请的内存,
还有一类为文件映射,这个文件可能是.dll/.exe也可能是一个数据的文件

上面的都是自己一层一层的找,windbg也提供了一个指令可以帮你类出来所有的节点,
!vad 0xxxxx
+0x014 u                用于标识内存属性之类的

 union
    {
        ULONG_PTR LongFlags;
        MMVAD_FLAGS VadFlags; 一般只用这个成员
    } u;

nt!_MMVAD_FLAGS
   +0x000 CommitCharge     //最大可提供物理页的数目
   +0x000 PhysicalMapping  
   +0x000 ImageMap         //1:镜像文件 0:其他
   +0x000 UserPhysicalPages 
   +0x000 NoChange         
   +0x000 WriteWatch       
   +0x000 Protection       //当前内存属性(读?写?执行)
//1:READONLY  2:EXECUTE  3:EXECUTE _READ  4:READWITER  
//5:WRITECOPY  6:EXECUTE_READWITER   7:EXECUTE_WRITECOPY

   +0x000 LargePages       
   +0x000 MemCommit        
   +0x000 PrivateMemory    1:内存是Private 0:这块内存是Mapped

在这里插入图片描述
《Windows内核原理与实现》- 243页

Private Memory(私有内存)

通过VirtualAlloc() / VirtualAllocEx()申请的: Private Memory
void main()
{
	printf("程序运行了...按下回车开始申请内存");
	getchar();

	//参数1:要申请的地址
	//参数2:大小 (以页为单位)
	//参数3:MEM_COMMIT(创建节点也分配物理页)  MEM_RESERVE(只创建节点不分配物理页)
	//参数4:访问权限
	LPVOID address = VirtualAlloc(NULL, 0x2000, MEM_COMMIT, PAGE_READWRITE);

	printf("内存地址:%X\n", address);
	getchar();
}

申请前
在这里插入图片描述

申请后
在这里插入图片描述

堆内存与栈内存

int x = 0x1111;

void main()
{
	printf("按下回车开始申请内存");
	getchar();

	int y = 0x2222;
	int* z = (int*)malloc(sizeof(int)*128);

	printf("全局变量:%X\n", &x);
	printf("栈:%X\n", &y);
	printf("堆:%X\n", z);

	getchar();
}

堆就是系统提前替我们申请的一块很大的内存,相当于批发
当我们用的时候调用malloc(),就是从这些已经分配好的内存中拿出一块

无论是堆还是栈都是系统提前就用VirtualAlloc()分配好的内存,
全局变量是程序编译完了它就会有一块属于自己的地址空间,属于映射得到的

在这里插入图片描述

Mapped Memory(映射内存)

通过CreateFileMapping() 映射的: Mapped Memory

在这里插入图片描述

映射内存分两类

  1. 多个进程共享一个物理页面
  2. 多个进程共享一个文件

共享内存

A进程
void main()
{
	//创建物理页
	HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUFSIZ, L"共享内存");

	//将物理页与线性地址进行映射			
	LPTSTR lpBuff = (LPTSTR)MapViewOfFile(g_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFSIZ);
	
	*(PDWORD)lpBuff = 0x12345678;

	printf("A进程写入地址  内容:%p - %x ", g_lpBuff,*(PDWORD)g_lpBuff);

	getchar();
}

B进程
void main()
{
	HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,L"共享内存");

	LPTSTR buffer = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS,0,0,BUFSIZ);

	printf("B进程读取:%x", *(PDWORD)buffer);

	getchar();
}

在这里插入图片描述

共享文件

void main()
{
	HANDLE hFile = CreateFile(L"C:\\XueTr.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	
	HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, BUFSIZ, NULL);
	
	LPTSTR lpBuff = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFSIZ);
	
	printf("%x", lpBuff);

	getchar();
}

在这里插入图片描述
共享文件如果A进程改了该映射的文件,所有使用该文件的进程都会被影响。

镜像文件

HMODULE hModule = ::LoadLibrary("C:\\XueTr.exe");
  1. LoadLibrary 就是通过内存映射的方式实现的
  2. 为了避免影响到别人,属性为:写拷贝
    在这里插入图片描述

写拷贝:

修改该模块里面的数据:系统是不让你修改这个物理页的,会映射一份新的物理页。
你修改的数据会放到一个新的物理页上,你的线性地址会指向这个新的物理页。

不修改该模块里面的数据:多个进程都是指向同一个物理页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值