简体   繁体   English

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

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

I'm trying to make a client which can send custom packets to a server using raw sockets.我正在尝试制作一个可以使用原始套接字将自定义数据包发送到服务器的客户端。 I've had success with udp packets but when I try to switch to tcp ones, wireshark doesnt catch anything.我在 udp 数据包上取得了成功,但是当我尝试切换到 tcp 数据包时,wireshark 没有捕获任何东西。 I have read that tcp data cannot be shared on raw sockets but I have seen people implementing that in linux.我读过 tcp 数据不能在原始套接字上共享,但我看到有人在 linux 中实现它。 Is the idea of sending tcp packets over raw sockets even correct?通过原始套接字发送 tcp 数据包的想法是否正确? And if it is, do I still need to use connect() method in the client code for tcp protocol?如果是,我还需要在客户端代码中为 tcp 协议使用 connect() 方法吗?

Here's the code I've wrote (works fine with udp packets)这是我编写的代码(适用于 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;
} 

I tried various comninations of protocols in socket like IPPROTO_RAW, IPPROTO_TCP, IPPROTO_IP but nothing seems to work with tcp packets我在套接字中尝试了各种协议组合,如 IPPROTO_RAW、IPPROTO_TCP、IPPROTO_IP,但似乎没有任何东西适用于 tcp 数据包

Windows does not allow TCP packets over RAW sockets, as documented on MSDN: Windows 不允许 TCP 数据包通过 RAW 套接字,如 MSDN 中所述:

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

On Windows 7, Windows Vista, Windows XP with Service Pack 2 (SP2), and Windows XP with Service Pack 3 (SP3), the ability to send traffic over raw sockets has been restricted in several ways:在 Windows 7、Windows Vista、带有 Service Pack 2 (SP2) 的 Windows XP 和带有 Service Pack 3 (SP3) 的 Windows XP 上,通过原始套接字发送流量的能力受到以下几种方式的限制:

  • TCP data cannot be sent over raw sockets. TCP 数据不能通过原始套接字发送。

  • ... ...

Linux does not have the same restriction that Windows does. Linux 没有与 Windows 相同的限制。

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

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