EPROCESS 结构

每个进程都有一个 EPROCESS 结构,里面保存着进程的各种信息,和相关结构的指针。EPROCESS 结构位于系统地址空间,所以访问这个结构需要有ring0的权限。使用 Win2k DDK 的 KD (内核调试器)我们可以得到 EPROCESS 结构的定义。注意下面的是 Win2k Build 2195 下的 EPROCESS 结构定义。

kd> !strct eprocess
!strct eprocess
struct _EPROCESS (sizeof=648)
+000 struct _KPROCESS Pcb
+000 struct _DISPATCHER_HEADER Header
+000 byte Type
+001 byte Absolute
+002 byte Size
+003 byte Inserted
+004 int32 SignalState
+008 struct _LIST_ENTRY WaitListHead
+008 struct _LIST_ENTRY *Flink
+00c struct _LIST_ENTRY *Blink
+010 struct _LIST_ENTRY ProfileListHead
+010 struct _LIST_ENTRY *Flink
+014 struct _LIST_ENTRY *Blink
+018 uint32 DirectoryTableBase[2]
+020 struct _KGDTENTRY LdtDescriptor
+020 uint16 LimitLow
+022 uint16 BaseLow
+024 union __unnamed9 HighWord
+024 struct __unnamed10 Bytes
+024 byte BaseMid
+025 byte Flags1
+026 byte Flags2
+027 byte BaseHi
+024 struct __unnamed11 Bits
+024 bits0-7 BaseMid
+024 bits8-12 Type
+024 bits13-14 Dpl
+024 bits15-15 Pres
+024 bits16-19 LimitHi
+024 bits20-20 Sys
+024 bits21-21 Reserved_0
+024 bits22-22 Default_Big
+024 bits23-23 Granularity
+024 bits24-31 BaseHi
+028 struct _KIDTENTRY Int21Descriptor
+028 uint16 Offset
+02a uint16 Selector
+02c uint16 Access
+02e uint16 ExtendedOffset
+030 uint16 IopmOffset
+032 byte Iopl
+033 byte VdmFlag
+034 uint32 ActiveProcessors
+038 uint32 KernelTime
+03c uint32 UserTime
+040 struct _LIST_ENTRY ReadyListHead
+040 struct _LIST_ENTRY *Flink
+044 struct _LIST_ENTRY *Blink
+048 struct _LIST_ENTRY SwapListEntry
+048 struct _LIST_ENTRY *Flink
+04c struct _LIST_ENTRY *Blink
+050 struct _LIST_ENTRY ThreadListHead
+050 struct _LIST_ENTRY *Flink
+054 struct _LIST_ENTRY *Blink
+058 uint32 ProcessLock
+05c uint32 Affinity
+060 uint16 StackCount
+062 char BasePriority
+063 char ThreadQuantum
+064 byte AutoAlignment
+065 byte State
+066 byte ThreadSeed
+067 byte DisableBoost
+068 byte PowerState
+069 byte DisableQuantum
+06a byte Spare[2]
+06c int32 ExitStatus
+070 struct _KEVENT LockEvent
+070 struct _DISPATCHER_HEADER Header
+070 byte Type
+071 byte Absolute
+072 byte Size
+073 byte Inserted
+074 int32 SignalState
+078 struct _LIST_ENTRY WaitListHead
+078 struct _LIST_ENTRY *Flink
+07c struct _LIST_ENTRY *Blink
+080 uint32 LockCount
+088 union _LARGE_INTEGER CreateTime
+088 uint32 LowPart
+08c int32 HighPart
+088 struct __unnamed3 u
+088 uint32 LowPart
+08c int32 HighPart
+088 int64 QuadPart
+090 union _LARGE_INTEGER ExitTime
+090 uint32 LowPart
+094 int32 HighPart
+090 struct __unnamed3 u
+090 uint32 LowPart
+094 int32 HighPart
+090 int64 QuadPart
+098 struct _KTHREAD *LockOwner
+09c void *UniqueProcessId
+0a0 struct _LIST_ENTRY ActiveProcessLinks
+0a0 struct _LIST_ENTRY *Flink
+0a4 struct _LIST_ENTRY *Blink
+0a8 uint32 QuotaPeakPoolUsage[2]
+0b0 uint32 QuotaPoolUsage[2]
+0b8 uint32 PagefileUsage
+0bc uint32 CommitCharge
+0c0 uint32 PeakPagefileUsage
+0c4 uint32 PeakVirtualSize
+0c8 uint32 VirtualSize
+0d0 struct _MMSUPPORT Vm
+0d0 union _LARGE_INTEGER LastTrimTime
+0d0 uint32 LowPart
+0d4 int32 HighPart
+0d0 struct __unnamed3 u
+0d0 uint32 LowPart
+0d4 int32 HighPart
+0d0 int64 QuadPart
+0d8 uint32 LastTrimFaultCount
+0dc uint32 PageFaultCount
+0e0 uint32 PeakWorkingSetSize
+0e4 uint32 WorkingSetSize
+0e8 uint32 MinimumWorkingSetSize
+0ec uint32 MaximumWorkingSetSize
+0f0 *VmWorkingSetList
+0f4 struct _LIST_ENTRY WorkingSetExpansionLinks
+0f4 struct _LIST_ENTRY *Flink
+0f8 struct _LIST_ENTRY *Blink
+0fc byte AllowWorkingSetAdjustment
+0fd byte AddressSpaceBeingDeleted
+0fe byte ForegroundSwitchCount
+0ff byte MemoryPriority
+100 union __unnamed13 u
+100 uint32 LongFlags
+100 struct _MMSUPPORT_FLAGS Flags
+100 bits0-0 SessionSpace
+100 bits1-1 BeingTrimmed
+100 bits2-2 ProcessInSession
+100 bits3-3 SessionLeader
+100 bits4-4 TrimHard
+100 bits5-5 WorkingSetHard
+100 bits6-6 WriteWatch
+100 bits7-31 Filler
+104 uint32 Claim
+108 uint32 NextEstimationSlot
+10c uint32 NextAgingSlot
+110 uint32 EstimatedAvailable
+114 uint32 GrowthSinceLastEstimate
+118 struct _LIST_ENTRY SessionProcessLinks
+118 struct _LIST_ENTRY *Flink
+11c struct _LIST_ENTRY *Blink
+120 void *DebugPort
+124 void *ExceptionPort
+128 struct _HANDLE_TABLE *ObjectTable
+12c void *Token
+130 struct _FAST_MUTEX WorkingSetLock
+130 int32 Count
+134 struct _KTHREAD *Owner
+138 uint32 Contention
+13c struct _KEVENT Event
+13c struct _DISPATCHER_HEADER Header
+13c byte Type
+13d byte Absolute
+13e byte Size
+13f byte Inserted
+140 int32 SignalState
+144 struct _LIST_ENTRY WaitListHead
+144 struct _LIST_ENTRY *Flink
+148 struct _LIST_ENTRY *Blink
+14c uint32 OldIrql
+150 uint32 WorkingSetPage
+154 byte ProcessOutswapEnabled
+155 byte ProcessOutswapped
+156 byte AddressSpaceInitialized
+157 byte AddressSpaceDeleted
+158 struct _FAST_MUTEX AddressCreationLock
+158 int32 Count
+15c struct _KTHREAD *Owner
+160 uint32 Contention
+164 struct _KEVENT Event
+164 struct _DISPATCHER_HEADER Header
+164 byte Type
+165 byte Absolute
+166 byte Size
+167 byte Inserted
+168 int32 SignalState
+16c struct _LIST_ENTRY WaitListHead
+16c struct _LIST_ENTRY *Flink
+170 struct _LIST_ENTRY *Blink
+174 uint32 OldIrql
+178 uint32 HyperSpaceLock
+17c struct _ETHREAD *ForkInProgress
+180 uint16 VmOperation
+182 byte ForkWasSuccessful
+183 byte MmAgressiveWsTrimMask
+184 struct _KEVENT *VmOperationEvent
+188 void *PaeTop
+18c uint32 LastFaultCount
+190 uint32 ModifiedPageCount
+194 void *VadRoot
+198 void *VadHint
+19c void *CloneRoot
+1a0 uint32 NumberOfPrivatePages
+1a4 uint32 NumberOfLockedPages
+1a8 uint16 NextPageColor
+1aa byte ExitProcessCalled
+1ab byte CreateProcessReported
+1ac void *SectionHandle
+1b0 struct _PEB *Peb
+1b4 void *SectionBaseAddress
+1b8 struct _EPROCESS_QUOTA_BLOCK *QuotaBlock
+1bc int32 LastThreadExitStatus
+1c0 struct _PAGEFAULT_HISTORY *WorkingSetWatch
+1c4 void *Win32WindowStation
+1c8 void *InheritedFromUniqueProcessId
+1cc uint32 GrantedAccess
+1d0 uint32 DefaultHardErrorProcessing
+1d4 void *LdtInformation
+1d8 void *VadFreeHint
+1dc void *VdmObjects
+1e0 void *DeviceMap
+1e4 uint32 SessionId
+1e8 struct _LIST_ENTRY PhysicalVadList
+1e8 struct _LIST_ENTRY *Flink
+1ec struct _LIST_ENTRY *Blink
+1f0 struct _HARDWARE_PTE_X86 PageDirectoryPte
+1f0 bits0-0 Valid
+1f0 bits1-1 Write
+1f0 bits2-2 Owner
+1f0 bits3-3 WriteThrough
+1f0 bits4-4 CacheDisable
+1f0 bits5-5 Accessed
+1f0 bits6-6 Dirty
+1f0 bits7-7 LargePage
+1f0 bits8-8 Global
+1f0 bits9-9 CopyOnWrite
+1f0 bits10-10 Prototype
+1f0 bits11-11 reserved
+1f0 bits12-31 PageFrameNumber
+1f0 uint64 Filler
+1f8 uint32 PaePageDirectoryPage
+1fc byte ImageFileName[16]
+20c uint32 VmTrimFaultValue
+210 byte SetTimerResolution
+211 byte PriorityClass
+212 byte SubSystemMinorVersion
+213 byte SubSystemMajorVersion
+212 uint16 SubSystemVersion
+214 void *Win32Process
+218 struct _EJOB *Job
+21c uint32 JobStatus
+220 struct _LIST_ENTRY JobLinks
+220 struct _LIST_ENTRY *Flink
+224 struct _LIST_ENTRY *Blink
+228 void *LockedPagesList
+22c void *SecurityPort
+230 struct _WOW64_PROCESS *Wow64Process
+238 union _LARGE_INTEGER ReadOperationCount
+238 uint32 LowPart
+23c int32 HighPart
+238 struct __unnamed3 u
+238 uint32 LowPart
+23c int32 HighPart
+238 int64 QuadPart
+240 union _LARGE_INTEGER WriteOperationCount
+240 uint32 LowPart
+244 int32 HighPart
+240 struct __unnamed3 u
+240 uint32 LowPart
+244 int32 HighPart
+240 int64 QuadPart
+248 union _LARGE_INTEGER OtherOperationCount
+248 uint32 LowPart
+24c int32 HighPart
+248 struct __unnamed3 u
+248 uint32 LowPart
+24c int32 HighPart
+248 int64 QuadPart
+250 union _LARGE_INTEGER ReadTransferCount
+250 uint32 LowPart
+254 int32 HighPart
+250 struct __unnamed3 u
+250 uint32 LowPart
+254 int32 HighPart
+250 int64 QuadPart
+258 union _LARGE_INTEGER WriteTransferCount
+258 uint32 LowPart
+25c int32 HighPart
+258 struct __unnamed3 u
+258 uint32 LowPart
+25c int32 HighPart
+258 int64 QuadPart
+260 union _LARGE_INTEGER OtherTransferCount
+260 uint32 LowPart
+264 int32 HighPart
+260 struct __unnamed3 u
+260 uint32 LowPart
+264 int32 HighPart
+260 int64 QuadPart
+268 uint32 CommitChargeLimit
+26c uint32 CommitChargePeak
+270 struct _LIST_ENTRY ThreadListHead
+270 struct _LIST_ENTRY *Flink
+274 struct _LIST_ENTRY *Blink
+278 struct _RTL_BITMAP *VadPhysicalPagesBitMap
+27c uint32 VadPhysicalPages
+280 uint32 AweLock

