Microsoft VC ++,vsnprintf和管道(IO)错误

时间:2018-07-07 07:21:11

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

我正在使用DLL注入来启动文件管道的客户端,该管道与记录消息的服务器通信。问题在于服务器仅接收一个填充有问号('?')字符的缓冲区。

客户端/可注入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;
}

P.S。如果有可能将调试器与可注入DLL一起使用,请告诉我!

1 个答案:

答案 0 :(得分:0)

此代码存在一些问题,我将在最后讨论。首先,一些有效的代码。请注意,我通过将它们全部集成到一个应用程序中(以便我可以轻松对其进行测试)并通过摆脱线程来对事情进行了一些简化,但这与您的问题无关。

#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。