在 c 中使用 sendto() 发送自定义 tcp 数据包? [英] sending custom tcp packet using sendto() in c?

查看:25
本文介绍了在 c 中使用 sendto() 发送自定义 tcp 数据包?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

准确地说,我创建了这个简单的 TCP 程序,通过网络向目标机器发送一个简单的 TCP 数据包,但不知何故,我真的不知道,我做错了什么,但它似乎没有发送任何数据包到目标主机.我也无法在我的wireshark中找到它.

To be precise I have created this simple TCP program to send a simple TCP packet over network to a destination machine, but somehow Don't really know, what am I doing wrong but it don't appear to be sending any packets to the destination host. also I can't find it in my wireshark.

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 它说:

while compiling it, it compiles just fine. also when I run the program as it suppose to be as ./rawtcp 192.168.1.152 1000 192.168.1.151 1000 it says:

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中看到它.

but while filtering wireshark as tcp.port == 1000 it never show up (Note: source ip is 192.168.1.152 and destination ip is 192.168.1.151). but If I do a simple hping3 -p 1000 192.168.1.151 it works just fine and I can see it in wireshark too.

如果您能告诉我我的代码做错了什么,我将不胜感激:)

I would really appreciate if you could tell me what am I doing wrong in my code :)

推荐答案

首先,您缺少 标头,它提供了 inet_addr 您正在使用的功能.由于隐式函数声明,您的程序仍应在 C89 下编译而无需此功能,但依赖此功能是不好的做法,并且可能会导致细微的错误.

First of all, you are missing the <arpa/inet.h> header, which provides the inet_addr function you are using. Your program should still compile under C89 without this due to implicit function declaration but it is bad practice to rely on that and it could lead to subtle bugs.

一个问题是您对 #pragma pack 的滥用.

One issue is your misuse of #pragma pack.

在声明结构体之前,您应该执行 #pragma pack(push, 1),它既将结构体包装对齐设置为 1,又将之前的包装对齐状态推入堆栈.完成声明结构后,您可以执行 #pragma pack(pop) 在任何以下声明中将结构打包重置为正常.就您的程序而言,在声明之后省略 #pragma pack(pop) 应该仍然有效,但最好重置它,以防您声明了任何其他您没有声明的结构体不想被压缩,或者之后包含任何其他头文件.

Before you declare your structs, you should do #pragma pack(push, 1), which both sets struct packing alignment to 1, and pushes the previous state of packing alignment to a stack. After you are finished declaring your structs, you can then do #pragma pack(pop) to reset struct packing back to normal in any following declarations. In the case of your program, omitting #pragma pack(pop) after your declarations should still work, but it is good practice to reset this, in case you ever do declare any other structs which you don't want to be tightly-packed, or you include any other header files afterwards.

#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 来解决此问题,并只保留一个 16 位 iph_offset 字段,填充零,它实际上跨越了 IP 头中的 3 位和 13 位字段的空间.

The other problem with your struct ipheader is that you have both an 8-bit iph_flags and a 16-bit iph_offset, while the flags field in an IP header should be 3 bits and the offset field should be 13 bits (both of these together adding up to 16 bits). This means you have an extra 8 bits in your header which shouldn't be there. Since you are filling both of these fields with zero anyway, you can fix this by removing iph_flags from your struct completely, and just keeping the one 16-bit iph_offset field, filled with zero, which actually spans the space of the 3-bit and the 13-bit field in the IP header.

通过这些修复,您的程序对我有用,并且可以在 Wireshark 中看到一个数据包.

With these fixes, your program works for me and a packet can be seen in Wireshark.

这篇关于在 c 中使用 sendto() 发送自定义 tcp 数据包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