windows内核情景分析学习笔记10

 1、共享映射区

对于用户空间的映射,一个物理页面通常只被映射到一个进程用户空间。

对于系统空间的映射,是由所有进程共享的。

但是,一个物理页面也可以被映射到多个进程的用户空间,映射到的虚拟地址可以不尽相同。由这样的物理页面映射在多个进程虚存空间形成的连续区间,称为"共享映射区"。

 

2、共享映射区存在的原因

①系统使用共享映射区载入并运行.exe和DLL文件。这大量的节约了页交换文件空间及程序启动的时间

②用户可使用共享映射区来访问磁盘上的数据文件。使我们可以避免直接对文件进行IO操作和对文件内容进行缓存

③通过使用共享映射区,可以在一台机器的不同进程间共享数据,效率很高。

 

3、创建共享映射区

若要创建共享影射区,需执行如下三个步骤:

①创建共享映射区对象

②分配虚存空间

③建立映射

 

3.1、创建共享映射区对象

首先,创建共享映射区对象使用NtCreateSection

NtCreateSection (OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG SectionPageProtection OPTIONAL, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL)

最后一个参数FileHandle,如要创建文件映射区,则应该首先打开文件,并获取文件句柄填入;如填入NULL,则创建共享映射区。

其实现为:

NTSTATUS NTAPI NtCreateSection (OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG SectionPageProtection OPTIONAL, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL) { LARGE_INTEGER SafeMaximumSize; PVOID SectionObject; KPROCESSOR_MODE PreviousMode; NTSTATUS Status; ... Status = MmCreateSection(&SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle, NULL); ... return Status; }   

显然,其函数主体为MmCreateSection

其实现为:

NTSTATUS NTAPI MmCreateSection (OUT PVOID * Section, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize, IN ULONG SectionPageProtection, IN ULONG AllocationAttributes, IN HANDLE FileHandle OPTIONAL, IN PFILE_OBJECT File OPTIONAL) { ULONG Protection; PROS_SECTION_OBJECT *SectionObject = (PROS_SECTION_OBJECT *)Section; /* * Check the protection */ Protection = SectionPageProtection & ~(PAGE_GUARD|PAGE_NOCACHE); if (Protection != PAGE_READONLY && Protection != PAGE_READWRITE && Protection != PAGE_WRITECOPY && Protection != PAGE_EXECUTE && Protection != PAGE_EXECUTE_READ && Protection != PAGE_EXECUTE_READWRITE && Protection != PAGE_EXECUTE_WRITECOPY) { return STATUS_INVALID_PAGE_PROTECTION; } //可执行文件 if (AllocationAttributes & SEC_IMAGE) { return(MmCreateImageSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle)); } //普通数据文件 if (FileHandle != NULL) { return(MmCreateDataFileSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes, FileHandle)); } //共享映射区(没有文件句柄) return(MmCreatePageFileSection(SectionObject, DesiredAccess, ObjectAttributes, MaximumSize, SectionPageProtection, AllocationAttributes)); }

这个函数一开始进行页面保护模式的合理性检查,检查后,就根据目标文件的性质不同进行不同的处理:

①如果是一个可执行文件,就通过MmCreateImageSection处理。因可执行文件有着特殊的结构。所以当做特例处理。

②如果是普通数据文件,由MmCreateDataFileSection创建文件映射区

③如果没有给定目标文件,那么就是创建“共享内存区”。(其实是以页面交换文件为目标文件)

 

以MmCreateDataFileSection为例,分析一下创建文件映射区的过程。

此函数中做了如下几件事情:

①创建映射区对象,由ObCreateObject完成

②获取数据文件对象,由ObReferenceObjectByHandle完成,并获取到数据文件的FILE_OBJECT结构指针

③构造一个映射段MM_SECTION_SEGMENT数据结构(ExAllocatePoolWithTag完成),让映射区对象与数据文件对象中的相关指针指向这个映射段。

④使映射区对象中的FileObject字段指向数据文件对象。

关键代码如下:

NTSTATUS NTAPI MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER UMaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle) { PROS_SECTION_OBJECT Section; NTSTATUS Status; LARGE_INTEGER MaximumSize; PFILE_OBJECT FileObject; PMM_SECTION_SEGMENT Segment; ULONG FileAccess; IO_STATUS_BLOCK Iosb; LARGE_INTEGER Offset; CHAR Buffer; FILE_STANDARD_INFORMATION FileInfo; /* * 创建映射区对象Section */ Status = ObCreateObject(ExGetPreviousMode(), MmSectionObjectType, ObjectAttributes, ExGetPreviousMode(), NULL, sizeof(ROS_SECTION_OBJECT), 0, 0, (PVOID*)(PVOID)&Section); /* * 初始化映射区对象Section */ RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT)); Section->SectionPageProtection = SectionPageProtection; Section->AllocationAttributes = AllocationAttributes; .... /* * 获取数据文件对象FileObject */ Status = ObReferenceObjectByHandle(FileHandle, FileAccess, IoFileObjectType, ExGetPreviousMode(), (PVOID*)(PVOID)&FileObject, NULL); ... /* 如果数据文件此前尚未用作文件映射区的后盾 * 则构造映射段MM_SECTION_SEGMENT结构 */ if (FileObject->SectionObjectPointer->DataSectionObject == NULL) { Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT), TAG_MM_SECTION_SEGMENT); //使映射区对象中的相关指针指向映射段 Section->Segment = Segment; Segment->ReferenceCount = 1; ExInitializeFastMutex(&Segment->Lock); /* * Set the lock before assigning the segment to the file object */ ExAcquireFastMutex(&Segment->Lock); //使数据文件对象中的相关指针指向映射段 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment; //映射段初始化 Segment->FileOffset = 0; Segment->Protection = SectionPageProtection; Segment->Flags = MM_DATAFILE_SEGMENT; Segment->Characteristics = 0; Segment->WriteCopy = FALSE; if (AllocationAttributes & SEC_RESERVE) { Segment->Length = Segment->RawLength = 0; } else { Segment->RawLength = MaximumSize.u.LowPart; Segment->Length = PAGE_ROUND_UP(Segment->RawLength); } Segment->VirtualAddress = 0; RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY)); } else { //如果数据文件已经映射到某个映射段,则可能需要扩展映射段大小(如果需要) Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer-> DataSectionObject; Section->Segment = Segment; (void)InterlockedIncrementUL(&Segment->ReferenceCount); MmLockSectionSegment(Segment); if (MaximumSize.u.LowPart > Segment->RawLength && !(AllocationAttributes & SEC_RESERVE)) { Segment->RawLength = MaximumSize.u.LowPart; Segment->Length = PAGE_ROUND_UP(Segment->RawLength); } } MmUnlockSectionSegment(Segment); //使映射区对象中的FileObject字段指向数据文件对象 Section->FileObject = FileObject; Section->MaximumSize = MaximumSize; CcRosReferenceCache(FileObject); *SectionObject = Section; return(STATUS_SUCCESS); } 、

 

