Recent

Author Topic: Building a packet from scratch (under Windows)  (Read 2558 times)

role_33

  • New Member
  • *
  • Posts: 19
Building a packet from scratch (under Windows)
« on: November 04, 2024, 05:08:08 am »
Hello all,

I need to communicate (from Windows) with a networked device, and unfortunately this is not something I have dealt with before (not from the programming perspective, anyway).
This means that, besides not knowing exactly how to implement the communications, I'm not sure what to look for.

Using Wireshark it looks like they are just sending a packet containing an IP header, a TCP header, and the raw bytes (something similar to https://inc0x0.com/tcp-ip-packets-introduction/tcp-ip-packets-3-manually-create-and-send-raw-tcp-ip-packets/ , but with ~20 data bytes at the end). The actual captures are on my work computer :/

So... apparently it's just a matter of populating a custom packet with the correct information.
However, from what I have found, it looks like this is not easy to do from Windows.

Here's the catch: I used ChatGPT to generate some sample code, and -although it starts by warning it requires some Linux features- the code compiles happily under windows with minimal changes.
But it doesn't send anything (Wireshark doesn't see anything on that network interface) - and I don't know if the problem is the code, or if it's a Windows limitation.

On a few posts I saw people indicating NPCAP was necessary to create custom packets, but don't explain much more.
Wireshark does install NPCAP, for that matter.

My question is: is it possible to use Sockets under windows to create (and send) custom packets?
If Sockets won't do the trick, is there a package (or something) I can use to it? I know of the existence of Synapse, but I'm not sure it allows to do this low-level packet crafting.

Thanks!

J

The code from the AI thingie is as follows (with a couple changes - from memory):
Code: Pascal  [Select][+][-]
  1. program SendRawTCPPacket;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   SysUtils, Sockets, ctypes; //BaseUnix; // J: Commenting out Linux stuff
  7.  
  8. type
  9.   TIpHeader = packed record
  10.     ver_len: Byte;              // Version and header length
  11.     tos: Byte;                  // Type of service
  12.     total_len: Word;            // Total length
  13.     id: Word;                   // Identification
  14.     frag_offset: Word;          // Fragment offset and flags
  15.     ttl: Byte;                  // Time to live
  16.     proto: Byte;                // Protocol (TCP = 6)
  17.     checksum: Word;             // Header checksum
  18.     src_addr: LongWord;         // Source IP address
  19.     dest_addr: LongWord;        // Destination IP address
  20.   end;
  21.  
  22.   TTcpHeader = packed record
  23.     src_port: Word;             // Source port
  24.     dest_port: Word;            // Destination port
  25.     seq_num: LongWord;          // Sequence number
  26.     ack_num: LongWord;          // Acknowledgment number
  27.     offset_res: Byte;           // Data offset and reserved bits
  28.     flags: Byte;                // Flags
  29.     window: Word;               // Window size
  30.     checksum: Word;             // Checksum
  31.     urgent_ptr: Word;           // Urgent pointer
  32.   end;
  33.  
  34. var
  35.   sock: cint;
  36.   destAddr: sockaddr_in;
  37.   packet: array[0..39] of Byte;
  38.   ipHeader: TIpHeader;
  39.   tcpHeader: TTcpHeader;
  40.   destIp: string;
  41.   destPort: Word;
  42.  
  43. function CalculateChecksum(buffer: PWord; size: Integer): Word;
  44. var
  45.   sum: LongWord;
  46.   i: Integer;
  47. begin
  48.   sum := 0;
  49.   for i := 0 to (size div 2) - 1 do
  50.     Inc(sum, buffer[i]);
  51.  
  52.   // Add carry bits
  53.   while (sum shr 16) > 0 do
  54.     sum := (sum and $FFFF) + (sum shr 16);
  55.  
  56.   Result := not Word(sum);
  57. end;
  58.  
  59. begin
  60.   // Set destination IP and port
  61.   destIp := '192.168.1.1';      // Replace with the target IP
  62.   destPort := 80;               // Replace with the target port (HTTP)
  63.  
  64.   // Create a raw socket
  65.   sock := fpSocket(AF_INET, SOCK_RAW, IPPROTO_TCP);
  66.   if sock < 0 then
  67.   begin
  68.     writeln('Failed to create raw socket. Root privileges are required.');
  69.     halt(1);
  70.   end;
  71.  
  72.   // Set up destination address
  73.   FillChar(destAddr, SizeOf(destAddr), 0);
  74.   destAddr.sin_family := AF_INET;
  75.   destAddr.sin_port := htons(destPort);
  76.   destAddr.sin_addr.s_addr := StrToHostAddr(destIp).s_addr;
  77.  
  78.   // Create IP header
  79.   FillChar(ipHeader, SizeOf(ipHeader), 0);
  80.   ipHeader.ver_len := (4 shl 4) or (SizeOf(ipHeader) div 4); // IPv4 and header length
  81.   ipHeader.ttl := 64;
  82.   ipHeader.proto := IPPROTO_TCP;
  83.   ipHeader.src_addr := StrToHostAddr('192.168.1.100').s_addr; // Replace with your IP
  84.   ipHeader.dest_addr := destAddr.sin_addr.s_addr;
  85.   ipHeader.total_len := htons(SizeOf(ipHeader) + SizeOf(tcpHeader));
  86.   ipHeader.checksum := CalculateChecksum(@ipHeader, SizeOf(ipHeader));
  87.  
  88.   // Create TCP header
  89.   FillChar(tcpHeader, SizeOf(tcpHeader), 0);
  90.   tcpHeader.src_port := htons(12345);      // Arbitrary source port
  91.   tcpHeader.dest_port := htons(destPort);
  92.   tcpHeader.seq_num := htonl(1);
  93.   tcpHeader.ack_num := 0;
  94.   tcpHeader.offset_res := (SizeOf(tcpHeader) div 4) shl 4;
  95.   tcpHeader.flags := $02;                  // SYN flag
  96.   tcpHeader.window := htons(1024);
  97.   tcpHeader.checksum := CalculateChecksum(@tcpHeader, SizeOf(tcpHeader));
  98.  
  99.   // Combine headers into packet
  100.   Move(ipHeader, packet, SizeOf(ipHeader));
  101.   Move(tcpHeader, packet[SizeOf(ipHeader)], SizeOf(tcpHeader));
  102.  
  103.   // Send packet
  104.   if fpSendTo(sock, @packet, SizeOf(packet), 0, @destAddr, SizeOf(destAddr)) < 0 then
  105.     writeln('Failed to send packet.')
  106.   else
  107.     writeln('Packet sent successfully.');
  108.  
  109.   // Close socket
  110.   //fpClose(sock); // J: Not available in Sockets under Windows
  111.   CloseSocket(sock)
  112. end.
  113.  

