内核中API与进程、线程和模块的监测

内核中API与进程、线程和模块的监测


内核中进程、线程和模块的监测API

windows ddk提供了3个API分别用于监视模块加载、线程创建和进程创建事件,分别是
PsSetCreateProcessNotifyRoutine,PsSetCreateThreadNotifyRoutine和 PsSetLoadImageNotifyRoutine。

一、原型

原型如下:
NTSTATUS
PsSetCreateProcessNotifyRoutine(
IN PCreate_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
IN BOOLEAN Remove //表示是设置NotifyRoutine还是取消以前设置的
);
VOID
(*PCreate_PROCESS_NOTIFY_ROUTINE) (
IN HANDLE ParentId, //触发事件的父进程id
IN HANDLE ProcessId, //触发时间的子进程的id
IN BOOLEAN Create //表示进程创建还是删除
);
其中ProcessId和ParentId分别是触发此事件的进程id和此进程的父进程id,Create表示是进程创建还是删除。

NTSTATUS
PsSetCreateThreadNotifyRoutine(
IN PCreate_THREAD_NOTIFY_ROUTINE NotifyRoutine
);
VOID
(*PCreate_THREAD_NOTIFY_ROUTINE) (
IN HANDLE ProcessId,
IN HANDLE ThreadId,
IN BOOLEAN Create
);
其中ThreadId和ProcessId分别为出发此事件的线程id和此线程的进程id,Create表示是线程创建还是删除。

NTSTATUS
PsSetLoadImageNotifyRoutine(
IN PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine
);
VOID
(*PLOAD_IMAGE_NOTIFY_ROUTINE) (
IN PUNICODE_STRING FullImageName,
IN HANDLE ProcessId, // where image is mapped
IN PIMAGE_INFO ImageInfo
);
其中FullImageName表示模块的名字,ProcessId表示模块映射到的进程id,ImageInfo此模块加载的信息。
typedef struct _IMAGE_INFO {
union {
ULONG Properties;
struct {
ULONG ImageAddressingMode : 8; //code addressing mode,一直为IMAGE_ADDRESSING_MODE_32BIT
ULONG SystemModeImage : 1; //system mode image,1表示内核模块,0为用户模块
ULONG ImageMappedToAllPids : 1; //mapped in all processes,总为0
ULONG Reserved : 22;//总为0
};
};
PVOID ImageBase; //模块映射的基址
ULONG ImageSelector; //0
ULONG ImageSize; //模块映射大小
ULONG ImageSectionNumber; //0
} IMAGE_INFO, *PIMAGE_INFO;


二、实现
参考WRK(Windows Research Kernel) v1.2中的NT Kernel源代码(windows server 2003 sp1)。
在ntos\ps\create.c文件中可以看到上述函数的实现,系统通过三个函数指针数组保存回调函数,如下:
#define PSP_MAX_Create_PROCESS_NOTIFY 8
ULONG PspCreateProcessNotifyRoutineCount; //计数器,下同
EX_CALLBACK PspCreateProcessNotifyRoutine[PSP_MAX_Create_PROCESS_NOTIFY];

#define PSP_MAX_Create_THREAD_NOTIFY 8
ULONG PspCreateThreadNotifyRoutineCount;
EX_CALLBACK PspCreateThreadNotifyRoutine[PSP_MAX_Create_THREAD_NOTIFY];

#define PSP_MAX_LOAD_IMAGE_NOTIFY 8
ULONG PspLoadImageNotifyRoutineCount;
EX_CALLBACK PspLoadImageNotifyRoutine[PSP_MAX_LOAD_IMAGE_NOTIFY];

设置回调函数就是往数组中填充函数指针,反之将数组清空便可卸载回调函数。
进程和线程的回调函数都会在 NtCreateThread-->PspCreateThread中调用,但进程回调函数只会在进程中线程数为0被调用,即创建进程后第一次创建线程时。模块加载的回调函数在 NtMapViewOfSection-->MmMapViewOfSection-->MiMapViewOfImageSection中调用。

只有PsSetCreateProcessNotifyRoutine可以通过设置remove参数来进行加载和卸载,ddk没有导出函数来卸载 CreateThreadNotifyRoutine和LoadImageNotifyRoutine,尽管wrk v1.2代码中有相应的实现函数。但卸载原理一样,就是将数组中对应的指针清空。win2000下卸载可以参考安全焦点上的文章《安全稳定的实现进线程监控http://www.xfocus.net/articles/200503/788.html》,作者通过硬编码这些数组的地址来进行卸载。

三、应用

内核模块调用以上三个API时要保证IRQL为PASSIVE_LEVEL,回调函数未卸载前,内核模块不能卸载。有了这三个函数就可以实现进程和线程的实时监控,LoadImageNotifyRoutine可以用来从内核模块中hook目标模块的IAT,比从用户空间进行hook要更难被检测。

文章来自: 本站原创
Tags:
评论: 0 | 查看次数: 9196