首页 > 解决方案 > Socket:sendto函数根据ip头长度值改变发送的数据

问题描述

我正在研究数据包伪造并使用原始套接字发送它

int s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);

我有几个问题。我伪造的数据包(以及我在网上找到的每个指南)都是从 iphdr 开始的。sendto 函数会自动将 ethhdr 添加到其中。由于我没有添加它,我无法更改它。
第一个问题是,有什么办法可以操纵 ethhdr.

其次,在 iphdr 中,我的 sendto 根据 iph->ihl 的值执行不同的操作。如果我将它设置为 5(通常是),它会改变

  1. 我的校验和值
  2. iph->tot_len 到大端
  3. 如果设置为 0.0.0.0,则 iphdr 中的源地址值到我的计算机 ip

但是,对于任何其他值,它都可以让数据包顺利通过而无需任何更改。

这是 iph->ihl 设置为 3(不是 5)的屏幕截图[完全相同]
这是 iph->ihl 设置为 5 的屏幕截图 [已更改]

以下是脚本:

接收文件

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<errno.h>

#include<sys/socket.h>
#include<sys/types.h>
#include<sys/ioctl.h>

#include<net/if.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<netinet/if_ether.h>
#include<netinet/udp.h>

#include<linux/if_packet.h>

#include<arpa/inet.h>

#define min(x,y) (x<y)?x:y

void pkt_hex_dump(uint8_t *data, size_t len)
{
    int rowsize = 16;
    int i, l, linelen, remaining;
    int li = 0;
    uint8_t ch; 

    printf("\nPacket hex dump:\n");
    printf("Packet Size = %ld\n", len);

    remaining = len;
    for (i = 0; i < len; i += rowsize) {
        printf("%06d\t", li);

        linelen = min(remaining, rowsize);
        remaining -= rowsize;

        for (l = 0; l < linelen; l++) {
            ch = data[l];
            printf("%02X ", (uint32_t) ch);
        }

        data += linelen;
        li += 10; 

        printf("\n");
    }
}

unsigned short csum(unsigned short *ptr,int nbytes) 
{
    register long sum;
    unsigned short oddbyte;
    register short answer;

    sum=0;
    while(nbytes>1) {
        sum+=*ptr++;
        nbytes-=2;
    }
    if(nbytes==1) {
        oddbyte=0;
        *((u_char*)&oddbyte)=*(u_char*)ptr;
        sum+=oddbyte;
    }

    sum = (sum>>16)+(sum & 0xffff);
    sum = sum + (sum>>16);
    answer=(short)~sum;
    
    return(answer);
}


int main()
{    
    int sock_r = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
    if (sock_r == -1) {
        printf("error in socket");
        return -1;
    }

    unsigned char* buffer = (unsigned char *)malloc(65536); 
    memset(buffer,0,65536);

    while(1)
    {
        int saddr_len, buflen;
        struct sockaddr saddr;

        saddr_len = sizeof saddr;
        buflen = recvfrom(sock_r, buffer, 65536, 0, &saddr, (socklen_t *)&saddr_len);
        printf("\n[NEW PACKET]\n");

        unsigned char* data = buffer + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);

        if(buflen<0)
        {
            printf("error in reading recvfrom function\n");
            return -1;
        }
        union ipv4
        {
            __be32 src;
            uint8_t ip[4];
        };
        
        struct iphdr *iph = (struct iphdr *)(buffer + sizeof(struct ethhdr));
        union ipv4 src_ip;
        src_ip.src = iph->saddr;

        struct udphdr *udph = (struct udphdr *)(buffer + sizeof(struct ethhdr) + sizeof(struct iphdr));

        printf("Source = %d.%d.%d.%d\n",(int)src_ip.ip[0],(int)src_ip.ip[1],(int)src_ip.ip[2],(int)src_ip.ip[3]);
        printf("Checksum Value : %d\n",(int)iph->check);
        printf("Checksum UDP : %d\n",(int)udph->check);
        printf("Data Received:\n%s\n",data);
        pkt_hex_dump((uint8_t *)iph,buflen - sizeof(struct ethhdr));

        //recheck csum values
        iph->check = 0;
        udph->source = 0;
        udph->dest = 0;
        udph->len = 0;  
        udph->check = 0;
        printf("\nRechecking Checksum:%4X\n\n",csum((unsigned short *)iph,iph->tot_len));
        
    }

    close(sock_r);
}

发送.c

