nmap部分源码分析
字数 1565 2025-08-22 18:37:15
Nmap源码分析与关键技术解析
一、Nmap概述
Nmap(Network Mapper)是一款开源的网络探测和安全审计工具,主要用于:
- 主机发现
- 端口扫描
- 服务版本检测
- 操作系统识别
- 脚本扫描(NSE)
二、源码结构分析
Nmap源码主要目录结构:
.github/ # GitHub问题模板
docs/ # 文档
licenses/ # 许可证文件
mswin32/ # Windows支持
macosx/ # macOS支持
nbase/ # 基础库
ncat/ # 新版nc实现
ndiff/ # 扫描结果比较工具
nping/ # 网络探测工具
nselib/ # Lua脚本库
nsock/ # 并行Socket处理库
scripts/ # 常用扫描Lua脚本
tests/ # 测试代码
zenmap/ # Python图形界面
三、核心执行流程
1. 主入口
main.cc中的main()函数仅调用nmap_main()(位于nmap.cc中),所有功能都在nmap_main()中有入口。
2. 扫描阶段
Nmap的10个主要扫描阶段:
- 脚本预扫描(可选)
- 目标枚举
- 主机发现(ping扫描)
- 反向DNS解析
- 端口扫描
- 版本检测
- 操作系统检测
- Traceroute
- 脚本扫描
- 输出
四、关键技术实现
1. 端口扫描技术
(1) SYN扫描(半开放扫描)
if (o.synscan)
ultra_scan(Targets, &ports, SYN_SCAN);
实现原理:
- 发送SYN包
- 收到SYN/ACK则认为端口开放
- 收到RST则认为端口关闭
- 不完成完整TCP三次握手
(2) TCP连接扫描
if (o.connectscan)
ultra_scan(Targets, &ports, CONNECT_SCAN);
实现原理:
- 使用系统
connect()函数建立完整TCP连接 - 更可靠但速度较慢
(3) UDP扫描
if (o.udpscan)
ultra_scan(Targets, &ports, UDP_SCAN);
实现原理:
- 发送UDP数据包
- 根据ICMP端口不可达消息判断端口状态
(4) 空闲扫描(Idle Scan)
if (o.idlescan) {
for (targetno = 0; targetno < Targets.size(); targetno++) {
o.current_scantype = IDLE_SCAN;
idle_scan(Targets[targetno], ports.tcp_ports, ports.tcp_count,
o.idleProxy, &ports);
}
}
特点:
- 利用僵尸主机的IP ID递增特性
- 实现完全欺骗扫描
- 不暴露扫描者真实IP
(5) FTP弹跳扫描
if (o.bouncescan) {
for (targetno = 0; targetno < Targets.size(); targetno++) {
o.current_scantype = BOUNCE_SCAN;
if (ftp.sd <= 0) ftp_anon_connect(&ftp);
if (ftp.sd > 0)
bounce_scan(Targets[targetno], ports.tcp_ports,
ports.tcp_count, &ftp);
}
}
原理:
- 利用FTP协议的PORT/EPRT命令特性
- 通过FTP服务器代理连接第三方主机
2. 主机发现技术
void sendPingProbe(UltraScanInfo *USI, HostScanStats *hss) {
switch(pingprobe->type) {
case PS_CONNECTTCP: sendConnectScanProbe(...); break;
case PS_TCP: case PS_UDP: case PS_SCTP: case PS_PROTO:
case PS_ICMP: sendIPScanProbe(...); break;
case PS_ARP: sendArpScanProbe(...); break;
case PS_ND: sendNDScanProbe(...); break;
}
}
支持的探测方式:
- TCP连接探测
- ICMP Echo请求
- ARP探测(局域网)
- ND探测(IPv6邻居发现)
- UDP探测
- SCTP探测
3. 服务版本检测
if (o.servicescan) {
o.current_scantype = SERVICE_SCAN;
service_scan(Targets);
}
实现流程:
- 初始化所有探测(
AllProbes::service_scan_init()) - 创建服务组(
ServiceGroup) - 启动探测(
launchSomeServiceProbes()) - 处理结果(
processResults())
关键技术:
- 多协议支持(TCP/UDP/SSL)
- 并行探测
- 响应匹配(精确匹配和模糊匹配)
4. 操作系统检测
if (o.osscan) {
OSScan os_engine;
os_engine.os_scan(Targets);
}
检测方法:
- TCP/IP协议栈指纹识别
- 响应分析
- 支持IPv4和IPv6
5. 路由追踪
if (o.traceroute)
traceroute(Targets);
实现分为:
- 直接连接目标(
traceroute_direct) - 远程目标(
traceroute_remote)
技术要点:
- TTL递增
- ICMP/UDP/TCP探测
- 跳数分析
6. Nmap脚本引擎(NSE)
#ifndef NOLUA
if (o.script || o.scriptversion) {
script_scan(Targets, SCRIPT_SCAN);
}
#endif
关键组件:
- Lua虚拟机集成
- 脚本分类(pre-scan/scan/post-scan)
- 丰富的脚本库(
nselib/)
五、关键数据结构
1. 扫描目标表示
class Target {
// 目标主机信息
// 端口状态
// 扫描结果
};
2. 端口列表
struct scan_lists {
int tcp_count;
unsigned short *tcp_ports;
int udp_count;
unsigned short *udp_ports;
// 其他协议端口
};
3. 扫描统计
class HostScanStats {
// 主机扫描状态
// 探测计数
// 超时处理
};
六、网络通信实现
1. 原始套接字
rawsd = nmap_raw_socket();
2. 数据包构造
// TCP包构造
u8 *build_tcp_raw(const struct in_addr *source, const struct in_addr *victim,
int ttl, u16 ipid, u8 tos, bool df, const u8 *ipopt,
int ipoptlen, u16 sport, u16 dport, u32 seq, u32 ack,
u8 reserved, u8 flags, u16 window, u16 urp,
const u8 *tcpopt, int tcpoptlen, const char *data,
u16 datalen, u32 *packetlen);
3. 发送接收
send_ip_packet(USI->rawsd, ethptr, hss->target->TargetSockAddr(),
packet, packetlen);
七、性能优化技术
- 并行扫描控制(
o.max_parallelism) - 端口随机化(
o.randomize_ports) - 常用端口优先(
random_port_cheat) - 超时动态调整
- 速率控制
八、扩展开发指南
1. 添加新扫描类型
- 在
stype枚举中添加新类型 - 实现对应的
ultra_scan处理 - 添加命令行参数支持
2. 开发NSE脚本
- 使用Lua编写脚本
- 放置在
scripts/目录 - 实现必要的回调函数
九、安全注意事项
- 权限要求(原始套接字需要root/Administrator)
- 扫描行为可能触发安全设备警报
- 遵守当地法律法规
十、总结
Nmap通过其模块化设计和丰富的功能集,成为网络探测和安全评估的瑞士军刀。理解其源码可以帮助:
- 深入掌握网络扫描原理
- 定制化开发专用扫描工具
- 更好地防御网络探测行为
- 学习高质量网络编程实践