一种针对线程的隐蔽式SSDT Hook

这篇文章介绍了本科大一时(2012年)发现的一种隐蔽式SSDT Hook方法,仅适用于32位的Windows系统。当时还没有Windows10,64位系统的普及率也不像现在这么高,当时主要涉及的是32位的Windows 7和Windows XP,代码在Windows XP SP3、Windows 2003 SP1/SP2、Windows 7 SP1、Windows 8下测试通过。

一、SSDT Hook

SSDT Hook是一种经典的Windows内核劫持手段,通过改变Windows用户层的应用程序(RING3)通过SYSENTER指令调用内核功能时进入内核层(RING0)后的系统服务分发(System Services Descriptor)流程,实现对用户层应用程序调用内核功能的拦截。
最古老的SSDT Hook实现是修改内核内存中实现系统服务分发的例程,对其进行kernel inline hook,后来又有人提出修改系统服务分发表(System Services Descriptor Table)的表项。这两种方式均属于全局Hook,缺点十分明显:容易被其它内核权限的程序发现,只需要对相关的例程和SSDT的表项进行校验,即可发现异常之处。

二、系统服务分发的流程

Windows的用户程序在需要调用内核服务时,先将需要调用的内核服务号存入eax寄存器中,随后调用sysenter指令(对于Pentium ii之前的CPUS是int 0x2e)切换到内核模式。sysenter会完成RING3-RING0的切换工作并保存一些信息,随后载入MSR中设定的CS、ESP、EIP的值。这部分操作在ntdll.dll中实现(可以自己编程替代),之后进入内核模式(RIN0),此时EIP指向_KiFastCallEntrt()(对于SYSENTER指令进入),在建立了一个陷阱帧并设置了相关寄存器的值之后进入真正的处理系统服务的例程_KiSystemServiceRepeat(),在WRK 1.2中相关代码如下(\base\ntos\ke\i386\trap.asm):

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
_KiFastCallEntry        proc

;
; Sanitize the segment registers
;
mov ecx, KGDT_R3_DATA OR RPL_MASK
push KGDT_R0_PCR
pop fs
mov ds, ecx
mov es, ecx

;
; When we trap into the kernel via fast system call we start on the DPC stack. We need
; shift to the threads stack before enabling interrupts.
;
mov ecx, PCR[PcTss] ;
mov esp, [ecx]+TssEsp0

push KGDT_R3_DATA OR RPL_MASK ; Push user SS
push edx ; Push ESP
pushfd
Kfsc10:
push 2 ; Sanitize eflags, clear direction, NT etc
add edx, 8 ; (edx) -> arguments
popfd ;
.errnz(EFLAGS_INTERRUPT_MASK AND 0FFFF00FFh)
or byte ptr [esp+1], EFLAGS_INTERRUPT_MASK/0100h ; Enable interrupts in eflags

push KGDT_R3_CODE OR RPL_MASK ; Push user CS
push dword ptr ds:[USER_SHARED_DATA+UsSystemCallReturn] ; push return address
push 0 ; put pad dword for error on stack
push ebp ; save the non-volatile registers
push ebx ;
push esi ;
push edi ;
mov ebx, PCR[PcSelfPcr] ; Get PRCB address
push KGDT_R3_TEB OR RPL_MASK ; Push user mode FS
mov esi, [ebx].PcPrcbData+PbCurrentThread ; get current thread address
;
; Save the old exception list in trap frame and initialize a new empty
; exception list.
;

push [ebx].PcExceptionList ; save old exception list
mov [ebx].PcExceptionList, EXCEPTION_CHAIN_END ; set new empty list
mov ebp, [esi].ThInitialStack

;
; Save the old previous mode in trap frame, allocate remainder of trap frame,
; and set the new previous mode.
;
push MODE_MASK ; Save previous mode as user
sub esp,TsPreviousPreviousMode ; allocate remainder of trap frame
sub ebp, NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH
mov byte ptr [esi].ThPreviousMode,MODE_MASK ; set new previous mode of user
;
; Now the full trap frame is build.
; Calculate initial stack pointer from thread initial stack to contain NPX and trap.
; If this isn't the same as esp then we are a VX86 thread and we are rejected
;

