2015-03-27 15 views
12
import socket 
import os 
import struct 
import sys 
from ctypes import * 

# host to listen on 
host = sys.argv[1] 

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) 

# create a raw socket and bind it to the public interface 
if os.name == "nt": 
    socket_protocol = socket.IPPROTO_IP 
else: 
    socket_protocol = socket.IPPROTO_ICMP 

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) 

sniffer.bind((host, 0)) 

# we want the IP headers included in the capture 
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) 

# if we're on Windows we need to send some ioctls 
# to setup promiscuous mode 
if os.name == "nt": 
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) 

try: 
    while True: 

     # read in a single 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 "Protocol: %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address) 

except KeyboardInterrupt: 
    # if we're on Windows turn off promiscuous mode 
    if os.name == "nt": 
     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) 

Questo è il codice dal libro Black Hat Python. Questo codice dovrebbe annusare i socket grezzi e visualizzare le informazioni dall'intestazione IP. Funziona bene per me su Windows (usando Windows 8.1 a 64 bit). Quando si tenta di eseguire questo su Linux (Kali linux 1.1.0-amd64) ottengo il seguente errorePython Sniffing da Black Hat Python book

ValueError: Buffer size too small (20 instead of at least 32 bytes) 

Per aggirare questo ho aggiunto 12 spazi per il buffer come questo

ip_header = IP(raw_buffer[0:20]+' '*12) 

Quando lo faccio che ottengo il seguente errore

struct.error: 'L' format requires 0 <= number <= 4294967295 

Ciò si verifica sulla linea

self.src_address = socket.inet_ntoa(struct.pack("<L",self.src)) 

Ho provato a cambiare il simbolo prima della L su> e! e l'ho provato con L tutti mi danno lo stesso problema. Ho anche provato self.src avvolgendoli in ntohs in questo modo

self.src_address = socket.inet_ntoa(struct.pack("<L",socket.ntohs(self.src))) 

sto pensando questo ha qualcosa a che fare con endianness ma non sono sicuro. Qualsiasi aiuto sarebbe molto apprezzato.

NOTA: su Windows è necessario eseguire come amministratore e su Linux è necessario eseguire come superutente a causa dei socket non elaborati. Se lo esegui su Linux, apri un altro terminale e fai ping a www.google.com in modo da poter generare alcuni pacchetti ICMP da acquisire.

EDIT: Ho provato anche invertendo il buffer con

ip_header = IP(raw_buffer[0:20][::-1]+' '*12) 

EDIT 2: ho provato sia 65535 e 65534 nella riga sotto prima di fare qualsiasi degli altri elementi che ho elencato qui.

raw_buffer = sniffer.recvfrom(65565)[0] 

EDIT 3: Questo ha lavorato su una macchina Ubuntu in esecuzione pitone 2.7.6 e il mio kali distro era 2.7.3 così ho deciso di ottenere l'ultima versione di Python sulla mia casella di Kali che sembra essere 2.7.9 . Ancora senza fortuna.

ho messo il seguente codice alla nuova funzione nella mia struttura per visualizzare la dimensione di buffer

print sizeof(self) 

Sulle mia macchine Ubuntu e finestre era 20 però sulla mia macchina kali era 32

+1

il tuo codice funziona. sys.version '2.7.6 (predefinito, 22 marzo 2014, 22:59:38) \ n [GCC 4.8.2]'. 'uname -a' 'laptop Linux 3.16.0-25-generiC# 33-Ubuntu SMP mar 4 nov 12:05:25 UTC 2014 i686 i686 i686 GNU/Linux' –

+0

prova a passare l'intero buffer' IP (raw_buffer) 'oppure 'IP (raw_buffer [: 32])' –

+0

Interessante .. ho appena visto la versione di python che ho sulla mia macchina Linux che è 2.7.3 e la mia finestra è 2.7.8 ... proverò ad aggiornare e vedere cosa accade. –

risposta

19
#raw_buffer = sniffer.recvfrom(65565)[0] 
raw_buffer = sniffer.recvfrom(65535)[0] 

formato paket IP è (2^16) - 1

Il problema è con i sistemi a 32 bit vs 64.
ip_header = IP(raw_buffer[:20]) funziona su x86 Ubuntu.
ip_header = IP(raw_buffer[:32]) funziona su amd64 CentOS 6.6 Python 2.6.6
ip_header = IP(raw_buffer) funziona in entrambi.

