c++ - Microsoft VC++、vsnprintf 和管道 (IO) 错误

标签 c++ windows visual-studio named-pipes dll-injection

我正在使用 DLL 注入(inject)来启动文件管道的客户端,它与记录消息的服务器对话。问题是服务器只接收到一个充满问号('?')字符的缓冲区。

客户端/可注入(inject) DLL:

#include <windows.h> 
#include <stdio.h> 
#include <tchar.h>
#include <strsafe.h>

#define BUFSIZE 1024*1024

HANDLE hPipe;
BOOL   fSuccess = FALSE;
DWORD  cbToWrite, cbWritten, dwMode;
const wchar_t* lpszPipename = TEXT("\\\\.\\pipe\\listen");
char write_buffer[BUFSIZE];

void init()
{
    hPipe = CreateFile(
        lpszPipename,   // pipe name 
        GENERIC_READ |  // read and write access 
        GENERIC_WRITE,
        0,              // no sharing 
        NULL,           // default security attributes
        OPEN_EXISTING,  // opens existing pipe 
        0,              // default attributes 
        NULL);          // no template file 

// The pipe connected; change to message-read mode. 

    dwMode = PIPE_READMODE_MESSAGE;
    fSuccess = SetNamedPipeHandleState(
        hPipe,    // pipe handle 
        &dwMode,  // new pipe mode 
        NULL,     // don't set maximum bytes 
        NULL);    // don't set maximum time 
}
#pragma warning(disable:4996)
void report(const char* frmt, ...)
{
    va_list args;
    va_start(args, frmt);
    vsnprintf(write_buffer, BUFSIZE, frmt, args);
    va_end(args);

    // Send a message to the pipe server. 

    fSuccess = WriteFile(
        hPipe,                  // pipe handle 
        write_buffer,             // message 
        strlen(write_buffer),              // message length 
        &cbWritten,             // bytes written 
        NULL);                  // not overlapped 

    return;
}

服务器:

#include <windows.h> 
#include <stdio.h> 
#include <tchar.h>
#include <strsafe.h>

#define BUFSIZE 1024*1024

BOOL   fConnected = FALSE;
DWORD  dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
const wchar_t* lpszPipename = TEXT("\\\\.\\pipe\\listen");

// The main loop creates an instance of the named pipe and 
// then waits for a client to connect to it. When the client 
// connects, a thread is created to handle communications 
// with that client, and this loop is free to wait for the
// next client connect request. It is an infinite loop.

for (;;)
{
    _tprintf(TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);
    hPipe = CreateNamedPipe(
        lpszPipename,             // pipe name 
        PIPE_ACCESS_DUPLEX,       // read/write access 
        PIPE_TYPE_MESSAGE |       // message type pipe 
        PIPE_READMODE_MESSAGE |   // message-read mode 
        PIPE_WAIT,                // blocking mode 
        PIPE_UNLIMITED_INSTANCES, // max. instances  
        BUFSIZE,                  // output buffer size 
        BUFSIZE,                  // input buffer size 
        0,                        // client time-out 
        NULL);                    // default security attribute 

    if (hPipe == INVALID_HANDLE_VALUE)
    {
        _tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
        return -1;
    }

    // Wait for the client to connect; if it succeeds, 
    // the function returns a nonzero value. If the function
    // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. 

    fConnected = ConnectNamedPipe(hPipe, NULL) ?
        TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

    if (fConnected)
    {
        printf("Client connected, creating a processing thread.\n");

        // Create a thread for this client. 
        hThread = CreateThread(
            NULL,              // no security attribute 
            0,                 // default stack size 
            InstanceThread,    // thread proc
            (LPVOID)hPipe,    // thread parameter 
            0,                 // not suspended 
            &dwThreadId);      // returns thread ID 

        if (hThread == NULL)
        {
            _tprintf(TEXT("CreateThread failed, GLE=%d.\n"), GetLastError());
            return -1;
        }
        else CloseHandle(hThread);
    }
    else
        // The client could not connect, so close the pipe. 
        CloseHandle(hPipe);
}

