16.1 Socket 端口扫描技术

2023-10-22 01:45

本文主要是介绍16.1 Socket 端口扫描技术,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

端口扫描是一种网络安全测试技术,该技术可用于确定对端主机中开放的服务,从而在渗透中实现信息搜集,其主要原理是通过发送一系列的网络请求来探测特定主机上开放的TCP/IP端口。具体来说,端口扫描程序将从指定的起始端口开始,向目标主机发送一条TCPUDP消息(这取决于端口的协议类型)。如果目标主机正在监听该端口,则它将返回一个确认消息,这表明该端口是开放的。如果没有响应,则说明该端口是关闭的或被过滤。

首先我们来了解一下阻塞与非阻塞模式:

  • 阻塞模式是指当I/O操作无法立即完成时,应用程序会阻塞并等待操作完成。例如,在使用阻塞套接字接收数据时,如果没有数据可用,则调用函数将一直阻塞,直到有数据可用为止。在这种模式下,I/O操作将会一直阻塞应用程序的进程,因此无法执行其他任务。

  • 非阻塞模式是指当I/O操作无法立即完成时,应用程序会立即返回并继续执行其他任务。例如,在使用非阻塞套接字接收数据时,如果没有数据可用,则调用函数将立即返回,并指示操作正在进行中,同时应用程序可以执行其他任务。在这种模式下,应用程序必须反复调用I/O操作以检查其完译状态,这通常是通过轮询或事件通知机制实现的。非阻塞模式允许应用程序同时执行多个任务,但每个I/O操作都需要增加一定的额外开销。

要实现端口探测我们可以通过connect()这个函数来实现,利用connect函数实现端口开放检查的原理是通过TCP协议的三次握手过程来探测目标主机是否开放目标端口。

TCP协议的三次握手过程中,客户端向服务器发送一个SYN标志位的TCP数据包。如果目标主机开放了目标端口并且正在监听连接请求,则服务器会返回一个带有SYNACK标志位的TCP数据包,表示确认连接请求并请求客户端确认。此时客户端回应一个ACK标志位的TCP数据包,表示确认连接请求,并建立了一个到服务器端口的连接。此时客户端和服务器端之间建立了一个TCP连接,可以进行数据传输。

如果目标主机没有开放目标端口或者目标端口已经被占用,则服务器不会响应客户端的TCP数据包,客户端会在一定时间后收到一个超时错误,表示连接失败。

因此,通过调用connect函数,可以向目标主机发送一个SYN标志位的TCP数据包并等待服务器响应,从而判断目标端口是否开放。如果connect函数返回0,则表示连接成功,目标端口开放;否则,连接失败,目标端口未开放或目标主机不可达。

// 探测网络端口开放情况
BOOL PortScan(char *Addr, int Port)
{WSADATA wsd;SOCKET sHost;SOCKADDR_IN servAddr;// 初始化套接字库if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0){return FALSE;}// 创建套接字sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == sHost){return FALSE;}// 设置连接地址和端口servAddr.sin_family = AF_INET;servAddr.sin_addr.S_un.S_addr = inet_addr(Addr);servAddr.sin_port = htons(Port);// 连接测试int retval = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));if (retval != SOCKET_ERROR){return TRUE;}WSACleanup();closesocket(sHost);return FALSE;
}int main(int argc, char* argv[])
{int port_list[] = { 80, 443, 445, 135, 139, 445 };int port_size = sizeof(port_list) / sizeof(int);for (int x = 0; x < port_size; x++){int ret = PortScan("8.141.58.64", port_list[x]);printf("循环次数: %d 端口: %d 状态: %d \n", x + 1, port_list[x], ret);}system("pause");return 0;
}

上述代码片段则是一个简单的端口探测案例,当运行后程序会调用connect函数向目标主机发送一个SYN标志位的TCP数据包,探测目标端口是否开放。如果目标主机响应带有SYNACK标志位的TCP数据包,则表示连接请求成功并请求确认,操作系统在自动发送带ACK标志位的TCP数据包进行确认,建立TCP连接;

