发信号的CreateEvent初始状态不是信令事件

时间:2018-01-13 12:21:08

标签: c++ windows ipc msdn io-redirection

我正在研究IO重定向程序,我成功为它创建了poc。该程序生成子进程并使用命名管道与它进行通信。只要管道上有数据,我就使用Event对象来获取事件。我默认情况下将事件设置为信号状态,但我没有第一次收到事件。要获得事件,我必须在输入管道上写。当我在输入管道上写一些命令时,我得到了事件,并获得旧命令的输出,而不是当前命令(请参阅输出)。

下面是工作代码。

#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <thread>
#include <string>

using namespace std;

#define input_pipe_name L"\\\\.\\pipe\\input"
#define output_pipe_name L"\\\\.\\pipe\\output"
#define process_name L"cmd.exe"

HANDLE input_pipe_handle;
HANDLE output_pipe_handle;

HANDLE input_file_handle;
HANDLE output_file_handle;


OVERLAPPED output_overlapped = { 0 };

BOOL InitHandels()
{
    input_pipe_handle = CreateNamedPipe(input_pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 120000, 0);
    SetHandleInformation(input_pipe_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
    if (input_pipe_handle == INVALID_HANDLE_VALUE)
    {
        cout << "pipe creation error: " << GetLastError() << endl;
        return FALSE;
    }

    output_pipe_handle = CreateNamedPipe(output_pipe_name, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 120000, 0);
    SetHandleInformation(output_pipe_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
    if (output_pipe_handle == INVALID_HANDLE_VALUE)
    {
        cout << "pipe creation error: " << GetLastError() << endl;
        return FALSE;
    }

    input_file_handle = CreateFile(input_pipe_name, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    if (input_file_handle == INVALID_HANDLE_VALUE)
    {
        cout << "file creation error: " << GetLastError() << endl;
        return FALSE;
    }

    output_file_handle = CreateFile(output_pipe_name, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    if (output_file_handle == INVALID_HANDLE_VALUE)
    {
        cout << "file creation error: " << GetLastError() << endl;
        return FALSE;
    }

    output_overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    ConnectNamedPipe(output_pipe_handle, &output_overlapped);
}



void CreateChildProcess()
{
    TCHAR szCmdline[] = L"cmd.exe";
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;

    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError = output_pipe_handle;
    siStartInfo.hStdOutput = output_pipe_handle;
    siStartInfo.hStdInput = input_pipe_handle;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    if (!CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo))
    {
        cout << "process creation error: " << GetLastError() << endl;
        //return FALSE;
    }
    else
    {
        HANDLE h_array[] = {output_overlapped.hEvent, piProcInfo.hProcess};

        for (;;)
        {
            DWORD result = WaitForMultipleObjects(2, h_array, FALSE, 1000);
            DWORD bwritten = 0, bread = 0;
            char buffer[4096];

            switch (result)
            {
                case WAIT_TIMEOUT:
                    //cout << "TimeOut" << endl;
                    break;

                case WAIT_OBJECT_0:
                    ReadFile(output_file_handle, buffer, sizeof(buffer), &bread, &output_overlapped);
                    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, bread, &bwritten, 0);
                    ResetEvent(output_overlapped.hEvent);
                    break;

                case WAIT_OBJECT_0 + 1: 
                    break;
                    //return FALSE;
            }
        }
    }
}

int main()
{
    DWORD bwritten;
    InitHandels();
    //CreateChildProcess();
    std::thread t1(CreateChildProcess);
    for (;;Sleep(1000))
    {
        std::string mystring;
        std::cin >> mystring;
        mystring.append("\n");
        WriteFile(input_file_handle, mystring.c_str(), mystring.length(), &bwritten, &output_overlapped);
        //WriteFile(input_file_handle, "dir\n", 4, &bwritten, &output_overlapped);
    }
    t1.join();
    return 0;
}

我得到以下输出

dir
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

D:\Programming\VS\to_post_on_stack\to_post_on_stack>hello
dir
 Volume in drive D has no label.
 Volume Serial Number is 54FB-7A94

 Directory of D:\Programming\VS\to_post_on_stack\to_post_on_stack

01/13/2018  05:36 PM    <DIR>          .
01/13/2018  05:36 PM    <DIR>          ..
01/13/2018  05:36 PM    <DIR>          Debug
01/12/2018  08:54 PM               608 stdafx.cpp
01/12/2018  08:54 PM               642 stdafx.h
01/12/2018  08:54 PM               630 targetver.h
01/13/2018  05:36 PM             7,434 to_post_on_stack.cpp
01/12/2018  08:54 PM             8,038 to_post_on_stack.vcxproj
01/12/2018  08:54 PM             1,277 to_post_on_stack.vcxproj.filters
               6 File(s)         18,629 bytes
               3 Dir(s)  39,347,019,776 bytes free

D:\Programming\VS\to_post_on_stack\to_post_on_stack>dir
hello
'hello' is not recognized as an internal or external command,
operable program or batch file.

D:\Programming\VS\to_post_on_stack\to_post_on_stack>dir

正如您在发送dir命令时在输出中看到的那样,我得到旧的输出。当我发送hello时,我得到dir之前执行的hello命令的输出。

所以任何人都可以指出我第一次没有得到信号的错误。为什么输出没有按顺序排列?

1 个答案:

答案 0 :(得分:2)

充满严重错误的代码示例:

第一个和主要:

  

如果使用FILE_FLAG_OVERLAPPED打开 hFile ,则执行以下操作   条件生效:

     

lpOverlapped 参数必须指向有效且唯一   OVERLAPPED结构,否则该函数可能会错误地报告   io操作已经完成。

  

操作重置 hEvent 成员指定的事件   当{I}开始I / O时,OVERLAPPED结构为非信号状态   操作。因此,调用者不需要这样做。

当io操作完成时 - io子系统写入 lpOverlapped 最终操作状态,传输的字节数,如果它包含事件 - 将此事件设置为信号状态。如果你在并发中使用相同的 lpOverlapped - 它们会相互覆盖结果,你永远不会知道 - 哪个操作真的完整 - 事件是一个,很常见!,如果你之前使用事件 - 系统重置事件也是如此开始io - 结果 - 一个操作可以完成并设置事件,然后另一个操作在此

之后重置它

你同时在2个线程中调用:

WriteFile(input_file_handle, ..&output_overlapped);
ReadFile(output_file_handle, .. &output_overlapped);

有了这个你已经有了UB,因为并发中使用了相同的&output_overlapped。我们需要为每个操作分配唯一重叠。如果你使用事件来检测完成 - 你需要创建几个事件 - 这根本不是好办法。在这里更好地使用iocp完成 - 我们不需要创建事件,我们不需要创建单独的线程。

ReadFile(output_file_handle, buffer, sizeof(buffer), &bread, &output_overlapped);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, bread, &bwritten, 0);
ResetEvent(output_overlapped.hEvent);

首先ReadFilehEvent结构的OVERLAPPED成员指定的事件重置为开始I / O操作时的非信号状态。因此,调用者不需要这样做。以及更多 - 当您致电ResetEvent时 - 操作已经完成 - 因此您重置已经发出信号的事件 - 结果您丢失了完成信号。如果调用ReasetEvent这需要 之前操作(具体情况为ReadFile),而不是 之后 - 这是错误。但是我们之前不需要这样做 - 因为io子系统无论如何都要这样做。

其他一个关键错误 - 在异步调用buffer, bread之后,我们无法在WriteFile中使用ReadFile - 调用尚未完成。和buffer的上下文尚未定义。

&bread在异步调用中未定义始终,且不得使用

  

lpNumberOfBytesRead 参数应设置为 NULL 。使用   GetOverlappedResult函数用于获取读取的实际字节数。   如果 hFile 参数与I / O完成端口关联,   你也可以通过调用来获取读取的字节数    GetQueuedCompletionStatus 功能。

其他一个非常常见的错误 - 我们创建 2 管道对(input_pipe_handleoutput_file_handle) - 这绝对不需要 - 我们可以使用 1 管道对。

SetHandleInformation过量的调用 - 我们只需通过SECURITY_ATTRIBUTES创建具有继承属性的句柄。

代码示例:

//#define _XP_SUPPORT_

struct IO_COUNT 
{
    HANDLE _hFile;
    HANDLE _hEvent;
    LONG _dwIoCount;

    IO_COUNT()
    {
        _dwIoCount = 1;
        _hEvent = 0;
    }

    ~IO_COUNT()
    {
        if (_hEvent)
        {
            CloseHandle(_hEvent);
        }
    }

    ULONG Create(HANDLE hFile);

    void BeginIo()
    {
        InterlockedIncrement(&_dwIoCount);
    }

    void EndIo()
    {
        if (!InterlockedDecrement(&_dwIoCount))
        {
            SetEvent(_hEvent);
        }
    }

    void Wait()
    {
        WaitForSingleObject(_hEvent, INFINITE);
    }
};

class U_IRP : OVERLAPPED 
{
    enum { connect, read, write  };

    IO_COUNT* _pIoObject;
    ULONG _code;
    LONG _dwRef;
    char _buffer[256];

    ~U_IRP()
    {
        _pIoObject->EndIo();
    }

    ULONG Read()
    {
        _code = read;

        AddRef();

        return CheckIoResult(ReadFile(_pIoObject->_hFile, _buffer, sizeof(_buffer), 0, this));
    }

    ULONG CheckIoResult(BOOL fOk)
    {
        if (fOk)
        {
#ifndef _XP_SUPPORT_
            OnIoComplete(NOERROR, InternalHigh);
#endif
            return NOERROR;
        }

        ULONG dwErrorCode = GetLastError();

        if (dwErrorCode != ERROR_IO_PENDING)
        {
            OnIoComplete(dwErrorCode, 0);
        }

        return dwErrorCode;
    }

    VOID OnIoComplete(DWORD dwErrorCode, DWORD_PTR dwNumberOfBytesTransfered)
    {
        switch (_code)
        {
        case connect:
            switch (dwErrorCode)
            {
            case ERROR_PIPE_CONNECTED:
            case ERROR_NO_DATA:
                dwErrorCode = NOERROR;
            case NOERROR:
                Read();
            }
            break;

        case read:
            if (dwErrorCode == NOERROR)
            {
                if (dwNumberOfBytesTransfered)
                {
                    if (int cchWideChar = MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, 0, 0))
                    {
                        PWSTR wz = (PWSTR)alloca(cchWideChar * sizeof(WCHAR));

                        if (MultiByteToWideChar(CP_OEMCP, 0, _buffer, (ULONG)dwNumberOfBytesTransfered, wz, cchWideChar))
                        {
                            if (int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, 0, 0, 0, 0))
                            {
                                PSTR sz = (PSTR)alloca(cbMultiByte);

                                if (WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, sz, cbMultiByte, 0, 0))
                                {
                                    DbgPrint("%.*s", cbMultiByte, sz);
                                }
                            }
                        }
                    }
                }
                Read();
            }
            break;
        case write:
            break;
        default:
            __debugbreak();
        }

        Release();

        if (dwErrorCode)
        {
            DbgPrint("[%u]: error=%u\n", _code, dwErrorCode);
        }
    }

    static VOID WINAPI _OnIoComplete(
        DWORD dwErrorCode,
        DWORD dwNumberOfBytesTransfered,
        LPOVERLAPPED lpOverlapped
        )
    {
        static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
    }

