{"id":15347693,"url":"https://github.com/seriouszyx/scannerrecognition","last_synced_at":"2025-07-11T17:39:43.895Z","repository":{"id":113680136,"uuid":"400552569","full_name":"seriouszyx/ScannerRecognition","owner":"seriouszyx","description":"网络扫描探测工具的分析与识别（Zmap | Angry IP Scanner | Masscan）","archived":false,"fork":false,"pushed_at":"2021-08-27T15:32:22.000Z","size":5695,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-11T15:11:22.928Z","etag":null,"topics":["angryip","masscan","zmap"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seriouszyx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-08-27T15:22:02.000Z","updated_at":"2025-03-12T03:01:32.000Z","dependencies_parsed_at":null,"dependency_job_id":"320240af-ad8d-47a2-8c23-ad4e584f2d92","html_url":"https://github.com/seriouszyx/ScannerRecognition","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/seriouszyx/ScannerRecognition","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouszyx%2FScannerRecognition","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouszyx%2FScannerRecognition/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouszyx%2FScannerRecognition/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouszyx%2FScannerRecognition/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seriouszyx","download_url":"https://codeload.github.com/seriouszyx/ScannerRecognition/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seriouszyx%2FScannerRecognition/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264863633,"owners_count":23675203,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angryip","masscan","zmap"],"created_at":"2024-10-01T11:38:16.717Z","updated_at":"2025-07-11T17:39:43.888Z","avatar_url":"https://github.com/seriouszyx.png","language":"Python","readme":"# 扫描探测工具识别\n\n网络扫描探测通常是发起网络入侵的第一步，攻击者可以利用扫描探测工具获取网络中的主机系统、TCP/UDP 端口的开放情况、子域名、网站指纹、WAF、CDN、中间件类别等重要信息，识别出存在安全漏洞的主机或系统，从而发起有针对性的网络入侵行为。此外，一些扫描工具同时具备漏洞利用的能力。因此，对网络扫描探测行为进行识别和研究，有利于及时发现网络攻击的前兆，发现网络攻击行为，快速定位网络服务中存在的漏洞，对网络安全防护工作十分有意义。\n\n# Zmap\n\n## 抓包分析\n\nZMap 被设计用来针对整个 IPv4 地址空间或其中的大部分实施综合扫描的工具。\n\n默认情况下，ZMap 会对于指定端口实施尽可能大速率的 TCP SYN 扫描。如下图所示，客户端在发送一个 SYN 包的时候，如果对方端口开放，就会发送一个 SYN-ACK，那么就表明这个端口开放，这时候我们发送 RST 包，防止占用对方资源；如果对方端口不开放，那么我们就会收到对方主机的 RST 包。\n\n![zmap.png](assets/zmap-20210827102120-zlgtynz.png)\n\n较为保守的情况下，对 10,000 个随机的地址的 80 端口以 10Mbps 的速度扫描，如下所示：\n\n![image.png](assets/image-20210820135020-04s278s.png)\n\n在生成的 csv 结果文件中，以下 IP 地址的 80 端口开放：\n\n```bash\n47.243.139.246\n20.205.204.152\n121.36.193.65\n156.245.39.71\n13.238.233.150\n142.234.31.240\n68.183.75.244\n185.48.122.237\n52.25.116.123\n104.127.1.181\n185.248.102.245\n95.217.201.8\n3.125.24.134\n23.15.117.202\n```\n\n抓包结果如下所示，Zmap 向随机的 10,000 个 IP 的 80 端口发送 SYN 数据包。\n\n![image.png](assets/image-20210820135938-euhwdad.png)\n\n如果 IP 的 80 端口开放，以 47.243.139.246 为例，筛选出的数据包如下图所示，具体解释为：\n\n1. 向 47.243.139.246 的 80 端口发送 SYN 数据包\n2. 接收到 47.243.139.246 的 80 端口的 SYN/ACK 包，证明该 IP 的 80 端口可用\n3. 向 47.243.139.246 的 80 端口发送 RST 数据包，防止占用对方资源\n\n![image.png](assets/image-20210820140403-p3m74a8.png)\n\n如果 IP 的 80 端口不开放，以 44.102.170.124 为例，筛选出的数据包如下图所示。Zmap 向其发送 SYN 请求后没有得到应答，故判断该 IP 的 80 端口不可用。\n\n![image.png](assets/image-20210820141018-vtwr862.png)\n\n查看 Zmap 向哪些 IP 发送了 RST 数据包，则证明这些 IP 的 80 端口可用。筛选结果如下图所示，目的地址与上述的 csv 结果文件一致。\n\n![image.png](assets/image-20210820141939-p5jvaf8.png)\n\n## 源码分析\n\nZmap 整体函数调用图如下所示。\n\n![image.png](assets/image-20210820142448-j6i10zm.png)\n\n通过图我们可以直观的看到整个程序调用的过程。Zmap 在启动时候，先获取环境信息，如 IP、网关等。然后读取配置文件选择使用哪种扫描方式，然后在 Probe_modules 切换到对应的模块，然后启动。\n\n下面侧重分析 SYN 扫描这个模块，整个执行的过程中，会有一个线程专门负责发送，另外有一个使用 libpcap 组件抓包，发送和接收就独立开来。\n\n[zmap/src/probe_modules/module_tcp_synscan.c](https://github.com/zmap/zmap/blob/main/src/probe_modules/module_tcp_synscan.c) 是用于执行 TCP SYN 扫描的探测模块，在初始化阶段的 synscan_init_perthread 函数中，依次调用 make_ip_header 函数和 make_tcp_header 函数进行数据包 header 的封装。\n\n```c\nstatic int synscan_init_perthread(\n    void *buf, macaddr_t *src, macaddr_t *gw,\n    port_h_t dst_port,\n    UNUSED void **arg_ptr)\n{\n    struct ether_header *eth_header = (struct ether_header *)buf;\n    make_eth_header(eth_header, src, gw);\n    struct ip *ip_header = (struct ip *)(\u0026eth_header[1]);\n    uint16_t len = htons(sizeof(struct ip) + ZMAP_TCP_SYNSCAN_TCP_HEADER_LEN);\n    make_ip_header(ip_header, IPPROTO_TCP, len);\n    struct tcphdr *tcp_header = (struct tcphdr *)(\u0026ip_header[1]);\n    make_tcp_header(tcp_header, dst_port, TH_SYN);\n    set_mss_option(tcp_header);\n    return EXIT_SUCCESS;\n}\n```\n\n这两个函数编写于 [zmap/src/probe_modules/packet.c](https://github.com/zmap/zmap/blob/main/src/probe_modules/packet.c) 中。分析 make_ip_header 函数可知，在下示第 7 行，IP 的 identification number 被设置为固定的 54321。\n\n```c\nvoid make_ip_header(struct ip *iph, uint8_t protocol, uint16_t len)\n{\n    iph-\u003eip_hl = 5;  // Internet Header Length\n    iph-\u003eip_v = 4;   // IPv4\n    iph-\u003eip_tos = 0; // Type of Service\n    iph-\u003eip_len = len;\n    iph-\u003eip_id = htons(54321); // identification number\n    iph-\u003eip_off = 0;\t   // fragmentation flag\n    iph-\u003eip_ttl = MAXTTL;      // time to live (TTL)\n    iph-\u003eip_p = protocol;      // upper layer protocol =\u003e TCP\n    // we set the checksum = 0 for now because that's\n    // what it needs to be when we run the IP checksum\n    iph-\u003eip_sum = 0;\n}\n```\n\n分析 make_tcp_header 函数可知，在下示第 10 行，TCP 的 window 被设置为固定的 65535。\n\n```c\nvoid make_tcp_header(struct tcphdr *tcp_header, port_h_t dest_port,\n\t\t     uint16_t th_flags)\n{\n    tcp_header-\u003eth_seq = random();\n    tcp_header-\u003eth_ack = 0;\n    tcp_header-\u003eth_x2 = 0;\n    tcp_header-\u003eth_off = 5; // data offset\n    tcp_header-\u003eth_flags = 0;\n    tcp_header-\u003eth_flags |= th_flags;\n    tcp_header-\u003eth_win = htons(65535); // largest possible window\n    tcp_header-\u003eth_sum = 0;\n    tcp_header-\u003eth_urp = 0;\n    tcp_header-\u003eth_dport = htons(dest_port);\n}\n```\n\n查看抓取的 SYN 数据包，如下图所示，IP 的 ID 和 TCP 的 window 确实为 54321 和 65535，所以这两个固定值可作为扫描器特征。\n\n![image.png](assets/image-20210820144125-sms9xlk.png)\n\n# Angry IP Scanner\n\n## 抓包分析\n\nAngry IP Scanner（简称 angryip） 是一款开源跨平台的网络扫描器，主要用于扫描 IP 地址和端口。\n\nangryip 默认使用 Windows ICMP 方法扫描各个 ip 地址，扫描每个 IP 的 80、443 和 8080 端口。以 IP 范围 123.56.104.200~123.56.104.250 为例，扫描结果如下图所示，红色代表 IP 不可用，蓝色代表 IP 可用端口不可用，绿色代表 IP 和端口均可用。\n\n![image.png](assets/image-20210821162502-x2i550s.png)\n\n在捕获的数据包中，以 123.56.104.218 为例，该 IP 被标记为绿色，下面是与它有关的数据包抓取结果。\n\n图中第一个红框处 angryip 与 123.56.104.218 进行了 3 次 ping，且都予以回复，说明该 IP 可用。第二个红框处 angryip 分别测试 123.56.104.218 的 80、443 和 8080 端口，其中 80 和 443 端口予以回复，说明这两个端口可用。\n\n![image.png](assets/image-20210821164724-13mkbn1.png)\n\n在不可用的 IP 中，以 123.56.104.204 为例，与它相关的数据包抓取结果如下。angryip 向其发送 3 次 ping 请求，都没有得到回复，则判断其 IP 不可用，也没有向其端口发送数据包。\n\n![image.png](assets/image-20210821165011-z99mlky.png)\n\n## 源码分析\n\n因为无论 IP 和端口是否可用，angryip 都会先发送 ping 数据包，所以通过 ping 阶段的源码分析其工具的特征。\n\n分析 [ipscan/test/net/azib/ipscan/core/net/ICMPSharedPingerTest.java](https://github.com/angryip/ipscan/blob/64ec7090acdba380a62d5d2e1a6c630cc5302197/test/net/azib/ipscan/core/net/ICMPSharedPingerTest.java) 源码，该测试类调用 pinger.ping()方法 3 次，并计算平均时长。\n\n```java\npublic class ICMPSharedPingerTest {\n    @Test @Ignore(\"this test works only under root\")\n    public void testPing() throws Exception {\n\tPinger pinger = new ICMPSharedPinger(1000);\n\tPingResult result = pinger.ping(new ScanningSubject(InetAddress.getLocalHost()), 3);\n\tassertTrue(result.getAverageTime() \u003e= 0);\n\tassertTrue(result.getAverageTime() \u003c 50);\n\tassertTrue(result.getTTL() \u003e= 0);\n    }\n}\n```\n\n该方法在 [ipscan/test/net/azib/ipscan/core/net/WindowsPinger.java](https://github.com/angryip/ipscan/blob/master/src/net/azib/ipscan/core/net/WindowsPinger.java) 中，源码如下所示，判断 IP 类型，并调用 IPv6 和 IPv4 对应的方法。\n\n```java\npublic PingResult ping(ScanningSubject subject, int count) throws IOException {\n    if (subject.isIPv6())\n\treturn ping6(subject, count);\n    else\n\treturn ping4(subject, count);\n}\n```\n\n以 IPv4 为例，方法中定义了数据包的数据大小为 32，即 sendDataSize = 32。后续使用 Memory()方法创建 SendData 对象，并未对其进行赋值，故默认值应全为 0。\n\n```java\nprivate PingResult ping4(ScanningSubject subject, int count) throws IOException {\n    Pointer handle = dll.IcmpCreateFile();\n    if (handle == null) throw new IOException(\"Unable to create Windows native ICMP handle\");\n\n    int sendDataSize = 32;\n    int replyDataSize = sendDataSize + (new IcmpEchoReply().size()) + 10;\n    Pointer sendData = new Memory(sendDataSize);\n    sendData.clear(sendDataSize);\n    Pointer replyData = new Memory(replyDataSize);\n\n    PingResult result = new PingResult(subject.getAddress(), count);\n    try {\n\tIpAddrByVal ipaddr = toIpAddr(subject.getAddress());\n\tfor (int i = 1; i \u003c= count \u0026\u0026 !currentThread().isInterrupted(); i++) {\n            int numReplies = dll.IcmpSendEcho(handle, ipaddr, sendData, (short) sendDataSize, null, replyData, replyDataSize, timeout);\n\t    IcmpEchoReply echoReply = new IcmpEchoReply(replyData);\n\t    if (numReplies \u003e 0 \u0026\u0026 echoReply.status == 0 \u0026\u0026 Arrays.equals(echoReply.address.bytes, ipaddr.bytes)) {\n\t\tresult.addReply(echoReply.roundTripTime);\n\t\tresult.setTTL(echoReply.options.ttl \u0026 0xFF);\n\t    }\n\t}\n    }\n    finally {\n\tdll.IcmpCloseHandle(handle);\n    }\n    return result;\n}\n```\n\n在实际抓包中，每个发出的 ICMP 请求中，Data 的大小均为 32 字节，且全为 0，所以可将它作为 angryip 的特征。\n\n![image.png](assets/image-20210821170747-7wqf3l2.png)\n\n# Masscan\n\n## 抓包分析\n\nMasscan 默认使用 SYN 扫描，以 IP 123.56.104.218 为例，扫描其 1~600 端口，结果如下所示。\n\n![image.png](assets/image-20210823210937-kuazqw2.png)\n\n抓包结果如下所示，Masscan 向 123.56.104.218 的 1~600 端口进行随机化扫描，发出 SYN 请求。\n\n![image.png](assets/image-20210823211248-lw6p85a.png)\n\n 查看 80 端口的数据包，下图可知 80 端口向 Masscan 回复，说明该端口可用。\n\n![image.png](assets/image-20210823211459-ubawizk.png)\n\n查看 81 端口的数据包，发现并没有数据包回复，说明该端口不可用。\n\n![image.png](assets/image-20210823211834-0iznt9h.png)\n\n筛选收到的 SYN/ACK 数据包，得到 22、443 和 80 端口，说明 123.56.104.218 的 1~600 中这 3 个端口可用。\n\n![image.png](assets/image-20210823212157-qaib7mv.png)\n\n## 源码分析\n\n观察抓包分析中结果可以发现，所有发出的 SYN 请求中，窗口大小都是 1024。\n\n![image.png](assets/image-20210823212622-zdayv8k.png)\n\n在 Masscan 的主函数 [masscan/src/main.c](https://github.com/robertdavidgraham/masscan/blob/master/src/main.c) 文件中，默认使用以下代码初始化 TCP 数据包的模板。\n\n```c\ntemplate_packet_init(\n    parms-\u003etmplset,\n    parms-\u003esource_mac,\n    parms-\u003erouter_mac_ipv4,\n    parms-\u003erouter_mac_ipv6,\n    masscan-\u003epayloads.udp,\n    masscan-\u003epayloads.oproto,\n    stack_if_datalink(masscan-\u003enic[index].adapter),\n    masscan-\u003eseed);\n```\n\n该函数位于 [masscan/src/templ.pkt.c](https://github.com/robertdavidgraham/masscan/blob/master/src/templ-pkt.c) 中，其中对于 TCP 的初始化代码如下所示。\n\n```c\n/* [TCP] */\n_template_init(\u0026templset-\u003epkts[Proto_TCP],\n               source_mac, router_mac_ipv4, router_mac_ipv6,\n               default_tcp_template,\n               sizeof(default_tcp_template)-1,\n               data_link);\ntemplset-\u003ecount++;\n```\n\n其中调用的 default_tcp_template 定义在该文件头部，下述 7 行指定 IP 的 length 为 40，下述 10 行指定 TLL 为 255，下述 18 行指定 ack 为 0，下述 21 行指定 window 的大小为 1024，可以将这些指标视为 Masscan 的特征。\n\n```c\nstatic unsigned char default_tcp_template[] =\n    \"\\0\\1\\2\\3\\4\\5\"  /* Ethernet: destination */\n    \"\\6\\7\\x8\\x9\\xa\\xb\"  /* Ethernet: source */\n    \"\\x08\\x00\"      /* Ethernet type: IPv4 */\n    \"\\x45\"          /* IP type */\n    \"\\x00\"\n    \"\\x00\\x28\"      /* total length = 40 bytes */\n    \"\\x00\\x00\"      /* identification */\n    \"\\x00\\x00\"      /* fragmentation flags */\n    \"\\xFF\\x06\"      /* TTL=255, proto=TCP */\n    \"\\xFF\\xFF\"      /* checksum */\n    \"\\0\\0\\0\\0\"      /* source address */\n    \"\\0\\0\\0\\0\"      /* destination address */\n\n    \"\\0\\0\"          /* source port */\n    \"\\0\\0\"          /* destination port */\n    \"\\0\\0\\0\\0\"      /* sequence number */\n    \"\\0\\0\\0\\0\"      /* ack number */\n    \"\\x50\"          /* header length */\n    \"\\x02\"          /* SYN */\n    \"\\x04\\x0\"        /* window fixed to 1024 */\n    \"\\xFF\\xFF\"      /* checksum */\n    \"\\x00\\x00\"      /* urgent pointer */\n    \"\\x02\\x04\\x05\\xb4\"  /* added options [mss 1460] */\n;\n```\n\n---\n\n参考：\n\n[互联网扫描器 ZMap 完全手册](https://linux.cn/article-5860-1.html)\n\n[zmap 源码解读之 zmap 扫描快的原因](https://nanshihui.github.io/2017/03/29/zmap%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB%E4%B9%8Bzmap%E6%89%AB%E6%8F%8F%E5%BF%AB%E7%9A%84%E5%8E%9F%E5%9B%A0/)\n\n**[Nmap_Bypass_IDS](https://github.com/al0ne/Nmap_Bypass_IDS)**\n\n[入侵检测——masscan(扫描篇)](https://blog.csdn.net/weixin_44288604/article/details/115656891)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseriouszyx%2Fscannerrecognition","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseriouszyx%2Fscannerrecognition","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseriouszyx%2Fscannerrecognition/lists"}