驱动笔记之二:一个成功的驱动程序与应用程序通信的实例(命名管道方式)

经过多次测试,一个笨拙的驱动程序和应用程序通信的实例终于完成了!我不会用 minifilter,所以就用了 named pipe 来解决。

代码如下:

// --- MessageThread.h

#include "wdm.h"

void MessageWorkerEntry(IN PVOID);
// --- MessageThread.cpp

#include "MessageThread.h"
#include "KernelPipe.h"

extern KEVENT keMsg;

void MessageWorkerEntry(IN PVOID pvContext)
{
    HANDLE hPipe = NULL;
    NTSTATUS ns = STATUS_UNSUCCESSFUL;
    PWCHAR pwszPipeName = L"\\??\\pipe\\HelloPipe";
    UNICODE_STRING uniPipeName={0};
    OBJECT_ATTRIBUTES ObjAttr={0};
    IO_STATUS_BLOCK IoStatus={0};

    KdPrint(("In MessageWorkerEntry."));

    RtlInitUnicodeString(&uniPipeName,pwszPipeName);
    InitializeObjectAttributes(&ObjAttr,&uniPipeName,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,NULL,NULL);
    KdPrint(("Pipe name: %wZ", uniPipeName));

    ns = ZwCreateFile(
         &hPipe,            // File handle
         FILE_WRITE_DATA | SYNCHRONIZE,     // Desired access
         &ObjAttr,         // Attributes
         &IoStatus,                  // IO Status Block
         NULL,                   // Allocation size
         0,                      // File attributes
         FILE_SHARE_READ | FILE_SHARE_WRITE, //File share
         FILE_OPEN,              // Create disposition (must exist)
         //FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,// Create options
         FILE_SYNCHRONOUS_IO_NONALERT,
         NULL,                   // EA buffer
         0 );                    // EA size

    KdPrint(("Open status: 0x%x", ns));
    KdPrint(("Pipe handle: %p", hPipe));

    if (NT_SUCCESS(ns))
    {
        ns = ZwSetNamedPipeState(hPipe, FILE_PIPE_MESSAGE_TYPE | FILE_PIPE_MESSAGE_MODE);
        KdPrint(("ZwSetNamedPipeState status: 0x%x", ns));
    }

    IO_STATUS_BLOCK isb;
    LARGE_INTEGER li;
    ns = STATUS_UNSUCCESSFUL;

    li.HighPart = -1;
    li.LowPart = FILE_WRITE_TO_END_OF_FILE;

    int i = 0;
    char ch[] = "This is some sort of test message.";

    for ( i=0; i<10; i++ )
    {
        ns = ZwWriteFile(hPipe, NULL, NULL, NULL, &isb, ch, (ULONG)strlen(ch), NULL, NULL);
        KdPrint(("ZwWriteFile status: 0x%x", ns));
    }

    KeSetEvent(&keMsg, 0, 0);
    ZwClose(hPipe);
    PsTerminateSystemThread(STATUS_SUCCESS);
}
// --- KernelPipe.h

#include "wdm.h"
#include "basetsd.h"
#include "ntdef.h"
//#include "ntifs.h"

#define FILE_PIPE_MESSAGE_MODE          0x00000001
#define FILE_PIPE_MESSAGE_TYPE          0x00000001
#define FILE_PIPE_QUEUE_OPERATION       0x00000000

#define FSCTL_PIPE_ASSIGN_EVENT             CTL_CODE(FILE_DEVICE_NAMED_PIPE, 0, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_DISCONNECT               CTL_CODE(FILE_DEVICE_NAMED_PIPE, 1, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_LISTEN                   CTL_CODE(FILE_DEVICE_NAMED_PIPE, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_PEEK                     CTL_CODE(FILE_DEVICE_NAMED_PIPE, 3, METHOD_BUFFERED, FILE_READ_DATA)
#define FSCTL_PIPE_QUERY_EVENT              CTL_CODE(FILE_DEVICE_NAMED_PIPE, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_TRANSCEIVE               CTL_CODE(FILE_DEVICE_NAMED_PIPE, 5, METHOD_NEITHER,  FILE_READ_DATA | FILE_WRITE_DATA)
#define FSCTL_PIPE_WAIT                     CTL_CODE(FILE_DEVICE_NAMED_PIPE, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_IMPERSONATE              CTL_CODE(FILE_DEVICE_NAMED_PIPE, 7, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_SET_CLIENT_PROCESS       CTL_CODE(FILE_DEVICE_NAMED_PIPE, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_QUERY_CLIENT_PROCESS     CTL_CODE(FILE_DEVICE_NAMED_PIPE, 9, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_GET_PIPE_ATTRIBUTE       CTL_CODE(FILE_DEVICE_NAMED_PIPE, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_SET_PIPE_ATTRIBUTE       CTL_CODE(FILE_DEVICE_NAMED_PIPE, 11, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_GET_CONNECTION_ATTRIBUTE CTL_CODE(FILE_DEVICE_NAMED_PIPE, 12, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_SET_CONNECTION_ATTRIBUTE CTL_CODE(FILE_DEVICE_NAMED_PIPE, 13, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_GET_HANDLE_ATTRIBUTE     CTL_CODE(FILE_DEVICE_NAMED_PIPE, 14, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_SET_HANDLE_ATTRIBUTE     CTL_CODE(FILE_DEVICE_NAMED_PIPE, 15, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define FSCTL_PIPE_FLUSH                    CTL_CODE(FILE_DEVICE_NAMED_PIPE, 16, METHOD_BUFFERED, FILE_WRITE_DATA)

#pragma comment(lib,"ntifs.lib")

extern "C"
__drv_maxIRQL(PASSIVE_LEVEL)
NTSYSAPI
NTSTATUS
NTAPI
ZwFsControlFile(
    __in HANDLE FileHandle,
    __in_opt HANDLE Event,
    __in_opt PIO_APC_ROUTINE ApcRoutine,
    __in_opt PVOID ApcContext,
    __out PIO_STATUS_BLOCK IoStatusBlock,
    __in ULONG FsControlCode,
    __in_bcount_opt(InputBufferLength) PVOID InputBuffer,
    __in ULONG InputBufferLength,
    __out_bcount_opt(OutputBufferLength) PVOID OutputBuffer,
    __in ULONG OutputBufferLength
    );

/*
extern "C"
__kernel_entry NTSYSCALLAPI
NTSTATUS
NTAPI
NtWriteFile (
    __in HANDLE FileHandle,
    __in_opt HANDLE Event,
    __in_opt PIO_APC_ROUTINE ApcRoutine,
    __in_opt PVOID ApcContext,
    __out PIO_STATUS_BLOCK IoStatusBlock,
    __in_bcount(Length) PVOID Buffer,
    __in ULONG Length,
    __in_opt PLARGE_INTEGER ByteOffset,
    __in_opt PULONG Key
    );
*/

#pragma pack(push,1)

/*
typedef unsigned long ULONG, ULONG32;
typedef unsigned short USHORT;
typedef unsigned char UINT8;
*/

typedef struct _WAIT_PIPE_PARAM
{
    __int64 liTimeOutvalue;
    ULONG   ulPipeNameLen;
    USHORT  bUsTimeoutValue;
} WAIT_PIPE_PARAM,*PWAIT_PIPE_PARAM;

typedef struct _NAMED_PIPE_CREATE_PARAMETERS  
{
    ULONG32      NamedPipeType;
    ULONG32      ReadMode;
    ULONG32      CompletionMode;
    ULONG32      MaximumInstances;
    ULONG32      InboundQuota;
    ULONG32      OutboundQuota;
    LARGE_INTEGER DefaultTimeout;
    UINT8        TimeoutSpecified;
    UINT8        _PADDING0_[0x7];
} NAMED_PIPE_CREATE_PARAMETERS, *PNAMED_PIPE_CREATE_PARAMETERS;

#pragma pack(pop)

#define WAIT_FORVER (0x8000000000000000)
#define PIPE_ACCESS_DUPLEX (3)

NTSTATUS ZwCreateNamedPipeFile (
                OUT PHANDLE phPipeHandle,
                IN ULONG ulDesiredAccess,
                IN POBJECT_ATTRIBUTES pObjAttr,
                OUT PIO_STATUS_BLOCK pIoStatus,
                IN ULONG ulShareAccess,
                IN ULONG ulCreateDisposition,
                IN ULONG ulCreateOptions,
                IN BOOLEAN bIsMsgType,
                IN BOOLEAN bIsMsgMode,
                IN BOOLEAN bIsNonBlocking,
                IN ULONG ulMaximumInstances,
                IN ULONG ulInBufSize,
                IN ULONG ulOutBufSize,
                IN PLARGE_INTEGER pliDefaultTimeout OPTIONAL );

NTSTATUS __stdcall ZwCreateNamedPipe(PWCHAR pwszPipeName,
                     ULONG ulMaxInBufSize,
                     ULONG ulMaxOutBufSize,
                     ULONG ulMaxClientCount,
                     PLARGE_INTEGER liTimeOut,
                     PHANDLE phPipe);

NTSTATUS __stdcall ZwConnectNamedPipe(HANDLE hPipe);

NTSTATUS __stdcall ZwDisconnectNamedPipe(HANDLE hPipe);

NTSTATUS __stdcall ZwSetNamedPipeState(HANDLE hPipe,ULONG ulMode);

NTSTATUS __stdcall ZwWaitNamedPipe(PUNICODE_STRING puniPipeName,LARGE_INTEGER liTimeOut);
// --- KernelPipe.cpp

#include "KernelPipe.h"

NTSTATUS ZwCreateNamedPipeFile (
                OUT PHANDLE phPipeHandle,
                IN ULONG ulDesiredAccess,
                IN POBJECT_ATTRIBUTES pObjAttr,
                OUT PIO_STATUS_BLOCK pIoStatus,
                IN ULONG ulShareAccess,
                IN ULONG ulCreateDisposition,
                IN ULONG ulCreateOptions,
                IN BOOLEAN bIsMsgType,
                IN BOOLEAN bIsMsgMode,
                IN BOOLEAN bIsNonBlocking,
                IN ULONG ulMaximumInstances,
                IN ULONG ulInBufSize,
                IN ULONG ulOutBufSize,
                IN PLARGE_INTEGER pliDefaultTimeout OPTIONAL )   
{    
    NAMED_PIPE_CREATE_PARAMETERS NamedPipeParms={0};
    NTSTATUS NtStatus={0};

    __try
    {    
        if ( pliDefaultTimeout )
        {
                NamedPipeParms.TimeoutSpecified = TRUE;
                NamedPipeParms.DefaultTimeout.QuadPart = pliDefaultTimeout->QuadPart;
        }
        else
        {    
                NamedPipeParms.TimeoutSpecified = FALSE;
        }  

        NamedPipeParms.NamedPipeType = bIsMsgType;
        NamedPipeParms.ReadMode = bIsMsgMode;
        NamedPipeParms.CompletionMode = bIsNonBlocking;
        NamedPipeParms.MaximumInstances = ulMaximumInstances;
        NamedPipeParms.InboundQuota = ulInBufSize;
        NamedPipeParms.OutboundQuota = ulOutBufSize;

        NtStatus = IoCreateFile(
                phPipeHandle,
                ulDesiredAccess,
                pObjAttr,
                pIoStatus,
                NULL,
                0,
                ulShareAccess,
                ulCreateDisposition,
                ulCreateOptions,
                NULL,
                0,
                CreateFileTypeNamedPipe,
                &NamedPipeParms,
                0    
                );

        return NtStatus;
    }    
    __except (EXCEPTION_EXECUTE_HANDLER)    
    {
        KdPrint (("ZwCreateNamedPipeFile: Exception occured.\n"));
        return GetExceptionCode();    
    }
}    

NTSTATUS __stdcall ZwCreateNamedPipe(PWCHAR pwszPipeName,
                     ULONG ulMaxInBufSize,
                     ULONG ulMaxOutBufSize,
                     ULONG ulMaxClientCount,
                     PLARGE_INTEGER pliTimeOut,
                     PHANDLE phPipe)
{
    NTSTATUS NtStatus=STATUS_SUCCESS;
    HANDLE hPipe=NULL;
    UNICODE_STRING uniPipeName={0};
    //ANSI_STRING ansiPipeName = {0};
    OBJECT_ATTRIBUTES ObjAttr={0};
    IO_STATUS_BLOCK IoStatus={0};

    RtlInitUnicodeString(&uniPipeName,pwszPipeName);
    InitializeObjectAttributes(&ObjAttr,&uniPipeName,OBJ_CASE_INSENSITIVE,NULL,NULL);
    //RtlInitAnsiString(&ansiPipeName, pszPipeName);
    //InitializeObjectAttributes(&ObjAttr,(ULONG)&ansiPipeName,OBJ_CASE_INSENSITIVE,NULL,NULL);
    KdPrint(("Name in ZwCreateNamedPipe: %wZ", &uniPipeName));

    NtStatus=ZwCreateNamedPipeFile(&hPipe,
            FILE_ANY_ACCESS,
            &ObjAttr,
            &IoStatus,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            FILE_CREATE,
            PIPE_ACCESS_DUPLEX,
            FILE_PIPE_MESSAGE_TYPE,
            FILE_PIPE_MESSAGE_MODE,
            FILE_PIPE_QUEUE_OPERATION,//blocking mode
            ulMaxClientCount,
            ulMaxInBufSize,
            ulMaxOutBufSize,
            pliTimeOut);

    if (NT_SUCCESS(NtStatus))
    {
        *phPipe=hPipe;
    }
    else
    {
        *phPipe=NULL;
    }

    return NtStatus;
}

NTSTATUS __stdcall ZwConnectNamedPipe(HANDLE hPipe)
{
    NTSTATUS NtStatus=STATUS_SUCCESS;
    IO_STATUS_BLOCK IoStatus={0};

    NtStatus=ZwFsControlFile(hPipe,NULL,NULL,NULL,&IoStatus,FSCTL_PIPE_LISTEN,NULL,0,NULL,0);

    return NtStatus;
}

NTSTATUS __stdcall ZwDisconnectNamedPipe(HANDLE hPipe)
{
    NTSTATUS NtStatus=STATUS_SUCCESS;
    IO_STATUS_BLOCK IoStatus={0};

    NtStatus=ZwFsControlFile(hPipe,NULL,NULL,NULL,&IoStatus,FSCTL_PIPE_DISCONNECT,NULL,0,NULL,0);

    return NtStatus;
}

NTSTATUS __stdcall ZwSetNamedPipeState(HANDLE hPipe,ULONG ulMode)
{
    NTSTATUS NtStatus=STATUS_UNSUCCESSFUL;
    ULONG    aBuf[2]={0};
    IO_STATUS_BLOCK IoStatus={0};

    do
    {
        if ((0xFFFFFFFC & ulMode)==0)
        {
            break;
        }

        aBuf[0]=((ulMode>>1) & 1);
        aBuf[1]=(ulMode & 1);

        NtStatus=ZwSetInformationFile(hPipe, &IoStatus, aBuf, sizeof(aBuf), FilePipeInformation);
    } while (FALSE);

    return NtStatus;
}

NTSTATUS __stdcall ZwWaitNamedPipe(PUNICODE_STRING puniPipeName,LARGE_INTEGER liTimeOut)
{
    NTSTATUS NtStatus=STATUS_UNSUCCESSFUL;
    IO_STATUS_BLOCK IoStatus={0};
    HANDLE hParent=NULL;
    OBJECT_ATTRIBUTES Oa={0};
    WCHAR  aTmpBuf[512]={0};
    PWAIT_PIPE_PARAM pWaitPipeParam=(PWAIT_PIPE_PARAM)aTmpBuf;
    INT   iShortNameOffset=wcslen(L"\\\\.\\pipe\\")*sizeof(WCHAR);
    UNICODE_STRING uniPipeParentName={0};

    do
    {
        if (!puniPipeName || puniPipeName->Length<iShortNameOffset)
        {
            NtStatus=STATUS_OBJECT_NAME_NOT_FOUND;
            break;
        }

        RtlInitUnicodeString(&uniPipeParentName,L"\\DosDevices\\pipe\\");
        InitializeObjectAttributes(&Oa,&uniPipeParentName,OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,NULL,NULL);
        NtStatus=ZwOpenFile(&hParent,0x100080,&Oa,&IoStatus,FILE_SHARE_WRITE | FILE_SHARE_READ,32);

        if (!NT_SUCCESS(NtStatus))
        {
            break;
        }

        pWaitPipeParam->liTimeOutvalue=liTimeOut.QuadPart;
        pWaitPipeParam->bUsTimeoutValue=TRUE;
        *((USHORT*)(&pWaitPipeParam->ulPipeNameLen))=puniPipeName->Length-iShortNameOffset;
        RtlCopyMemory((PVOID)((ULONG_PTR)pWaitPipeParam+sizeof(WAIT_PIPE_PARAM)),&puniPipeName->Buffer[iShortNameOffset/sizeof(WCHAR)],pWaitPipeParam->ulPipeNameLen);
        NtStatus=ZwFsControlFile(hParent,NULL,NULL,NULL,&IoStatus,FSCTL_PIPE_WAIT,&pWaitPipeParam,14+pWaitPipeParam->ulPipeNameLen,NULL,0);
    } while (FALSE);

    if (hParent)
    {
        ZwClose(hParent);
    }

    return NtStatus;
}

就可以了。

要点:

  1. 打开命名管道的时候要在 ObjAttr 中使用 OBJ_KERNEL_HANDLE,使其附加在系统进程([System])里。
  2. ZwCreateFileZwWriteFile 必须处于同一个线程中。(我之前尝试用 PsCreateSystemThreadlpContext 来传递 hPipe,结果 ZwWriteFile 返回 STATUS_INVALID_HANDLE(此时的 hPipe 为2位~4位16进制码)。

服务器端代码如下(我保证只会有一个线程调用 ZwWriteFile,所以我没考虑同步的问题):

#include "SnoopyTester.h"

int main()
{
    HANDLE hPipe = NULL;
    BOOL fRead = FALSE;
    DWORD i = 0;
    DWORD dwBytesRead = 0;
    char buffer[100] = {0};

    hPipe = CreateNamedPipe("\\\\.\\pipe\\HelloPipe", PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_NOWAIT, 2, 100, 100, (DWORD)-1, NULL);

    if ((hPipe) && (hPipe != INVALID_HANDLE_VALUE))
    {
        for ( i = 0; i <= MAXDWORD; i++ )
        {
            fRead = ReadFile(hPipe, buffer, 100, &dwBytesRead, NULL);

            if (fRead || dwBytesRead)
            {
                printf("Message received: %s\n", buffer);
                memset(buffer, 0, 100);
            }
        }

        printf("Application ends.\n");
        getchar();

        return 0;
    }
    else
    {
        printf("Cannot create named pipe \\\\.\\pipe\\HelloPipe\n");
        getchar();

        return 0;
    }
}

其中 SnoopyTester.h 包含了 stdio.h 和 windows.h。

运行结果如下:

Sc

分享到 评论