文章目录
Windows 会话内存隔离原理
我们知道Windows是一个多会话的系统,会话也就是Session,每个会话都会形成一个会话隔离的概念;例如我们远程进去一台PC,那么这个PC就会出现两个会话界面,远程的看到的是真实的桌面信息,但是被远程的电脑看到的是锁屏的界面;一个系统怎么呈现出两种不同的显示信息呢?这个就是用会话隔离来实现的。
1. 会话内存
首先需要明白一个概念的是,我们在显示器上面看到的东西,都是内存(或者显存)里面的东西,如果我们在同一个时候,同一个内存位置看到的东西不同,那么,肯定就是看到不同的内存。会话内存就是利用会话的概念来隔离不同虚拟地址对应的物理地址。
我们看一下内存访问的过程,先来看一下MmAccessFault
内存访问的过程:
NTSTATUS
MmAccessFault (
IN ULONG_PTR FaultStatus,
IN PVOID VirtualAddress,
IN KPROCESSOR_MODE PreviousMode,
IN PVOID TrapInformation
)
{
//...
SessionStatus = MiCheckPdeForSessionSpace (VirtualAddress);
//...
}
例如这个函数的调用堆栈如下:
# ChildEBP RetAddr Args to Child
00 edd5bb20 8051d621 c05e6000 85b48cb0 edd5bc04 nt!MiCheckPdeForSessionSpace+0x69
01 edd5bb6c 8054151c 00000001 c05e6000 00000000 nt!MmAccessFault+0x2b9
02 edd5bb6c 805a9b43 00000001 c05e6000 00000000 nt!KiTrap0E+0xcc
03 edd5bc04 805a8022 c05e5500 00000422 c05e7610 nt!MiAddMappedPtes+0x69
04 edd5bc2c 805a8421 e1cd2718 007f015c edd5bc68 nt!MiMapViewInSystemSpace+0xe2
05 edd5bc44 bf8b923d e1cd2718 edd5bc68 edd5bc64 nt!MmMapViewInSessionSpace+0x37
06 edd5bc6c bf809848 e1cd2718 6d626b47 000005a0 win32k!pvAllocateKernelSection+0x4c
07 edd5bcb0 bf813b72 00001680 00000000 00000000 win32k!SURFMEM::bCreateDIB+0x175
08 edd5bd14 bf813ca1 e14d8008 00000006 0108002e win32k!hsurfCreateCompatibleSurface+0xe6
09 edd5bd50 8053e638 e14d8008 000005a0 000002f0 win32k!GreCreateCompatibleBitmap+0xe9
0a edd5bd50 7c92e4f4 e14d8008 000005a0 000002f0 nt!KiFastCallEntry+0xf8
0b 006df970 77ef70f2 77ef709d 1e0107f8 000005a0 ntdll!KiFastSystemCallRet
MiCheckPdeForSessionSpace
这个函数主要通过MmSessionSpace
获取PTE信息。
NTSTATUS
FASTCALL
MiCheckPdeForSessionSpace(
IN PVOID VirtualAddress
)
{
//...
SessionVirtualAddress = MiGetVirtualAddressMappedByPte ((PMMPTE) VirtualAddress);
PointerPde = MiGetPteAddress (VirtualAddress);
Index = MiGetPdeSessionIndex (SessionVirtualAddress);
PointerPde->u.Long = MmSessionSpace->PageTables[Index].u.Long;
//...
}
从这里我们可以猜测,会话内存的不是通过CR3获取到PDE然后再获取PTE,然后再寻址,每个会话空间保存着会话内存的PTE,会话空间的结构如下:
kd> dt nt!_MM_SESSION_SPACE 93a00000
+0x000 ReferenceCount : 0n8
+0x004 u : <unnamed-tag>
+0x008 SessionId : 1
+0x00c ProcessReferenceToSession : 0n9
+0x010 ProcessList : _LIST_ENTRY [ 0x87a287b4 - 0x88355c14 ]
+0x018 LastProcessSwappedOutTime : _LARGE_INTEGER 0x0
+0x020 SessionPageDirectoryIndex : 0x1da08
+0x024 NonPagablePages : 0x28d
+0x028 CommittedPages : 0x179b
+0x02c PagedPoolStart : 0x80000000 Void
+0x030 PagedPoolEnd : 0xffbfffff Void
+0x034 SessionObject : 0x87a24cf8 Void
+0x038 SessionObjectHandle : 0x800002a8 Void
+0x03c ResidentProcessCount : 0n8
+0x040 SessionPoolAllocationFailures : [4] 0
+0x050 ImageList : _LIST_ENTRY [ 0x878a1148 - 0x86668b90 ]
+0x058 LocaleId : 0x804
+0x05c AttachCount : 0
+0x060 AttachGate : _KGATE
+0x070 WsListEntry : _LIST_ENTRY [ 0x83f55408 - 0x8ca3c070 ]
+0x080 Lookaside : [25] _GENERAL_LOOKASIDE
+0xd00 Session : _MMSESSION
+0xd38 PagedPoolInfo : _MM_PAGED_POOL_INFO
+0xd70 Vm : _MMSUPPORT
+0xddc Wsle : 0x93a30048 _MMWSLE
+0xde0 DriverUnload : 0x948722b9 void win32k!Win32KDriverUnload+0
+0xe00 PagedPool : _POOL_DESCRIPTOR
+0x1f40 PageTables : 0x87a25000 _MMPTE
+0x1f48 SpecialPool : _MI_SPECIAL_POOL
+0x1f80 SessionPteLock : _KGUARDED_MUTEX
+0x1fa0 PoolBigEntriesInUse : 0n405
+0x1fa4 PagedPoolPdeCount : 0xd
+0x1fa8 SpecialPoolPdeCount : 0
+0x1fac DynamicSessionPdeCount : 0
+0x1fb0 SystemPteInfo : _MI_SYSTEM_PTE_TYPE
+0x1fe0 PoolTrackTableExpansion : (null)
+0x1fe4 PoolTrackTableExpansionSize : 0
+0x1fe8 PoolTrackBigPages : 0x88214000 Void
+0x1fec PoolTrackBigPagesSize : 0x400
+0x1ff0 IoState : 6 ( IoSessionStateLoggedOn )
+0x1ff4 IoStateSequence : 7
+0x1ff8 IoNotificationEvent : _KEVENT
+0x2008 SessionPoolPdes : _RTL_BITMAP
+0x2010 CpuQuotaBlock : (null)
对于在会话内存中的内存都是通过PageTables : 0x87a25000 _MMPTE
来寻址真实的物理内存,所以就算两个会话有相同的地址,但是PageTables
不同,使用的内存也是不同的。
如何判断虚拟地址是在会话空间中呢?有如下的宏定义可以判断(在MiCheckPdeForSessionSpace
也是通过如下宏判断地址是否是会话内存地址):
#define MI_IS_SESSION_IMAGE_ADDRESS(VirtualAddress) \
((PVOID)(VirtualAddress) >= (PVOID)MiSessionImageStart && (PVOID)(VirtualAddress) < (PVOID)(MiSessionImageEnd))
#define MI_IS_SESSION_POOL_ADDRESS(VirtualAddress) \
((PVOID)(VirtualAddress) >= (PVOID)MiSessionPoolStart && (PVOID)(VirtualAddress) < (PVOID)MiSessionPoolEnd)
#define MI_IS_SESSION_ADDRESS(_VirtualAddress) \
((PVOID)(_VirtualAddress) >= (PVOID)MmSessionBase && (PVOID)(_VirtualAddress) < (PVOID)(MiSessionSpaceEnd))
#define MI_IS_SESSION_PTE(_Pte) \
((PMMPTE)(_Pte) >= MiSessionBasePte && (PMMPTE)(_Pte) < MiSessionLastPte)
#define MI_IS_SESSION_IMAGE_PTE(_Pte) \
((PMMPTE)(_Pte) >= MiSessionImagePteStart && (PMMPTE)(_Pte) < MiSessionImagePteEnd)
#define SESSION_GLOBAL(_Session) (_Session->GlobalVirtualAddress)
在XP环境下面,会话空间的系统布局如下(在图中,会话映像文件区包括 win32k.sys、视频驱动程序以及一些打印驱动程序的映像文件;会话内存池是指属于会话空间的换页内存池):
2. 会话的创建
在Windows下面有一个smss.exe的进程,我们称作这个进程为会话管理进程,这个进程有一个重要的点就是创建会话,创建会话的堆栈如下:
kd> !thread
THREAD 8e408040 Cid 0230.023c Teb: 022e6000 Win32Thread: 00000000 RUNNING on processor 0
Impersonation token: 919bbcd8 (Level Delegation)
Owning Process 8ee2f8c0 Image: smss.exe
Attached Process 8ee2c8c0 Image: smss.exe
Wait Start TickCount 479 Ticks: 0
Context Switch Count 3 IdealProcessor: 0
UserTime 00:00:00.000
KernelTime 00:00:00.000
Win32 Start Address ntdll!TppWorkerThread (0x779477b0)
Stack Init 8c8f7ca0 Current 8c8f6da4 Base 8c8f8000 Limit 8c8f5000 Call 00000000
Priority 11 BasePriority 11 PriorityDecrement 0 IoPriority 2 PagePriority 5
ChildEBP RetAddr Args to Child
8c8f73cc 816b9d8b 8f8e6120 00000000 81661cd4 nt!MiSessionCreateInternal (FPO: [Non-Fpo])
8c8f73d8 81661cd4 00000000 00000000 8ee2c8c0 nt!MiSessionCreate+0x2b (FPO: [0,0,0])
8c8f7414 815fa1c1 8c8f76f4 00000000 00000000 nt!MiMapProcessExecutable+0xa2 (FPO: [Non-Fpo])
8c8f74d4 815f848f 8f8e6120 8c8f76f4 00000000 nt!MmInitializeProcessAddressSpace+0x1c9 (FPO: [Non-Fpo])
8c8f76d4 81619ff4 0247f370 78640061 ffffff38 nt!PspAllocateProcess+0xb75 (FPO: [Non-Fpo])
8c8f7be0 813ed28b 0247f644 0247f648 02000000 nt!NtCreateUserProcess+0x5c4 (FPO: [Non-Fpo])
8c8f7be0 77991570 0247f644 0247f648 02000000 nt!KiSystemServicePostCall (FPO: [0,3] TrapFrame @ 8c8f7c14)
0247f494 00000000 00000000 00000000 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
在xp下面这个堆栈有点不一样,如下:
kd> !thread
THREAD 85cf85a0 Cid 0178.017c Teb: 7ffdd000 Win32Thread: 00000000 RUNNING on processor 0
Not impersonating
DeviceMap e1004440
Owning Process 00000000 Image:
Attached Process 85cf8818 Image: smss.exe
Wait Start TickCount 916 Ticks: 1 (0:00:00:00.015)
Context Switch Count 65 IdealProcessor: 0
UserTime 00:00:00.000
KernelTime 00:00:00.140
Stack Init bac38000 Current bac37bd4 Base bac38000 Limit bac35000 Call 00000000
Priority 11 BasePriority 11 PriorityDecrement 0 IoPriority 0 PagePriority 0
ChildEBP RetAddr Args to Child
bac37b9c 805aab1c bac37d08 00000004 00000004 nt!MiSessionCreateInternal (FPO: [Non-Fpo])
bac37bb4 80607465 bac37d08 bac37d64 0015fe28 nt!MmSessionCreate+0x66 (FPO: [Non-Fpo])
bac37d50 8053e638 0000002f 0015feac 00000004 nt!NtSetSystemInformation+0x5ef (FPO: [Non-Fpo])
bac37d50 7c92e4f4 0000002f 0015feac 00000004 nt!KiFastCallEntry+0xf8 (FPO: [0,0] TrapFrame @ bac37d64)
0015fe10 7c92dd4c 4858879d 0000002f 0015feac ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0015fe14 4858879d 0000002f 0015feac 00000004 ntdll!NtSetSystemInformation+0xc (FPO: [3,0,0])
0015fe4c 48588d40 0015feac 4858c614 0015ff6c smss!SmpLoadSubSystemsForMuSession+0x9c (FPO: [Non-Fpo])
0015fecc 48588f27 0015ff6c 00000005 00000000 smss!SmpLoadDataFromRegistry+0x3e3 (FPO: [Non-Fpo])
0015ff18 48589bfc 0015ff6c 0015ff64 00000005 smss!SmpInit+0x1bd (FPO: [Non-Fpo])
0015ffa8 4858ad97 00000001 00162340 00162348 smss!main+0x68 (FPO: [Non-Fpo])
0015fff4 00000000 7ffde000 000000c8 0000019a smss!NtProcessStartup+0x1d2 (FPO: [Non-Fpo])
很显然,xp下面,这个流程相对来说要清楚一些; 基本流程如下:
- 当Smss接到一个创建会话的请求时,它首先调用
NtSetSystemInformation
,请求建立内核模式的会话数据结构。 - 这个函数依次调用内部的内存管理器函数
MmSessionCreate
,该函数建立起会话虚拟地址空间,该地址空间中包含会话中的换页内存池,以及由Win32子系统的内核模式部分(win32k.sys)和其他的会话空间设备驱动程序所分配的、属于每个会话的数据结构。
其实MmSessionSpace
这个会话空间的地址都是固定好了的值,如下:
MmSessionSpace = (PMM_SESSION_SPACE)((ULONG_PTR)MmSessionBase +
MmSessionSize -
MmSessionImageSize -
MM_ALLOCATION_GRANULARITY);
值和计算公式:
0: kd> dd nt!MmSessionSpace L1
80567b08 bf7f0000
0: kd> ? 0xc0000000-00800000-0x10000
Evaluate expression: -1082195968 = bf7f0000
3. smss进程基本作用
smss称作会话管理进程,会话简单的说,用户登陆到windows系统之后,不管该用户是本地登陆的,还是远程登陆,系统都会为这个用户分配一个新的会话ID(SID)。
会话管理进程(\Windows\System32\Smss.exe
)是系统中第一个创建的用户态模式进程,负责完成执行体和内核的初始化工作的内核模式系统线程在最后阶段创建了实际的Smss
进程。在启动Windows
的过程中,会话管理器负责许多重要的步骤,比如:
- 打开额外的页面文件
- 执行延迟的文件改名和删除操作
- 创建系统环境变量。
- 将子系统进程(通常只有
Csrss.exe
)和winlogon.exe
进程启动起来,winlogon
进程依次会创建其他的系统进程。 Smss
中的主线程在执行了这些初始化步骤后,一直在Csrss
和Winlogon
的进程句柄上等待。如果这两个进程中的任何一个非正常终止了,则Smss
让系统崩溃掉(崩溃代码是STATUS_SYSTEM_PROCESS_TERMINATED
或0xC000021A),因为Windows要依赖于这两个进程的存在才能运行得下去。- 同时,
Smss
等待加载子系统的请求、调试事件,以及创建新的终端服务器会话(terminal server sessions
)的请求。
在注册表的HKLM\SYSTEM\CurrentControlSet\Control\Session Manager
下面,你可以放到放多配置信息,它们驱动了Smss的初始化步骤。
同时终端服务会话(Terminal Services session
)的创建是由Smss
来完成的。当Smss
接到一个创建会话的请求时,它首先调用NtSetSystemInformation
,请求建立内核模式的会话数据结构。这又依次调用内部的内存管理器函数MmSessionCreate
,该函数建立起会话虚拟地址空间,该地址空间中包含会话中的换页内存池,以及由Win32子系统的内核模式部分(win32k.sys)和其他的会话空间设备驱动程序所分配的、属于每个会话的数据结构。然后,Smss
为该会话创建Winlogon
和Csrss
的实例。
4. 会话观察
Windows系统是支持多会话的,因此会话空间(session space)包含了一些针对每个会话的全局信息。会话(session)是由进程和其他的系统对象(比如窗口站、桌面和窗口)构成的,它们代表了一个用户的工作站登录会话。会话具体是由如下几个部分组成的:
- 每个会话包含一个单独的
win32k.sys
- 专门的换页池区域
- 私有windows子系统和登陆进程的拷贝
- 系统空间中被映射的空间,被称为会话空间的区域
在Windbg中我们可以查看当前Windows下存在多少个会话:
kd> !session
Sessions on machine: 2
Valid Sessions: 0 1
Current Session 1
如果需要查看会话下面有多少进程的话,可以使用如下:
kd> !sprocess 0
Dumping Session 0
_MM_SESSION_SPACE 97de0000
_MMSESSION 97de0064
PROCESS 8d851180 SessionId: 0 Cid: 0284 Peb: 03181000 ParentCid: 0274
DirBase: 3e0e0100 ObjectTable: 93408a00 HandleCount: 350.
Image: csrss.exe
PROCESS 9391c740 SessionId: 0 Cid: 02c8 Peb: 02830000 ParentCid: 0274
DirBase: 3e0e0180 ObjectTable: 93408ec0 HandleCount: 156.
Image: wininit.exe
PROCESS 9069f040 SessionId: 0 Cid: 0334 Peb: 02d7d000 ParentCid: 02c8
DirBase: 3e0e0200 ObjectTable: 954aa200 HandleCount: 343.
Image: services.exe
PROCESS 90fd3640 SessionId: 0 Cid: 034c Peb: 02cf7000 ParentCid: 02c8
DirBase: 3e0e0140 ObjectTable: 954aa840 HandleCount: 869.
Image: lsass.exe
PROCESS 906ab040 SessionId: 0 Cid: 039c Peb: 02afe000 ParentCid: 02c8
DirBase: 3e0e0280 ObjectTable: 954aaa80 HandleCount: 31.
Image: fontdrvhost.exe
kd> !sprocess 1
Dumping Session 1
_MM_SESSION_SPACE a3ea9000
_MMSESSION a3ea9064
PROCESS 93915380 SessionId: 1 Cid: 02d0 Peb: 031f4000 ParentCid: 02c0
DirBase: 3e0e01c0 ObjectTable: 954aa9c0 HandleCount: 283.
Image: csrss.exe
PROCESS 8eed9440 SessionId: 1 Cid: 0304 Peb: 02fb9000 ParentCid: 02c0
DirBase: 3e0e00c0 ObjectTable: 954aa740 HandleCount: 271.
Image: winlogon.exe
PROCESS 906a7600 SessionId: 1 Cid: 0394 Peb: 02eb2000 ParentCid: 0304
DirBase: 3e0e0240 ObjectTable: 954aa900 HandleCount: 31.
Image: fontdrvhost.exe
PROCESS 8c884040 SessionId: 1 Cid: 0478 Peb: 027f8000 ParentCid: 0304
DirBase: 3e0e0380 ObjectTable: 954aad80 HandleCount: 783.
Image: dwm.exe
PROCESS 8c7b4600 SessionId: 1 Cid: 0630 Peb: 02a51000 ParentCid: 04d4
DirBase: 3e0e05c0 ObjectTable: 8378fd80 HandleCount: 592.
Image: sihost.exe
其中_MM_SESSION_SPACE a3ea9000
和 _MMSESSION a3ea9064
分别表示会话的信息,我们可以查看会话内存信息如下:
kd> dt nt!_MM_SESSION_SPACE a3ea9000
+0x000 ReferenceCount : 0n26
+0x004 u : <anonymous-tag>
+0x008 SessionId : 1
+0x00c ProcessReferenceToSession : 0n27
+0x010 ProcessList : _LIST_ENTRY [ 0x93915470 - 0x8f4089b0 ]
+0x018 SessionPageDirectoryIndex : 0x17984
+0x01c NonPagablePages : 0x42
+0x020 CommittedPages : 0x443
+0x024 PagedPoolStart : 0x80000000 Void
+0x028 PagedPoolEnd : 0xffbfffff Void
+0x02c SessionObject : 0x942fffc8 Void
+0x030 SessionObjectHandle : 0x800005c0 Void
+0x034 ImageTree : _RTL_AVL_TREE
+0x038 LocaleId : 0x804
+0x03c AttachCount : 0
+0x040 AttachGate : _KGATE
+0x050 WsListEntry : _LIST_ENTRY [ 0x818d7d80 - 0x97de0050 ]
+0x058 PagedPoolInfo : _MM_PAGED_POOL_INFO
+0x064 Session : _MMSESSION
+0x078 CombineDomain : 0x00000001`00000005
+0x080 Vm : _MMSUPPORT_FULL
+0x180 WorkingSetList : _MMWSL_INSTANCE
+0x198 HeapState : 0x93930000 Void
+0x1c0 PagedPool : _POOL_DESCRIPTOR
+0x2c0 DriverUnload : _MI_SESSION_DRIVER_UNLOAD
+0x2c4 TopLevelPteLockBits : [128] 0
+0x4c8 PageTables : [1024] _MMPTE
+0x24c8 SessionPteLock : _EX_PUSH_LOCK
+0x24cc PoolBigEntriesInUse : 0n178
+0x24d0 PagedPoolPdeCount : 0n7
+0x24d4 DynamicSessionPdeCount : 0
+0x24d8 SystemPteInfo : _MI_SYSTEM_PTE_TYPE
+0x250c PoolTrackTableExpansion : 0x922c1000 Void
+0x2510 PoolTrackTableExpansionSize : 0x55
+0x2514 PoolTrackBigPages : 0x93937000 Void
+0x2518 PoolTrackBigPagesSize : 0x200
+0x251c PermittedFaultsTree : _RTL_AVL_TREE
+0x2520 IoState : 6 ( IoSessionStateLoggedOn )
+0x2524 IoStateSequence : 7
+0x2528 IoNotificationEvent : _KEVENT
+0x2538 ServerSilo : (null)
+0x2540 CreateTime : 0x535a2c4
+0x3000 PoolTags : [16384] "--- memory read error at address 0xa3eac000 ---"
如果需要看会话内存的使用情况可以使用!poolused 8
,如下:
kd> !poolused 8
*** CacheSize too low - increasing to 51 MB
Max cache size is : 53657600 bytes (0xccb0 KB)
Total memory in cache : 15140 bytes (0xf KB)
Number of regions cached: 95
765 full reads broken into 783 partial reads
counts: 679 cached/104 uncached, 86.72% cached
bytes : 62363 cached/10308 uncached, 85.82% cached
** Transition PTEs are implicitly decoded
** Prototype PTEs are implicitly decoded
.
Sorting by Session Tag
NonPaged Paged
Tag Allocs Used Allocs Used
CHip 0 0 9 4512 UNKNOWN pooltag 'CHip', please update pooltag.
//...
knlf 0 0 538 25808 GDITAG_FONT_LINK , Binary: win32k.sys
TOTAL 381 27432 10336 3571504
5. GDI隔离的原理
说到会话隔离,其中显而易见的就是GDI对象的隔离,所有的GDI内存都是跟会话相关的,那么GDI内存是怎么做到会话隔离的呢?我们可以先看一下GDI创建的时候内存从哪里来的。
5.1 从CreateWindow出发
CreateWindow
底层会调用IntCreateWindow
来创建桌面对象的内存,基本流程如下
IntCreateWindow(...)
{
//创建桌面对象
UserCreateObject
//初始化桌面对象的数据
//将窗口插入到当前线程中
InsertTailList (&pti->WindowListHead, &pWnd->ThreadListEntry);
}
所有的GDI对象的创建都是通过UserCreateObject
来完成的,对于Windows这种对象,使用的是AllocDeskThreadObject
这个函数来创建内存。
static PVOID AllocDeskThreadObject(
_In_ PDESKTOP pDesk,
_In_ PTHREADINFO pti,
_In_ SIZE_T Size,
_Out_ PVOID* HandleOwner)
{
PTHRDESKHEAD ObjHead;
ASSERT(Size > sizeof(*ObjHead));
ASSERT(pti != NULL);
if (!pDesk)
pDesk = pti->rpdesk;
ObjHead = DesktopHeapAlloc(pDesk, Size);
if (!ObjHead)
return NULL;
RtlZeroMemory(ObjHead, Size);
ObjHead->pSelf = ObjHead;
ObjHead->rpdesk = pDesk;
ObjHead->pti = pti;
IntReferenceThreadInfo(pti);
*HandleOwner = pti;
/* It's a thread object, but it still count as one for the process */
pti->ppi->UserHandleCount++;
return ObjHead;
}
static __inline PVOID
DesktopHeapAlloc(IN PDESKTOP Desktop,
IN SIZE_T Bytes)
{
return RtlAllocateHeap(Desktop->pheapDesktop,
HEAP_NO_SERIALIZE,
Bytes);
}
从上面代码我们可以发现Window的内存来自Desktop->pheapDesktop
这个堆。
5.2 桌面堆的来源
在创建桌面的适合,使用的是UserInitializeDesktop
函数来初始化桌面堆,桌面堆的初始化如下:
static NTSTATUS
UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
{
//...
pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
&DesktopHeapSystemBase,
HeapSize);
//...
}
这个堆我们可以看到从pdesk->hsectionDesktop
映射取中分配,这个函数如下:
PWIN32HEAP
UserCreateHeap(OUT PVOID *SectionObject,
IN OUT PVOID *SystemBase,
IN SIZE_T HeapSize)
{
LARGE_INTEGER SizeHeap;
PWIN32HEAP pHeap = NULL;
NTSTATUS Status;
SizeHeap.QuadPart = HeapSize;
/* Create the section and map it into session space */
Status = MmCreateSection((PVOID*)SectionObject,
SECTION_ALL_ACCESS,
NULL,
&SizeHeap,
PAGE_EXECUTE_READWRITE, /* Would prefer PAGE_READWRITE, but thanks to RTL heaps... */
SEC_RESERVE | 1,
NULL,
NULL);
if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
return FALSE;
}
Status = MmMapViewInSessionSpace(*SectionObject,
SystemBase,
&HeapSize);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(*SectionObject);
*SectionObject = NULL;
SetLastNtError(Status);
return FALSE;
}
/* Create the heap */
pHeap = IntUserHeapCreate(*SectionObject,
SystemBase,
HeapSize);
if (pHeap == NULL)
{
ObDereferenceObject(*SectionObject);
*SectionObject = NULL;
SetLastNtError(STATUS_UNSUCCESSFUL);
}
return pHeap;
}
NTSTATUS
NTAPI
MmMapViewInSessionSpace(IN PVOID Section,
OUT PVOID *MappedBase,
IN OUT PSIZE_T ViewSize)
{
PAGED_CODE();
// HACK
if (MiIsRosSectionObject(Section))
{
return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
}
/* Process must be in a session */
if (PsGetCurrentProcess()->ProcessInSession == FALSE)
{
DPRINT1("Process is not in session\n");
return STATUS_NOT_MAPPED_VIEW;
}
/* Use the system space API, but with the session view instead */
ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
return MiMapViewInSystemSpace(Section,
&MmSessionSpace->Session,
MappedBase,
ViewSize);
}
从这里我们可以发现,所有的内存都是通过会话内存来的,也就是说通过桌面这个堆分配的GDI内存,都落在了会话内存中了,因此GDI对象也就通过会话进行内存隔离了(关键函数在于MmMapViewInSessionSpace
)。
一般来说,我们都是通过如下代码从会话空间的地址范围中获取虚拟地址:
StartBit = RtlFindClearBitsAndSet (Session->SystemSpaceBitMap,
SizeIn64k,
0);
Base = (PVOID)((PCHAR)Session->SystemSpaceViewStart + ((ULONG_PTR)StartBit * X64K));
5.3 会话内存相关设置
有时我们通常会遇到桌面堆内存用尽,导致异常的情况,其实我们可以在注册表下面配置这些东西,如下:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management]
"ClearPageFileAtShutdown"=dword:00000000
"DisablePagingExecutive"=dword:00000000
"LargeSystemCache"=dword:00000000
"NonPagedPoolQuota"=dword:00000000
"NonPagedPoolSize"=dword:00000000
"PagedPoolQuota"=dword:00000000
"PagedPoolSize"=dword:00000000
"PagingFiles"=hex(7):3f,00,3a,00,5c,00,70,00,61,00,67,00,65,00,66,00,69,00,6c,\
00,65,00,2e,00,73,00,79,00,73,00,00,00,00,00
"SecondLevelDataCache"=dword:00000000
"SessionPoolSize"=dword:00000004
"SessionViewSize"=dword:00000030
"SystemPages"=dword:00000000
"PhysicalAddressExtension"=dword:00000001
"FeatureSettings"=dword:00000000
"ExistingPageFiles"=hex(7):5c,00,3f,00,3f,00,5c,00,45,00,3a,00,5c,00,70,00,61,\
00,67,00,65,00,66,00,69,00,6c,00,65,00,2e,00,73,00,79,00,73,00,00,00,00,00