#include<stdio.h>   //for printf
#include<string.h> //memset
#include<sys/socket.h>  //for socket ofcourse
#include<stdlib.h> //for exit(0);
#include<errno.h> //For errno - the error number
#include<netinet/udp.h> //Provides declarations for udp header
#include<netinet/ip.h>  //Provides declarations for ip header


#define min(x,y) (x<y)?x:y

void pkt_hex_dump(uint8_t *data, size_t len)
{
    int rowsize = 16;
    int i, l, linelen, remaining;
    int li = 0;
    uint8_t ch; 

    printf("\nPacket hex dump:\n");
    printf("Packet Size = %ld\n", len);

    remaining = len;
    for (i = 0; i < len; i += rowsize) {
        printf("%06d\t", li);

        linelen = min(remaining, rowsize);
        remaining -= rowsize;

        for (l = 0; l < linelen; l++) {
            ch = data[l];
            printf("%02X ", (uint32_t) ch);
        }

        data += linelen;
        li += 10; 

        printf("\n");
    }
}

struct pseudo_header
{
    u_int32_t source_address;
    u_int32_t dest_address;
    u_int8_t placeholder;
    u_int8_t protocol;
    u_int16_t udp_length;
};

unsigned short csum(unsigned short *ptr,int nbytes) 
{
    register long sum;
    unsigned short oddbyte;
    register short answer;

    sum=0;
    while(nbytes>1) {
        sum+=*ptr++;
        nbytes-=2;
    }
    if(nbytes==1) {
        oddbyte=0;
        *((u_char*)&oddbyte)=*(u_char*)ptr;
        sum+=oddbyte;
    }

    sum = (sum>>16)+(sum & 0xffff);
    sum = sum + (sum>>16);
    answer=(short)~sum;
    
    return(answer);
}

int main (void)
{
    int s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
    
    if(s == -1)
    {
        perror("Failed to create raw socket");
        exit(1);
    }
    
    struct sockaddr_in sin;
    
    sin.sin_family = AF_INET;
    sin.sin_port = htons(80);
    sin.sin_addr.s_addr = inet_addr ("10.0.2.15");

    char *datagram = (char*)malloc(4096);
    memset (datagram, 0, 4096);

    struct iphdr *iph = (struct iphdr *)datagram;
    char* data = datagram + sizeof(struct iphdr) + sizeof(struct udphdr);
    strcpy(data,"Hello I am here.\n");
    
    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len = sizeof (struct iphdr) + sizeof (struct udphdr) + strlen(data);
    iph->id = htonl (85492);    
    iph->frag_off = 0;
    iph->ttl = 255;
    iph->protocol = IPPROTO_UDP;
    iph->check = 0;     //Set to 0 before calculating checksum
    //iph->saddr = inet_addr ( "1.2.3.4" ); //Spoof the source ip address
    iph->daddr = sin.sin_addr.s_addr;

    iph->check = csum ((unsigned short *) datagram, iph->tot_len);

    struct udphdr *udph = (struct udphdr *)(datagram + sizeof(struct iphdr));
    udph->source = htons (6666);
    udph->dest = htons (8622);
    udph->len = htons(8 + strlen(data));    
    udph->check = 0;    //leave checksum 0 now, filled later by pseudo header

    struct pseudo_header psh;
    
    psh.source_address = inet_addr( "1.2.3.4" );
    psh.dest_address = sin.sin_addr.s_addr;
    psh.placeholder = 0;
    psh.protocol = IPPROTO_UDP;
    psh.udp_length = htons(sizeof(struct udphdr) + strlen(data) );
    
    int psize = sizeof(struct pseudo_header) + sizeof(struct udphdr) + strlen(data);
    unsigned char *pseudogram = malloc(psize);
    
    memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
    memcpy(pseudogram + sizeof(struct pseudo_header) , udph , sizeof(struct udphdr) + strlen(data));
    
    udph->check = csum( (unsigned short*) pseudogram , psize);

    //while (1)
    {
        if (sendto (s, datagram, iph->tot_len , 0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
        {
            perror("sendto failed");
        }
        else
        {
            printf ("Packet Send. Length : %d \n" , iph->tot_len);
            printf("Checksum Value : %d\n",(int)iph->check);
            printf("Checksum UDP : %d\n",(int)udph->check);
            pkt_hex_dump(datagram,iph->tot_len);
        }
    }
    close(s);
    return 0;
}

PS任何人都可以指出为什么数据包被发送两次,因为我在脚本中只有一次sendto函数。

标签: clinuxsocketsnetworkingspoofing

解决方案


推荐阅读