📜 ⬆️ ⬇️

We write a simple sniffer for Windows

In this article we will look at creating a simple sniffer under Windows.
Who cares, welcome under cat.

Introduction


Objective: to write a program that will capture network traffic (Ethernet, WiFi) transmitted via IP.
Tools: Visual Studio 2005 or higher.
The approach described here does not belong personally to the author and is successfully used in many commercial as well as categorically free programs (hello, GPL).
This work is intended primarily for beginners in network programming, who, however, have at least basic knowledge of sockets in general, and windows-sockets in particular. Here I will often write well-known things, because the subject area is specific, if you miss something - there will be porridge in your head.

I hope you will be interested.

Theory (read optional, but desirable)


Currently, the vast majority of modern information networks are based on the foundation of the TCP / IP protocol stack. The TCP / IP protocol stack (Transmission Control Protocol / Internet Protocol) is a collective name for network protocols of different levels used in networks. In this article, we will be primarily interested in the IP protocol - a routable network protocol used for unwarranted delivery of data divided into so-called packets (a more accurate term is a datagram) from one node to another.
Of particular interest to us are IP-packets designed to transmit information. This is a fairly high level of the network OSI-data model, when you can be cut off from the device and data transmission medium, operating only with a logical representation.
It is quite logical that sooner or later there should be tools for intercepting, controlling, recording and analyzing network traffic. Such tools are usually called traffic analyzers, packet analyzers or sniffers (from English to sniff - sniff). This is a network traffic analyzer, a program or a hardware-software device intended for interception and subsequent analysis, or only analysis of network traffic intended for other nodes. [one]
')

Practice (talk essentially)


At the moment, created a lot of software to listen to traffic. The most famous of them: Wireshark . Naturally, the goal is not to reap its laurels - we are interested in the task of intercepting traffic using the usual "listening" network interface. It is important to understand that we are not going to engage in hacking and intercept someone else's traffic. You just need to view and analyze the traffic that passes through our host.

For what it may be needed:
  1. View the current traffic flow through the network connection (incoming / outgoing / total).
  2. Redirect traffic for subsequent analysis to another host.
  3. Theoretically, you can try to use it to hack a WiFi-network (are we not going to do this?).

Unlike Wireshark, which is based on the libpcap / WinPcap library, our analyzer will not use this driver. What is already there, we will have no driver at all, and we are not going to write our own NDIS (horror of horrors!). You can read about this in this topic . He will simply be a passive observer using only the WinSock library. Using the driver in this case is redundant.

How so? Very simple.
The key step in turning a simple network application into a network analyzer is switching the network interface to listening mode (promiscuous mode), which will allow it to receive packets addressed to other interfaces on the network. This mode forces the network board to accept all frames, regardless of who they are addressed to on the network. [2]

Starting from Windows 2000 (NT 5.0), it became very easy to create a program for listening to a network segment, because its network driver allows you to switch the socket to receive all packets.

Enabling illegible mode

long flag = 1; SOCKET socket; #define SIO_RCVALL 0x98000001 ioctlsocket(socket, SIO_RCVALL, &RS_Flag); 

Our program operates with IP packets, and uses the Windows Sockets version 2.2 library and raw sockets. In order to get direct access to the IP packet, you need to create a socket as follows:

Making a raw socket

 s = socket(AF_INET, SOCK_RAW, IPPROTO_IP); 

Here, instead of the constant SOCK_STREAM (TCP protocol) or SOCK_DGRAM (UDP protocol), we use the value SOCK_RAW . Generally speaking, working with raw sockets is interesting not only in terms of traffic capture. In fact, we get complete control over the formation of the package. Rather, we configure it manually, which allows, for example, to send a specific ICMP packet ...

Go ahead. It is known that an IP packet consists of a header, service information and, in fact, data. I advise you to look here to refresh knowledge. We describe in the form of the structure the IP header (thanks to the excellent article on the RSDN [3] ):

