简体   繁体   English

WDM驱动程序和用户模式通信:最佳实践和回调问题

[英]WDM driver and user-mode communication: best practices and callback questions

The purpose of my driver is to notify a user-mode application about every callback that I receive, passing the data I get from those registered routines to it. 驱动程序的目的是将收到的每个回调通知用户模式应用程序,并将从这些已注册例程获取的数据传递给它。 The user-mode application will then print on the screen (it's a simple Win32 console application) every information it receives from kernel. 然后,用户模式应用程序将从内核接收到的所有信息都打印在屏幕上(这是一个简单的Win32控制台应用程序)。 I currently register three callbacks: PsSetCreateProcessNotifyRoutineEx , PsSetCreateProcessNotifyRoutineEx2 and PsSetLoadImageNotifyRoutine . 我目前注册了三个回调: PsSetCreateProcessNotifyRoutineExPsSetCreateProcessNotifyRoutineEx2PsSetLoadImageNotifyRoutine I would like to know: 我想知道:

1) Which is the "best" method for communication between kernel-mode and user-mode, considering that many processes and many images can be loaded at the same time? 1)考虑到可以同时加载许多进程和许多映像,哪种是在内核模式和用户模式之间进行通信的“最佳”方法?

2) Should I implement such a method for every call or should I store some information and push them to user-mode every, let's say, 0.5 seconds? 2)我应该为每个呼叫实现这样的方法还是应该存储一些信息,并且每隔0.5秒将它们推送到用户模式?

I actually use the following code for defining the IOCTL in the driver: 我实际上使用以下代码在驱动程序中定义IOCTL:

#define IOCTL_RECEIVE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_READ_DATA)

This is the code of my DispatchDeviceControl function: 这是我的DispatchDeviceControl函数的代码:

NTSTATUS DispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    KIRQL CurrentIRQL = KeGetCurrentIrql();
    DbgPrint("DispatchDeviceControl called at IRQL level: %d", CurrentIRQL);

    PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS status = STATUS_SUCCESS;
    PVOID buffer = Irp->AssociatedIrp.SystemBuffer;
    ULONG inLength = irpsp->Parameters.DeviceIoControl.InputBufferLength;
    ULONG outLength = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
    ULONG returnLength = 0;
    pMsg PMSG = NULL;

    switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
    {   
    case IOCTL_RECEIVE:
        DbgPrint("IOCTL_RECEIVE message sent\n");
        KeWaitForSingleObject(&kEvent, Executive, KernelMode, 0, NULL);
        PMSG = (pMsg)ExInterlockedRemoveHeadList(&listhead, &spinlock);
        // Copy data to the buffer
        RtlCopyMemory((PCHAR)buffer, (PCHAR)PMSG, sizeof(Msg));
        // Release the structure
        ExFreePool(PMSG);
        // Set returnLength
        returnLength = sizeof(Msg);
        break;
    default:
        status = STATUS_INVALID_PARAMETER;
    }
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = returnLength;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return status;
}

And the code of one of my callback routines: 和我的回调例程之一的代码:

_Use_decl_annotations_
VOID prcmPsCreateProcessNotifyRoutineEx2(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
    // If process is exiting, just return immediately
    if (CreateInfo == NULL)
        return;

    pMsg PMSG = (pMsg)ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(Msg), 'prcm');

    if (PMSG == NULL)
    {
        DbgPrint("PsCreateProcessNotifyRoutineEx2: ERROR allocating Pool space for data to be sent to user mode\n");
        return;
    }

    // Fill data
    PMSG->ParentId = NULL;
    PMSG->ProcessId = ProcessId;
    PMSG->FullImageName = NULL;

    ExInterlockedInsertHeadList(&listhead, (PLIST_ENTRY)PMSG, &spinlock);
    KeSetEvent(&kEvent, 0, FALSE);

    return;
}

And finally the struct definition (I included some example values, but the first element is obviously the LIST_ENTRY : 最后是struct定义(我提供了一些示例值,但第一个元素显然是LIST_ENTRY

typedef struct {
    LIST_ENTRY listhead;
    HANDLE ParentId;
    HANDLE ProcessId;
    PUNICODE_STRING FullImageName;
} Msg, *pMsg;

For your reference, I correctly call in my DriverEntry function: 供您参考,我正确调用了DriverEntry函数:

KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);
KeInitializeSpinLock(&spinlock);
InitializeListHead(&listhead);

In my user-mode console application, I create a thread in the main routine and in its relative thread function I constantly check for DeviceIoControl 's return value in order to print in real time the information that I get from the driver. 在我的用户模式控制台应用程序中,我在主例程中创建一个线程,并在其相对线程函数中不断检查DeviceIoControl的返回值,以便实时打印从驱动程序获得的信息。

3) A little bit off-topic but I think it is something related: is it normal for me to miss some notifications in user-mode, with this code? 3)有点偏离主题,但我认为这是相关的:使用此代码,在用户模式下错过一些通知是否正常? Does anybody know why? 有人知道为什么吗?

OSR wrote a great article, The Inverted Call Model . OSR撰写了一篇很棒的文章, 《反向调用模型》 The basic design of such a system consists of a service, a driver, and some protocol between the two. 这种系统的基本设计包括服务,驱动程序和两者之间的某些协议。 It works with monolithic drivers as well as layered drivers and only requires that the driver be able to receive device control operations. 它与单片驱动程序以及分层驱动程序一起使用,仅要求驱动程序能够接收设备控制操作。

I would also like to mention that you are using both PsSetCreateProcessNotifyRoutineEx and PsSetCreateProcessNotifyRoutineEx2. 我还想提到您同时使用了PsSetCreateProcessNotifyRoutineEx和PsSetCreateProcessNotifyRoutineEx2。 You should use one or the other. 您应该使用其中一个。 PsSetCreateProcessNotifyRoutineEx2 is supported starting with Windows 10, version 1703 Windows Server 2016 . Windows 10版本1703 Windows Server 2016开始支持PsSetCreateProcessNotifyRoutineEx2。 It provides better functionality, so I would recommend to use it if possible. 它提供了更好的功能,因此,我建议尽可能使用它。

You can determine which to use by using MmGetSystemRoutinAddress for dynamic usage of of PsSetCreateProcessNotifyRoutineEx2, if it is unavailable then use PsSetCreateProcessNotifyRoutineEx. 您可以通过使用MmGetSystemRoutinAddress来动态使用PsSetCreateProcessNotifyRoutineEx2来确定使用哪个(如果不可用),然后使用PsSetCreateProcessNotifyRoutineEx。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM