1.线性地址的管理

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

线性地址有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. 为了避免影响到别人,属性为:写拷贝
    在这里插入图片描述

写拷贝:

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 内存的编址方式是通过地址编码来描述内存位置的一种方式。 2. CPU 寻址是指 CPU 在内存中搜索指定地址的过程。 3. 程序的逻辑地址是指程序中变量或参数的地址,它是指程序的相对位置而不是绝对位置。 4. 程序的线性地址是指程序在内存中的实际位置,它是绝对地址。 5. 程序的物理地址是指程序在物理内存中的位置,它是绝对地址。 6. 程序的地址重定位是指将程序的逻辑地址转换为物理地址的过程。 7. 程序的链接是指将各个模块组合在一起,以形成一个完整程序的过程。 8. 存储器管理程序的功能包括内存分配、收回和保护。 9. 定长的连续内存分配是指把程序分成相同大小的内存块,然后连续分配给程序的过程。 10. 不定长的连续内存分配是指把程序按实际需要分配不同大小的内存块,然后连续分配给程序的过程,常用的算法有最佳适配算法和最先适配算法。 11. 分段式内存分配是把程序分成若干段,根据段的长度和大小来分配内存的过程。 12. 分页式内存分配是把程序分成若干页,根据页的大小来分配内存的过程。1) 分层页表的工作原理是通过多级页表来提高内存的寻址速度,优点是提高了内存的寻址速度,缺点是需要更多的内存空间来存储页表。2) 哈希页表的工作原理是建立哈希表来存储页面的位置,优点是减少了内存的查找时间,缺点是建立哈希表非常耗时。3) 倒置页表的工作原理是建立一个倒置表,即物理地址对应逻辑地址,优点是可以提高内存寻址的速度,缺点是缺乏灵活性。 13. 段页式内存分配是把程序以段和页的形式分配内存,以便更好地使用内存空间。 14. TLB(快表)是一个特殊的硬件,它可以加快程序的内存寻址速度,它可以存储页表中的常用项,以便快速查找。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值