x86进程线程

1、EPROCESS

进程结构,每个进程都有这样一个结构。EPROCESS中还有一个KPROCESS,其中EPROCESS被称为执行体,主要是给R3进行访问;KPROCESS才是真正的对象结构。

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//0x2c0 bytes (sizeof)
struct _EPROCESS
{
struct _KPROCESS Pcb; //0x0
struct _EX_PUSH_LOCK ProcessLock; //0x98
union _LARGE_INTEGER CreateTime; //0xa0
union _LARGE_INTEGER ExitTime; //0xa8
struct _EX_RUNDOWN_REF RundownProtect; //0xb0
VOID* UniqueProcessId; //0xb4
struct _LIST_ENTRY ActiveProcessLinks; //0xb8
ULONG ProcessQuotaUsage[2]; //0xc0
ULONG ProcessQuotaPeak[2]; //0xc8
volatile ULONG CommitCharge; //0xd0
struct _EPROCESS_QUOTA_BLOCK* QuotaBlock; //0xd4
struct _PS_CPU_QUOTA_BLOCK* CpuQuotaBlock; //0xd8
ULONG PeakVirtualSize; //0xdc
ULONG VirtualSize; //0xe0
struct _LIST_ENTRY SessionProcessLinks; //0xe4
VOID* DebugPort; //0xec
union
{
VOID* ExceptionPortData; //0xf0
ULONG ExceptionPortValue; //0xf0
ULONG ExceptionPortState:3; //0xf0
};
struct _HANDLE_TABLE* ObjectTable; //0xf4
struct _EX_FAST_REF Token; //0xf8
ULONG WorkingSetPage; //0xfc
struct _EX_PUSH_LOCK AddressCreationLock; //0x100
struct _ETHREAD* RotateInProgress; //0x104
struct _ETHREAD* ForkInProgress; //0x108
ULONG HardwareTrigger; //0x10c
struct _MM_AVL_TABLE* PhysicalVadRoot; //0x110
VOID* CloneRoot; //0x114
volatile ULONG NumberOfPrivatePages; //0x118
volatile ULONG NumberOfLockedPages; //0x11c
VOID* Win32Process; //0x120
struct _EJOB* volatile Job; //0x124
VOID* SectionObject; //0x128
VOID* SectionBaseAddress; //0x12c
ULONG Cookie; //0x130
ULONG Spare8; //0x134
struct _PAGEFAULT_HISTORY* WorkingSetWatch; //0x138
VOID* Win32WindowStation; //0x13c
VOID* InheritedFromUniqueProcessId; //0x140
VOID* LdtInformation; //0x144
VOID* VdmObjects; //0x148
ULONG ConsoleHostProcess; //0x14c
VOID* DeviceMap; //0x150
VOID* EtwDataSource; //0x154
VOID* FreeTebHint; //0x158
union
{
struct _HARDWARE_PTE PageDirectoryPte; //0x160
ULONGLONG Filler; //0x160
};
VOID* Session; //0x168
UCHAR ImageFileName[15]; //0x16c
UCHAR PriorityClass; //0x17b
struct _LIST_ENTRY JobLinks; //0x17c
VOID* LockedPagesList; //0x184
struct _LIST_ENTRY ThreadListHead; //0x188
VOID* SecurityPort; //0x190
VOID* PaeTop; //0x194
volatile ULONG ActiveThreads; //0x198
ULONG ImagePathHash; //0x19c
ULONG DefaultHardErrorProcessing; //0x1a0
LONG LastThreadExitStatus; //0x1a4
struct _PEB* Peb; //0x1a8
struct _EX_FAST_REF PrefetchTrace; //0x1ac
union _LARGE_INTEGER ReadOperationCount; //0x1b0
union _LARGE_INTEGER WriteOperationCount; //0x1b8
union _LARGE_INTEGER OtherOperationCount; //0x1c0
union _LARGE_INTEGER ReadTransferCount; //0x1c8
union _LARGE_INTEGER WriteTransferCount; //0x1d0
union _LARGE_INTEGER OtherTransferCount; //0x1d8
ULONG CommitChargeLimit; //0x1e0
volatile ULONG CommitChargePeak; //0x1e4
VOID* AweInfo; //0x1e8
struct _SE_AUDIT_PROCESS_CREATION_INFO SeAuditProcessCreationInfo; //0x1ec
struct _MMSUPPORT Vm; //0x1f0
struct _LIST_ENTRY MmProcessLinks; //0x25c
VOID* HighestUserAddress; //0x264
ULONG ModifiedPageCount; //0x268
union
{
ULONG Flags2; //0x26c
struct
{
ULONG JobNotReallyActive:1; //0x26c
ULONG AccountingFolded:1; //0x26c
ULONG NewProcessReported:1; //0x26c
ULONG ExitProcessReported:1; //0x26c
ULONG ReportCommitChanges:1; //0x26c
ULONG LastReportMemory:1; //0x26c
ULONG ReportPhysicalPageChanges:1; //0x26c
ULONG HandleTableRundown:1; //0x26c
ULONG NeedsHandleRundown:1; //0x26c
ULONG RefTraceEnabled:1; //0x26c
ULONG NumaAware:1; //0x26c
ULONG ProtectedProcess:1; //0x26c
ULONG DefaultPagePriority:3; //0x26c
ULONG PrimaryTokenFrozen:1; //0x26c
ULONG ProcessVerifierTarget:1; //0x26c
ULONG StackRandomizationDisabled:1; //0x26c
ULONG AffinityPermanent:1; //0x26c
ULONG AffinityUpdateEnable:1; //0x26c
ULONG PropagateNode:1; //0x26c
ULONG ExplicitAffinity:1; //0x26c
};
};
union
{
ULONG Flags; //0x270
struct
{
ULONG CreateReported:1; //0x270
ULONG NoDebugInherit:1; //0x270
ULONG ProcessExiting:1; //0x270
ULONG ProcessDelete:1; //0x270
ULONG Wow64SplitPages:1; //0x270
ULONG VmDeleted:1; //0x270
ULONG OutswapEnabled:1; //0x270
ULONG Outswapped:1; //0x270
ULONG ForkFailed:1; //0x270
ULONG Wow64VaSpace4Gb:1; //0x270
ULONG AddressSpaceInitialized:2; //0x270
ULONG SetTimerResolution:1; //0x270
ULONG BreakOnTermination:1; //0x270
ULONG DeprioritizeViews:1; //0x270
ULONG WriteWatch:1; //0x270
ULONG ProcessInSession:1; //0x270
ULONG OverrideAddressSpace:1; //0x270
ULONG HasAddressSpace:1; //0x270
ULONG LaunchPrefetched:1; //0x270
ULONG InjectInpageErrors:1; //0x270
ULONG VmTopDown:1; //0x270
ULONG ImageNotifyDone:1; //0x270
ULONG PdeUpdateNeeded:1; //0x270
ULONG VdmAllowed:1; //0x270
ULONG CrossSessionCreate:1; //0x270
ULONG ProcessInserted:1; //0x270
ULONG DefaultIoPriority:3; //0x270
ULONG ProcessSelfDelete:1; //0x270
ULONG SetTimerResolutionLink:1; //0x270
};
};
LONG ExitStatus; //0x274
struct _MM_AVL_TABLE VadRoot; //0x278
struct _ALPC_PROCESS_CONTEXT AlpcContext; //0x298
struct _LIST_ENTRY TimerResolutionLink; //0x2a8
ULONG RequestedTimerResolution; //0x2b0
ULONG ActiveThreadsHighWatermark; //0x2b4
ULONG SmallestTimerResolution; //0x2b8
struct _PO_DIAG_STACK_RECORD* TimerResolutionStackRecord; //0x2bc
};
  • Pcb:Kprocess结构体。内核成员,见下文。

  • ProcessLock:R3进程锁。修改EPROCESS结构存放锁结构,防止同时修改。改完了置0.

  • CreateTime:进程的创建时间。

  • ExitTime:进程的退出时间。

  • RundownProtect:进程锁。该字段置值后,进程无法被访问、打开、结束,相当于保护。但是会容易卡死。

