您的位置:首页 > 产品设计 > UI/UE

使用VirtualQuery查看内存页面信息

2010-12-29 09:22 211 查看
本文介绍了使用VirtualQuery函数查看进程内存地址空间的页面分配情况,并给出了一个例程。

1 Win32内存布局简介

在16位CPU的时代,受寻址范围的限制,系统所能使用的内存空间是非常少的。据说当年Bill Gates曾说过“640K内存对任何人来说都够用了”,现在看来似乎很好笑,不过做过DOS编程的人大概都还会怀念那个时代。后来有了386,有了保护模式,有了虚拟内存,多任务终于成为现实。

在Win32环境下,寻址不再需要遵照“段首址:偏移”的格式(当然这样也不算错),每个进程拥有独立的4GB地址空间,当然物理内存总是宝贵的,因此并不会给每个进程分配那么多空间。段寄存器不再用来存放段首址,而是存放所谓的选择子(Segment Selector),所有的段共用00000000到FFFFFFFF的4GB地址空间,内存分段概念已经过时了,至少在绝大多数应用中你不用和段寄存器打交道了。

4GB内存空间被分为用户空间(00000000到7FFFFFFF)和系统空间(80000000到FFFFFFFF),这两部分各占2GB,当然如果你愿意的话也可以把用户空间设为3GB系统空间设为1GB。用户空间的可访问范围是00010000到7FFEFFFF,这两个值是调用GetSystemInfo函数得到的,这个范围比上面提到的2GB小,是因为系统在两端预留了一部分空间作为“隔离带”。

本文中用VirtualQuery查看内存页面信息其实查看的就是从00010000到7FFEFFFF这段空间,这不足2GB的空间也被划分为不同的段落,用于存放可执行映像、全局数据、栈、堆、DLL等。

2 VirtualQuery与VirtualQueryEx

VirutalQuery提供某一段内存区域的页面信息。函数原型如下:
DWORD VirtualQuery(
LPCVOID lpAddress, // address of region
PMEMORY_BASIC_INFORMATION lpBuffer, // address of information buffer
DWORD dwLength // size of buffer
);

Windows给应用程序分配内存时是以页为单位的,页的大小通常是4KB。而VirtualQuery函数会给出lpAddress这个地址所在的页面以及与它相邻的具有相同属性的页面的信息,你可以通过查看MEMORY_BASIC_INFORMATION结构的内容来检索这些信息,MEMORY_BASIC_INFORMATION定义如下:
typedef struct _MEMORY_BASIC_INFORMATION { // mbi
PVOID BaseAddress; // base address of region
PVOID AllocationBase; // allocation base address
DWORD AllocationProtect; // initial access protection
DWORD RegionSize; // size, in bytes, of region
DWORD State; // committed, reserved, free
DWORD Protect; // current access protection
DWORD Type; // type of pages
} MEMORY_BASIC_INFORMATION;
其中,BaseAddress是lpAddress所在页面的基地址,AllocationBase是用VirtualAlloc函数分配此页面时的基地址,可以小于等于BaseAddress,原因是VirtualAlloc可以一次分配很多页。AllocationProtect是分配时的保护属性,比如只读、读写、可执行等,Protect是现在的保护属性。State可以取MEM_COMMIT,MEM_FREE,MEMRESERVE三者之一,其中只有MEM_COMMIT状态的页面是实际分配了物理内存的可以访问。Type描述有关页面共享的属性。用过金山游侠的都知道,我们在游戏内存中找数据时只需要搜索具有MEM_COMMIT和writable属性的页面。

VirtualQuery的局限是一次只能得到一组连续页面的信息,然而这样连续的页面通常有几十组甚至上百组之多。要查看完整的地址空间状况,需要循环调用VirtualQuery函数。

VirtualQueryEx和 VirtualQuery的用法一样,只是多了一个参数——进程句柄,因此你可以用它来查看其它进程的地址空间。Win32 API里还有很多像这样成对存在的函数。

3 程序实例

这个例程用一个循环搜索用户地址空间并打印出VirtualQuery得到的页面信息,用过CheatEngine的应该知道CheatEngine也有这个功能,我在写这个程序时参考了CheatEngine的源代码。

// meminfo.cpp, use VirtualQuery to get information about allocated memory pages
// VirtualQuery,在winbase.h中声明,引入库为kernel32.lib
// 本程序在WinXP,MinGW G++ 3.4.5测试通过
// 龙第九子 2008/05/03

#include <windows.h>
#include <stdio.h>
#include <string>