遍历所有进程

系统需要能够方便的遍历所有进程。所以进程的 EPROCESS 结构使用链表链在了一起。

所有进程(除了Idle进程)的 EPROCESS 通过 EPROCESS 结构偏移+a0处的 LIST_ENTRY ActiveProcessLinks 链在一起。
+0a0 struct _LIST_ENTRY ActiveProcessLinks
+0a0 struct _LIST_ENTRY *Flink
+0a4 struct _LIST_ENTRY *Blink

通过全局变量 PsActiveProcessHead 可以找到这个链。
PID 为 0 的 Idle 进程并没有链在这个链上。我们可以通过全局变量 PsIdleProcess 找到 Idle 进程的 EPROCESS。

使用 LIST_ENTRY 结构来组织链表在 Win2k 中非常常见。Flink,Blink指向的都是另一个 LIST_ENTRY 结构。

对于 Win2k Build 2195 来说,全局变量 PsActiveProcessHead 的地址是 0x8046a180,开始处就是一个 LIST_ENTRY 结构。顺着这个 LIST_ENTRY 结构的 Flink 或者 Blink,我们可以找到每一个进程(除了Idle)的EPROCESS 结构中的 LIST_ENTRY ActiveProcessLinks。这个 ActiveProcessLinks 地址偏移 -0a0 处就是 EPROCESS 结构的地址。从 PsActiveProcessHead 开始顺着这条链一直走下去,直到重新回到 PsActiveProcessHead ,链已经循环了,就说明已经遍历了整个链。