image-20230613124753624

  • UniqueProcessId:进程ID。任务管理器中显示的进程ID就是这个。

  • ActiveProcessLinks:双向链表。包括了windows中所有活动的进程。全局变量“PsActiveProcessHead”指向了这个链表的头部。通过该全局变量可以遍历整条链表。
    image-20230613160050089

  • ProcessQuotaUsage:进程物理页相关统计信息。

  • ProcessQuotaPeak:进程物理页相关统计信息。

  • CommitCharge:进程虚拟内存相关统计信息。

  • QuotaBlock:进程虚拟内存相关统计信息。

  • CpuQuotaBlock:进程虚拟内存相关统计信息。

  • SessionProcessLinks:会话进程链表。保存了当前登录的用户的所有进程。

  • DebugPort:调试相关。如果该进程处于调试状态,这里会有值(一个结构体),该结构体用于进程与调试器之间通信。通过循环清0可以达到反调试效果。

  • ExceptionPortData:调试相关。

  • ObjectTable:进程的句柄表。句柄相关章节再学。

  • Token:进程Token,外边的提权实际上就是拷贝这个位置的Token。System/Administror

  • WorkingSetPage:表明当前进程用了多少个物理页。

  • ImageFileName:当前进程的进程名,但是只有15个字节,要获取完整的可以获取SeAuditProcessCreationInfo

  • ThreadListHead:当前进程内所有线程的链表。

  • ActiveThreads:当前进程内活动的线程数量。

  • Peb。就是3环下该进程的PEB。(PEB结构此处不赘述了,网上有非常多的PEB结构说明。)

  • SeAuditProcessCreationInfo:当前进程完整路径。函数SeLocateProcessImageName就是获取的这个位置。

  • Flags2:一个联合体,每个位影响该进程的一些属性。

    • ProtectedProcess:进程保护位。该位置1后该进程被保护。CE看不到图片,打不开了进程。OD附加进程列表遍历不到。一个最简单的进程保护。

image-20230613125912245

  • +Flags:一个联合体,每个位影响该进程的一些属性。

    • ProcessExiting:进程退出标志位。置1后表明该进程已退出,但实际还在运行。可以达到反调试的效果。同时进程无法使用任务管理器结束。

    • ProcessDelete:进程退出标志位。置1后表明该进程已退出,但实际还在运行。可以达到反调试的效果。同时进程无法使用任务管理器结束。
      image-20230613130000778

    • BreakOnTermination:该位置1后,任务管理器结束进程时将提示“是否结束系统进程XXX”。结束后windbg将会断下。
      image-20230613130019847

    • VmTopDown:该位置1时,VirtualAlloc一类的申请内存函数将会从大地址开始申请。

    • ProcessInserted:该位置0后,OD附加进程列表找不到该进程。任务管理器结束不掉该进程。CE打不开该进程,无图标。

  • ExitStatus:进程退出状态码。进程创建时默认值是250(0x103)。如果不是这个值基本上就是进程退出了。

  • VadRoot:标识当前进程用户空间(低2G)中哪些地址没被分配。该成员指向了一个二叉树。

2.1 KPROCESS

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
//0x98 bytes (sizeof)
struct _KPROCESS
{
struct _DISPATCHER_HEADER Header; //0x0
struct _LIST_ENTRY ProfileListHead; //0x10
ULONG DirectoryTableBase; //0x18
struct _KGDTENTRY LdtDescriptor; //0x1c
struct _KIDTENTRY Int21Descriptor; //0x24
struct _LIST_ENTRY ThreadListHead; //0x2c
ULONG ProcessLock; //0x34
struct _KAFFINITY_EX Affinity; //0x38
struct _LIST_ENTRY ReadyListHead; //0x44
struct _SINGLE_LIST_ENTRY SwapListEntry; //0x4c
volatile struct _KAFFINITY_EX ActiveProcessors; //0x50
union
{
struct
{
volatile LONG AutoAlignment:1; //0x5c
volatile LONG DisableBoost:1; //0x5c
volatile LONG DisableQuantum:1; //0x5c
volatile ULONG ActiveGroupsMask:1; //0x5c
volatile LONG ReservedFlags:28; //0x5c
};
volatile LONG ProcessFlags; //0x5c
};
CHAR BasePriority; //0x60
CHAR QuantumReset; //0x61
UCHAR Visited; //0x62
UCHAR Unused3; //0x63
ULONG ThreadSeed[1]; //0x64
USHORT IdealNode[1]; //0x68
USHORT IdealGlobalNode; //0x6a
union _KEXECUTE_OPTIONS Flags; //0x6c
UCHAR Unused1; //0x6d
USHORT IopmOffset; //0x6e
ULONG Unused4; //0x70
union _KSTACK_COUNT StackCount; //0x74
struct _LIST_ENTRY ProcessListEntry; //0x78
volatile ULONGLONG CycleTime; //0x80
ULONG KernelTime; //0x88
ULONG UserTime; //0x8c
VOID* VdmTrapcHandler; //0x90
};
  • Header:可等待对象头部。所有0环结构体只要以_DISPATCHER_HEADER结构开头的,都可以使用WaitForSingleObject等待。如互斥体、事件。

  • ProfileListHead:性能分析相关,任务管理器,性能栏那些数据。

  • DirectoryTableBase:页目录表基址。物理地址,指向页目录表,CR3中的值就从这里获取。

  • LdtDescriptor:进程LDT描述符。

  • Int21Descriptor:为了兼容DOS下可通过Int21调用系统功能。

  • ThreadListHead:当前进程的所有线程结构体链表。这个位置- KTHREAD->ThreadListHead == KTHREAD

  • ProcessLock:进程锁,作用和EPROCESS中的一样,但这个是RO用的。

  • Affinity:亲核性。规定了当前进程内的所有线程可以在哪些CPU上跑,4字节,共32位,每一位对应一个CPU核。如000000A1,转换为二进制为1010 0001,则该进程中的线程只能在0、5、7号CPU上运行。因此32位系统最多支持32核CPU,64位系统支持64核CPU。该值仅为线程结构中的亲核性做初始化赋值使用,没有实际的限制功能。

    如果只有1个CPU,但此处值为2(0010),则该进程为一个“死”了的进程。

  • ReadyListHead:当前进程内的就绪线程链表,保存的值指向了ETHREAD->WaitListEntry地址。当进程被换出内存以后,所属的线程一旦就绪就会加入到这个链表中,然后换入进程,并将就绪线程链表全部加入全局的就绪线程链表。

  • SwapListEntry:当进程被换出内存时会通过这个位置将进程加入到KiProcessOutSwapListHead,换入时加入到KiProcessInSwapListHead

  • ActiveProcessors:当前进程内正在运行的线程运行在哪些CPU上。

  • AutoAlignment:强制内存对齐。一般为0。

  • DisableBoost:置1为关闭当前进程内所有线程的时间碎片。(置1后,不会由于时间中断触发线程切换)

  • BasePriority:基础优先级。该进程内所有线程最初的优先级。

  • QuantumReset:当前进程内线程的初始时间碎片。每一次时钟中断会将线程中的时间碎片减6,为0时,切换线程。线程从就绪变为运行时,会从这个值中取到初始的时间碎片。改大这个值会让该进程内的线程跑的更久。

  • ProcessListEntry:系统内所有进程的链表。win7及以上此处为空,已弃用。

  • Flags:进程NX位的局部开关。结构为_KEXECUTE_OPTIONS的联合体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //0x1 bytes (sizeof)
    union _KEXECUTE_OPTIONS
    {
    UCHAR ExecuteDisable:1; //0x0
    UCHAR ExecuteEnable:1; //0x0
    UCHAR DisableThunkEmulation:1; //0x0
    UCHAR Permanent:1; //0x0
    UCHAR ExecuteDispatchEnable:1; //0x0
    UCHAR ImageDispatchEnable:1; //0x0
    UCHAR DisableExceptionChainValidation:1; //0x0
    UCHAR Spare:1; //0x0
    volatile UCHAR ExecuteOptions; //0x0
    };
    • ExecuteOptions:写1,数据段可以当做代码执行。写0数据段不能当做代码执行。某些检测弱的游戏可以通过对这个置1,然后申请一段可读可写内存作为shellcode执行
  • CycleTime:当前进程执行了多少个指令周期。当进程结束时才会被赋值,指明了该进程存活了多久。

  • KernelTime:(统计信息)当前进程在0环的运行时间。当进程结束时才会被赋值,指明了该进程存活了多久。

  • UserTime:(统计信息)当前进程在3环的运行时间。当进程结束时才会被赋值,指明了该进程存活了多久。

  • VdmTrapcHandler:虚拟8086模式时使用。

2、ETHREAD

与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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//0x2b8 bytes (sizeof)
struct _ETHREAD
{
struct _KTHREAD Tcb; //0x0
union _LARGE_INTEGER CreateTime; //0x200
union
{
union _LARGE_INTEGER ExitTime; //0x208
struct _LIST_ENTRY KeyedWaitChain; //0x208
};
LONG ExitStatus; //0x210
union
{
struct _LIST_ENTRY PostBlockList; //0x214
struct
{
VOID* ForwardLinkShadow; //0x214
VOID* StartAddress; //0x218
};
};
union
{
struct _TERMINATION_PORT* TerminationPort; //0x21c
struct _ETHREAD* ReaperLink; //0x21c
VOID* KeyedWaitValue; //0x21c
};
ULONG ActiveTimerListLock; //0x220
struct _LIST_ENTRY ActiveTimerListHead; //0x224
struct _CLIENT_ID Cid; //0x22c
union
{
struct _KSEMAPHORE KeyedWaitSemaphore; //0x234
struct _KSEMAPHORE AlpcWaitSemaphore; //0x234
};
union _PS_CLIENT_SECURITY_CONTEXT ClientSecurity; //0x248
struct _LIST_ENTRY IrpList; //0x24c
ULONG TopLevelIrp; //0x254
struct _DEVICE_OBJECT* DeviceToVerify; //0x258
union _PSP_CPU_QUOTA_APC* CpuQuotaApc; //0x25c
VOID* Win32StartAddress; //0x260
VOID* LegacyPowerObject; //0x264
struct _LIST_ENTRY ThreadListEntry; //0x268
struct _EX_RUNDOWN_REF RundownProtect; //0x270
struct _EX_PUSH_LOCK ThreadLock; //0x274
ULONG ReadClusterSize; //0x278
volatile LONG MmLockOrdering; //0x27c
union
{
ULONG CrossThreadFlags; //0x280
struct
{
ULONG Terminated:1; //0x280
ULONG ThreadInserted:1; //0x280
ULONG HideFromDebugger:1; //0x280
ULONG ActiveImpersonationInfo:1; //0x280
ULONG Reserved:1; //0x280
ULONG HardErrorsAreDisabled:1; //0x280
ULONG BreakOnTermination:1; //0x280
ULONG SkipCreationMsg:1; //0x280
ULONG SkipTerminationMsg:1; //0x280
ULONG CopyTokenOnOpen:1; //0x280
ULONG ThreadIoPriority:3; //0x280
ULONG ThreadPagePriority:3; //0x280
ULONG RundownFail:1; //0x280
ULONG NeedsWorkingSetAging:1; //0x280
};
};
union
{
ULONG SameThreadPassiveFlags; //0x284
struct
{
ULONG ActiveExWorker:1; //0x284
ULONG ExWorkerCanWaitUser:1; //0x284
ULONG MemoryMaker:1; //0x284
ULONG ClonedThread:1; //0x284
ULONG KeyedEventInUse:1; //0x284
ULONG RateApcState:2; //0x284
ULONG SelfTerminate:1; //0x284
};
};
union
{
ULONG SameThreadApcFlags; //0x288
struct
{
UCHAR Spare:1; //0x288
volatile UCHAR StartAddressInvalid:1; //0x288
UCHAR EtwPageFaultCalloutActive:1; //0x288
UCHAR OwnsProcessWorkingSetExclusive:1; //0x288
UCHAR OwnsProcessWorkingSetShared:1; //0x288
UCHAR OwnsSystemCacheWorkingSetExclusive:1; //0x288
UCHAR OwnsSystemCacheWorkingSetShared:1; //0x288
UCHAR OwnsSessionWorkingSetExclusive:1; //0x288
UCHAR OwnsSessionWorkingSetShared:1; //0x289
UCHAR OwnsProcessAddressSpaceExclusive:1; //0x289
UCHAR OwnsProcessAddressSpaceShared:1; //0x289
UCHAR SuppressSymbolLoad:1; //0x289
UCHAR Prefetching:1; //0x289
UCHAR OwnsDynamicMemoryShared:1; //0x289
UCHAR OwnsChangeControlAreaExclusive:1; //0x289
UCHAR OwnsChangeControlAreaShared:1; //0x289
UCHAR OwnsPagedPoolWorkingSetExclusive:1; //0x28a
UCHAR OwnsPagedPoolWorkingSetShared:1; //0x28a
UCHAR OwnsSystemPtesWorkingSetExclusive:1; //0x28a
UCHAR OwnsSystemPtesWorkingSetShared:1; //0x28a
UCHAR TrimTrigger:2; //0x28a
UCHAR Spare1:2; //0x28a
UCHAR PriorityRegionActive; //0x28b
};
};
UCHAR CacheManagerActive; //0x28c
UCHAR DisablePageFaultClustering; //0x28d
UCHAR ActiveFaultCount; //0x28e
UCHAR LockOrderState; //0x28f
ULONG AlpcMessageId; //0x290
union
{
VOID* AlpcMessage; //0x294
ULONG AlpcReceiveAttributeSet; //0x294
};
struct _LIST_ENTRY AlpcWaitListEntry; //0x298
ULONG CacheManagerCount; //0x2a0
ULONG IoBoostCount; //0x2a4
ULONG IrpListLock; //0x2a8
VOID* ReservedForSynchTracking; //0x2ac
struct _SINGLE_LIST_ENTRY CmCallbackListHead; //0x2b0
};
  • Tcb:KTHREAD成员,见下文。
  • StartAddress:线程函数起始地址。
  • Cid:当前线程的线程ID。为_CLIENT_ID结构,包含了线程ID和所属的进程ID。
  • Win32StartAddress:GUI线程函数起始地址。如果一个线程属于GUI线程则这个位置为真实的线程起始地址,否则为null。
  • ThreadListEntry:当前进程内所有线程的双向链表。
  • RundownProtect:与EPROCESS效果一致。
  • ThreadLock:与EPROCESS效果一致。

2.1 KTHREAD

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//0x200 bytes (sizeof)
struct _KTHREAD
{
struct _DISPATCHER_HEADER Header; //0x0
volatile ULONGLONG CycleTime; //0x10
volatile ULONG HighCycleTime; //0x18
ULONGLONG QuantumTarget; //0x20
VOID* InitialStack; //0x28
VOID* volatile StackLimit; //0x2c
VOID* KernelStack; //0x30
ULONG ThreadLock; //0x34
union _KWAIT_STATUS_REGISTER WaitRegister; //0x38
volatile UCHAR Running; //0x39
UCHAR Alerted[2]; //0x3a
union
{
struct
{
ULONG KernelStackResident:1; //0x3c
ULONG ReadyTransition:1; //0x3c
ULONG ProcessReadyQueue:1; //0x3c
ULONG WaitNext:1; //0x3c
ULONG SystemAffinityActive:1; //0x3c
ULONG Alertable:1; //0x3c
ULONG GdiFlushActive:1; //0x3c
ULONG UserStackWalkActive:1; //0x3c
ULONG ApcInterruptRequest:1; //0x3c
ULONG ForceDeferSchedule:1; //0x3c
ULONG QuantumEndMigrate:1; //0x3c
ULONG UmsDirectedSwitchEnable:1; //0x3c
ULONG TimerActive:1; //0x3c
ULONG SystemThread:1; //0x3c
ULONG Reserved:18; //0x3c
};
LONG MiscFlags; //0x3c
};
union
{
struct _KAPC_STATE ApcState; //0x40
struct
{
UCHAR ApcStateFill[23]; //0x40
CHAR Priority; //0x57
};
};
volatile ULONG NextProcessor; //0x58
volatile ULONG DeferredProcessor; //0x5c
ULONG ApcQueueLock; //0x60
ULONG ContextSwitches; //0x64
volatile UCHAR State; //0x68
CHAR NpxState; //0x69
UCHAR WaitIrql; //0x6a
CHAR WaitMode; //0x6b
volatile LONG WaitStatus; //0x6c
struct _KWAIT_BLOCK* WaitBlockList; //0x70
union
{
struct _LIST_ENTRY WaitListEntry; //0x74
struct _SINGLE_LIST_ENTRY SwapListEntry; //0x74
};
struct _KQUEUE* volatile Queue; //0x7c
ULONG WaitTime; //0x80
union
{
struct
{
SHORT KernelApcDisable; //0x84
SHORT SpecialApcDisable; //0x86
};
ULONG CombinedApcDisable; //0x84
};
VOID* Teb; //0x88
struct _KTIMER Timer; //0x90
union
{
struct
{
volatile ULONG AutoAlignment:1; //0xb8
volatile ULONG DisableBoost:1; //0xb8
volatile ULONG EtwStackTraceApc1Inserted:1; //0xb8
volatile ULONG EtwStackTraceApc2Inserted:1; //0xb8
volatile ULONG CalloutActive:1; //0xb8
volatile ULONG ApcQueueable:1; //0xb8
volatile ULONG EnableStackSwap:1; //0xb8
volatile ULONG GuiThread:1; //0xb8
volatile ULONG UmsPerformingSyscall:1; //0xb8
volatile ULONG VdmSafe:1; //0xb8
volatile ULONG UmsDispatched:1; //0xb8
volatile ULONG ReservedFlags:21; //0xb8
};
volatile LONG ThreadFlags; //0xb8
};
VOID* ServiceTable; //0xbc
struct _KWAIT_BLOCK WaitBlock[4]; //0xc0
struct _LIST_ENTRY QueueListEntry; //0x120
struct _KTRAP_FRAME* TrapFrame; //0x128
VOID* FirstArgument; //0x12c
union
{
VOID* CallbackStack; //0x130
ULONG CallbackDepth; //0x130
};
UCHAR ApcStateIndex; //0x134
CHAR BasePriority; //0x135
union
{
CHAR PriorityDecrement; //0x136
struct
{
UCHAR ForegroundBoost:4; //0x136
UCHAR UnusualBoost:4; //0x136
};
};
UCHAR Preempted; //0x137
UCHAR AdjustReason; //0x138
CHAR AdjustIncrement; //0x139
CHAR PreviousMode; //0x13a
CHAR Saturation; //0x13b
ULONG SystemCallNumber; //0x13c
ULONG FreezeCount; //0x140
volatile struct _GROUP_AFFINITY UserAffinity; //0x144
struct _KPROCESS* Process; //0x150
volatile struct _GROUP_AFFINITY Affinity; //0x154
ULONG IdealProcessor; //0x160
ULONG UserIdealProcessor; //0x164
struct _KAPC_STATE* ApcStatePointer[2]; //0x168
union
{
struct _KAPC_STATE SavedApcState; //0x170
struct
{
UCHAR SavedApcStateFill[23]; //0x170
UCHAR WaitReason; //0x187
};
};
CHAR SuspendCount; //0x188
CHAR Spare1; //0x189
UCHAR OtherPlatformFill; //0x18a
VOID* volatile Win32Thread; //0x18c
VOID* StackBase; //0x190
union
{
struct _KAPC SuspendApc; //0x194
struct
{
UCHAR SuspendApcFill0[1]; //0x194
UCHAR ResourceIndex; //0x195
};
struct
{
UCHAR SuspendApcFill1[3]; //0x194
UCHAR QuantumReset; //0x197
};
struct
{
UCHAR SuspendApcFill2[4]; //0x194
ULONG KernelTime; //0x198
};
struct
{
UCHAR SuspendApcFill3[36]; //0x194
struct _KPRCB* volatile WaitPrcb; //0x1b8
};
struct
{
UCHAR SuspendApcFill4[40]; //0x194
VOID* LegoData; //0x1bc
};
struct
{
UCHAR SuspendApcFill5[47]; //0x194
UCHAR LargeStack; //0x1c3
};
};
ULONG UserTime; //0x1c4
union
{
struct _KSEMAPHORE SuspendSemaphore; //0x1c8
UCHAR SuspendSemaphorefill[20]; //0x1c8
};
ULONG SListFaultCount; //0x1dc
struct _LIST_ENTRY ThreadListEntry; //0x1e0
struct _LIST_ENTRY MutantListHead; //0x1e8
VOID* SListFaultAddress; //0x1f0
struct _KTHREAD_COUNTERS* ThreadCounters; //0x1f4
struct _XSTATE_SAVE* XStateSave; //0x1f8
};
  • Header:可等待对象头部。
  • InitialStack:线程切换相关。当前线程的栈底。栈底-29C是TrapFrame结构首地址。
  • StackLimit:线程切换相关。当前线程的最大栈顶。ESP不能小于这个值。
  • KernelStack:线程切换相关。线程切换时,存储当前线程切换时的ESP,被切换回来时,从这里恢复ESP。
  • ThreadLock:跟上边的一致
  • Running:线程状态,正在运行中为1,否则为0。
  • Alerted:可警惕性。APC相关,后续APC章节学习。
  • MiscFlags:
    • KernelStackResident:堆栈可扩展位。为1时,线程内核堆栈可以被扩大。0时无法扩大。
    • SystemThread:为1时,该线程为内核线程,否则为用户线程。
  • ApcState:APC相关,后续APC章节学习。
  • Priority:当前线程的优先级。如存储11,则优先级为11,当前线程存储在第11个就绪链表中。优先级数字越大,优先级越低。默认线程优先级为8,存储在第8个就绪链表中。
  • NextProcessor:线程下次运行在哪个核上,如果为0,则随机。
  • ApcQueueLock:APC相关,后续APC章节学习。
  • ContextSwitches:当前线程切换了多少次。
  • State:线程状态。就绪、等待、运行等。
  • Teb:3环的TEB。
  • EnableStackSwap:内核栈是否可以换出内存。
  • ServiceTable:系统服务表地址,系统调用章节已学过。
  • WaitBlock:当前线程正在等待的对象,由KPROCESS->ReadyThreadList指向。
  • TrapFrame:TrapFrame结构,进0环时保存3环寄存器的值,系统调用章节已学过。
  • BasePriority:线程基础优先级。这个值就是所属进程的BasePriority值。
  • PreviousMode:先前模式。一些内核函数会判断这个值。
  • FreezeCount:被挂起的次数。如果一直为0则无法被恢复。
  • Process:该线程的父进程(创建该线程的进程)。
  • ApcStatePointer:APC相关,后续APC章节学习。
  • SavedApcState:APC相关,后续APC章节学习。
  • Win32Thread:win32线程,如果该线程是UI图形线程,就会多一个win32线程结构体。
  • ThreadListEntry:当前进程所有线程的双向链表。这个位置- KPROCESS->ThreadListEntry == KPROCESS

