在c中使用sendto()发送自定义tcp包?

时间:2018-01-19 09:47:53

标签: c sockets networking tcp

准确地说,我已经创建了这个简单的TCP程序,通过网络将简单的TCP数据包发送到目标计算机,但不知怎的,我真的不知道,我做错了什么,但它看起来不是将任何数据包发送到目标主机。我也无法在wirehark中找到它。

rawtcp.c:

//---cat rawtcp.c---
// Run as root or SUID 0, just datagram no data/payload

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <netinet/tcp.h>

// Packet length
#define PCKT_LEN 8192
#pragma pack(push, 1)
// May create separate header file (.h) for all
// headers' structures
// IP header's structure
struct ipheader {
 unsigned char      iph_ihl:4, /* Little-endian */
                iph_ver:4;
 unsigned char      iph_tos;
 unsigned short int iph_len;
 unsigned short int iph_ident;
 unsigned short int iph_offset;
 unsigned char      iph_ttl;
 unsigned char      iph_protocol;
 unsigned short int iph_chksum;
 unsigned int       iph_sourceip;
 unsigned int       iph_destip;
};

/* Structure of a TCP header */
struct tcpheader {
 unsigned short int tcph_srcport;
 unsigned short int tcph_destport;
 unsigned int       tcph_seqnum;
 unsigned int       tcph_acknum;
 unsigned char      tcph_reserved:4, tcph_offset:4;
 // unsigned char tcph_flags;
 unsigned int
       tcp_res1:4,       /*little-endian*/
       tcph_hlen:4,      /*length of tcp header in 32-bit words*/
       tcph_fin:1,       /*Finish flag "fin"*/
       tcph_syn:1,       /*Synchronize sequence numbers to start a connection*/
       tcph_rst:1,       /*Reset flag */
       tcph_psh:1,       /*Push, sends data to the application*/
       tcph_ack:1,       /*acknowledge*/
       tcph_urg:1,       /*urgent pointer*/
       tcph_res2:2;

 unsigned short int tcph_win;
 unsigned short int tcph_chksum;
 unsigned short int tcph_urgptr;
};
#pragma pack(pop)
// Simple checksum function, may use others such as Cyclic Redundancy Check, CRC
unsigned short csum(unsigned short *buf, int len)
{
        unsigned long sum;
        for(sum=0; len>0; len--)
        sum += *buf++;
        sum = (sum >> 16) + (sum &0xffff);
        sum += (sum >> 16);
        return (unsigned short)(~sum);
}

