繁体   English   中英

如何使用winsock中的原始套接字发送tcp数据包?

[英]How to send tcp packets using raw sockets in winsock?

我正在尝试制作一个可以使用原始套接字将自定义数据包发送到服务器的客户端。 我在 udp 数据包上取得了成功,但是当我尝试切换到 tcp 数据包时,wireshark 没有捕获任何东西。 我读过 tcp 数据不能在原始套接字上共享,但我看到有人在 linux 中实现它。 通过原始套接字发送 tcp 数据包的想法是否正确? 如果是,我还需要在客户端代码中为 tcp 协议使用 connect() 方法吗?

这是我编写的代码(适用于 udp 数据包)

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <iostream>
#include <string>

#pragma comment(lib, "Ws2_32.lib")

#define PORT 9090

using namespace std;

// custom headers

struct tcpheader {
    unsigned short int th_sport;
    unsigned short int th_dport;
    unsigned int th_seq;
    unsigned int th_ack;
    unsigned char th_x2 : 4, th_off : 4;
    unsigned char th_flags;
    unsigned short int th_win;
    unsigned short int th_sum;
    unsigned short int th_urp;
}; /* total tcp header length: 20 bytes (=160 bits) */

struct udphdr
{
    unsigned short udp_sport;
    unsigned short udp_dport;
    unsigned short udp_len;
    unsigned short udp_sum;
};

struct ipheader {
    unsigned char ip_hl : 4, ip_v : 4; /* this means that each member is 4 bits */
    unsigned char ip_tos;
    unsigned short int ip_len;
    unsigned short int ip_id;
    unsigned short int ip_off;
    unsigned char ip_ttl;
    unsigned char ip_p;
    unsigned short int ip_sum;
    unsigned int ip_src;
    unsigned int ip_dst;
}; /* total ip header length: 20 bytes (=160 bits) */

// checksum calculator

uint16_t
checksum(uint16_t* addr, int len)
{
    int count = len;
    register uint32_t sum = 0;
    uint16_t answer = 0;

    while (count > 1) {
        sum += *(addr++);
        count -= 2;
    }

    if (count > 0) {
        sum += *(uint8_t*)addr;
    }

    while (sum >> 16) {
        sum = (sum & 0xffff) + (sum >> 16);
    }

    answer = ~sum;

    return (answer);
}


int main() {

    WSADATA ws;
    char datagram[4096];
    int bOpt = 1;

    // Setting up the enviornment for sockets

    if (WSAStartup(MAKEWORD(2, 2), &ws) != 0) {
        cout << "WSAStartup() failed: " << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }

    // Creating a raw socket

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

    if (s == INVALID_SOCKET) {
        cout << "WSASocket() failed: " << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }

    // definfing the ip and tcp headers

    struct ipheader* iph = (struct ipheader*)datagram;
    // struct udphdr* udph = (struct udphdr*) (datagram + sizeof(struct ipheader));
    struct tcpheader* tcph = (struct tcpheader*)(datagram + sizeof(struct ipheader));
    
    string str = "Hello Server, What's running?";
    char* msg = const_cast<char*> (str.c_str());

    memcpy(datagram + sizeof(struct ipheader) + sizeof(struct udphdr), msg, sizeof(str));
    struct sockaddr_in sendTo;

    sendTo.sin_family = AF_INET;
    sendTo.sin_port = htons(PORT);
    inet_pton(AF_INET, "127.0.0.1", &sendTo.sin_addr);
    
    // memset(datagram, 0, sizeof datagram);

    // filling the packet : setting fields of headers

    // ip header

    iph->ip_hl = 5;
    iph->ip_v = 4;
    iph->ip_tos = 0;
    iph->ip_len = sizeof(struct ipheader) + sizeof(struct tcpheader);
    iph->ip_id = 1;
    iph->ip_off = 0;
    iph->ip_ttl = 255;
    iph->ip_p = 6;
    iph->ip_sum = 0;
    inet_pton(AF_INET, "127.0.0.1", &iph->ip_src);
    iph->ip_dst = sendTo.sin_addr.s_addr;

    // udp header

    // udph->udp_sport = htons(1234);
    // udph->udp_dport = htons(PORT);
    // udph->udp_len = sizeof(struct udphdr);
    // udph->udp_sum = 0;

    // tcp header

    tcph->th_sport = htons(1234);
    tcph->th_dport = htons(PORT);
    tcph->th_seq = rand();
    tcph->th_ack = 0;
    tcph->th_x2 = 0;
    tcph->th_off = 0;
    tcph->th_flags = 2; // SYN 
    tcph->th_win = htons(65535);
    tcph->th_sum = 0;
    tcph->th_urp = 0;

    // calculate checksum

    // udph->udp_sum = checksum((unsigned short*)&udph, sizeof(struct udphdr));
    iph->ip_sum = checksum((unsigned short*)&iph, sizeof(struct ipheader));


    // setting socket option {IP_HDRINCL} for choosing custom ip header instead of kernel header
    
    if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char*)&bOpt, sizeof(bOpt)) == SOCKET_ERROR) {
        cout << "setsockopt(IP_HDRINCL) failed: " << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }

    while (1) {
        int val = sendto(s, datagram, sizeof(datagram), 0, (sockaddr*)&sendTo, sizeof(sendTo));
        if (val == -1) {
            cout << "failed to send packet: " << WSAGetLastError() << endl;
            WSACleanup();
            return -1;
        }
        cout << val<<" ";
        Sleep(1000);
    }


    return 0;
} 

我在套接字中尝试了各种协议组合,如 IPPROTO_RAW、IPPROTO_TCP、IPPROTO_IP,但似乎没有任何东西适用于 tcp 数据包

Windows 不允许 TCP 数据包通过 RAW 套接字,如 MSDN 中所述:

https://docs.microsoft.com/en-us/windows/win32/winsock/tcp-ip-raw-sockets-2

在 Windows 7、Windows Vista、带有 Service Pack 2 (SP2) 的 Windows XP 和带有 Service Pack 3 (SP3) 的 Windows XP 上,通过原始套接字发送流量的能力受到以下几种方式的限制:

  • TCP 数据不能通过原始套接字发送。

  • ...

Linux 没有与 Windows 相同的限制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM