应用软件在用户空间可以访问的最低地址是MM_LOWEST_USER_ADDRESS,定义为0x10000,就是一个64KB边界的地方,从0开始的64KB不能访问
KI_USER_SHARED_DATA这个常数定义为0xFFDF0000,是一个区间的起点。这个区间是在系统空间,却又划出来让用户空间的程序访问,目的是用来让用户空间的程序访问内核中的一些数据。而且,这个区间是由系统空间和所有用户空间共享即为所有进程所共享的。
nt!_KUSER_SHARED_DATA +0x000 TickCountLow : Uint4B +0x004 TickCountMultiplier : Uint4B +0x008 InterruptTime : _KSYSTEM_TIME +0x014 SystemTime : _KSYSTEM_TIME +0x020 TimeZoneBias : _KSYSTEM_TIME +0x02c ImageNumberLow : Uint2B +0x02e ImageNumberHigh : Uint2B +0x030 NtSystemRoot : [260] Uint2B +0x238 MaxStackTraceDepth : Uint4B +0x23c CryptoExponent : Uint4B +0x240 TimeZoneId : Uint4B +0x244 Reserved2 : [8] Uint4B +0x264 NtProductType : _NT_PRODUCT_TYPE +0x268 ProductTypeIsValid : UChar +0x26c NtMajorVersion : Uint4B +0x270 NtMinorVersion : Uint4B +0x274 ProcessorFeatures : [64] UChar +0x2b4 Reserved1 : Uint4B +0x2b8 Reserved3 : Uint4B +0x2bc TimeSlip : Uint4B +0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE +0x2c8 SystemExpirationDate : _LARGE_INTEGER +0x2d0 SuiteMask : Uint4B +0x2d4 KdDebuggerEnabled : UChar +0x2d5 NXSupportPolicy : UChar +0x2d8 ActiveConsoleId : Uint4B +0x2dc DismountCount : Uint4B +0x2e0 ComPlusPackage : Uint4B +0x2e4 LastSystemRITEventTickCount : Uint4B +0x2e8 NumberOfPhysicalPages : Uint4B +0x2ec SafeBootMode : UChar +0x2f0 TraceLogging : Uint4B +0x2f8 TestRetInstruction : Uint8B +0x300 SystemCall : Uint4B +0x304 SystemCallReturn : Uint4B +0x308 SystemCallPad : [3] Uint8B +0x320 TickCount : _KSYSTEM_TIME +0x320 TickCountQuad : Uint8B +0x330 Cookie : Uint4B
地址0xFFDF0000就是KUSER_SHARED_DATA数据结构的起点,而用户空间的程序则可以通过指针SharedUserData读取这个结构中各个成分的内容。
进程用户空间的创建:
1.进程控制块EPROCESS结构中有个成分VadRoot,就是代表用户空间的MADRESS_SPACE数据结构。所以在创建进程时要通过MmInitializeAddressSpace()对新建进程的地址空间VadRoot进行初始化,特别是将此数据结构中的成分LowestAddress设置成MM_LOWEST_USER_ADDRESS
2.建立用户共享区,把从USER_SHARED_DATA即0xFFDF0000开始的一个页面分配给用户空间,让用户空间的程序可以对该页面作“可执行和只读”访问。实际大小是一个页面4KB。
3.目标EXE映像的映射,在调用NtCreateProcess之前,调用者已经为目标映像创建了一个Section即“文件映射区”,通过MmMapViewOfSection把这个映射区映射到新建进程的用户空间。
4.把许多动态链接库也装入或映射到用户空间。ntdll.dll是最基本的DLL,Windows内核在初始化阶段首次需要装入这个DLL的时候为其创建一个文件映射区,并使一个全局指针PspSystemDllSection指向该对象的数据结构,以后凡需要装入这个DLL的进程就只有映射这个文件映射区就可以了。
5.进程环境块PEB的建立,有PspCreateProcess调用MmCreatePeb完成的。MiCreatePeborTeb在分配地址空间时是由上而下的,所以是从0x7FFE0000开始往下搜索,以找到第一块满足大小要求的区间。由于此时的用户空间尚是空白,所要求的地址肯定能够满足,而PEB和TEB的大小都是一个页面,即0x1000(4KB),所以PEB分得的地址肯定是0x7FFDF000。
6.通过MmMapViewOfSection将NLS的码表映射到用户空间。
7.初始化PEB,PEB占用一个页面,但是实际使用的大小却没有那么大,所以把剩下的部分用做一个存储堆(Heap)指针数组。剩余的部分能容纳多少个指针,这个进程最多就可以有多少个堆。
8.把进程参数写入新进程的参数块。
9.创建新进程的第一个线程及TEB。