2.2 线程伪装

使用pchunter随便查看一个进程的线程。

image-20230615094127635

然后使用Windbg查看对应的ETHREAD结构体。

image-20230615094234272

由于该进程具有GUI,因此Win32StartAddress不为null。然后将这个位置的值修改位其他线程的入口。

image-20230615094549890

image-20230615094610181

回到虚拟机重新查看线程所属的模块。

image-20230615094642370

可以看到已经被伪装。

2.3 杀死进程

杀死进程实际上等同于杀死线程,如果一个进程里没有任何线程在执行则进程等于结束。而线程只能自己杀死自己,原理是利用APC插入一个函数到线程中,这个函数的作用就是杀死自己,然后当线程调度后会执行这个函数就把自己杀死了。

杀死进程有三种方法:

  • 调用API杀死
  • 清空进程中所有的线程链表。(API本质上就是这样)
  • 暴力抹除进程内存。(把进程代码直接抹除或者设置为不可执行,程序就会保存退出)

2.3.1 NtTerminateProcess逆向

首先会判断EPROCESS.RundownProtect位,如果为1则不允许结束。这个位在上边有介绍。

image-20230615095932177

然后就是结束所有的线程。

image-20230615095952824

PspTerminateAllThreads中实际上就是遍历线程后调用PspTerminateThreadByPointer结束,进入PspTerminateThreadByPointer

image-20230615100143668

这里需要补充的是,由于EPROCESS、KPROCESS、ETHREAD、KTHREAD都有一个ThreadListEntry。他们之间的关系如下

EPROCESS->ThreadListEntry指向的位置是ETHREAD->ThreadListEntry

KPROCESS->ThreadListEntry指向的位置是KTHREAD->ThreadListEntry

因此如果要遍历线程需要上边对应的关系去减掉对应ThreadListEntry在*THREAD中的偏移(*表示K或者E),才能拿到线程头

3、KPCR

FS段,R0下叫做KPCR(内核进程控制区),R3下叫做PEB(进程环境块)。每一个核独有一个,这个结构记录了这个核的CPU信息。内核变量KeNumberProcessors用于记录当前机器有多少个核。

image-20230613122919271

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
//0x3748 bytes (sizeof)
struct _KPCR
{
union
{
struct _NT_TIB NtTib; //0x0
struct
{
struct _EXCEPTION_REGISTRATION_RECORD* Used_ExceptionList; //0x0
VOID* Used_StackBase; //0x4
VOID* Spare2; //0x8
VOID* TssCopy; //0xc
ULONG ContextSwitches; //0x10
ULONG SetMemberCopy; //0x14
VOID* Used_Self; //0x18
};
};
struct _KPCR* SelfPcr; //0x1c
struct _KPRCB* Prcb; //0x20
UCHAR Irql; //0x24
ULONG IRR; //0x28
ULONG IrrActive; //0x2c
ULONG IDR; //0x30
VOID* KdVersionBlock; //0x34
struct _KIDTENTRY* IDT; //0x38
struct _KGDTENTRY* GDT; //0x3c
struct _KTSS* TSS; //0x40
USHORT MajorVersion; //0x44
USHORT MinorVersion; //0x46
ULONG SetMember; //0x48
ULONG StallScaleFactor; //0x4c
UCHAR SpareUnused; //0x50
UCHAR Number; //0x51
UCHAR Spare0; //0x52
UCHAR SecondLevelCacheAssociativity; //0x53
ULONG VdmAlert; //0x54
ULONG KernelReserved[14]; //0x58
ULONG SecondLevelCacheSize; //0x90
ULONG HalReserved[16]; //0x94
ULONG InterruptMode; //0xd4
UCHAR Spare1; //0xd8
ULONG KernelReserved2[17]; //0xdc
struct _KPRCB PrcbData; //0x120
};
  • NtTib:

    • Used_ExceptionList:异常处理程序链表。
    • Used_StackBase:当前线程的栈底。
    • Spare2/StackLimit:当前线程的堆栈大小。
    • Used_Self:指向NtTib自身,也是KPCR自身(fs:[0x18])。
  • TssCopy:0x40处TSS的初始值。

  • SelfPcr:指向KPCR自身。与UsedSelf不同,SelfPcr必然指向KPCR自身,UsedSelf有时候会指向TEB。

  • Prcb:KPRCB结构指针,实际上指向的是0x120的PrcbData。

  • KdVersionBlock:只有第一个核有值,其他核为0;

    image-20230614011025900

    image-20230614011102894

    并且这个位置的值大有用处!!!!这个KdVersionBlock对应的结构为_DBGKD_GET_VERSION64

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //0x28 bytes (sizeof)
    struct _DBGKD_GET_VERSION64
    {
    USHORT MajorVersion; //0x0
    USHORT MinorVersion; //0x2
    UCHAR ProtocolVersion; //0x4
    UCHAR KdSecondaryVersion; //0x5
    USHORT Flags; //0x6
    USHORT MachineType; //0x8
    UCHAR MaxPacketType; //0xa
    UCHAR MaxStateChange; //0xb
    UCHAR MaxManipulate; //0xc
    UCHAR Simulation; //0xd
    USHORT Unused[1]; //0xe
    ULONGLONG KernBase; //0x10
    ULONGLONG PsLoadedModuleList; //0x18
    ULONGLONG DebuggerDataList; //0x20
    };

    image-20230614011455243

    • KernBase:ntoskrnl.exe的基地址
    • PsLoadedModuleList:模块链表。
    • DebuggerDataList:最牛逼的东西,里边存着很多公开/未公开的内容。使用dds查看。

    image-20230614011631290

    可以看到里边有很多东西,比如句柄表PspCidTable,对于他的这个结构在WRK有给出部分(由于系统一直在更新,所以内容也一直在添加)。_KDDEBUGGER_DATA64

    image-20230614011853388

    image-20230614011901258

  • IDT:当前线程的IDT表地址。

  • GDT:当前线程的GDT表地址。

  • TSS:指向当前线程的TSS表。

  • SetNumber:当前CPU编号。从1开始。

  • Number:当前CPU编号。从0开始。

  • PrcbData:KPRCB结构,扩展结构,见后文。

3.1 KPRCB

内核进程控制区域块。内核变量KiProcessorBlock中保存了KPRCB的地址。