public:

    void AddRef()
    {
        InterlockedIncrement(&_dwRef);
    }

    void Release()
    {
        if (!InterlockedDecrement(&_dwRef)) delete this;
    }

    U_IRP(IO_COUNT* pIoObject) : _pIoObject(pIoObject)
    {
        _dwRef = 1;
        pIoObject->BeginIo();
        RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
    }

    ULONG Write(const void* pvBuffer, ULONG cbBuffer)
    {
        _code = write;

        AddRef();

        return CheckIoResult(WriteFile(_pIoObject->_hFile, pvBuffer, cbBuffer, 0, this));
    }

    ULONG Connect()
    {
        _code = connect;

        AddRef();

        return CheckIoResult(ConnectNamedPipe(_pIoObject->_hFile, this));
    }

    static ULONG Bind(HANDLE hFile)
    {
        return BindIoCompletionCallback(hFile, U_IRP::_OnIoComplete, 0)  
#ifndef _XP_SUPPORT_
            && SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)
#endif

        ? NOERROR : GetLastError();
    }
};

ULONG IO_COUNT::Create(HANDLE hFile)
{
    _hFile = hFile;
    if (_hEvent = CreateEvent(0, TRUE, FALSE, 0))
    {
        return U_IRP::Bind(hFile);
    }
    return GetLastError();
}

