Some notes taken from Vivek Ramachandran’s course on Penetration Testing with Python

Network Packets

Packets are layered in encapsulated data like so:

> Ethernet

> IP


> Application Data

First 14 bytes are the ethernet header

Network Byte Order

Unlike Little Endian format on a regular computer, the network protocols will send the byte ordering Big Endian format.  This means that we have to be clear on our format for sending/injecting packets.

Python & Struct to Pack

The struct module allows us to pack and unpack data for packets.

For example, to create a null byte, we would do:

struct.pack(“x”)  # where x is the format.

We have a table of formats that we can choose from:

x = no value; c = string of length 1; b = signed integer; B = unsigned char; ? = boolean; h = short integer; H unsigned short integer; i = int; I = unsigned int; l = long integer; L = unsigned Long integer; q = long long integer; L = unsigned long long integer; f = float; d = double; s = char[] (string); P = char[] (string); p = void (integer)

To pack something like the value of 1, we would do:

struct.pack(“B”, 1)

By default it’s in Little Endian format.  To put this into Big Endian Format, we can add the bang like so:

struct.pack(“!B”, 1) which actually won’t appear any different (since there’s only one byte.  If however we were using an unsigned short with 2 Bytes…

struct.pack(“H”, 1) we’ll see a difference between that and this: struct.pack(“!H”, 1)

To capture a longer set of bytes we can do:

struct.pack(“2s”, “hi”)

Python & Struct to UnPack

It does the opposite process… let’s say we had a Long of 4 bytes like:


if we do struct.unpack(“!L”, “\x00\x00\x00\x01”) it will return (1,)

Python & Raw Sockets

The socket module allows us to make a call similar to setting up a server using raw sockets:

sniff_socket = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))

The most confusing part of the above for me was the socket.htons method and it’s parameter value of 0x0800.  Vivek Ramachandran explains that this is the protocol value (in hex) that we are interested in.  0x0800 is the IP protocol, if you check if_ether.h on a Linux system you should get the full list of hex codes avail.  I actually couldn’t find this on OSX itself, but I found a resource on EtherTypes at Wikipedia:

EtherType values for some notable protocols[6]
EtherType Protocol
0x0800 Internet Protocol version 4 (IPv4)
0x0806 Address Resolution Protocol (ARP)
0x0842 Wake-on-LAN[7]
0x22F3 IETF TRILL Protocol
0x6003 DECnet Phase IV
0x8035 Reverse Address Resolution Protocol
0x809B AppleTalk (Ethertalk)
0x80F3 AppleTalk Address Resolution Protocol (AARP)
0x8100 VLAN-tagged frame (IEEE 802.1Q) and Shortest Path Bridging IEEE 802.1aq[8]
0x8137 IPX
0x8204 QNX Qnet
0x86DD Internet Protocol Version 6 (IPv6)
0x8808 Ethernet flow control
0x8819 CobraNet
0x8847 MPLS unicast
0x8848 MPLS multicast
0x8863 PPPoE Discovery Stage
0x8864 PPPoE Session Stage
0x8870 Jumbo Frames (proposed)[2][3]
0x887B HomePlug 1.0 MME
0x888E EAP over LAN (IEEE 802.1X)
0x8892 PROFINET Protocol
0x889A HyperSCSI (SCSI over Ethernet)
0x88A2 ATA over Ethernet
0x88A4 EtherCAT Protocol
0x88A8 Provider Bridging (IEEE 802.1ad) & Shortest Path Bridging IEEE 802.1aq[9]
0x88AB Ethernet Powerlink[citation needed]
0x88CC Link Layer Discovery Protocol (LLDP)
0x88E1 HomePlug AV MME[citation needed]
0x88E3 Media Redundancy Protocol (IEC62439-2)
0x88E5 MAC security (IEEE 802.1AE)
0x88E7 Provider Backbone Bridges (PBB) (IEEE 802.1ah)
0x88F7 Precision Time Protocol (PTP) over Ethernet (IEEE 1588)
0x88FB Parallel Redundancy Protocol (PRP)
0x8902 IEEE 802.1ag Connectivity Fault Management (CFM) Protocol / ITU-T Recommendation Y.1731 (OAM)
0x8906 Fibre Channel over Ethernet (FCoE)
0x8914 FCoE Initialization Protocol
0x8915 RDMA over Converged Ethernet (RoCE)
0x891D TTEthernet Protocol Control Frame (TTE)
0x892F High-availability Seamless Redundancy (HSR)
0x9000 Ethernet Configuration Testing Protocol[10]


If you get the error:

AttributeError: ‘module’ object has no attribute ‘PF_PACKET’

this is due to PF_PACKET being a Linux specific attribute. As mentioned on Stack Overflow, if using Windows or OSX you should use AF_INET instead, like so:

sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)

The protocol attributes are not as easily discoverable (no handy dandy list that i can find…) For example, what if I wanted the ARP protocol….

At any rate, when you output your result, you’ll get some hex output and perhaps the interface ip.

Vivek Ramachandran runs through some Python slicing to get the first 14 bytes (Ethernet Header) with the following:

>>> eh = pkt[0][0:14]


That strips out the first 14 bytes which is the ethernet header

By bringing in the struct and binascii modules we can make this usable:

import struct

import binascii

and then do:

unpacked_header = struct.pack(“!6s6s2s”, eh)  # !6s6s2s is Big Endian format (!) of 14 bytes (6, 6, 2) 

binascii.hexlify(unpacked_header[0]) # returns a real result to us.



No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *