加速 Windows C 端口扫描器
Speedup Windows C Port Scanner
所以我在 windows 上为 C 开发了一个端口扫描器,但我注意到在某些 IP 上它运行得非常慢。这是我的代码:
DWORD WINAPI connectPortW(LPVOID lpParam)
{
HANDLE hStdout;
PMYDATA pDataArray;
WSADATA firstsock;
SOCKET s;
struct sockaddr_in sa;
int err;
char * openPorts = (char *)malloc(sizeof(char)*256);
memset(&openPorts[0], 0, strlen(openPorts));
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if(hStdout == INVALID_HANDLE_VALUE )
{
return 1;
}
pDataArray = (PMYDATA)lpParam;
strncpy((char *)&sa,"",sizeof sa);
sa.sin_family = AF_INET;
if (WSAStartup(MAKEWORD(2,0),&firstsock) != 0)
{
fprintf(stderr,"WSAStartup() failed");
exit(1);
}
sa.sin_addr.s_addr = inet_addr(pDataArray->ip);
s = socket(AF_INET, SOCK_STREAM, 0); //make net a valid socket handle
if(s < 0)
{
perror("\nSocket creation failed"); // perror function prints an error message to stderr
exit(1);
}
sa.sin_port = htons(pDataArray->port);
err = connect(s, (struct sockaddr *)&sa, sizeof sa);
//connection not accepted
if(err == SOCKET_ERROR)
{
printf("%s %-5d Winsock Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
strcpy("NULL", openPorts);
fflush(stdout);
}
//connection accepted
else
{
printf("%s %-5d accepted \n", pDataArray->ip, pDataArray->port);
sprintf(openPorts, "%i,", pDataArray->port);
if(shutdown(s, SD_BOTH ) == SOCKET_ERROR )
{
perror("\nshutdown");
exit(1);
}
}
closesocket(s);
fflush(stdout);
strcpy(pDataArray->openPorts, openPorts);
free(openPorts);
return 0;
}
请记住,我已经使用了线程,并且每个线程都为同一 IP 上的不同端口 (0 - 1024) 调用此函数。
那么我怎样才能加快速度呢?我一直看到人们谈论非阻塞,这会加快它的速度吗?如果是的话,我该如何实现它。谢谢!
编辑:在上述 'slow' ip
之一上从 0 - 1024 扫描需要 614 秒(10 分钟)
编辑 2:我开始尝试使用非阻塞...我这样做对吗?
ioctlsocket(s, FIONBIO, &on);
connect(s, (struct sockaddr *)&sa, sizeof sa);
FD_ZERO(&fds);
FD_SET(s, &fds);
err = select(s, &fds, &fds, &fds, &tv);
if (err != SOCKET_ERROR && err != 0)
{
sprintf(openPorts + strlen(openPorts),"%i,", pDataArray->port);
}
closesocket(s);
编辑 3:似乎这种新方法给我的结果不准确,但速度要快得多。与同一 IP 上 运行 nmap 的结果相比,我似乎获得了更多的开放端口。
我看到你的线程代码有很多问题:
如果发生故障就是内存泄漏
您在 openports
变量上调用 memset()
时误用了 strlen()
。只需完全删除 memset()
并在分配 openports
时使用 calloc()
或 LocalAlloc(LMEM_ZEROINIT)
代替。或者,只使用调用堆栈,因为变量很小: char openPorts[256] = {0};
或者更好,甚至根本不使用本地 openports
变量,只需直接写入 pDataArray->openPorts
当你有可用的结果。
你根本不应该使用 exit()
。请改用 return
。
多次调用 WSAStartup()
/WSACleanup()
在技术上并不违法,因为 WinSock 是引用计数的,但是最好只在程序中调用一次 startup/exit,不是每个线程。但是,由于您正在调用 WSAStartup()
,因此您 必须 调用 WSACleanup()
以保持 WinSock 引用计数平衡。
你想用 strcpy("NULL", openPorts);
做什么?您正在写入只读存储器。我想你的意思是 strcpy(openPorts, "NULL");
而不是。
如果多个线程共享一个缓冲区,写入 pDataArray->openPorts
不是线程安全的(您在 sprintf()
字符串中使用 ,
意味着可能是这样的)。在跨多个线程写入缓冲区时,您需要同步对缓冲区的访问。您可以为此目的使用临界区或互斥量。
也就是说,您正在使用阻塞套接字,因此 connect()
将阻塞线程直到 WinSock 内部超时,这在慢速网络上可能需要一段时间。为了加快速度,使用 ioctrlsocket(FIONBIO)
将套接字切换到非阻塞模式,然后使用 select()
为 connect()
实现自己的超时,例如:
DWORD WINAPI connectPortW(LPVOID lpParam)
{
PMYDATA pDataArray = (PMYDATA) lpParam;
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdout == INVALID_HANDLE_VALUE)
return 1;
WSADATA wsa;
int err = WSAStartup(MAKEWORD(2,0), &wsa);
if (err != 0)
{
fprintf(stderr, "%s %d WSAStartup() failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
return 1;
}
SOCKET s = socket(AF_INET, SOCK_STREAM, 0); //make net a valid socket handle
if (s == INVALID_SOCKET)
{
fprintf(stderr, "%s %d Socket creation failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
WSACleanup();
return 1;
}
u_long enabled = 1;
if (ioctlsocket(s, FIONBIO, &enabled) == SOCKET_ERROR)
{
fprintf(stderr, "%s %d Socket non-blocking mode failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
closesocket(s);
WSACleanup();
return 1;
}
struct sockaddr_in sa = {0};
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(pDataArray->ip);
sa.sin_port = htons(pDataArray->port);
if (connect(s, (struct sockaddr *)&sa, sizeof sa) == SOCKET_ERROR)
{
err = WSAGetLastError();
if (err != WSAEWOULDBLOCK)
{
fprintf(stderr, "%s %d Socket connect failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
closesocket(s);
WSACleanup();
return 1;
}
fd_set wfd, efd;
FD_ZERO(s, &wfd);
FD_SET(s, &wfd);
FD_ZERO(s, &efd);
FD_SET(s, &efd)'
timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
err = select(0, NULL, &wfd, &efd, &timeout);
if (err == SOCKET_ERROR)
{
fprintf(stderr, "%s %d Socket select failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
closesocket(s);
WSACleanup();
return 1;
}
if (err == 0)
{
// connect timeout
closesocket(s);
WSACleanup();
return 0;
}
if (FD_ISSET(s, &efd))
{
err = 0;
getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof err);
closesocket(s);
WSACleanup();
switch (err)
{
case WSAETIMEDOUT: // connect timeout
case WSAECONNREFUSED: // port closed
return 0;
}
fprintf(stderr, "%s %d Socket connect failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
return 1;
}
}
// connected!
printf("%s %d accepted\n", pDataArray->ip, pDataArray->port);
// note, this is not thread-safe! Need to sync access to openPorts...
sprintf(pDataArray->openPorts + strlen(pDataArray->openPorts), "%d,", pDataArray->port);
closesocket(s);
WSACleanup();
return 0;
}
所以我在 windows 上为 C 开发了一个端口扫描器,但我注意到在某些 IP 上它运行得非常慢。这是我的代码:
DWORD WINAPI connectPortW(LPVOID lpParam)
{
HANDLE hStdout;
PMYDATA pDataArray;
WSADATA firstsock;
SOCKET s;
struct sockaddr_in sa;
int err;
char * openPorts = (char *)malloc(sizeof(char)*256);
memset(&openPorts[0], 0, strlen(openPorts));
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if(hStdout == INVALID_HANDLE_VALUE )
{
return 1;
}
pDataArray = (PMYDATA)lpParam;
strncpy((char *)&sa,"",sizeof sa);
sa.sin_family = AF_INET;
if (WSAStartup(MAKEWORD(2,0),&firstsock) != 0)
{
fprintf(stderr,"WSAStartup() failed");
exit(1);
}
sa.sin_addr.s_addr = inet_addr(pDataArray->ip);
s = socket(AF_INET, SOCK_STREAM, 0); //make net a valid socket handle
if(s < 0)
{
perror("\nSocket creation failed"); // perror function prints an error message to stderr
exit(1);
}
sa.sin_port = htons(pDataArray->port);
err = connect(s, (struct sockaddr *)&sa, sizeof sa);
//connection not accepted
if(err == SOCKET_ERROR)
{
printf("%s %-5d Winsock Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
strcpy("NULL", openPorts);
fflush(stdout);
}
//connection accepted
else
{
printf("%s %-5d accepted \n", pDataArray->ip, pDataArray->port);
sprintf(openPorts, "%i,", pDataArray->port);
if(shutdown(s, SD_BOTH ) == SOCKET_ERROR )
{
perror("\nshutdown");
exit(1);
}
}
closesocket(s);
fflush(stdout);
strcpy(pDataArray->openPorts, openPorts);
free(openPorts);
return 0;
}
请记住,我已经使用了线程,并且每个线程都为同一 IP 上的不同端口 (0 - 1024) 调用此函数。
那么我怎样才能加快速度呢?我一直看到人们谈论非阻塞,这会加快它的速度吗?如果是的话,我该如何实现它。谢谢!
编辑:在上述 'slow' ip
之一上从 0 - 1024 扫描需要 614 秒(10 分钟)编辑 2:我开始尝试使用非阻塞...我这样做对吗?
ioctlsocket(s, FIONBIO, &on);
connect(s, (struct sockaddr *)&sa, sizeof sa);
FD_ZERO(&fds);
FD_SET(s, &fds);
err = select(s, &fds, &fds, &fds, &tv);
if (err != SOCKET_ERROR && err != 0)
{
sprintf(openPorts + strlen(openPorts),"%i,", pDataArray->port);
}
closesocket(s);
编辑 3:似乎这种新方法给我的结果不准确,但速度要快得多。与同一 IP 上 运行 nmap 的结果相比,我似乎获得了更多的开放端口。
我看到你的线程代码有很多问题:
如果发生故障就是内存泄漏
您在
openports
变量上调用memset()
时误用了strlen()
。只需完全删除memset()
并在分配openports
时使用calloc()
或LocalAlloc(LMEM_ZEROINIT)
代替。或者,只使用调用堆栈,因为变量很小:char openPorts[256] = {0};
或者更好,甚至根本不使用本地openports
变量,只需直接写入pDataArray->openPorts
当你有可用的结果。你根本不应该使用
exit()
。请改用return
。多次调用
WSAStartup()
/WSACleanup()
在技术上并不违法,因为 WinSock 是引用计数的,但是最好只在程序中调用一次 startup/exit,不是每个线程。但是,由于您正在调用WSAStartup()
,因此您 必须 调用WSACleanup()
以保持 WinSock 引用计数平衡。你想用
strcpy("NULL", openPorts);
做什么?您正在写入只读存储器。我想你的意思是strcpy(openPorts, "NULL");
而不是。
如果多个线程共享一个缓冲区,写入
pDataArray->openPorts
不是线程安全的(您在sprintf()
字符串中使用,
意味着可能是这样的)。在跨多个线程写入缓冲区时,您需要同步对缓冲区的访问。您可以为此目的使用临界区或互斥量。
也就是说,您正在使用阻塞套接字,因此 connect()
将阻塞线程直到 WinSock 内部超时,这在慢速网络上可能需要一段时间。为了加快速度,使用 ioctrlsocket(FIONBIO)
将套接字切换到非阻塞模式,然后使用 select()
为 connect()
实现自己的超时,例如:
DWORD WINAPI connectPortW(LPVOID lpParam)
{
PMYDATA pDataArray = (PMYDATA) lpParam;
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdout == INVALID_HANDLE_VALUE)
return 1;
WSADATA wsa;
int err = WSAStartup(MAKEWORD(2,0), &wsa);
if (err != 0)
{
fprintf(stderr, "%s %d WSAStartup() failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
return 1;
}
SOCKET s = socket(AF_INET, SOCK_STREAM, 0); //make net a valid socket handle
if (s == INVALID_SOCKET)
{
fprintf(stderr, "%s %d Socket creation failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
WSACleanup();
return 1;
}
u_long enabled = 1;
if (ioctlsocket(s, FIONBIO, &enabled) == SOCKET_ERROR)
{
fprintf(stderr, "%s %d Socket non-blocking mode failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
closesocket(s);
WSACleanup();
return 1;
}
struct sockaddr_in sa = {0};
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(pDataArray->ip);
sa.sin_port = htons(pDataArray->port);
if (connect(s, (struct sockaddr *)&sa, sizeof sa) == SOCKET_ERROR)
{
err = WSAGetLastError();
if (err != WSAEWOULDBLOCK)
{
fprintf(stderr, "%s %d Socket connect failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
closesocket(s);
WSACleanup();
return 1;
}
fd_set wfd, efd;
FD_ZERO(s, &wfd);
FD_SET(s, &wfd);
FD_ZERO(s, &efd);
FD_SET(s, &efd)'
timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
err = select(0, NULL, &wfd, &efd, &timeout);
if (err == SOCKET_ERROR)
{
fprintf(stderr, "%s %d Socket select failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
closesocket(s);
WSACleanup();
return 1;
}
if (err == 0)
{
// connect timeout
closesocket(s);
WSACleanup();
return 0;
}
if (FD_ISSET(s, &efd))
{
err = 0;
getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof err);
closesocket(s);
WSACleanup();
switch (err)
{
case WSAETIMEDOUT: // connect timeout
case WSAECONNREFUSED: // port closed
return 0;
}
fprintf(stderr, "%s %d Socket connect failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
return 1;
}
}
// connected!
printf("%s %d accepted\n", pDataArray->ip, pDataArray->port);
// note, this is not thread-safe! Need to sync access to openPorts...
sprintf(pDataArray->openPorts + strlen(pDataArray->openPorts), "%d,", pDataArray->port);
closesocket(s);
WSACleanup();
return 0;
}