# EPROCESS

概述

  1. 每个 Windows 进程在 0 环都有一个对应的 EPROCESS 结构体
  2. 该结构体包含了进程的所有重要信息

kd> dt _EPROCESS

1
2
3
4
5
6
7
8
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER // 进程创建时间
+0x078 ExitTime : _LARGE_INTEGER // 进程退出时间
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void // 进程 PID
+0x088 ActiveProcessLinks : _LIST_ENTRY // 双向链表(详见下文)

ActiveProcessLinks 是一个双向链表,用于将系统中所有活动进程串联在一起:

  1. 所有活动进程都通过此链表连接
  2. 全局变量 PsActiveProcessHead 指向链表头
  3. Flink (第一个成员)指向后一个进程的 ActiveProcessLinksBlink (第二个成员)指向前一个进程的 ActiveProcessLinks

注意:链表指针指向的位置是下一个进程结构体的 ActiveProcessLinks 成员,而非 EPROCESS 起始地址,访问 EPROCESS 其他字段时需要手动调整偏移。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
+0x090 QuotaUsage       : [3] Uint4B           // 物理页相关统计信息
+0x09c QuotaPeak : [3] Uint4B // 物理页相关统计信息
+0x0a8 CommitCharge : Uint4B // 虚拟内存相关统计信息
+0x0ac PeakVirtualSize : Uint4B // 虚拟内存相关统计信息
+0x0b0 VirtualSize : Uint4B // 虚拟内存相关统计信息
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void // 调试端口
+0x0c0 ExceptionPort : Ptr32 Void // 异常端口
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE // 句柄表,记录了进程使用的所有句柄
// 可遍历句柄表查看哪些进程使用了某个句柄,用于反调试
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : Uint4B
+0x114 ForkInProgress : Ptr32 _ETHREAD
+0x118 HardwareTrigger : Uint4B
+0x11c VadRoot : Ptr32 Void // VAD 树根节点,标识 0~2G 哪些地址已被占用
+0x120 VadHint : Ptr32 Void
+0x124 CloneRoot : Ptr32 Void
+0x128 NumberOfPrivatePages : Uint4B
+0x12c NumberOfLockedPages : Uint4B
+0x130 Win32Process : Ptr32 Void
+0x134 Job : Ptr32 _EJOB
+0x138 SectionObject : Ptr32 Void
+0x13c SectionBaseAddress : Ptr32 Void
+0x140 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x144 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x148 Win32WindowStation : Ptr32 Void
+0x14c InheritedFromUniqueProcessId : Ptr32 Void
+0x150 LdtInformation : Ptr32 Void
+0x154 VadFreeHint : Ptr32 Void
+0x158 VdmObjects : Ptr32 Void
+0x15c DeviceMap : Ptr32 Void
+0x160 PhysicalVadList : _LIST_ENTRY
+0x168 PageDirectoryPte : _HARDWARE_PTE_X86
+0x168 Filler : Uint8B
+0x170 Session : Ptr32 Void
+0x174 ImageFileName : [16] UChar // 进程镜像文件名(最多 16 字节)
+0x184 JobLinks : _LIST_ENTRY
+0x18c LockedPagesList : Ptr32 Void
+0x190 ThreadListHead : _LIST_ENTRY
+0x198 SecurityPort : Ptr32 Void
+0x19c PaeTop : Ptr32 Void
+0x1a0 ActiveThreads : Uint4B // 活动线程数量
+0x1a4 GrantedAccess : Uint4B
+0x1a8 DefaultHardErrorProcessing : Uint4B
+0x1ac LastThreadExitStatus : Int4B
+0x1b0 Peb : Ptr32 _PEB // 进程环境块(3 环结构体)
+0x1b4 PrefetchTrace : _EX_FAST_REF
+0x1b8 ReadOperationCount : _LARGE_INTEGER
+0x1c0 WriteOperationCount : _LARGE_INTEGER
+0x1c8 OtherOperationCount : _LARGE_INTEGER
+0x1d0 ReadTransferCount : _LARGE_INTEGER
+0x1d8 WriteTransferCount : _LARGE_INTEGER
+0x1e0 OtherTransferCount : _LARGE_INTEGER
+0x1e8 CommitChargeLimit : Uint4B
+0x1ec CommitChargePeak : Uint4B
+0x1f0 AweInfo : Ptr32 Void
+0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1f8 Vm : _MMSUPPORT
+0x238 LastFaultCount : Uint4B
+0x23c ModifiedPageCount : Uint4B
+0x240 NumberOfVads : Uint4B
+0x244 JobStatus : Uint4B
+0x248 Flags : Uint4B
+0x248 CreateReported : Pos 0, 1 Bit
+0x248 NoDebugInherit : Pos 1, 1 Bit
+0x248 ProcessExiting : Pos 2, 1 Bit
+0x248 ProcessDelete : Pos 3, 1 Bit
+0x248 Wow64SplitPages : Pos 4, 1 Bit
+0x248 VmDeleted : Pos 5, 1 Bit
+0x248 OutswapEnabled : Pos 6, 1 Bit
+0x248 Outswapped : Pos 7, 1 Bit
+0x248 ForkFailed : Pos 8, 1 Bit
+0x248 HasPhysicalVad : Pos 9, 1 Bit
+0x248 AddressSpaceInitialized : Pos 10, 2 Bits
+0x248 SetTimerResolution : Pos 12, 1 Bit
+0x248 BreakOnTermination : Pos 13, 1 Bit
+0x248 SessionCreationUnderway : Pos 14, 1 Bit
+0x248 WriteWatch : Pos 15, 1 Bit
+0x248 ProcessInSession : Pos 16, 1 Bit
+0x248 OverrideAddressSpace : Pos 17, 1 Bit
+0x248 HasAddressSpace : Pos 18, 1 Bit
+0x248 LaunchPrefetched : Pos 19, 1 Bit
+0x248 InjectInpageErrors : Pos 20, 1 Bit
+0x248 VmTopDown : Pos 21, 1 Bit
+0x24c ExitStatus : Int4B
+0x250 NextPageColor : Uint2B
+0x252 SubSystemMinorVersion : UChar
+0x253 SubSystemMajorVersion : UChar
+0x252 SubSystemVersion : Uint2B
+0x254 PriorityClass : UChar
+0x255 WorkingSetAcquiredUnsafe : UChar
+0x258 Cookie : Uint4B