cmp ebp, esp
jne short Kfsc91

;
; Set the new trap frame address.
;
and dword ptr [ebp].TsDr7, 0
test byte ptr [esi].ThDebugActive, 0ffh ; See if we need to save debug registers
mov [esi].ThTrapFrame, ebp ; set new trap frame address

jnz Dr_FastCallDrSave ; if nz, debugging is active on thread

Dr_FastCallDrReturn: ;

SET_DEBUG_DATA ; Note this destroys edi
sti ; enable interrupts

?FpoValue = 0

;
; (eax) = Service number
; (edx) = Callers stack pointer
; (esi) = Current thread address
;
; All other registers have been saved and are free.
;
; Check if the service number within valid range
;

_KiSystemServiceRepeat:
mov edi, eax ; copy system service number
shr edi, SERVICE_TABLE_SHIFT ; isolate service table number
and edi, SERVICE_TABLE_MASK ;
mov ecx, edi ; save service table number
add edi, [esi]+ThServiceTable ; compute service descriptor address
mov ebx, eax ; save system service number
and eax, SERVICE_NUMBER_MASK ; isolate service table offset

这里有一个值得注意的地方:SSDT的地址并非是固定或者存在某个全局变量之中,而是通过PCR拿到当前线程的KTHREAD之后,在KTHREAD之中获取。
KTHREAD的结构如下(位于\base\ntos\inc\ke.h):

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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
typedef struct _KTHREAD {

//
// The dispatcher header and mutant listhead are fairly infrequently
// referenced.
//

DISPATCHER_HEADER Header;
LIST_ENTRY MutantListHead;

//
// The following fields are referenced during context switches and wait
// operatings. They have been carefully laid out to get the best cache
// hit ratios.
//

PVOID InitialStack;
PVOID StackLimit;
PVOID KernelStack;

KSPIN_LOCK ThreadLock;
union {
KAPC_STATE ApcState;
struct {
UCHAR ApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
BOOLEAN ApcQueueable;
volatile UCHAR NextProcessor;
volatile UCHAR DeferredProcessor;
UCHAR AdjustReason;
SCHAR AdjustIncrement;
};
};

KSPIN_LOCK ApcQueueLock;

#if !defined(_AMD64_)

ULONG ContextSwitches;
volatile UCHAR State;
UCHAR NpxState;
KIRQL WaitIrql;
KPROCESSOR_MODE WaitMode;

#endif

LONG_PTR WaitStatus;
union {
PKWAIT_BLOCK WaitBlockList;
PKGATE GateObject;
};

BOOLEAN Alertable;
BOOLEAN WaitNext;
UCHAR WaitReason;
SCHAR Priority;
UCHAR EnableStackSwap;
volatile UCHAR SwapBusy;
BOOLEAN Alerted[MaximumMode];
union {
LIST_ENTRY WaitListEntry;
SINGLE_LIST_ENTRY SwapListEntry;
};

PRKQUEUE Queue;

#if !defined(_AMD64_)

ULONG WaitTime;
union {
struct {
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};

ULONG CombinedApcDisable;
};

#endif

PVOID Teb;
union {
KTIMER Timer;
struct {
UCHAR TimerFill[KTIMER_ACTUAL_LENGTH];

//
// N.B. The following bit number definitions must match the
// following bit field.
//
// N.B. These bits can only be written with interlocked
// operations.
//

#define KTHREAD_AUTO_ALIGNMENT_BIT 0
#define KTHREAD_DISABLE_BOOST_BIT 1

union {
struct {
LONG AutoAlignment : 1;
LONG DisableBoost : 1;
LONG ReservedFlags : 30;
};

LONG ThreadFlags;
};
};
};

union {
KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + 1];
struct {
UCHAR WaitBlockFill0[KWAIT_BLOCK_OFFSET_TO_BYTE0];
BOOLEAN SystemAffinityActive;
};

struct {
UCHAR WaitBlockFill1[KWAIT_BLOCK_OFFSET_TO_BYTE1];
CCHAR PreviousMode;
};

struct {
UCHAR WaitBlockFill2[KWAIT_BLOCK_OFFSET_TO_BYTE2];
UCHAR ResourceIndex;
};

struct {
UCHAR WaitBlockFill3[KWAIT_BLOCK_OFFSET_TO_BYTE3];
UCHAR LargeStack;
};

#if defined(_AMD64_)

struct {
UCHAR WaitBlockFill4[KWAIT_BLOCK_OFFSET_TO_LONG0];
ULONG ContextSwitches;
};

struct {
UCHAR WaitBlockFill5[KWAIT_BLOCK_OFFSET_TO_LONG1];
volatile UCHAR State;
UCHAR NpxState;
KIRQL WaitIrql;
KPROCESSOR_MODE WaitMode;
};

struct {
UCHAR WaitBlockFill6[KWAIT_BLOCK_OFFSET_TO_LONG2];
ULONG WaitTime;
};

struct {
UCHAR WaitBlockFill7[KWAIT_BLOCK_OFFSET_TO_LONG3];
union {
struct {
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};

ULONG CombinedApcDisable;
};
};

#endif

};

