• 原始套接字--icmp相关


    icmp请求

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <netinet/ip_icmp.h>
    #include <sys/time.h>
    
    /* icmp报文长度 */
    #define ICMP_PACKET_LEN sizeof(struct icmp)
    
    void err_exit(const char *err_msg)
    {
        perror(err_msg);
        exit(1);
    }
    
    /* 校验和 */
    unsigned short check_sum(unsigned short *addr, int len)
    {
        int nleft = len;
        int sum = 0;
        unsigned short *w = addr;
        unsigned short answer = 0;
    
        while(nleft > 1)
        {
            sum += *w++;
            nleft -= 2;
        }
        if(nleft == 1)
        {
            *(unsigned char *)(&answer) = *(unsigned char *)w;
            sum += answer;
        }
    
        sum = (sum >> 16) + (sum & 0xffff);
        sum += (sum >> 16);
        answer = ~sum;
    
        return answer;
    }
    
    /* 填充icmp报文 */
    struct icmp *fill_icmp_packet(int icmp_type, int icmp_sequ)
    {
        struct icmp *icmp_packet;
    
        icmp_packet = (struct icmp *)malloc(ICMP_PACKET_LEN);
        icmp_packet->icmp_type = icmp_type;
        icmp_packet->icmp_code = 0;
        icmp_packet->icmp_cksum = 0;
        icmp_packet->icmp_id = htons(getpid());
        icmp_packet->icmp_seq = htons(icmp_sequ);
        /* 发送时间 */
        gettimeofday((struct timeval *)icmp_packet->icmp_data, NULL);
        /* 校验和 */
        icmp_packet->icmp_cksum = check_sum((unsigned short *)icmp_packet, ICMP_PACKET_LEN);
    
        return icmp_packet;
    }
    
    /* 发送icmp请求 */
    void icmp_request(const char *dst_ip, int icmp_type, int icmp_sequ)
    {
        struct sockaddr_in dst_addr;
        struct icmp *icmp_packet;
        int sockfd, ret_len;
        char buf[ICMP_PACKET_LEN];
    
        /* 请求的地址 */
        bzero(&dst_addr, sizeof(struct sockaddr_in));
        dst_addr.sin_family = AF_INET;
        dst_addr.sin_addr.s_addr = inet_addr(dst_ip);
    
        if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
            err_exit("sockfd()");
    
        /* icmp包 */
        icmp_packet = fill_icmp_packet(icmp_type, icmp_sequ);
        memcpy(buf, icmp_packet, ICMP_PACKET_LEN);
    
        /* 发送请求 */
        ret_len = sendto(sockfd, buf, ICMP_PACKET_LEN, 0, (struct sockaddr *)&dst_addr, sizeof(struct sockaddr_in));
        if (ret_len > 0)
            printf("sendto() ok!!!
    ");
    
        close(sockfd);
    }
    
    int main(int argc, const char *argv[])
    {
        if (argc != 2)
        {
            printf("usage:%s dst_ip
    ", argv[0]);
            exit(1);
        }
    
        /* 发送icmp请求 */
        icmp_request(argv[1], 8, 1);
    
        return 0;
    }

    icmp接收

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/time.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/ip_icmp.h>
    
    /* IP首部长度 */
    #define IP_HEADER_LEN sizeof(struct ip)
    /* icmp报文长度 */
    #define ICMP_PACKET_LEN sizeof(struct icmp)
    /* IP + ICMP长度 */
    #define IP_ICMP_PACKET_LEN IP_HEADER_LEN + ICMP_PACKET_LEN
    
    void err_exit(const char *err_msg)
    {
        perror(err_msg);
        exit(1);
    }
    
    /* 计算发送时间与接收时间的毫秒差 */
    float time_interval(struct timeval *recv_time, struct timeval *send_time)
    {
        float msec = 0;
    
        /* 如果接收的时间微妙小于发送的微妙 */
        if (recv_time->tv_usec < send_time->tv_usec)
        {
            recv_time->tv_sec -= 1;
            recv_time->tv_usec += 1000000;
        }
        msec = (recv_time->tv_sec - send_time->tv_sec) * 1000.0 + (recv_time->tv_usec - send_time->tv_usec) / 1000.0;
    
        return msec;
    }
    
    int main(void)
    {
        struct ip *ip_header;
        struct icmp *icmp_packet;
        char buf[IP_ICMP_PACKET_LEN];
        struct timeval *recv_timeval, *send_timeval;
        int sockfd, ret_len;
    
        if ((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1)
            err_exit("sockfd()");
    
        recv_timeval = malloc(sizeof(struct timeval));
        while (1)
        {
            ret_len = recv(sockfd, buf, IP_ICMP_PACKET_LEN, 0);
            if (ret_len > 0)
            {
                /* 接收时间 */
                gettimeofday(recv_timeval, NULL);
                /* 取出ip首部 */
                /* 取出icmp报文 */
                ip_header = (struct ip *)buf;
                icmp_packet = (struct icmp *)(buf + IP_HEADER_LEN);
                /* 取出发送时间 */
                send_timeval = (struct timeval *)icmp_packet->icmp_data;
                printf("===============================
    ");
                printf("from ip:%s
    ", inet_ntoa(ip_header->ip_src));
                printf("icmp_type:%d
    ", icmp_packet->icmp_type);
                printf("icmp_code:%d
    ", icmp_packet->icmp_code);
                printf("time interval:%.3fms
    ", time_interval(recv_timeval, send_timeval));
            }
        }
    
        free(recv_timeval);
        close(sockfd);
        return 0;
    }

    arp-icmp应答伪装

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <sys/ioctl.h>
    #include <sys/types.h>
    #include <net/ethernet.h>
    #include <net/if_arp.h>
    #include <net/if.h>
    #include <netinet/if_ether.h>
    #include <netinet/ip_icmp.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netpacket/packet.h>
    #include <arpa/inet.h>
    
    #ifndef arp_hrd /*android not define struct ether_arp*/
    struct ether_arp {
        struct    arphdr ea_hdr;        /* fixed-size header */
        u_int8_t arp_sha[ETH_ALEN];    /* sender hardware address */
        u_int8_t arp_spa[4];        /* sender protocol address */
        u_int8_t arp_tha[ETH_ALEN];    /* target hardware address */
        u_int8_t arp_tpa[4];        /* target protocol address */
    };
    #define    arp_hrd    ea_hdr.ar_hrd
    #define    arp_pro    ea_hdr.ar_pro
    #define    arp_hln    ea_hdr.ar_hln
    #define    arp_pln    ea_hdr.ar_pln
    #define    arp_op    ea_hdr.ar_op
    #endif
    
    #define xprint_log(fmt, ...) 
        printf("[%04d]%s() " fmt, __LINE__, __FUNCTION__, ####__VA_ARGS__)
    #define xprint_err(fmt, ...) 
        printf("[%04d]%s() err: " fmt, __LINE__, __FUNCTION__, ####__VA_ARGS__)
    
    #define xdebug 0
    #define xunused __attribute__((unused))
    
    #define HDR_LEN_ETH  sizeof(struct ether_header)
    #define HDR_LEN_ARP  sizeof(struct ether_arp)
    #define HDR_LEN_IP   sizeof(struct ip)
    #define HDR_LEN_ICMP sizeof(struct icmp)
    
    
    static unsigned char  s_frame_data[ETH_FRAME_LEN];
    static unsigned int   s_frame_size = 0;
    static int            s_interface_index = -1;
    static unsigned char  s_interface_mac[ETH_ALEN];
    static struct in_addr s_interface_ip; 
    static unsigned char  s_src_mac[ETH_ALEN] = {0x38,0x97,0xd6,0x51,0xa0,0x02};
    
    
    static int 
    xsend_frame_ether(uint8_t *frame, int size, int ifindex, int skfd);
    static int
    xrecv_frame_ether(uint8_t *frame, int size, int ifindex, int skfd);
    
    static int 
    xsend_reply_arp(in_addr_t ipaddr, int skfd);
    static int
    xsend_reply_icmp(in_addr_t ipaddr, int skfd);
    
    static uint16_t 
    xutil_check_sum(uint16_t* data, int size);
    static void
    xutil_swap_int(uint32_t *a, uint32_t *b);
    
    static int xunused
    xdump_frame_byte(uint8_t *data, int size);
    static int xunused
    xdump_frame_ether(struct ether_header *eth);
    static int xunused
    xdump_frame_arp  (struct ether_arp *arp);
    static int xunused
    xdump_frame_ip   (struct ip *iph);
    static int xunused
    xdump_frame_icmp (struct icmp *icmph);
    
    
    #define __DEFINITION__
    
    
    static uint16_t 
    xutil_check_sum(uint16_t* data, int size)
    {
        unsigned int cksm = 0;
        
        while (size > 1) {
            cksm += *data++;
            size -= sizeof(uint16_t);
        }
        
        if (size) {
            cksm += *(uint8_t*)data;
        }
        
        cksm  = (cksm>>16) + (cksm&0xffff); 
        cksm += (cksm>>16); 
        
        return (uint16_t)(~cksm);
    }
    
    static void
    xutil_swap_int(uint32_t *a, uint32_t *b)
    {
        *a = *a ^ *b;
        *b = *a ^ *b;
        *a = *a ^ *b;
        return ;
    }
    
    static int
    xdump_frame_byte(uint8_t *data, int size)
    {
        int i;
    
        for(i=0; i<size; i++) {
            if((i%16) == 0) {
                printf( "[%02x] ", i/16 );
            }
            printf( "%02x ", data[i] );
            if(((i+1)%16) == 0) {
                printf( "
    " );
            }
        }
    
        printf( "
    " );
        return 0;
    }
    
    static int
    xdump_frame_ether(struct ether_header *eth)
    {
        if (NULL == eth) {
            return -1;
        }
    
        printf("========frame ether========
    ");
        printf("type :0x%04x
    ", htons(eth->ether_type));
        printf("d-mac:%02x-%02x-%02x-%02x-%02x-%02x
    ",
            eth->ether_dhost[0], eth->ether_dhost[1], eth->ether_dhost[2], 
            eth->ether_dhost[3], eth->ether_dhost[4], eth->ether_dhost[5]);
    
        printf("s-mac:%02x-%02x-%02x-%02x-%02x-%02x
    ",
            eth->ether_shost[0], eth->ether_shost[1], eth->ether_shost[2], 
            eth->ether_shost[3], eth->ether_shost[4], eth->ether_shost[5]);
        return 0;
    }
    
    static int
    xdump_frame_arp  (struct ether_arp *arp)
    {
        if (NULL == arp) {
            return -1;
        }
    
        printf("========frame arp  ========
    ");
        printf("arp_hrd=%d    
    ", htons(arp->arp_hrd));
        printf("arp_pro=0x%04x
    ", htons(arp->arp_pro));
        printf("arp_op =%d    
    ", htons(arp->arp_op));
        printf("arp_sdr=%02x-%02x-%02x-%02x-%02x-%02x %d.%d.%d.%d
    ", 
            arp->arp_sha[0], arp->arp_sha[1], arp->arp_sha[2], 
            arp->arp_sha[3], arp->arp_sha[4], arp->arp_sha[5], 
            arp->arp_spa[0], arp->arp_spa[1], arp->arp_spa[2], 
            arp->arp_spa[3]);
        printf("arp_tgr=%02x-%02x-%02x-%02x-%02x-%02x %d.%d.%d.%d
    ", 
            arp->arp_tha[0], arp->arp_tha[1], arp->arp_tha[2], 
            arp->arp_tha[3], arp->arp_tha[4], arp->arp_tha[5], 
            arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], 
            arp->arp_tpa[3]);
        return 0;
    }
    
    static int
    xdump_frame_ip(struct ip *iph)
    {
        if (NULL == iph) {
            return -1;
        }
        
        printf("========frame ip   ========
    ");
        printf("ip_v  =0x%x
    ", iph->ip_v             ); /* 4位版本号           */
        printf("ip_hl =0x%x
    ", iph->ip_hl            ); /* 4位IP头部长度 32bit */
        printf("ip_tos=0x%x
    ", iph->ip_tos           ); /* 8位服务类型         */
        printf("ip_len=0x%x
    ", htons(iph->ip_len)    ); /*16位数据包总长度     */
        printf("ip_id =0x%x
    ", htons(iph->ip_id)     ); /*16位标志符           */
        printf("ip_off=0x%x
    ", htons(iph->ip_off)    ); /* 3位标记+13位片偏移  */
        printf("ip_ttl=0x%x
    ", iph->ip_ttl           ); /* 8位生存时间         */
        printf("ip_p  =0x%x
    ", iph->ip_p             ); /* 8位协议号           */
        printf("ip_sum=0x%x
    ", htons(iph->ip_sum)    ); /*16位首部校验和       */
        printf("ip_src=%s  
    ", inet_ntoa(iph->ip_src)); /*32位源地址           */
        printf("ip_dst=%s  
    ", inet_ntoa(iph->ip_dst)); /*32位目的地址         */
        return 0;
    }
    
    static int 
    xdump_frame_icmp (struct icmp *icmph)
    {
        if (NULL == icmph) {
            return -1;
        }
        
        printf("========frame icmp ========
    ");
        printf("icmp_type =0x%x
    ", icmph->icmp_type ); /*  8位类型          */
        printf("icmp_code =0x%x
    ", icmph->icmp_code ); /*  8位代码          */
        printf("icmp_cksum=0x%x
    ", icmph->icmp_cksum); /* 16位校验和        */
        printf("icmp_id   =0x%x
    ", icmph->icmp_id   ); /* 16位识别号 进程id */
        printf("icmp_seq  =0x%x
    ", icmph->icmp_seq  ); /* 16位序列号        */
        return 0;
    }
    
    static int 
    xsend_frame_ether(uint8_t *frame, int size, int ifindex, int skfd)
    {
        struct sockaddr_ll sll;
        socklen_t          sln = 0;
        
        struct sockaddr_ll *psll = NULL;
    
        if (-1 !=  ifindex) {
            bzero(&sll, sizeof(sll));
            sll.sll_ifindex  = ifindex;
            sll.sll_family   = PF_PACKET;
            sll.sll_protocol = htons(ETH_P_ALL);
    
            psll = &sll;
            sln  = sizeof(struct sockaddr_ll);
        }
        
        size = sendto(skfd, frame, size, 0, (struct sockaddr*)psll, sln);
        if (size < 0) {
            xprint_err("ioctl() SIOCGIFINDEX failed! errno=%d (%s)
    ", 
                errno, strerror(errno));
        }
            
        return size;
    }
    
    static int
    xrecv_frame_ether(uint8_t *frame, int size, int ifindex, int skfd)
    {
        struct sockaddr_ll sll;
        socklen_t          sln = sizeof(struct sockaddr_ll);
    
        struct sockaddr_ll *psll = NULL;
        socklen_t          *psln = NULL;
    
        if (NULL==frame || size<=0) {
            xprint_err("param failed! frame=%p size=%d
    ", frame, size);
            return -1;
        }
    
        if (-1 !=  ifindex) {
            bzero(&sll, sizeof(sll));
            sll.sll_ifindex  = ifindex;
            sll.sll_family   = PF_PACKET;
            sll.sll_protocol = htons(ETH_P_ALL);
    
            psll = &sll;
            psln = &sln;
        }
    
        memset(frame, 0, size*sizeof(uint8_t));
        size = recvfrom(skfd, frame, size, 0, (struct sockaddr*)psll, psln);
    
        if (size < 0) {
            xprint_err("recvfrom() failed! errno=%d (%s)
    ", 
                errno, strerror(errno));
        }
        return size;
    }
    
    static int 
    xsend_reply_arp(in_addr_t ipaddr, int skfd)
    {
        struct ether_header *eth = NULL;
        struct ether_arp    *arp = NULL;
        
        eth = (struct ether_header*)s_frame_data;
        arp = (struct ether_arp*)(s_frame_data + HDR_LEN_ETH);
    
        if (*(unsigned int*)arp->arp_tpa != ipaddr) {
            return -1;
        }  
       
        /*ether*/
        memcpy(eth->ether_dhost, eth->ether_shost, ETH_ALEN);
        memcpy(eth->ether_shost, s_src_mac       , ETH_ALEN);
       
        /*arp*/
        arp->arp_op = htons(ARPOP_REPLY);
        memcpy(arp->arp_tha, arp->arp_sha, ETH_ALEN);
        memcpy(arp->arp_tpa, arp->arp_spa, 4);
    
        memcpy(arp->arp_sha, s_src_mac, ETH_ALEN);
        memcpy(arp->arp_spa, &ipaddr, 4);
    
    #if xdebug
        printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ");
        printf("========frame size:%d
    ", s_frame_size);
        xdump_frame_ether(eth);
        xdump_frame_arp  (arp);
        printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ");
        printf("
    ");
    #endif
    
        if (s_frame_size != xsend_frame_ether(
            s_frame_data, s_frame_size, s_interface_index, skfd)) {
            return -1;
        }
    
        xprint_log("ok. size=%d
    ", s_frame_size);
        return 0;
    }
    
    static int 
    xsend_reply_icmp(in_addr_t ipaddr, int skfd)
    {
        struct ip *iph           = NULL;
        struct ether_header *eth = NULL;
        struct icmp *icmph       = NULL;
        
        eth   = (struct ether_header*)s_frame_data;
        iph   = (struct ip*)(s_frame_data + HDR_LEN_ETH);
        icmph = (struct icmp*)(s_frame_data + HDR_LEN_ETH + HDR_LEN_IP);
        
        if ((iph->ip_p!=IPPROTO_ICMP) || iph->ip_dst.s_addr!=ipaddr) {
            return 1;
        }
    
        /*ether*/
        memcpy(eth->ether_dhost, eth->ether_shost, ETH_ALEN);
        memcpy(eth->ether_shost, s_src_mac, ETH_ALEN);
        
        /*ip*/
        xutil_swap_int(&(iph->ip_src.s_addr), &(iph->ip_dst.s_addr));
        iph->ip_off = 0;
        iph->ip_sum = 0;
        iph->ip_sum = xutil_check_sum((uint16_t*)iph, HDR_LEN_IP);
    
        /*icmp*/
        icmph->icmp_type  = ICMP_ECHOREPLY;
        icmph->icmp_cksum = 0;
        icmph->icmp_cksum = 
            xutil_check_sum((uint16_t*)icmph, s_frame_size-HDR_LEN_ETH-HDR_LEN_IP);
    
    #if xdebug
        printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ");
        printf("========frame size:%d
    ", s_frame_size);
        xdump_frame_ether(eth  );
        xdump_frame_ip   (iph  );
        xdump_frame_icmp (icmph);
        printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ");
        printf("
    ");
    #endif
    
        if (s_frame_size != xsend_frame_ether( 
            s_frame_data, s_frame_size, s_interface_index, skfd)) {
            return -1;
        }
    
        xprint_log("ok. size=%d
    ", s_frame_size);
        return 0;
    }
    
    int main(int argc, char **argv)
    {
        int       skfd       = -1;
        in_addr_t xping_addr = 0;
       
        if (argc <= 2) {
            printf("usage: %s interface ipaddr
    ",argv[0]);
            printf("   ex: %s eth0 192.168.88.1
    ", argv[0]);
            return -1;
        }
    
        skfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
        if (skfd < 0) {
            xprint_err("socket() failed! errno=%d (%s)
    ", errno, strerror(errno));
            return -1;
        } 
    
        struct ifreq ifr;
        bzero(&ifr,sizeof(ifr));
        strcpy(ifr.ifr_name, argv[1]);
        if (-1 == ioctl(skfd, SIOCGIFINDEX, &ifr)) {
            xprint_err("ioctl() SIOCGIFINDEX failed! errno=%d (%s)
    ", 
                errno, strerror(errno));
            return -1;
        }
        s_interface_index = ifr.ifr_ifindex;
        
        if (-1 == ioctl(skfd, SIOCGIFHWADDR, &ifr)) {
            xprint_err("ioctl() SIOCGIFHWADDR failed! errno=%d (%s)
    ", 
                errno, strerror(errno));
            return -1;
        }
        memcpy(s_interface_mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
    
        if (-1 == ioctl(skfd, SIOCGIFADDR, &ifr)) {
            xprint_err("ioctl() SIOCGIFADDR failed! errno=%d (%s)
    ", 
                errno, strerror(errno));
            return -1;
        }
        s_interface_ip.s_addr = 
            ((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr.s_addr;
            
        if (-1 == ioctl(skfd, SIOCGIFFLAGS, &ifr)) {
            xprint_err("ioctl() IFF_PROMISC failed! errno=%d (%s)
    ", 
                errno, strerror(errno));
            return -1;
        }
    
        if ((ifr.ifr_flags&IFF_PROMISC) != IFF_PROMISC) {
            ifr.ifr_flags |= IFF_PROMISC;
            if(-1 == ioctl(skfd, SIOCSIFFLAGS, &ifr)) {
                xprint_err("ioctl() IFF_PROMISC failed! errno=%d (%s)
    ", 
                    errno, strerror(errno));
                return -1;
            }
        }
    
        printf("========host info  ========
    ");
        printf("ifr_ifindex=%d %s
    ", s_interface_index, argv[1]);
        printf("ifr_hwaddr =%02x-%02x-%02x-%02x-%02x-%02x
    ", 
            s_interface_mac[0], s_interface_mac[1], s_interface_mac[2], 
            s_interface_mac[3], s_interface_mac[4], s_interface_mac[5]);
        printf("ifr_addr   =%s
    ", inet_ntoa(s_interface_ip));
        printf("ifr_flags  =IFF_PROMISC
    ");
        printf("pid        =0x%x
    ", getpid());
        printf("header_eth =%d
    ", HDR_LEN_ETH);
        printf("header_arp =%d
    ", HDR_LEN_ARP);
        printf("header_ip  =%d
    ", HDR_LEN_IP);
        printf("header_icmp=%d
    ", HDR_LEN_ICMP);
        printf("
    ");
    
        printf("press any key continue!
    ");
        getchar();
        printf("waiting for someone ping %s ...
    ", argv[2]);
        
    #if 0
        int on = 1;
        if (0 != setsockopt(skfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))) {
            xprint_err("setsockopt() IP_HDRINCL failed! errno=%d (%s)
    ", 
                errno, strerror(errno));
            return -1;
        }
    #endif    
        
        xping_addr = inet_addr(argv[2]);
        
        while(1) {
            uint16_t ether_type = 0;
            struct ether_header* eth = NULL;
            
            memset(s_frame_data, 0x00, sizeof(unsigned char)*ETH_FRAME_LEN);
            s_frame_size = xrecv_frame_ether(s_frame_data, ETH_FRAME_LEN, 
                s_interface_index, skfd);
                
            eth = (struct ether_header*)s_frame_data;
            ether_type = htons(eth->ether_type);
    
            switch(ether_type) {
                case ETHERTYPE_ARP: {
                    xsend_reply_arp(xping_addr, skfd);
                    break;
                }
                case ETHERTYPE_IP: {     
                    xsend_reply_icmp(xping_addr, skfd);
                    break;
                }
                default: {
                    break;
                }
            }
        }
        
        close(skfd);
        return 0;
    }
  • 相关阅读:
    css3 flex 布局
    用CSS3 & jQuery创建apple TV海报视差效果
    JavaScript知识点的总结
    javascript 常用DOM操作整理
    html打造动画【系列4】哆啦A梦
    如何掌握jQuery插件开发(高能)
    前端基础进阶(一):内存空间详细图解
    JavaScript中数组对象详解
    [zhuan]JNIEnv解析
    在 C Level 用 dlopen 使用 第三方的 Shared Library (.so)
  • 原文地址:https://www.cnblogs.com/tla001/p/6592418.html
Copyright © 2020-2023  润新知