DonAlfredo

  • Hero Member
  • *****
  • Posts: 1781
Re: Building a packet from scratch (under Windows)
« Reply #1 on: November 04, 2024, 08:23:05 am »
The mORmot2 has a nice OS-independent socket implementation.
https://github.com/synopse/mORMot2/blob/master/src/net/mormot.net.sock.pas
Works very well on many OS, including Windows.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Building a packet from scratch (under Windows)
« Reply #2 on: November 04, 2024, 09:59:35 am »
The code from the AI thingie is as follows (with a couple changes - from memory):

Please do not quote AI-generated code, since the next time an AI walks this forum it will reinforce its belief that it is a viable solution: even if in fact it is utter rubbish.

I think we need to know more about what you're trying to do and the device at the far end. Specifically, are you absolutely sure that you're using TCP- which is not normally packet-based- or are you using TCP/IP as a generic term?

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Warfley

  • Hero Member
  • *****
  • Posts: 1763
Re: Building a packet from scratch (under Windows)
« Reply #3 on: November 04, 2024, 01:00:21 pm »
TCP is a stream based protocol. You can't just simply send TCP packets and expect anything to work. TCP maintains an internal state (sequence number, acknowledge number, congestion window and a whole bunch more).

So if you don't want to intercept an existing stream, you need to establish your own TCP connection to the device and then communicate with whatever application layer protocol the device is using.
A simple TCP stream you can easily open up with TSocketStream from the unit ssockets. It follows roughly the Berkley Sockets API which you should get familiar with anyway if you want to do anything with regards to networking.

But the more interesting thing is of course what the application layer protocol is, if there is TLS encryption, etc.
Without that knowledge you won't be able to do anything

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Building a packet from scratch (under Windows)
« Reply #4 on: November 04, 2024, 01:50:08 pm »
TCP is a stream based protocol. You can't just simply send TCP packets and expect anything to work. TCP maintains an internal state (sequence number, acknowledge number, congestion window and a whole bunch more).

It is of course common to build ICMP and UDP packets from scratch (ping, WOL etc.), which is why I think we might possibly have a terminology problem here.