LIST_ENTRY QueueListEntry;

//
// The following fields are accessed during system service dispatch.
//

PKTRAP_FRAME TrapFrame;
PVOID CallbackStack;
PVOID ServiceTable;

#if defined(_AMD64_)

ULONG KernelLimit;

#endif

//
// The following fields are referenced during ready thread and wait
// completion.
//

UCHAR ApcStateIndex;
UCHAR IdealProcessor;
BOOLEAN Preempted;
BOOLEAN ProcessReadyQueue;

#if defined(_AMD64_)

PVOID Win32kTable;
ULONG Win32kLimit;

#endif

BOOLEAN KernelStackResident;
SCHAR BasePriority;
SCHAR PriorityDecrement;
CHAR Saturation;
KAFFINITY UserAffinity;
PKPROCESS Process;
KAFFINITY Affinity;

//
// The below fields are infrequently referenced.
//

PKAPC_STATE ApcStatePointer[2];
union {
KAPC_STATE SavedApcState;
struct {
UCHAR SavedApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
CCHAR FreezeCount;
CCHAR SuspendCount;
UCHAR UserIdealProcessor;
UCHAR CalloutActive;

#if defined(_AMD64_)

BOOLEAN CodePatchInProgress;

#elif defined(_X86_)

UCHAR Iopl;

#else

UCHAR OtherPlatformFill;

#endif

};
};

PVOID Win32Thread;
PVOID StackBase;
union {
KAPC SuspendApc;
struct {
UCHAR SuspendApcFill0[KAPC_OFFSET_TO_SPARE_BYTE0];
SCHAR Quantum;
};

struct {
UCHAR SuspendApcFill1[KAPC_OFFSET_TO_SPARE_BYTE1];
UCHAR QuantumReset;
};

struct {
UCHAR SuspendApcFill2[KAPC_OFFSET_TO_SPARE_LONG];
ULONG KernelTime;
};

struct {
UCHAR SuspendApcFill3[KAPC_OFFSET_TO_SYSTEMARGUMENT1];
PVOID TlsArray;
};

struct {
UCHAR SuspendApcFill4[KAPC_OFFSET_TO_SYSTEMARGUMENT2];
PVOID BBTData;
};

struct {
UCHAR SuspendApcFill5[KAPC_ACTUAL_LENGTH];
UCHAR PowerState;
ULONG UserTime;
};
};

union {
KSEMAPHORE SuspendSemaphore;
struct {
UCHAR SuspendSemaphorefill[KSEMAPHORE_ACTUAL_LENGTH];
ULONG SListFaultCount;
};
};

LIST_ENTRY ThreadListEntry;
PVOID SListFaultAddress;

#if defined(_WIN64)

LONG64 ReadOperationCount;
LONG64 WriteOperationCount;
LONG64 OtherOperationCount;
LONG64 ReadTransferCount;
LONG64 WriteTransferCount;
LONG64 OtherTransferCount;

