gpt4 book ai didi

c++ - CreateEvent 初始状态信号不是信号事件

转载 作者:可可西里 更新时间:2023-11-01 10:13:53 25 4
gpt4 key购买 nike

我正在研究 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 时,我得到了我在 hello 之前执行的 dir 命令的输出。

谁能指出我第一次没有收到信号的错误。为什么输出没有按顺序进行?

最佳答案

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

第一个也是主要的:

If hFile was opened with FILE_FLAG_OVERLAPPED, the following conditions are in effect:

The lpOverlapped parameter must point to a valid and unique OVERLAPPED structure, otherwise the function can incorrectly report that the io operation is complete.

io operation resets the event specified by the hEvent member of the OVERLAPPED structure to a nonsignaled state when it begins the I/O operation. Therefore, the caller does not need to do that.

当 io 操作完成时 - io 子系统写入 lpOverlapped 操作的最终状态、传输的字节数,如果它包含事件 - 将此事件设置为信号状态。如果您在并发中使用相同的 lpOverlapped - 它们会相互覆盖结果,而且您​​永远不知道 - 哪个操作真正完成 - 事件是一个,常见!,如果您使用事件 - 之前的系统重置事件begin 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);

首先 ReadFile 在开始 I/O 时将 OVERLAPPED 结构的 hEvent 成员指定的事件重置为无信号状态手术。因此,调用者不需要这样做。以及更多 - 当您调用 ResetEvent - 操作可能已经完成 - 因此您重置已经发出信号的事件 - 结果您丢失了完成信号。如果调用 ReasetEvent 这需要执行 before io 操作(在具体情况下为 ReadFile)而不是 after - 这是错误.然而我们不需要这样做和之前 - 因为 io 子系统无论如何都会这样做。

还有一个严重错误 - 我们不能在 WriteFile 中使用 buffer, bread 就在 asynchronous 调用 ReadFile 之后> - 调用尚未完成。和 buffer 的上下文尚未定义。

&bread 在异步调用中总是未定义,根本不能使用

The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to get the actual number of bytes read. If the hFile parameter is associated with an I/O completion port, you can also get the number of bytes read by calling the GetQueuedCompletionStatus function.

还有一个非常常见的错误 - 我们创建了 2 管道对(input_pipe_handle, output_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);
}
}

关于c++ - CreateEvent 初始状态信号不是信号事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48239819/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com