如果目标主机没有响应或者响应带有RST标志位的TCP数据包,则表示连接请求失败,目标端口为未开放状态。通过此方式,程序可以快速检测多个端口是否开放,该程序运行后输出效果如下图所示;

上述代码虽然可以实现端口扫描,但是读者应该会发现此方法扫描很慢,这是因为扫描器每次只能链接一个主机上的端口只有当connect函数返回后才会执行下一次探测任务,而如果需要提高扫描效率那么最好的方法是采用非阻塞的扫描模式,使用非阻塞模式我们可以在不使用多线程的情况下提高扫描速度。

非阻塞模式所依赖的核心函数为select()函数是一种用于多路I/O复用的系统调用,在Windows中提供了对该系统调用的支持。select()函数可以同时监听多个文件或套接字(socket)的可读、可写和出错状态,并返回有状态变化的文件或套接字的数量,在使用该函数时读者应率先调用ioctlsocket()函数,并设置FIONBIO套接字为非阻塞模式。

select 函数的基本语法如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

参数解释:

  • nfds:需要监听的文件或套接字最大编号加1
  • readfds:可读文件或套接字集合
  • writefds:可写文件或套接字集合
  • exceptfds:出错文件或套接字集合
  • timeout:超时时间,如果为NULL,则表示一直等待直到有事件发生

select 函数会阻塞进程,直到在需要监听的文件或套接字中有一个或多个文件或套接字发送了需要监听的事件,或者超时时间到达。当select()函数返回时,可以通过fd_set集合来查询有状态变化的文件或套接字。

select 函数的原理是将调用进程的文件或套接字加入内核监测队列,等待事件发生。当某个文件或套接字有事件发生时,内核会将其添加到内核缓冲区中,同时在返回时告诉进程有哪些套接字可以进行I/O操作,进程再根据文件或套接字的状态进行相应的处理。使用select()函数可以大大提高I/O操作的效率,减少资源占用。

如下代码实现的是一段简单的端口扫描程序,用于检查目标主机的一段端口范围内是否有端口处于开放状态。该函数中通过设置fd_set类型的掩码(mask)并加入套接字,使用select()函数查询该套接字的可写状态,并设置超时时间为1毫秒,如果返回值为0,则目标端口未开放,继续下一个端口的扫描。如果返回值为正数,则目标端口已成功连接(开放),输出扫描结果并继续下一个端口的扫描。

该代码中使用了非阻塞套接字和select()函数的组合来实现非阻塞IO。非阻塞套接字可以使程序不会在等待数据到来时一直阻塞,而是可以在等待数据到来的同时进行其他操作,从而提高程序的效率。select()函数则可以同时等待多个套接字的数据到来,从而使程序更加高效地进行I/O操作。

// 非阻塞端口探测
void PortScan(char *address, int StartPort, int EndPort)
{SOCKADDR_IN ServAddr;TIMEVAL TimeOut;FD_SET mask;TimeOut.tv_sec = 0;// 设置超时时间为500毫秒TimeOut.tv_usec = 1000;// 指定模式unsigned long mode = 1;// 循环扫描端口for (int port = StartPort; port <= EndPort; port++){SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);ServAddr.sin_family = AF_INET;ServAddr.sin_addr.S_un.S_addr = inet_addr(address);ServAddr.sin_port = htons(port);FD_ZERO(&mask);FD_SET(sock, &mask);// 设置为非阻塞模式ioctlsocket(sock, FIONBIO, &mode);connect(sock, (struct sockaddr *)&ServAddr, sizeof(ServAddr));// 查询可写入状态 如果不为0则说明这个端口是开放的int ret = select(0, 0, &mask, 0, &TimeOut);if (ret != 0 && ret != -1){printf("扫描地址: %-13s --> 端口: %-5d --> 状态: [Open] \n", address, port);}else{printf("扫描地址: %-13s --> 端口: %-5d --> 状态: [Close] \n", address, port);}}
}int main(int argc, char *argv[])
{char *Addr[2] = { "192.168.1.1", "192.168.1.10" };WSADATA wsa;if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0){exit(0);}for (int x = 0; x < 2; x++){PortScan(Addr[x], 1, 255);}WSACleanup();system("pause");return 0;
}