std::string FormatMemInfo(MEMORY_BASIC_INFORMATION &meminfo)
{
// this function gives you a human-readable formatted output of struct meminfo

// use local variables for short
PVOID base_address = meminfo.BaseAddress; // ***
PVOID alloc_base = meminfo.AllocationBase;
DWORD alloc_protect = meminfo.AllocationProtect;
DWORD region_size = meminfo.RegionSize; // ***
DWORD state = meminfo.State; // ***
DWORD protect = meminfo.Protect; // ***
DWORD type = meminfo.Type;

// format the AllocationProtect field
std::string s_alloc_protect;
if(alloc_protect & PAGE_NOACCESS) // 0x0001
s_alloc_protect = "NoAccess";
if(alloc_protect & PAGE_READONLY) // 0x0002
s_alloc_protect = "Readonly";
else if(alloc_protect & PAGE_READWRITE) // 0x0004
s_alloc_protect = "ReadWrite";
else if(alloc_protect & PAGE_WRITECOPY) // 0x0008
s_alloc_protect = "WriteCopy";
else if(alloc_protect & PAGE_EXECUTE) // 0x0010
s_alloc_protect = "Execute";
else if(alloc_protect & PAGE_EXECUTE_READ) // 0x0020
s_alloc_protect = "Execute_Read";
else if(alloc_protect & PAGE_EXECUTE_READWRITE) // 0x0040
s_alloc_protect = "Execute_ReadWrite";
else if(alloc_protect & PAGE_EXECUTE_WRITECOPY) // 0x0080
s_alloc_protect = "Execute_WriteCopy";
if(alloc_protect & PAGE_GUARD) // 0x0100
s_alloc_protect += "+Guard";
if(alloc_protect & PAGE_NOCACHE) // 0x0200
s_alloc_protect += "+NoCache";

// format the State field
std::string s_state;
if(state == MEM_COMMIT) // accessible, physical memory is allocated.
s_state = "Commit ";
else if(state == MEM_FREE) // unaccessible, AllocationBase, AllocationProtect, Protect, and Type are undefined.
s_state = "Free ";
else if(state == MEM_RESERVE) // unaccessible, Protect is undefined.
s_state = "Reserve";
else // this case is not expected to happen
s_state = "Damned ";

// format the Protect field
std::string s_protect;
if(protect & PAGE_NOACCESS)
s_protect = "NoAccess";
if(protect & PAGE_READONLY)
s_protect = "Readonly";
else if(protect & PAGE_READWRITE)
s_protect = "ReadWrite";
else if(protect & PAGE_WRITECOPY)
s_protect = "WriteCopy";
else if(protect & PAGE_EXECUTE)
s_protect = "Execute";
else if(protect & PAGE_EXECUTE_READ)
s_protect = "Execute_Read";
else if(protect & PAGE_EXECUTE_READWRITE)
s_protect = "Execute_ReadWrite";
else if(protect & PAGE_EXECUTE_WRITECOPY)
s_protect = "Execute_WriteCopy";
if(protect & PAGE_GUARD)
s_protect += "+Guard";
if(protect & PAGE_NOCACHE)
s_protect += "+NoCache";

// format the Type field
std::string s_type;
if(type == MEM_IMAGE)
s_type = "Image ";
else if(type == MEM_MAPPED)
s_type = "Free ";
else if(type == MEM_PRIVATE) // most cases
s_type = "Private";
else
s_type = "- ";

// final string
char buf[128] = {'/0'};
sprintf(buf, "%8X %8X %25s %7s %25s %7s %8X", base_address, alloc_base,
s_alloc_protect.c_str(), s_state.c_str(), s_protect.c_str(), s_type.c_str(), region_size);

return std::string(buf);
}

int main()
{
SYSTEM_INFO info;
GetSystemInfo(&info);
//DWORD pagesize = info.dwPageSize; // page size, usually 4k
DWORD lowerbound = (DWORD)info.lpMinimumApplicationAddress; // starting address, normally 0x10000
DWORD upperbound = (DWORD)info.lpMaximumApplicationAddress; // end address, normally 0x7FFEFFFF

MEMORY_BASIC_INFORMATION meminfo;
DWORD ptr = lowerbound;
while(ptr <= upperbound)
{
if(VirtualQuery((void*)ptr, &meminfo, sizeof(meminfo)) == 0) // if the queried address unaccessible
break;

std::string s_meminfo = FormatMemInfo(meminfo); // format
puts(s_meminfo.c_str()); // output

int oldptr = ptr;
ptr = (DWORD)meminfo.BaseAddress + meminfo.RegionSize;
if(ptr <= oldptr) // prevent unendable iteration
break;
}

return 0;
}

参考资料:
1 《Windows汇编语言程序设计教程》电子工业出版社 2005.4
2 Win32 Developer's References, by Microsoft
3 从进程中获取QQ号码 http://blog.163.com/dave22@126/blog/static/238654112007812104236800/ 4 CheatEngine 5.4源代码 http://www.cheatengine.org/downloads/CheatEngine54src.rar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: