驱动笔记之一:内核命名管道问题的解决

(老鸟们请不要笑话我……)

最近正在做一个驱动程序,需要用到内核与用户层通信。我是个驱动开发的新手,所以想回归最简单最传统的 Windows 编程模式,第一个反应就是:命名管道。所以不要问我为什么不用 minifilter 的通信机制,原因很简单——我不会。

内核管道的实现在这里:http://blog.csdn.net/skyseacsn/article/details/6962858

#pragma pack(push,1)
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 )       
{        
    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,     
                     LARGE_INTEGER liTimeOut,     
                     PHANDLE phPipe)     
{     
    NTSTATUS NtStatus=STATUS_SUCCESS;     
    HANDLE hPipe=NULL;     
    UNICODE_STRING uniPipeName={0};     
    OBJECT_ATTRIBUTES ObjAttr={0};     
    IO_STATUS_BLOCK IoStatus={0};     

    RtlInitUnicodeString(&uniPipeName,pwszPipeName);     
    InitializeObjectAttributes(&ObjAttr,&uniPipeName,OBJ_CASE_INSENSITIVE,NULL,NULL);     

    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,     
            &liTimeOut);     

    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;     
}

(吐槽一下百度空间的“插入代码”功能,总是出现神排版!)

引入了管道之后,我尝试创建管道,但是一直返回 0xc0000033(STATUS_OBJECT_NAME_INVALID)。我纳闷了,为什么呢?我查看了 KdPrint("zW"),使用的名称“\.\pipe\hellopipe”确实被正确地转成了 Unicode 字符串啊!那么为什么系统总是说我的名称错了?

我查遍了所有的资料,里面写的都是,本地命名管道(服务端只能采用这种格式)采用“\.\pipe\pipename”的格式,远程管道可以采用“\servername\pipe\pipename”的格式。那么为什么我的名称是不对的?
后来我偶然找到了一个页面(http://us.generation-nt.com/answer/create-named-pipe-kernel-driver-help-27790252.html),里面讲述了原因。翻译如下:

Thus whereas in user-mode format of pipe is , in kernel it is “\??\pipe\pipename”. In addition, in kernel default security context is LocalSystem which is not allowed to access network.

所以尽管用户态的管道名称格式为“\server\pipe\pipename”,在内核中其为“\??\pipe\pipename”。而且,在内核中,默认安全上下文是 LocalSystem,该安全上下文不允许访问网络。

好了,知道原因,修改代码,调试,成功~

分享到 评论