本项目采用C语言开发,旨在创建一个高效的端口扫描工具。该工具能够快速检测目标主机开放的服务端口,适用于网络安全性评估和维护。
编写端口扫描软件的代码在网上有很多资源可供参考,但这些代码可能并不适合初学者使用。一些代码较长且包含了多线程技术的应用,这对编程基础不深的人来说可能会感到困惑;另一些则速度较慢,在实际应用中效果不佳。今天我将介绍自己在学习Winsock后编写的一款端口扫描软件。
端口扫描的基本原理很简单:从起始端口号到结束端口号逐一检查每一个端口,并找出打开的那些进行输出即可,大致逻辑如下:
```c
for(CurrPort=StartPort;CurrPort<=EndPort;CurrPort++) {
scan的执行体;
}
```
该软件未使用多线程技术,因此无需考虑复杂的并发概念。接下来将从两个方面来介绍如何实现这款端口扫描器:一是如何确定哪些端口是打开状态;二是怎样提升扫描效率。
### 一、识别开放端口
在讨论具体方法之前先了解一下`connect()`函数的作用。该函数用于通过指定的套接字与特定IP地址和端口号建立连接,其原型为:
```c
int connect(SOCKET s, const struct sockaddr FAR* name, int namelen);
```
参数`s`表示需要进行连接操作的套接字句柄;`name`指向一个包含要连接服务器IP地址及端口信息的sockaddr_in结构体;而`namelen`则指定了该结构体的实际大小。如果成功建立链接,函数将返回0值,否则会返回SOCKET_ERROR。
利用此特性,在尝试与目标主机上的每一个待测端口进行通信时,可以通过检查connect()的结果来判断相应端口是否开放。下面给出实现这段逻辑的代码片段,并附有详细注释:
```c
int scan(char *Ip, int StartPort, int EndPort) {
clock_t StartTime,EndTime; // 记录扫描开始和结束时间
float CostTime; // 总耗时
WSADATA wsa;
SOCKET s;
struct sockaddr_in server;
for(int CurrPort = StartPort; CurrPort <= EndPort; ++CurrPort) {
int ret;
WSAStartup(MAKEWORD(2, 2), &wsa); // 初始化Winsock库
memset(&server, 0x00, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr= inet_addr(Ip);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 创建TCP套接字
server.sin_port = htons(CurrPort);
ret = connect(s,(struct sockaddr *)&server,sizeof(server));
if (0 == ret) {
printf(%s:%d\n, Ip,CurrPort);
closesocket(s);
}
}
EndTime=clock();
CostTime=(float)(EndTime-StartTime)/CLOCKS_PER_SEC;
printf(Cost time: %f seconds.\n, CostTime);
WSACleanup(); // 清理Winsock环境
}
```
以上代码实现了端口扫描的核心逻辑,通过循环遍历指定范围内所有可能的TCP端口号,并尝试建立连接以判断它们是否开放。