查看某个进程的 EPROCESS 信息:

1
dt _EPROCESS [进程 EPROCESS 地址]

# +0x000 Pcb : _KPROCESS

_KPROCESSEPROCESS 的第一个成员,包含了进程调度和地址空间管理的关键信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
kd> dt _KPROCESS
ntdll!_KPROCESS
+0x000 Header : _DISPATCHER_HEADER // 可等待对象,可作为 WaitForSingleObject 等函数的参数
// 常用于 Mutex 互斥体、Event 事件等
+0x010 ProfileListHead : _LIST_ENTRY
+0x018 DirectoryTableBase : [2] Uint4B // 页目录表基址,进程最重要的成员之一
// 控制了页目录表 = 控制了所有物理页
// 其值即 CR3 寄存器的值
+0x020 LdtDescriptor : _KGDTENTRY
+0x028 Int21Descriptor : _KIDTENTRY
+0x030 IopmOffset : Uint2B
+0x032 Iopl : UChar
+0x033 Unused : UChar
+0x034 ActiveProcessors : Uint4B
+0x038 KernelTime : Uint4B // 进程在 0 环运行的时间
+0x03c UserTime : Uint4B // 进程在 3 环运行的时间
+0x040 ReadyListHead : _LIST_ENTRY
+0x048 SwapListEntry : _SINGLE_LIST_ENTRY
+0x04c VdmTrapcHandler : Ptr32 Void
+0x050 ThreadListHead : _LIST_ENTRY
+0x058 ProcessLock : Uint4B
+0x05c Affinity : Uint4B // 规定进程中所有线程能在哪些 CPU 上运行
// 值为 1 (0b01):只能在 CPU 0 上运行
// 值为 3 (0b11):能在 CPU 0、1 上运行
// 值为 4 (0b100):只能在 CPU 2 上运行
// 值为 5 (0b101):能在 CPU 0、2 上运行
// 32 位系统最多支持 32 核,64 位系统最多 64 核
// 若设置为不存在的 CPU,该进程将无法运行
+0x060 StackCount : Uint2B
+0x062 BasePriority : Char // 基础优先级,进程中所有线程的最低优先级
+0x063 ThreadQuantum : Char
+0x064 AutoAlignment : UChar
+0x065 State : UChar
+0x066 ThreadSeed : UChar
+0x067 DisableBoost : UChar
+0x068 PowerState : UChar
+0x069 DisableQuantum : UChar
+0x06a IdealNode : UChar
+0x06b Flags : _KEXECUTE_OPTIONS
+0x06b ExecuteOptions : UChar