#endif

} KTHREAD, *PKTHREAD, *PRKTHREAD;

#if !defined(_X86AMD64_) && defined(_AMD64_)

C_ASSERT((FIELD_OFFSET(KTHREAD, ServiceTable) + 16) == FIELD_OFFSET(KTHREAD, Win32kTable));
C_ASSERT((FIELD_OFFSET(KTHREAD, ServiceTable) + 8) == FIELD_OFFSET(KTHREAD, KernelLimit));
C_ASSERT((FIELD_OFFSET(KTHREAD, Win32kTable) + 8) == FIELD_OFFSET(KTHREAD, Win32kLimit));

#endif

也就是说,在完成系统服务调用的过程中,系统内核实际使用的是SSDT地址是当前线程的PKTHREAD->ServiceTable,只要修改了这个值,也能实现SSDT Hook,并且这个值仅对当前线程有效,并不会产生全局效应,hook的隐蔽性更强。
配合内核重载,这种形式的SSDT Hook可以保护制定的线程,绕过其他程序的SSDT/Kernel Inline Hook(对于Object Hook无效)。

三、针对线程的隐蔽式SSDT Hook实现

首先实现一些辅助函数和数据结构:

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
typedef struct _KSERVICE_TABLE_DESCRIPTOR 
{
PULONG_PTR Base;
PULONG Counter;
ULONG Limit;
PUCHAR Number;
}KSYSTEM_SERVICE_TABLE,*PKSYSTEM_SERVICE_TABLE,**PPKSYSTEM_SERVICE_TABLE;

PEPROCESS PsLookUpProcessByName(PUCHAR TragetProcessName){

PEPROCESS Process;

LIST_ENTRY ProcessLinksHead;
LIST_ENTRY ProcessLinks;
ANSI_STRING TragetName;
ANSI_STRING ProcessName;

Process = PsGetCurrentProcess();
RtlInitAnsiString(&TragetName,(PCSZ)TragetProcessName);

ProcessLinks = *(PLIST_ENTRY)((PUCHAR)Process + Offset_ActiveProcessLinks_KPROCESS);

ProcessLinksHead = ProcessLinks;


do{

RtlInitAnsiString(&ProcessName,(PCSZ)(PsGetProcessImageFileName((PEPROCESS)ProcessLinks.Flink) - Offset_ActiveProcessLinks_KPROCESS));

if(RtlEqualString(&TragetName,&ProcessName,0)){

return (PEPROCESS)RtlPointerAddOffset(ProcessLinks.Flink,- Offset_ActiveProcessLinks_KPROCESS);/*(PEPROCESS)(ProcessLinks.Flink - Offset_ActiveProcessLinks_KPROCESS);*/
}

ProcessLinks = *ProcessLinks.Flink;
}while(ProcessLinks.Flink != ProcessLinksHead.Flink);

KdPrint(("PsLookUpProcessByName() -- Can't Find Traget Process\n"));

return NULL;

}

PKTHREAD PsGetGUIThread(){


PEPROCESS PTragetProcess;
LIST_ENTRY ThreadList;
LIST_ENTRY ThreadListHead;
PULONG PWin32Thread;

KdPrint(("PsGetGUIThread()\n"));

PTragetProcess = PsLookUpProcessByName((PUCHAR)"csrss.exe");
if(NULL == PTragetProcess){
KdPrint((" PsGetGUIThread() -- Can't Find Traget Process\n"));
return NULL;
}
ThreadList = *(PLIST_ENTRY)((PUCHAR)PTragetProcess + Offset_ThreadListHead_KPROCESS);
ThreadListHead = ThreadList;


do{

PWin32Thread = (PULONG)((PUCHAR)ThreadList.Flink + (Offset_Win32Thread_KTHREAD - Offset_ThreadListEntry_KTHREAD));
if(0 != *PWin32Thread){

KdPrint(("PsGetGUIThread() -- return %x\n",(PKTHREAD)((PUCHAR)PWin32Thread - Offset_Win32Thread_KTHREAD)));

return (PKTHREAD)((PUCHAR)PWin32Thread - Offset_Win32Thread_KTHREAD);
}

ThreadList = *ThreadList.Flink;

}while(ThreadList.Flink != ThreadListHead.Flink);

KdPrint(("PsGetGUIThread() -- Can't Find GUI Thread\n\n\n"));

return NULL;
}

