Microsoft VC++、vsnprintf 和管道 (IO) 错误
Microsoft VC++, vsnprintf, and Pipes (IO) Bug
我正在使用 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 的调试器,请告诉我!
这段代码有一些问题,我会在最后解决。首先,一些有效的代码。请注意,我通过将其全部放入单个应用程序(以便我可以轻松地对其进行测试)并通过摆脱线程来稍微简化了事情,但是 none 在您的问题中很重要。
#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"模式下,在命令行指定send
。否则它 运行s 作为服务器。我的服务器永远 运行s。 Ctrl+C杀掉它。
那么你的代码有什么问题?好吧,大多数情况下,它有点混合了 ANSI 和 UNICODE 字符串。你需要对这种事情更加小心,你还需要适当地计算缓冲区大小。这一切都在上面的代码中修复了,这就是我发布它的原因。另外,就良好的编程习惯而言:
- 你应该更彻底地检查错误。
- 正如所写,服务器假定发送给它的字符串是以 NUL 结尾的,但客户端不会那样发送(所以我修复了客户端)。
- 当发送方关闭其管道末端时,服务器需要跳出其接收循环。
- 在本地声明局部变量! (并在适当的时候将它们作为参数传递。)不要使用不必要的全局变量。
- 使用#define _CRT_SECURE_NO_WARNINGS 比明确禁用如果不这样做会收到的警告要好。
我的代码修复了所有这些问题。 HTH.
我正在使用 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 的调试器,请告诉我!
这段代码有一些问题,我会在最后解决。首先,一些有效的代码。请注意,我通过将其全部放入单个应用程序(以便我可以轻松地对其进行测试)并通过摆脱线程来稍微简化了事情,但是 none 在您的问题中很重要。
#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"模式下,在命令行指定send
。否则它 运行s 作为服务器。我的服务器永远 运行s。 Ctrl+C杀掉它。
那么你的代码有什么问题?好吧,大多数情况下,它有点混合了 ANSI 和 UNICODE 字符串。你需要对这种事情更加小心,你还需要适当地计算缓冲区大小。这一切都在上面的代码中修复了,这就是我发布它的原因。另外,就良好的编程习惯而言:
- 你应该更彻底地检查错误。
- 正如所写,服务器假定发送给它的字符串是以 NUL 结尾的,但客户端不会那样发送(所以我修复了客户端)。
- 当发送方关闭其管道末端时,服务器需要跳出其接收循环。
- 在本地声明局部变量! (并在适当的时候将它们作为参数传递。)不要使用不必要的全局变量。
- 使用#define _CRT_SECURE_NO_WARNINGS 比明确禁用如果不这样做会收到的警告要好。
我的代码修复了所有这些问题。 HTH.