我们来看一下进线程监视在底层是如何实现的,在win2000源代码中先找到创建线程的函数实现:
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// \win2k\private\ntos\ps\create.h
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
PspCreateThread(
...
...
)
{
...
if (PspCreateProcessNotifyRoutineCount != 0) { //首先调用进程监控函数
ULONG i;
for (i=0; i if (PspCreateProcessNotifyRoutine[i] != NULL) {
(*PspCreateProcessNotifyRoutine[i])( Process->InheritedFromUniqueProcessId,
Process->UniqueProcessId,
TRUE
);
}
}
}
}
...
...
if (PspCreateThreadNotifyRoutineCount != 0) {
ULONG i;
for (i=0; i if (PspCreateThreadNotifyRoutine[i] != NULL) {
(*PspCreateThreadNotifyRoutine[i])( Thread->Cid.UniqueProcess,
Thread->Cid.UniqueThread,
TRUE
);
}
}
}
...
...
}
从上面可以看到,在每创建一个线程后会调用PspCreateProcessNotifyRoutine[i]地址指向的函数.而PsSetCreateThreadNotifyRoutine的作用就是将PspCreateThreadNotifyRoutine[i]数组设置值,该值就是监视函数的地址.
NTSTATUS
PsSetCreateThreadNotifyRoutine(
IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
)
{
ULONG i;
NTSTATUS Status;
Status = STATUS_INSUFFICIENT_RESOURCES;
for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i += 1) {
if (PspCreateThreadNotifyRoutine[i] == NULL) {
PspCreateThreadNotifyRoutine[i] = NotifyRoutine;
PspCreateThreadNotifyRoutineCount += 1;
Status = STATUS_SUCCESS;
break;
}
}
return Status;
}
上面的一些结构如下:
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// \win2k\private\ntos\ps\psp.h
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
#define PSP_MAX_CREATE_THREAD_NOTIFY 8 //最大监视数目
ULONG PspCreateThreadNotifyRoutineCount; //用来记数
PCREATE_THREAD_NOTIFY_ROUTINE PspCreateThreadNotifyRoutine[ PSP_MAX_CREATE_THREAD_NOTIFY ]; //函数地址数组
而PCREATE_THREAD_NOTIFY_ROUTINE定义如下:
typedef
VOID
(*PCREATE_THREAD_NOTIFY_ROUTINE)(
IN HANDLE ProcessId,
IN HANDLE ThreadId,
IN BOOLEAN Create
);
相应的,进程的结构也是一样的.
通过上面,我们可以看到,只要我们找出该函数数组地址,在我们退出驱动时先将其全部清零,清零的大小为PSP_MAX_CREATE_THREAD_NOTIFY,
这样的话下一次的进线程操作就不会调用这个函数指针了.也就让系统回到正常,我们再通过PsSetCreateProcessNotifyRoutine来验证一下:
NTSTATUS
PsSetCreateProcessNotifyRoutine(
IN PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
IN BOOLEAN Remove
)
{
ULONG i;
for (i=0; i < PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
if (Remove) {
if (PspCreateProcessNotifyRoutine[i] == NotifyRoutine) { //清除时就是简单的赋植操作
PspCreateProcessNotifyRoutine[i] = NULL;
PspCreateProcessNotifyRoutineCount -= 1; //将计数器减一
return STATUS_SUCCESS;
}
} else {
if (PspCreateProcessNotifyRoutine[i] == NULL) { //设置时也是简单的赋值操作
PspCreateProcessNotifyRoutine[i] = NotifyRoutine;
PspCreateProcessNotifyRoutineCount += 1; //将计数器加一
return STATUS_SUCCESS;
}
}
}
return Remove ? STATUS_PROCEDURE_NOT_FOUND : STATUS_INVALID_PARAMETER;
}
好了,方法已经知道了,只要找出地址,我们就能够"全身而退"了.看一下windows2003下面的PsRemoveCreateThreadNotifyRoutine实现:
lkd> u PsRemoveCreateThreadNotifyRoutine l 20
nt!PsRemoveCreateThreadNotifyRoutine:
80651d7b 53 push ebx
80651d7c 56 push esi
80651d7d 57 push edi
80651d7e 33db xor ebx,ebx
80651d80 bf400f5780 mov edi,0x80570f40 //起始地址
80651d85 57 push edi
80651d86 e8a7500100 call nt!ExWaitForRundownProtectionRelease+0x5cf (80666e32)
80651d8b 8bf0 mov esi,eax
80651d8d 85f6 test esi,esi
80651d8f 7420 jz nt!PsRemoveCreateThreadNotifyRoutine+0x36 (80651db1)
80651d91 56 push esi
80651d92 e8ba1bffff call nt!IoReportTargetDeviceChange+0x7aa0 (80643951)
80651d97 3b442410 cmp eax,[esp+0x10]
80651d9b 750d jnz nt!PsRemoveCreateThreadNotifyRoutine+0x2f (80651daa)
80651d9d 56 push esi
80651d9e 6a00 push 0x0
80651da0 57 push edi
80651da1 e8c54f0100 call nt!ExWaitForRundownProtectionRelease+0x508 (80666d6b)
80651da6 84c0 test al,al
80651da8 751b jnz nt!PsRemoveCreateThreadNotifyRoutine+0x4a (80651dc5)
80651daa 56 push esi
80651dab 57 push edi
80651dac e892510100 call nt!ExWaitForRundownProtectionRelease+0x6e0 (80666f43)
80651db1 43 inc ebx
80651db2 83c704 add edi,0x4
80651db5 83fb08 cmp ebx,0x8 //看是否到了最大数(8)
80651db8 72cb jb nt!PsRemoveCreateThreadNotifyRoutine+0xa (80651d85)
80651dba b87a0000c0 mov eax,0xc000007a
80651dbf 5f pop edi
80651dc0 5e pop esi
80651dc1 5b pop ebx
80651dc2 c20400 ret 0x4
lkd> dd 0x80570f40 //设置了监视函数后
80570f40 e316e557 00000000 00000000 00000000
.............................
lkd> dd 0x80570f40 //清除了监视函数后
80570f40 00000000 00000000 00000000 00000000
哈哈.下面是实现代码,代码中实现了进线的的监视,并且实现了远线程的监视:
Drivers.c
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Made By ZwelL
#include "ntddk.h"
#include "windef.h"
#include "define.h"
#define SYSNAME "System"
#define VERSIONLEN 100
const WCHAR devLink[] = L"\\??\\MyEvent";
const WCHAR devName[] = L"\\Device\\MyEvent";
UNICODE_STRING devNameUnicd;
UNICODE_STRING devLinkUnicd;
PVOID gpEventObject = NULL; // 与应用程序通信的 Event 对象
ULONG ProcessNameOffset =0;
PVOID outBuf[255];
BOOL g_bMainThread;
ULONG g_dwParentId;
CHECKLIST CheckList;
ULONG BuildNumber; //系统版本号
ULONG SYSTEMID; //System进程的ID
PWCHAR Version[VERSIONLEN];
NTSTATUS PsLookupProcessByProcessId(IN ULONG ulProcId, OUT PEPROCESS * pEProcess);