下面我们看实际的例子

kd> ? PsActiveProcessHead
? PsActiveProcessHead
Evaluate expression: -2142854784 = 8046a180
// Win2k Build 2195 的 PsActiveProcessHead 位于地址 8046a180

kd> dd 8046a180 l 2
dd 8046a180 l 2
8046a180 8141e0c0 82fa4b00
// 8046a180 处的 LIST_ENTRY,注意 PsActiveProcessHead 并不是某个进程的 +0a0 ActiveProcessLinks
// 我们看看 Flink 找到的进程

kd> ? 8141e0c0-a0
? 8141e0c0-a0
Evaluate expression: -2126389216 = 8141e020
// ActiveProcessLinks 位于 EPROCESS 的 +0a0 处。
// 我们得到的是 ActiveProcessLinks 的地址,所以 EPROCESS 在得到地址 -0a0 处。

kd> !process 8141e020 0
!process 8141e020 0
PROCESS 8141e020 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000
DirBase: 00030000 ObjectTable: 81452a68 TableSize: 46.
Image: System
// 是PID为8的进程 System
... 
// 顺着 Flink 一直走下去,我们省去了中间的过程

kd> dd 82fa4b00 l 2
dd 82fa4b00 l 2
82fa4b00 8046a180 807e90c0
// 看到了这个 ActiveProcessLinks 的 Flink 为 8046a180 ,就是 PsActiveProcessHead 
// 说明已经遍历了整个 ActiveProcessLinks 链

