Python Read ethernet frames using socket on Windows? - python

I'm trying to read Ethernet (IEEE 802.2 / 3) frames using primarily socket.
The application shuld just sniff ethernet frames and depending on the content, act on it. However, there are almost no information on how to do this on Windows, the default (unix way) being socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0800)). This is nonexistent in winsock apparently. So how do I sniff eth frames?
I suspect I need to bind to a MAC using socket.bind() instead of IP.
My current piece of code:
def _receive(interface): #Receive Eth packets.
#Interface = '192.168.0.10'
sock2 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
sock2.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1))
sock2.bind((interface, 0))
while True:
data, sender = sock2.recvfrom(1500)
handle_data(sender, data)
Gets me nowhere. I see packets on Local connection in Wireshark, but it's not picked up in python..
On linux, I can do sock_raw = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_802_2)) , then bind and setsockopt(sock_raw, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq))
I would like to not have to depend on too many external libraries becuase this is supposed to be distributed and thus pretty lightweight. pip install-able packages are OK though, they can be bundled with the installer..

Python's socket doesn't come with sniffing capabilites. Simple as that.
The idea of having a network stack in your operating system is that programs "register" for specific types of packets to be delivered to them – typically, this is something like listening on a IP port, ie. one to two levels above raw ethernet packets.
To get all raw ethernet packets, your operating system's network stack needs some kind of driver/interface to support you with that. That's why wireshark needs WinPcap.
My guess is you're going to be pretty happy with pypcap, which probably is PyPi/pip installable.

Related

Building a packet sniffer with Python + sockets

