简体   繁体   中英

Python packet sniffer using ctypes crashes when copying socket buffer

I'm trying to capture the first 20 bytes (full packet minus the options) of an IP packet, populate a struct of ctype members, and print the information I want to the screen (protocol, source and destination address). My IP class is as follows:

import socket
import os
import struct
from ctypes import *

# host to listen on
host = "192.168.0.187"
# our IP header
class IP(Structure):
        _fields_ = [
            ("ihl", c_ubyte, 4),
            ("version", c_ubyte, 4),
            ("tos", c_ubyte),
            ("len", c_ushort),
            ("id", c_ushort),
            ("offset", c_ushort),
            ("ttl", c_ubyte),
            ("protocol_num", c_ubyte),
            ("sum", c_ushort),
            ("src", c_ulong),
            ("dst", c_ulong)
        ]
    def __new__(self, socket_buffer=None):
        return self.from_buffer_copy(socket_buffer)
    def __init__(self, socket_buffer=None):
        # map protocol constants to their names
        self.protocol_map = {1:"ICMP", 6:"TCP", 17:"UDP"}
        # human readable IP addresses
        self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
        self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))
        # human readable protocol
        try:
            self.protocol = self.protocol_map[self.protocol_num]
        except:
            self.protocol = str(self.protocol_num)

Now, I create the socket, bind it to the host, and loop to get the packets:

# create socket and bind to public interface (os dependent)
if os.name == "nt":
    socket_protocol = socket.IPPROTO_IP
else:
    socket_protocol = socket.IPPROTO_ICMP

# create raw socket
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
# include header information
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

try:
    while True:
        # read in a packet
        raw_buffer = sniffer.recvfrom(65565)[0]
        # create an IP header from the first 20 bytes of the buffer
        ip_header = IP(raw_buffer[0:20])
        # print out the protocol that was detected and the hosts
        print "Protocol: %s %s -> %s" % (ip_header.protocol, ip_header.src_¬
                                address, ip_header.dst_address)
# handle CTRL-C
except KeyboardInterrupt:
    # if we're using Windows, turn off promiscuous mode
    if os.name == "nt":
        sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

When run using c_ulong as data type for the "src" and "dst" _fields_ struct members, I get the following error (running in one terminal while pinging nostarch.com in another):

在此处输入图片说明

I postulated that perhaps the size of the c_ulong was larger than a byte, thus throwing off my requirement for the first 20 bytes (I'm very new to python). I then changed the c_ulong to c_ushort and ran it again:

在此处输入图片说明

Actual ping path:

在此处输入图片说明

So, while the script ran without error, it's cutting off the src and dst addresses.

Why is it asking for at least 32 bytes when I'm telling it I only want the first 20?

(I'm in Kali64 VBox VM, running on Win7 host, using Python 2.7)

Any help is appreciated.

The size of IP should be verified, ie

print(sizeof(IP))

should return 20 bytes. Since ctypes.u_long is 8 in case of 64bit linux, the size will be 32 bytes (4 bytes extra due to padding, 8 bytes due to integer sizes). Either use ctypes.u_int or explicit sizes as follows:

from ctypes import *

class IP(Structure):
    _fields_ = [ ("version", c_uint8, 4),
                 ("ihl", c_uint8, 4),
                 ("tos", c_uint8),
                 ("len", c_uint16),
                 ("id", c_uint16),
                 ("offset", c_uint16),
                 ("ttl", c_uint8),
                 ("protocol_num", c_uint8),
                 ("sum", c_uint16),
                 ("src", c_uint32),
                 ("dst", c_uint32) ]

print(sizeof(IP))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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