Devi cambiare questi,

("src",   c_ulong), 
("dst",   c_ulong) 

self.src_address = socket.inet_ntoa(struct.pack("<L",self.src)) 
self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst)) 

in

("src",   c_uint32), 
("dst",   c_uint32) 

self.src_address = socket.inet_ntoa(struct.pack("@I",self.src)) 
self.dst_address = socket.inet_ntoa(struct.pack("@I",self.dst)) 

'@I' è unisigned int in modo nativo. perché c_ulong è 4 byte in i386 e 8 in amd64. Controllare quanto segue,

struct.calcsize('@BBHHHBBHLL') 

è 20 a 386 e 32 in amd64 che è formato di _fields_. In realtà sono 28 byte in amd64 più 4 byte riempiti per l'allineamento delle parole.

ip_header = IP(raw_buffer[:20]) ora funziona correttamente indipendentemente dalle piattaforme.

+0

Ho notato anche quello. Ho provato 65535 e 65534 prima di nient'altro che nessun valore ha funzionato –

+0

Basta leggere la modifica e funziona. Grazie Nizam! Ho intenzione di testare le risposte di your e MadMan2064 su più piattaforme e dare agli altri la possibilità di suonare prima di dare il controllo di risposta. –

+0

Per me, su Lubuntu 64bit, tutto ciò che ho cambiato sono stati i due c_ulong in c_uint32, e ha funzionato. Solo quei due c_type. Non è stato necessario modificare la stringa del formato del pacchetto (' Totem

4

Quindi è un problema 64/32 bit. Il fatto che avesse bisogno di 32 byte invece di 20 significa che la struttura non era imballata correttamente. "c_ulong" è 64 bit in linux a 64 bit e veniva mappato in quel modo nella classe "IP".

L'intestazione IP è di 20 byte + campi facoltativi. Gli indirizzi IP di origine e destinazione terminano con il byte 20, che è ciò che sta sollevando la struttura IP corrente. (se vuoi le opzioni, dovrai analizzarle a mano).

Ho cercato i campi di bit UDP e li ho impostati direttamente nella classe "IP". Guardando i documenti ctypes, i tipi interi possono essere mappati per limitare il numero di bit.

class IP(Structure): 

    _fields_ = [ 
     ("ihl",   c_ubyte, 4), 
     ("version",  c_ubyte, 4), 
     ("tos",   c_ubyte, 8), 
     ("len",   c_ushort, 16), 
     ("id",   c_ushort, 16), 
     ("offset",  c_ushort, 16), 
     ("ttl",   c_ubyte, 8), 
     ("protocol_num", c_ubyte, 8), 
     ("sum",   c_ushort, 16), 
     ("src",   c_uint, 32), 
     ("dst",   c_uint, 32), 
    ] 

Se si sommano gli offset bit, si sommano a 160. 160/8 = 20 byte, che è ciò ctypes confezioni questa struct a.

L'esecuzione di un ping produce qualcosa che sembra accettabile.

Protocol: ICMP 127.0.0.1 -> 127.0.0.1 
Protocol: ICMP 127.0.0.1 -> 127.0.0.1 
Protocol: ICMP 127.0.0.1 -> 127.0.0.1 
Protocol: ICMP 127.0.0.1 -> 127.0.0.1 

Inoltre, dimensione del pacchetto è una funzione di MTU (Maximum Transfer Unit o), quindi se si prevede di eseguire questo su Ethernet, il fattore limitante è la MTU del telaio. I pacchetti più grandi verranno frammentati nello stack tcp/ip prima di essere espulsi dalla porta ethernet.

$ ifconfig eth0 
eth0  Link encap:Ethernet HWaddr 00:00:00:ff:ff:ff 
      UP BROADCAST MULTICAST MTU:1500 Metric:1 

Inoltre, questa domanda dovrebbe aiutare a chiarire la questione sul perché alcune piattaforme hanno interi di dimensioni diverse e anela:

What is the bit size of long on 64-bit Windows?

In alternativa, ho scoperto che dpkt è piuttosto buona biblioteca per decodificare/codificare i pacchetti ip, a meno che non sia specificamente necessario utilizzare o volere i ctypes.

https://code.google.com/p/dpkt/