关于监控文件系统上的IRP序列

最近在写一个基于生物免疫学原理的恶意代码检测系统,其中需要应用程序在Windows内核中产生的IRP序列。本文总结一下怎么获得文件系统上的IRP序列。希望对需要ring0主动向ring3频繁通信的朋友有点帮助


基本框架是Minifilter。向minifilter注册回调函数来监控走过文件系统设备上的IRP。

Minifilter框架详见微软WDK文档:http://msdn.microsoft.com/en-us/library/ff540402.aspx


接下来就是如何拿到IRP序列并传输到ring3层:

首先得申请一块ring0和ring3的共享内存

1. ExAllocatePool申请一块非分页内存

2. IoAllocateMdl得到内存块的描述符列表MDL

3. MmBuildMdlForNonPagedPool将2中得到的MDL更新成物理页的描述符

通过向minifilter发送消息(FilterSendMessage),或者通过DeviceIoControl,来获得ring0中申请的内存的ring3地址

1. 在处理ring3消息的函数中通过MmMapLockedPages将物理页映射到当前进程地址空间,拿到虚拟地址

2. 将得到的当前进程地址空间地址通过输出缓冲区传回ring3


接下来就可以通过向那段共享内存写入数据,并用事件(Event)来通知ring3接收数据。这时候注意得让ring3在读取完毕后通过一个事件或者别的同步手段通知内核继续IRP捕获。

能这么做的原因是minifilter似乎已经完成了IRP请求的序列化,通过测试发现,针对单个进程捕获到的IRP是串行的。所以可以KeWaitForSingleObject来等待用户完成而不会导致序列数据的丢失。


接下来贴关键代码:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
	IN PUNICODE_STRING RegistryPath)
{
	/******************************** 建立共享内存 ********************************/
	g_sysAddr = ExAllocatePool(NonPagedPool, sizeof(Message));
	g_pMdl = IoAllocateMdl(g_sysAddr, sizeof(Message), FALSE, FALSE, NULL);
	MmBuildMdlForNonPagedPool(g_pMdl);
}


将ring3传进来的句柄换成事件的内核对象指针PRKEVENT
NTSTATUS IRPSetEvent(IN HANDLE hEvent, OUT PRKEVENT *pEvent)
{
	NTSTATUS status = ObReferenceObjectByHandle(
		hEvent,
		SYNCHRONIZE,
		*ExEventObjectType,
		KernelMode,
		(PVOID *)pEvent,
		NULL
		);
	if(!NT_SUCCESS(status))
	{
		*pEvent = NULL;
	}
	else
	{
		ObDereferenceObject(*pEvent);			// 解除引用,防止句柄泄露
	}
	return status;
}

得到当前进程地址空间的指针

PVOID IRPGetSharedAddress(IN PVOID SystemBuffer)
{
	PVOID userAddr = MmMapLockedPages(g_pMdl, UserMode);
	PMessage pMsg = (PMessage)SystemBuffer;
	pMsg->uAddr = (ULONG)userAddr;
	return userAddr;
}

IRP过滤函数里

// Do something
KeSetEvent(g_p_0to3_Event, 0, FALSE);

// 等待ring3完成事件
KeWaitForSingleObject(g_p_3to0_Event, Executive, KernelMode, FALSE, NULL);


本人水平有限,请各位专家大牛斧正

你可能感兴趣的:(关于监控文件系统上的IRP序列)