资源下载地址:https://download.csdn.net/download/sheziqiong/85638489
资源下载地址:https://download.csdn.net/download/sheziqiong/85638489

一、技术原理

端口扫描技术向目标系统的TCP/UDP端口发送探测数据包,记录目标系统的响应,通过分析响应来查看该系统处于监听或运行状态的服务。

1. TCP扫描

常见的tcp端口扫描方式有以下三种:

1.1 全扫描(connect)

扫描主机尝试(使用三次握手)与目标主机的某个端口建立正规的连接,连接由系统调用connect()开始,如果端口开放,则连接将建立成功,否则,返回-1,则表示端口关闭。全扫描流程图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BMZ32BlH-1645250441735)(img/007S8ZIlgy1geb1onsvvuj30np0ra74n.jpg)]

  • 优点:编程简单,只需要一个API connect(),比较可靠,因为TCP是可靠协议,当丢包的时候,会重传SYN帧。
  • 缺点
    • 正因为TCP的可靠性,所以当端口不存在的时候,源主机会不断尝试发SYN帧企图得到ack的应答,多次尝试后才会放弃,因此造成了扫描的时间较长。
    • connect的扫描方式可能较容易被目标主机发现。

1.2 半扫描(SYN)

在这种技术中,扫描主机向目标主机的选择端口发送SYN数据段:

  • 如果应答是RST,那么说明端口是关闭的,按照设定继续扫描其他端口;
  • 如果应答中包含SYN和ACK,说明目标端口处于监听状态。

由于SYN扫描时,全连接尚未建立,所以这种技术通常被称为“半连接”扫描,有以下优缺点:

  • 优点:在于即使日志中对于扫描有所记录,但是尝试进行连接的记录均为连接建立未成功记录。
  • 缺点:在大部分操作系统中,发送主机需要构造适用于这种扫描的IP包,实现复杂。并且容易被发现。

1.3 秘密扫描(FIN)

TCP FIN扫描技术使用FIN数据包探测端口:

  • 当一个FIN数据包到达一个关闭的端口,数据包会被丢掉,并返回一个RST数据包;
  • 当一个FIN数据包到达一个打开的端口,数据包只是简单丢掉,不返回RST数据包。

TCP FIN扫描又称作秘密扫描,优缺点如下:

  • 优点:不包含标准的TCP三次握手协议的任何部分,无法被记录,能躲避IDS、防火墙、包过滤器和日志审计,比SYN扫描隐蔽很多。
  • 缺点
    • 在Windows下,无论端口是否监听,都返回RET数据包,无法判断;
    • 可靠性不高,当收不到应答包时,不确定是端口在监听还是丢包了。

2.UDP扫描

对UDP端口扫描时,给一个端口发送UDP报文,如果端口是开放的,则没有响应,如果是关闭的,对方会恢复一个ICMP端口不可达报文。

  • 优点:Linux Windows 都能用
  • 缺点:也是不可靠的,因为返回的是错误信息,所以速度相对于TCP的FIN,SYN扫描要慢一些,如果发送的UDP包太快了,回应的ICMP包会出现大量丢失的现象。

3. 总结

以上四种扫描方式的判别方法总结如下:

扫描方式 端口开放 端口关闭
TCP connect() connect连接成功 connect()返回-1
TCP SYN 返回SYN及ACK 返回RST
TCP FIN 不作应答 返回RST
UDP扫描 不作应答 返回ICMP不可达报文

二、工具设计

我们使用了C和Go两种语言来实现了端口扫描工具,分别由两名组员完成。每种语言分别实现了TCP-connect、SYN、FIN、UDP这四种扫描方式。

为了提高扫描速度,我们分别利用了两种语言的特色:

1. Go语言实现

我们采用了Go的携程+生产者消费者模型,让多个生产者发出消息,并同时让多个消费者监听返回,如果收到了对应的返回,则说明端口开放。这样的模型一方面可以实现并行从而加快扫描速度,另一方面使用异步模型,可以有效减少因为Socket IO等待的时间。

2. C语言实现

我们采用了多线程的方式。多线程以实现并行从而加快扫描速度,当轮询多个socket io,并且其中某个socket io有响应时,则表示该端口的扫描报文已返回,这样就可以有效减少因为Socket IO等待的时间。

三、具体实现

1. Go语言的具体实现(lfy)

1.1 整体架构

生产者和消费者模型的具体实现:

func producer(jobs chan *scanJob, ports []uint16) {for _, p := range ports {s := scanJob{Laddr: LAddr,Raddr: RAddr,SPort: uint16(random(10000, 65535)),DPort: p,}jobs <- &s}jobs <- &scanJob{Stop: true}close(jobs)
}
http://www.biyezuopin.vip
func consumer(jobs <-chan *scanJob, results chan<- *scanResult) {for {if j, ok := <-jobs; ok {if j.Stop == true {time.Sleep(time.Duration(*TimeOut) * time.Second)StopChan <- true} else if *ScanWay == "SYN" {SynScan(j)time.Sleep(1e7)} else if *ScanWay == "TCP" {TcpScan(j, results)} else if *ScanWay == "FIN" {FinScan(j)time.Sleep(1e7)} else if *ScanWay == "UDP" {UdpScan(j, results)time.Sleep(1e7)}}}
}

如上,producer函数是生产者,consumer函数是消费者。jobs是一个channel,channel是go语言中用于协程间通信的管道。整个生产者消费者模型的实现主要通过go协程来实现,同时也通过多个go携程来进行并发扫描。通过将scanJob的任务结构体输入jobs中,使消费者可以对生产的任务进行扫描。consumer是消费者,通过从jobs channel中取出job,并根据scanway进行进行响应的扫描。j.Stop是停止的flag,当channel中收到j.Stop时,则传一个true进入StopChan停止扫描。

go producer(jobs, ports)for {select {case res := <-results:fmt.Println("Open: ", res.Port)case <-StopChan:if *ScanWay == "FIN" {for _, v := range ports {if vis[v] == false {fmt.Println("Open: ", v)}}}eTime := time.Now().Unix()fmt.Println("Time: ", eTime-sTime, "s")os.Exit(0)}
}

go producer产生一个producer协程生成任务,接下来通过轮询results channel来获得结果。

1.2 全扫描(connect方式)

TCP扫描的实现最简单,只需要调用Dial函数在TCP层建立socket连接三次握手即可,成功则表示端口开放。这里为了加速,设置了握手的timeout。

func TcpScan(j *scanJob, result chan<- *scanResult) {target := fmt.Sprintf("%s:%d", j.Raddr, j.DPort)conn, err := net.DialTimeout("tcp", target, time.Duration(*TimeOut)*time.Second)if err == nil {result <- &scanResult{Port: j.DPort,}defer conn.Close()}
}

1.3 SYN、FIN扫描的具体实现

  • SYN和FIN包的制作与发送

可以看到synscan是直接构造tcp包的,是工作在ip层。而SYN扫描的精髓是手工构造的syn包,因此这里重点关注一下makePkg函数。

1.4 UDP扫描的具体实现

由于UDP端口并不常见,而且目前比较常见的UDP扫描方式比较慢,因此这里对UDP扫描做了一些改进。
目前,互联网上开放的UDP端口主要有:53、123、161,因此这里暂时只对常见端口做了定制数据,当发送这些数据时,如果有返回则表示端口开放。

1.5 过程中踩的坑

如上图所示,左边的是能收到回显的syn包,右边是不能的,右边的最后0000就是Urgent Pointer,之前不一样的只有checksum,因此两边除了option部分完全相同,但左边收到了ack应答,而右边并没有收到。这个bug让我甚至怀疑tcp三次握手是否和option字段有关。

后来通过查阅资料发现,这与MTU有关,左边帧长大于64字节可以发送,而右边小于64字节,无法发送。因此我在原来的代码中去掉了option部分,并padding了12个\0,这样就能成功发送了。

2. C语言的具体实现(jyx)

2.1 整体架构

该程序在linux下用c语言实现TCP的三种端口扫描方式(connect、SYN、FIN)和UDP端口扫描。我用一个函数数组存放四个实现函数,根据选择的端口扫描方式,在创建线程的时候选择不同的函数来执行,主线程挂起等待扫描线程结束,最后打印开放的端口。用一个全局的队列来存放开放的端口,链表实现。除了connect方式,其它的方式需要知道源主机的ip,因此在创建扫描线程之前,先获得本机的ip地址,作为参数传给扫描线程。

每个扫描线程(tcpXXXScanPort)会建立一个线程池,对每个要扫描的端口都创建一个线程tcpXXXScanEach(线程配置成detach属性),具体的实现每种方式有些差别。

