fennishias/Port-Scanner-C

GitHub: fennishias/Port-Scanner-C

一个基于C语言的原始套接字端口扫描器,用于网络安全评估。

Stars: 0 | Forks: 0

# 端口扫描器-C # 在C语言中构建原始套接字端口扫描器 我是如何从将Nmap封装在C程序中发展到编写自己的TCP连接扫描器的 ## 为什么我要构建这个 我想了解端口扫描器实际上是如何工作的。大多数教程只是教你如何**运行**Nmap——但我想知道Nmap本身在做什么。所以我从围绕Nmap的简单C封装开始,然后从头开始使用原始套接字重写了它。 *"你的C程序在封装Nmap时永远不会触及一个网络数据包。它纯粹是一个智能接口,组装正确的命令并执行它。"* ## 第1版——在C中封装Nmap 它从用户那里读取目标IP和端口范围,使用`sprintf()`构建Nmap命令字符串,然后使用`system()`运行它。 ``` #include //printf, fgets #include //system() #include //strlen int main(){ char target[100]; //stores the IP/hostname the user types char ports[50]; //stores the port range char command[300]; //stores the final nmap command //This asks the user for a target printf("Enter target IP or hostname:"); fgets(target, sizeof(target),stdin); //This removes the newline character that fgets add at the end target[strlen(target) -1] = '\0'; //This asks the user for ports to scan printf("Enter port range (1 - 10000):"); fgets(ports,sizeof(ports),stdin); //This removes the newline character ports[strlen(ports) -1] ='\0'; //Here we build an nmap scan command sprintf(command, "nmap -p %s %s", ports,target); //This run the command system(command); return 0; } ``` **编译和运行:** ``` gcc -o scanner port_scanner.c sudo ./scanner ``` ## 第2版——原始套接字(无Nmap) 真正的版本使用`socket()`、`connect()`和`setsockopt()`从Linux套接字API。对于每个端口,它尝试完整的TCP握手——如果连接成功,端口是开放的。我试了几个端口。 ### 逐步工作原理 | 步骤 | 函数 | 做什么 | |------|----------|--------------| | 1 | `socket()` | 打开一个TCP套接字——就像在拨打电话之前拿起电话 | | 2 | `setsockopt()` | 设置1秒的超时,以便过滤端口不会永远挂起 | | 3 | `connect()` | 如果端口开放,返回`0`;如果关闭或过滤,返回`-1` | | 4 | `close()` | 在每次检查后清理文件描述符 | ### 完整代码 ``` #include #include #include #include //close() #include //It calls socket(), connect() #include //for SO_RCVTIMED AND SO_SNDTIMED #include // used in sockaddr_in , IPV4 socket addresses #include // inet_addr() #include // fcntl() for non-blocking #include // errno #include #define TIMEOUT_SEC 1 // seconds to wait per port //Defining few port names for nicer output const char *port_name(int port) { switch (port) { case 21: return "FTP"; case 22: return "SSH"; case 23: return "Telnet"; case 25: return "SMTP"; case 53: return "DNS"; case 80: return "HTTP"; case 110: return "POP3"; case 143: return "IMAP"; case 443: return "HTTPS"; case 3306: return "MYSQL"; case 3389: return "RDP"; case 5432: return "PostgresSQL"; default: return "unknown"; } } /* scan_port() - tries to TCP-connect to ip:port, return 1 if open and 0 if closed/filtered */ int scan_port(const char *ip, int port){ int sock; struct sockaddr_in target; struct timeval timeout; /* first we create a TCP socket */ sock =socket(AF_INET,SOCK_STREAM,0); if (sock <0) return 0; /*second we creste a timeout so we dont wait forever*/ timeout.tv_sec =TIMEOUT_SEC; timeout.tv_usec =0; setsockopt(sock, SOL_SOCKET,SO_RCVTIMEO,&timeout, sizeof(timeout)); setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); /*Thirdly we fill in the destination address*/ memset(&target, 0, sizeof(target)); target.sin_family =AF_INET; //IPV4 target.sin_port = htons(port); //port in network byte order target.sin_addr.s_addr = inet_addr(ip); //IP address //Lastly on the TCP Socket we try to connect- if it succeeds, the port is open int result = connect(sock, (struct sockaddr *)&target, sizeof(target)); /* finally we close the socket after checking*/ close(sock); return(result ==0); //0 means connected =open } int main(){ char ip[100]; int start_port, end_port; int open_count= 0; //Input from user printf("\n == Mariquita Port Scanner == \n\n"); printf(" Target IP:"); fgets(ip, sizeof(ip), stdin); ip[strlen(ip) -1] = '\0'; printf(" start port:"); scanf("%d", &start_port); printf(" End Port: "); scanf("%d", &end_port); /* validating range */ if (start_port < 1||end_port > 65535 || start_port > end_port) { printf("\n invalid port range, use 1-65535. \n\n"); return 1; } printf("\nscanning%sports %d-%d ... \n", ip, start_port, end_port); printf(" %-8d%-10s%s\n", "PORT", "STATE", "SERVICE"); /*Scanning each port*/ for (int port=start_port; port <=end_port; port++) { if (scan_port(ip,port)) { printf(" %-8d%-10s%s\n",port,"OPEN",port_name(port)); open_count++; } } /*output all ports*/ printf(" ----------------------------------\n"); printf(" %d open ports(s) found\n\n",open_count); return 0; } ``` **编译和运行:** ``` gcc -o Mscanner raw_scanner.c ./Mscanner ``` **示例输出:** ``` === Mariquita Port Scanner === Target IP: 192.168.1.1 Start port: 1 End port: 1000 Scanning 192.168.1.1 ports 1-1000 ... PORT STATE SERVICE ---------------------------------------- 22 OPEN SSH 80 OPEN HTTP 443 OPEN HTTPS ---------------------------------------- 3 open port(s) found. ``` ## 我学到了什么 从头编写端口扫描器让我比任何教程都更了解网络。关键要点是: **每个网络服务都是一个监听端口的进程。** 当`connect()`成功时,另一端接受了握手。当它因`ECONNREFUSED`失败时,操作系统本身拒绝了它。当它超时时,防火墙默默地丢弃了数据包。 **我们的扫描器和Nmap之间的区别**在于Nmap执行的是SYN扫描——它只发送TCP握手的第一个数据包,永远不会完成连接。这需要root权限,但速度更快,更隐蔽。我们的版本执行的是完整的连接扫描,无需root权限,但稍微更容易被检测到。 ## 法律声明 仅扫描你拥有或明确书面授权测试的系统。未经授权的端口扫描可能在你的国家是非法的。 ## 📋 要求 - GCC编译器 - Linux(在Kali Linux上测试过) - 不需要外部库 ``` sudo apt install gcc # if not already installed ``` ## 可在Linux和MacOs系统上运行。 *在Kali Linux上构建 · 用C语言编写 · 无外部依赖*
标签:connect, C语言编程, DNS解析, Hpfeeds, Linux网络编程, Nmap, setsockopt, socket, TCP连接, 云资产清单, 代码示例, 内核驱动, 原始套接字, 子域名枚举, 学习资源, 实践项目, 客户端加密, 底层原理, 开源项目, 情报分析, 技术交流, 技术博客, 技术探索, 技术社区, 教育项目, 数据分析, 数据管道, 数据统计, 端口扫描, 系统安全, 系统调用, 网络协议, 网络安全, 网络工具, 网络诊断, 虚拟驱动器, 计算机科学, 软件工程, 逆向工程, 隐私保护