加速Windows C端口扫描器

问题描述:

因此,我开发了Windows上的C端口扫描器,但我注意到在某些IP上它运行得非常慢。这里是我的代码吧:加速Windows C端口扫描器

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; 
} 

请记住我已经使用线程,每个线程调用此功能以不同的端口(0 - 1024)在同一个IP。

那么我该如何加速呢?我一直在看人们在谈论非阻塞,是否会加快速度,如果可以,我该如何实现这一点。谢谢!

编辑:正在614秒(10分钟),从0到扫描 - 1024对上述“慢” IP的

编辑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的结果相比,我似乎获得了更多的开放端口。

+3

你知道你的瓶颈是什么?你有分析过吗? –

+0

不,我不知道什么是瓶颈,我怎么能找出瓶颈呢? –

+0

使用异步io你不能加速从一些特定的IP回复。这完全不取决于你的代码。但使用异步io - 你可以使你的代码更有效,更少的资源使用。 1024线程??这是噩梦。与异步io你只使用线程池与固定的线程数(通常每个核心一个线程) – RbMm

我看到了很多的问题,你的线程代码:

  • 如果故障发生它正在泄漏内存。

  • 当您在openports变量上调用memset()时,您误用了strlen()。只需删除memset(),并在分配openports时使用calloc()LocalAlloc(LMEM_ZEROINIT)。或者,只需使用调用堆栈,因为变量很小:char openPorts[256] = {0};或更好,根本不要使用本地变量openports,只需在有可用结果时直接写入pDataArray->openPorts即可。

  • 根本不应该使用exit()。改为使用return

  • 它在技术上并不违法调用WSAStartup()/WSACleanup()多次,因为是的WinSock引用计数,但是最好是在程序启动/退出只有一次打电话给他们,而不是每个线程。但是,由于您要拨打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; 
} 
+0

非常感谢你!你的榜样帮了大忙。现在,如果我想缩短超时时间,请将timeout.tv_sec = 5更改为3或2以下的值。 –

+0

@JustinBraham:是的。 [阅读文档](https://msdn.microsoft.com/en-us/library/windows/desktop/ms740560.aspx)。 –