由于调用线程只能传递一个参数,所以我把要传递的信息放在一个数据结构里面,用指针传给子线程。以下是自己定义的要传递的数据结构:

struct ScanSock
{unsigned short portStart;unsigned short portEnd;char destIP[16];char sourIP[16];
};struct ScanParam
{unsigned short sourPort;unsigned short destPort;char destIP[16];char sourIP[16];
};
  • 代码分析:

    • 这里也是一样贴出头文件,包含了函数和全局变量的声明

      #ifndef TCPSYNSCAN_H_H
      #define TCPSYNSCAN_H_H#include "mysock.h"int synCnt;
      static pthread_mutex_t syn_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
      static pthread_mutex_t syn_num_mutex = PTHREAD_MUTEX_INITIALIZER;void* tcpSynScanPort(void *arg);
      void* tcpSynScanEach(void *arg);
      void* tcpSynScanRecv(void *arg);#endif
      
    • 实现本模块还需要定义tcp伪首部,伪首部是一个虚拟的数据结构,其中的信息是从数据报所在IP分组头的分组头中提取的,既不向下传送也不向上递交,而仅仅是为计算校验和。这样的校验和,既校验了TCP&UDP用户数据的源端口号和目的端口号以及TCP&UDP用户数据报的数据部分,又检验了IP数据报的源IP地址和目的地址。伪报头保证TCP&UDP数据单元到达正确的目的地址。

      struct PseudoHdr
      {unsigned int    sIP;unsigned int    dIP;char            useless;char            protocol;unsigned short  length;
      };
      
    • checksum函数

      unsigned short checksum(unsigned char*buf, unsigned int len)
      {//对每16位进行反码求和(高位溢出位会加到低位),即先对每16位求和,在将得到的和转为反码unsigned long sum = 0;unsigned short *pbuf;pbuf = (unsigned short*)buf;//转化成指向16位的指针while(len > 1)//求和{sum+=*pbuf++;len-=2;}if(len)//如果len为奇数,则最后剩一位要求和sum += *(unsigned char*)pbuf;sum = (sum>>16)+(sum & 0xffff);sum += (sum>>16);//上一步可能产生溢出return (unsigned short)(~sum);
      }
      

2.4 秘密扫描(FIN扫描)

  • 整体思路:

    众所周知,当调用close()时要经历四次挥手的过程FIN-ACK-FIN-ACK。当我们发送FIN帧给一个非监听的端口时,会有RST应答,反之,发给一个正在监听的端口时,不会有任何回应。这中扫描速度快,隐蔽性好,但是对windows系统无效。

  • 具体细节:

    和SYN扫描基本相同,就是构造的数据包有一点差别,标志位FIN设为0。还有对接收数据包的处理不完全相同。

  • 代码分析:

    给出头文件,全局变量定义和函数声明:

    #ifndef TCPFINSCAN_H_H
    #define TCPFINSCAN_H_H#include "mysock.h"int finCnt;
    static pthread_mutex_t fin_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
    static pthread_mutex_t fin_num_mutex = PTHREAD_MUTEX_INITIALIZER;void* tcpFinScanPort(void *arg);
    void* tcpFinScanEach(void *arg);
    void* tcpFinScanRecv(void *arg);#endif
    

2.5 UDP扫描

  • 整体思路:

    给一个端口发送UDP报文,如果端口是开放的,则没有响应,如果端口是关闭的,对方会回复一个ICMP端口不可达报文。主扫描线程udpIcmpScanPort建立一个线程池,对每个要扫描的端口都创建一个线程udpIcmpScanEach,负责发送UDP包到各端口,另有一个线程udpIcmpScanRecv负责处理所有收到的数据包。发送的数据包要自己组装,协议可以使用原始套接字,协议选择UDP,接收时另外建立一个socket,也是使用原始套接字,协议选择ICMP。

  • 遇到的问题和解决方案:

    1. 发送频率太快会大量丢包

      按照SYN或者FIN的频率发送UDP包的话,会出现大量ICMP或者UDP丢包现象。频率的大小和扫描端口的数量有关系,需要扫描的数量越大,用来记录目前存在线程数的全局变量udpCnt越容易超过上限,程序就卡死在这里了。

      我想到两种解决方法,一个是把频率调低,频率降为原来的1/2时扫描1000个包还行。但是总觉得这种方法有侥幸的感觉。我尝试使用第二种方法,还是原来的频率,加了一个定时器(信号ALARM实现),在规定的时间内(我设置的是30秒)如果程序卡死在这里,则重新发送UDP包,速度可以做到跟FIN差不多,程序也没有卡死。

    2. 选择icmp相关数据结构的问题

      linux提供的有关icmp的数据结构有两种,icmp与icmphdr,我用sizeof测了一下,前者是20字节,后者是8字节。因为抓包来看,接收到的ICMP报文是IP首部(20字节)+ICMP首部(8字节)+发送的IP层的UDP报文,所以其实是IP首部(20字节,目的端口方)+ICMP首部(8字节)+IP首部(20字节,端口扫描方)+UDP首部+UDP数据,所以我用的是icmphdr。

  • 代码分析:

    头文件

    #ifndef UDPICMPSCAN_H_H
    #define UDPICMPSCAN_H_H#include "mysock.h"int udpCnt;
    static pthread_mutex_t udp_printf_mutex = PTHREAD_MUTEX_INITIALIZER;
    static pthread_mutex_t udp_num_mutex = PTHREAD_MUTEX_INITIALIZER;void* udpIcmpScanPort(void *arg);
    void* udpIcmpScanEach(void *arg);
    void* udpIcmpScanRecv(void *arg);
    void alarm_udp(int signo);#endif
    

四、实际效果对比

目前实际应用最广泛的端口扫描工具有nmap和zmap,nmap强在以功能强大、扫描结果精准,zmap的优点主要是扫描速度快,号称44分钟扫遍整个互联网。但是zmap不支持多端口批量扫描,因此,我们将我们写的两款端口扫描器与nmap这款工业界十分出名的产品做了比较。

nmap及zmap的各种扫描方式对应的参数:
-sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans
-sU: UDP Scan
-sN/sF/sX: TCP Null, FIN, and Xmas scans

Go-Portscan

1. SYN方式

可以看到,对于SYN方式,go扫描器的扫描结果与nmap基本没有差别,并且速度快很多。这里快的原因应该主要是nmap对服务指纹进行了确认,因此速度慢了一点。

2. TCP方式

nmap中出现了很多filter的端口,nmap的文档指出filter的端口实际上是不开放的,因此这里可以看到我们还是快了一些,并且结果正确。

3. FIN方式

4. UDP方式

这里可以看到,UDP扫描简直慢到了极点。为了扫描的准确性,nmap慢到了不可忍耐。而通过我们的改进,虽然麻烦了一些,但是速度提升了很多。

C-portscan

1. connect方式

对于connect方式,扫描速度还是比较慢的,但是由于多线程加速,还是快了不少

2. SYN扫描

相比于全扫描,半扫描的速度大大加快,同时准确度也有所保障

3. FIN扫描

理论上来说FIN扫描的速度应该是最快的,但是这里结果并不是这样,是因为FIN扫描不可靠,如果未收到数据包,不能确定是丢包了还是端口开放,所以会对那些未响应的端口多次发送数据包来确认,以减少丢包带来的差错,所以扫描所耗时间较长。

4. UDP扫描

可以看到,UDP扫描是非常非常慢的

五、总结与反思

​ 通过这次实验,加深了我们对计算机网络协议的理解与应用,在这个过程中学到了很多知识,对网络编程有所了解。然而,我们所完成的程序与专业的端口扫描软件的准确度和扫描速度还是相差很多,希望能在以后的学习中将其多加改进。

资源下载地址:https://download.csdn.net/download/sheziqiong/85638489
资源下载地址:https://download.csdn.net/download/sheziqiong/85638489

