版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
页以及页内偏移是多少这三样信息!DWORDg_var;0x那么这条赋值语句编译后对应的汇编语句为:movDWORD 0xVAmov寻址过程为:CPUMMU0x转换为物理地址movDWORD { PDE=总页表[vaPDE二级页表=PDE.PageAddr;//得出本二级页表的地址 If(PTE空白)//PTE触发0x0e号页面异常(具体为缺页异常)触发0x0e号页面异常(具体为缺页异常)If(CR0.wp==1&&PTE.Writable==false)发0x0e号页面异常(具体为页面保护越权异常)pacs.basePTE.PageAddrva.页内偏移//得出对应的物理地址}上面的过程比较简单,由于每次内存都要先一次PTE获取该虚拟页面对应的物理页面,再物理页面读得对应的数据,因此实际问了两次物理内存,如果类似于每条这样的Mov指令都要物理内存两次,才能获得数据,效率就很低。因此,cpu中专门开辟了一个二级缓冲,用来保存那些频繁另外有一个问题需要说明下:va>pava->la->pa,PTE.PageAddr的址都在0x处,长度为4GB,也即相当于Windows没有采取分段机制。前面讲过,cs是GDTPTESSDT、IDTCRO中的wp即写保护位,这样就可以修改了)StructMADDRESS_SPACE//地址空间描述符{MEMORY_AREA*MemoryRoot;//本地址空间的已分配区段表(AVL EPROCESS*Process;//USHORT*PageTableRefCountTable;}地址空间中所有已分配的区段都记录在一张表中,这个表不是简单的数组,而是一个AVL找效率。每个区段的基址都对齐64KB4KB(64KB整倍数),各个区段之间可以有空隙,Struct {Void*StartingAddress;64KB4KBMEMORY_AREA*Parent;//AVL树中的父节点MEMORY_AREA*LeftChild;//左边的子节点MEMORY_AREA*RightChild;//ULONGtype;//本区段的类型ULONGflags;//当初分配本区段时的分配标志ULONG{{ROS_SECTION_OBJECT*MM_SECTION_SEGMENT*SegmentMEMORY_AREA_PEB_OR_TEB://用于PEB、TEB的区段MEMORY_AREA_MDL_MAP://内核中于建立MDL映射的区段StructMM_REGION//区块描述符{ULONGprotect;//本区块的保护权限,可读、可写、可执行的组合ULONGlength;//区块长度,对齐页面大小(4KB)LIST_ENTRYRegionListEntry;//用来挂入所在区段的表} MmLocateMemoryAreaByAddress(MADDRESS_SPACE*as,void*MmFindGap(MADDRESS_SPACE*as,ULONGlen,ULONGAlignGranularity,BOOLMmLocateMemoryAreaByRegion(MADDRESS_SPACE*as,void*addr,ULONGMmCreateMemoryArea(MADDRESS_SPACE*as,type,void**BaseAddr,Len,protect,bFixedAddr, MEMORY_AREA**Result){UINTBaseAlign;//区段的基址对齐粒度BaseAlignIf(*BaseAddrNULL&&!bFixedAddr)//if{*BaseAddr=MmFindGap(as,Len,BaseAlign,AllocFlags}{*BaseAddr=Align(*BaseAddr,Returnfail;Return}Memory_Area*Area=ExAllocatePool(NonPagePool,sizeof(*Area),tag);树Returnsucc;}谓分配,包含reserve型分配(即预定型分配)commit型分配(即提交型分配)MmFindRegion(void*AreaBaseAddr,LIST_ENTRY*RegionListHead,void*TgtAddr,Void**RegionBaseAddr)MmSplitRegion(MM_REGION*rgn,BaseAddr,StartAddr,Len,NewType,NewProtectMmAlterRegion(AreaBaseAddr, NewType,NewProtect,PHYSICAL_PAGEMmPageArray[];//LIST_ENTRYZeroedPageListHead;//空闲物理页面链表(且物理页面已清0)LIST_ENTRYUnzeroedPageListHead;//空闲物理页面链表(但物理页面尚未清0)物理页号,又叫pfn),每个描述符是一个PHYSICAL_PAGE结构体StructPHYSICAL_PAGE//物理页面描述{Consumer;//该物理页面的消费用途(用户/内核分页池/内核页池/文件缓冲四种)Zero;//标志本页面是否已清0ReferenceCount;//计数,一旦减到0,页面就变为空闲状态,进入空闲链表SWAPENTRYSavedSwapEntry;//对应的来源页文件,用于置换,一般为空MapCount;//同一个物理页面可以映射到N个进程的N个虚拟页面MM_RMAP_ENTRY*RmapListHead;//本物理页面映射给的那些虚拟页面,组成的链表}起初处于空闲并清0的状态,然后应内存分配要求分配给4个消费者之一,同时,将该物理内存紧张,被迫释放换到外存,而重新进入空闲状态,但此时尚未清0,将进入物理内存清0,转入ZeroedPageList链表,等候下次被分配。如此周而复返…MmAllocPage(ULONG{PFN_NUMBERPfn;//物理页号BOOLEANNeedClearFALSE;//是否需要if(ZeroedPageList链表为空){}
if(UnzeroedPageList为空return0;//如果两个空闲链表都为空就失败PageDescriptor=MiRemoveHeadList(&MmPageListHead);NeedClear=TRUE;PageDescriptor=PageDescriptor->ReferenceCount=1;//刚分配的物理页面的计数为1if(NeedClear)PfnPageDescriptor-MmPageArray;//pfn=数组的索引,就是物理页号returnPfn;}MmRequestPageMemoryConsumer(consumer,PFN*{{Call对应消费者的自我页面修剪函数}{{*pfn=Return}{Returnsucc;}}*pfn=}NTSTATUSMmReleasePageMemory(consumer,{pfn.ReferenceCount--;//递减本页面的计数{将这个页面挂入系统UnzeroedPageList链表}}1(已分配且已映射4、奔(尚未分配,以上情况都不满足2繁的那些虚拟页面映射着物理页面(最频繁的那些虚拟页面就构成了一个进程的工作集访(自一个面读ip到理 另外,32位系统中每个进程有1024个二级页表外加一个页 。咋一看,似乎系统中有1025个页表维持着 。这样,系统中实际上共有1024个二级页表(包括页 #defineADDR_TO_PDE_OFFSET(addr) (v/(1024*4kb))#defineADDR_TO_PAGE_TABLE(addr) 页PDEPDE=0,就表示那个二级页表尚未分配,4GB地址空间,因此,页中的绝大多数PDE都是空的,实际的二级页面个数往往很少。每个虚拟页面的映射描述符(PTE)的位置是固定的,根据虚拟页号可以自然算出那个虚拟页面的映射映射描述符是页表的,现在看一下PTE它的结构。PTE4B,Struct{{{BoolbPresent;//重点字段,表示该虚拟页面是否映射到了物理内存BoolbWritable;//表示这个虚拟页面是否可写BoolbUserBoolbReaded;//表示本虚拟页面自从上次置换到内存后是否曾被读过BoolbDirty;//表示本虚拟页面自从上次置换到内存后是否曾被写过UINTpfn;//关键字段,表示本虚拟页面对应的物理页号{}}MmCreateVirtualMap(process,FirstVirtualPageAddr, {If(VirtualPageCount!=PfnCount)Returnfail;Void*CurPageAddr=FirstVirtualPageAddr;//当前虚拟页面的地址PTE*Pt;//当前虚拟页面的PTE在二级页表中对应的位置For(inti=0;i<VirtualPageCount;i{OldPte*PtPTEIf(OldPte映射到了页文件)return=Process.地址空间.PageTableRefCountTable[ADDR_TO_PAGE_TABLE(CurPageAddr)If(OldPte映射到了某物理内存页面)}}MmDeleteVirtualMap(process,PageAddr,bPhysicalPage,BOOL*bDirty,PFN*{PTEProcess.地址空间.PageTableRefCountTable[ADDR_TO_PAGE_TABLE(CurPageAddr)If(Process.地址空间.PageTableRefCountTable[ADDR_TO_PAGE_TABLE(CurPageAddr)0)}但是,有时候,我们明明已经知道了某个东西固定在物理内存条某处,假如系统时间的值固定存放在物理内存条的物理地址0x #define Void*MmCreateHyperspaceMap{For(i=pfn%1024;i<1024;i++,Pte++)//先遍历后面的那些PTE{If(*pte{}}{For(i=0;i<pfn%1024;i++,Pte++){If(*pte{}}//end}//endReturnHYPERSPACE+}就是要找到这个虚拟页面的PTE映射描述符,那么如何查找呢?#definePAGETABLE_MAP如前文所述,每个进程的页表区都实弹的占据着对应的物理内存,系统为了方便,把每个进程的页表区都事先固定映射到了虚拟地址0xC ……#definePAGEDIR_MAP(PAGETABLE_MAP+ PAGEDIR_MAP=PAGETABLE_MAP+idx*=PAGETABLE_MAP+=PAGETABLE_MAP+(PAGETABLE_MAP/(1024*4kb))*=PAGETABLE_MAP+ #defineADDR_TO_PDE(PageAddr)PAGEDIR_MAPPageAddr/(1024*1024)//直接推算PDE#defineADDR_TO_PTE(PageAddr)PAGETABLE_MAPPageAddr/1024PTEPTE*MmGetPageTableForProcess(process,{ULONGPDE_IDX=ADDR_TO_PDE_OFFSET(PageAddr);//计算该虚拟页面的映射描述符在哪个二级页表中PDE*PageDir;//页 {PFNpfn=process.pcb.DirectoryTableBase;//获得那个进程的页 ReturnNULL;//若整个二级页面尚未分配,返回NULL PTE*pte=MmCreateHyperspaceMap(Pfn);//再临时映射二级页表本身,以便Returnpte+ADDR_TO_PTE_OFFSET(PageAddr);//OK,}{ }}前面,各个进程的用户地址空间是私有的,各不相同的,内核地址空间部分则几乎完全相同,为什么MmUpdatePageDir(process,每当内核地址空间中的某组页面的映射发生变化,系统就会调用这个函数将内核地址空间中从对应页表中,这样,就使得每个进程的内核页面映射都相同,落到同一个物理页面或者文件页面中。但虚拟页面由每个进程自己单独映射,各不相同。Void*Kernel32.VirtualAlloc(void*BaseAddr,Len,AllocType,{Void*NTDLL.NtVirtualAlloc(&addr,Len,AllocType,{Mov{…NtAllocateVirtualMemory(hCurProcess,&BaseAddr,&Len,AllocType, }Return}Return}NtAllocateVirtualMemory(hProcess,void**BaseAddr,int*Len,AllocType,{ReturnEPROCESS*process;//该进程对象的内核结构Type=(AllocType& MADDRESS_SPACE*As=process->VadRoot;//VadRoot表示该进程的用户地址空间If(*BaseAddr!=NULL)//if用户给定了分配的起始地址,必须从那儿分配{{AreaLen=Area->EndAddress–Area-If(AreaLen>={MmAlterRegion(As,Area->StratingAddr,Area->表,*BaseAddr,*Len,Type,protectReturn}Return}//end}//endReturnsucc;}//end注意,上面函数分配的区段尚未建立映射,既没有映射到物理内存,也没有映射到页文件,但是,该区段已经VLPEpu常不管是缺页异常还是越权异常,都叫页面异常。一旦发生异常,cpu自动从当前cpu的IDT[异常号{……}NTSTATUSMmAccessFault(boolbProtect,MemoryAddr,Mode,void*{ReturnMmpAccessFault(Mode,MemoryAddr,TrapInfo?TRUE:FALSE);ReturnMmNotPresentFault(Mode,MemoryAddr,}{{MemoryArea=MmLocateMemoryAreaByAddress(AddressSpace,//如果一个页面尚未映射,那么它的PTE==0,这种情况缺页异常,如果该地址落在了一个已经if(MemoryArea==NULL||MemoryArea->DeleteInProgress)return(STATUS_ACCESS_VIOLATION);switch(MemoryArea-{caseMEMORY_AREA_PAGED_POOL://分页池中的区段Status= Status=MmNotPresentFaultSectionView(AddressSpace,MemoryArea,Address);Status=MmNotPresentFaultVirtualMemory(AddressSpace,MemoryArea,Address);}}while(Status==}{NTSTATUSwin32ExcepCode;//由cpuwin32异常码If(Region->Type==MEM_RESERVE||Region->Protect==PAGE_NO_ACCESS){returnwin32ExcepCode;}等待那个线程处理完缺页异常,returnsucc;{}MmInsertRmap(pfn,AddressSpace->process,Align(Address,4kb));Return}NTSTATUS{MDL…FileOffset=SwapEntry.PageNo*4kb;if(Status==STATUS_PENDING){KeWaitForSingleObject(&Event,Executive,KernelMode,//看到没,Status=}…Return}存后才返回原处,继续执行。但是KeWaitForSingleObject这个函数,如果是要等待的话,只能运行在就会因为KeWaitForSingleObject的问题而。DDKCallersofKeWaitForSingleObjectmustberunningatIRQL<=DISPATCH_LEVEL.However,ifTimeout=NULLor*Timeout!=0,thecallermustberunningatIRQL<=APC_LEVELandinanonarbitrarythreadcontext.”TimeoutNULL&&*Timeout==0的情况下,才可以在DISPATCH_LEVEL开始发生置换操作,而是物理内存快要用完(小于64个页面)时,系统就开始着手置换操作了。MmPageOutVirtualMemory(MADDRESS_SPACE*as,MEMORY_AREA*Area,{PTEPFNSavedSwapEntry=pfn.SavedSwapEntry;If(pte.bDirtyfalse)//如果该页面未脏,那好办{MmDeleteVirtualMap(cess,PageAddr,…);//删除该虚拟页面对应的原PTEIf(SavedSwapEntry0if{//将该虚拟页面对应的PTE重定向映射到原先的页文件中的那个页面MmCreatePageFileMap(cess,PageAddr,SavedSwapEntry);Pfn.SavedSwapEntry=0;}Returnsucc;}{NewSwapEntry=MmAllocSwapPage();//从磁盘上的页文件中分配一个文件页面MmDeleteVirtualMap(cess,PageAddr,…);//删除该虚拟页面对应的原PTEMmCreatePageFileMap(cess,PageAddr,NewSwapEntry);//重定向Pfn.SavedSwapEntry=0;}}{{{While(5)}{{}}}//end}看下典型的User消费者是如何修剪自己的物理页面的MmTrimUserMemory(ToTrimPageCount,ULONG*{While(pfn!=0&&{}Return}NTSTATUS{MemoryArea=MmLocateMemoryAreaByAddress(AddressSpace,{Return}Elseif(MemoryArea->Type{…}}当一个文件映射到虚拟内存后,一读写对应的虚拟内存,势必缺页异常,系统的缺页异常处理函数自动处理,把文件页面调入读入物理内存。这样,就间接地对文件进行了IO。Struct{CSHORTtype;//CSHORTsize;//本结构体的实际长度(结构体后面经常可以衔接其他数据,size包含了那部分的长度ULONGprotect;//sectionULONGLONGMaxSize;//sectionULONGAllocationAttributes;//包含了本section的文件类型FILE_OBJECT*FileObject;//section的那个文件对象(文件句柄){MM_SECTION_SEGMENT*Segment;//数据文件section中的唯一segmentMM_IMAGE_SECTION_OBJECT*ImageSegments;//Segment数组segment,对应PE文件中的每个“节”,如.TEXT节,.DATA节,.RSRC节struct{ULONG_PTRULONG_PTR ULONG_PTRUSHORTUSHORTImageCharacteristics;USHORTMachine;BOOLEANULONGNrSegments;ImageSection中的segmentULONGPE文件头的节表区中每个节的格式定义为://参考《WindowsPE指南》一Struct{BYTEName[IMAGE_SIZEOF_SHORT_NAME=8];//8个字节的节名如".text" DWORDVirtualSize;//该节未对齐前的原始数据大小DWORDVirtualAddress;//该节的RVADWORDSizeOfRawData;//该节的FAS也即文件对齐大小,一般指对齐512B后的大小DWORDPointerToRawData;FOA,即文件偏移DWORDPointerToRelocations;//于obj文件DWORDPointerToLinenumbers;//用于调试 //于obj文 DWORDCharacteristics;该节的属性(可读、可写、可执行、可共享、可丢弃、可分页等属性}Characteristics 该节中包含有已初始化的数据如.dataT_UNINITIALIZED_DATA该节中包含有尚未初始化的数据,如.bss.data?_MEM_NOT_CACHED 该节交换到页文件中,sys文件中的节(除.page)都不可换_MEM_SHAREDdll中的共享节。也即表示本节是否允许写复_MEM_EXECUTE_MEM_READ_MEM_WRITEStruct{ ULONGRawLength;//本节在文件中的原始实际长度ULONGLength;//本节对齐后的长度(4KB)ULONGprotect;//可读、可写、可执行这些保护属性ULONGReferenceCount;ULONGFlags;ULONG }这个文件,映射这个文件section就可以了。NtCreateSection(hFile,HANDLE*hSection,DesiredAccess,ObjectAttribute,MaxSize,protect,{基本参数检查MmCreateSection(hFile,&SectionObject,DesiredAccess,ObjectAttribute,MaxSize,protect,AllocAttr);ObInsertObject(SectionObject,…,hSection);//将对象插入对象 }MmCreateSection(hFile,ROS_SECTION_OBJEC**SectionObject, DesiredAccess,ObjectAttribute, protect,AllocAttr,){If(AllocAttr&SEC_IMAGE)//ifhFileIf(hFile!=NULL)//创建普通数据文件}在此不详述,内容参考《WindowsPE指南》}}{FILE_OBJECT*FileObject;MM_SECTION_SEGMENT*Segment;ROS_SECTION_OBJECT*Section;ObCreateObject(MmSectionObjectType,ObjectAttribute,sizeof(ROS_SECTION_OBJECT),&Section,*SectionObject=If(MaxSize>Segment=ExAllocatePool(NonPagePool,sizeof(MM_SECTION_SEGMENT));Segment->Flags=MM_DATAFILE_SEGMENT;//标志这是普通数据文件中的一个segmentSegment->VirtualAddress=0;//rva=0Section->MaxSize=MaxSize;//记录本section对象的长度 }【section.segment.视图.页面】{
AllocType,protect,hProcess,void**BaseAddrIf(PreviousMode==UserMode)ObReferenceObjectByHandle(hSectionSection);//获得对应的对象{
,AllocType,protect,hProcess,void**BaseAddr起作用,将自动把每个完整segment当做一个视图来映射。If(Section->AllocationAttribute&{ULONGULONGULONG_PTRULONGPMM_SECTION_SEGMENTSectionSegments;ImageSectionObject=Section->ImageSection;SectionSegmentsImageSectionObject->Segments;//节数组NrSegmentsImageSectionObject->NrSegments;//该pe文件中的节数ImageBase=(ULONG_PTR)*BaseAddress;if(ImageBase==ImageBase=ImageSectionObject->ImageBase;ImageSize=0;for(i=0;i<NrSegments;i++){ {ULONG_PTRMaxExtent=SectionSegments[i].VirtualAddressSectionSegments[i].Length;ImageSize=max(ImageSize,MaxExtent);}}ImageSectionObject->ImageSize=if(MmLocateMemoryAreaByRegion(AddressSpace,ImageBase,PAGE_ROUND_UP(ImageSize))){if((*BaseAddress)NULL)//如果用户的要求是必须加载到预期地址处,返回失败!ImageBaseMmFindGap(AddressSpace,ImageSize,PAGE_SIZE,FALSE);for(i=0;i<
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年青海农牧科技职业学院单招职业适应性考试题库及一套完整答案详解
- 2026年陕西能源职业技术学院单招职业适应性测试题库含答案详解(a卷)
- 国家事业单位招聘2024国家文化和旅游部人才中心招聘1人笔试历年参考题库典型考点附带答案详解
- 国家事业单位招聘2024国家农业农村部食物与营养发展研究所招聘笔试历年参考题库典型考点附带答案详解
- 2026年阜阳科技职业学院单招职业技能测试题库附答案详解(完整版)
- 国家事业单位招聘2023国际小水电中心招聘事业编制工作人员拟聘笔试历年参考题库典型考点附带答案详解
- 2026年陕西航天职工大学单招职业适应性测试题库含答案详解(模拟题)
- 2026年陕西能源职业技术学院单招职业技能考试题库附参考答案详解(研优卷)
- 2026年陕西铁路工程职业技术学院单招职业适应性测试题库及答案详解(全优)
- 2026年青岛港湾职业技术学院单招职业技能测试题库附参考答案详解(模拟题)
- 种植多肉教学课件
- 语文●全国Ⅰ卷丨2024年普通高等学校招生全国统一考试语文试卷及答案
- (高清版)DG∕TJ 08-2405-2022 水运工程装配式护岸结构技术标准
- 2025智能接地箱技术规范
- 抗癫痫发作药物联合使用中国专家共识2025
- 春天的秘密幼儿园教育
- 人工智能在档案管理中的应用与发展
- 《医学影像检查技术学》课件-足X线摄影
- 部队采购招标资料3篇
- 南京财经大学C语言期末(共六卷)含答案解析
- 2024年度中国协同办公平台行业研究报告
评论
0/150
提交评论