0
点赞
收藏
分享

微信扫一扫

MmCreateSection/MmMapViewOfSection个人注释及理解(二)


    MmCreateSection/MmMapViewOfSection个人注释及理解(一)中提到内存映射的第一步创建Section对象建立Segment与文件的关系,彼时尚未建立虚拟内存因此不能被访问。此文是内存映射的第二步,MmMapViewOfSection建立虚拟映射。<windows内核中Section\Segment\ControlArea\Subsection\MMVAD之间的关系>一文中列出了5种和内存映射有关的对象,而<MmCreateSection/MmMapViewOfSection个人注释及理解(一)>中已经描述了其中4种对象,剩下的MMVAD对象肯定是要在这篇文章中出现了。

    MmMapViewOfSection说的是映射一个视图到进程地址空间中,视图怎么理解?个人认为,就是映射整个Segment->ThePtes原型pte阵列的一部分(也可以全部)当进程地址空间中。MmMapViewOfSection的核心思想是在进程vad树中搜索一段大小能容下将要被映射的视图的大小,且没有被占用的虚拟地址空间。

     MmMapViewOfSection的入口参数SectionToMap/Process是最主要的参数,从Section对象可以找到ControlArea,有了它可以找到其他所有相关的对象。而Process提供vad树,所有已被占用的虚拟内存都被记录在这颗树中。对于映像文件,进入MiMapViewOfImageSection函数。

首先设置映像文件起始地址:

BasedAddress = ControlArea->Segment->BasedAddress;


在MiCreateImageFileMap函数中设置:NewSegment->BasedAddress = (PVOID) NextVa;而NextVa的值为PE文件中设置的加载内存,就熟悉的0x40000。

然后计算将要被映射的文件占用的虚拟空间的起止,同时计算这段空间是否被占用,如果被占用还要在进程vad树中查找空闲的虚拟空间(要不然还不让加载程序不成?):

/*
BasedAddress是整个段对象的起始地址,下面的运算
(PVOID)((ULONG_PTR)BasedAddress +
(ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart));获得要建立视图的
起始地址应该相对于BasedAddress偏移多少
*/
StartingAddress = (PVOID)((ULONG_PTR)BasedAddress +
(ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart));

EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress +
*CapturedViewSize - 1) | (PAGE_SIZE - 1));
。。。
<pre name="code" class="cpp"> Vad = (PMMVAD) TRUE;
NeededViewSize = *CapturedViewSize;

if ((StartingAddress >= MM_LOWEST_USER_ADDRESS) &&
(StartingAddress <= MM_HIGHEST_VAD_ADDRESS) &&
(((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) -
(ULONG_PTR)StartingAddress >= *CapturedViewSize) &&

(EndingAddress <= MM_HIGHEST_VAD_ADDRESS)) {

Vad = (PMMVAD) (ULONG_PTR) MiCheckForConflictingVadExistence (Process, StartingAddress, EndingAddress);
}

//
// If the VAD address is not NULL, then a conflict was discovered.
// Attempt to select another address range in which to map the image.
//

//vad!=NULL,进程已被占用,只能另外找空闲的虚拟地址空间
if (Vad != NULL) {

。。。


            if (Process->VmTopDown == 1) {



                if (ZeroBits != 0) {


                    HighestUserAddress = (PVOID)((ULONG_PTR)MM_USER_ADDRESS_RANGE_LIMIT >> ZeroBits);


                    if (HighestUserAddress > MM_HIGHEST_VAD_ADDRESS) {


                        HighestUserAddress = MM_HIGHEST_VAD_ADDRESS;


                    }


                }


                else {


                    HighestUserAddress = MM_HIGHEST_VAD_ADDRESS;


                }



                Status = MiFindEmptyAddressRangeDown (&Process->VadRoot,


                                                      NeededViewSize,


                                                      HighestUserAddress,


                                                      X64K,


                                                      &StartingAddress);


            }


            else {


                Status = MiFindEmptyAddressRange (NeededViewSize,


                                                  X64K,


                                                  (ULONG)ZeroBits,


                                                  &StartingAddress);


            }



            if (!NT_SUCCESS (Status)) {


                MiDereferenceControlArea (ControlArea);


                return Status;


            }



            EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress +


                                        *CapturedViewSize - 1) | (PAGE_SIZE - 1));



MiFindEmptyAddressRange完成在进程vad树中搜索符合大小要求的空闲地址的任务,并返回其起始地址。


有了虚拟地址,这就要建立和初始化MMVAD来进行管理,这是和内存映射相关的5个对象中最后一个出场的:

Vad = ExAllocatePoolWithTag (NonPagedPool, sizeof(MMVAD), MMVADKEY);
。。。
RtlZeroMemory (Vad, sizeof(MMVAD));
Vad->StartingVpn = MI_VA_TO_VPN (LargeStartingAddress);
Vad->EndingVpn = MI_VA_TO_VPN (LargeEndingAddress);


还记得,Segment对象中的ThePtes原型pte阵列么?他记录了被映射的各个文件页面在整个文件中的偏移情况。现在这段页面即将被映射到进程空间中,总的有相应的对象记录吧,于是MMVAD承担了这项工作:

Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
Vad->LastContiguousPte = MM_ALLOCATION_FILLS_VAD;

看看与Subsection->SubsectionBase相关的设置,在MiCreateImageFileMap中可以找到如下的代码段:

//NewSegment->PrototypePte = &NewSegment->ThePtes[0];
PointerPte = NewSegment->PrototypePte;
Subsection->SubsectionBase = PointerPte;

。。。
*Segment = NewSegment;
RtlCopyMemory (NewSegment, OldSegment, sizeof(SEGMENT));

//
// Align the prototype PTEs on the proper boundary.
//

NewPointerPte = &NewSegment->ThePtes[0];
NewSegment->PrototypePte = &NewSegment->ThePtes[0];

PointerPte = NewSegment->PrototypePte +
(PointerPte - OldSegment->PrototypePte);

。。。
for (i = 0; i < SubsectionsAllocated; i += 1) {

//
// Note: SubsectionsAllocated is always 1 (for wx86), so this loop
// is executed only once.
//

NewSubsection->ControlArea = (PCONTROL_AREA) NewControlArea;

NewSubsection->SubsectionBase = NewSegment->PrototypePte +
(Subsection->SubsectionBase - OldSegment->PrototypePte);


。。。
Subsection->SubsectionBase = PointerPte;


至此,MiMapViewOfImageSection调创建MMVAD对象完成并加入到进程VAD树,返回模块实际被映射的虚拟内存,最后退出:

MiInsertVad (Vad, Process);
Process->VirtualSize += LargeOutputViewSize;
。。。
*CapturedViewSize = OutputViewSize;
*CapturedBase = OutputStartingAddress;



举报

相关推荐

0 条评论