基于C语言的端口扫描工具设计与实现相关推荐

  1. 扫描服务器端口信息工具,服务器端口扫描工具

    服务器端口扫描工具 内容精选 换一换 2.3.2 端口扫描Internet上的大部分服务都使用一种基于TCP/IP协议的客户机/服务器的模式.在这种模式下,服务器端在某个TCP或UDP(User Da ...

  2. “诸神之眼”——Nmap端口扫描工具使用小手册

    "诸神之眼"--Nmap端口扫描工具使用小手册 1.Nmap介绍 1.1.Nmap简介 Nmap ("Network Mapper(网络映射器)") 是一款开放 ...

  3. 【java毕业设计】基于java+BS的QQ屏幕截图工具设计与实现(毕业论文+程序源码)——屏幕截图工具

    基于java+BS的QQ屏幕截图工具设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+BS的QQ屏幕截图工具设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦. 文章目录: ...

  4. 端口扫描工具终极用法

    了解更多:端口扫描工具终极用法 | 至察助安 - 网络安全干货博客 为什么要做c段探测,运营商分配给IDC机房地址时大部分都是连续IP地址,租给客户(渗透目标)时很大概率会分配同C段内IP地址(除非目 ...

  5. 交换机端口扫描工具小课堂

    交换机端口扫描工具指用于探测服务器或主机开放交换机端口情况的工具.常被计算机管理员用于确认安全策略,同时被攻击者用于识别目标主机上的可运作的网络服务. 什么是端口扫描工具? 交换机端口扫描定义是客户端 ...

  6. java 2048游戏_JAVA2048游戏 本课程设计是基于java语言的2048小游戏设计 联合开发网 - pudn.com...

    JAVA2048游戏 所属分类:游戏 开发工具:Java 文件大小:789KB 下载次数:4 上传日期:2020-11-23 10:57:11 上 传 者:滴滴滴大萌 说明:  本课程设计是基于jav ...

  7. 端口扫描php,端口扫描工具

    端口扫描工具是指用于探测服务器或主机开放端口情况的工具,常被计算机管理员用于确认安全策略,同时被攻击者用于识别目标主机上的可运作的网络服务. 端口扫描工具(Port Scanner)指用于探测服务器或 ...

  8. 基于图文界面的蓝牙扫描工具btscanner

    基于图文界面的蓝牙扫描工具btscanner btscanner是Kali Linux内置的一款蓝牙扫描工具.它提供图文界面,更便于渗透测试人员查看扫描信息.该工具会自动使用主机所有的蓝牙接口,并提供 ...

  9. 海量端口扫描工具masscan

    海量端口扫描工具masscan masscan号称是互联网上最快的端口扫描工具,可以6分钟扫描整个互联网,每秒可以发送一百万个数据包.为了提高处理速度,masscan定制了TCP/IP栈,从而不影响本 ...

  10. ifconfig没有命令 kali_kali学习笔记之——端口扫描工具

    之前的kali学习笔记分别介绍了kali的网卡问题和隐藏ssid的扫描 蟪蛄语春秋:kali学习笔记之--wi read():Network is down问题​zhuanlan.zhihu.com ...

最新文章

  1. python接口自动化测试框架链接数据库_python接口自动化测试框架实现之操作mysq数据库...
  2. 鸟哥的Linux私房菜(基础学习,服务器架设)
  3. 深度解读:都是顶薪为什么浓眉远超卡哇伊?
  4. Mybatis的核心——SqlSession解读
  5. HttpClient4.5.2调用示例(转载+原创)
  6. 谷歌搜索和谷歌站内搜索
  7. Linux 下安装Nginx,配置自启动
  8. 农村民间借贷一分利息,有借条受法律保护吗?
  9. 《20170911-构建之法:现代软件工程-阅读笔记》
  10. python终端指令大全_使用python模拟命令行终端的示例
  11. 动态BGP和静态BGP的含义与区别
  12. python get,post提交表单
  13. 【java】JMH微基准测试,报错Unable to find the resource: /META-INF/BenchmarkList
  14. Go语言程序的数组初始化
  15. sklearn 交叉验证与参数寻优
  16. 你我贷CTO冯炯:互联网金融的P2P+O2O怎么做?
  17. RemObjects Elements with water v10.0.0.2595 + CRACK
  18. Synonyms 中文近义词工具包 -- 支持文本对齐,推荐算法,相似度计算,语义偏移,关键字提取,概念提取,自动摘要,搜索引擎等
  19. 结构体定义的几种形式
  20. 如何做将两张图片合二为一

热门文章

  1. 高速公路上边有没有人脸识别摄像头_人脸识别特征介绍以及难题概括
  2. PoE交换机可以当普通交换机使用吗?
  3. TwinCAT3安装教程-EtherCAT学习
  4. TwinCAT3 EL6070-1648授权模块使用
  5. PostgreSQL创建、连接企业型地理数据库
  6. 单片机小车电机运转不起来
  7. linux中mvn命令的下载与安装
  8. R语言将两个矩阵数据进行相乘
  9. 矩阵和向量的求导法则
  10. 专访Token经济设计专家叶开:Token设计画布与10大设计模式