首页>>科技 >>内容

c++匿名管道,使用匿名管道技术获取CMD命令的执行结果

发布时间:2023-09-18 09:52:08编辑:温柔的背包来源:

很多朋友对c++匿名管道,使用匿名管道技术获取CMD命令的执行结果不是很了解,每日小编刚好整理了这方面的知识,今天就来带大家一探究竟。

c++匿名管道,使用匿名管道技术获取CMD命令的执行结果

前言

远程CMD是指恶意程序接收到控制端发送的CMD命令后,在本地执行CMD命令,并将执行结果传回控制端。本文将演示如何使用匿名管道技术来获取CMD命令的执行结果。

相关API

CreatePipe:用于创建管道

PeekNamedPipe:用于判断管道中是否有数据

ReadFile、WriteFile:用于向管道读取或写入数据

CreateProcess:用于创建CMD子进程,可以指定启动信息(STARTUPINFO)

实现原理

管道是一种进程间通信技术。 Windows上的进程间通信技术还包括文件映射、共享内存、邮槽、剪贴板、事件等。

管道分为命名管道和匿名管道:

* 匿名管道只能在父子进程之间通信,数据传输是单向的,不能通过网络进行通信;

* 命名管道可以在任何进程之间进行通信,数据传输是双向的,但只有一端可以同时读写。

由于远程CMD中只需要执行CMD指令的结果,因此可以使用匿名管道。使用流程如下:

1. 使用CreatePipe创建匿名管道,并获取管道数据读句柄和管道数据写句柄。

2、初始化进程结构,并将管道写句柄分配给新进程控制台窗口的缓存句柄;

3、使用CreateProcess创建一个新进程来执行CMD命令,并等待命令执行结束;

4、循环中使用PeekNamedPipe函数判断管道中是否有数据,并通过管道读句柄从缓冲区中获取执行结果。

5. 关闭句柄并释放资源。

编码实现

关键代码

//执行cmd命令,获取执行结果数据bool PipeCmd(TCHAR* cmd_str, std:string outbuf){BOOL bRet=FALSE;HANDLE hReadPipe=NULL;HANDLE hWritePipe=NULL;SECURITY_ATTRIBUTES securityAttributes={0};STARTUPINFO si={0};PROCESS_INFORMATION pi={0};//设置管道的安全属性securityAttributes.bInheritHandle=TRUE;securityAttributes.nLength=sizeof(securityAttributes);securityAttributes.lpSecurityDescriptor=NULL;//创建匿名管道bRet=:CreatePipe(hReadPipe, hWritePipe, securityAttributes, 0);if(FALSE==bRet){printf('CreatePipe');returnfalse;}//设置新的流程参数si.cb=sizeof(si);si.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES ;si.wShowWindow=SW_HIDE;si.hStdError=hWritePipe;si.hStdOutput=hWritePipe;//创建新进程执行命令,并将执行结果写入匿名管道bRet=:CreateProcess(NULL, cmd_str, NULL , NULL, TRUE , 0, NULL, NULL, si, pi);if(FALSE==bRet){printf('CreateProcess');returnfalse;}//等待命令执行结束:WaitForSingleObject(pi.hThread , INFINITE);WaitForSingleObject(pi.hProcess, INFINITE);//不断从匿名管道读取结果到输出缓冲区while(true){char buf[2048]{};DWORD readbytes=0;DWORD availablebytes=0 ;if(!PeekNamedPipe(hReadPipe,NULL,0,NULL,availbytes,NULL))break;if(!availbytes)break;if(!ReadFile(hReadPipe,buf,min(sizeof(buf)- 1,availbytes),readbytes , NULL) || ! readbytes) break;buf[readbytes]=0;outbuf +=buf;}//关闭句柄并释放内存:CloseHandle(pi.hThread);CloseHandle(pi.hProcess);CloseHandle(hWritePipe);CloseHandle(hReadPipe);returntrue;}

上面的代码中,首先调用CreatePipe函数创建一个匿名管道,并将返回的读写句柄保存到hReadPipe和hWritePipe变量中。然后,通过设置STARTUPINFO结构体中的参数,将新进程的标准错误输出和标准输出重定向到管道中,从而可以将命令执行结果写入管道中。接下来,调用CreateProcess函数创建一个新进程,并将命令行命令作为参数传入。 CreateProcess函数执行成功后,新进程开始执行命令,并将命令执行结果写入管道。之后,循环调用PeekNamedPipe和ReadFile函数,不断从管道中读取数据,并将读取的数据存储到输出缓冲区中。区。最后,代码关闭句柄并释放内存。

测试实施

测试代码

intmain(intargc, char** argv){TCHAR cmd_str[]=L'ping 127.0.0.1';//执行cmd命令,获取执行结果数据std:stringoutbuf;if(false==PipeCmd(cmd_str, outbuf) ) ){printf('管道cmd错误。');}else{printf('CMD执行结果为: %s', outbuf.c_str());}system('pause');return0;}

远程传输

上一节仅获取了cmd命令的执行结果。要实现远程传输,需要增加网络传输部分。

测试

服务器

#DefinePort 9982inttest_server(){socket listerfd=socket(pf_inet,sock_stream,ipproto_tcp); sockaddr_in bindaddr; bindaddr.sin_family=af_inet=af_inet; (SOCKADDR*)bindaddr, sizeof(SOCKADDR));listen(listenfd, 1);sockaddr_in clientaddr;intclientaddrlen=sizeof(SOCKADDR);SOCKET clientfd=接受(listenfd, (SOCKADDR*)clientaddr, clientaddrlen);charbuf[1024]{ };intrecvbytes=recv(clientfd, buf, 1024, 0);if(recvbytes=0) return-1;std:stringoutbuf;PipeCmd((LPTSTR)buf, outbuf);std:cout outbuf std:endl; closesocket(clientfd);closesocket(listenfd);return0;}

客户

inttest_client(){SOCKET connfd=套接字(PF_INET, SOCK_STREAM, IPPROTO_TCP);structsockaddr_inServerAddr;ServerAddr.sin_family=AF_INET;ServerAddr.sin_addr.S_un.S_addr=inet_addr('127.0.0.1');ServerAddr.sin_port=htons(PORT) ;连接(connfd, (SOCKADDR*)ServerAddr, sizeof(ServerAddr));TCHAR cmdbuf[100]=_T('ipconfig');发送(connfd, (char*)cmdbuf, (lstrlen(cmdbuf) + 1) * sizeof( TCHAR), 0);closesocket(connfd);return0;

影响

启动服务器后,客户端向服务器发送指令。服务器收到指令后开始处理,最终测试可以正确处理。

概括

上面的远程CMD是通过匿名管道实现的,直接执行cmd命令,这样就不需要创建管道来传递命令了。远程CMD 实现方法有多种变体,例如双管道远程CMD 和零管道远程CMD。他们对应的思路是控制CMD的输出和输入重定向位置,如:

远程CMD的双管道实现就是将CMD进程的输入输出句柄替换为读写管道的句柄。

远程CMD的零管道实现就是用socket句柄替代CMD进程的输入输出句柄。

评论柳青

以上知识分享希望能够帮助到大家!