读者可自行编译并运行上述代码片段,默认会扫描Addr[2]数组内的两个IP地址的1-255端口范围开放情况,读者可感觉到效率上变得快了许多,输出效果如下图所示;

上述代码虽然增加的扫描速度但是还可以进一步优化,我们可以通过增加信号机制,通过使用信号可以很好的控制扫描并发连接数,增加了线程控制将会使扫描器更加稳定,同时我们还引用了多线程模式,通过两者的结合可以极大的提高扫描质量和效率。

基于信号的端口扫描,也称为异步IO端口扫描,是一种高效的端口扫描技术,可以利用操作系统的信号机制提高网络I/O的效率。基于信号的端口扫描具有非阻塞和异步的特性,可以最大限度地提高网络I/O效率,同时在大并发量下表现出更好的性能。但是,使用时需要小心处理信号的相关问题,避免死锁和数据不一致。

#include <stdio.h>
#include <winsock2.h>#pragma comment (lib, "ws2_32")typedef struct _THREAD_PARAM
{char *HostAddr;             // 扫描主机DWORD dwStartPort;          // 端口号HANDLE hEvent;              // 事件句柄HANDLE hSemaphore;          // 信号量句柄
}THREAD_PARAM;// 最大线程数,用于控制信号量数量
#define MAX_THREAD 10// 线程扫描函数
DWORD WINAPI ScanThread(LPVOID lpParam)
{// 拷贝传递来的扫描参数THREAD_PARAM ScanParam = { 0 };MoveMemory(&ScanParam, lpParam, sizeof(THREAD_PARAM));// 设置信号SetEvent(ScanParam.hEvent);WSADATA wsa;WSAStartup(MAKEWORD(2, 2), &wsa);// 初始化套接字SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);sockaddr_in sockaddr;// 填充扫描地址与端口sockaddr.sin_family = AF_INET;sockaddr.sin_addr.S_un.S_addr = inet_addr(ScanParam.HostAddr);sockaddr.sin_port = htons(ScanParam.dwStartPort);// 开始连接if (connect(s, (SOCKADDR*)&sockaddr, sizeof(SOCKADDR)) == 0){printf("地址: %-16s --> 端口: %-5d --> 信号量: %-5d 状态: [Open] \n",ScanParam.HostAddr, ScanParam.dwStartPort, ScanParam.hSemaphore);}else{printf("地址: %-16s --> 端口: %-5d --> 信号量: %-5d 状态: [Close] \n",ScanParam.HostAddr, ScanParam.dwStartPort, ScanParam.hSemaphore);}closesocket(s);WSACleanup();// 释放一个信号量ReleaseSemaphore(ScanParam.hSemaphore, 1, NULL);return 0;
}int main(int argc, char *argv[])
{// 线程参数传递THREAD_PARAM ThreadParam = { 0 };// 设置线程信号SetEvent(ThreadParam.hEvent);// 创建事件HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);// 创建信号HANDLE hSemaphore = CreateSemaphore(NULL, MAX_THREAD, MAX_THREAD, NULL);ThreadParam.hEvent = hEvent;ThreadParam.hSemaphore = hSemaphore;ThreadParam.HostAddr = "59.110.117.109";for (DWORD port = 1; port < 4096; port++){// 判断信号量DWORD dwWaitRet = WaitForSingleObject(hSemaphore, 200);if (dwWaitRet == WAIT_OBJECT_0){ThreadParam.dwStartPort = port;// 启动扫描线程HANDLE hThread = CreateThread(NULL, 0, ScanThread, (LPVOID)&ThreadParam, 0, NULL);// 等待事件WaitForSingleObject(hEvent, INFINITE);// 重置信号ResetEvent(hEvent);}else if (dwWaitRet == WAIT_TIMEOUT){continue;}}system("pause");return 0;
}

