阿里网站seo,电商平台如何宣传,网站颜色搭配技巧,网站建设 点指成名资源下载地址#xff1a;https://download.csdn.net/download/sheziqiong/85638489 资源下载地址#xff1a;https://download.csdn.net/download/sheziqiong/85638489
一、技术原理
端口扫描技术向目标系统的TCP/UDP端口发送探测数据包#xff0c;记录目标系统的响应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()返回-1TCP SYN返回SYN及ACK返回RSTTCP FIN不作应答返回RSTUDP扫描不作应答返回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是一个channelchannel是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.hint 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分组头的分组头中提取的既不向下传送也不向上递交而仅仅是为计算校验和。这样的校验和既校验了TCPUDP用户数据的源端口号和目的端口号以及TCPUDP用户数据报的数据部分又检验了IP数据报的源IP地址和目的地址。伪报头保证TCPUDP数据单元到达正确的目的地址。 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 (sum16)(sum 0xffff);sum (sum16);//上一步可能产生溢出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.hint 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);#endif2.5 UDP扫描 整体思路 给一个端口发送UDP报文如果端口是开放的则没有响应如果端口是关闭的对方会回复一个ICMP端口不可达报文。主扫描线程udpIcmpScanPort建立一个线程池对每个要扫描的端口都创建一个线程udpIcmpScanEach负责发送UDP包到各端口另有一个线程udpIcmpScanRecv负责处理所有收到的数据包。发送的数据包要自己组装协议可以使用原始套接字协议选择UDP接收时另外建立一个socket也是使用原始套接字协议选择ICMP。 遇到的问题和解决方案 发送频率太快会大量丢包 按照SYN或者FIN的频率发送UDP包的话会出现大量ICMP或者UDP丢包现象。频率的大小和扫描端口的数量有关系需要扫描的数量越大用来记录目前存在线程数的全局变量udpCnt越容易超过上限程序就卡死在这里了。 我想到两种解决方法一个是把频率调低频率降为原来的1/2时扫描1000个包还行。但是总觉得这种方法有侥幸的感觉。我尝试使用第二种方法还是原来的频率加了一个定时器信号ALARM实现在规定的时间内我设置的是30秒如果程序卡死在这里则重新发送UDP包速度可以做到跟FIN差不多程序也没有卡死。 选择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.hint 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和zmapnmap强在以功能强大、扫描结果精准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 scansGo-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