It is of course common to send a brief TCP stream to a remote port.

But the only time I've had consider building TCP packets from scratch- particularly with customised header information- was when doing a bit of hacking against a local-but-undocumented device... and even there a simple fpConnect() with careful examination of the response might have been adequate.

(I'd have to work through my code in more detail to check that... looking at my notes suggests that I had to do it that way because I wanted to look very carefully at application-level checksums etc... but that my code's brute-force performance was still a long way behind Nmap etc.)

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

role_33

  • New Member
  • *
  • Posts: 19
Re: Building a packet from scratch (under Windows)
« Reply #5 on: November 04, 2024, 08:23:06 pm »
The code from the AI thingie is as follows (with a couple changes - from memory):

Please do not quote AI-generated code, since the next time an AI walks this forum it will reinforce its belief that it is a viable solution: even if in fact it is utter rubbish.

I think we need to know more about what you're trying to do and the device at the far end. Specifically, are you absolutely sure that you're using TCP- which is not normally packet-based- or are you using TCP/IP as a generic term?

MarkMLl

Point taken.

Unfortunately, my actual code resides at a company computer, and the moment I take *anything* out of it I'll be answering questions for the next 3 months, so I just posted the original code spitted out by the mightly overlord :/
Which -surprisingly- compiles just fine :)

And you are correct: I'm using TCP/IP as a generic term.

The device I'm working with is a piece of automotive hardware, and I'm afraid I can't say much more without getting myself in trouble.
This piece of hardware runs a daemon that responds to the data sent to it, but there's no mention on the spec about HOW to send that data, so I'm relying on my observations of what Wireshark reports.

Namely, the packet contains:
- Destination MAC Address | Source MAC addresses | 0x0800 (IPv4, StreamIndex 0)
- A standard IPV4 header. It reports the protocol to be TCP (6)
- A standard TCP header
- Some data bytes <-- This is the only thing contained in the specs I received

The IPv4 and TCP headers look just like the ones on the website I linked (except the flags set are different)

Wireshark reports the "Protocols in frame" as eth:ethertype:ip:tcp:data

The (redacted) packet looks like this:
Code: [Select]
Frame 10: 73 bytes on wire (584 bits), 73 bytes captured (584 bits) on interface \Device\NPF_{xxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}, id 0
    Section number: 1
    Interface id: 0 (\Device\NPF_{xxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})
    Encapsulation type: Ethernet (1)
    Arrival Time: Oct 30, 2024 21:14:34.307664000 Eastern Daylight Time
    UTC Arrival Time: Oct 31, 2024 01:14:34.307664000 UTC
    Epoch Arrival Time: 1730337274.307664000
    [Time shift for this packet: 0.000000000 seconds]
    [Time delta from previous captured frame: 1.801229000 seconds]
    [Time delta from previous displayed frame: 1.801229000 seconds]
    [Time since reference or first frame: 6.935502000 seconds]
    Frame Number: 10
    Frame Length: 73 bytes (584 bits)
    Capture Length: 73 bytes (584 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: eth:ethertype:ip:tcp:data]
    [Coloring Rule Name: TCP]
    [Coloring Rule String: tcp]
Ethernet II, Src: (xx:xx:xx:xx:xx:xx), Dst: (xx:xx:xx:xx:xx:xx)
    Destination: AsixElectron_dc:02:fd (00:0e:c6:dc:02:fd)
    Type: IPv4 (0x0800)
    [Stream index: 0]
Internet Protocol Version 4, Src: 192.168.xxx.xxx, Dst: 192.168.xxx.xxx
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
    Total Length: 59
    Identification: 0x694d (26957)
    010. .... = Flags: 0x2, Don't fragment
    ...0 0000 0000 0000 = Fragment Offset: 0
    Time to Live: 128
    Protocol: TCP (6)
    Header Checksum: 0x0000 [validation disabled]
    [Header checksum status: Unverified]
    Source Address: 192.168.xxx.xxx
    Destination Address: 192.168.xxx.xxx
    [Stream index: 0]
