nmap-1.51 源码解析 - 前言 && 端口的选择解析
字数 1672 2025-08-11 21:26:18
Nmap 1.51 源码解析:端口选择机制详解
1. 源码阅读目的与方法
1.1 阅读目的
- 深入理解 Nmap 的核心扫描技术
- 学习 Nmap 的编码风格和实现方式
- 复用相关模块到自己的代码中
1.2 阅读方法
- 从
nmap.c中的main函数开始顺序阅读 - 以 Nmap 提供的参数为模块进行分解
- 逐个实现小模块功能,逐步构建整体理解
2. 端口选择机制概述
Nmap 提供了两种主要的端口选择方式:
- 快速扫描模式 (
-F参数):从/etc/services文件中获取端口 - 自定义端口模式 (
-p参数):用户手动指定端口范围
重要限制:-F 和 -p 参数不能同时使用,否则会导致冲突。
3. 快速扫描模式 (-F 参数)
3.1 函数原型
unsigned short *getfastports(int tcpscan, int udpscan)
3.2 参数说明
tcpscan:是否进行 TCP 扫描udpscan:是否进行 UDP 扫描
3.3 实现逻辑
- 打开
/etc/services文件 - 逐行解析文件内容
- 根据协议类型(TCP/UDP)筛选端口
- 将选中的端口存入数组返回
3.4 关键代码解析
while(fgets(line, 80, fp)) {
res = sscanf(line, "%*s %u/%s", &portno, proto);
if (res == 2 && portno != 0 && portno != lastport) {
lastport = portno;
if (tcpscan && proto[0] == 't')
ports[portindex++] = portno;
else if (udpscan && proto[0] == 'u')
ports[portindex++] = portno;
}
}
3.5 特殊处理
- TCP 端口:几乎获取所有 TCP 端口
- UDP 端口:跳过同时支持 TCP 和 UDP 的端口(如 DNS 53 端口)
3.6 辅助函数
void *safe_malloc(int size) {
void *mymem;
if (size < 0) fatal("Tried to malloc negative amount of memmory!!!");
if ((mymem = malloc(size)) == NULL)
fatal("Malloc Failed! Probably out of space.");
return mymem;
}
4. 自定义端口模式 (-p 参数)
4.1 函数原型
unsigned short *getpts(char *origexpr)
4.2 支持的格式
- 单端口:
-p8080 - 端口范围:
-p200-1024 - 混合指定:
-p8080,200-255 - 开放范围:
-p60000-(表示60000-65535)
4.3 实现逻辑
- 去除输入字符串中的空格
- 按逗号分隔不同的端口指定
- 解析每个部分的端口范围
- 将选中的端口存入数组返回
4.4 关键代码解析
while((p = strchr(expr, ','))) {
*p = '\0';
if (*expr == '-') {
start = 1;
end = atoi(expr+1);
} else {
start = end = atoi(expr);
if ((q = strchr(expr, '-')) && *(q+1)) {
end = atoi(q+1);
} else if (q && !*(q+1)) {
end = 65535;
}
}
// 添加端口到数组
for(j=start; j <= end; j++) {
ports[i++] = j;
}
expr = p + 1;
}
4.5 特殊处理
-开头:表示从1到指定端口(如-100表示1-100)-结尾:表示从指定端口到65535(如60000-)- 中间
-:表示端口范围(如20-25)
5. 实现对比
| 特性 | getfastports (快速扫描) | getpts (自定义端口) |
|---|---|---|
| 数据来源 | /etc/services 文件 | 用户输入 |
| TCP 端口处理 | 获取几乎所有端口 | 精确获取指定范围 |
| UDP 端口处理 | 跳过TCP/UDP共用端口 | 无特殊处理 |
| 性能考虑 | 优化UDP扫描速度 | 完全由用户控制 |
| 使用场景 | 快速扫描常见服务 | 精确扫描目标端口 |
6. 开发注意事项
- 内存管理:使用
safe_malloc确保内存分配安全 - 错误处理:对非法输入进行严格检查
- 性能优化:避免重复端口和不必要的扫描
- 协议区分:正确处理TCP和UDP端口的不同需求
7. 历史背景
- 早期版本中UDP扫描性能问题突出,因此设计了特殊的过滤逻辑
getpts函数命名源于getports名称已被占用- 开发者曾考虑使用负数表示UDP端口,但最终放弃该方案
8. 实际应用示例
8.1 快速扫描TCP端口
unsigned short *ports = getfastports(1, 0);
8.2 自定义端口扫描
unsigned short *ports = getpts("20-25,80,443,60000-");
8.3 完整流程
- 根据参数选择端口获取方式
- 调用相应函数获取端口数组
- 进行扫描操作
- 释放相关资源
9. 总结
Nmap 1.51 的端口选择机制体现了以下设计原则:
- 灵活性:提供自动和手动两种端口选择方式
- 实用性:针对不同协议优化扫描策略
- 健壮性:严格的输入验证和错误处理
- 高效性:优化内存使用和扫描效率
通过深入理解这些机制,可以更好地使用Nmap进行网络扫描,也能在自己的网络工具中借鉴这些优秀的设计思路。