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连接, 云资产清单, 代码示例, 内核驱动, 原始套接字, 子域名枚举, 学习资源, 实践项目, 客户端加密, 底层原理, 开源项目, 情报分析, 技术交流, 技术博客, 技术探索, 技术社区, 教育项目, 数据分析, 数据管道, 数据统计, 端口扫描, 系统安全, 系统调用, 网络协议, 网络安全, 网络工具, 网络诊断, 虚拟驱动器, 计算机科学, 软件工程, 逆向工程, 隐私保护