重载内核并初始化SSDT:

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
NTSTATUS KeInitSSDTData(PDRIVER_OBJECT   DriverObject){

NTSTATUS Status;
PKTHREAD PCUIThread;
PKTHREAD PGUIThread;


KdPrint(("InitSSDTData()\n"));
if(SSDIF&&SDIF){

Status = STATUS_UNSUCCESSFUL;
return Status;
}

PCUIThread = PsGetCUIThread();
PGUIThread = PsGetGUIThread();

if(!(PCUIThread && PGUIThread)){

KdPrint(("InitSSDTData() -ERROR to get threadinfo \n"));
KdPrint(("PCUIThread:%x\n",PCUIThread));
KdPrint(("PGUIThread:%x\n",PGUIThread));
Status = STATUS_UNSUCCESSFUL;
return Status;
}



Status = ReLoadNtos(DriverObject,RetAddress);

if(!(NT_SUCCESS(Status))){

KdPrint(("InitSSDTData() -ERROR to reload ntos \n"));

return Status;
}

Status = ReloadWin32K(DriverObject);
if(!(NT_SUCCESS(Status))){

KdPrint(("InitSSDTData() -ERROR to reload Win32k \n"));

return Status;
}


KdPrint(("Safe_ServiceDescriptorTable:%x \n",Safe_ServiceDescriptorTable));
__try{

PSystemSSDT = *(PPKSYSTEM_SERVICE_TABLE)((PUCHAR)PCUIThread + (Offset_ServiceTable_KTHREAD));
PSystemSSDTShadow = *(PPKSYSTEM_SERVICE_TABLE)((PUCHAR)PGUIThread + (Offset_ServiceTable_KTHREAD));

}
__except(EXCEPTION_EXECUTE_HANDLER){

KdPrint(("InitSSDTData() -ERROR ACCESS_VIOLATION \n"));
return STATUS_ACCESS_VIOLATION;

}

PSafeSSDT = (PKSYSTEM_SERVICE_TABLE)Safe_ServiceDescriptorTable;

PSafeSSDTShadow = (PKSYSTEM_SERVICE_TABLE) ExAllocatePool(PagedPool,32);

if(NULL == PSafeSSDTShadow){

Status = STATUS_UNSUCCESSFUL;
return Status;
}

(*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[0].Base = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[0].Base;
(*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[0].Counter = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[0].Counter;
(*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[0].Limit = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[0].Limit;
(*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[0].Number = (*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDT)[0].Number;

(*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[1].Base = Safe_ServiceDescriptorShadowSSDTTable->ServiceTable;
(*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[1].Counter = Safe_ServiceDescriptorShadowSSDTTable->CounterTable;
(*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[1].Limit = Safe_ServiceDescriptorShadowSSDTTable->TableSize;
(*(KSYSTEM_SERVICE_TABLE(*) [])PSafeSSDTShadow)[1].Number = Safe_ServiceDescriptorShadowSSDTTable->ArgumentTable;

SSDIF = 1;//Set flag

SDIF = 1;//Set flag

ProcessInfo.ProcessCount = 0;
ProcessInfo.ProcessListHead.Blink = (PLIST_ENTRY)&ProcessInfo;
ProcessInfo.ProcessListHead.Flink = (PLIST_ENTRY)&ProcessInfo;

ExInitializeFastMutex(&ProcessInfo.FastMutex);
//ObvInitVirtualHandleMoudle();
Status = InitializeCommonVariables();

#ifdef DEBUGINFO

KdPrint(("ProcessInfoTable:%x\n",&ProcessInfo));

#endif

return STATUS_SUCCESS;
}

替换PKTHREAD->ServiceTable:

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
NTSTATUS ReplaceServiceTableOfThread(PKTHREAD PTragetThread){

PULONG PWin32Thread;
PULONG* PServiceTable;
PWin32Thread = (PULONG)((PUCHAR)PTragetThread + Offset_Win32Thread_KTHREAD);
PServiceTable = (PULONG*)((PUCHAR)PTragetThread + Offset_ServiceTable_KTHREAD);

__try{

if(0 == *PWin32Thread){

//On CUIThread.Use SSDT

if(0 == SDIF){
//The Date of My SSDT have not be inited
return STATUS_UNSUCCESSFUL;
}
if(*PServiceTable != (PULONG)PSafeSSDT){

*PServiceTable = (PULONG)PSafeSSDT;
}
}
else{
if(0 == SSDIF){
//The Date of My SSDT have not be inited
return STATUS_UNSUCCESSFUL;
}
if(*PServiceTable != (PULONG)PSafeSSDTShadow){

*PServiceTable = (PULONG)PSafeSSDTShadow;
}
}

}
__except(EXCEPTION_EXECUTE_HANDLER){

KdPrint(("ReplaceServiceTableOfThread() -ERROR ACCESS_VIOLATION \n"));
return STATUS_ACCESS_VIOLATION;

}


return STATUS_SUCCESS;
}

处理指定进程(作为暴露给外界的接口,参数为进程名):

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
NTSTATUS KeReplaceServiceTableOfProcessCheck(PEPROCESS TragetProcess){


NTSTATUS Status;
PLIST_ENTRY NextEntry;
PPROCESS_PROTECT_INFO_ENTRY PProcessInfoEntry;
PVOID HandleTable;


ExAcquireFastMutex(&(ProcessInfo.FastMutex));

//Check that the TragetProcess is not in the table
NextEntry = ProcessInfo.ProcessListHead.Flink;

while(NextEntry != (PLIST_ENTRY)&(ProcessInfo.ProcessListHead)){

PProcessInfoEntry = (PPROCESS_PROTECT_INFO_ENTRY)NextEntry;

if(PProcessInfoEntry->Process == TragetProcess){

//The TragetProcess was in the table,just return success

Status = STATUS_SUCCESS;
goto End;
}

NextEntry = PProcessInfoEntry->ProcessList.Flink;
}

//We ture that is a new process
//Now,Init process info and protect the trager process

//�ڽ�����Ϣ������ӱ���

PProcessInfoEntry = (PPROCESS_PROTECT_INFO_ENTRY)ExAllocatePool(PagedPool,sizeof(PROCESS_PROTECT_INFO_ENTRY));

if(!PProcessInfoEntry){

Status = STATUS_NO_MEMORY;
goto End;
}

Status = ObvCreateVirtualHandleTable(TragetProcess,(PPVIRTUAL_HANDLE_TABLE)&HandleTable);

if(!NT_SUCCESS(Status)){

ExFreePool(PProcessInfoEntry);
goto End;
}

PProcessInfoEntry->Process = TragetProcess;
PProcessInfoEntry->HabdleTable = HandleTable;
PProcessInfoEntry->ProcessList.Flink = (PLIST_ENTRY)&ProcessInfo.ProcessListHead;
PProcessInfoEntry->ProcessList.Blink = (PLIST_ENTRY)ProcessInfo.ProcessListHead.Blink;

((PPROCESS_PROTECT_INFO_ENTRY)(PProcessInfoEntry->ProcessList.Blink))->ProcessList.Flink = (PLIST_ENTRY)PProcessInfoEntry;

ProcessInfo.ProcessListHead.Blink = (PLIST_ENTRY)PProcessInfoEntry;
ProcessInfo.ProcessCount++;

//--
Status = KeReplaceServiceTableOfProcess(TragetProcess);

//##########Hide Process###############
//
//Status = PsHideProcess(TragetProcess);

//#####################################

//Check that is success to replace the ServiceTable
if(!NT_SUCCESS(Status)){

ProcessInfo.ProcessListHead.Blink = (PLIST_ENTRY)PProcessInfoEntry->ProcessList.Blink;
((PPROCESS_PROTECT_INFO_ENTRY)(PProcessInfoEntry->ProcessList.Blink))->ProcessList.Flink = (PLIST_ENTRY)&ProcessInfo.ProcessListHead;
ExFreePool(PProcessInfoEntry);
}


End:

ExReleaseFastMutex(&ProcessInfo.FastMutex);

return Status;

}

NTSTATUS KeReplaceServiceTableOfProcessByName(char* ProcessName){

NTSTATUS Status = STATUS_UNSUCCESSFUL;
PEPROCESS PTragetProcess = PsLookUpProcessByName((PUCHAR)ProcessName);

KdPrint((" ReplaceServiceTableOfProcessByName()\n"));

if(PTragetProcess == NULL){
return Status;
}

Status = KeReplaceServiceTableOfProcessCheck(PTragetProcess);

return Status;
}

进程/线程创建通知,防止冲突造成蓝屏:

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
VOID 
PsCreateProcessNotify(
HANDLE ParentId,
HANDLE ProcessID,
BOOLEAN Create)
{

NTSTATUS Status;
PEPROCESS Process;
PPROCESS_PROTECT_INFO_ENTRY NextProcess;
PVIRTUAL_HANDLE_TABLE HandleTable;
ANSI_STRING ProcessName;

Status = PsLookupProcessByProcessId(ProcessID,&Process);
if(!NT_SUCCESS(Status)){//PID->PEPROCESS

return;
}

//RObfDereferenceObjectSafe(Process);
ObDereferenceObject(Process);
if(!Create){
//���̹ر�֪ͨ
//�������Ƿ�Ϊ�ܱ������̣������ͷ���Ӧ��Դ


ExAcquireFastMutex(&(ProcessInfo.FastMutex));

NextProcess = (PPROCESS_PROTECT_INFO_ENTRY)ProcessInfo.ProcessListHead.Flink;

while(NextProcess != (PPROCESS_PROTECT_INFO_ENTRY)&ProcessInfo.ProcessListHead){

if(NextProcess->Process == Process){


HandleTable = (PVIRTUAL_HANDLE_TABLE)NextProcess->HabdleTable;

//����վ����ָ��
NextProcess->HabdleTable = NULL;

Status = ObvFreeVirtualHandleTable(HandleTable);

(NextProcess->ProcessList.Flink)->Blink = (NextProcess->ProcessList.Blink);
(NextProcess->ProcessList.Blink)->Flink = (NextProcess->ProcessList.Flink);
ProcessInfo.ProcessCount--;
ExFreePool(NextProcess);

//�Ѿ��ͷŵ��ˣ�ֱ���ͷ��� + ����
//�����غ������Ϊδ֪��
ExReleaseFastMutex(&ProcessInfo.FastMutex);
return;

}
NextProcess = (PPROCESS_PROTECT_INFO_ENTRY)NextProcess->ProcessList.Flink;
}



ExReleaseFastMutex(&ProcessInfo.FastMutex);

}
/*
else if(FocusOn){

RtlInitAnsiString(&ProcessName,(PCSZ)(PsGetProcessImageFileName(Process)));
if(!strcmp(ProcessName.Buffer,(char*)ProcessSpyOn)){

KeReplaceServiceTableOfProcessCheck(Process);
}
}*/

return;
}

四、其他部分

代码来自当年自己做的小项目,年代有点久远很多细节想不起来了,那时候编码习惯也不是很好,注释不多而且还保留了大段的废弃代码;当年写了文档但是毁于今年一场小地震造成的硬盘损毁。
伸手党没法直接拿去用,里面包含了对object hook的处理部分,下次有空再写。
当年配合简单的进程断链隐藏可以通过大部分常见的检测方法而且未被发现,代码里用了不少硬编码,如果用在用户量大的产品中,最好通过搜索实现结构偏移定位。
另外,Windows NT 6.0 64位系统下该方法失效,因为SSDT获取不再通过PKTHREAD->ServiceTable。