DWORD WINAPI InstanceThread(LPVOID lpvParam)
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming
// client connections.
{
    HANDLE hHeap = GetProcessHeap();
    TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(TCHAR));
    TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(TCHAR));

    DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
    BOOL fSuccess = FALSE;
    HANDLE hPipe = NULL;

    // Print verbose messages. In production code, this should be for debugging only.
    printf("InstanceThread created, receiving and processing messages.\n");

    // The thread's parameter is a handle to a pipe object instance. 

    hPipe = (HANDLE)lpvParam;

    // Loop until done reading
    while (1)
    {
        // Read client requests from the pipe. This simplistic code only allows messages
        // up to BUFSIZE characters in length.
        fSuccess = ReadFile(
            hPipe,        // handle to pipe 
            pchRequest,    // buffer to receive data 
            BUFSIZE,    // size of buffer 
            &cbBytesRead, // number of bytes read 
            NULL);        // not overlapped I/O 

        // Process the incoming message.
        _tprintf(TEXT("Client Request String:\"%s\"\n"), pchRequest);
    }

    FlushFileBuffers(hPipe);
    DisconnectNamedPipe(hPipe);
    CloseHandle(hPipe);

    HeapFree(hHeap, 0, pchRequest);
    HeapFree(hHeap, 0, pchReply);

    printf("InstanceThread exitting.\n");
    return 1;
}

附言如果有什么方法可以使用带有可注入(inject) DLL 的调试器,请告诉我!

最佳答案

这段代码有一些问题,我会在最后解决。首先,一些有效的代码。请注意,我通过将其全部放入单个应用程序(以便我可以轻松地对其进行测试)并通过摆脱线程来稍微简化了事情,但在您的问题的上下文中,这些都不重要。

#define _CRT_SECURE_NO_WARNINGS

#include <windows.h> 
#include <stdio.h> 
#include <tchar.h>
#include <strsafe.h>

#define BUFSIZE     1024*1024

const TCHAR* lpszPipename = TEXT("\\\\.\\pipe\\listen");
char write_buffer [BUFSIZE];

HANDLE init()
{
    HANDLE hPipe = CreateFile(
        lpszPipename,   // pipe name 
        GENERIC_READ |  // read and write access 
        GENERIC_WRITE,
        0,              // no sharing 
        NULL,           // default security attributes
        OPEN_EXISTING,  // opens existing pipe 
        0,              // default attributes 
        NULL);          // no template file 

    if (hPipe == INVALID_HANDLE_VALUE)
    {
        printf ("CreateFile returned error %d\n", GetLastError ());
        return INVALID_HANDLE_VALUE;
    }

// The pipe connected; change to message-read mode. 

    DWORD dwMode = PIPE_READMODE_MESSAGE;
    BOOL fSuccess = SetNamedPipeHandleState(
        hPipe,    // pipe handle 
        &dwMode,  // new pipe mode 
        NULL,     // don't set maximum bytes 
        NULL);    // don't set maximum time 

    if (!fSuccess)
    {
        printf ("SetNamedPipeHandleState returned error %d\n", GetLastError ());
        CloseHandle (hPipe);
        return INVALID_HANDLE_VALUE;
    }

   return hPipe;    
}

void report(HANDLE hPipe, const char *frmt, ...)
{
    va_list args;
    va_start(args, frmt);
    _vsnprintf(write_buffer, BUFSIZE, frmt, args);
    va_end(args);

    // Send a message to the pipe server. 

    DWORD cbWritten;
    BOOL fSuccess = WriteFile(
        hPipe,                              // pipe handle 
        write_buffer,                       // message 
        (DWORD) strlen (write_buffer) + 1,  // message length, including EOS
        &cbWritten,                         // bytes written 
        NULL);                              // not overlapped 

    if (!fSuccess)
        printf ("WriteFile returned error %d\n", GetLastError ());
}

int _tmain (int argc, TCHAR **argv)
{
    if (argc > 1 && _tcscmp (argv [1], __T ("send")) == 0)
    {
        // send
        HANDLE hPipe = init ();
        if (hPipe != INVALID_HANDLE_VALUE)
        {
            report (hPipe, "A message to you, Rudi");
            CloseHandle (hPipe);
        }
        return 0;
    }

    // receive
    for (;;)
    {
        _tprintf(TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);
        HANDLE hPipe = CreateNamedPipe(
            lpszPipename,             // pipe name 
            PIPE_ACCESS_DUPLEX,       // read/write access 
            PIPE_TYPE_MESSAGE |       // message type pipe 
            PIPE_READMODE_MESSAGE |   // message-read mode 
            PIPE_WAIT,                // blocking mode 
            PIPE_UNLIMITED_INSTANCES, // max. instances  
            BUFSIZE,                  // output buffer size 
            BUFSIZE,                  // input buffer size 
            0,                        // client time-out 
            NULL);                    // default security attribute 

        if (hPipe == INVALID_HANDLE_VALUE)
        {
            printf ("CreateNamedPipe failed, GLE=%d.\n", GetLastError());
            return -1;
        }

        // Wait for the client to connect; if it succeeds, 
        // the function returns a nonzero value. If the function
        // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. 

        BOOL fConnected = ConnectNamedPipe(hPipe, NULL) ?
            TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

        if (!fConnected)
        {
             printf ("Error %d connecting named pipe\n", GetLastError());
             return 255;
        }

        printf ("Client connected\n");
        HANDLE hHeap = GetProcessHeap();
        char* pchRequest = (char*) HeapAlloc(hHeap, 0, BUFSIZE);

        // Loop until done reading
        while (1)
        {
            // Read client requests from the pipe. This simplistic code only allows messages
            // up to BUFSIZE characters in length.
            DWORD cbBytesRead = 0;
            BOOL fSuccess = ReadFile(
                hPipe,        // handle to pipe 
                pchRequest,    // buffer to receive data 
                BUFSIZE,    // size of buffer 
                &cbBytesRead, // number of bytes read 
                NULL);        // not overlapped I/O 

            if (!fSuccess)
                break;

            // Process the incoming message.
            printf("Client Request String:\"%s\"\n", pchRequest);
        }

        FlushFileBuffers(hPipe);
        DisconnectNamedPipe(hPipe);
        CloseHandle(hPipe);
        HeapFree(hHeap, 0, pchRequest);
    }

    return 0;
}

要在“发送”模式下运行它,请在命令行上指定send。否则它作为服务器运行。我的服务器永远运行。用 Ctrl+C 杀死它。

那么你的代码出了什么问题?好吧,大多数情况下,它有点混合了 ANSI 和 UNICODE 字符串。你需要对这种事情更加小心,你还需要适本地计算缓冲区大小。这一切都在上面的代码中修复了,这就是我发布它的原因。此外,就良好的编程习惯而言:

  • 您应该更彻底地检查错误。
  • 正如所写,服务器假定发送给它的字符串是以 NUL 结尾的,但客户端不会那样发送它(所以我修复了客户端)。
  • 当发送方关闭其管道末端时,服务器需要跳出其接收循环。
  • 在本地声明局部变量! (并在适当的时候将它们作为参数传递。)不要使用不必要的全局变量。
  • 使用#define _CRT_SECURE_NO_WARNINGS 比显式禁用您收到的警告要好。

我的代码修复了所有这些问题。 HTH.

关于c++ - Microsoft VC++、vsnprintf 和管道 (IO) 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51220926/

相关文章:

java - OpenCV java.lang.UnsatisfiedLinkError

c# - Windows 窗体文本框 : Selection and Caret position

c++ - 调试 :FASTLINK - What is this error?

c++ - JSONCPP 正在向字符串添加额外的双引号

c++ - Linux服务方法

c++ - 字符串文字匹配 bool 重载而不是 std::string

c - 为什么 ShellExecuteEx 不设置 SHELLEXECUTEINFO.hProcess 的值?

windows - iis7 默认 mime 类型行为

c# - 如何通过 VSPackage 取消 ToolWindowPane 或 Visual Studio IDE 关闭操作?

javascript - 更好的体验/自动化清洁和构建