对于 Win2k Build 2195 来说,全局变量 PsIdleProcess 的地址是 0x8046a1fc,是一个 EPROCESS 指针,直接指向 Idle 进程的 EPROCESS 结构。

kd> ? PsIdleProcess
? PsIdleProcess
Evaluate expression: -2142854660 = 8046a1fc
// Win2k Build 2195 的 PsIdleProcess 位于地址 8046a1fc

kd> dd 8046a1fc l 1
dd 8046a1fc l 1
8046a1fc 8046bb60
// PsIdleProcess 的值为 8046bb60

kd> !process 8046bb60 0
!process 8046bb60 0
PROCESS 8046bb60 SessionId: 0 Cid: 0000 Peb: 00000000 ParentCid: 0000
DirBase: 00030000 ObjectTable: 81452a68 TableSize: 46.
Image: Idle
// 可以看到 8046bb60 是指向 EPROCESS 的,并且这个 EPROCESS 是 PID 为 0 的 Idle 进程的 EPROCESS。


进程名

EPROCESS 中保存着一个给人看的进程名,Windows 任务管理器中显示的进程名,就是从这里获得的。
更准确的叫法是映像名称。
+1fc byte ImageFileName[16]
ImageFileName[16] 是一个16个字节长的字节数组,保存着进程名。当进程名的长度大于等于16个字节时,在 ImageFileName[16] 只保存进程名的前15个字节,ImageFileName[16] 最后一个字节为0,字符串的结束符。
不同进程的进程名可以相同,比如打开多个记事本,那么每个记事本的 ImageFileName[16] 都是 "NOTEPAD.EXE",进程名只是给人看的,每个进程的 进程ID 都是不同的。

kd> !process 200 0
!process 200 0
Searching for Process with Cid == 200
PROCESS 8105c2e0 SessionId: 0 Cid: 0200 Peb: 7ffdf000 ParentCid: 01f0
DirBase: 00564000 ObjectTable: 81090c48 TableSize: 298.
Image: Explorer.exe

kd> db 8105c2e0+1fc l 10
db 8105c2e0+1fc l 10
8105c4dc 45 78 70 6c 6f 72 65 72-2e 65 78 65 00 00 00 00 Explorer.exe....


进程ID (PID)

+09c void *UniqueProcessId

父进程ID  

+1c8 void *InheritedFromUniqueProcessId


页目录

+018 uint32 DirectoryTableBase[2]

每个进程有自己4G地址空间,当进程切换时,就需要切换地址空间。也就是切换页目录页表。所以每个进程都需要保存自己页目录的地址。每个进程的页目录所在物理页的物理地址,就保存在进程 EPROCESS 结构偏移 +018 处 DirectoryTableBase数组的第0项中,即 DirectoryTableBase[0]。对于执行地址转换的 CPU 来说需要知道页目录所在物理页的物理地址就可以进行地址转换。对于维护进程的页目录和页表的系统来说,需要把页目录和页表所在的物理页映射到地址空间中。

