
🎬 HoRain云小助手:个人主页
🔥 个人专栏: 《Linux 系列教程》《c语言教程》
⛺️生活的理想,就是为了理想的生活!
⛳️ 推荐
前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。
专栏介绍
| 专栏名称 | 专栏介绍 |
| 本专栏主要撰写C干货内容和编程技巧,让大家从底层了解C,把更多的知识由抽象到简单通俗易懂。 | |
| 本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘,一起解密网络协议在运行中协议的基本运行机制! | |
| 全面深入解析 docker 容器,从基础到进阶,涵盖原理、操作、实践案例,助您精通 docker。 | |
| 本专栏主要撰写Linux干货内容,从基础到进阶,知识由抽象到简单通俗易懂,帮你从新手小白到扫地僧。 | |
| 本专栏着重撰写Python相关的干货内容与编程技巧,助力大家从底层去认识Python,将更多复杂的知识由抽象转化为简单易懂的内容。 | |
| 本专栏主要是发布一些考试和练习题库(涵盖软考、HCIE、HRCE、CCNA等) |
目录

利用ICMP协议判断UDP端口状态,是一种基于网络协议底层交互的间接探测方法。其核心原理在于利用UDP协议无连接的特性,通过分析系统返回的特定ICMP错误消息来推断端口状态。
🧠 探测原理
这种方法的核心机制如下:
-
发送UDP探测包:向目标主机的特定UDP端口发送一个数据包。
-
触发ICMP响应:如果目标端口没有应用程序监听,目标主机的网络协议栈通常会根据RFC 792规范,生成一个 ICMP目标不可达(Type 3)消息,并且其中的代码(Code)为3,表示“端口不可达”。
-
分析结果:
-
如果收到了 ICMP Type 3, Code 3 的响应,则可以推断该UDP端口是关闭的(未开放)。
-
如果在设定的超时时间内没有收到任何ICMP响应,则可能意味着:
-
端口是开放的,并且服务(可能)静默丢弃了数据包。
-
端口可能是关闭的,但中间的防火墙或网络设备拦截了UDP探测包或返回的ICMP消息,导致响应无法传回。
-
-
下表对比了不同探测方法的原理和特点:
| 方法名称 | 原理 | 常用工具/示例 | 优点 | 缺点/局限性 |
|---|---|---|---|---|
| ICMP端口不可达 | 利用关闭端口返回ICMP错误消息的特性 | 原始套接字编程 | 原理明确,是协议标准行为 | 可靠性低(防火墙会过滤ICMP) |
| 协议特定交互 | 发送符合特定服务协议(如DNS、NTP)的合法请求,期待应用层响应 |
| 结果相对准确,可确认服务运行状态 | 仅适用于已知协议的服务端口 |
| 工具自动化扫描 | 工具结合多种探测技巧(如ICMP错误、协议探针)进行综合判断 |
| 功能全面,自动化程度高,结果更可靠 | 扫描速度可能较慢 |
⚙️ 实现方式与示例
你可以通过以下两种主要方式来实现UDP端口存活状态的检测。
-
使用现有扫描工具
使用像Nmap这样的专业工具是最快捷、最有效的方式。它内部实现了我们讨论的ICMP检测逻辑以及其他更高级的启发式方法。
执行UDP扫描的基本命令是:
nmap -sU -p <端口号> <目标IP地址>Nmap会对结果进行综合判断,其输出结果可能包括:
-
open:端口开放。 -
closed:端口关闭(明确收到了ICMP端口不可达消息)。 -
open|filtered:未收到响应,端口可能开放但被防火墙过滤,也可能只是服务不响应。 -
closed|filtered:难以判断是关闭还是被过滤。
-
-
编程实现(以Go语言示例)
如果需要将探测功能集成到自己的应用程序中,或者想更深入理解过程,可以通过编程实现。以下是一个简化的Go语言示例,使用了
golang.org/x/net/icmp库:package main import ( "fmt" "net" "time" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" ) func checkUDPPortWithICMP(targetAddr string, timeout time.Duration) (bool, error) { // 解析目标地址 udpAddr, err := net.ResolveUDPAddr("udp", targetAddr) if err != nil { return false, fmt.Errorf("解析地址失败: %v", err) } // 1. 创建UDP连接,用于发送探测包 udpConn, err := net.ListenUDP("udp", nil) if err != nil { return false, fmt.Errorf("创建UDP连接失败: %v", err) } defer udpConn.Close() // 2. 创建ICMP原始套接字,用于监听ICMP回复 icmpConn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") if err != nil { return false, fmt.Errorf("创建ICMP监听失败: %v", err) } defer icmpConn.Close() icmpConn.SetReadDeadline(time.Now().Add(timeout)) // 3. 发送UDP探测包 if _, err := udpConn.WriteTo([]byte("Probe"), udpAddr); err != nil { return false, fmt.Errorf("发送UDP包失败: %v", err) } // 4. 尝试读取ICMP回复 reply := make([]byte, 1500) for { n, peer, err := icmpConn.ReadFrom(reply) if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { // 超时,未收到ICMP端口不可达,推断端口可能开放 return true, nil } return false, err } // 检查回复是否来自目标主机 if peer.String() != udpAddr.IP.String() { continue } // 解析ICMP消息 msg, err := icmp.ParseMessage(ipv4.ICMPType, reply[:n]) if err != nil { continue } // 5. 判断是否为“端口不可达”消息 if msg.Type == ipv4.ICMPTypeDestinationUnreachable { if code, ok := msg.Code.(int); ok && code == 3 { // Code 3 表示端口不可达 return false, fmt.Errorf("端口不可达 (ICMP Type 3, Code 3)") } } } } func main() { target := "192.168.1.1:161" // 示例目标地址和UDP端口 isReachable, err := checkUDPPortWithICMP(target, 2*time.Second) if err != nil { fmt.Printf("端口可能关闭或出错: %v\n", err) } else { fmt.Println("端口可能开放(未收到ICMP端口不可达响应)") } }重要提示:在类Unix系统上运行此类需要创建原始套接字(Raw Socket)的程序时,通常需要管理员权限(如sudo)。
⚠️ 注意事项与局限性
尽管ICMP探测方法有其价值,但在实际应用中必须认识到它的局限性:
-
可靠性受限:这是最主要的问题。许多网络防火墙会配置为不发送ICMP端口不可达消息,或者直接丢弃UDP探测包。这会导致即使端口是关闭的,你也收不到任何响应,从而可能误判为端口开放(即
open|filtered状态)。 -
速率限制:为了防止ICMP洪水攻击,操作系统通常会对ICMP错误消息的生成速率进行限制(例如,Linux下默认每4秒最多80个)。这会导致大规模扫描速度非常慢。
-
需要权限:创建原始ICMP套接字通常需要root或管理员权限。
-
结果推断性:此方法只能相对可靠地判断端口“关闭”,而对于“开放”的判断是推断性的(基于没有收到否定应答),不如TCP的SYN-ACK握手那样明确。
希望这些信息能帮助你理解和运用ICMP协议来判断UDP端口的存活状态。如果你有特定的使用场景(例如,想探测内网的特定服务端口),我可以提供更具体的工具命令或脚本思路。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍
🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

1187

被折叠的 条评论
为什么被折叠?



