2015-03-27 18 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) 

To jest kod z książki Black Hat Python. Ten kod ma na celu wykrycie surowych gniazd i wyświetlenie informacji z nagłówka IP. Działa dobrze dla mnie w systemie Windows (przy użyciu Windows 8.1 64bit). Gdy próbuję uruchomić to na linux (Kali Linux 1.1.0-amd64) pojawia się następujący błądPython Sniffing z książki Black Hat Python

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

Aby obejść że dodałem 12 obowiązuje do bufora jak ten

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

When robię, że pojawia się następujący błąd

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

ten występuje na linii

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

Próbowałem zmienić symbol przed L na> i! i próbowałem tego z L, wszyscy oni dają mi ten sam problem. Próbowałem też owinąć self.src w ntohs jak tak

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

myślę to ma coś wspólnego z bajt, ale nie jestem pewien. Każda pomoc będzie bardzo ceniona.

UWAGA: W systemie Windows musisz działać jako administrator, a na Linuksie musisz działać jako superuser z powodu surowych gniazd. Jeśli uruchomisz to na Linuksie, otwórz inny terminal i pinguj www.google.com, aby móc wygenerować kilka pakietów ICMP do przechwycenia.

EDIT: Próbowałem również odwrócenie bufor z

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

EDIT 2: Próbowałem zarówno 65535 i 65534 na poniższej linii przed wykonaniem którejkolwiek z pozostałych pozycji I tutaj wymienione.

raw_buffer = sniffer.recvfrom(65565)[0] 

EDIT 3: To działało na komputerze z systemem ubuntu Pythona 2.7.6 i mój kali distro został 2.7.3 więc postanowiłem pobrać najnowszą wersję Pythona na moim polu kali co zdarza się 2.7.9 . Wciąż nie ma szczęścia.

umieściłem następujący kod do nowej funkcji w moim struktury aby zobaczyć rozmiar bufora

print sizeof(self) 

na moim Ubuntu i Windows maszyn było 20 ale na moim komputerze kali było 32

+1

Twój kod działa. sys.version '2.7.6 (domyślnie, 22 marca 2014, 22:59:38) \ n [GCC 4.8.2]'. 'uname -a' 'Linux laptop 3.16.0-25-generiC# 33-Ubuntu SMP Wt. 4 listopada 12:05:25 UTC 2014 i686 i686 i686 GNU/Linux' –

+0

spróbuj przekazać cały bufor' IP (raw_buffer) 'lub 'IP (raw_buffer [: 32])' –

+0

Interesujące .. po prostu przyjrzałem się wersji pythona, którą mam na moim komputerze w Linuksie, która jest 2.7.3, a moje okno to 2.7.8 ... Spróbuję uaktualnić i zobaczyć co dzieje się. –

Odpowiedz

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

wielkość paket IP (2^16) - 1

problem jest z 32 vs. 64 bitowych.
ip_header = IP(raw_buffer[:20]) działa na Ubuntu x86.
ip_header = IP(raw_buffer[:32]) działa na amd64 CentOS 6.6 Python 2.6.6
ip_header = IP(raw_buffer) działa w obu.

Musisz zmienić te,

("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)) 

do

("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' jest unisigned int w natywnej kolejności. ponieważ c_ulong ma 4 bajty w i386 i 8 w amd64. Sprawdź poniższe

struct.calcsize('@BBHHHBBHLL') 

20 w i386 i amd64 32 w którym jest wielkość _fields_. W rzeczywistości jest to 28 bajtów w amd64 plus 4 bajty dopełnione do wyrównania słów.

ip_header = IP(raw_buffer[:20]) teraz działa poprawnie niezależnie od platform.

+0

Też to zauważyłem. Próbowałem 65535 i 65534 zanim cokolwiek innego nie działało. –

+0

Po prostu przeczytaj edycję i działa. Dzięki Nizam! Zamierzam przetestować odpowiedzi zarówno twoich, jak i MadMan2064 na wielu platformach i dać innym szansę na zagłosowanie zanim trafię na czek odpowiedzi. –

+0

Dla mnie na 64-bitowym lubuntu wszystko, co zmieniłem, to dwa c_ulong do c_uint32 i zadziałało. Tylko te dwa c_types. Nie trzeba zmieniać łańcucha formatu pakietu (" Totem

4

Jest to problem 64/32 bitowy. Fakt, że potrzebował 32 bajtów zamiast 20 oznacza, że ​​struktura nie była poprawnie spakowana. "c_ulong" ma 64 bity w 64-bitowym linuksie i był mapowany w ten sposób w klasie "IP".

Nagłówek IP to 20 bajtów + pola opcjonalne. Źródłowe i docelowe adresy IP kończą się bajtem 20, co jest tym, co podnosi aktualna struktura IP. (Jeśli chcesz mieć opcje, będziesz musiał je przeanalizować ręcznie).

Sprawdziłem pola bitowe UDP i bezpośrednio ustawiłem je w klasie "IP". Patrząc na dokumenty ctypes, typy całkowite można odwzorować, aby ograniczyć liczbę bitów.

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), 
    ] 

Jeśli suma przesunięć bitowych, że sumują się do 160. 160/8 = 20 bajtów, czyli co ctypes podobne pakiety do tej struktury.

Uruchamianie tego przy użyciu polecenia ping daje coś, co wygląda na akceptowalne.

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 

Ponadto, rozmiar pakietu jest funkcją MTU (Maximum lub moduł transferu), więc jeśli planujesz działa to na ethernet, czynnikiem ograniczającym jest MTU ramki. Większe pakiety zostaną pofragmentowane w stosie tcp/ip przed wypchnięciem z portu Ethernet.

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

Ponadto, kwestia ta powinna przyczynić się do wyjaśnienia kwestii, dlaczego niektóre platformy mają różne wielkości ints i tęskni:

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

jako alternatywa, odkryłem, że dpkt jest raczej dobre biblioteki do dekodowania/kodowania pakietów IP, chyba że musisz użyć lub chcesz ctypes.

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