void ChildTest()
{
    static const WCHAR name[] = L"\\\\?\\pipe\\somename";

    HANDLE hFile = CreateNamedPipeW(name, 
        PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED, 
        PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, NMPWAIT_USE_DEFAULT_WAIT, 0);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        IO_COUNT obj;

        if (obj.Create(hFile) == NOERROR)
        {
            BOOL fOk = FALSE;

            static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };

            STARTUPINFOW si = { sizeof(si) };
            PROCESS_INFORMATION pi;

            si.dwFlags = STARTF_USESTDHANDLES;

            si.hStdError = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 
                FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);

            if (si.hStdError != INVALID_HANDLE_VALUE)
            {
                si.hStdInput = si.hStdOutput = si.hStdError;

                WCHAR ApplicationName[MAX_PATH];
                if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
                {
                    if (CreateProcessW(ApplicationName , 0, 0, 0, TRUE, 0, 0, 0, &si, &pi))
                    {
                        CloseHandle(pi.hThread);
                        CloseHandle(pi.hProcess);
                        fOk = TRUE;
                    }
                }

                CloseHandle(si.hStdError);
            }

            if (fOk)
            {
                U_IRP* p;

                if (p = new U_IRP(&obj))
                {
                    p->Connect();
                    p->Release();
                }

                obj.EndIo();

                //++ simulate user commands
                static PCSTR commands[] = { "dir\r\n", "ver\r\n", "exit\r\n" };
                ULONG n = RTL_NUMBER_OF(commands);
                PCSTR* psz = commands;
                do 
                {
                    if (p = new U_IRP(&obj))
                    {
                        PCSTR command = *psz++;
                        p->Write(command, (ULONG)strlen(command) * sizeof(CHAR));
                        p->Release();
                    }

                } while (--n);
                //--

                obj.Wait();
            }
        }

        CloseHandle(hFile);
    }
}