最近在写一个基于生物免疫学原理的恶意代码检测系统,其中需要应用程序在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); }
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; }
// Do something KeSetEvent(g_p_0to3_Event, 0, FALSE); // 等待ring3完成事件 KeWaitForSingleObject(g_p_3to0_Event, Executive, KernelMode, FALSE, NULL);
本人水平有限,请各位专家大牛斧正