由于 KPROCESSEPROCESS 的第一个成员,两者起始地址相同,因此可以使用同一个地址查看:

1
dt _KPROCESS [进程 EPROCESS 地址]

# +0x1b0 Peb : Ptr32 _PEB

PEB(Process Environment Block,进程环境块)是进程在 3 环的一个结构体,包含了进程的模块列表、调试状态等信息。

详情可参考潘爱民老师《Windows 内核原理与实现》第三章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
kd> dt _PEB
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar // 若进程处于被调试状态,值为 1
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA // 模块加载信息(详见下文)
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 FastPebLockRoutine : Ptr32 Void
+0x024 FastPebUnlockRoutine : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 AtlThunkSListPtr32 : Uint4B
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 Void
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32 void
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 Void
+0x1fc ProcessAssemblyStorageMap : Ptr32 Void
+0x200 SystemDefaultActivationContextData : Ptr32 Void
+0x204 SystemAssemblyStorageMap : Ptr32 Void
+0x208 MinimumStackCommit : Uint4B

从 3 环访问 PEB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <windows.h>
#include <winternl.h>
#include <stdio.h>

int main() {
PPEB pPeb = NULL;
#ifdef _WIN64
pPeb = (PPEB)__readgsqword(0x60); // 64 位:GS:[0x60] -> PEB
#else
pPeb = (PPEB)__readfsdword(0x30); // 32 位:FS:[0x30] -> PEB(FS:[0] 指向 TEB,TEB+0x30 为 PEB 指针)
#endif
printf("PEB Address: 0x%p\n", pPeb);
return 0;
}

从 0 环访问 PEB

1
dt _PEB [进程 PEB 地址]

# +0x00c Ldr : Ptr32 _PEB_LDR_DATA

Ldr 包含了当前进程已加载模块(DLL 和 EXE)的信息,通过三种排列顺序的双向链表进行组织:

1
2
3
4
5
6
7
8
9
kd> dt _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
+0x000 Length : Uint4B
+0x004 Initialized : UChar
+0x008 SsHandle : Ptr32 Void
+0x00c InLoadOrderModuleList : _LIST_ENTRY // 按加载顺序排列
+0x014 InMemoryOrderModuleList : _LIST_ENTRY // 按内存地址顺序排列
+0x01c InInitializationOrderModuleList : _LIST_ENTRY // 按初始化顺序排列
+0x024 EntryInProgress : Ptr32 Void

# 练习:使用 WinDbg 对进程进行断链

目标:将 notepad 从进程链表中摘除,使其在任务管理器中不可见。

一、 打开一个 notepad。

二、 在 WinDbg 中使用 !process 0 0 命令定位 notepad 的 EPROCESS

三、 查看 notepad 的 EPROCESS 结构体,定位 ActiveProcessLinks 成员。

四、 执行断链操作。

ActiveProcessLinks 是一个双向链表,断链的本质就是将前后节点直接相连,绕过 notepad 节点:

1
2
3
4
5
// 将 notepad 后一个进程的 Blink 改为指向 notepad 的前一个进程
kd> ed (0x805648b8 + 4) 0x85eead48

// 将 notepad 前一个进程的 Flink 改为指向 notepad 的后一个进程
kd> ed 0x85eead48 0x805648b8

五、 恢复系统运行后打开任务管理器,此时已看不到 notepad 进程,但 notepad 程序仍然在正常运行。

说明:断链只是将进程从 ActiveProcessLinks 链表中移除,任务管理器通过遍历该链表获取进程列表,因此断链后进程 "消失" 了。但操作系统调度线程并不依赖此链表,所以程序本身的运行不受影响。