3.2 映射区与映射段的区别

一个映射区由一个或多个映射段组成。对于映射的是可执行文件,其文件本身就分为多个段,例如代码段、数据段等,所以映射区也得要分成若干个映射段;对于映射的是数据文件,则只有一个映射段。

 

映射区对象结构

typedef struct _ROS_SECTION_OBJECT { CSHORT Type; CSHORT Size; LARGE_INTEGER MaximumSize; ULONG SectionPageProtection; ULONG AllocationAttributes; PFILE_OBJECT FileObject; //指向文件对象 union { PMM_IMAGE_SECTION_OBJECT ImageSection; //可执行文件 PMM_SECTION_SEGMENT Segment; //数据文件(单个映射段) }; } ROS_SECTION_OBJECT, *PROS_SECTION_OBJECT;

 

映射段对象结构

 typedef struct _MM_SECTION_SEGMENT { LONG FileOffset; /* 本映射段起点对应于文件内部的位移*/ ULONG_PTR VirtualAddress; /* Start offset into the address range for image sections */ ULONG RawLength; /* length of the segment which is part of the mapped file */ ULONG Length; /* absolute length of the segment */ ULONG Protection; FAST_MUTEX Lock; /* lock which protects the page directory */ ULONG ReferenceCount; SECTION_PAGE_DIRECTORY PageDirectory; //页面目录(指向一个映射段页面表) ULONG Flags; ULONG Characteristics; BOOLEAN WriteCopy; } MM_SECTION_SEGMENT, *PMM_SECTION_SEGMENT;

 

3.3、分配虚存空间并建立映射

这时,映射区对象建立完毕,而并未实际建立映射。实际的映射是通过系统调用NtMapViewOfSection完成的。

这个系统调用的作用是,将一个映射区对象的一部分或全部映射到某个进程的用户空间。

对于数据文件,其最终调用的是MmMapViewOfSegment(因数据文件只有一个段)。

static NTSTATUS MmMapViewOfSegment(PMMSUPPORT AddressSpace, PROS_SECTION_OBJECT Section, PMM_SECTION_SEGMENT Segment, PVOID* BaseAddress, SIZE_T ViewSize, ULONG Protect, ULONG ViewOffset, ULONG AllocationType) { PMEMORY_AREA MArea; NTSTATUS Status; PHYSICAL_ADDRESS BoundaryAddressMultiple; BoundaryAddressMultiple.QuadPart = 0; Status = MmCreateMemoryArea(AddressSpace, MEMORY_AREA_SECTION_VIEW, BaseAddress, ViewSize, Protect, &MArea, FALSE, AllocationType, BoundaryAddressMultiple); if (!NT_SUCCESS(Status)) { return(Status); } ObReferenceObject((PVOID)Section); MArea->Data.SectionData.Segment = Segment; MArea->Data.SectionData.Section = Section; MArea->Data.SectionData.ViewOffset = ViewOffset; MArea->Data.SectionData.WriteCopyView = FALSE; MmInitializeRegion(&MArea->Data.SectionData.RegionListHead, ViewSize, 0, Protect); return(STATUS_SUCCESS); }

此函数很简单,就是申请一块虚存空间,并将对应的MEMORY_AREA结构的分量设置成指向映射区就完事了。

 

你可能感兴趣的:(windows,object,null,Integer,Access,attributes)