image-20230613123642965

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
//0x3628 bytes (sizeof)
struct _KPRCB
{
USHORT MinorVersion; //0x0
USHORT MajorVersion; //0x2
struct _KTHREAD* CurrentThread; //0x4
struct _KTHREAD* NextThread; //0x8
struct _KTHREAD* IdleThread; //0xc
UCHAR LegacyNumber; //0x10
UCHAR NestingLevel; //0x11
USHORT BuildType; //0x12
CHAR CpuType; //0x14
CHAR CpuID; //0x15
union
{
USHORT CpuStep; //0x16
struct
{
UCHAR CpuStepping; //0x16
UCHAR CpuModel; //0x17
};
};
struct _KPROCESSOR_STATE ProcessorState; //0x18
ULONG KernelReserved[16]; //0x338
ULONG HalReserved[16]; //0x378
ULONG CFlushSize; //0x3b8
UCHAR CoresPerPhysicalProcessor; //0x3bc
UCHAR LogicalProcessorsPerCore; //0x3bd
UCHAR PrcbPad0[2]; //0x3be
ULONG MHz; //0x3c0
UCHAR CpuVendor; //0x3c4
UCHAR GroupIndex; //0x3c5
USHORT Group; //0x3c6
ULONG GroupSetMember; //0x3c8
ULONG Number; //0x3cc
UCHAR PrcbPad1[72]; //0x3d0
struct _KSPIN_LOCK_QUEUE LockQueue[17]; //0x418
struct _KTHREAD* NpxThread; //0x4a0
ULONG InterruptCount; //0x4a4
ULONG KernelTime; //0x4a8
ULONG UserTime; //0x4ac
ULONG DpcTime; //0x4b0
ULONG DpcTimeCount; //0x4b4
ULONG InterruptTime; //0x4b8
ULONG AdjustDpcThreshold; //0x4bc
ULONG PageColor; //0x4c0
UCHAR DebuggerSavedIRQL; //0x4c4
UCHAR NodeColor; //0x4c5
UCHAR PrcbPad20[2]; //0x4c6
ULONG NodeShiftedColor; //0x4c8
struct _KNODE* ParentNode; //0x4cc
ULONG SecondaryColorMask; //0x4d0
ULONG DpcTimeLimit; //0x4d4
ULONG PrcbPad21[2]; //0x4d8
ULONG CcFastReadNoWait; //0x4e0
ULONG CcFastReadWait; //0x4e4
ULONG CcFastReadNotPossible; //0x4e8
ULONG CcCopyReadNoWait; //0x4ec
ULONG CcCopyReadWait; //0x4f0
ULONG CcCopyReadNoWaitMiss; //0x4f4
volatile LONG MmSpinLockOrdering; //0x4f8
volatile LONG IoReadOperationCount; //0x4fc
volatile LONG IoWriteOperationCount; //0x500
volatile LONG IoOtherOperationCount; //0x504
union _LARGE_INTEGER IoReadTransferCount; //0x508
union _LARGE_INTEGER IoWriteTransferCount; //0x510
union _LARGE_INTEGER IoOtherTransferCount; //0x518
ULONG CcFastMdlReadNoWait; //0x520
ULONG CcFastMdlReadWait; //0x524
ULONG CcFastMdlReadNotPossible; //0x528
ULONG CcMapDataNoWait; //0x52c
ULONG CcMapDataWait; //0x530
ULONG CcPinMappedDataCount; //0x534
ULONG CcPinReadNoWait; //0x538
ULONG CcPinReadWait; //0x53c
ULONG CcMdlReadNoWait; //0x540
ULONG CcMdlReadWait; //0x544
ULONG CcLazyWriteHotSpots; //0x548
ULONG CcLazyWriteIos; //0x54c
ULONG CcLazyWritePages; //0x550
ULONG CcDataFlushes; //0x554
ULONG CcDataPages; //0x558
ULONG CcLostDelayedWrites; //0x55c
ULONG CcFastReadResourceMiss; //0x560
ULONG CcCopyReadWaitMiss; //0x564
ULONG CcFastMdlReadResourceMiss; //0x568
ULONG CcMapDataNoWaitMiss; //0x56c
ULONG CcMapDataWaitMiss; //0x570
ULONG CcPinReadNoWaitMiss; //0x574
ULONG CcPinReadWaitMiss; //0x578
ULONG CcMdlReadNoWaitMiss; //0x57c
ULONG CcMdlReadWaitMiss; //0x580
ULONG CcReadAheadIos; //0x584
ULONG KeAlignmentFixupCount; //0x588
ULONG KeExceptionDispatchCount; //0x58c
ULONG KeSystemCalls; //0x590
ULONG AvailableTime; //0x594
ULONG PrcbPad22[2]; //0x598
struct _PP_LOOKASIDE_LIST PPLookasideList[16]; //0x5a0
struct _GENERAL_LOOKASIDE_POOL PPNPagedLookasideList[32]; //0x620
struct _GENERAL_LOOKASIDE_POOL PPPagedLookasideList[32]; //0xf20
volatile ULONG PacketBarrier; //0x1820
volatile LONG ReverseStall; //0x1824
VOID* IpiFrame; //0x1828
UCHAR PrcbPad3[52]; //0x182c
VOID* volatile CurrentPacket[3]; //0x1860
volatile ULONG TargetSet; //0x186c
VOID (* volatileWorkerRoutine)(VOID* arg1, VOID* arg2, VOID* arg3, VOID* arg4); //0x1870
volatile ULONG IpiFrozen; //0x1874
UCHAR PrcbPad4[40]; //0x1878
volatile ULONG RequestSummary; //0x18a0
struct _KPRCB* volatile SignalDone; //0x18a4
UCHAR PrcbPad50[56]; //0x18a8
struct _KDPC_DATA DpcData[2]; //0x18e0
VOID* DpcStack; //0x1908
LONG MaximumDpcQueueDepth; //0x190c
ULONG DpcRequestRate; //0x1910
ULONG MinimumDpcRate; //0x1914
ULONG DpcLastCount; //0x1918
ULONG PrcbLock; //0x191c
struct _KGATE DpcGate; //0x1920
UCHAR ThreadDpcEnable; //0x1930
volatile UCHAR QuantumEnd; //0x1931
volatile UCHAR DpcRoutineActive; //0x1932
volatile UCHAR IdleSchedule; //0x1933
union
{
volatile LONG DpcRequestSummary; //0x1934
SHORT DpcRequestSlot[2]; //0x1934
struct
{
SHORT NormalDpcState; //0x1934
union
{
volatile USHORT DpcThreadActive:1; //0x1936
SHORT ThreadDpcState; //0x1936
};
};
};
volatile ULONG TimerHand; //0x1938
ULONG LastTick; //0x193c
LONG MasterOffset; //0x1940
ULONG PrcbPad41[2]; //0x1944
ULONG PeriodicCount; //0x194c
ULONG PeriodicBias; //0x1950
ULONGLONG TickOffset; //0x1958
struct _KTIMER_TABLE TimerTable; //0x1960
struct _KDPC CallDpc; //0x31a0
LONG ClockKeepAlive; //0x31c0
UCHAR ClockCheckSlot; //0x31c4
UCHAR ClockPollCycle; //0x31c5
UCHAR PrcbPad6[2]; //0x31c6
LONG DpcWatchdogPeriod; //0x31c8
LONG DpcWatchdogCount; //0x31cc
LONG ThreadWatchdogPeriod; //0x31d0
LONG ThreadWatchdogCount; //0x31d4
volatile LONG KeSpinLockOrdering; //0x31d8
ULONG PrcbPad70[1]; //0x31dc
struct _LIST_ENTRY WaitListHead; //0x31e0
ULONG WaitLock; //0x31e8
ULONG ReadySummary; //0x31ec
ULONG QueueIndex; //0x31f0
struct _SINGLE_LIST_ENTRY DeferredReadyListHead; //0x31f4
ULONGLONG StartCycles; //0x31f8
volatile ULONGLONG CycleTime; //0x3200
volatile ULONG HighCycleTime; //0x3208
ULONG PrcbPad71; //0x320c
ULONGLONG PrcbPad72[2]; //0x3210
struct _LIST_ENTRY DispatcherReadyListHead[32]; //0x3220
VOID* ChainedInterruptList; //0x3320
LONG LookasideIrpFloat; //0x3324
volatile LONG MmPageFaultCount; //0x3328
volatile LONG MmCopyOnWriteCount; //0x332c
volatile LONG MmTransitionCount; //0x3330
volatile LONG MmCacheTransitionCount; //0x3334
volatile LONG MmDemandZeroCount; //0x3338
volatile LONG MmPageReadCount; //0x333c
volatile LONG MmPageReadIoCount; //0x3340
volatile LONG MmCacheReadCount; //0x3344
volatile LONG MmCacheIoCount; //0x3348
volatile LONG MmDirtyPagesWriteCount; //0x334c
volatile LONG MmDirtyWriteIoCount; //0x3350
volatile LONG MmMappedPagesWriteCount; //0x3354
volatile LONG MmMappedWriteIoCount; //0x3358
volatile ULONG CachedCommit; //0x335c
volatile ULONG CachedResidentAvailable; //0x3360
VOID* HyperPte; //0x3364
UCHAR PrcbPad8[4]; //0x3368
UCHAR VendorString[13]; //0x336c
UCHAR InitialApicId; //0x3379
UCHAR LogicalProcessorsPerPhysicalProcessor; //0x337a
UCHAR PrcbPad9[5]; //0x337b
ULONG FeatureBits; //0x3380
union _LARGE_INTEGER UpdateSignature; //0x3388
volatile ULONGLONG IsrTime; //0x3390
ULONGLONG RuntimeAccumulation; //0x3398
struct _PROCESSOR_POWER_STATE PowerState; //0x33a0
struct _KDPC DpcWatchdogDpc; //0x3468
struct _KTIMER DpcWatchdogTimer; //0x3488
VOID* WheaInfo; //0x34b0
VOID* EtwSupport; //0x34b4
union _SLIST_HEADER InterruptObjectPool; //0x34b8
union _SLIST_HEADER HypercallPageList; //0x34c0
VOID* HypercallPageVirtual; //0x34c8
VOID* VirtualApicAssist; //0x34cc
ULONGLONG* StatisticsPage; //0x34d0
VOID* RateControl; //0x34d4
struct _CACHE_DESCRIPTOR Cache[5]; //0x34d8
ULONG CacheCount; //0x3514
ULONG CacheProcessorMask[5]; //0x3518
struct _KAFFINITY_EX PackageProcessorSet; //0x352c
ULONG PrcbPad91[1]; //0x3538
ULONG CoreProcessorSet; //0x353c
struct _KDPC TimerExpirationDpc; //0x3540
ULONG SpinLockAcquireCount; //0x3560
ULONG SpinLockContentionCount; //0x3564
ULONG SpinLockSpinCount; //0x3568
ULONG IpiSendRequestBroadcastCount; //0x356c
ULONG IpiSendRequestRoutineCount; //0x3570
ULONG IpiSendSoftwareInterruptCount; //0x3574
ULONG ExInitializeResourceCount; //0x3578
ULONG ExReInitializeResourceCount; //0x357c
ULONG ExDeleteResourceCount; //0x3580
ULONG ExecutiveResourceAcquiresCount; //0x3584
ULONG ExecutiveResourceContentionsCount; //0x3588
ULONG ExecutiveResourceReleaseExclusiveCount; //0x358c
ULONG ExecutiveResourceReleaseSharedCount; //0x3590
ULONG ExecutiveResourceConvertsCount; //0x3594
ULONG ExAcqResExclusiveAttempts; //0x3598
ULONG ExAcqResExclusiveAcquiresExclusive; //0x359c
ULONG ExAcqResExclusiveAcquiresExclusiveRecursive; //0x35a0
ULONG ExAcqResExclusiveWaits; //0x35a4
ULONG ExAcqResExclusiveNotAcquires; //0x35a8
ULONG ExAcqResSharedAttempts; //0x35ac
ULONG ExAcqResSharedAcquiresExclusive; //0x35b0
ULONG ExAcqResSharedAcquiresShared; //0x35b4
ULONG ExAcqResSharedAcquiresSharedRecursive; //0x35b8
ULONG ExAcqResSharedWaits; //0x35bc
ULONG ExAcqResSharedNotAcquires; //0x35c0
ULONG ExAcqResSharedStarveExclusiveAttempts; //0x35c4
ULONG ExAcqResSharedStarveExclusiveAcquiresExclusive; //0x35c8
ULONG ExAcqResSharedStarveExclusiveAcquiresShared; //0x35cc
ULONG ExAcqResSharedStarveExclusiveAcquiresSharedRecursive; //0x35d0
ULONG ExAcqResSharedStarveExclusiveWaits; //0x35d4
ULONG ExAcqResSharedStarveExclusiveNotAcquires; //0x35d8
ULONG ExAcqResSharedWaitForExclusiveAttempts; //0x35dc
ULONG ExAcqResSharedWaitForExclusiveAcquiresExclusive; //0x35e0
ULONG ExAcqResSharedWaitForExclusiveAcquiresShared; //0x35e4
ULONG ExAcqResSharedWaitForExclusiveAcquiresSharedRecursive; //0x35e8
ULONG ExAcqResSharedWaitForExclusiveWaits; //0x35ec
ULONG ExAcqResSharedWaitForExclusiveNotAcquires; //0x35f0
ULONG ExSetResOwnerPointerExclusive; //0x35f4
ULONG ExSetResOwnerPointerSharedNew; //0x35f8
ULONG ExSetResOwnerPointerSharedOld; //0x35fc
ULONG ExTryToAcqExclusiveAttempts; //0x3600
ULONG ExTryToAcqExclusiveAcquires; //0x3604
ULONG ExBoostExclusiveOwner; //0x3608
ULONG ExBoostSharedOwners; //0x360c
ULONG ExEtwSynchTrackingNotificationsCount; //0x3610
ULONG ExEtwSynchTrackingNotificationsAccountedCount; //0x3614
struct _CONTEXT* Context; //0x3618
ULONG ContextFlags; //0x361c
struct _XSAVE_AREA* ExtendedState; //0x3620
};
  • CurrentThread:当前CPU正在跑的线程。
  • NextThread:将要切换的线程。
  • IdleThread:如果没有要切换的线程,CPU将要跑的空闲线程。
  • DispatcherReadyListHead:线程就绪位,共32位,哪一位为1说明哪一个核上有线程在跑。

