JIURL玩玩Win2k进程线程篇 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 的系统,我们有一种方法,可以不用切换地址空间,直接访问指定进程的地址空间中的内存。 我们知道 Win2k 把物理内存的前 512M 的每个物理页,一一对应的映射到地址空间的 80000000-9FFFFFFF 这一段 LargePage 区域中。物理内存不到512M的,有多少就映射多少。这样我们就可以访问所有不超过 512M 的物理内存(当然 80000000-9FFFFFFF 在系统地址空间中,访问需要 ring0 的权限)。对于一个小于512M的物理地址,我们可以用这个物理地址加上 80000000 ,得到该物理页映射的一页地址空间的虚拟地址,我们可以使用这个虚拟地址,来访问这个物理中的内容。从前面分析 DirectoryTableBase 时,SoftICE 的输出也可以看出这一点,比如,物理地址 7c4000 ,对应虚拟地址 807C4000 。 对于一个指定的进程,我们可以通过进程链表找到它,并计算出它的 EPROCESS 的地址。就可以找到 EPROCESS 中的该进程页目录所在物理页的物理地址,即 DirectoryTableBase[0] 的值。该进程页目录所在物理页的物理地址如果小于512M,我们就可以计算出这个物理页被映射到地址空间 80000000-9FFFFFFF 中的虚拟地址,就可以访问该进程的页目录。就可以找到该进程的所有页表的物理地址,同样我们根据页表的物理地址,可以算出页表在 80000000-9FFFFFFF 中的虚拟地址,就可以访问该进程的所有页表。就可以找到该进程所有页对应的物理页的物理地址。根据这个物理地址,我们可以算出该物理页在 80000000-9FFFFFFF 中的虚拟地址,从而访问改进程的某一页中的数据。 我们用 SoftICE 实际演示一下,读取进程 Explorer 地址空间 0x400000 中的内容。 :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 06A45000 84090220 02A8 conime *00030000 8046BB60 0000 Idle // Explorer 的 EPROCESS 地址为 8105D600 ,我们通过进程链表也可以找到 :dd 8105d600+18 l 10 0010:8105D618 007C4000 06165000 00000000 00000000 .@|..P.......... // Explorer 的页目录所在物理页的地址为 007C4000  :dd (80000000+7c4000)+(400000>22)*4 l 10 0010:807C4004 028D4067 0268E067 01E35067 0219B067 [email protected]... // (80000000+7c4000) 就是 Explorer 的页目录的虚拟地址 // (400000>22) 取虚拟地址 400000 的高10bit,这是页目录索引。 // 一个页目录项大小为4个字节,所以 (400000>22)*4 为 400000 对应页目录项在页目录中偏移地址 // 所以 400000 对应的页表的物理地址为 028D4000 (别忘了页目录项和页表项的低12位是标志) :dd (80000000+28d4000)+((400000&3ff000)>12)*4 l 10 0010:828D4000 02963005 00612025 08332C34 02954025 .0..% a.4,3.%@.. // ((400000&3ff000)>12) ,取虚拟地址 400000 作为页表索引的10bit, // ((400000&3ff000)>12)*4 为 400000 在对应页表项在页表中偏移地址 // 所以 400000 对应的物理页的物理地址为 02963000 :dd (80000000+2963000)+(400000&fff) l 10 0010:82963000 00905A4D 00000003 00000004 0000FFFF MZ.............. // (400000&fff),取虚拟地址 400000 的低12bit作为页内偏移。 // 最后得到进程 Explorer 虚拟地址 400000 处的4个字节值为 00905A4D ,就是"MZ.." // 切换到 Explorer 地址空间,验证一下 :addr explorer :dd 400000 l 10 0010:00400000 00905A4D 00000003 00000004 0000FFFF MZ.............. // 可以看到 Explorer 地址空间虚拟地址 00400000 处的值正是 00905A4D ,"MZ.." 进程的 HANDLE_TABLE +128 struct _HANDLE_TABLE *ObjectTable 进程的 PEB +1b0 struct _PEB *Peb 线程链表 进程的所有线程通过 LIST_ENTRY 结构链在了一个双向循环链表上。 一个链表是以 EPROCESS 结构的 KPROCESS Pcb 中的 ThreadListHead 为链表的链表头。链上的每一项是一个线程的 KTHREAD ETHREAD 结构的 Tcb 中的 ThreadListEntry 。 另一个链表是以 EPROCESS 结构中的 ThreadListHead 为链表的链表头。链上的每一项是一个线程的 ETHREAD 结构中的 ThreadListEntry 。 通过这两个链表中的任何一个,都可以找到一个进程的所有线程的 ETHREAD 结构,当然找到 ETHREAD 结构,就可以找到 ETHREAD 结构中的 KTHREAD。 KTHREAD 链表 struct _EPROCESS (sizeof=648) +000 struct _KPROCESS Pcb +050 struct _LIST_ENTRY ThreadListHead +050 struct _LIST_ENTRY *Flink +054 struct _LIST_ENTRY *Blink struct _ETHREAD (sizeof=584) +000 struct _KTHREAD Tcb +1a4 struct _LIST_ENTRY ThreadListEntry +1a4 struct _LIST_ENTRY *Flink +1a8 struct _LIST_ENTRY *Blink ETHREAD 链表 struct _EPROCESS (sizeof=648) +270 struct _LIST_ENTRY ThreadListHead +270 struct _LIST_ENTRY *Flink +274 struct _LIST_ENTRY *Blink struct _ETHREAD (sizeof=584) +240 struct _LIST_ENTRY ThreadListEntry +240 struct _LIST_ENTRY *Flink +244 struct _LIST_ENTRY *Blink VAD  +194 void *VadRoot +198 void *VadHint VadRoot 是进程 Vad 二叉树的根节点的指针。 通过反汇编和分析函数 MiLocateAddress() 可以知道 ,VadHint 是上一次 MiLocateAddress() 找到的 Vad 的指针。 WorkingSet +0e0 uint32 PeakWorkingSetSize +0e4 uint32 WorkingSetSize +0e8 uint32 MinimumWorkingSetSize +0ec uint32 MaximumWorkingSetSize +0f0 *VmWorkingSetList WorkingSet 的相关信息。 VmWorkingSetList 是指向 MMWSL 结构的指针。值总是 C0502000 。 Section +1ac void *SectionHandle +1b4 void *SectionBaseAddress SectionHandle 是句柄,可以用这个句柄在句柄表中找到对应的对象。 SectionBaseAddress 是载入地址空间的基地址。 进程对象的对象体 需要说明的是 EPROCESS 就是进程对象的对象体,象其他类型的对象一样,EPROCESS 之前也有对象头。使用 kd 可以很容易看到这一点 kd> !process 8 0 !process 8 0 Searching for Process with Cid == 8 PROCESS 8141e020 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000 DirBase: 00030000 ObjectTable: 81452a68 TableSize: 106. Image: System kd> !object 8141e020 !object 8141e020 Object: 8141e020 Type: (814524e0) Process ObjectHeader: 8141e008 HandleCount: 2 PointerCount: 36 根据 EPROCESS 的地址,kd 可以正确分析出对象类型是进程,说明了 EPROCESS 的确是对象体

你可能感兴趣的:(JIURL玩玩Win2k进程线程篇 EPROCESS)