So I'm trying to build a packet sniffer in Python to deepen my understanding of networking. Thing is, it has turned out to be a tad bit more confusing than I initially anticipated. The problem is that all resources with thorough explanations cover the scenario of creating sockets for client/server data sending/receiving purposes.
At this point, I've successfully created some classes that handle packet header decoding for IPv4 and ICMP. Now, since my socket code only seemed to capture ICMP packets, I've been trying to configure it so that I can catch all traffic reaching my wifi interface, but I still almost exclusively see ICMP packets (with localhost as both source and destination).
So, I have some questions which I'd like to get answered. But first, my code:
import socket
import sys
from protocols.ipv4 import IPv4
PACKET_SIZE = 65535
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
sock.bind(("0.0.0.0", 0))
try:
while True:
# read in a packet
raw_buffer = sock.recvfrom(PACKET_SIZE)[0]
# create an IP packet object
ip_header = IPv4(raw_buffer)
# print the packet
print(ip_header)
except KeyboardInterrupt:
print("\nExiting...")
sock.close()
sys.exit(0)
This is how I've understood it:
First I'm creating a socket with socket.socket, where I specify address family, socket type and protocol. In my case, I'm selecting the AF_INET family which I don't really understand very well, but it seems to yield packets from the network layer. The socket type is set to SOCK_RAW meaning that I want the raw sockets as opposed to using SOCK_STREAM for TCP connections and SOCK_DGRAM for UDP. The last argument IPPROTO_IP just indicates that I want IP packets only.
Then, I'm binding the socket to 0.0.0.0 which supposedly means "any address" as described here.
What I don't understand:
Initially, I saw some examples of creating a sniffer socket which used the AF_PACKET address family. I soon found out that this address family is not available on macos (which I'm using). Why is that? What is an address family how does it relate to sockets? Is there an alternative way to catch packets from lower levels? In Wireshark I can see ethernet datagrams, so it seems possible.
As I've stated, I want to sniff all the traffic reaching my wifi interface. How does the socket know which interface I want it to operate on? Also I've learned that network interfaces can be put into different modes like monitor or promiscuous, how does that relate to sockets and my goal of catching packets?
Why am I almost only catching ICMP packets? What is the purpose of these packets with localhost both as destination and source?
I know there are lots of gaps in my current understanding of this. I'm not sure if I'll be able to get this to work, but I'm curious and I'd be grateful for any kind of answer or even just some good resources to check out.
Edit: My main question is where can I find out more about sockets in the context of packet sniffing?

generating a spoofed UDP packet in python

how can i create a spoofed UDP packet using python sockets,without using scapy library.
i have created the socket like this
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.sendto(bytes('', "utf-8"), ('192.168.1.9', 7043))# 192.168.1.9dest 7043 dest port
This is one of the first results for google searches like "spoofing udp packet python" so I am going to expand #Cukic0d's answer using scapy.
Using the scapy CLI tool (some Linux distributions package it separately to the scapy Python library ):
pkt = IP(dst="1.1.1.1")/UDP(sport=13338, dport=13337)/"fm12abcd"
send(pkt)
This sends a UDP Packet to the IP 1.1.1.1 with the source port 13338, destination port 13337 and the content fm12abcd.
If you need to a certain interface for some reason (like sending over a VPN that isn't your default route) you can use send(pkt, iface='tun0') to specify it.
One difference to #Cukic0d's answer is that this solution is more flexible by sending a layer 3 packet with send instead of a layer 2 packet with sendp. So it isn't necessary to prepend the correct Ethernet header with Ether() which can cause issues in some scenarios, e.g.:
WARNING: Could not get the source MAC: Unsupported address family (-2) for interface [tun0]
WARNING: Mac address to reach destination not found. Using broadcast.
I think you mean changing the source and destination addresses from the IP layer (on which the UDP layer is based).
To do so, you will need to use raw sockets. (SOCK_RAW), meaning that you have to build everything starting from the Ethernet layer to the UDP layer.
Honestly, without scapy, that’s a lot of hard work. If you wanted to use scapy, it would take 2 lines:
pkt = Ether()/IP(src=“...”, dst=“...”)/UDP()/...
sendp(pkt)
I really advice you to use scapy. The code itself is quite small so I don’t see a reason not to use it. It’s defiantly the easiest in python

Sending custom frame / packet in Python

I read many articles and found how to send custom packet based on IP using socket(AF_INET, SOCK_RAW, IPPROTO_RAW). But I want to send completely custom packet starting from Ethernet header. I can't send ARP packet if I can't form Ethernet header cause ARP don't based IP. Please, help!
P.S. I am on Windows 7, not Linux :(
In python, the easiest way is to use the cross-platform scapy library. It’s well known for that
Scapy
You can sniff, send.... lots of packets, add your own protocols, use existing ones... and it works on nearly all platforms. (On windows, it uses Npcap/Winpcap)
You can then build an ARP packet using
from scapy.all import *
pkt = ARP()
pkt.show()
sendp(Ether(dst=..., src=...)/pkt)
Which will create such packets
###[ ARP ]###
hwtype= 0x1
ptype= 0x800
hwlen= 6
plen= 4
op= who-has
hwsrc= 00:50:56:00:1e:3d
psrc= 212.83.148.19
hwdst= 00:00:00:00:00:00
pdst= 0.0.0.0
To build the packet, use the / operator
ether = Ether()
ether.src = “00:00:00:00:00:00”
ether.dst = “ff:ff:ff:ff:ff:ff”
arp = ARP()
[edit arp.psrc, arp.pdst, arp.hwsrc, arp.hwdst]
packet = ether/arp
sendp(packet) # sens packet on layer 2
Have a look at its Scapy documentation
There's no cross-platform way to do what you want, of course.
Python is just passing these values through to the underlying C API. So, on a platform with a complete BSD sockets API including the packet interface, you can just use AF_PACKET and the other appropriate flags. (I think you'd want ETH_P_ALL or ETH_P_802_2 rather than IPPROTO_RAW, or you might want SOCK_DGRAM… anyway, read your platform's man packet and figure it out based on what you actually need to do.) On Linux, at least most of these flags should be available on the SOCKET module; on other Unixes, they often don't get picked up, so you have to manually look them up in the system headers and use hardcoded constant ints in your code.
Unfortunately, if you're on Windows, this doesn't do any good. While WinSock has a feature they call TCP/IP Raw Sockets, accessed via SOCK_RAW, and recent versions of Python do expose this, it's just an emulation of a small subset of what actual BSD sockets implementations can do, and doesn't offer any way to go below the IP level (hence the name of the feature).
Microsoft's solution to this used to be that you'd write a TDI provider with the DDK, which would implement whatever protocol you wanted to expose as another WinSock protocol, and then your application-level code could just use that protocol the same way it would use, e.g., TCP. From the linked document above, it looks like this is obsolete, but the replacement seems like the same idea but with different acronyms (and, presumably, different APIs).
On the other hand, I'm pretty sure Windows already comes with protocols for ARP, ICMP, and anything other protocols needed for its usermode tools (because they obviously can't be written around raw packets). I'm just not sure how to access them.
As far as I know, the usual alternative is to use WinPcap.
While this was originally designed to be a packet capture library, it also implements a complete link-level socket interface that you can use for sending and receiving raw frames.
And there are Python wrappers for it, like WinPcapy.
So, as long as you can require that the WinPcap driver be installed, you can write ARP code, etc., on Windows in Python. It's just different from doing it on Unix.
In fact, one of the examples on the front page of WinPcapY, "Easy Packet sending", should get you started:
from winpcapy import WinPcapUtils
# Build a packet buffer
# This example-code is built for tutorial purposes, for actual packet crafting use modules like dpkt
arp_request_hex_template = "%(dst_mac)s%(src_mac)s08060001080006040001" \
"%(sender_mac)s%(sender_ip)s%(target_mac)s%(target_ip)s" + "00" * 18
packet = arp_request_hex_template % {
"dst_mac": "aa"*6,
"src_mac": "bb"*6,
"sender_mac": "bb"*6,
"target_mac": "cc"*6,
# 192.168.0.1
"sender_ip": "c0a80001",
# 192.168.0.2
"target_ip": "c0a80002"
}
# Send the packet (ethernet frame with an arp request) on the interface
WinPcapUtils.send_packet("*Ethernet*", packet.decode("hex"))

Serial data over UDP Sockets in Python

I may be going about this the wrong way but that's why I'm asking the question.
I have a source of serial data that is connected to a SOC then streams the serial data up to a socket on my server over UDP. The baud rate of the raw data is 57600, I'm trying to use Python to receive and parse the data. I tested that I'm receiving the data successfully on the port via the script below (found here: https://wiki.python.org/moin/UdpCommunication)
import socket
UDP_IP = "MY IP"
UDP_PORT = My PORT
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print "received message:", data
Since I'm not reading the data with the .serial lib in Python or setting the baud rate to read at it comes all garbled, as would be expected. My end goal is to be able to receive and parse the data for server side processing and also have another client connect to the raw data stream piped back out from the server (proxy) which is why I'm not processing the data directly from the serial port on the device.
So my question is, how can I have Python treat the socket as a serial port that I can set a baud rate on and #import serial and .read from? I can't seem to find any examples online which makes me think I'm overlooking something simple or am trying to do something stupid.
sadface
You can't treat a socket as a serial line. A socket can only send and receive data (data stream for TCP, packets for UDP). If you would need a facility to control the serial line on the SOC you would need to build an appropriate control protocol over the socket, i.e. either use another socket for control like FTP does or use in-band control and distinguish between controlling and data like HTTP does. And of course both sides of the connection have to understand this protocol.
Build on facts
A first thing to start with is to summarise facts -- begining from the very SystemOnChip (SOC) all the way up ...:
an originator serial-bitstream parameters ::= 57600 Bd, X-<dataBIT>-s, Y-<stopBIT>, Z-<parityBIT>,
a mediator receiving process de-framing <data> from a raw-bitstream
a mediator upStream sending to server-process integration needs specification ( FSA-model of a multi-party hand-shaking, fault-detection, failure-state-resilience, logging, ... )
T.B.C. ...
Design as per a valid requirement list
A few things work as a just bunch of SLOC one-liners. Design carefully against the validated Requirement List as a principle. It saves both your side and the cooperating Team(s).
Test on models
Worth a time to test on-the-fly, during the efforts to progress from simple parts to more complex, multiparty scenarios.
Integrate on smarter frameworks
Definitely a waste of time to reinvent wheel. Using a smart framework for server-side integration will unlock a lot of your time and energy on your ProblemDOMAIN-specific tasks, rather than to waste both the former and the latter for writing your just-another-socket-framework ( destined in majority of cases to failure ... )
Try ZeroMQ or a nanomsg Scale-able Formal Communication Patterns Framework for smart-services to send de-framed data from your serial-bitStream source and you are almost there.

Python Raw Sockets (Windows): Sniffing Ethernet Frames

I have seen several examples of creating sockets to sniffing for IP Packets, for example using:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
What I am trying to achieve, is sniffing for Ethernet Frames and analysing the data received in Windows. The packets I am interested in are PPPoE Frames not containing IP.
In Linux (using python) I was able to achieve this using :
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(3))
s.setsockopt(socket.SOL_SOCKET, IN.SO_BINDTODEVICE, struct.pack("%ds"%(len("eth0")+1,),"eth0"))
while condition:
pkt = s.recvfrom(1500)
addToQueue(filter(pkt))
Now due to the differences betweeen linux sockets and WinSock2 API, I am having the following compatibility issues :
There is no IN package for windows. That means the SO_BINDTODEVICE is not present. How do I sniff everything coming on eth0 interface?
What should I use for protocol option in socket() constructor as I dont want to limit it to IPPROTO_IP.
Can anyone point me to the right direction ? I went through similar questions but none of them really solved my problem as they were all concerned with IP Packet sniffing
Note: I know libraries like Scapy could be used for sniffing, but it loses packets if we are trying to do any elaborate filtering (or use the prn function) and does not suit what I am trying to do. Raw sockets fit my need perfectly.
I can't verify this without a Windows box but I think all you need is ...
HOST = socket.gethostbyname(socket.gethostname())
s = socket.socket(socket.AF_INET, socket.SOCK_RAW)
s.bind((HOST, 0))
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
while condition:
pkt = s.recvfrom(1500)
addToQueue(filter(pkt))
Additionally, I'd recommend that you look in to using something like pypcap (or another libpcap wrapper) instead.
FTR
Note: I know libraries like Scapy could be used for sniffing, but it loses packets if we are trying to do any elaborate filtering (or use the prn function) and does not suit what I am trying to do. Raw sockets fit my need perfectly.
If you get Scapy and set conf.use_pcap = False, you can create a Windows raw socket by using sock = conf.L2socket() which according to yourself wont "lose packets".
You can then call recv() or recv_raw() on it like a regular socket, if really you want not to use Scapy's dissection.

Categories