:
endl;
}
步骤4:
单击“Build”菜单中的“Compile5-1.cpp”命令,并单击“是”按钮确认。
系统对5-1.cpp进行编译。
步骤5:
编译完成后,单击“Build”菜单中的“Build5-1.exe”命令,建立5-1.exe可执行文件。
操作能否正常进行?
如果不行,则可能的原因是什么?
_不可以运行,#pragmacomment(lib,"shlwapi.lib")指令错误,在pragma与comment中间应该有空格___________________________________________________________________
________________________________________________________________________
步骤6:
在工具栏单击“ExecuteProgram”(执行程序)按钮,执行5-1.exe程序。
运行结果(分行书写。
如果运行不成功,则可能的原因是什么?
):
1)虚拟存每页容量为:
4.00KB
2)最小应用地址:
0*00010000
3)最大应用地址:
0*7ffeffff
4)当前可供应用程序使用的存空间为:
2.00GB
5)当前计算机的实际存大小为:
1.99GB
1)虚拟存每页容量为:
2)最小应用地址:
3)最大应用地址为:
4)当前可供应用程序使用的存空间为:
5)当前计算机的实际存大小为:
阅读和分析程序5-1,请回答问题:
1)理论上每个windows应用程序可以独占的最大存储空间是:
____4GB____
2)在程序5-1中,用于检索系统中虚拟存特性的API函数是:
DWORD
提示:
可供应用程序使用的存空间实际上已经减去了开头与结尾两个64KB的保护区。
虚拟存空间中的64KB保护区是防止编程错误的一种Windows方式。
任何对存中这一区域的访问(读、写、执行)都将引发一个错误陷井,从而导致错误并终止程序的执行。
也就是说,假如用户有一个NULL指针(地址为0),但仍试图在此之前很近的地址处使用另一个指针,这将因为试图从更低的保留区域读写数据,从而产生意外错误并终止程序的执行。
[2]WindowsXp虚拟存
背景知识
在WindowsXp环境下,4GB的虚拟地址空间被划分成两个部分:
低端2GB提供给进程使用,高端2GB提供给系统使用。
这意味着用户的应用程序代码,包括DLL以及进程使用的各种数据等,都装在用户进程地址空间(低端2GB)。
用户过程的虚拟地址空间也被分成三部分:
1)虚拟存的已调配区(committed):
具有备用的物理存,根据该区域设定的访问权限,用户可以进行写、读或在其中执行程序等操作。
2)虚拟存的保留区(reserved):
没有备用的物理存,但有一定的访问权限。
3)虚拟存的自由区(free):
不限定其用途,有相应的PAGE_NOACCESS权限。
与虚拟存区相关的访问权限告知系统进程可在存中进行何种类型的操作。
例如,用户不能在只有PAGE_READONLY权限的区域上进行写操作或执行程序;也不能在只有PAGE_EXECUTE权限的区域里进行读、写操作。
而具有PAGE_NOACCESS权限的特殊区域,则意味着不允许进程对其地址进行任何操作。
在进程装入之前,整个虚拟存的地址空间都被设置为只有PAGE_NOACCESS权限的自由区域。
当系统装入进程代码和数据后,才将存地址的空间标记为已调配区或保留区,并将诸如EXECUTE、READWRITE和READONLY的权限与这些区域相关联。
如表3-2所示,给出了MEMORY_BASIC_INFORMAITON的结构,此数据描述了进程虚拟存空间中的一组虚拟存页面的当前状态,期中State项表明这些区域是否为自由区、已调配区或保留区;Protect项则包含了windows系统为这些区域添加了何种访问保护;type项则表明这些区域是课执行图像、存映射文件还是简单的私有存。
VirsualQueryEX()API能让用户在指定的进程中,对虚拟存地址的大小和属性进行检测。
Windows还提供了一整套能使用户精确控制应用程序的虚拟地址空间的虚拟存API。
一些用于虚拟存操作及检测的API如表3-2所示。
表3-1MEMORY_BASIC_INFORMAITON结构的成员
成员名称
目的
PVOIDBaseAddress
虚拟存区域开始处的指针
PVOIDAllocationBase
如果这个特定的区域为子分配区的话,则为虚拟存外面区域的指针;否则此值与BaseAddress相同
DWORDAllocationProtect
虚拟存最初分配区域的保护属性。
其可能值包括:
PAGE_NOACCESS,PAGE_READONLY,PAGE_READWRITE和PAGE_EXECUTE_READ
DWORDRegionSize
虚拟存区域的字节数
DWORDState
区域的当前分配状态。
其可能值为MEM_COMMIT,MEM_PREE和MEM_RESERVE
DWORDProtect
虚拟存当前的保护属性。
可能值与AllocationProtect成员的相同
DWORDType
虚拟存区域中出现的页面类型。
可能值为MEM_IMAGE,MEM_MAPPED和MEM_PRIVATE
表3-2虚拟存的API
API名称
描述
VisualQueryEX()
通过填充MEMORY_BASIC_INFORMATION结构检测进程虚拟存的区域
VisualAlloc()
保留或调配进程的部分虚拟存,设置分配和保护标志
VirsualFree()
释放或收回应用程序使用的部分虚拟地址
VirsualProtect()
改变虚拟存区域保护规
VirsualLock()
防止系统将虚拟存区域通过系统交换到页面文件中
VirsualUnlock()
释放虚拟存的锁定区域,必要时,允许系统将其交换到页面文件中
提供虚拟存分配功能的是VirtualAlloc()API。
该API支持用户向系统要求新的虚拟存或改变已分配存的当前状态。
用户若想通过VirtualAlloc()函数使用虚拟存,可以采用两种方式通知系统:
1)简单地将存容保存在地址空间
2)请求系统返回带有物理存储区(RAM的空间或换页文件)的部分地址空间
用户可以用flAllocationType参数(commit和reserve)来定义这些方式,用户可以通知Windows按只读、读写、不可读写、执行或特殊方式来处理新的虚拟存。
与VirtualAlloc()函数对应的是VirtualFree()函数,其作用是释放虚拟存中的已调配页或保留页。
用户可利用dwFreeType参数将已调配页修改成保留页属性。
VirtualProtect()是VirtualAlloc()的一个辅助函数,利用它可以改变虚拟存区的保护规。
实验目的
1)通过实验了解WindowsXp存的使用,学习如何在应用程序中管理存,体会Windows应用程序存的简单性和自我防护能力。
2)学习检查虚拟存空间或对其进行操作。
3)了解WindowsXp的存结构和虚拟存的管理,进而了解进程堆和Windows为使用存而提供的一些扩展功能。
工具/准备工作
在开始本实验之前,请回顾教科书的相关容。
您需要做以下准备:
1)一台运行WindowsXpProfessional操作系统的计算机。
2)计算机中需安装VisualC++6.0专业版或企业版。
实验容与步骤
1.虚拟存的检测
清单5-2所示的程序使用VirtualQueryEX()函数来检查虚拟存空间。
步骤1:
登录进入WindowsXpProfessional。
步骤2:
在“开始”菜单中单击“程序”-“MicrosoftVisualStudio6.0”–“MicrosoftVisualC++6.0”命令,进入VisualC++窗口。
步骤3:
在工具栏单击“打开”按钮,在“打开”对话框中找到并打开实验源程序5-2.cpp。
清单5-2检测进程的虚拟地址空间
//工程vmwalker
#include
#include
#include
#include
#pragmacomment(lib,"Shlwapi.lib")
//以可读方式对用户显示保护的辅助方法。
//保护标记表示允许应用程序对存进行访问的类型
//以及操作系统强制访问的类型
inlineboolTestSet(DWORDdwTarget,DWORDdwMask)
{
return((dwTarget&dwMask)==dwMask);
}
#defineSHOWMASK(dwTarget,type)\
if(TestSet(dwTarget,PAGE_##type))\
{std:
:
cout<<","<<#type;}
voidShowProtection(DWORDdwTarget)
{
SHOWMASK(dwTarget,READONLY);
SHOWMASK(dwTarget,GUARD);
SHOWMASK(dwTarget,NOCACHE);
SHOWMASK(dwTarget,READWRITE);
SHOWMASK(dwTarget,WRITECOPY);
SHOWMASK(dwTarget,EXECUTE);
SHOWMASK(dwTarget,EXECUTE_READ);
SHOWMASK(dwTarget,EXECUTE_READWRITE);
SHOWMASK(dwTarget,EXECUTE_WRITECOPY);
SHOWMASK(dwTarget,NOACCESS);
}
//遍历整个虚拟存并对用户显示其属性的工作程序的方法
voidWalkVM(HANDLEhProcess)
{
//首先,获得系统信息
SYSTEM_INFOsi;
:
:
ZeroMemory(&si,sizeof(si));
:
:
GetSystemInfo(&si);
//分配要存放信息的缓冲区
MEMORY_BASIC_INFORMATIONmbi;
:
:
ZeroMemory(&mbi,sizeof(mbi));
//循环整个应用程序地址空间
LPCVOIDpBlock=(LPVOID)si.lpMinimumApplicationAddress;
while(pBlock{
//获得下一个虚拟存块的信息
if(:
:
VirtualQueryEx(
hProcess,//相关的进程
pBlock,//开始位置
&mbi,//缓冲区
sizeof(mbi))==sizeof(mbi))//大小的确认
{
//计算块的结尾及其大小
LPCVOIDpEnd=(PBYTE)pBlock+mbi.RegionSize;
TCHARszSize[MAX_PATH];
:
:
StrFormatByteSize(mbi.RegionSize,szSize,MAX_PATH);
//显示块地址和大小
std:
:
cout.fill('0');
std:
:
cout
<:
hex<:
setw(8)<<(DWORD)pBlock
<<"-"
<:
hex<:
setw(8)<<(DWORD)pEnd
<<(:
:
strlen(szSize)==7?
"(":
"(")<<<")";
//显示块的状态
switch(mbi.State)
{
caseMEM_COMMIT:
std:
:
cout<<"Committed";
break;
caseMEM_FREE:
std:
:
cout<<"Free";
break;
caseMEM_RESERVE:
std:
:
cout<<"Reserved";
break;
}
//显示保护
if(mbi.Protect==0&&mbi.State!
=MEM_FREE)
{
mbi.Protect=PAGE_READONLY;
}
ShowProtection(mbi.Protect);
//显示类型
switch(mbi.Type){
caseMEM_IMAGE:
std:
:
cout<<",Image";
break;
caseMEM_MAPPED:
std:
:
cout<<",Mapped";
break;
caseMEM_PRIVATE:
std:
:
cout<<",Private";
break;
}
//检验可执行的影像
TCHARszFilename[MAX_PATH];
if(:
:
GetModuleFileName(
(HMODULE)pBlock,//实际虚拟存的模块句柄
szFilename,//完全指定的文件名称
MAX_PATH)>0)//实际使用的缓冲区大小
{
//除去路径并显示
:
:
PathStripPath(szFilename);
std:
:
cout<<",Module:
"<}
std:
:
cout<:
endl;
//移动块指针以获得下一下个块
pBlock=pEnd;
}
}
}
voidmain()
{
//遍历当前进程的虚拟存
:
:
WalkVM(:
:
GetCurrentProcess());
}
清单5-2中显示一个walkVM()函数开始于某个进程可访问的最低端虚拟地址处,并在其中显示各块虚拟存的特性。
虚拟存中的块由VirsualQueryEX()API定义成连续快或具有相同状态(自由区,已调配区等)的存,并分配以一组统一的保护标志(只读、可执行等)。
步骤4:
单击“Build”菜单中的“Compile5-2.cpp”命令,并单击“是”按钮确认。
系统对5-2.cpp进行编译。
步骤5:
编译完成后,单击“Build”菜单中的“Build5-2.exe”命令,建立5-2.exe可执行文件。
操作能否正常进行?
如果不行,则可能的原因是什么?
___能运行_________________________________________________________________
________________________________________________________________________
步骤6:
在工具栏单击“ExecuteProgram”(执行程序)按钮,执行5-2.exe程序。
1)分析运行结果(如果运行不成功,则可能的原因是什么)
按committed,reserved,free等三种虚拟地址空间分别记录实验数据,其中“描述”是对该组数据的简单描述,例如,对下列一组数据:
00010000-0001Xp<8.00KB>Committed,READWRITE,Private可描述为:
具有READWRITE权限的已调配私有存区。
将系统当前的自由区(Free)虚拟地址空间填入表3-3中。
地址
大小
虚拟地址
空间类型
访问权限
描述
00010000-00012000
8.00KB
free
READWRITE
具有READWRITE权限的已调配私有存区
00012000-00020000
56.0KB
free
NOACCESS
没有任何权限的已调配的共有存区
00020000-00021000
4.00KB
free
READWRITE
具有READWRITE权限的已调配私有存区
00021000-00030000
60.0KB
free
NOACCESS
没有任何权限的已调配的共有存区
00030000-0012c000
0.98MB
free
READONLY
只具有READ权限的已调配私有存区
0012c000-0012d000
4.00KB
free
READWRITE
具有READWRITE权限的已调配私有存区
0012d000-00130000
12.0KB
free
READWRITE
具有READWRITE权限的已调配私有存区
00130000-00133000
12.0KB
free
READONLY
只具有READ权限的已调配存区
表3-3实验记录
地址
大小
虚拟空间类型
访问权限
描述
free
free
free
free
free
free
free
将系统当前的已调配区(Committed)虚拟地址空间填入表3-4中。
表3-4实验记录
00010000-00020000(64.0KB)Committed,READWRITE,Mapped
00020000-00030000(64.0KB)Committed,READWRITE,Mapped
0012d000-0012e000(4.00KB)Committed,GUARD,READWRITE,Private
0012e000-00130000(8.00KB)Committed,READWRITE,Private
00130000-00134000(16.0KB)Committed,READONLY,Mapped
00140000-001a7000(412KB)Committed,READONLY,Mapped
001b0000-001b1000(4.00KB)Com