下面使用 SoftICE 来说明,进程 EPROCESS 中的 DirectoryTableBase数组,有2个元素,其中DirectoryTableBase[0] 就是该进程的页目录的物理地址,也就是该进程 CR3 的值。

:addr
CR3 LDT Base:Limit KPEB Addr PID Name
00030000 8141E020 0008 System
04E4B000 810F7580 008C smss
06582000 810E8D60 00A8 csrss
07607000 810CC2C0 00BC winlogon
07A49000 810C1500 00D8 services
0799A000 810BFD60 00E4 lsass
00AFD000 810A2D60 0174 svchost
00F21000 81092940 0190 svchost
007C4000 8105D600 0200 Explorer
024B9000 824D0020 0260 internat
042B2000 8423E860 0180 conime
*00030000 8046BB60 0000 Idle
// addr 列出当前进程,注意每个进程的 CR3 , SoftICE 显示的 KPEB Addr 就是 EPROCESS 的地址

// 转换到 explorer 的地址空间,EPROCESS 在 8105D600 
// 偏移 +018 处 是 uint32 DirectoryTableBase[2] 数组
:addr explorer
:dd 8105d600+18 l 10
0010:8105D618 007C4000 06165000 00000000 00000000 .@|..P..........
// DirectoryTableBase[0] 物理地址为 007C4000 看看这个物理地址,被映射到了哪些虚拟地址
:phys 7c4000
807C4000
C0300000
// C0300000 是进程页目录被映射到的地址空间
:phys 6165000
86165000
C0301000

// 情况一样
:addr internat
:dd 824d0020+18 l 10
0010:824D0038 024B9000 024BA000 00000000 00000000 ..K...K.........
:phys 24b9000
824B9000
C0300000
:phys 24ba000
824BA000
C0301000

// 情况一样
:addr csrss
:dd 810e8d60+18 l 10
0010:810E8D78 06582000 06563000 00000000 00000000 . X..0V.........
:phys 6582000
86582000
C0300000
:phys 6563000
86563000
C0301000

// 注意这是 System 进程
// System 的 DirectoryTableBase[0] 仍然映射到了 C0300000
// 但是 System 的 DirectoryTableBase[1] 情况和别的进程不一样
:addr system
:dd 8141e020+18 l 10
0010:8141E038 00030000 00000000 00000000 00000000 ................
:phys 30000
80030000
C0300000
C04FB000
EB3F1000
F09CA000
:phys 00000000
80000000
C0200000
EB3C1000
F09D4000

我们可以看到 DirectoryTableBase[0] 中就是页目录所在物理页的物理地址。每个进程 EPROCESS +18 处4个字节的值和 该进程 CR3 中的值是一样的。 EPROCESS +18 处4个字节的物理地址都映射到了 C0300000,而 C0300000 正是页目录所在物理页映射到地址空间中的虚拟地址。我也检查了一个进程的 DirectoryTableBase[0] (EPROCESS +18 处4个字节的值)所指的物理页中的内容,里面的确是 该进程页目录的内容。

从 SoftICE 的输出中,我们可以看到 DirectoryTableBase[1] 中的物理地址所指的物理页,被映射到了地址空间 C0301000 处,也就是 C0301000-C0301FFF 这个页表,我们计算可以得到这个页表负责的是 C0400000-C07fffff 这 4M 虚拟地址空间的映射。我们知道每个进程的 Working Set 开始于 C0502000 ,就落在这个范围之内,而每个进程都有自己的 Working Set。

其实一个进程保存自己的页目录的物理地址就足够了。由于每个进程的 Working Set 在 C0400000-C07fffff 这个范围之内,每个进程的 Working Set 是不同的,所以负责这个范围的页表也是不同的,所以有理由多保存一个负责地址空间 映射的页表的物理地址。不过要注意的是,进程 System 的 DirectoryTableBase[1] 值为0,并不是对应地址空间范围 C0400000-C07fffff 的页表所在物理页的物理地址。


不切换进程,直接访问其他进程的地址空间

我们使用(驱动程序中使用)KeAttachProcess(),KeDetachProcess() 切换地址空间到一个指定进程的地址空间,然后访问这个进程地址空间中的内存。现在对于物理内存不超过 512M 的系统,我们有一种方法,可以不用切换地址空间,直接访问指定进程的地址空间中的内存

你可能感兴趣的:(驱动)