Transmission Control Protocol, Src Port: xxxxx, Dst Port: xxxxx, Seq: 14, Ack: 17, Len: 19
    Source Port: xxxxx
    Destination Port: xxxxx
    [Stream index: 0]
    [Stream Packet Number: 4]
    [Conversation completeness: Incomplete (12)]
    [TCP Segment Len: 19]
    Sequence Number: 14    (relative sequence number)
    Sequence Number (raw): 3481193771
    [Next Sequence Number: 33    (relative sequence number)]
    Acknowledgment Number: 17    (relative ack number)
    Acknowledgment number (raw): 769143398
    0101 .... = Header Length: 20 bytes (5)
    Flags: 0x018 (PSH, ACK)
        000. .... .... = Reserved: Not set
        ...0 .... .... = Accurate ECN: Not set
        .... 0... .... = Congestion Window Reduced: Not set
        .... .0.. .... = ECN-Echo: Not set
        .... ..0. .... = Urgent: Not set
        .... ...1 .... = Acknowledgment: Set
        .... .... 1... = Push: Set
        .... .... .0.. = Reset: Not set
        .... .... ..0. = Syn: Not set
        .... .... ...0 = Fin: Not set
        [TCP Flags: ·······AP···]
    Window: 512
    [Calculated window size: 512]
    [Window size scaling factor: -1 (unknown)]
    Checksum: 0x5448 [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    [Timestamps]
    [SEQ/ACK analysis]
    TCP payload (19 bytes)
Data (19 bytes)
    Data: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
    [Length: 19]

Finally, yes, I'm aware there's a lot more to the communication with any device. I just want to make sure I manage to put something out on the NIC (i.e. making sure I'm using the correct method) before investing a lot of time on this approach. I was expecting the device I'm trying to communicate with to reject the packet, but I think it's failing before anything is put out there.

J

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Building a packet from scratch (under Windows)
« Reply #6 on: November 04, 2024, 09:24:59 pm »
OK, so establish the TCP connection (behind the scenes several handshake packets are exchanged) then send byte sequence as required and either wait or close the connection. For something like this you should find the standard sockets unit entirely adequate.

Strictly speaking, you don't know whether your byte sequence goes as one or multiple packets: that detail's handled by the TCP protocol stack. In practical terms I'd expect it to be a single packet up to ~1Kbytes, but TCP handles byte streams rather than datagrams/packets which are the province of UDP.

If you were in the UK and you needed confidential help I'd suggest we did it via a one-penny order/invoice (i.e. enough to establish a contractual relationship) but it sounds like you're getting to grips with things fairly nicely :-)

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

BSaidus

  • Hero Member
  • *****
  • Posts: 600
  • lazarus 1.8.4 Win8.1 / cross FreeBSD
Re: Building a packet from scratch (under Windows)
« Reply #7 on: November 04, 2024, 09:34:52 pm »
Hello,
I remember somedays (years) ago, I used libnet for building a custom packets
see : https://github.com/libnet/libnet
lazarus 1.8.4 Win8.1 / cross FreeBSD
dhukmucmur vernadh!

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Building a packet from scratch (under Windows)
« Reply #8 on: November 04, 2024, 10:10:46 pm »
I remember somedays (years) ago, I used libnet for building a custom packets
see : https://github.com/libnet/libnet

Yes, but (checking the link) I think that's more oriented towards Nmap-style port-scanning etc. Broadly speaking I think we've managed to get past that, to the stage where we're just crafting a sequence of bytes to be sent (i.e. the sort of thing netcat or echo>netpipes could do).

My professional curiosity is reined in, but I remember a colleague whose PhD was based on automotive networking and was sponsored by Ripaults.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Warfley

  • Hero Member
  • *****
  • Posts: 1763
Re: Building a packet from scratch (under Windows)
« Reply #9 on: November 04, 2024, 11:10:03 pm »
The device I'm working with is a piece of automotive hardware, and I'm afraid I can't say much more without getting myself in trouble.
This piece of hardware runs a daemon that responds to the data sent to it, but there's no mention on the spec about HOW to send that data, so I'm relying on my observations of what Wireshark reports.

Again, it's TCP, think in connections and streams. What do you want to do? Do you want to intercept an existing stream or do you want (to replay) your own stream?

If you don't know, look at the communication of the real software you are capturing. Is the communication in short connections, e.g. a request response scheme where each request has its own connection? Easiest way to check is to check the port numbers of the client, if they change regularly, it's probably a new connection. But port numbers can be fixed so alternatively check for TCP handshakes and fin packages (Wireshark can filter those)
If this is the case take all the packets that belong to one connection (i.e. from syn to fin) concat the data sent and just put it into a TCP stream socket and see what happens.


If there is one connection that you want to intercept, it's gonna be much harder, because as I said already, TCP has internal state, meaning you need to read out the state from the communications, and then craft your own custom TCP packets using IP RAW sockets: https://learn.microsoft.com/en-us/windows/win32/winsock/tcp-ip-raw-sockets-2
The problem then is that any packets you intercept will mess up the state at your actual client and it will probably crash whatever application it is you are running.
Instead you should just provide your application as proxy service which can intercept messages in between. This way a consistent state is maintained at the real client, preventing it from crashing

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1430
    • Lebeau Software
Re: Building a packet from scratch (under Windows)
« Reply #10 on: November 05, 2024, 11:09:46 am »
Namely, the packet contains:
- Destination MAC Address | Source MAC addresses | 0x0800 (IPv4, StreamIndex 0)
- A standard IPV4 header. It reports the protocol to be TCP (6)
- A standard TCP header
- Some data bytes <-- This is the only thing contained in the specs I received


Then there is no reason you shouldn't be able to use a standard TCP socket via the Winsock API on Windows to establish a TCP connection and send packets. You don't need to concern yourself with the Ethernet, IP, and TCP headers, Winsock will handle those for you. Focus only on the data stream, per the specs.
« Last Edit: November 05, 2024, 11:11:30 am by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

role_33

  • New Member
  • *
  • Posts: 19
Re: Building a packet from scratch (under Windows)
« Reply #11 on: November 06, 2024, 06:36:03 pm »
Hello peeps,

@Warfley: the device I'm talking to just responds to commands being sent on the data portion of the packet (and sends an acknowledgment packet back). There isn't a "stream" per se (besides the package sequence numbering) and no need to intercept packages from other devices. The ports are well defined (always the same).

I have one piece of software right now that can communicate with this device, but it's closed source, support is *bad*, doesn't have an API, and it has a million options I don't need. This software crashing is the main reason I'm looking into developing an alternative.

@Remy: can you point me to an example that does something similar - under Windows? I haven't tried recently, but I recall that when I first attempted to send the data packets, those resulted on something different (and much larger) than what the packets I can see on Wireshark from the (bad, but somewhat working) existing program.

J

MarkMLl

  • Hero Member
  • *****
  • Posts: 8039
Re: Building a packet from scratch (under Windows)
« Reply #12 on: November 06, 2024, 07:22:00 pm »
There isn't a "stream" per se (besides the package sequence numbering)

Oh yes there is. /By/ /definition/ TCP is a stream protocol, and neither end knows- or should care- whether the other is handling it in largish chunks, byte-by-byte and so on.

The sequence numbers (unless you're adding some at the application level) are outside your control. In particular they change arbitrarily if the TCP passes through something that isn't IP at some intermediate routing stage, if it's rewritten by NAT, or if a long TCP message is broken into multiple shorter ones (or vice versa).

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Warfley

  • Hero Member
  • *****
  • Posts: 1763
Re: Building a packet from scratch (under Windows)
« Reply #13 on: November 06, 2024, 07:41:22 pm »
Ok, just lets make it very simple, you have this packet with the data that is sent to the device, just try to open a connection and send the data manually:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   ssockets;
  7.  
  8. const
  9.   {Put in the data you read out of wireshark }
  10.   DataToSend: Array[0..18] of Byte = ($00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00);
  11. var
  12.   s: TInetSocket;
  13.   buff: array[0..1023] of Byte;
  14. begin
  15.   s:=TInetSocket.Create('IP of your device', Port of your Device);
  16.   try
  17.     s.Connect;
  18.     s.write(DataToSend,Length(DataToSend)));
  19.     s.Read(buff,SizeOf(buff));
  20.   finally
  21.     s.Free;
  22.   end;
  23. end.

And look what happens. If it works (in that the connection is at least accepted, what happens afterwards I can't tell), you know the device is kind of using one-shot request response schemes. Then it is rather easy.

role_33

  • New Member
  • *
  • Posts: 19
Re: Building a packet from scratch (under Windows)
« Reply #14 on: November 06, 2024, 07:46:07 pm »
Ok... let's reword that :)

The only packets on that NIC going to/from the device I'm communicating with are the ones generated when the control software sends a user-initiated command (and that's exactly one packet per command, unless the DF flag on the IPv4 allows fragmentation somehow), and the acknowledgement from the device (one packet).

From time to time you see the computer trying to find something there using UDP, but I know for a fact the device ignores those.

There isn't any kind of handshaking going on between the computer and the device on a regular basis.

I intend to replace the piece of cr_p software that is currently being used completely, so I don't risk any conflicts.

J

 

TinyPortal © 2005-2018