3.2 代码获取KiProcessorBlock

由于KiProcessorBlock是未公开的变量,因此不能直接使用。但是可以通过上边的KdVersionBlock进行获取。由于只有0核下这个成员才有值,因此需要将当前的运行线程切换到0核,可以通过函数KeSetSystemAffinityThread指定当前线程到0核执行。

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
#include <ntifs.h>
#include <intrin.h>


typedef struct _DBGKD_GET_VERSION64
{
USHORT MajorVersion; //0x0
USHORT MinorVersion; //0x2
UCHAR ProtocolVersion; //0x4
UCHAR KdSecondaryVersion; //0x5
USHORT Flags; //0x6
USHORT MachineType; //0x8
UCHAR MaxPacketType; //0xa
UCHAR MaxStateChange; //0xb
UCHAR MaxManipulate; //0xc
UCHAR Simulation; //0xd
USHORT Unused[1]; //0xe
ULONGLONG KernBase; //0x10
ULONGLONG PsLoadedModuleList; //0x18
ULONGLONG DebuggerDataList; //0x20
}DBGKD_GET_VERSION64, *PDBGKD_GET_VERSION64;

VOID unloaddrv(PDRIVER_OBJECT pDrv)
{

}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDrv,PUNICODE_STRING pReg)
{
pDrv->DriverUnload = unloaddrv;

KeSetSystemAffinityThread(0);//设置到0核
PKPCR kpcr = (PKPCR)__readfsdword(0x1C); //SelfPcr
PDBGKD_GET_VERSION64 pdbgVer = (PDBGKD_GET_VERSION64)kpcr->KdVersionBlock;
ULONG_PTR DbgDataList = *(PULONG_PTR)(pdbgVer->DebuggerDataList);
DbgPrint("DbgDataList=%p\n", DbgDataList);
ULONG_PTR KiProcessorBlock = *(PULONG_PTR)(DbgDataList + 0x218); //KiProcessorBlock - *DebuggerDataList = 0x218
DbgPrint("KiProcessorBlock=%p\n", KiProcessorBlock);
KeRevertToUserAffinityThread();//恢复
return STATUS_SUCCESS;
}

