'''
version 6.1
Basic packet capture
October 2014
Version 5 : Classes based version
Version 6 : Capture to file (pcap format)
6.1 doc review
author: Guillermo Gomez (a.k.a. Momo)
'''
import argparse, os, socket, struct, time
import dpkt, IP_addr
'''
just keep in mind...rfc 791 and rfc 792
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
'''
def tcp_flags_to_text (tcp_flags): # returns a string with TCP FLAGS
tcp_flags_names = {1:'fin', 2:'syn', 4:'rst', 8:'push', 16:'ack', 32:'urg'}
flags_text=''
for i in range (5,-1,-1):
if tcp_flags >= 2**i:
tcp_flags -= 2**i
flags_text += tcp_flags_names[2**i]
flags_text += ' '
return flags_text
def icmp_type_to_text (icmp_type): # returns a string with ICMP Type
icmp_type_names = { 0:'Echo Reply',
3:'Destination Unreachable',
4:'Source Quench',
5:'Redirect',
6:'Alternate Host Address',
8:'Echo Request',
9:'Router Advertisement',
10:'Router Selection',
11:'Time Exceeded',
12:'Parameter Problem',
13:'Timestamp',
14:'Timestamp Reply',
15:'Information Request',
16:'Information Reply',
17:'Address Mask Request',
18:'Address Mask Reply',
30:'Traceroute',
31:'Datagram Conversion Error',
32:'Mobile Host Redirect',
37:'Domain Name Request',
38:'Domain Name Reply'}
try:
icmp_type_text = icmp_type_names[icmp_type]
except:
icmp_type_text = ''
return icmp_type_text
def pcap_header():
magic_number = '\xd4\xc3\xb2\xa1'
Major_version = '\x02\x00'
Minor_version = '\x04\x00'
GMT_ZONE_offset_dif = '\x00\x00\x00\x00'
Timestamp_Accuracy = '\x00\x00\x00\x00'
Snapshoot_Length = '\x00\x00\x04\x00'
LL_Header_Type = '\x01\x00\x00\x00'
Header = magic_number + \
Major_version + \
Minor_version + \
GMT_ZONE_offset_dif + \
Timestamp_Accuracy + \
Snapshoot_Length + \
LL_Header_Type
return Header
def pcap_pkt(buff, timestamp):
pkt_tmp = struct.pack ('I', timestamp)
pkt_mcr = struct.pack ('I', (1e6 * (timestamp % 1)))
Saved_size = struct.pack ('I', (len (buff) + 0xe))
Pkt_size = struct.pack ('I', (len (buff) + 0xe))
dst_mac = '\xff\xff\xff\xff\xff\xff'
src_mac = '\x00\x00\x00\x00\x00\x00'
Ether_type = '\x08\x00'
Packet_pcap = pkt_tmp + \
pkt_mcr + \
Saved_size + \
Pkt_size + \
dst_mac + \
src_mac + \
Ether_type + \
buff
return Packet_pcap
def main():
'''
Starts analyzer
'''
parser = argparse.ArgumentParser(
prog = 'dump',
description = 'Simple IP Packet Capture',
epilog = ' Hope you enjoy ;)')
parser.add_argument ('-f', dest='filter',\
help='Host or Network like "x.x.x.x/nn" or "x.x.x.x/m.m.m.m"')
parser.add_argument ('-p', dest='protocol', default ='ALL',\
help='Protocol to capture: ICMP, TCP, UDP, OTHERS or ALL')
parser.add_argument ('-n', dest='pktnum', type=int, default=100,\
help='Number of packets matching filter to capture')
parser.add_argument ('-c', dest='filename', \
help='Capture to file')
parser.add_argument ('-d', '--debug', action='store_true', \
help='Debug mode. Take care.')
options = parser.parse_args()
debug = options.debug
protocol_number = {'ICMP':1, 'TCP':6, 'UDP':17, 'OTHERS':0, 'ALL':0}
protocol_name = {1:'ICMP', 6:'TCP', 17:'UDP'}
# check protocol
options.protocol = options.protocol.upper()
if (options.protocol not in protocol_number):
print (parser.usage)
exit(0)
# check filter
if options.filter != None:
Filter_IP = IP_addr.IP_addr(options.filter)
if debug:
print (Filter_IP.address,
Filter_IP.mask,
Filter_IP.prefix,
Filter_IP.network,
Filter_IP.error_description)
if Filter_IP.error_description <> '':
print (Filter_IP.error_description)
exit (0)
else:
Filter_IP = IP_addr.IP_addr('0.0.0.0/0')
# check number of packets to capture
if options.pktnum == None:
repeat = 100
else:
repeat = options.pktnum
# check file name
if options.filename == None:
Capture_to_file = False
else:
Capture_to_file = True
Dump_file = open (options.filename , "wb" )
Dump_file.write(pcap_header())
# the public network interface
HOST = socket.gethostbyname(socket.gethostname())
# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))
# Include IP headers
# s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# receive requested number of packages
print ('\n Will Capture {} pakets of {} type.'.format(repeat, options.protocol))
print ('\n Will filter {}/{}\n'.format(Filter_IP.network, Filter_IP.prefix))
print ('\n Press Ctrl+C to stop\n')
total_tcp_pkt = total_udp_pkt = total_icmp_pkt = total_unk_pkt = total_pkt = analyzed_pkt = 0
capture_init = time.time()
PKT_IP_src = IP_addr.IP_addr()
PKT_IP_dst = IP_addr.IP_addr()
PKT_IP_src.prefix = PKT_IP_dst.prefix = Filter_IP.prefix
try:
while repeat > 0:
(buff, address) = s.recvfrom(65565)
pkt_time = time.time() - capture_init
IP_pkt = dpkt.ip.IP(buff)
PKT_IP_src.address = socket.inet_ntoa(IP_pkt.src)
PKT_IP_dst.address = socket.inet_ntoa(IP_pkt.dst)
PKT_IP_src.calculate_network()
PKT_IP_dst.calculate_network()
analyzed_pkt += 1
if Filter_IP.network == PKT_IP_src.network or Filter_IP.network == PKT_IP_dst.network:
if IP_pkt.p == 17 and options.protocol in ('UDP', 'ALL'): # UDP
repeat -= 1
total_udp_pkt += 1
UDP_pkt = dpkt.udp.UDP()
UDP_pkt = IP_pkt.data
print ('{:>8.4f} - {:>15}:{:>5} --> {:>15}:{:>5} : UDP'.format(
pkt_time,
PKT_IP_src.address, UDP_pkt.sport,
PKT_IP_dst.address, UDP_pkt.dport))
if Capture_to_file:
Dump_file.write(pcap_pkt(buff, time.time()))
if debug:
print (len(buff), '->', IP_pkt)
elif IP_pkt.p ==6 and options.protocol in ('TCP', 'ALL'): # TCP
repeat -= 1
total_tcp_pkt += 1
TCP_pkt = dpkt.tcp.TCP()
TCP_pkt = IP_pkt.data
print ('{:>8.4f} - {:>15}:{:>5} --> {:>15}:{:>5} : TCP {} '.format(
pkt_time,
PKT_IP_src.address, TCP_pkt.sport,
PKT_IP_dst.address, TCP_pkt.dport,
tcp_flags_to_text(TCP_pkt.flags)))
if Capture_to_file:
Dump_file.write(pcap_pkt(buff, time.time()))
if debug:
print (len(buff), '->', IP_pkt)
elif IP_pkt.p ==1 and options.protocol in ('ICMP', 'ALL'): # ICMP
repeat -= 1
total_icmp_pkt += 1
ICMP_pkt = dpkt.icmp.ICMP()
ICMP_pkt = IP_pkt.data
print ('{:>8.4f} - {:>15} --> {:>15} : ICMP {}'.format(
pkt_time,
PKT_IP_src.address,
PKT_IP_dst.address,
icmp_type_to_text(ICMP_pkt.type)))
if Capture_to_file:
Dump_file.write(pcap_pkt(buff, time.time()))
if debug:
print (len(buff), '->', IP_pkt)
elif IP_pkt.p not in (1,6,17) and options.protocol in ('OTHERS', 'ALL'): # OTHER protocol
repeat -= 1
total_unk_pkt += 1
print ('{:>8.4f} - {:>15} --> {:>15} : IP Protocol {:>5}'.format(
pkt_time,
PKT_IP_src.address,
PKT_IP_dst.address,
IP_pkt.p))
if Capture_to_file:
Dump_file.write(pcap_pkt(buff, time.time()))
if debug:
print (len(buff), '->', IP_pkt)
except KeyboardInterrupt:
pass
# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
# close capture file
if Capture_to_file:
Dump_file.close()
total_pkt = total_tcp_pkt + total_udp_pkt + total_icmp_pkt + total_unk_pkt
print ('\n Analyzed {} packets'.format(analyzed_pkt))
print ('\n Captured {} packets:'.format(total_pkt))
if total_pkt == 0:
exit(0)
print (' TCP : {:.>5} ___ {:.>3}%'.format(total_tcp_pkt, 100*total_tcp_pkt/total_pkt))
print (' UDP : {:.>5} ___ {:.>3}%'.format(total_udp_pkt, 100*total_udp_pkt/total_pkt))
print (' ICMP : {:.>5} ___ {:.>3}%'.format(total_icmp_pkt, 100*total_icmp_pkt/total_pkt))
print (' UNK : {:.>5} ___ {:.>3}%'.format(total_unk_pkt, 100*total_unk_pkt/total_pkt))
if __name__ == "__main__":
main()
No comments:
Post a Comment