函数原型:
NTSTATUS
MmInitializeProcessAddressSpace (
IN PEPROCESS ProcessToInitialize,
IN PEPROCESS ProcessToClone OPTIONAL,
IN PVOID SectionToMap OPTIONAL,
IN OUT PULONG CreateFlags,
OUT POBJECT_NAME_INFORMATION *AuditName OPTIONAL
);
函数主要功能:
此函数的主要功能如函数名,初始化新进程的地址空间,主要有两部分操作:
1. 初始化新进程 EPROCESS对象有关变量
2. 对新进程的可执行映像文件在地址空间的映射,或者在拷贝父进程地址空间。
函数参数:
ProcessToInitialize,即为要初始化的目标进程。
ProcessToClone,可选参数,表示新进程的地址空间可以从该进程拷贝获得。
SectionToMap,可选参数,一个内存区对象,表示在新进程地址空间中映射此对象。
CreateFlags, 传入传出参数,表示各种与进程创建相关的标志,与内存管理有关的标志是PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS,其传出值表示是否成功地以大页面的方式来映射进程的映像文件。
AuditName,可选参数,输出参数,是一个对象名称信息指针。
一些相关的名词解释:
PDE:Page Directory Entry, 页目录项
PTE:Page Table Entry, 页表项
超空间:一个用于映射进程工作集列表的特殊区域,在执行一些操作时它也被临时用于映射其他的物理页面,这样的操作有零化空闲列表上的页面、使其他页表中的页表项无效,以及创建进程。
系统页表项:系统PTE池,其中的PTE被用于映射系统页面,比如I/O空间、内核栈,以及内存描述符列表。通过检查性能工具中的“Memory: Free System Page Table Entries”计数器的值,你可以知道有多少系统PTE是可以使用的。
换页内存池:可换页的系统内存堆。
非换页内存池:不可换页的系统内存堆,通常有两部分在系统空间的低端和高端各有一部分。
PFN数据库:页帧数据库(Page Frame Number Database),是一个阵列或一个数组,每一个页面对应有一个PFN项,记录了该页面的使用情况,包括它的状态、对应页表项的地址等信息。
自旋锁:是一种在内核定义,只能在内核态下使用的同步机制。自旋锁用来保护共享数据或者资源,使得并发执行的程序或者在高优先级IRQL的对称多处理器的程序能够正确访问这些数据。
工作集:是指一个进程当前正在使用的物理页面的集合。
VAD:Virtual Address Descriptor,虚拟地址描述符。Windows的进程地址空间是通过VAD来管理的,VAD对象描述了一些连续的地址范围,在整个地址空间中,保留的或提交的地址范围有可能是不连续的,所以,Windows使用一棵平衡二叉搜索树(称为AVL树)来管理VAD对象。VadRoot的类型为MM_AVL_TABLE,VAD树中的各节点类型为MMADDRESS_NODE,当使用AVL树来管理虚拟地址空间中的VAD时,树中节点的真正类型为MMVAD。需要说明的一点是,由于VadRoot的BalancedRoot域是一个内嵌结构体它无法从MMADDRESS_NODE适应MMVAD,所以,虚拟地址空间VAD树的真正根节点是VadRoot.BalancedRoot.RightChild,VadRoot.BalancedRoot.LeftChild并未使用。VadRoot.BalancedRoot.RightChild的静态类型是MMADDRESS_NODE,但其实际的对象类型是MMVAD。从这两种类型的定义可以看出,MMVAD是MMADDRESS_NODE的一个扩展,它增加了几个数据成员。
函数调用时机:
此函数是在创建进程时,由父进程在NtCreateProcessEx() 中, 在初始化完新进程对象的进程页目录和超空间的页帧号,以及新进程的句柄表之后调用的。
调用此函数初始进程空间有四种可能性:
1. Boot Process,系统引导时调用,无父进程。
2. System Process,系统进程的初始化,无父进程,无文件区域对象。
3. User Process (Cloned Address Space),根据父进程克隆地址空间。
4. User Process (New Image Address Space),根据指定文件区域对象映射地址空间
函数具体分析:
一个新进程在创建时,父进程调用此函数属于第四种,以下用上述所说第四种的执行过
程来分析此函数的具体功能。在第四种情况调用时,进入函数之前,在NtCreateProcessEx()函数中已调用了MmCreateProcessAddressSpace() 函数创建了进程地址空间,而且在新进程地址空间中,系统空间部分的页目录项已经被拷贝到页目录中,系统空间已经映射好,新进程的超空间也已初始化,但新进程的用户空间部分,即0x00000000~0x7fffffff,还是一片空白,此函数就是对用户空间部分的初始化。
调用函数具体流程如下:
1. 得到当前线程内核线程块KTHREAD结构体指针
mov ebx, large fs:124h ; 得到 _KTHREAD 结构体指针
.text:00447001 push edi .text:00447002 mov ecx, offset _MmExpansionLock .text:00447007 call ds:__imp_@KfAcquireSpinLock@4 ; KfAcquireSpinLock(x)
即:
.text:00447010 lea edi, [esi+_EPROCESS.Flags] .text:00447016 test byte ptr [edi+2], 80h ; ProcessToInitialize->PdeUpdateNeeded .text:00447016 ; 比较是否需要更新PDE
4. 调用KeAttachProcess() 函数把当前线程附载到待初始化的进程对象上,此时执行的进程空间就由父进程转到新进程的进程空间,以便于做一些访问新进程页面的操作。在函数最后退出前会调用KeDetachProcess(),从将线程恢复到父进程中。即:
.text:0044703F push esi ; Process .text:00447040 call _KeAttachProcess@4 ; KeAttachProcess(x)
.text:0044704D mov eax, 800h ; PS_PROCESS_FLAGS_ADDRESS_SPACE2 .text:00447052 lock or [edi], eax
.text:00447055 lea eax, [esi+_EPROCESS.AddressCreationLock] .text:0044705B and [eax+_KGUARDED_MUTEX.Owner], 0 .text:0044705F and [eax+_KGUARDED_MUTEX.Contention], 0 .text:00447063 xor edi, edi .text:00447065 inc edi .text:00447066 lea ecx, [eax+_KGUARDED_MUTEX.Gate] ; Gate .text:00447069 mov [eax], edi .text:0044706B call @KeInitializeGate@4 ; KeInitializeGate(x)
.text:00447077 lea eax, [esi+_EPROCESS.VadRoot] .text:0044707D mov [eax], eax ;设置根节点 .text:0044707F lea eax, [esi+_EPROCESS.Vm.LastTrimTime] .text:00447085 push eax ; CurrentTime .text:00447086 call _KeQuerySystemTime@4 ; KeQuerySystemTime(x) .text:0044708B mov eax, _MmWorkingSetList
有关,是否开PAE和页表大小有几种不同情况,现讨论未开启PAE的一种,一些常量值如下定义:
#define PDE_BASE PDE_BASE_X86
#define PDE_BASE_X86 0xc0300000
#define PTE_BASE 0xc0000000
#define MM_PTE_READWRITE MM_PTE_WRITE_MASK
#define MM_PTE_WRITE_MASK 0x800
#define MM_PTE_CACHE 0x0
.text:004470A3 mov eax, 0C0300C00h ; 0C0300C00h .text:004470A3 ; = 0x300C00 + 0xc0000000 .text:004470A3 ; = ((PMMPTE)((0xc0300 << 2) + PTE_BASE)) .text:004470A3 ; = ((PMMPTE)(((0xc0300000 >> 12) << 2) + PTE_BASE)) .text:004470A3 ; = ((PMMPTE)(((((ULONG)(va)) >> 12) << 2) + PTE_BASE)) .text:004470A3 ; = MiGetPteAddress (PDE_BASE); .text:004470A8 push eax ; PointerPte .text:004470A9 mov eax, dword ptr [eax+_MMPTE.u] .text:004470AB shr eax, 0Ch ; 右移12位,eax = _MMPTE.u.Hard.PageFrameNumber .text:004470AE push eax ; PageFrameIndex .text:004470AF call _MiInitializePfn@12 ; MiInitializePfn(x,x,x) .text:004470B4 push edi ; ModifiedState .text:004470B5 mov eax, 0C0300C04h ; PointerPte .text:004470B5 ; = MiGetPdeAddress (HYPER_SPACE); .text:004470B5 ; = ((PMMPTE)(((0xc0400000 >> 22) << 2) + PDE_BASE)) .text:004470B5 ; = ((PMMPTE)((0x301 << 2) + PDE_BASE)) .text:004470B5 ; = 0xC04 + 0xc0300000 .text:004470B5 ; = 0xc0300C04 .text:004470B5 ; .text:004470BA push eax ; PointerPte .text:004470BB mov eax, dword ptr [eax+_MMPTE.u] .text:004470BD shr eax, 0Ch ; 右移12位,eax = _MMPTE.u.Hard.PageFrameNumber .text:004470C0 push eax ; PageFrameIndex .text:004470C1 call _MiInitializePfn@12 ; MiInitializePfn(x,x,x)
.text:004470C6 mov ecx, 0C0301404h ; PointerPte .text:004470C6 ; = MiGetPteAddress (0xC0501000) .text:004470C6 ; = ((PMMPTE)(((0xC0501000 >> 12) << 2) + PTE_BASE)) .text:004470C6 ; = ((PMMPTE)((0xC0501 << 2) + PTE_BASE)) .text:004470C6 ; = 0x301404 + 0xc0000000 .text:004470C6 ; = 0xC0301404 .text:004470CB mov [ebp+PointerPte], ecx .text:004470CE call @MiDetermineUserGlobalPteMask@4 ; fastcall .text:004470D3 mov edi, eax .text:004470D5 or edi, MmProtectToPteMask_4_ ; .text:004470DB mov [ebp+NumberOfPages], 2 .text:004470E2 or edi, 42h ; 42h = HARDWARE_PTE_DIRTY_MASK .text:004470E2 ; TempPte.u.Long |= HARDWARE_PTE_DIRTY_MASK .text:004470E5 LoopInitializePfn: .text:004470E5 ; CODE XREF: MmInitializeProcessAddressSpace(x,x,x,x,x)+127 j .text:004470E5 mov ecx, [ebp+PointerPte] .text:004470E8 mov eax, [ecx] .text:004470EA push 1 ; ModifiedState .text:004470EC shr eax, 0Ch ; eax = PointerPte->u.Hard.PageFrameNumber .text:004470EF push ecx ; PointerPte .text:004470F0 push eax ; PageFrameIndex .text:004470F1 mov [ebp+VadBitMapPage], eax .text:004470F4 mov dword ptr [ecx], 80h ; 80h = MM_KERNEL_DEMAND_ZERO_PTE .text:004470F4 ; .text:004470F4 ; MI_WRITE_INVALID_PTE (PointerPte, DemandZeroPte); .text:004470F4 ; == (*(PointerPte) = (DemandZeroPte)) .text:004470F4 ; == (*(PointerPte) = (MM_KERNEL_DEMAND_ZERO_PTE)) .text:004470F4 ; == == (*(PointerPte) = (0x80)) .text:004470FA call _MiInitializePfn@12 ; MiInitializePfn(x,x,x) .text:004470FF mov eax, [ebp+VadBitMapPage] .text:00447102 shl eax, 0Ch ; eax = PointerPte->u.Hard.PageFrameNumber .text:00447105 and edi, 0FFFh ; 高12位以上清0 .text:0044710B or edi, eax ; MI_WRITE_VALID_PTE (PointerPte, TempPte); .text:0044710D mov eax, [ebp+PointerPte] .text:00447110 add [ebp+PointerPte], 4 ; PointerPte += 1 (PointerPte为指针) .text:00447114 dec [ebp+NumberOfPages] ; NumberOfPages-- .text:00447117 mov [eax], edi .text:00447119 jnz short LoopInitializePfn
.text:0044711B mov dl, [ebp+OldIrql] ; UNLOCK_PFN (OldIrql); .text:0044711E push 2 ; #define LockQueuePfnLock 2 .text:00447120 pop ecx .text:00447121 call ds:__imp_@KeReleaseQueuedSpinLock@8 ; KeReleaseQueuedSpinLock(x,x)
.text:0044715A push esi ; CurrentProcess .text:0044715B call _MiInitializeWorkingSetList@4 ; MiInitializeWorkingSetList(x)
.text:00447165 lea ecx, [eax-10000h] ; MM_HIGHEST_VAD_ADDRESS .text:00447165 ; = ((PVOID)((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (64 * 1024))) .text:00447165 ; = 0x7ffeffff - 65536 .text:00447165 ; = 0x7ffeffff - 0x10000 .text:00447165 ; = 0x7ffdffff .text:0044716B xor edi, edi .text:0044716D mov eax, 7FFE0000h ; MM_SHARED_USER_DATA_VA .text:00447172 cmp ecx, eax .text:00447174 mov [ebp+BaseAddress], edi .text:00447177 jbe No_3G_No_64 ;
.text:0044730E cmp _MmAllocationPreference, edi ; 检查当前进程是否是否由高到低分配虚拟地址空间 .text:00447314 jz short No_HightToLow
.text:0044734F mov eax, [eax+_SECTION.Segment] .text:00447352 mov ecx, [eax+_SEGMENT.ControlArea] .text:00447354 mov ecx, [ecx+_CONTROL_AREA.FilePointer] .text:00447357 mov edi, [eax+_SEGMENT.u2.ImageInformation] .text:0044735A mov eax, dword ptr [ecx+_FILE_OBJECT.FileName.Length] .text:0044735D mov edx, [ecx+_FILE_OBJECT.FileName.Buffer] .text:00447360 movzx eax, ax .text:00447363 add eax, edx ; Length+Buffer .text:00447365 xor ebx, ebx .text:00447367 test edx, edx ; FileName.Buffer != NULL .text:00447369 jz short Buffer_Null ; sizeof (ProcessToInitialize->ImageFileName) .text:0044736B jmp short Buffer_NotNull .text:0044736D ; --------------------------------------------------------------------------- .text:0044736D No_Path_Sig: ; MmInitializeProcessAddressSpace(x,x,x,x,x)+386 j .text:0044736D dec eax .text:0044736E dec eax .text:0044736F cmp word ptr [eax], '\' ; '\' == OBJ_NAME_PATH_SEPARATOR .text:00447373 jz short Path_Sig .text:00447375 inc ebx .text:00447376 .text:00447376 Buffer_NotNull: ;: MmInitializeProcessAddressSpace(x,x,x,x,x)+379 j .text:00447376 cmp eax, edx .text:00447378 ja short No_Path_Sig .text:0044737A jmp short Buffer_Null ; sizeof (ProcessToInitialize->ImageFileName) .text:0044737C ; --------------------------------------------------------------------------- .text:0044737C .text:0044737C Path_Sig: ; CODE XREF: MmInitializeProcessAddressSpace(x,x,x,x,x)+381 j .text:0044737C inc eax .text:0044737D inc eax .text:0044737E .text:0044737E Buffer_Null: ; MmInitializeProcessAddressSpace(x,x,x,x,x)+377 j .text:0044737E ; MmInitializeProcessAddressSpace(x,x,x,x,x)+388 j .text:0044737E cmp ebx, 10h ; sizeof (ProcessToInitialize->ImageFileName) .text:00447381 lea edx, [esi+_EPROCESS.ImageFileName] .text:00447387 jb short Hight_SizeOf .text:00447389 push 0Fh .text:0044738B pop ebx .text:0044738C .text:0044738C Hight_SizeOf: ;: MmInitializeProcessAddressSpace(x,x,x,x,x)+395 j .text:0044738C test ebx, ebx .text:0044738E jz short SizeOf_isNull .text:00447390 mov [ebp+ProcessToClone], ebx .text:00447393 .text:00447393 loc_447393: ; MmInitializeProcessAddressSpace(x,x,x,x,x)+3AB j .text:00447393 mov bl, [eax] ; *Dst++ = (UCHAR)*Src++; .text:00447395 mov [edx], bl .text:00447397 inc edx .text:00447398 inc eax .text:00447399 inc eax .text:0044739A dec [ebp+ProcessToClone] .text:0044739D jnz short loc_447393 ; *Dst++ = (UCHAR)*Src++;
.text:004473C4 mov al, byte ptr [edi+_SECTION_IMAGE_INFORMATION.___u5._s0.SubSystemMajorVersion] .text:004473C7 mov [esi+_EPROCESS.___u70._s0.SubSystemMajorVersion], al .text:004473CD mov al, byte ptr [edi+_SECTION_IMAGE_INFORMATION.___u5._s0.SubSystemMinorVersion] .text:004473D0 mov edi, [ebp+CreateFlags] .text:004473D3 mov [esi+_EPROCESS.___u70._s0.SubSystemMinorVersion], al
.text:004473D9 xor eax, eax .text:004473DB test byte ptr [edi], 10h ; PROCESS_CREATE_FLAGS_LARGE_PAGES .text:004473DE mov [ebp+BaseAddress], ebx .text:004473E1 mov [ebp+ViewSize], ebx .text:004473E4 mov dword ptr [ebp+SectionOffset], ebx ; (SectionOffset).LowPart = 0; .text:004473E7 mov dword ptr [ebp+SectionOffset+4], ebx ; (SectionOffset).HighPart = 0; .text:004473EA jz short NO_LARGE_PAGES .text:004473EC mov eax, 20000000h ; MEM_LARGE_PAGES .text:004473F1 .text:004473F1 NO_LARGE_PAGES: ; CODE XREF: MmInitializeProcessAddressSpace(x,x,x,x,x)+3F8 j .text:004473F1 push 4 ; Win32Protect = PAGE_READWRITE .text:004473F3 push eax ; AllocationType .text:004473F4 push 1 ; InheritDisposition .text:004473F6 lea eax, [ebp+ViewSize] .text:004473F9 push eax ; CapturedViewSize .text:004473FA lea eax, [ebp+SectionOffset] .text:004473FD push eax ; SectionOffset .text:004473FE push ebx ; CommitSize .text:004473FF push ebx ; ZeroBits .text:00447400 lea eax, [ebp+BaseAddress] .text:00447403 push eax ; CapturedBase .text:00447404 push esi ; Process .text:00447405 push [ebp+SectionToMap] ; SectionToMap .text:00447408 call _MmMapViewOfSection@40 ; MmMapViewOfSection(x,x,x,x,x,x,x,x,x,x)
.text:00447441 push ebx ; UseLargePages .text:00447442 push ebx ; DllBase .text:00447443 push esi ; Process .text:00447444 call _PsMapSystemDll@12 ; PsMapSystemDll(x,x,x)
.text:00447456 push esi ; WsInfo .text:00447457 call _MiAllowWorkingSetExpansion@4 ; MiAllowWorkingSetExpansion(x) .text:0044745C call _KeDetachProcess@0 ; KeDetachProcess()
注: 因本人水平有限,加上时间仓促,难免存在错误与纰漏之处,恳请各位高手给予指正!