int main(int argc, char *argv[])
{
    int sd;

    // No data, just datagram
    char buffer[PCKT_LEN];

    // The size of the headers
    struct ipheader *ip = (struct ipheader *) buffer;
    struct tcpheader *tcp = (struct tcpheader *) (buffer + sizeof(struct ipheader));
    struct sockaddr_in sin, din;

    int one = 1;
    const int *val = &one;
    memset(buffer, 0, PCKT_LEN);

    if(argc != 5)
    {
        printf("- Invalid parameters!!!\n");
        printf("- Usage: %s <source hostname/IP> <source port> <target hostname/IP> <target port>\n", argv[0]);
        exit(-1);
    }

    sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    if(sd < 0)
    {
        perror("socket() error");
        exit(-1);
    } else
        printf("socket()-SOCK_RAW and tcp protocol is OK.\n");

    // The source is redundant, may be used later if needed
    // Address family
    sin.sin_family = AF_INET;
    din.sin_family = AF_INET;

    // Source port, can be any, modify as needed
    sin.sin_port = htons(atoi(argv[2]));
    din.sin_port = htons(atoi(argv[4]));

    // Source IP, can be any, modify as needed
    sin.sin_addr.s_addr = inet_addr(argv[1]);
    din.sin_addr.s_addr = inet_addr(argv[3]);

    // IP structure
    ip->iph_ihl = 5;
    ip->iph_ver = 4;
    ip->iph_tos = 16;
    ip->iph_len = htons(sizeof(struct ipheader) + sizeof(struct tcpheader));
    ip->iph_ident = htons(54321);
    ip->iph_offset = 0;
    ip->iph_ttl = 64;
    ip->iph_protocol = 6; // TCP
    ip->iph_chksum = 0; // Done by kernel

    // Source IP, modify as needed, spoofed, we accept through command line argument
    ip->iph_sourceip = inet_addr(argv[1]);

    // Destination IP, modify as needed, but here we accept through command line argument
    ip->iph_destip = inet_addr(argv[3]);

    // The TCP structure. The source port, spoofed, we accept through the command line
    tcp->tcph_srcport = htons(atoi(argv[2]));

    // The destination port, we accept through command line
    tcp->tcph_destport = htons(atoi(argv[4]));
    tcp->tcph_seqnum = htonl(1);
    tcp->tcph_acknum = 0;
    tcp->tcph_offset = 5;
    tcp->tcph_syn = 1;
    tcp->tcph_ack = 0;
    tcp->tcph_win = htons(32767);
    tcp->tcph_chksum = 0; // Done by kernel
    tcp->tcph_urgptr = 0;

    // IP checksum calculation
    ip->iph_chksum = htons(csum((unsigned short *) buffer, (sizeof(struct ipheader) + sizeof(struct tcpheader))));

    // Inform the kernel do not fill up the headers' structure, we fabricated our own
    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0)
    {
        perror("setsockopt() error");
        exit(-1);
    } else
        printf("setsockopt() is OK\n");
    printf("Using:::::Destination IP: %s port: %u, Source IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));

    // sendto() loop, send every 2 second for 50 counts
    unsigned int count;
    for(count = 0; count < 20; count++)
    {
        if(sendto(sd, buffer, ip->iph_len, 0, (struct sockaddr *)&din, sizeof(din)) < 0)
    // Verify
    {
        perror("sendto() error");
        exit(-1);
    } else
        printf("Count #%u - sendto() is OK\n", count);

        sleep(2);
   }
   close(sd);
   return 0;
}

在编译时,它编译得很好。当我运行程序时,因为它假设为./rawtcp 192.168.1.152 1000 192.168.1.151 1000,它说:

socket()-SOCK_RAW and tcp protocol is OK.
setsockopt() is OK
Using:::::Destination IP: 192.168.1.151 port: 1000, Source IP: 192.168.1.152     port: 1000.
Count #0 - sendto() is OK
Count #1 - sendto() is OK

但是在将wireshark过滤为tcp.port == 1000时它永远不会显示(注意:源IP为192.168.1.152,目标IP为192.168.1.151)。但如果我做一个简单的hping3 -p 1000 192.168.1.151它就可以了,我也可以在wireshark中看到它。

如果你能在我的代码中告诉我我做错了什么,我真的很感激。)

2 个答案:

答案 0 :(得分:2)

首先,您缺少<arpa/inet.h>标题,该标题提供您正在使用的inet_addr函数。由于隐式函数声明,你的程序仍然应该在C89下编译而没有这个,但依靠它可能会导致细微的错误。

一个问题是您滥用#pragma pack

在声明结构之前,应该执行#pragma pack(push, 1),它们将struct packing alignment设置为1,并将先前的打包对齐状态推送到堆栈。在完成声明结构之后,您可以执行#pragma pack(pop)以在以下任何声明中将结构包装重置为正常。对于您的程序,在您的声明之后省略#pragma pack(pop)仍然有效,但最好重新设置,以防您声明任何其他您不想紧密结构的结构 - 打包,或者之后包含任何其他头文件。

#pragma pack(push, 1)
struct a {
    /* ... */
};

struct b {
    /* ... */
};
#pragma pack(pop)

struct ipheader的另一个问题是,您同时拥有8位iph_flags和16位iph_offset,而IP标头中的标志字段应为3位偏移字段应为13位(这两者一起加起来为16位)。这意味着你的标题中还有8位不应该存在。由于您无论如何都要将这两个字段填充为零,因此可以通过从结构中完全删除iph_flags来修复此问题,并保留一个填充为0的16位iph_offset字段,实际跨越IP报头中3位和13位字段的空间。

通过这些修复程序,您的程序可以为我工作,并且可以在Wireshark中看到数据包。

答案 1 :(得分:-1)

我认为你的端口已被使用。

您应该将端口更改为30000或更高,1000端口通常由其他应用程序使用

您还应该使用 telnet [ip] [port]

进行调试