16.1 Socket 端口扫描技术

这篇具有很好参考价值的文章主要介绍了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数据包,则表示连接请求失败,目标端口为未开放状态。通过此方式,程序可以快速检测多个端口是否开放,该程序运行后输出效果如下图所示;

16.1 Socket 端口扫描技术

上述代码虽然可以实现端口扫描,但是读者应该会发现此方法扫描很慢,这是因为扫描器每次只能链接一个主机上的端口只有当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端口范围开放情况,读者可感觉到效率上变得快了许多,输出效果如下图所示;

16.1 Socket 端口扫描技术

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

基于信号的端口扫描,也称为异步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个线程,即实现了控制线程并发,又实现了端口多线程扫描效果,如下图所示;

16.1 Socket 端口扫描技术文章来源地址https://www.toymoban.com/news/detail-710900.html

到了这里,关于16.1 Socket 端口扫描技术的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 关于socket的地址复用和端口复用技术与UDP并发

     一. socket五元组 linux: setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,(const void *)reuse , sizeof(int)); setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,(const void *)reuse , sizeof(int)); windows: setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const char*)reuse , sizeof(int));  socket是系统级资源(区别于进程级和线程级,在本机上是全局唯一的

    2024年02月05日
    浏览(26)
  • ElasticSearch 学习9 spring-boot ,elasticsearch7.16.1实现中文拼音分词搜索

    一、elasticsearch官网下载:Elasticsearch 7.16.1 | Elastic 二、拼音、ik、繁简体转换插件安装 ik分词:GitHub - medcl/elasticsearch-analysis-ik: The IK Analysis plugin integrates Lucene IK analyzer into elasticsearch, support customized dictionary. 拼音分词:GitHub - medcl/elasticsearch-analysis-pinyin: This Pinyin Analysis plugin is

    2024年01月22日
    浏览(41)
  • docker搭建最新ELFK分布式日志收集系统(elasticsearch+logstash+filebeats+kibana7.16.1)

    随着分布式项目的集群部署,日志的存储也分散开来,在日后出现问题进行日志定位时就会出现很困难,服务器很多会做负载均衡,这样最终请求所落在的服务器也随机起来,所以好的方式就是集中收集起来,不需要一台一台服务器去查,方便查看。 ELFK是Elasticsearch+Logstash+F

    2024年02月08日
    浏览(37)
  • electron+vue3全家桶+vite项目搭建【16.1】electron多窗口,pinia状态同步,扩展store方法,主动同步pinia的状态【推荐】

    demo项目地址 我们之前写了一个自动同步pinia状态的插件,可以参考如下文章 electron+vue3全家桶+vite项目搭建【16】electron多窗口,pinia状态无法同步更新问题解决 这里面有一个较大的弊端,就是pinia中的store,只要其中的某个属性修改,就会触发这个store的全量更新,当我们有一

    2024年02月11日
    浏览(54)
  • 1.3端口扫描:利用Nmap工具进行端口扫描

    1、预备知识:Nmap介绍         nmap的功能:端口扫描、主机发现、服务/版本识别、操作系统识别、网络路由跟踪、Nmap脚本引擎;         nmap的扫描方式:Half-open scanning,默认扫描方式;TCP connect;TCP ACK scanning;TCP FIN/Xmass/NULL scanning。 2、实验条件:         攻击机:

    2024年02月06日
    浏览(45)
  • 关于nmap端口扫描与阻止恶意端口扫描的实验

    准备环境 Nmap Nmap用于允许系统管理员查看一个大的网络系统有哪些主机,以及其上运行何种服务。它支持多种协议的扫描,如UDP、TCP connect()、TCP SYN(half open)、ftp proxy(bounce attack)、Reverse-ident、ICMP(ping sweep)、FIN、ACK sweep、Xmas Tree、SYN sweep和Null扫描。Nmap还提供了一些实

    2023年04月09日
    浏览(28)
  • 主动扫描-Nmap-端口、系统、服务扫描

    目录 一、使用Nmap进行端口扫描 1、关于端口的介绍 ① 个数 ② 作用 ③ 分类 2、Nmap中的各种端口扫描技术 ① SYN扫描 ② Connect扫描 ③ UDP扫描 ④ 端口扫描范围的确定 二、使用Nmap扫描目标系统 1、识别原理 2、参数与意义  3、远程判断目标系统的方法 三、使用Nmap扫描目标服务

    2024年02月09日
    浏览(36)
  • 网络扫描,端口扫描,漏洞扫描,带你认识nmap

    nmap是一款用于网络发现和安全评估的开源工具。它可以扫描网络主机,了解主机的开放端口和服务信息,甚至可以对操作系统进行识别。 以下是nmap主要用途: 网络发现:nmap可以扫描网络上的主机,了解主机的IP地址、MAC地址等信息。 端口扫描:nmap可以扫描网络上的主机,

    2024年02月13日
    浏览(31)
  • python3-端口扫描(TCP connect扫描,SYN扫描,FIN扫描)

    利用python3进行端口扫描,TCP的方式有connect扫描,SYN扫描,FIN扫描,NULL扫描,ACK扫描,Xmas,windows扫描。本次展示前三种,代码仅供参数: 扫描方式 1:TCP connect扫描 扫描端与目标主机建立tcp连接,完成三次握手后,扫描端主动关闭连接(缺点:目标主机会记录下连接内容) 扫

    2023年04月08日
    浏览(36)
  • 如何扫描网址ip的特定端口或扫描全部网段

    一 nc sudo apt install netcat-openbsd 或sudo apt install ncat 二 telnet sudo apt install telnet 转义符为 ‘^]’. ctrl + ] 之后quit 或 q 三 nmap 扫描TCP开放端口 扫描UDP开放端口 需root权限 nmap 扫描整个网段

    2024年01月18日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包