读者可自行编译并运行上述代码,将对特定IP地址进行端口探测,每次启用10个线程,即实现了控制线程并发,又实现了端口多线程扫描效果,如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/e9090338.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

这篇关于16.1 Socket 端口扫描技术的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/258301

相关文章

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

金融业开源技术 术语

金融业开源技术  术语 1  范围 本文件界定了金融业开源技术的常用术语。 本文件适用于金融业中涉及开源技术的相关标准及规范性文件制定和信息沟通等活动。

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

系统架构设计师: 信息安全技术

简简单单 Online zuozuo: 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo : 文章目录 系统架构设计师: 信息安全技术前言信息安全的基本要素:信息安全的范围:安全措施的目标:访问控制技术要素:访问控制包括:等保

前端技术(七)——less 教程

一、less简介 1. less是什么? less是一种动态样式语言,属于css预处理器的范畴,它扩展了CSS语言,增加了变量、Mixin、函数等特性,使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 ,也可以借助Node.js在服务端运行。 less的中文官网:https://lesscss.cn/ 2. less编译工具 koala 官网 http://koala-app.

Spring的设计⽬标——《Spring技术内幕》

读《Spring技术内幕》第二版,计文柯著。 如果我们要简要地描述Spring的设计⽬标,可以这么说,Spring为开发者提供的是⼀个⼀站式的轻量级应⽤开发框架(平台)。 作为平台,Spring抽象了我们在 许多应⽤开发中遇到的共性问题;同时,作为⼀个轻量级的应⽤开发框架,Spring和传统的J2EE开发相⽐,有其⾃⾝的特点。 通过这些⾃⾝的特点,Spring充分体现了它的设计理念:在

java线程深度解析(六)——线程池技术

http://blog.csdn.net/Daybreak1209/article/details/51382604 一种最为简单的线程创建和回收的方法: [html]  view plain copy new Thread(new Runnable(){                @Override               public voi

java线程深度解析(二)——线程互斥技术与线程间通信

http://blog.csdn.net/daybreak1209/article/details/51307679      在java多线程——线程同步问题中,对于多线程下程序启动时出现的线程安全问题的背景和初步解决方案已经有了详细的介绍。本文将再度深入解析对线程代码块和方法的同步控制和多线程间通信的实例。 一、再现多线程下安全问题 先看开启两条线程,分别按序打印字符串的

SSM项目使用AOP技术进行日志记录

本步骤只记录完成切面所需的必要代码 本人开发中遇到的问题: 切面一直切不进去,最后发现需要在springMVC的核心配置文件中中开启注解驱动才可以,只在spring的核心配置文件中开启是不会在web项目中生效的。 之后按照下面的代码进行配置,然后前端在访问controller层中的路径时即可观察到日志已经被正常记录到数据库,代码中有部分注释,看不懂的可以参照注释。接下来进入正题 1、导入m

嵌入式技术的核心技术有哪些?请详细列举并解释每项技术的主要功能和应用场景。

嵌入式技术的核心技术包括处理器技术、IC技术和设计/验证技术。 1. 处理器技术    通用处理器:这类处理器适用于不同类型的应用,其主要特征是存储程序和通用的数据路径,使其能够处理各种计算任务。例如,在智能家居中,通用处理器可以用于控制和管理家庭设备,如灯光、空调和安全系统。    单用途处理器:这些处理器执行特定程序,如JPEG编解码器,专门用于视频信息的压缩或解压。在数字相机中,单用途