程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 一個簡單的端口掃描程序題

一個簡單的端口掃描程序題

編輯:關於VC++

一、TCP掃描技術

常用的端口掃描技術有很多種,如 TCP connect() 掃描 、TCP SYN 掃描、TCP FIN 掃描 等,網絡上也有很多文章專門介紹,比如 :http://www.antai-genecon.com/suml/zhishiyy/jingong/duankougj.htm 上就介紹了很多我的程序 所使用的最基本的掃描技術:TCP 掃描。

操作系統提供的 connect() 系統調用,用來與每一個感興趣的目標計算機的端口進行連接。如果端口處於偵聽狀態,那麼connect()就能成功。否則,這個端口是不能用的,即沒有提供服務。這個技術的一個最大的優點是,你不需要任何權限。系統中的任何用戶都有權利使用這個調用。另一個好處就是速度。如果對每個目標端口以線性的方式,使用單獨的connect()調用,那麼將會花費相當長的時間,你可以通過同時打開多個套接字,從而加速掃描。使用非阻塞 I/O 允許你設置一個低的時間用盡周期,同時觀察多個套接字。但這種方法的缺點是很容易被發覺,並且被過濾掉。目標計算機的logs文件會顯示一連串的連接和連接是出錯的服務消息,並且能很快的使它關閉。

作者提示:未經許可掃描他人的計算機端口屬非法行為。本程序只是展示一種端口掃描技術,請不得將其用於非法目的,否則後果自負。

二、程序簡介

為了提高掃描速度,本程序采用了多線程技術和非阻塞I/O的技術。程序的主界面是一個對話框,下面是程序框架示意圖:

1、全局變量:

以下是所有全局變量的定義:HWND g_hWnd = NULL; //處理消息的窗口句柄
unsigned long g_ulAddr = INADDR_NONE; //掃描的主機地址
DWORD g_dwTimeOut = 1000; //連接超時時間,以ms計
bool g_bTerminate = false; //是否用戶發出結束掃描的標志
short g_nMaxThread = 200; //最大允許的掃描線程數,經試驗不宜大於200
short g_nThreadCount = 0; //當前正在掃描的進程數
2、StartScan 線程:

這個線程完成的任務是啟動具體的掃描DoScanPort線程,對某一個端口進行掃描。該進程啟動的DoScanPort線程的最大數量在設定的值范圍內,如果線程數已達最大,則會等待某些線程結束後再啟動新的線程。如果用戶發出結束掃描信息,則不再啟動新的線程,而等待已啟動的線程結束後返回。下面是這線程的執行體代碼:DWORD WINAPI StartScan(LPVOID lpParam)
{
  tag_PORTS* pScanParam = (tag_PORTS*)lpParam;

  DWORD dwThreadId;
  unsigned short i;
  if (pScanParam->bSepecifiedPort)
  {
    for(i=0; i<=pScanParam->nCount; i++)
    {
      if (g_bTerminate)
      {
        break;  //用戶已發出結束掃描命令
      }
      while(g_nThreadCount >= g_nMaxThread)
      {
        Sleep(10);
      }

      if (CreateThread(NULL,
               0,
               DoScanPort,
               (LPVOID)new short(pScanParam->nArrOfPorts[i]),
               0,
               &dwThreadId) != NULL)
      {
        g_nThreadCount ++;
      }
    }
  }
  else
  {
    for(i=pScanParam->iStartPort; i<=pScanParam->iEndPort; i++)
    {
      if (g_bTerminate)
      {
        break;  //用戶已發出結束掃描命令
      }
        while(g_nThreadCount >= g_nMaxThread)
      {
        Sleep(10);
      }
      if (CreateThread(NULL, 0, DoScanPort, (LPVOID)new short(i), 0, &dwThreadId) != NULL)
      {
        g_nThreadCount ++;
      }
    }
  }

  //等待各端口掃描線程結束
  while (g_nThreadCount > 0)
  {
    Sleep(50);
  }
  ::SendMessage(g_hWnd, SCAN_THREAD, STARTSCAN_COMPLETE, 0);
  delete pScanParam;
  return ERROR_SUCCESS;
}

3、DoScanPort 線程:

這個線程負責具體掃描指定的端口,並將結果SendMessage給主對話框。下面是其代碼:

DWORD WINAPI DoScanPort(LPVOID lpParam)
{
  DWORD dwRet;
  short nPort = *(short*)  lpParam;
  delete lpParam;

  SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
  if(sock == INVALID_SOCKET)
  {
    AfxMessageBox("創建套接字失敗!");
    dwRet = ERROR_CREATE_SOCKET;
  }
  else
  {
    unsigned long flag = 1;
    if ((ioctlsocket(sock, FIONBIO, &flag) != 0))
    {
      AfxMessageBox("未能改為非阻塞模式!");
      dwRet = ERROR_MODIFY_FIONBIO;
    }
    else
    {
      sockaddr_in severAddr;
      severAddr.sin_family = AF_INET;
      severAddr.sin_port = htons(nPort);
      severAddr.sin_addr.S_un.S_addr = g_ulAddr;
      connect(sock, (sockaddr*)&severAddr, sizeof(severAddr));

      struct fd_set mask;
      FD_ZERO(&mask);
      FD_SET(sock, &mask);

      struct timeval timeout;
      timeout.tv_sec = g_dwTimeOut / 1000;
      timeout.tv_usec = g_dwTimeOut % 1000;

      switch(select(0, NULL, &mask, NULL, &timeout))
      {
      case -1:
        dwRet = ERROR_SELECT;
        break;

      case 0:
        dwRet = ERROR_SELECT_TIMEOUT;
        break;

      default:
        dwRet = ERROR_SUCCESS;
      };
    }
    closesocket(sock);
  }
  g_nThreadCount --;
  if (dwRet == ERROR_SUCCESS)
  {
    ::SendMessage(g_hWnd, SCAN_THREAD, DOSCAN_FIND_PORT, nPort);
  }
  else
  {
    ::SendMessage(g_hWnd, SCAN_THREAD, DOSCAN_END_PORT, nPort);
  }
  return dwRet;
}

三、運行結果

本程序在VC6+WinXp下編寫調試運行正確,在Win98下運行正確。

在我的計算機掃描本機1-5000號端口,超時設置1000ms,200個最大線程數,約需要45秒。當超時設置再短一些時速度可達每秒150個端口的速度。

四、結束語

事實上,速度要想再提高,可能需要其它方法了。如果線程數開得過多,則由於線程的調度開銷過大,速度反而會降低。如果超時設置過短,可能引起掃描的結果不正確(這須視網絡情況決定),並且由於是多線程,超時等待的時間也可能小於因線程調度而等待的時間,則也不能提高速度。

另外我還發現了一個問題,就是掃描本機時,如果以IP地址127.0.0.1時,則139#端口沒有開放;如果設置為本機的實際IP時則139#端口是開放的,這不知是何原因,還望高手指點。

本程序還有一個需要改進的地方,就是不象大多數端口掃描器可掃描指定的IP段,而只設計成掃描指定的某一台主機。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved