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段,而只设计成扫描指定的某一台主机。

     
  • 相关阅读:
    CodeForces 347B Fixed Points (水题)
    CodeForces 347A Difference Row (水题)
    CodeForces 346A Alice and Bob (数学最大公约数)
    CodeForces 474C Captain Marmot (数学,旋转,暴力)
    CodeForces 474B Worms (水题,二分)
    CodeForces 474A Keyboard (水题)
    压力测试学习(一)
    算法学习(一)五个常用算法概念了解
    C#语言规范
    异常System.Threading.Thread.AbortInternal
  • 原文地址:https://www.cnblogs.com/S-volcano/p/5030844.html
Copyright © 2011-2022 走看看