简介
现了一个基于Python的TCP SYN扫描工具,采用Scapy库构造和解析网络报文,并采用线程池技术提高扫描效率。
实现原理
TCP SYN扫描的原理是利用了TCP连接建立过程中的三次握手特性,以半开连接(Half-open scanning)的方式探测目标主机的服务端口是否开放。具体步骤如下:
- 三次握手过程:
- 在正常情况下,客户端(即扫描器)发起一个TCP连接时,会发送一个SYN(同步序列编号)报文到服务器。
- 服务器接收到SYN报文后,如果该端口处于监听状态,则会回复一个SYN+ACK(同步并确认)报文作为回应。
- 客户端在接收到SYN+ACK之后,通常会回复一个ACK(确认)报文完成三次握手,然后双方就可以开始传输数据。
- SYN扫描技术:
- 在TCP SYN扫描中,扫描器模拟正常的连接请求,向目标主机和指定端口发送一个SYN报文。
- 如果目标端口是开放的,它会返回一个SYN+ACK报文;若端口未打开或被防火墙阻止,则会返回一个RST(复位)报文。
- 但是,不同于正常的连接建立过程,扫描器在接收到SYN+ACK报文后,并不会继续完成第三次握手(即不发送ACK),而是直接丢弃这个连接尝试,因此不会建立起完整的连接。
- 根据收到的响应类型,扫描器就能判断端口的状态:收到SYN+ACK则认为端口开放,收到RST则认为端口关闭或不可达。
运行流程
本TCP SYN扫描工具的执行过程如下:
- 初始化阶段:
- 引入必要的Python库,包括scapy.all、argparse、logging以及concurrent.futures.ThreadPoolExecutor。
- 设置Scapy运行时日志级别为ERROR,以减少无关输出。
- 定义命令行参数解析器argparse.ArgumentParser,用于接收用户输入的被扫描主机IP地址(通过"-H"或"--host"指定)和端口范围(通过"-p"或"--ports"指定,默认值为"1-1024")。
- 参数处理阶段:
- 解析用户输入的参数,若同时提供了主机IP地址和端口范围,则将端口范围字符串按'-'分割后转化为整数列表,并生成相应的端口号序列。
- 扫描准备阶段:
- 函数tcpSynScan_single(target, port)针对单个目标端口进行TCP SYN半打开扫描。该函数构造一个SYN标志设置为"S"的TCP数据包并发送至指定主机和端口,根据返回的数据包判断端口状态。
- 并发扫描阶段:
- 函数tcpSynScan_parallel(target, ports)利用线程池ThreadPoolExecutor实现对目标主机多个端口的同时扫描。
- 将目标主机IP地址与待扫描的端口列表作为参数传递给executor.map()函数,它会并发地调用tcpSynScan_single对每个端口执行扫描操作。
- 扫描结果以布尔值形式存储在results变量中,对应表示各个端口是否开放。
- 结果展示阶段:
- 使用zip(ports, results)将扫描结果与原始端口号配对,然后循环遍历这些配对信息。
- 对于检测到开放的端口,在控制台输出格式化的信息,内容包括:扫描主机IP地址、端口号以及其状态(开放或关闭)。
综上所述,整个项目实现了从参数接收、并发扫描到结果显示的完整TCP SYN扫描流程,有效地提升了扫描效率且保持了对目标主机端口状态准确判断的能力。
代码实现
执行单个端口的TCP SYN扫描。
tcpSynScan_single(target, port):针对单个目标端口执行TCP SYN半打开扫描技术。通过发送一个SYN标志设置为"S"的TCP报文至指定IP地址的目标主机和端口,并监测其响应。若目标端口对SYN报文返回SYN+ACK(表示端口开放),则函数返回True;否则,若收到RST(复位)标志或无响应,则认为端口关闭,函数返回False。
dart
def tcpSynScan_single(target, port):
"""
执行单个端口的TCP SYN扫描。
参数:
- target: 目标主机的IP地址。
- port: 要扫描的端口号。
返回值:
- 如果端口对SYN报文响应为开放(SA),则返回True;
- 如果端口响应为关闭(RST),或无响应,则返回False。
"""
send = sr1(IP(dst=target) / TCP(dport=port, flags="S"), timeout=2, verbose=0)
if (send is None):
return False
elif send.haslayer("TCP"):
if send["TCP"].flags == "SA":
return True
elif send["TCP"].flags in ["RA", "R"]: # RST响应
return False
else:
return False
并行执行TCP SYN扫描。
tcpSynScan_parallel(target, ports):在给定的目标主机上并发地对多个端口执行tcpSynScan_single函数,利用线程池ThreadPoolExecutor以优化资源分配与提高扫描速度。该函数接收一个IP地址和端口列表作为输入参数,扫描完成后将打印出所有开放端口的相关信息。
dart
def tcpSynScan_parallel(target, ports):
"""
并行执行TCP SYN扫描。
参数:
- target: 目标主机的IP地址。
- ports: 要扫描的端口列表。
说明:
使用线程池并发地对多个端口执行tcpSynScan_single函数。
"""
with ThreadPoolExecutor(max_workers=50) as executor:
results = executor.map(tcpSynScan_single, [target] * len(ports), ports)
for port, is_open in zip(ports, results):
status = "开放" if is_open else "关闭"
if status == "开放":
print("[+] 扫描主机: %-13s 端口: %-5s %s" % (target, port, status))
参数设置
dart
if __name__ == "__main__":
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
parser = argparse.ArgumentParser(description='TCP SYN扫描工具')
parser.add_argument("-H", "--host", dest="host", help="输入一个被扫描主机IP地址")
parser.add_argument("-p", "--ports", dest="ports", default="1-1024", help="输入端口范围,默认为1-1024")
args = parser.parse_args()
if args.host and args.ports:
start, end = map(int, args.ports.split('-'))
ports = list(range(start, end + 1))
tcpSynScan_parallel(args.host, ports)
else:
parser.print_help()
优化建议
-
线程池参数调整: 目前程序设置的线程池最大工作线程数为50,这一数值可以根据目标主机的网络状况、系统资源和实际需求进行动态调整。可以通过实验评估不同数量的工作线程对扫描效率和系统负载的影响,选择最佳线程数。
-
超时时间优化: 代码中sr1()函数的超时时间为2秒,这个值可能不足以应对所有网络环境下的响应情况,可以考虑增加一个配置选项允许用户自定义超时时间,以适应不同的网络延迟。
-
错误处理与重试机制: 在网络不稳定或目标主机有防护措施的情况下,单次SYN请求可能会失败。为了提高扫描稳定性,可以添加重试机制,当某端口在首次扫描无响应或者收到非预期响应时,在设定次数内重新尝试发送SYN报文。
-
结果存储与展示: 当扫描大量端口时,仅打印出开放端口的信息可能不够全面。可将扫描结果保存至文件,便于后续分析。同时,可考虑提供更详细的结果输出选项,如显示扫描进度、总扫描耗时等信息。
-
资源消耗控制: 考虑到大规模扫描可能会导致系统资源(CPU、内存、网络带宽)过度消耗,应引入资源限制策略,例如,根据系统的可用资源动态调节线程池大小,或在检测到系统资源紧张时暂停部分扫描任务。
-
合法性检查与合规性: 实施网络扫描前,确保符合当地法律法规和网络使用政策,并对目标IP地址进行合法性验证,防止误操作或其他法律风险。
-
性能监控与调优: 可以添加性能监控模块,实时记录并分析扫描过程中的各项指标,包括线程执行情况、CPU和内存占用、网络流量等,为进一步优化提供数据支持。