zoukankan      html  css  js  c++  java
  • 一、TCP扫描技术

     

     

     

    一、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、全局变量:

    以下是所有全局变量的定义:

    1.HWND g_hWnd = NULL; //处理消息的窗口句柄
    2.unsigned long g_ulAddr = INADDR_NONE; //扫描的主机地址
    3.DWORD g_dwTimeOut = 1000; //连接超时时间,以ms计
    4.bool g_bTerminate = false//是否用户发出结束扫描的标志
    5.short g_nMaxThread = 200; //最大允许的扫描线程数,经试验不宜大于200
    6.short g_nThreadCount = 0; //当前正在扫描的进程数

    2、StartScan 线程:

    这个线程完成的任务是启动具体的扫描DoScanPort线程,对某一个端口进行扫描。该进程启动的DoScanPort线程的最大数量在设定的值范围内,如果线程数已达最大,则会等待某些线程结束后再启动新的线程。如果用户发出结束扫描信息,则不再启动新的线程,而等待已启动的线程结束后返回。下面是这线程的执行体代码:

    01.DWORD WINAPI StartScan(LPVOID lpParam)
    02.{  
    03.tag_PORTS* pScanParam = (tag_PORTS*)lpParam;
    04. 
    05.DWORD dwThreadId;
    06.unsigned short i;
    07. 
    08.if (pScanParam->bSepecifiedPort)
    09.{
    10.for(i=0; i<=pScanParam->nCount; i++)
    11.{
    12.if (g_bTerminate)  
    13.{
    14.break;  //用户已发出结束扫描命令
    15.}
    16.while(g_nThreadCount >= g_nMaxThread)
    17.{
    18.Sleep(10);
    19.}          
    20. 
    21.if (CreateThread(NULL,
    22.0,
    23.DoScanPort,
    24.(LPVOID)new short(pScanParam->nArrOfPorts[i]),
    25.0,
    26.&dwThreadId) != NULL)
    27.{
    28.g_nThreadCount ++;
    29.}
    30.}  
    31.}
    32.else
    33.{      
    34.for(i=pScanParam->iStartPort; i<=pScanParam->iEndPort; i++)
    35.{
    36.if (g_bTerminate)  
    37.{
    38.break;  //用户已发出结束扫描命令
    39.}
    40.while(g_nThreadCount >= g_nMaxThread)
    41.{
    42.Sleep(10);
    43.}  
    44. 
    45.if (CreateThread(NULL, 0, DoScanPort, (LPVOID)new short(i), 0, &dwThreadId) != NULL)
    46.{
    47.g_nThreadCount ++;
    48.}
    49.}
    50.}
    51. 
    52.//等待各端口扫描线程结束
    53.while (g_nThreadCount > 0)
    54.{
    55.Sleep(50);
    56.}
    57.::SendMessage(g_hWnd, SCAN_THREAD, STARTSCAN_COMPLETE, 0);
    58.delete pScanParam;
    59.return ERROR_SUCCESS;
    60.}

    3、DoScanPort 线程:

    这个线程负责具体扫描指定的端口,并将结果SendMessage给主对话框。下面是其代码:

    01.DWORD WINAPI DoScanPort(LPVOID lpParam)
    02.{
    03.DWORD dwRet;
    04.short nPort = *(short*) lpParam;
    05.delete lpParam;
    06. 
    07.SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    08.if(sock == INVALID_SOCKET)
    09.{
    10.AfxMessageBox("创建套接字失败!");
    11.dwRet = ERROR_CREATE_SOCKET;
    12.}
    13.else
    14.{      
    15.unsigned long flag = 1;    
    16.if ((ioctlsocket(sock, FIONBIO, &flag) != 0))
    17.{
    18.AfxMessageBox("未能改为非阻塞模式!");
    19.dwRet = ERROR_MODIFY_FIONBIO;
    20.}
    21.else
    22.{
    23.sockaddr_in severAddr;
    24.severAddr.sin_family = AF_INET;
    25.severAddr.sin_port = htons(nPort);     
    26.severAddr.sin_addr.S_un.S_addr = g_ulAddr;
    27.connect(sock, (sockaddr*)&severAddr, sizeof(severAddr));
    28. 
    29.struct fd_set mask;        
    30.FD_ZERO(&mask);
    31.FD_SET(sock, &mask);
    32. 
    33.struct timeval timeout;
    34.timeout.tv_sec = g_dwTimeOut / 1000;
    35.timeout.tv_usec = g_dwTimeOut % 1000;
    36. 
    37.switch(select(0, NULL, &mask, NULL, &timeout))
    38.{
    39.case -1:
    40.dwRet = ERROR_SELECT;
    41.break;
    42. 
    43.case 0:
    44.dwRet = ERROR_SELECT_TIMEOUT;
    45.break;
    46. 
    47.default:       
    48.dwRet = ERROR_SUCCESS;
    49.};
    50.}      
    51.closesocket(sock);
    52.}
    53.g_nThreadCount --;
    54.if (dwRet == ERROR_SUCCESS)
    55.{
    56.::SendMessage(g_hWnd, SCAN_THREAD, DOSCAN_FIND_PORT, nPort);
    57.}
    58.else
    59.{
    60.::SendMessage(g_hWnd, SCAN_THREAD, DOSCAN_END_PORT, nPort);
    61.}
    62.return dwRet;
    63.}

    三、运行结果

    本程序在VC6+WinXp下编写调试运行正确,在Win98下运行正确。

    在我的计算机扫描本机1-5000号端口,超时设置1000ms,200个最大线程数,约需要45秒。当超时设置再短一些时速度可达每秒150个端口的速度。

    四、结束语

    事实上,速度要想再提高,可能需要其它方法了。如果线程数开得过多,则由于线程的调度开销过大,速度反而会降低。如果超时设置过短,可能引起扫描的结果不正确(这须视网络情况决定),并且由于是多线程,超时等待的时间也可能小于因线程调度而等待的时间,则也不能提高速度。

    另外我还发现了一个问题,就是扫描本机时,如果以IP地址127.0.0.1时,则139#端口没有开放;如果设置为本机的实际IP时则139#端口是开放的,这不知是何原因,还望高手指点。

    本程序还有一个需要改进的地方,就是不象大多数端口扫描器可扫描指定的IP段,而只设计成扫描指定的某一台主机。

     
  • 相关阅读:
    网上找的面试题-之一
    python里的Join函数
    【转载51CTO】Linux中引号那些事儿
    【面试编程题】巧妙排序:排序只有1,2,3三个元素的数组,不能统计1,2,3的个数。
    [转载]mininet的安装和使用
    Open vSwitch源码阅读【转】及自己的理解【稍后更新】
    7、8月份安排 进度条
    请不要忽视基础小细节
    【编程之美】2.20 程序理解问题
    GDOI2017爆炸记
  • 原文地址:https://www.cnblogs.com/S-volcano/p/5030844.html
Copyright © 2011-2022 走看看