HoRain云--UDP端口探测:ICMP协议实战指南

  

🎬 HoRain云小助手个人主页

 🔥 个人专栏: 《Linux 系列教程》《c语言教程

⛺️生活的理想,就是为了理想的生活!


⛳️ 推荐

前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。

专栏介绍

专栏名称

专栏介绍

《C语言》

本专栏主要撰写C干货内容和编程技巧,让大家从底层了解C,把更多的知识由抽象到简单通俗易懂。

《网络协议》

本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘,一起解密网络协议在运行中协议的基本运行机制!

《docker容器精解篇》

全面深入解析 docker 容器,从基础到进阶,涵盖原理、操作、实践案例,助您精通 docker。

《linux系列》

本专栏主要撰写Linux干货内容,从基础到进阶,知识由抽象到简单通俗易懂,帮你从新手小白到扫地僧。

《python 系列》

本专栏着重撰写Python相关的干货内容与编程技巧,助力大家从底层去认识Python,将更多复杂的知识由抽象转化为简单易懂的内容。

《试题库》

本专栏主要是发布一些考试和练习题库(涵盖软考、HCIE、HRCE、CCNA等)

目录

⛳️ 推荐

专栏介绍

🧠 探测原理

⚙️ 实现方式与示例

⚠️ 注意事项与局限性


img

利用ICMP协议判断UDP端口状态,是一种基于网络协议底层交互的间接探测方法。其核心原理在于利用UDP协议无连接的特性,通过分析系统返回的特定ICMP错误消息来推断端口状态。

🧠 探测原理

这种方法的核心机制如下:

  • 发送UDP探测包:向目标主机的特定UDP端口发送一个数据包。

  • 触发ICMP响应:如果目标端口没有应用程序监听,目标主机的网络协议栈通常会根据RFC 792规范,生成一个 ICMP目标不可达(Type 3)消息,并且其中的代码(Code)为3,表示“端口不可达”。

  • 分析结果

    • 如果收到了 ICMP Type 3, Code 3​ 的响应,则可以推断该UDP端口是关闭的(未开放)。

    • 如果在设定的超时时间内没有收到任何ICMP响应,则可能意味着:

      1. 端口是开放的,并且服务(可能)静默丢弃了数据包。

      2. 端口可能是关闭的,但中间的防火墙或网络设备拦截了UDP探测包或返回的ICMP消息,导致响应无法传回。

下表对比了不同探测方法的原理和特点:

方法名称

原理

常用工具/示例

优点

缺点/局限性

ICMP端口不可达

利用关闭端口返回ICMP错误消息的特性

原始套接字编程

原理明确,是协议标准行为

可靠性低(防火墙会过滤ICMP)

协议特定交互

发送符合特定服务协议(如DNS、NTP)的合法请求,期待应用层响应

dig @8.8.8.8 example.com

结果相对准确,可确认服务运行状态

仅适用于已知协议的服务端口

工具自动化扫描

工具结合多种探测技巧(如ICMP错误、协议探针)进行综合判断

nmap -sU -p 53 <目标IP>

功能全面,自动化程度高,结果更可靠

扫描速度可能较慢

⚙️ 实现方式与示例

你可以通过以下两种主要方式来实现UDP端口存活状态的检测。

  1. 使用现有扫描工具

    使用像Nmap这样的专业工具是最快捷、最有效的方式。它内部实现了我们讨论的ICMP检测逻辑以及其他更高级的启发式方法。

    执行UDP扫描的基本命令是:

    nmap -sU -p <端口号> <目标IP地址>

    Nmap会对结果进行综合判断,其输出结果可能包括:

    • open:端口开放。

    • closed:端口关闭(明确收到了ICMP端口不可达消息)。

    • open|filtered:未收到响应,端口可能开放但被防火墙过滤,也可能只是服务不响应。

    • closed|filtered:难以判断是关闭还是被过滤。

  2. 编程实现(以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 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值