image-20230614014234470

4、OBJECT_HEADER

每一个对象都有一个对象头,然后接着才是对象类型的结构内容。

image-20230613130813145

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//0x20 bytes (sizeof)
struct _OBJECT_HEADER
{
LONG PointerCount; //0x0
union
{
LONG HandleCount; //0x4
VOID* NextToFree; //0x4
};
struct _EX_PUSH_LOCK Lock; //0x8
UCHAR TypeIndex; //0xc
UCHAR TraceFlags; //0xd
UCHAR InfoMask; //0xe
UCHAR Flags; //0xf
union
{
struct _OBJECT_CREATE_INFORMATION* ObjectCreateInfo; //0x10
VOID* QuotaBlockCharged; //0x10
};
VOID* SecurityDescriptor; //0x14
struct _QUAD Body; //0x18
};
  • PointerCount:对应的内核对象被使用了多少次。如调用ObXXXX函数都会将该值+1。该值为0时,对应的内核对象就会被释放。
  • HandleCount:句柄引用。
  • TypeIndex:对应的内核对象的类型。
  • Flags:详细作用未知。当此处的值被置为4时(对应的对象为进程对象时),对应的进程被保护,无法打开、附加。且不影响进程功能。仅对3环起作用。
  • Body:接着实际对象类型的结构数据,比如EPROCESS,ETHREAD等等。因此一个对象-0x18可以得到对象头的首地址。

5、Window查询就绪线程函数

KPRCB中ReadySummary成员(0x31EC)为就绪位图,4字节32位。每一位对应一条就绪链表。某一位为1则说明对应的就绪链表中有等待执行的线程。32条就绪链表存储在KPRCB中DispatcherReadyListHead成员。如:ReadySummary值为5,二进制0101,说明第0号、第3号就绪链表中存在待执行线程。在线程切换时就会从这两个链表中取待切换的线程。

Windows中KiFindReadyThread用来查询就绪函数。

image-20230613160832341

通过对函数的交叉引用可知前一个参数为ReadySummary后两个都为KPRCB指针。

image-20230613160745296

bsr从低->高扫描源操作数第一个为1的位置,下标保存到目的操作数。

bx = 0x10 -> 0001 0000

bsr ax,bx -> ax = 4

  • 没有就绪线程时,CPU会运行一个系统事先准备好的空线程。(Ide)
  • 线程亲核性决定了线程可以跑在哪个核上。如果恶意修改某个线程结构的亲核性为不存在的核,等同于杀死线程。

6、Windows交换线程函数

Windows中有两种线程切换方式,一种是主动切换(WaitForSingleObject、Sleep),另一种是被动切换(CPU时钟)。

线程状态(来自WKR):

1
2
3
4
5
6
7
8
9
10
11
ypedef enum _KTHREAD_STATE {
Initialized, //初始化状态
Ready, //就绪状态,此时线程在就绪链表中等待被取出。
Running, //运行状态
Standby, //备用状态,当一个线程被设置到KPRCB的NextThread成员时,为备用状态,线程切换时会直接切换到NextThread中的线程。
Terminated, //结束状态。
Waiting, //等待状态。
Transition, //交换状态。 当线程优先级很低,执行频率很低时,会被交换到磁盘上。此时为交换状态。内部细分为:正在换出、已经换出、正在换入、已经换入。
DeferredReady, //没用到
GateWait //没用到
} KTHREAD_STATE;

6.1 主动切换

1
WaitForSingleObject->NtWaitForSingleObject->KiCommitThreadWait->KiSwapThread
1
Sleep->SleepEx->RtlDelayExecution->NtDelayExecution->KiCommitThreadWait->KiSwapThread

6.1.1 KiSwapThread逆向

image-20230614114646701

image-20230614114706308

image-20230614114718633

KiSwapThread只是做了查询就绪函数,实际上线程交换是在KiSwapContext

6.1.2 KiSearchForNewThread逆向

image-20230614114935264

image-20230614115004134

image-20230614115014216

KiSearchForNewThread函数主要是对KiSwapThread调用时出入的KPRCB参数进行查找就绪函数,如果当前核找不到则切换到其他核找。

6.1.3 SwapContext逆向

image-20230614115135812

首先将新线程的状态设置为就绪,然后获取新线程的堆栈作为SwapContext_PatchXSave参数调用。

image-20230614115229779

接着是判断新旧线程是否来自同一个进程。如果是同一个进程则进行切换环境。

image-20230614115305076

如果不是同一个进程则会切换CR3操作之类的。

image-20230614115612425

1、用户层绝大部分API的调用都会导致线程的切换

2、本质上没有进程切换,只是在线程切换时如果不是同一进程则会顺带CR3进行处理。

6.2 被动切换

被动切换实际上是CPU时钟切换,windbg使用!idt中断表,其中有一项名为hal!HalpHpetClockInterrupt

image-20230614120241008

可以看到属于hal模块,因为CPU操作属于硬件操作。hal负责与硬件进行操作。Windows系统对于不同的处理器提供了不同的hal模块。在Windbg中输入命令lm查看当前Windows使用的hal模块全名称。

image-20230614120410056

ida打开后跳转到HalpHpetClockInterrupt.

image-20230614120813868

简单分析后实际上有用的也就KeUpdateSystemTime函数,进入查看。

image-20230614120850794

发现来自导入表。

image-20230614120909664

回到ntotkrnl查看该函数。

image-20230614120949595

函数很长,实际上有用的是最后的返回函数。

image-20230614121210212

继续回到hal查看HalRequestSoftwareInterrupt

image-20230614121501799

进入KfLowerIrql.

image-20230614121554962

进入HalpCheckForSoftwareInterrupt.

image-20230614121614349

进入HalpDispatchSoftwareInterrupt

image-20230614121631225

回到ntoskrnl进入KiDispatchInterrupt.

image-20230614121711042

CPU时钟的调用流程如下:

1
2
3
4
5
6
7
8
9
hal!HalpHpetClockInterrupt
nt!KeUpdateSystemTime
nt!KeUpdateRunTime
hal!HalRequestSoftwareInterrupt
hal!KfLowerIrql
hal!HalpCheckForSoftwareInterrupt
hal!HalpDispatchSoftwareInterrupt
nt!KiDispatchInterrupt
SwapContext

只是简单跟了一下~~~~。

另外,当软件发生异常的时候(实际上就是中断),就会进行线程切换。因此,在Windows操作系统中,调用绝大部分API 以及 触发各种异常 均会导致线程的切换。如果想让自己的线程永远占有CPU,则不可以调用API、不能触发异常(内存访问也可能触发缺页异常,在用户层是看不出来的)。