Description of the IP packet structure

 typedef struct _IPHeader { unsigned char ver_len; //     unsigned char tos; //   unsigned short length; //    unsigned short id; // Id unsigned short flgs_offset; //    unsigned char ttl; //   unsigned char protocol; //  unsigned short xsum; //   unsigned long src; // IP-  unsigned long dest; // IP-  unsigned short *params; //  ( 320 ) unsigned char *data; //  ( 65535 ) }IPHeader; 

The main function of the listening algorithm will be as follows:

Single packet capture function

 IPHeader* RS_Sniff() { IPHeader *hdr; int count = 0; count = recv(RS_SSocket, (char*)&RS_Buffer[0], sizeof(RS_Buffer), 0); if (count >= sizeof(IPHeader)) { hdr = (LPIPHeader)malloc(MAX_PACKET_SIZE); memcpy(hdr, RS_Buffer, MAX_PACKET_SIZE); RS_UpdateNetStat(count, hdr); return hdr; } else return 0; } 

Everything is simple: we get a portion of the data using the standard socket function recv , and then copy them into an IPHeader structure.
And finally, we run an infinite packet capture loop:

Capturing all the packets that get to our network interface.

 while (true) { IPHeader* hdr = RS_Sniff(); //  IP- if (hdr) { //     } } 

A little offtopic

Here and below, the author made RS_ (from Raw Sockets) for some important functions and variables. The project did 3-4 years ago, and there was a crazy thought to write a full-fledged library for working with raw sockets. As is often the case, after receiving some meaningful (for the author) results, the enthusiasm died away, and then the case did not go further.

In principle, it is possible to go further and describe the headers of all subsequent protocols that are above. To do this, you need to analyze the protocol field in the IPHeader structure. Look at the example code (yes, there should be a switch, damn it!), Where the header colorization takes place depending on which protocol has the packet encapsulated in IP:

 /* *    */ void ColorPacket(const IPHeader *h, const u_long haddr, const u_long whost = 0) { if (h->xsum) SetConsoleTextColor(0x17); //     else SetConsoleTextColor(0x07); //   if (haddr == h->src) { SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_RED | FOREGROUND_INTENSITY); // ""    } else if (haddr == h->dest) { SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_GREEN | FOREGROUND_INTENSITY); // ""    } if (h->protocol == PROT_ICMP || h->protocol == PROT_IGMP) { SetConsoleTextColor(0x70); // ICMP- } else if(h->protocol == PROT_IP || h->protocol == 115) { SetConsoleTextColor(0x4F); // IP-in-IP-, L2TP } else if(h->protocol == 53 || h->protocol == 56) { SetConsoleTextColor(0x4C); // TLS, IP with Encryption } if(whost == h->dest || whost == h->src) { SetConsoleTextColor(0x0A); } } 


However, this is significantly beyond the scope of this article. For our example of training, it will be enough to look at the ip-addresses of the hosts from which traffic is sent and calculate its quantity per unit of time (the finished program in the archive at the end of the article).

In order to display the IP header data, you must implement the function to convert the header (but not the data) of the datagram to a string. As an example of implementation, you can offer this option:

Convert IP header to string

 inline char* iph2str(IPHeader *iph) { const int BUF_SIZE = 1024; char *r = (char*)malloc(BUF_SIZE); memset((void*)r, 0, BUF_SIZE); sprintf(r, "ver=%d hlen=%d tos=%d len=%d id=%d flags=0x%X offset=%d ttl=%dms prot=%d crc=0x%X src=%s dest=%s", BYTE_H(iph->ver_len), BYTE_L(iph->ver_len)*4, iph->tos, ntohs(iph->length), ntohs(iph->id), IP_FLAGS(ntohs(iph->flgs_offset)), IP_OFFSET(ntohs(iph->flgs_offset)), iph->ttl, iph->protocol, ntohs(iph->xsum), nethost2str(iph->src), nethost2str(iph->dest) ); return r; } 

Based on the above basic information, it turns out that such a small program (the terrible name ss, abbr. From the English simple sniffer) that implements local listening of IP traffic. Its interface is shown below.



Source and binary code is provided as is, such as it was several years ago. Now I'm scared to look at him, and yet, it is quite readable (of course, you can’t be so self-confident). Even Visual Studio Express 2005 will suffice for compilation.

What we got in the end:

Thank you for your attention, I congratulate the habrovchan and habrovchanok and all-all-all with the coming Christmas!

Download project Discovery Password: habr

upd: According to Weageoo , I put it on a githab .

Source: https://habr.com/ru/post/164901/


All Articles