Search code examples
c++cwindowsdriverminifilter

Mini-filter receives constant value from user-mode application


I'm having an issue where I send a message to user-mode from kernel-mode using FltSendMessage that expects a reply. The struct being passed contains an int that is set to either 0 or 1. User-mode replies by setting this flag and calling FilterReplyMessage. However, when the message is received by the kernel, its value is always 56. No matter what number I set the flag to in user-mode, the kernel always receives the value 56. I'm confused as to where my error is.

I've tried changing the data type of passFlag from int to other types (USHORT etc..) which I knew probably wouldn't make a difference, but was worth a try.

Because the kernel message is replied to successfully (Checking user-mode HRESULT returns no errors and there is no timeout so if no reply is received the system would hang, which it does not), I know the error must be with the buffers being passed between user-mode and kernel-mode. I can't seem to find the reason why the passFlag is not being interpreted correctly in kernel-mode.

Can anyone help?

Shared Structure:

typedef struct _REPLY_MESSAGE_STRUCT {

    // Message header.
    FILTER_REPLY_HEADER header;

        // Flag to be set 
        // by user mode.
        int passFlag;

}REPLY_MESSAGE_STRUCT, *PREPLY_MESSAGE_STRUCT; 

Kernel Code:

DbgPrint("Sending Message...\n");

    replyBuffer.passFlag = 0;
    ULONG replySize = ((ULONG)sizeof(replyBuffer.header)) + ((ULONG)sizeof(replyBuffer));
    REPLY_MESSAGE_STRUCT replyBuffer;

    // Note: Try-catch statement has been omitted in this question to save time.
    // In the actual code there is a try-catch statement surrounding FltSendMessage.
    status = FltSendMessage(imageFilterData.filterHandle,
                    &imageFilterData.clientPort,
                    (PVOID)&sendingBuffer.messageBuffer,
                    sizeof(sendingBuffer.messageBuffer),
                    (PVOID)&replyBuffer,
                    &replySize,
                    0
                    );
    // Always returns 56
    // When a reply has been received.
    DbgPrint("Message received: %i\n", replyBuffer.passFlag);

User code:

// User-mode buffer is the same size as kernel-mode buffer.
ULONG replySize = ((ULONG)sizeof(replyBuffer.header)) + ((ULONG)sizeof(replyBuffer));
replyMessage.header.MessageId = messageFromKernel.header.MessageId;
REPLY_MESSAGE_STRUCT replyMessage;

// User-mode setting flag.
replyMessage.passFlag = 1;

// Flag is changed to 1 successfully.
printf("Test: %i\n", replyMessage.passFlag);

// Reply is sent successfully, but flag value on kernel end is always 56
hResult = FilterReplyMessage(port,
    &replyMessage.header,
    replySize);

_com_error err2(hResult);

errorMessage = err2.ErrorMessage();

// No errors.
printf("Result: %s\n", errorMessage);

What I have tried:

  • Changing the datatype of passFlag.

  • Going through every step before and after FltSendMessage and FilterReply message to find if the value is being changed before being sent back to the kernel.


Solution

  • you are using error data in call FltSendMessage:

    ReplyBuffer is pointer to custom user defined data. it must not begin from FILTER_REPLY_HEADER SenderBuffer is pointer to custom user defined data. it must not begin from FILTER_MESSAGE_HEADER

    first of all you need define structures, that are shared between kernel and user mode, for message and reply. for example

    struct SCANNER_NOTIFICATION {
        // any custom data
        int someData;
    
    };
    
    struct SCANNER_REPLY {
        // any custom data
        int passFlag;
    };
    

    and in kernel mode you direct use it as is:

    SCANNER_NOTIFICATION send;
    SCANNER_REPLY reply;
    ULONG ReplyLength = sizeof(reply);
    
    FltSendMessage(*, *, &send, sizeof(send), &reply, &ReplyLength, *);
    

    in user mode you need define 2 additional structures:

    struct SCANNER_MESSAGE : public FILTER_MESSAGE_HEADER, public SCANNER_NOTIFICATION {};
    
    struct SCANNER_REPLY_MESSAGE : public FILTER_REPLY_HEADER, public SCANNER_REPLY {};
    

    (i use c++ style here, when here used c style) and in user mode we need use next, for example:

    SCANNER_MESSAGE* mesage;
    FilterGetMessage(*, mesage, sizeof(SCANNER_MESSAGE), *);
    

    and

        SCANNER_REPLY_MESSAGE reply;
        reply.MessageId = mesage->MessageId;
        FilterReplyMessage(*, &reply, sizeof(reply));