#! /usr/bin/env python ############################################################################# ## ## ## scapy.py --- Interactive packet manipulation tool ## ## see http://www.secdev.org/projects/scapy/ ## ## for more informations ## ## ## ## Copyright (C) 2003 Philippe Biondi ## ## ## ## This program is free software; you can redistribute it and/or modify it ## ## under the terms of the GNU General Public License version 2 as ## ## published by the Free Software Foundation; version 2. ## ## ## ## This program is distributed in the hope that it will be useful, but ## ## WITHOUT ANY WARRANTY; without even the implied warranty of ## ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## ## General Public License for more details. ## ## ## ############################################################################# # # $Log: scapy.py,v $ # Revision 1.0.0.20 2005/08/28 18:01:12 pbi # - 802.11 tweaks # # Revision 1.0.0.19 2005/08/28 18:00:14 pbi # - added Packet.decode_payload_as() # # Revision 1.0.0.18 2005/08/28 17:51:05 pbi # - Added XShortEnumField() # # Revision 1.0.0.17 2005/08/17 18:11:13 pbi # - fixed crc32() computation for big endian systems # # Revision 1.0.0.16 2005/08/17 12:54:47 pbi # - fix regression introduced in 1.0.0.4 (netstat parsing) # # Revision 1.0.0.15 2005/08/16 17:00:35 pbi # - fixed socket creation/attach filter race condition for L2Socket and L3PacketSocket. # No more packets shoud go through the filter. # # Revision 1.0.0.14 2005/08/16 16:58:59 pbi # - don't return outgoing packets in L2Socket and L3PacketSocket # - L2Socket and L3PacketSocket don't catch the exception if conf.dissector=1 # # Revision 1.0.0.13 2005/08/16 16:56:09 pbi # - enhanced Packet.summary() code # # Revision 1.0.0.12 2005/08/16 16:53:31 pbi # - keep tcp/udp ports numeric in traceroute result # # Revision 1.0.0.11 2005/08/15 09:27:45 pbi # - added NTP.mysummary() # # Revision 1.0.0.10 2005/08/15 09:18:56 pbi # - fixed Ether.summary() (P. Lalet) # # Revision 1.0.0.9 2005/08/10 22:18:25 pbi # - moved code to build answering machines' functions into a metaclass # # Revision 1.0.0.8 2005/08/10 20:05:45 pbi # - added MobileIP protocol (rfc3344 and friends) (B. Andersson) # # Revision 1.0.0.7 2005/08/10 20:01:56 pbi # - changed Ether.mysummary() (P. Lalet) # - Update of Sebek protocols (P. Lalet) # # Revision 1.0.0.6 2005/08/10 19:53:19 pbi # - fix problem in declaraion of answering machine functions # # Revision 1.0.0.5 2005/08/10 15:43:03 pbi # - added resolution of numbers from /etc/ethertypes, /etc/protocols and # /etc/services (P. Lalet) # - tweaked some mysummary() accordingly # # Revision 1.0.0.4 2005/08/10 14:48:06 pbi # - Better netstat parsing for OpenBSD (P. Lalet) # # Revision 1.0.0.3 2005/08/10 14:41:21 pbi # - fixed regression introduced by previous patch : Gen and Packet are not # classes anymore but types. # # Revision 1.0.0.2 2005/08/09 21:40:57 pbi # - added ChangeDefaultValues metaclass to easily make a variant of a protocol # # Revision 1.0.0.1 2005/08/09 18:30:10 pbi # Release 1.0.0 # # Revision 1.0 2005/08/09 18:26:09 pbi # 1.0 release # # Revision 0.9.17.110 2005/08/09 18:19:17 pbi # - nothing # # Revision 0.9.17.109 2005/08/08 13:57:16 pbi # - replaced use of __builtins__ by globals() # - promiscuous mode is now default mode # - added HTML color theme # # Revision 0.9.17.108 2005/08/05 14:12:48 pbi # - fix: IP fragmentation offset needs to be 0 for payload to be decoded # (actually fixed in 0.9.17.106) # # Revision 0.9.17.107 2005/08/05 14:04:03 pbi # - added 'filter' parameter to PacketList.padding() # - added PacketList.nzpadding() method # - added 'lfilter' parameter to sniff() # # Revision 0.9.17.106 2005/08/05 14:02:19 pbi # - removed scapy module reloading to prepare interactive mode # - tweaked interact() function, now fully functionnal # # Revision 0.9.17.105 2005/07/20 16:24:06 pbi # - small fix nmap database class # # Revision 0.9.17.104 2005/07/20 16:22:51 pbi # - modified Packet.guess_payload_class() semantic : added the payload as parameter # - fixed TCP.answers() to take in account length of payload # - added timeout arg to arping() # # Revision 0.9.17.103 2005/06/07 10:18:27 pbi # - added a try/catch for get_if_hw_addr # - fixed the netstat parsing for OpenBSD # - changed Dot11WEP's key ID field from "key" to "keyid" # # Revision 0.9.17.102 2005/06/07 09:54:51 pbi # - added LEShortEnumField # - added L2CAP layer # - added Bluetooth supersocket # - added srbt() and srbt1() # # Revision 0.9.17.101 2005/05/30 17:21:48 pbi # - Fixes for 0.9.17.100 # # Revision 0.9.17.100 2005/05/30 17:08:41 pbi # - added NetBIOS, SMB & Co support (Sebastien Chenevot & Sylvain Sarmejeanne) # # Revision 0.9.17.99 2005/05/28 14:28:40 pbi # - WEP support and ICV computation # # Revision 0.9.17.98 2005/05/27 23:05:35 pbi # -fixed a smlal bug in graphic traceroute # # Revision 0.9.17.97 2005/05/27 19:53:04 pbi # - added WEP ciphering to Dot11WEP # # Revision 0.9.17.96 2005/05/25 15:15:10 pbi # - ability to give a WEP key as an argument to unwep() # # Revision 0.9.17.95 2005/05/25 15:05:03 pbi # - fixed pcap supersockets warnings # # Revision 0.9.17.94 2005/05/25 15:01:24 pbi # - fixed/cleaned ISAKMP # # Revision 0.9.17.93 2005/05/25 15:00:34 pbi # - fixed Packet.remove_underlayer() args # - fixed FieldLenField # - added Atheros Prism Header linktype # # Revision 0.9.17.92 2005/05/18 16:59:32 pbi # - some voip_play() stuff # # Revision 0.9.17.91 2005/05/18 16:59:01 pbi # - added BIOCIMMEDIATE option to fix BSD's BPF/pcap/select() behaviour issues # - made PCAP/DNET the default mode, even for Linux (it seems quicker) # # Revision 0.9.17.90 2005/05/18 16:57:07 pbi # - purge ARP cache when changing IP address of an interface # - fixed loopback interface detection get_if_raw_hwaddr() for dnet # - changed a bit Dot11PacketList behaviour # - fixed build() overload by EAP class # - fixed close()/recv() mix up in L2pcapListenSocket # # Revision 0.9.17.89 2005/05/03 19:18:22 pbi # - DNET/PCAP stuff reordering # # Revision 0.9.17.88 2005/05/03 00:10:12 pbi # - made Padding not be seen as a payload # # Revision 0.9.17.87 2005/04/29 22:37:39 pbi # - added L2 recognition for L2pcapListenSocket # - workarround for a bug in libpcap/wrapper?. .next() sometimes returns None # - added consistant get_if_addr() and get_if_raw_addr() # - added ifadd(), ifdel() and ifchange() methods to Route class # # Revision 0.9.17.86 2005/04/27 21:14:24 pbi # - small code cleaning # # Revision 0.9.17.85 2005/04/27 13:53:32 pbi # - early BSD port with libdnet and libpcap wrappers # # Revision 0.9.17.84 2005/04/24 14:57:45 pbi # - added a usable geolocation database from GeoIP. # # Revision 0.9.17.83 2005/04/24 10:34:57 pbi # - fixed fragment() (Peter Hardy) # # Revision 0.9.17.82 2005/04/23 15:29:21 pbi # - fixed sndrcv() when given an empty set of packets # # Revision 0.9.17.81 2005/04/23 13:55:32 pbi # - Some Sebek layers fixes (Pierre Lalet) # # Revision 0.9.17.80 2005/04/23 13:43:16 pbi # - Early IrDA support (Pierre Lalet) # # Revision 0.9.17.79 2005/04/23 13:42:34 pbi # - fixed SebekV1 and SebekV2 (Pierre Lalet) # # Revision 0.9.17.78 2005/04/23 13:41:33 pbi # - fixed BitField (Pierre Lalet) # # Revision 0.9.17.77 2005/04/23 13:36:15 pbi # - added threshold for warnings # # Revision 0.9.17.76 2005/04/23 11:27:51 pbi # - Renamed SndRcvAns into SndRcvList # # Revision 0.9.17.75 2005/04/23 11:26:12 pbi # - added color display in srloop() # # Revision 0.9.17.74 2005/04/22 13:30:10 pbi # - fixed dhcp_request() # - changed make_table semantic : take one lambda instead of 3 # - fixed import_hexcap() # - fixed StrLenField # - changed traceroute() and arping() to also return unanswered packets # - ls() now sorts its output alphabetically # - LaTeX color theme for straight copy/paste into your doc. # # Revision 0.9.17.73 2005/04/15 15:56:08 pbi # - fixed ARP.answers()' return value # - made TracerouteResult.graph() use both ASN information source # # Revision 0.9.17.72 2005/04/09 22:25:23 pbi # - fix route.route() to handle extended IP sets (ex. 192.168.*.1-5) # - generalised statistics in packet lists # - added Dot11PacketList() # - added some DHCP options # - fixes in DHCP options building # - modified unwep() to decrypt a WEP packet if it was not already done # # Revision 0.9.17.71 2005/04/06 10:49:11 pbi # - forgotten debug msg in Net() # # Revision 0.9.17.70 2005/04/04 17:58:15 pbi # - modified Net() to recognize things like 172.16.*.1-10 # # Revision 0.9.17.69 2005/04/04 14:24:00 pbi # - fix DHCP # - added dhcp_request() # # Revision 0.9.17.68 2005/03/28 22:18:04 pbi # - first attempt with time skew graphing # # Revision 0.9.17.67 2005/03/28 22:17:44 pbi # - use gzip compression for load_object/save_object # - made RandNum() and Emph() pickable # - changed prompt color in default color theme # # Revision 0.9.17.66 2005/03/28 14:30:01 pbi # - more DHCP work # # Revision 0.9.17.65 2005/03/28 14:29:03 pbi # - first attempt to generate libnet C code from a packet # # Revision 0.9.17.64 2005/03/28 14:28:20 pbi # - forgot to delete temporary variables in scapy's global scope # # Revision 0.9.17.63 2005/03/28 14:22:38 pbi # - added colors, color themes, colored prompt # # Revision 0.9.17.62 2005/03/24 16:19:33 pbi # - made it possible to use a PacketList as a parameter for send* or sr* # # Revision 0.9.17.61 2005/03/23 18:27:06 pbi # - used init_cookie for ISAKMP.answers() # - raised an exception in route.make_route if parameters are incomplete # # Revision 0.9.17.60 2005/03/23 17:07:56 pbi # - fixed session loading with -s # - prevented save_session() to trash current session # - changed AnsweringMachine to make send_reply() a bit more generic # # Revision 0.9.17.59 2005/03/22 16:52:44 pbi # - added _elt2show() to PacketList # - changed PacketList.show() to use _elt2show() # # Revision 0.9.17.58 2005/03/22 16:21:39 pbi # - added conversation() to PacketList # - added padding() to PacketList # - fixed StrNullField # - added haslayer_str() to Packet # - changed Packet.sprintf() to use haslayer_str # - changed answers() to ask payload if same class as other # - add count parameter to rdpcap # # Revision 0.9.17.57 2005/03/16 14:18:28 pbi # - added StrNullField # # Revision 0.9.17.56 2005/03/14 18:14:28 pbi # - LLNumTypes fix # - Added linktype recognition to PcapWriter class # # Revision 0.9.17.55 2005/03/14 17:59:23 pbi # - indentation cosmetic fix # # Revision 0.9.17.54 2005/03/14 17:53:56 pbi # - wrpcap() now writes the correct linktype in the pcap file # # Revision 0.9.17.53 2005/03/14 17:22:23 pbi # - added ISAKMP transforms decoding # # Revision 0.9.17.52 2005/03/14 16:40:58 pbi # - added ikescan() # - added ISAKMPTransformField # - fixed PacketList's private methods names do begin only with one "_" # # Revision 0.9.17.51 2005/03/14 13:03:11 pbi # - added a prn parameter to PacketList's summary() and nsummary() # # Revision 0.9.17.50 2005/03/14 12:56:24 pbi # - make internal methods of PacketResult begins with __ # # Revision 0.9.17.49 2005/03/14 12:52:41 pbi # - Deprecated display() method (for all objects). Use show() instead. # # Revision 0.9.17.48 2005/03/14 12:48:29 pbi # - Modified PacketField to stop at Padding instead of Raw # - Added PacketLenField # - More ISAKMP rework. Almost working. # # Revision 0.9.17.47 2005/03/14 10:20:49 pbi # - added unwep() method to Dot11 packets # - fixed 4 missing bytes in Dot11WEP # # Revision 0.9.17.46 2005/03/08 17:56:49 pbi # - added a possibility to give a hint for srp() to choose the intended interface # - added is_promisc() to find boxes in promisc mode (will not always work) (Javier Merino) # # Revision 0.9.17.45 2005/03/08 17:21:14 pbi # - added PacketField # - ISAKMP work # # Revision 0.9.17.44 2005/03/06 17:50:06 pbi # - changed PCAP and DNET defaults # # Revision 0.9.17.43 2005/03/03 17:15:26 pbi # - ISAKMP work # # Revision 0.9.17.42 2005/03/02 18:09:00 pbi # - added make_world_trace() method to TracerouteResult for a xtraceroute-like # # Revision 0.9.17.41 2005/02/20 22:33:55 pbi # - Sebek protocol definitions enhancements (Pierre Lalet) # # Revision 0.9.17.40 2005/02/20 22:31:49 pbi # - added ARP answering machine (farpd) (Pierre Lalet) # # Revision 0.9.17.39 2005/02/20 22:22:23 pbi # - Graphic traceroute enhanced to cope with TCP, UDP, ICMP or other traceroutes # - ASN clustering in graphic traceroute can be controlled with the "ASN" parameter # # Revision 0.9.17.38 2005/02/18 21:03:26 pbi # - MGCP early support # - RandString() # # Revision 0.9.17.37 2005/02/10 22:33:13 pbi # - export_object()/import_object() to copy/paste base64 gzipped pickled objects # - prevent save_session from deleting unpicklable objects # - added hexdump() and hexraw() methods to PacketList object # - Raw packet answers any Raw packet # - added conf.checkIPaddr to recognize broadcast replies (BOOTP/DHCP) # # Revision 0.9.17.36 2005/02/02 22:39:48 pbi # - added GPRS dummy packet class # # Revision 0.9.17.35 2005/01/29 00:29:25 pbi # - added l4 parameter to traceroute() for UDP, ICMP and other layer 4 traceroutes # - tweaked TracerouteResult display() # # Revision 0.9.17.34 2005/01/26 23:43:19 pbi # - removed some outdated functions # # Revision 0.9.17.33 2005/01/26 23:41:58 pbi # - small simplification of TracerouteResult display() thanks to new sprintf() # conditionnal statement # # Revision 0.9.17.32 2005/01/26 23:12:59 pbi # - added conditionnal statements in format strings # # Revision 0.9.17.31 2005/01/26 22:30:36 pbi # - removed an uneeded "else" in sprintf() # # Revision 0.9.17.30 2005/01/22 22:25:24 pbi # - re-added node coloring lost code line in traceroute graphing code # # Revision 0.9.17.29 2005/01/22 21:48:55 pbi # - fixed need for warning() before it was declared # # Revision 0.9.17.28 2005/01/22 21:47:11 pbi # - added ARPingResult to handle arping() results # - moved ARPing displaying logic to ARPing object # # Revision 0.9.17.27 2005/01/22 21:42:59 pbi # - added args todo_graph() # - added TracerouteResults object to handle traceroute results # - moved traceroute displaying logic to TracerouteResult object # - moved traceroute graphing logic to TracerouteResult object # # Revision 0.9.17.26 2005/01/20 22:59:07 pbi # - graph_traceroute : added AS clustering, colors, tweaks # # Revision 0.9.17.25 2005/01/17 22:10:58 pbi # - added do_graph() to draw GraphViz graphs using SVG output, displayed with ImageMagick # - added graph_traceroute() to make a graph from multiple traceroutes # - added timeout parameter to traceroute() # # Revision 0.9.17.24 2005/01/13 14:25:00 pbi # - added Sebek v1 and v2 protocols (Pierre Lalet) # # Revision 0.9.17.23 2005/01/10 21:55:14 pbi # - addded promisc and iface parameters to L3RawSocket # # Revision 0.9.17.22 2004/12/26 18:07:43 pbi # - Improved PacketList with stability by addition and slicing # - Added plot() to PacketList using Gnuplot # - Added StrStopField # - Added conf.debug_disssector to prevent dissector's exception from being catched # - Added CookedLinux packet type # - Show linktype number when it is unknown # # Revision 0.9.17.21 2004/12/26 16:04:57 pbi # - removed strace in soxmix command line # - DHCP support (from Mattias Wadman) # - added missing make_table to PacketList class # - have UDP class asks its payload for answers() # # Revision 0.9.17.20 2004/12/01 17:13:28 pbi # - Early WEP support # - voip_play() tweaks # - Added LEShortField for Dot11 SC field # # Revision 0.9.17.19 2004/10/18 13:42:50 pbi # - HSRP early support # - Cisco CSSP Skinny early support # - added Little Endian IntEnumField # - added filter() method to PacketList # - some voip_play() work # - loop parameter value in send*() is used as the time to sleep between 2 loops # # Revision 0.9.17.18 2004/09/21 21:45:20 pbi # - added recv() method to PcapReader to emulate a SuperSocket # - added "offline" parameter to sniff() to use sniff on pcap files # - removed voip_play_offline() and renamed voip_play_sniff() to voip_play() # which is now available to play offline # # Revision 0.9.17.17 2004/09/21 21:32:41 pbi # - added early PPPoE support (Ralf Ertzinger) # - fixed DNS summary() to handle empty queries or answers # # Revision 0.9.17.16 2004/09/21 14:58:15 pbi # - added VOIP playing functions (not tested) # # Revision 0.9.17.15 2004/09/17 22:00:47 pbi # - transfert traceroute() and arping() options to sndrcv() ("retry", etc.) # - fixed retry option in sndrcv() # - tweaked AnweringMachine class # - rewrited airpwn to use AnsweringMachine # # Revision 0.9.17.14 2004/09/13 16:57:01 pbi # - added loopback routing # # Revision 0.9.17.13 2004/09/12 21:44:45 pbi # - AnsweringMachine working as I wanted! # # Revision 0.9.17.12 2004/09/10 16:54:46 pbi # - AnsweringMachine twaking # - added DNS spoofing answering machine # # Revision 0.9.17.11 2004/09/08 13:42:38 pbi # - renamed ScapyPcapWriter class to PcapWriter # - added linktype parameter to PcapWriter (William McVey) # - added PcapReader class (William McVey) # # Revision 0.9.17.10 2004/09/08 13:06:01 pbi # - added some text correspondances to Radius code field # # Revision 0.9.17.9 2004/09/06 14:28:02 pbi # - early radius support # # Revision 0.9.17.8 2004/09/06 14:17:11 pbi # - added "store" parameter to sniff() # - added AnsweringMachine class to handle request/response protocols # - replaced bootpd by a AnsweringMachine subclass # - created DHCP answering machine draft # # Revision 0.9.17.7 2004/09/03 22:11:35 pbi # - finished airpwn() # # Revision 0.9.17.6 2004/08/13 16:49:51 pbi # - added first version of airpwn() clone # # Revision 0.9.17.5 2004/08/11 15:25:08 pbi # - added RIP protocol # # Revision 0.9.17.4 2004/08/09 14:00:20 pbi # - added gzip support to sessions saving # - can force pickle protocol to inferior values for pickle backward compatility # # Revision 0.9.17.3 2004/08/07 10:59:34 pbi # - fixed self reloading when launched from a different directory # - fixed session reloading problems with PacketList() and SndRcvAns() # - added load_session(), save_session(), update_session() # # Revision 0.9.17.2 2004/07/28 21:16:12 pbi # - added nsummary() method to SndRcvList() class # # Revision 0.9.17.1 2004/07/26 19:52:55 pbi # Release 0.9.17 # # Revision 0.9.16.18 2004/07/26 19:50:16 pbi # - added ScapyPcapWriter class (William McVey) # # Revision 0.9.16.17 2004/07/26 19:24:48 pbi # - do not need to be named 'scapy.py' anymore # - use of PacketList() for rdpcap() and sniff() # - fixed a bug in StrFixedLenField # - early IKE and ISAKMP support # # Revision 0.9.16.16 2004/07/16 15:39:37 pbi # - small fix on bootpd # # Revision 0.9.16.15 2004/07/10 13:13:25 pbi # - finished testing ethertype in supersockets to decide wether or not to apply BPF filters # # Revision 0.9.16.14 2004/07/10 13:06:38 pbi # - do not apply any BPF filter if ethertype is given to a supersocket (so that ARP requests will work # whatever the conf.except_filter value is) # # Revision 0.9.16.13 2004/07/09 09:11:15 pbi # - changed the header and blocked the licence to GPLv2 only # # Revision 0.9.16.12 2004/07/09 09:07:41 pbi # - added an independant routing table (conf.route) and methods to manipulate it # - tweaked results stats # # Revision 0.9.16.11 2004/07/05 22:43:49 pbi # - wrapper classes for results presentations and manipulation # - sndrcv() retry auto adjustment when giving a negative value # # Revision 0.9.16.10 2004/07/05 08:53:41 pbi # - added retry option to sndrcv() # - improved debug class # - added ottl() and hops() methods for IPTools class # - improved UDP and ICMP summary() # # Revision 0.9.16.9 2004/06/07 16:09:21 pbi # - fix again TCP.answers() and TCPerror.answers() # # Revision 0.9.16.8 2004/06/07 16:06:27 pbi # - fixed conf.checkIPsrc behaviour of answers() and hashret() for TCP/UDP/TCPerror/UDPerror # - added conf.debug_match to keep track of unanswered packets in debug.sent and debug.recv # # Revision 0.9.16.7 2004/06/07 09:20:43 pbi # - added LEIntField and StrFixedLenField # - added partial PrismHeader support # # Revision 0.9.16.6 2004/04/29 15:46:19 pbi # - fixed fragment() # # Revision 0.9.16.5 2004/03/31 09:24:43 pbi # - fix nmap fingerprint db parsing to handle the new format (Jochen Bartl) # # Revision 0.9.16.4 2004/03/23 08:45:10 pbi # - Support for reading big endian pcap files (Pekka Pietikainen) # # Revision 0.9.16.3 2004/02/28 11:12:12 pbi # - got rid of some future warnings (N. Bareil ) # - improved BitField() for arbitrary length bit fields (N. Bareil ) # - NTP protocol (N. Bareil ) # # Revision 0.9.16.2 2004/02/22 17:49:51 pbi # added first sketch of a bootp daemon: bootpd() # # Revision 0.9.16.1 2004/01/26 18:01:00 pbi # Release 0.9.16 # # Revision 0.9.15.15 2004/01/26 18:00:08 pbi # - added more text for DNS codes # # Revision 0.9.15.14 2004/01/15 13:24:48 pbi # - fixed the case where IP field is a list of nets # - randomize IPID in traceroute() to work better with conf.checkIPsrc=0 # - added make_tex_table() and make_lined_table() # - added IPID_count() to identify machines with their IPID # - added sport and dport args to fragleak() # # Revision 0.9.15.13 2004/01/11 11:47:07 pbi # - srploop() and srloop() improvements # # Revision 0.9.15.12 2004/01/11 01:28:21 pbi # - srloop() and srploop() improvements # # Revision 0.9.15.11 2004/01/11 01:07:05 pbi # - srloop() and srploop() improvements # # Revision 0.9.15.10 2004/01/10 23:42:58 pbi # - added srloop() and srploop() functions # # Revision 0.9.15.9 2004/01/10 23:40:51 pbi # - added # # Revision 0.9.15.8 2004/01/09 16:42:42 pbi # - improved send() and sendp() with parameters loop and verbose # # Revision 0.9.15.7 2004/01/09 16:04:07 pbi # - fixed ARP opcodes values # # Revision 0.9.15.6 2004/01/09 15:53:46 pbi # - added RARP and IARP req/resp description in ARP operation Enum field # # Revision 0.9.15.5 2003/12/19 15:54:30 pbi # - added checkIPID and checkIPsrc options in conf to recognize IP in ICMP errors from broken IP stacks (see conf.__doc__) # - changed default TCP source port to 20 (Muahahahah!) # - tweaked TCP summary # - changed default UDP source and destination ports to 53 # - created import_hexcap() to copy-paste an hexcap from tcpdump -xX, and get a string to feed IP() or ARP() or whatever # - created make_table() to present results in a table from a list, and functions that map the list to x,y and z=f(x,y). # # Revision 0.9.15.4 2003/10/30 16:11:41 pbi # - little enhancements to the DNS packets # - added dyndns_add() and dyndns_del() (rfc2136) # - fixed a format string error (3 times) # # Revision 0.9.15.3 2003/10/16 10:41:42 biondi # - redesign summary() method # - fixed Dot11 addresses fields # # Revision 0.9.15.2 2003/10/15 14:41:09 biondi # - caching format size (calcsize()) in Field main class # - allow first packet desassembly to fail in SuperSockets, falling back to Raw # # Revision 0.9.15.1 2003/10/02 15:24:29 pbi # Release 0.9.15 # # Revision 0.9.14.8 2003/10/02 15:16:26 pbi # - small fix for p0f_base # - lazy loading for p0f, queso and nmap knowledge databases # # Revision 0.9.14.7 2003/10/02 14:14:17 pbi # - added a LongField # - added classes and bonds for 802.11 # - added error handling and magic checks for rdpcap() # # Revision 0.9.14.6 2003/09/12 14:45:35 pbi # - had Dot11 working # # Revision 0.9.14.5 2003/09/12 10:04:05 pbi # - added summary() method to Packet objects # # Revision 0.9.14.4 2003/09/12 09:28:28 pbi # - added SNAP protocol # - catched broken pipe exception when shild die in sndrcv() # - fixed default L2socket type in srp() and srp1() (ETH_P_ALL) # - fixed format string in attach_filter() # # Revision 0.9.14.3 2003/09/10 08:47:41 pbi # - fixed the fact that bpf filters were generated in cooked mode, and thus did # not work # - filter on socket type ETH_P_ARP instead of using a bpf filter for ARP replies # - fixed the way of handling the SuperSocket close. # - uniformised the naming for interface parameter : iface instead of iff # - fixed the FutureWarning for long integers # - fixed a typo in 3 format strings (%*i instead of %i) # # Revision 0.9.14.2 2003/07/20 00:12:04 pbi # -added "-i any" for tcpdump to compile filters even if they don't work on main interface # - put PPP special case before layer 2 general case in a super socket # - added th filter parameter to L3RawSocket # - added a special case in getmacbyip() when loopback interface is concernet # - added value for RAWIP linktype in pcap capture files # # Revision 0.9.14.1 2003/06/25 13:18:23 pbi # Release 0.9.14, from 0.9.13.4 # # Revision 0.9.13.4 2003/06/25 12:35:57 pbi # - fixed a regression in L3PacketSocket for ppp links # # Revision 0.9.13.3 2003/05/31 14:01:12 biondi # - more tweaks on Packet.sprintf(). Added __doc__. # # Revision 0.9.13.2 2003/05/31 13:17:42 biondi # - small tweaks in Packet.sprintf() # # Revision 0.9.13.1 2003/05/16 13:34:30 pbi # Release 0.9.13 # # Revision 0.9.12.9 2003/05/16 13:32:38 pbi # - fixed verbose parameter in nmap_fp() # # Revision 0.9.12.8 2003/05/16 13:28:49 pbi # - small enhancements in self-documentation # - added early experiemental support for BOOTP and 802.11 # # Revision 0.9.12.7 2003/05/16 11:25:48 pbi # - added workarroung python bug 643005 (socket.inet_aton("255.255.255.255")) # - use answers() method instead of operator # - added hashret() method : returns a hash that is invariant for a packet and its reply # - use hashret() in sndrcv() for dramatic improvements for matching replies on big set of packets # - change report_ports() to return a string instead of printing # # Revision 0.9.12.6 2003/05/16 09:28:40 pbi # - improved the __repr__() method of Packet class # # Revision 0.9.12.5 2003/05/12 15:15:02 pbi # - added minttl parameter to traceroute() # # Revision 0.9.12.4 2003/05/06 13:39:21 pbi # - Improved random number object (thanks to O. Poyen) # # Revision 0.9.12.3 2003/05/06 10:45:27 pbi # - fixed a name overlap on "type" in L2ListenSocket and L3PacketSocket (thanks to E. M. Hopper) # # Revision 0.9.12.2 2003/05/06 10:41:58 pbi # - externalized conversion from probes to signature with nmap_probes2sig() use probe results from, say, a pcap file # # Revision 0.9.12.1 2003/04/27 10:07:30 pbi # Release 0.9.12 # # Revision 0.9.11.5 2003/04/27 10:04:03 pbi # - Fixed long int conversion in attach_filter() # # Revision 0.9.11.4 2003/04/27 10:00:57 pbi # - rectification in SetGen to unroll Gen instances in lists # - Completed DNS types and qtypes names # - Small tuning in nmap_match_one_sig() # - Parallelized nmap_sig() # # Revision 0.9.11.3 2003/04/24 12:47:49 pbi # - removed 4 byte IP string autorecognition. Never used and broken for 4 byte names # - added "islist" flag to fields to distinguish a list value from a list of values # - changed TCP options from dict to list to preserve order and redundancy # - added conf.except_filter, to have every command ignore your own traffic (BPF filter) # - worked in progress for nmap OS fingerprint. Added PU test. Fixed other tests. # - added nmap_sig2txt() to transform a signature to its text form, suitable for nmap base # # Revision 0.9.11.2 2003/04/23 21:23:30 pbi # - small fixes in init_queso() # - experimental support of nmap fingerprinting (not complete yet) # # Revision 0.9.11.1 2003/04/22 14:38:16 pbi # Release 0.9.11 # # Revision 0.9.10.8 2003/04/22 14:37:32 pbi # - fixed bug in getmacbyip() using dnet module # - deactivated getmacbyip() using dnet module because it did not resolve unknown IPs # - added some commands listed by lsc() # # Revision 0.9.10.7 2003/04/22 13:55:01 pbi # - some getattr/setattr/delattr enhancements # # Revision 0.9.10.6 2003/04/22 13:52:00 pbi # - added experimental support for QueSO OS fingerprinting. Has someone a *recent* database ? # # Revision 0.9.10.5 2003/04/18 17:45:15 pbi # - improved the completer to complete with protocol fields # - small fix in get_working_if() # # Revision 0.9.10.4 2003/04/16 14:53:36 pbi # - added option to include padding or not # # Revision 0.9.10.3 2003/04/16 14:35:32 pbi # - added L2dnetSocket() # - improved arping() # # Revision 0.9.10.2 2003/04/16 12:40:40 pbi # - fixed the case when the history file does not exist # # Revision 0.9.10.1 2003/04/14 15:43:45 pbi # Release 0.9.10 # # Revision 0.9.9.15 2003/04/14 15:42:47 pbi # - added L3pcapListenSocket # - fixed L3ListenSocket to use ETH_P_ALL instead of ETH_P_IP by default # # Revision 0.9.9.14 2003/04/14 14:57:53 pbi # - reworked L3dnetSocket # # Revision 0.9.9.13 2003/04/14 13:53:28 pbi # - added completion (rlcompleter) and history support # # Revision 0.9.9.12 2003/04/14 10:05:42 pbi # - bugfixed the close() method of some supersockets # # Revision 0.9.9.11 2003/04/13 21:41:01 biondi # - added get_working_if() # - use get_working_if() for default interface # # Revision 0.9.9.10 2003/04/12 23:33:42 biondi # - add DNS layer (do not compress when assemble, answers() is missing) # # Revision 0.9.9.9 2003/04/12 22:15:40 biondi # - added EnumField # - used EnumField for ARP(), ICMP(), IP(), EAPOL(), EAP(),... # # Revision 0.9.9.8 2003/04/11 16:52:29 pbi # - better integration of libpcap and libdnet, if available # # Revision 0.9.9.7 2003/04/11 15:49:31 pbi # - some tweaks about supersockets close() and __del__() (not satisfied) # - added L3dnetSocket, that use libdnet and libpcap if available # # Revision 0.9.9.6 2003/04/11 13:46:49 pbi # - fixed a regression in bitfield dissection # - tweaked and fixed a lot of small things arround supersockets # # Revision 0.9.9.5 2003/04/10 14:50:22 pbi # - clean session only if it is to be saved # - forgot to give its name to Padding class # - fixed the NoPayload comparison tests so that they work on reloaded sessions # # Revision 0.9.9.4 2003/04/10 13:45:22 pbi # - Prepared the configuration of L2/L3 supersockets # # Revision 0.9.9.3 2003/04/08 18:34:48 pbi # - little fix in L2ListenSocket.__del__() # - added doc and options in Conf class # - added promisc support for L3PacketSocket, so that you can get answers to spoofed packets # # Revision 0.9.9.2 2003/04/08 17:42:19 pbi # - added extract_padding() method to UDP # # Revision 0.9.9.1 2003/04/08 17:23:33 pbi # Release 0.9.9 # # Revision 0.9.8.9 2003/04/08 17:22:25 pbi # - use cPickle instead of pickle (quicker and works with __getattr__() recursion) # - small fixes on send() and sendp() # # Revision 0.9.8.8 2003/04/08 16:48:04 pbi # - EAPOL overload Ether dst with PAE_GROUP_ADDR # - tuning in ports_report() # - tuning in fragleak # # Revision 0.9.8.7 2003/04/07 15:32:10 pbi # - uses /usr/bin/env invocation # # Revision 0.9.8.6 2003/04/07 14:57:12 pbi # - catch error during payload dissection and consider payload as raw data # # Revision 0.9.8.5 2003/04/07 14:43:13 pbi # - srp() becomes srp1() and sr() equivalent for L2 is called srp() # - hastype() Packet methods renamed to haslayer() # - added getlayer() Packet method # - added padding detection for layers that have a length field # - added fragment() that fragment an IP packet # - added report_ports() to scan a machine and output LaTeX report # # Revision 0.9.8.4 2003/04/01 11:19:06 pbi # - added FlagsField(), used for TCP and IP # - rfc3514 compliance # # Revision 0.9.8.3 2003/03/28 14:55:18 pbi # Added pkt2uptime() : uses TCP timestamp to predict when the machine was booted # # Revision 0.9.8.2 2003/03/27 15:58:54 pbi # - fixed sprintf() regression to use attributes from a packet that are not fields (eg: payload) # # Revision 0.9.8.1 2003/03/27 15:43:20 pbi # Release 0.9.8 # # Revision 0.9.7.9 2003/03/27 15:07:42 pbi # - add filter support for sr(), sr1() and srp() # - use filters for getmacbyip() and traceroute() for better reliability under heavy load # # Revision 0.9.7.8 2003/03/27 14:45:11 pbi # - better timeout management in sndrcv # - bugfixed sys.exit() imbrication issues # - some self documentation # - added lsc()command # # Revision 0.9.7.7 2003/03/26 17:51:33 pbi # - Added IPTool class, to add commands like whois() to IP layer. # - Have unknown class attributes be asked to payload before raising an exception. # # Revision 0.9.7.6 2003/03/26 17:35:36 pbi # More powerful sprintf format string : %[fmt[r],][cls[:nb].]field% where fmt is a classic one, r can be # appended for raw substitution (ex: IP.flags=0x18 instead of SA), nb is the number of the layer we want # (ex: for IP/IP packets, IP:2.src is the src of the upper IP layer). Special case : "%.time" is the creation time. # Ex : p.sprintf("%.time% %-15s,IP.src% -> %-15s,IP.dst% %IP.chksum% %03xr,IP.proto% %r,TCP.flags%") # # Revision 0.9.7.5 2003/03/26 14:47:39 pbi # Added creation time packet. Supported by read/write pcap. # # Revision 0.9.7.4 2003/03/26 14:25:09 pbi # Added the NoPayload terminal class # # Revision 0.9.7.3 2003/03/26 13:31:11 pbi # Fixed RCS Id # # Revision 0.9.7.2 2003/03/26 13:30:05 pbi # Adding RCS Id # # from __future__ import generators RCSID="$Id: scapy.py,v 1.0.0.20 2005/08/28 18:01:12 pbi Exp $" VERSION = RCSID.split()[2]+"beta" def usage(): print "Usage: scapy.py [-s sessionfile]" sys.exit(0) ##########[XXX]#=-- ## # Next things to do : # # - fields to manage variable length hw addr (ARP, BOOTP, etc.) # - improve pcap capture file support # - better self-doc # ## ##########[XXX]#=-- ############################# ##### Logging subsystem ##### ############################# import logging,traceback,time class ScapyFreqFilter(logging.Filter): def __init__(self): logging.Filter.__init__(self) self.warning_table = {} def filter(self, record): wt = conf.warning_threshold if wt > 0: stk = traceback.extract_stack(limit=1) caller = stk[0][1] tm,nb = self.warning_table.get(caller, (0,0)) ltm = time.time() if ltm-tm > wt: tm = ltm nb = 0 else: if nb < 2: nb += 1 if nb == 2: record.msg = "more "+record.msg else: return 0 self.warning_table[caller] = (tm,nb) return 1 log_scapy = logging.getLogger("scapy") console_handler = logging.StreamHandler() console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) log_scapy.addHandler(console_handler) log_runtime = logging.getLogger("scapy.runtime") # logs at runtime log_runtime.addFilter(ScapyFreqFilter()) log_interactive = logging.getLogger("scapy.interactive") # logs in interactive functions log_loading = logging.getLogger("scapy.loading") # logs when loading scapy if __name__ == "__main__": log_scapy.setLevel(1) ################## ##### Module ##### ################## import socket, sys, getopt, string, struct, random, os import cPickle, copy, types, gzip, base64, re from select import select from fcntl import ioctl import fcntl try: import Gnuplot GNUPLOT=1 except ImportError: log_loading.info("did not find gnuplot lib. Won't be able to plot") GNUPLOT=0 LINUX=sys.platform.startswith("linux") OPENBSD=sys.platform.startswith("openbsd") BIG_ENDIAN= struct.pack("H",1) == "\x00\x01" if LINUX: DNET=PCAP=0 else: DNET=PCAP=1 if PCAP: try: import pcap PCAP = 1 except ImportError: log_loading.info("did not find pcap module. Fallback to linux primitives") PCAP = 0 if DNET: try: import dnet DNET = 1 except ImportError: log_loading.info("did not find dnet module. Fallback to linux primitives") DNET = 0 try: from Crypto.Cipher import ARC4 except ImportError: log_loading.info("Can't find Crypto python lib. Won't be able to decrypt WEP") # Workarround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470 try: socket.inet_aton("255.255.255.255") except socket.error: def inet_aton(x): if x == "255.255.255.255": return "\xff"*4 else: return socket.inet_aton(x) else: inet_aton = socket.inet_aton ############ ## Consts ## ############ ETHER_ANY = "\x00"*6 ETHER_BROADCAST = "\xff"*6 ETH_P_ALL = 3 ETH_P_IP = 0x800 ETH_P_ARP = 0x806 # From net/if_arp.h ARPHDR_ETHER = 1 ARPHDR_METRICOM = 23 ARPHDR_PPP = 512 ARPHDR_LOOPBACK = 772 # From bits/ioctls.h SIOCGIFHWADDR = 0x8927 # Get hardware address SIOCGIFADDR = 0x8915 # get PA address SIOCGIFNETMASK = 0x891b # get network PA mask SIOCGIFNAME = 0x8910 # get iface name SIOCSIFLINK = 0x8911 # set iface channel SIOCGIFCONF = 0x8912 # get iface list SIOCGIFFLAGS = 0x8913 # get flags SIOCSIFFLAGS = 0x8914 # set flags SIOCGIFINDEX = 0x8933 # name -> if_index mapping SIOCGIFCOUNT = 0x8938 # get number of devices # From if.h IFF_UP = 0x1 # Interface is up. IFF_BROADCAST = 0x2 # Broadcast address valid. IFF_DEBUG = 0x4 # Turn on debugging. IFF_LOOPBACK = 0x8 # Is a loopback net. IFF_POINTOPOINT = 0x10 # Interface is point-to-point link. IFF_NOTRAILERS = 0x20 # Avoid use of trailers. IFF_RUNNING = 0x40 # Resources allocated. IFF_NOARP = 0x80 # No address resolution protocol. IFF_PROMISC = 0x100 # Receive all packets. # From netpacket/packet.h PACKET_ADD_MEMBERSHIP = 1 PACKET_DROP_MEMBERSHIP = 2 PACKET_RECV_OUTPUT = 3 PACKET_RX_RING = 5 PACKET_STATISTICS = 6 PACKET_MR_MULTICAST = 0 PACKET_MR_PROMISC = 1 PACKET_MR_ALLMULTI = 2 # From bits/socket.h SOL_PACKET = 263 # From asm/socket.h SO_ATTACH_FILTER = 26 SOL_SOCKET = 1 # From net/route.h RTF_UP = 0x0001 # Route usable # From BSD net/bpf.h #BIOCIMMEDIATE=0x80044270 BIOCIMMEDIATE=-2147204496 MTU = 1600 # file parsing to get some values : spaces = re.compile("[ \t]+|\n") IP_PROTOS={} try: f=open("/etc/protocols") for l in f: try: if l[0] in ["#","\n"]: continue lt = tuple(re.split(spaces, l)) if len(lt) < 3: continue IP_PROTOS.update({lt[2]:int(lt[1])}) except: log_loading.info("Couldn't parse one line from protocols file (" + l + ")") f.close() except IOError: log_loading.info("Can't open protocols file") ETHER_TYPES={} try: f=open("/etc/ethertypes") for l in f: try: if l[0] in ["#","\n"]: continue lt = tuple(re.split(spaces, l)) if len(lt) < 2: continue ETHER_TYPES.update({lt[0]:int(lt[1], 16)}) except: log_loading.info("Couldn't parse one line from ethertypes file (" + l + ")") f.close() except IOError,msg: log_loading.info("Can't open ethertypes file") TCP_SERVICES={} UDP_SERVICES={} try: f=open("/etc/services") for l in f: try: if l[0] in ["#","\n"]: continue lt = tuple(re.split(spaces, l)) if len(lt) < 2: continue if lt[1].endswith("/tcp"): TCP_SERVICES.update({lt[0]:int(lt[1].split('/')[0])}) elif lt[1].endswith("/udp"): UDP_SERVICES.update({lt[0]:int(lt[1].split('/')[0])}) except: log_loading.warning("Couldn't parse one line from protocols file (" + l + ")") f.close() except IOError: log_loading.info("Can't open services file") ########### ## Tools ## ########### def sane(x): r="" for i in x: j = ord(i) if (j < 32) or (j >= 127): r=r+conf.color_theme.not_printable+"."+conf.color_theme.normal else: r=r+i return r def hexdump(x): x=str(x) l = len(x) for i in range(l): print "%02X" % ord(x[i]), if (i % 16 == 15): print " "+sane(x[i-15:i+1]) if ((l%16) != 0): print " "*(16-(l%16))+" "+sane(x[l-(l%16):]) def linehexdump(x): x = str(x) l = len(x) for i in range(l): print "%02X" % ord(x[i]), print " "+sane(x) if BIG_ENDIAN: CRCPOLY=0x04c11db7L else: CRCPOLY=0xedb88320L def crc32(crc, x): for c in x: crc ^= ord(c) for i in range(8): if crc & 1: crc y = CRCPOLY else: y = 0 crc >>= 1 crc ^= y return crc def checksum(pkt): pkt=str(pkt) s=0 if len(pkt) % 2 == 1: pkt += "\0" for i in range(len(pkt)/2): s = s + (struct.unpack("!H",pkt[2*i:2*i+2])[0]) s = (s >> 16) + (s & 0xffff) s += s >> 16 return ~s & 0xffff def warning(x): log_runtime.warning(x) def mac2str(mac): return "".join(map(lambda x: chr(int(x,16)), mac.split(":"))) def str2mac(s): return ("%02x:"*6)[:-1] % tuple(map(ord, s)) def strxor(x,y): return "".join(map(lambda x,y:chr(ord(x)^ord(y)),x,y)) def atol(x): try: ip = inet_aton(x) except socket.error: ip = inet_aton(socket.gethostbyname(x)) return struct.unpack("I", ip)[0] def ltoa(x): return socket.inet_ntoa(struct.pack("I", x)) def do_graph(graph,type="svg",target="| display"): """do_graph(graph, type="svg",target="| display"): graph: GraphViz graph description type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option target: filename or redirect. Defaults pipe to Imagemagick's display program""" w,r = os.popen2("dot -T %s %s" % (type,target)) w.write(graph) w.close() ############################## ## Session saving/restoring ## ############################## def save_session(fname, session=None, pickleProto=-1): if session is None: session = scapy_session to_be_saved = session.copy() if to_be_saved.has_key("__builtins__"): del(to_be_saved["__builtins__"]) for k in to_be_saved.keys(): if type(to_be_saved[k]) in [types.TypeType, types.ClassType, types.ModuleType]: log_interactive.error("[%s] (%s) can't be saved." % (k, type(to_be_saved[k]))) del(to_be_saved[k]) try: os.rename(fname, fname+".bak") except OSError: pass f=gzip.open(fname,"w") cPickle.dump(to_be_saved, f, pickleProto) f.close() def load_session(fname): try: s = cPickle.load(gzip.open(fname)) except IOError: s = cPickle.load(open(fname)) scapy_session.clear() scapy_session.update(s) def update_session(fname): try: s = cPickle.load(gzip.open(fname)) except IOError: s = cPickle.load(open(fname)) scapy_session.update(s) def export_object(obj): print base64.encodestring(gzip.zlib.compress(cPickle.dumps(obj,2),9)) def import_object(obj=None): if obj is None: obj = sys.stdin.read() return cPickle.loads(gzip.zlib.decompress(base64.decodestring(obj.strip()))) def save_object(fname, obj): cPickle.dump(obj,gzip.open(fname,"w")) def load_object(fname): return cPickle.load(gzip.open(fname)) ################# ## Debug class ## ################# class debug: recv=[] sent=[] match=[] #################### ## IP Tools class ## #################### class IPTools: """Add more powers to a class that have a "src" attribute.""" def whois(self): os.system("whois %s" % self.src) def ottl(self): t = [32,64,128,255]+[self.ttl] t.sort() return t[t.index(self.ttl)+1] def hops(self): return self.ottl()-self.ttl-1 ############################## ## Routing/Interfaces stuff ## ############################## class Route: def __init__(self): self.resync() self.s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def resync(self): self.routes = read_routes() def __repr__(self): rt = "Network Netmask Gateway Iface Output IP\n" for net,msk,gw,iface,addr in self.routes: rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net), ltoa(msk), gw, iface, addr) return rt def make_route(self, host=None, net=None, gw=None, dev=None): if host is not None: thenet,msk = host,32 elif net is not None: thenet,msk = net.split("/") msk = int(msk) else: raise Exception("make_route: Incorrect parameters. You should specify a host or a net") if gw is None: gw="0.0.0.0" if dev is None: if gw: nhop = gw else: nhop = thenet dev,ifaddr,x = self.route(nhop) else: ifaddr = get_if_addr(dev) return (atol(thenet),(1L<= 0: ok = 1 if l.find("Mtu") >= 0: mtu = True continue if ok == 0: continue if not l: break if mtu: dest,gw,fl,ref,use,mtu,netif = l.split()[:7] else: dest,gw,fl,ref,use,netif = l.split()[:6] if dest == "default": dest = 0L netmask = 0L else: if "/" in dest: dest,netmask = dest.split("/") netmask = (1L << int(netmask))-1 else: netmask = (1L << ((dest.count(".")+1)*8))-1 dest += ".0"*(3-dest.count(".")) dest, = struct.unpack("I",inet_aton(dest)) if not "G" in fl: gw = '0.0.0.0' ifaddr = get_if_addr(netif) routes.append((dest,netmask,gw,netif,ifaddr)) f.close() return routes def read_interfaces(): i = dnet.intf() ifflist = {} def addif(iff,lst): if not iff.has_key("addr"): return if not iff.has_key("link_addr"): return rawip = iff["addr"].data ip = socket.inet_ntoa(rawip) rawll = iff["link_addr"].data ll = str2mac(rawll) lst[iff["name"]] = (rawll,ll,rawip,ip) i.loop(addif, ifflist) return ifflist else: def read_routes(): f=open("/proc/net/route","r") routes = [] s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x","lo")) addrfamily = struct.unpack("h",ifreq[16:18])[0] if addrfamily == socket.AF_INET: ifreq2 = ioctl(s, SIOCGIFNETMASK,struct.pack("16s16x","lo")) msk = struct.unpack("I",ifreq2[20:24])[0] dst = struct.unpack("I",ifreq[20:24])[0] & msk ifaddr = socket.inet_ntoa(ifreq[20:24]) routes.append((dst, msk, "0.0.0.0", "lo", ifaddr)) else: warning("Interface lo: unkownn address family (%i)"% addrfamily) for l in f.readlines()[1:]: iff,dst,gw,flags,x,x,x,msk,x,x,x = l.split() if int(flags,16) & RTF_UP == 0: continue ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",iff)) addrfamily = struct.unpack("h",ifreq[16:18])[0] if addrfamily == socket.AF_INET: ifaddr = socket.inet_ntoa(ifreq[20:24]) else: warning("Interface %s: unkownn address family (%i)"%(iff, addrfamily)) continue routes.append((long(dst,16), long(msk,16), socket.inet_ntoa(struct.pack("I",long(gw,16))), iff, ifaddr)) f.close() return routes def get_if(iff,cmd): s=socket.socket() ifreq = ioctl(s, cmd, struct.pack("16s16x",iff)) s.close() return ifreq def get_if_index(iff): return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0]) def get_if_addr(iff): return socket.inet_ntoa(get_if_raw_addr(iff)) def get_if_hwaddr(iff): addrfamily, mac = get_if_raw_hwaddr(iff) if addrfamily in [ARPHDR_ETHER,ARPHDR_LOOPBACK]: return str2mac(mac) else: raise Exception("Unsupported address family (%i)"%addrfamily) ##################### ## ARP cache stuff ## ##################### ARPTIMEOUT=120 # XXX Fill arp_cache with /etc/ether and arp cache arp_cache={} if 0 and DNET: ## XXX Can't use this because it does not resolve IPs not in cache dnet_arp_object = dnet.arp() def getmacbyip(ip): iff,a,gw = conf.route.route(ip) if iff == "lo": return "ff:ff:ff:ff:ff:ff" if gw != "0.0.0.0": ip = gw res = dnet_arp_object.get(dnet.addr(ip)) if res is None: return None else: return res.ntoa() else: def getmacbyip(ip): iff,a,gw = conf.route.route(ip) if iff == "lo": return "ff:ff:ff:ff:ff:ff" if gw != "0.0.0.0": ip = gw if arp_cache.has_key(ip): mac, timeout = arp_cache[ip] if timeout and (time.time()-timeout < ARPTIMEOUT): return mac res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip), type=ETH_P_ARP, iface = iff, timeout=2, verbose=0) if res is not None: mac = res.payload.hwsrc arp_cache[ip] = (mac,time.time()) return mac return None #################### ## Random numbers ## #################### class RandField: pass class RandNum(RandField): min = 0 max = 0 def __init__(self, min, max): self.min = min self.max = max def randint(self): # XXX: replace with sth that guarantee unicity return random.randint(self.min, self.max) def __getattr__(self, attr): return getattr(self.randint(), attr) class RandByte(RandNum): def __init__(self): RandNum.__init__(self, 0, 255) class RandShort(RandNum): def __init__(self): RandNum.__init__(self, 0, 65535) class RandInt(RandNum): def __init__(self): # Well, 2147483647 won't be reached because max+1 must be int # and 2147483647+1 is longint. (random module limitation) RandNum.__init__(self, 0, 2147483646) class RandString(RandField): def __init__(self, size, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"): self.chars = chars self.size = size def randstr(self): s = "" for i in range(self.size): s += random.choice(self.chars) return s def __getattr__(self, attr): return getattr(self.randstr(), attr) ################ ## Generators ## ################ class Gen(object): def __iter__(self): return iter([]) class SetGen(Gen): def __init__(self, set): if type(set) is list: self.set = set elif isinstance(set, PacketList): self.set = list(set) else: self.set = [set] def transf(self, element): return element def __iter__(self): for i in self.set: if (type(i) is tuple) and (len(i) == 2): if (i[0] <= i[1]): j=i[0] while j <= i[1]: yield j j += 1 elif isinstance(i, Gen): for j in i: yield j else: yield i def __repr__(self): return "" % self.set.__repr__() class Net(Gen): """Generate a list of IPs from a network address or a name""" name = "ip" ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$") def __init__(self, net): self.repr=net tmp=net.split('/')+["32"] if not self.ipaddress.match(net): tmp[0]=socket.gethostbyname(tmp[0]) netmask = int(tmp[1]) def parse_digit(a,netmask): netmask = min(8,max(netmask,0)) if a == "*": a = (0,256) elif a.find("-") >= 0: x,y = map(int,a.split("-")) if x > y: y = x a = (x & (0xffL<>(8-netmask))))+1) else: a = (int(a) & (0xffL<>(8-netmask)))+1) return a self.parsed = map(lambda x,y: parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32))) def __iter__(self): for d in xrange(*self.parsed[3]): for c in xrange(*self.parsed[2]): for b in xrange(*self.parsed[1]): for a in xrange(*self.parsed[0]): yield "%i.%i.%i.%i" % (a,b,c,d) def __repr__(self): return "" % self.repr ############# ## Results ## ############# class PacketList: res = [] def __init__(self, res, name="PacketList", stats=None): if stats is None: stats = [ TCP,UDP,ICMP ] self.stats = stats self.res = res self.listname = name def _elt2pkt(self, elt): return elt def _elt2sum(self, elt): return elt.summary() def _elt2show(self, elt): return self._elt2sum(elt) def __repr__(self): # stats=dict.fromkeys(self.stats,0) ## needs python >= 2.3 :( stats = dict(map(lambda x: (x,0), self.stats)) other = 0 for r in self.res: f = 0 for p in stats: if self._elt2pkt(r).haslayer(p): stats[p] += 1 f = 1 break if not f: other += 1 s = "" for p in stats: s += " %s%s%s:%s%i%s" % (conf.color_theme.packetlist_proto, p.name, conf.color_theme.punct, conf.color_theme.packetlist_value, stats[p], conf.color_theme.punct) s += " %sOther%s:%s%i%s" % (conf.color_theme.packetlist_proto,conf.color_theme.punct, conf.color_theme.packetlist_value, other, conf.color_theme.punct) return "%s<%s%s%s:%s>%s" % (conf.color_theme.punct, conf.color_theme.packetlist_name, self.listname, conf.color_theme.punct, s, conf.color_theme.normal, ) def __getattr__(self, attr): return getattr(self.res, attr) def __getslice__(self, *args, **kargs): return self.__class__(self.res.__getslice__(*args, **kargs), name="mod %s"%self.listname) def __add__(self, other): return self.__class__(self.res+other.res, name="%s+%s"%(self.listname,other.listname)) def summary(self, prn=None, filter=None): for r in self.res: if filter is not None: if not filter(r): continue if prn is None: print self._elt2sum(r) else: print prn(r) def nsummary(self,prn=None, filter=None): for i in range(len(self.res)): if filter is not None: if not filter(self.res[i]): continue if prn is None: print "%04i %s" % (i,self._elt2sum(self.res[i])) else: print "%04i %s" % (i,prn(self.res[i])) def display(self): # Deprecated. Use show() self.show() def show(self): for i in range(len(self.res)): print "%04i %s" % (i,self._elt2show(self.res[i])) def filter(self, func): return self.__class__(filter(func,self.res), name="filtered %s"%self.listname) def make_table(self, *args, **kargs): return make_table(self.res, *args, **kargs) def make_lined_table(self, *args, **kargs): return make_lined_table(self.res, *args, **kargs) def make_tex_table(self, *args, **kargs): return make_tex_table(self.res, *args, **kargs) def plot(self, f, **kargs): g=Gnuplot.Gnuplot() g.plot(Gnuplot.Data(map(f,self.res), **kargs)) return g def hexdump(self): for p in self: hexdump(self._elt2pkt(p)) def hexraw(self): for i in range(len(self.res)): p = self._elt2pkt(self.res[i]) print "%04i %s %s" % (i,p.sprintf("%.time%"),self._elt2sum(self.res[i])) if p.haslayer(Raw): hexdump(p.getlayer(Raw).load) def padding(self, filter=None): for i in range(len(self.res)): p = self._elt2pkt(self.res[i]) if p.haslayer(Padding): if not filter or filter(p): print "%04i %s %s" % (i,p.sprintf("%.time%"),self._elt2sum(self.res[i])) hexdump(p.getlayer(Padding).load) def nzpadding(self, filter=None): for i in range(len(self.res)): p = self._elt2pkt(self.res[i]) if p.haslayer(Padding): pad = p.getlayer(Padding).load if pad == "\x00"*len(pad): continue if not filter or filter(p): print "%04i %s %s" % (i,p.sprintf("%.time%"),self._elt2sum(self.res[i])) hexdump(p.getlayer(Padding).load) def conversations(self, getsrc=None, getdst=None,**kargs): if getsrc is None: getsrc = lambda x:x.getlayer(IP).src if getdst is None: getdst = lambda x:x.getlayer(IP).dst conv = {} for p in self.res: p = self._elt2pkt(p) try: c = (getsrc(p),getdst(p)) except: #XXX warning() continue conv[c] = conv.get(c,0)+1 gr = 'digraph "conv" {\n' for s,d in conv: gr += '\t "%s" -> "%s"\n' % (s,d) gr += "}\n" do_graph(gr, **kargs) def timeskew_graph(self, ip, **kargs): b = filter(lambda x:x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), self.res) c = [] for p in b: opts = p.getlayer(TCP).options for o in opts: if o[0] == "Timestamp": c.append((p.time,o[1][0])) d = map(lambda (x,y): (x%2000,((x-c[0][0])-((y-c[0][1])/1000.0))),c) g = Gnuplot.Gnuplot() g.plot(Gnuplot.Data(d,**kargs)) return g class Dot11PacketList(PacketList): def __init__(self, res, name="Dot11List", stats=None): if stats is None: stats = [Dot11WEP, Dot11Beacon, UDP, ICMP, TCP] PacketList.__init__(self, res, name, stats) def toEthernet(self): data = map(lambda x:x.getlayer(Dot11), filter(lambda x : x.haslayer(Dot11) and x.type == 2, self.res)) r2 = [] for p in data: q = p.copy() q.unwep() r2.append(Ether()/q.payload.payload.payload) #Dot11/LLC/SNAP/IP return PacketList(r2,name="Ether from %s"%self.listname) class SndRcvList(PacketList): def __init__(self, res, name="Results", stats=None): PacketList.__init__(self, res, name, stats) def _elt2pkt(self, elt): return elt[1] def _elt2sum(self, elt): return "%s ==> %s" % (elt[0].summary(),elt[1].summary()) class ARPingResult(SndRcvList): def __init__(self, res, name="ARPing", stats=None): PacketList.__init__(self, res, name, stats) def display(self): for s,r in self.res: print r.sprintf("%Ether.src% %ARP.psrc%") class TracerouteResult(SndRcvList): def __init__(self, res, name="Traceroute", stats=None): PacketList.__init__(self, res, name, stats) self.graphdef = None self.graphASN = 0 self.hloc = None self.nloc = None def display(self): # Deprecated. Use show() self.show() def show(self): return self.make_table(lambda (s,r): (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), s.ttl, r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) def world_trace(self): ips = {} rt = {} ports_done = {} for s,r in self.res: ips[r.src] = None if s.haslayer(TCP) or s.haslayer(UDP): trace_id = (s.src,s.dst,s.proto,s.dport) elif s.haslayer(ICMP): trace_id = (s.src,s.dst,s.proto,s.type) else: trace_id = (s.src,s.dst,s.proto,0) trace = rt.get(trace_id,{}) if not r.haslayer(ICMP) or r.type != 11: if ports_done.has_key(trace_id): continue ports_done[trace_id] = None trace[s.ttl] = r.src rt[trace_id] = trace trt = {} for trace_id in rt: trace = rt[trace_id] loctrace = [] for i in range(max(trace.keys())): ip = trace.get(i,None) if ip is None: continue loc = locate_ip(ip) if loc is None: continue # loctrace.append((ip,loc)) # no labels yet loctrace.append(loc) if loctrace: trt[trace_id] = loctrace tr = map(lambda x: Gnuplot.Data(x,with="lines"), trt.values()) g = Gnuplot.Gnuplot() world = Gnuplot.File(conf.gnuplot_world,with="lines") g.plot(world,*tr) return g def make_graph(self,ASN): self.graphASN = ASN ips = {} rt = {} ports = {} ports_done = {} for s,r in self.res: ips[r.src] = None if s.haslayer(TCP) or s.haslayer(UDP): trace_id = (s.src,s.dst,s.proto,s.dport) elif s.haslayer(ICMP): trace_id = (s.src,s.dst,s.proto,s.type) else: trace_id = (s.src,s.dst,s.proto,0) trace = rt.get(trace_id,{}) if not r.haslayer(ICMP) or r.type != 11: if ports_done.has_key(trace_id): continue ports_done[trace_id] = None p = ports.get(r.src,[]) if r.haslayer(TCP): p.append(r.sprintf(" %TCP.sport%: %TCP.flags%")) trace[s.ttl] = r.sprintf('"%IP.src%":T%ir,TCP.sport%') elif r.haslayer(UDP): p.append(r.sprintf(" %UDP.sport%")) trace[s.ttl] = r.sprintf('"%IP.src%":U%ir,UDP.sport%') elif r.haslayer(ICMP): p.append(r.sprintf(" ICMP %ICMP.type%")) trace[s.ttl] = r.sprintf('"%IP.src%":I%ir,ICMP.type%') else: p.append(r.sprintf(" IP %IP.proto%")) trace[s.ttl] = r.sprintf('"%IP.src%":P%ir,IP.proto%') ports[r.src] = p else: trace[s.ttl] = r.sprintf('"%IP.src%"') rt[trace_id] = trace # Fill holes with unk%i nodes unk = 0 blackholes = [] bhip = {} for rtk in rt: trace = rt[rtk] k = trace.keys() for n in range(min(k), max(k)): if not trace.has_key(n): trace[n] = "unk%i" % unk unk += 1 if not ports_done.has_key(rtk): if rtk[2] == 1: #ICMP bh = "%s %i" % (rtk[1],rtk[3]) elif rtk[2] == 6: #TCP bh = "%s:%i/tcp" % (rtk[1],rtk[3]) elif rtk[2] == 17: #UDP bh = '%s:%i/udp' % (rtk[1],rtk[3]) else: bh = '%s,proto %i' % (rtk[1],rtk[2]) ips[bh] = None bhip[rtk[1]] = bh bh = '"%s"' % bh trace[max(k)+1] = bh blackholes.append(bh) # Find AS numbers def getASNlist_radb(list): def parseWhois(x): asn,desc = None,"" for l in x.splitlines(): if not asn and l.startswith("origin:"): asn = l[7:].strip() if l.startswith("descr:"): if desc: desc += r"\n" desc += l[6:].strip() if asn is not None and desc: break return asn,desc.strip() ASNlist = [] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("whois.ra.net",43)) for ip in list: s.send("-k %s\n" % ip) asn,desc = parseWhois(s.recv(8192)) ASNlist.append((ip,asn,desc)) return ASNlist def getASNlist_cymru(list): ASNlist = [] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("whois.cymru.com",43)) s.send("begin\r\n"+"\r\n".join(list)+"\r\nend\r\n") r = "" while 1: l = s.recv(8192) if l == "": break r += l s.close() for l in r.splitlines()[1:]: asn,ip,desc = map(str.strip, l.split("|")) if asn == "NA": continue asn = int(asn) ASNlist.append((ip,asn,desc)) return ASNlist ASN_query_list = dict.fromkeys(map(lambda x:x.split(":")[0],ips)).keys() if ASN in [1,2]: ASNlist = getASNlist_cymru(ASN_query_list) elif ASN == 3: ASNlist = getASNlist_radb(ASN_query_list) else: ASNlist = [] if ASN == 1: ASN_ans_list = map(lambda x:x[0], ASNlist) ASN_remain_list = filter(lambda x: x not in ASN_ans_list, ASN_query_list) if ASN_remain_list: ASNlist += getASNlist_radb(ASN_remain_list) ASNs = {} ASDs = {} for ip,asn,desc, in ASNlist: if asn is None: continue iplist = ASNs.get(asn,[]) if ip in bhip: if ip in ports: iplist.append(ip) iplist.append(bhip[ip]) else: iplist.append(ip) ASNs[asn] = iplist ASDs[asn] = desc def makecol(lstcol): b = [] for i in range(len(lstcol)): for j in range(len(lstcol)): for k in range(len(lstcol)): if i != j or j != k or k != i: b.append('"#%s%s%s"' % (lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)])) return b backcolorlist=makecol(["60","86","ba","ff"]) forecolorlist=makecol(["a0","70","40","20"]) clustcol = 0 edgecol = 0 s = "digraph trace {\n" s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" s += "\n#ASN clustering\n" for asn in ASNs: s += '\tsubgraph cluster_%s {\n' % asn s += '\t\tcolor=%s;' % backcolorlist[clustcol%(len(backcolorlist))] s += '\t\tnode [fillcolor=%s,style=filled];' % backcolorlist[clustcol%(len(backcolorlist))] clustcol += 1 s += '\t\tfontsize = 10;' s += '\t\tlabel = "%s\\n[%s]"\n' % (asn,ASDs[asn]) for ip in ASNs[asn]: s += '\t\t"%s";\n'%ip s += "\t}\n" s += "#endpoints\n" for p in ports: s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p,p,"|".join(ports[p])) s += "\n#Blackholes\n" for bh in blackholes: s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" for rtk in rt: s += "#---[%s\n" % `rtk` s += '\t\tedge [color=%s];\n' % forecolorlist[edgecol%(len(forecolorlist))] edgecol += 1 trace = rt[rtk] k = trace.keys() for n in range(min(k), max(k)): s += '\t%s ->\n' % trace[n] s += '\t%s;\n' % trace[max(k)] s += "}\n"; self.graphdef = s def graph(self, ASN=1, **kargs): """x.graph(ASN=1, other args): ASN=0 : no clustering ASN=1 : use whois.cymru.net AS clustering ASN=2 : use whois.ra.net AS clustering other args are passed to do_graph()""" if self.graphdef is None or self.graphASN != ASN: self.make_graph(ASN) do_graph(self.graphdef, **kargs) ############ ## Fields ## ############ class Field: islist=0 def __init__(self, name, default, fmt="H"): self.name = name if fmt[0] in "@=<>!": self.fmt = fmt else: self.fmt = "!"+fmt self.default = self.any2i(None,default) self.sz = struct.calcsize(self.fmt) def h2i(self, pkt, x): return x def i2h(self, pkt, x): return x def m2i(self, pkt, x): return x def i2m(self, pkt, x): if x is None: x = 0 return x def any2i(self, pkt, x): return x def i2repr(self, pkt, x): if x is None: x = 0 return repr(self.i2h(pkt,x)) def addfield(self, pkt, s, val): return s+struct.pack(self.fmt, self.i2m(pkt,val)) def getfield(self, pkt, s): return s[self.sz:], self.m2i(pkt, struct.unpack(self.fmt, s[:self.sz])[0]) def do_copy(self, x): if hasattr(x, "copy"): return x.copy() elif type(x) is list: return x[:] else: return x def __eq__(self, other): return self.name == other def __hash__(self): return hash(self.name) def __repr__(self): return self.name def copy(self): return copy.deepcopy(self) class Emph: fld = "" def __init__(self, fld): self.fld = fld def __getattr__(self, attr): return getattr(self.fld,attr) class MACField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "6s") def i2m(self, pkt, x): return mac2str(x) def m2i(self, pkt, x): return str2mac(x) def any2i(self, pkt, x): if type(x) is str and len(x) is 6: x = self.m2i(pkt, x) return x def i2repr(self, pkt, x): return self.i2h(pkt, x) class DestMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: dstip = None if isinstance(pkt.payload, IP): dstip = pkt.payload.dst elif isinstance(pkt.payload, ARP): dstip = pkt.payload.pdst if isinstance(dstip, Gen): warning("Dest mac not calculated if more than 1 dest IP (%s)"%repr(dstip)) return None x = "ff:ff:ff:ff:ff:ff" if dstip is not None: m=getmacbyip(dstip) if m: x = m else: warning("Mac address for %s not found\n"%dstip) return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) class SourceMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: dstip = None if isinstance(pkt.payload, IP): dstip = pkt.payload.dst elif isinstance(pkt.payload, ARP): dstip = pkt.payload.pdst if isinstance(dstip, Gen): warning("Source mac not calculated if more than 1 dest IP (%s)"%repr(dstip)) return None x = "00:00:00:00:00:00" if dstip is not None: iff,a,gw = conf.route.route(dstip) m = get_if_hwaddr(iff) if m: x = m return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) class ARPSourceMACField(MACField): def __init__(self, name): MACField.__init__(self, name, None) def i2h(self, pkt, x): if x is None: dstip = pkt.pdst if isinstance(dstip, Gen): warning("Source mac not calculated if more than 1 dest IP (%s)"%repr(dstip)) return None x = "00:00:00:00:00:00" if dstip is not None: iff,a,gw = conf.route.route(dstip) m = get_if_hwaddr(iff) if m: x = m return MACField.i2h(self, pkt, x) def i2m(self, pkt, x): return MACField.i2m(self, pkt, self.i2h(pkt, x)) class Dot11AddrMACField(MACField): def is_applicable(self, pkt): return 1 def addfield(self, pkt, s, val): if self.is_applicable(pkt): return MACField.addfield(self, pkt, s, val) else: return s def getfield(self, pkt, s): if self.is_applicable(pkt): return MACField.getfield(self, pkt, s) else: return s,None class Dot11Addr2MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type == 1: return pkt.subtype in [ 0xb, 0xa, 0xe, 0xf] # RTS, PS-Poll, CF-End, CF-End+CF-Ack return 1 class Dot11Addr3MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type in [0,2]: return 1 return 0 class Dot11Addr4MACField(Dot11AddrMACField): def is_applicable(self, pkt): if pkt.type == 2: if pkt.FCfield & 0x3 == 0x3: # To-DS and From-DS are set return 1 return 0 class IPField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "4s") def h2i(self, pkt, x): if type(x) is str: try: inet_aton(x) except socket.error: x = Net(x) elif type(x) is list: x = map(Net, x) return x def i2m(self, pkt, x): return inet_aton(x) def m2i(self, pkt, x): return socket.inet_ntoa(x) def any2i(self, pkt, x): # if type(x) is str and len(x) == 4: # x = self.m2i(pkt, x) return self.h2i(pkt,x) def i2repr(self, pkt, x): return self.i2h(pkt, x) class SourceIPField(IPField): def __init__(self, name, dstname): IPField.__init__(self, name, None) self.dstname = dstname def i2m(self, pkt, x): if x is None: iff,x,gw = conf.route.route(getattr(pkt,self.dstname)) return IPField.i2m(self, pkt, x) def i2h(self, pkt, x): if x is None: dst=getattr(pkt,self.dstname) if isinstance(dst,Gen): r = map(conf.route.route, dst) r.sort() if r[0] == r[-1]: x=r[0][1] else: warning("More than one possible route for %s"%repr(dst)) return None else: iff,x,gw = conf.route.route(dst) return IPField.i2h(self, pkt, x) class ByteField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "B") class XByteField(ByteField): def i2repr(self, pkt, x): if x is None: x = 0 return hex(self.i2h(pkt, x)) class X3BytesField(XByteField): def __init__(self, name, default): Field.__init__(self, name, default, "I") def addfield(self, pkt, s, val): return s+struct.pack(self.fmt, self.i2m(pkt,val))[:3] def getfield(self, pkt, s): return s[3:], self.m2i(pkt, struct.unpack(self.fmt, "\x00"+s[:3])[0]) class ShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "H") class LEShortField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "@H") class XShortField(ShortField): def i2repr(self, pkt, x): if x is None: x = 0 return hex(self.i2h(pkt, x)) class IntField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "I") class LEIntField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "@I") class XIntField(IntField): def i2repr(self, pkt, x): if x is None: x = 0 return hex(self.i2h(pkt, x)) class LongField(Field): def __init__(self, name, default): Field.__init__(self, name, default, "Q") class XLongField(LongField): def i2repr(self, pkt, x): if x is None: x = 0 return hex(self.i2h(pkt, x)) class StrField(Field): def __init__(self, name, default, fmt="H", remain=0): Field.__init__(self,name,default,fmt) self.remain = remain def i2m(self, pkt, x): if x is None: x = "" return x def addfield(self, pkt, s, val): return s+self.i2m(pkt, val) def getfield(self, pkt, s): if self.remain == 0: return "",self.m2i(pkt, s) else: return s[-self.remain:],self.m2i(pkt, s[:-self.remain]) class PacketField(StrField): def __init__(self, name, default, cls): StrField.__init__(self, name, default) self.cls = cls def i2m(self, pkt, i): return str(i) def m2i(self, pkt, m): return self.cls(m) def getfield(self, pkt, s): i = self.m2i(pkt, s) remain = "" if i.haslayer(Padding): r = i.getlayer(Padding) del(r.underlayer.payload) remain = r.load return remain,i class PacketLenField(PacketField): def __init__(self, name, default, cls, fld): PacketField.__init__(self, name, default, cls) self.fld = fld def getfield(self, pkt, s): l = getattr(pkt, self.fld) l += pkt.fields_desc[pkt.fields_desc.index(self.fld)].shift i = self.m2i(pkt, s[:l]) return s[l:],i class StrFixedLenField(StrField): def __init__(self, name, default, length): StrField.__init__(self, name, default) self.length = length def getfield(self, pkt, s): return s[self.length:], self.m2i(pkt,s[:self.length]) def addfield(self, pkt, s, val): return s+struct.pack("%ss"%self.length,self.i2m(pkt, val)) class NetBIOSNameField(StrFixedLenField): def __init__(self, name, default, length=31): StrFixedLenField.__init__(self, name, default, length) def i2m(self, pkt, x): if x is None: x = "" x += " "*(self.length/2-1) x = x[:(self.length/2-1)] x = "".join(map(lambda x: chr(0x41+(ord(x)>>4))+chr(0x41+(ord(x)&0xf)), x)) x = " "+x return x def m2i(self, pkt, x): x = x.strip("\x00").strip(" ") return "".join(map(lambda x,y: chr((((ord(x)-1)&0xf)<<4)+((ord(y)-1)&0xf)), x[::2],x[1::2])) class StrLenField(StrField): def __init__(self, name, default, fld): StrField.__init__(self, name, default) self.fld = fld def getfield(self, pkt, s): l = getattr(pkt, self.fld) # add the shift from the length field f = pkt.fields_desc[pkt.fields_desc.index(self.fld)] if isinstance(f, FieldLenField): l += f.shift return s[l:], self.m2i(pkt,s[:l]) class FieldLenField(Field): def __init__(self, name, default, fld, fmt = "H", shift=0): Field.__init__(self, name, default, fmt) self.fld = fld self.shift = shift def i2m(self, pkt, x): if x is None: f = pkt.fields_desc[pkt.fields_desc.index(self.fld)] v = f.i2m(pkt,getattr(pkt, self.fld)) x = len(v)-self.shift return x # def i2h(self, pkt, x): # if x is None: # f = pkt.fields_desc[pkt.fields_desc.index(self.fld)] # v = f.i2m(pkt,getattr(pkt, self.fld)) # x = len(v)+self.shift # return x ISAKMPTransformTypes = { "Encryption": (1, { "DES-CBS" : 1, "3DES-CBC" : 5, }), "Hash": (2, { "MD5": 1, "SHA": 2, }), "Authentication":(3, { "PSK": 1, }), "GroupDesc": (4, { "768MODPgr" : 1, "1024MODPgr" : 2, }), "LifeType": (11,{ "Seconds":1, }), "LifeDuration": (12,{}), } ISAKMPTransformNum = {} for n in ISAKMPTransformTypes: val = ISAKMPTransformTypes[n] tmp = {} for e in val[1]: tmp[val[1][e]] = e ISAKMPTransformNum[val[0]] = (n,tmp) del(n) del(e) del(tmp) del(val) class ISAKMPTransformSetField(StrLenField): islist=1 def type2num(self, (typ,enc)): if ISAKMPTransformTypes.has_key(typ): val = ISAKMPTransformTypes[typ] else: val = (int(typ),{}) if val[1].has_key(enc): enc = val[1][enc] else: enc = int(enc) return ((val[0] | 0x8000L) << 16) | enc def num2type(self, num): typ = (num >> 16) & 0x7fff enc = num & 0xffff val = ISAKMPTransformNum.get(typ,(typ,{})) enc = val[1].get(enc,enc) return (val[0],enc) def i2m(self, pkt, i): if i is None: return "" i = map(self.type2num, i) return struct.pack("!"+"I"*len(i),*i) def m2i(self, pkt, m): lst = struct.unpack("!"+"I"*(len(m)/4),m) lst = map(self.num2type, lst) return lst def getfield(self, pkt, s): l = getattr(pkt, self.fld) l += pkt.fields_desc[pkt.fields_desc.index(self.fld)].shift i = self.m2i(pkt, s[:l]) return s[l:],i class StrNullField(StrField): def addfield(self, pkt, s, val): return s+self.i2m(pkt, val)+"\x00" def getfield(self, pkt, s): l = s.find("\x00") if l < 0: #XXX \x00 not found return "",s return s[l+1:],self.m2i(pkt, s[:l]) class StrStopField(StrField): def __init__(self, name, default, stop, additionnal=0): Field.__init__(self, name, default) self.stop=stop self.additionnal=additionnal def getfield(self, pkt, s): l = s.find(self.stop) if l < 0: return "",s # raise Exception,"StrStopField: stop value [%s] not found" %stop l += len(self.stop)+self.additionnal return s[l:],s[:l] class LenField(Field): def i2m(self, pkt, x): if x is None: x = len(pkt.payload) return x class BCDFloatField(Field): def i2m(self, pkt, x): return int(256*x) def m2i(self, pkt, x): return x/256.0 class BitField(Field): def __init__(self, name, default, size): Field.__init__(self, name, default) self.size = size def addfield(self, pkt, s, val): if val is None: val = 0 if type(s) is tuple: s,bitsdone,v = s else: bitsdone = 0 v = 0 v <<= self.size v |= val & ((1L<= 8: bitsdone -= 8 s = s+struct.pack("!B", v >> bitsdone) v &= (1L<> (nb_bytes*8 - self.size - bn) bn += self.size s = s[bn/8:] bn = bn%8 if bn: return (s,bn),b else: return s,b class XBitField(BitField): def i2repr(self, pkt, x): return hex(self.i2h(pkt,x)) class EnumField(Field): def __init__(self, name, default, enum, fmt = "H"): Field.__init__(self, name, default, fmt) i2s = self.i2s = {} s2i = self.s2i = {} if type(enum) is list: keys = xrange(len(enum)) else: keys = enum.keys() if filter(lambda x: type(x) is str, keys): i2s,s2i = s2i,i2s for k in keys: i2s[k] = enum[k] s2i[enum[k]] = k def any2i(self, pkt, x): if type(x) is str: x = self.s2i[x] return x def i2repr(self, pkt, x): return self.i2s.get(x, x) class BitEnumField(BitField,EnumField): def __init__(self, name, default, size, enum): EnumField.__init__(self, name, default, enum) self.size = size def any2i(self, pkt, x): return EnumField.any2i(self, pkt, x) def i2repr(self, pkt, x): return EnumField.i2repr(self, pkt, x) class ShortEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "H") class LEShortEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "@H") class ByteEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "B") class IntEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "I") class LEIntEnumField(EnumField): def __init__(self, name, default, enum): EnumField.__init__(self, name, default, enum, "@I") class XShortEnumField(ShortEnumField): def i2repr(self, pkt, x): return self.i2s.get(x, hex(x)) class FlagsField(BitField): def __init__(self, name, default, size, names): BitField.__init__(self, name, default, size) self.multi = type(names) is list if self.multi: self.names = map(lambda x:[x], names) else: self.names = names def any2i(self, pkt, x): if type(x) is str: if self.multi: x = map(lambda y:[y], x.split("+")) y = 0 for i in x: y |= 1 << self.names.index(i) x = y return x def i2repr(self, pkt, x): if self.multi: r = [] else: r = "" i=0 while x: if x & 1: r += self.names[i] i += 1 x >>= 1 if self.multi: r = "+".join(r) return r class IPoptionsField(StrField): def i2m(self, pkt, x): return x+"\x00"*(3-((len(x)+3)%4)) def getfield(self, pkt, s): opsz = (pkt.ihl-5)*4 if opsz < 0: warning("bad ihl (%i). Assuming ihl=5"%pkt.ihl) opsz = 0 return s[opsz:],s[:opsz] TCPOptions = ( { 2 : ("MSS","!H"), 3 : ("WScale","!B"), 4 : ["SAckOK",None], 5 : ["SAck","!II"], 8 : ["Timestamp","!II"], 14 : ["AltChkSum","!BH"], 15 : ["AltChkSumOpt",None] }, { "MSS":2, "WScale":3, "SAckOK":4, "SAck":5, "Timestamp":8, "AltChkSum":14, "AltChkSumOpt":15, } ) class TCPOptionsField(StrField): islist=1 def getfield(self, pkt, s): opsz = (pkt.dataofs-5)*4 if opsz < 0: warning("bad dataofs (%i). Assuming dataofs=5"%pkt.dataofs) opsz = 0 return s[opsz:],self.m2i(pkt,s[:opsz]) def m2i(self, pkt, x): opt = [] while x: onum = ord(x[0]) if onum == 0: break if onum == 1: opt.append(("NOP",None)) x=x[1:] continue olen = ord(x[1]) oval = x[2:olen] if TCPOptions[0].has_key(onum): oname, ofmt = TCPOptions[0][onum] if ofmt: oval = struct.unpack(ofmt, oval) if len(oval) == 1: oval = oval[0] opt.append((oname, oval)) else: opt.append((onum, oval)) x = x[olen:] return opt def i2m(self, pkt, x): opt = "" for oname,oval in x: if type(oname) is str: if oname == "NOP": opt += "\x01" continue elif TCPOptions[1].has_key(oname): onum = TCPOptions[1][oname] ofmt = TCPOptions[0][onum][1] if ofmt is not None: if type(oval) is not tuple: oval = (oval,) oval = struct.pack(ofmt, *oval) else: warning("option [%s] unknown. Skipped."%oname) continue else: onum = oname if type(oval) is not str: warning("option [%i] is not string."%onum) continue opt += chr(onum)+chr(2+len(oval))+oval return opt+"\x00"*(3-((len(opt)+3)%4)) class DNSStrField(StrField): def i2m(self, pkt, x): x = x.split(".") x = map(lambda y: chr(len(y))+y, x) x = "".join(x) if x[-1] != "\x00": x += "\x00" return x def getfield(self, pkt, s): n = "" while 1: l = ord(s[0]) s = s[1:] if not l: break if l & 0xc0: raise Exception("DNS message can't be compressed at this point!") else: n += s[:l]+"." s = s[l:] return s, n class DNSRRCountField(ShortField): def __init__(self, name, default, rr): ShortField.__init__(self, name, default) self.rr = rr def i2m(self, pkt, x): if x is None: x = getattr(pkt,self.rr) i = 0 while isinstance(x, DNSRR) or isinstance(x, DNSQR): x = x.payload i += 1 x = i return x def i2h(self, pkt, x): return self.i2m(pkt, x) def DNSgetstr(s,p): name = "" q = 0 while 1: if p >= len(s): warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s))) break l = ord(s[p]) p += 1 if l & 0xc0: if not q: q = p+1 p = ((l & 0x3f) << 8) + ord(s[p]) - 12 continue elif l > 0: name += s[p:p+l]+"." p += l continue break if q: p = q return name,p class DNSRRField(StrField): def __init__(self, name, countfld, passon=1): StrField.__init__(self, name, None) self.countfld = countfld self.passon = passon def i2m(self, pkt, x): if x is None: return "" return str(x) def decodeRR(self, name, s, p): ret = s[p:p+10] type,cls,ttl,rdlen = struct.unpack("!HHIH", ret) p += 10 rr = DNSRR("\x00"+ret+s[p:p+rdlen]) if rr.type in [2, 3, 4, 5]: rr.rdata = DNSgetstr(s,p)[0] del(rr.rdlen) p += rdlen rr.rrname = name return rr,p def getfield(self, pkt, s): if type(s) is tuple : s,p = s else: p = 0 ret = None c = getattr(pkt, self.countfld) while c: c -= 1 name,p = DNSgetstr(s,p) rr,p = self.decodeRR(name, s, p) if ret is None: ret = rr else: ret.add_payload(rr) if self.passon: return (s,p),ret else: return s[p:],ret class DNSQRField(DNSRRField): def decodeRR(self, name, s, p): ret = s[p:p+4] p += 4 rr = DNSQR("\x00"+ret) rr.qname = name return rr,p class RDataField(StrLenField): def m2i(self, pkt, s): if pkt.type == 1: s = socket.inet_ntoa(s) return s def i2m(self, pkt, s): if pkt.type == 1: if s: s = inet_aton(s) elif pkt.type in [2,3,4,5]: s = "".join(map(lambda x: chr(len(x))+x, s.split("."))) if ord(s[-1]): s += "\x00" return s class RDLenField(Field): def __init__(self, name): Field.__init__(self, name, None, "H") def i2m(self, pkt, x): if x is None: rdataf = pkt.fieldtype["rdata"] x = len(rdataf.i2m(pkt, pkt.rdata)) return x def i2h(self, pkt, x): if x is None: rdataf = pkt.fieldtype["rdata"] x = len(rdataf.i2m(pkt, pkt.rdata)) return x # seconds between 01-01-1900 and 01-01-1970 ntp_basetime = 2208988800 class TimeStampField(BitField): def __init__(self, name, default, size): BitField.__init__(self, name, default, size) self.size = size def getfield(self, pkt, s): s,timestamp = BitField.getfield(self, pkt, s) if timestamp: # timestamp is a 64 bits field : # + first 32 bits : number of seconds since 1900 # + last 32 bits : fraction part timestamp >>= 32 timestamp -= ntp_basetime from time import gmtime, strftime b = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime(timestamp)) else: b = 'None' return s, b def addfield(self, pkt, s, val): t = -1 if type(val) is str: from time import strptime, mktime t = int(mktime(strptime(val))) + ntp_basetime + 3600 else: if val == -1: from time import time t = int(time()) + ntp_basetime else: t = val t <<= 32 return BitField.addfield(self,pkt,s, t) class FloatField(BitField): def getfield(self, pkt, s): s,b = BitField.getfield(self, pkt, s) # fraction point between bits 15 and 16. sec = b >> 16 frac = b & (1L << (32+1)) - 1 frac /= 65536.0 b = sec+frac return s,b ########################### ## Packet abstract class ## ########################### class Packet(Gen): name="abstract packet" fields_desc = [] aliastypes = [] overload_fields = {} underlayer = None payload_guess = [] initialized = 0 def __init__(self, pkt="", **fields): self.time = time.time() self.aliastypes = [ self.__class__ ] + self.aliastypes self.default_fields = {} self.overloaded_fields = {} self.fields={} self.fieldtype={} self.__dict__["payload"] = NoPayload() for f in self.fields_desc: self.default_fields[f] = f.default self.fieldtype[f] = f self.initialized = 1 if pkt: self.dissect(pkt) for f in fields.keys(): self.fields[f] = self.fieldtype[f].any2i(self,fields[f]) def add_payload(self, payload): if payload is None: return elif not isinstance(self.payload, NoPayload): self.payload.add_payload(payload) else: if isinstance(payload, Packet): self.__dict__["payload"] = payload payload.add_underlayer(self) for t in self.aliastypes: if payload.overload_fields.has_key(t): self.overloaded_fields = payload.overload_fields[t] break elif type(payload) is str: self.__dict__["payload"] = Raw(load=payload) else: raise TypeError("payload must be either 'Packet' or 'str', not [%s]" % repr(payload)) def remove_payload(self): self.payload.remove_underlayer(self) self.__dict__["payload"] = NoPayload() self.overloaded_fields = {} def add_underlayer(self, underlayer): self.underlayer = underlayer def remove_underlayer(self,other): self.underlayer = None def copy(self): clone = self.__class__() clone.fields = self.fields.copy() for k in clone.fields: clone.fields[k]=self.fieldtype[k].do_copy(clone.fields[k]) clone.default_fields = self.default_fields.copy() clone.overloaded_fields = self.overloaded_fields.copy() clone.overload_fields = self.overload_fields.copy() clone.underlayer=self.underlayer clone.__dict__["payload"] = self.payload.copy() clone.payload.add_underlayer(clone) return clone def __getattr__(self, attr): if self.initialized: fld = self.fieldtype.get(attr) if fld is None: i2h = lambda x,y: y else: i2h = fld.i2h for f in ["fields", "overloaded_fields", "default_fields"]: fields = self.__dict__[f] if fields.has_key(attr): return i2h(self, fields[attr] ) return getattr(self.payload, attr) raise AttributeError(attr) def __setattr__(self, attr, val): if self.initialized: if self.default_fields.has_key(attr): fld = self.fieldtype.get(attr) if fld is None: any2i = lambda x,y: y else: any2i = fld.any2i self.fields[attr] = any2i(self, val) elif attr == "payload": self.remove_payload() self.add_payload(val) else: self.__dict__[attr] = val else: self.__dict__[attr] = val def __delattr__(self, attr): if self.initialized: if self.fields.has_key(attr): del(self.fields[attr]) return elif self.default_fields.has_key(attr): return elif attr == "payload": self.remove_payload() return if self.__dict__.has_key(attr): del(self.__dict__[attr]) else: raise AttributeError(attr) def __repr__(self): s = "" for f in self.fields_desc: if f in self.fields: val = f.i2repr(self, self.fields[f]) elif f in self.overloaded_fields: val = f.i2repr(self, self.overloaded_fields[f]) else: continue if isinstance(f, Emph): ncol = conf.color_theme.emph_field_name vcol = conf.color_theme.emph_field_value else: ncol = conf.color_theme.field_name vcol = conf.color_theme.field_value s += " %s%s%s=%s%s%s" % (ncol, f.name, conf.color_theme.punct, vcol, val, conf.color_theme.punct) return "%s<%s%s%s%s |%s%s>%s"% (conf.color_theme.punct, conf.color_theme.layer_name, self.__class__.__name__, conf.color_theme.punct, s, repr(self.payload), conf.color_theme.punct, conf.color_theme.normal) def __str__(self): return self.__iter__().next().build() def __div__(self, other): if isinstance(other, Packet): cloneA = self.copy() cloneB = other.copy() cloneA.add_payload(cloneB) return cloneA elif type(other) is str: return self/Raw(load=other) else: return other.__rdiv__(self) def __rdiv__(self, other): if type(other) is str: return Raw(load=other)/self else: raise TypeError def __len__(self): return len(self.__str__()) def do_build(self): p="" for f in self.fields_desc: p = f.addfield(self, p, self.__getattr__(f)) pkt = p+self.payload.build(internal=1) return pkt def post_build(self, pkt): return pkt def build(self,internal=0): p = self.post_build(self.do_build()) if not internal and self.haslayer(Padding): p += self.getlayer(Padding).load return p def extract_padding(self, s): return s,None def post_dissect(self, s): return s def do_dissect(self, s): flist = self.fields_desc[:] flist.reverse() while s and flist: f = flist.pop() s,fval = f.getfield(self, s) self.fields[f] = fval s = self.post_dissect(s) payl,pad = self.extract_padding(s) self.do_dissect_payload(payl) if pad and conf.padding: self.add_payload(Padding(pad)) def do_dissect_payload(self, s): if s: cls = self.guess_payload_class(s) try: p = cls(s) except: if conf.debug_dissector: log_runtime.error("%s dissector failed" % cls.name) raise else: p = Raw(s) self.add_payload(p) def dissect(self, s): return self.do_dissect(s) def guess_payload_class(self, payload): for t in self.aliastypes: for fval, cls in t.payload_guess: ok = 1 for k in fval.keys(): if fval[k] != getattr(self,k): ok = 0 break if ok: return cls return None def hide_defaults(self): for k in self.fields.keys(): if self.default_fields.has_key(k): if self.default_fields[k] == self.fields[k]: del(self.fields[k]) self.payload.hide_defaults() def __iter__(self): def loop(todo, done, self=self): if todo: eltname = todo.pop() elt = self.__getattr__(eltname) if not isinstance(elt, Gen): if self.fieldtype[eltname].islist: elt = SetGen([elt]) else: elt = SetGen(elt) for e in elt: done[eltname]=e for x in loop(todo[:], done): yield x else: if isinstance(self.payload,NoPayload): payloads = [None] else: payloads = self.payload for payl in payloads: done2=done.copy() for k in done2: if isinstance(done2[k], RandField): done2[k] = done2[k]*1 pkt = self.__class__(**done2) pkt.underlayer = self.underlayer pkt.overload_fields = self.overload_fields.copy() if payl is None: yield pkt else: yield pkt/payl return loop(map(lambda x:str(x), self.fields.keys()), {}) def send(self, s, slp=0): for p in self: s.send(str(p)) if slp: time.sleep(slp) def __gt__(self, other): if isinstance(other, Packet): return other < self elif type(other) is str: return 1 else: raise TypeError((self, other)) def __lt__(self, other): if isinstance(other, Packet): return self.answers(other) elif type(other) is str: return 1 else: raise TypeError((self, other)) def hashret(self): return self.payload.hashret() def answers(self, other): if other.__class__ == self.__class__: return self.payload.answers(other.payload) return 0 def haslayer(self, cls): if self.__class__ == cls: return 1 return self.payload.haslayer(cls) def haslayer_str(self, cls): if self.__class__.__name__ == cls: return 1 return self.payload.haslayer_str(cls) def getlayer(self, cls): if self.__class__ == cls: return self return self.payload.getlayer(cls) def display(self,*args,**kargs): # Deprecated. Use show() self.show(*args,**kargs) def show(self, lvl=0): print "%s---[ %s%s%s ]---%s" % (conf.color_theme.punct, conf.color_theme.layer_name, self.name, conf.color_theme.punct, conf.color_theme.normal) for f in self.fields_desc: if isinstance(f, Emph): ncol = conf.color_theme.emph_field_name vcol = conf.color_theme.emph_field_value else: ncol = conf.color_theme.field_name vcol = conf.color_theme.field_value print "%s%s%-10s%s= %s%s%s" % (" "*lvl, ncol, f.name, conf.color_theme.punct, vcol, f.i2repr(self,self.__getattr__(f)), conf.color_theme.normal) self.payload.display(lvl+1) def show2(self): self.__class__(str(self)).show() def sprintf(self, fmt, relax=1): """sprintf(format, [relax=1]) -> str where format is a string that can include directives. A directive begins and ends by % and has the following format %[fmt[r],][cls[:nb].]field%. fmt is a classic printf directive, "r" can be appended for raw substitution (ex: IP.flags=0x18 instead of SA), nb is the number of the layer we want (ex: for IP/IP packets, IP:2.src is the src of the upper IP layer). Special case : "%.time%" is the creation time. Ex : p.sprintf("%.time% %-15s,IP.src% -> %-15s,IP.dst% %IP.chksum% " "%03xr,IP.proto% %r,TCP.flags%") Moreover, the format string can include conditionnal statements. A conditionnal statement looks like : {layer:string} where layer is a layer name, and string is the string to insert in place of the condition if it is true, i.e. if layer is present. If layer is preceded by a "!", the result si inverted. Conditions can be imbricated. A valid statement can be : p.sprintf("This is a{TCP: TCP}{UDP: UDP}{ICMP:n ICMP} packet") p.sprintf("{IP:%IP.dst% {ICMP:%ICMP.type%}{TCP:%TCP.dport%}}") A side effect is that, to obtain "{" and "}" characters, you must use "%(" and "%)". """ escape = { "%": "%", "(": "{", ")": "}" } # Evaluate conditions while "{" in fmt: i = fmt.rindex("{") j = fmt[i+1:].index("}") cond = fmt[i+1:i+j+1] k = cond.find(":") if k < 0: raise Exception("Bad condition in format string: [%s] (read sprintf doc!)"%cond) cond,format = cond[:k],cond[k+1:] res = False if cond[0] == "!": res = True cond = cond[1:] if self.haslayer_str(cond): res = not res if not res: format = "" fmt = fmt[:i]+format+fmt[i+j+2:] # Evaluate directives s = "" while "%" in fmt: i = fmt.index("%") s += fmt[:i] fmt = fmt[i+1:] if fmt[0] in escape: s += escape[fmt[0]] fmt = fmt[1:] continue try: i = fmt.index("%") sfclsfld = fmt[:i] fclsfld = sfclsfld.split(",") if len(fclsfld) == 1: f = "s" clsfld = fclsfld[0] elif len(fclsfld) == 2: f,clsfld = fclsfld else: raise Exception cls,fld = clsfld.split(".") num = 1 if ":" in cls: cls,num = cls.split(":") num = int(num) fmt = fmt[i+1:] except: raise Exception("Bad format string [%%%s%s]" % (fmt[:25], fmt[25:] and "...")) else: if fld == "time": val = time.strftime("%H:%M:%S.%%06i", time.localtime(self.time)) % int((self.time-int(self.time))*1000000) elif cls == self.__class__.__name__ and hasattr(self, fld): if num > 1: val = self.payload.sprintf("%%%s,%s:%s.%s%%" % (f,cls,num-1,fld), relax) f = "s" elif f[-1] == "r": # Raw field value val = getattr(self,fld) f = f[:-1] if not f: f = "s" else: val = getattr(self,fld) if fld in self.fieldtype: val = self.fieldtype[fld].i2repr(self,val) else: val = self.payload.sprintf("%%%s%%" % sfclsfld, relax) f = "s" s += ("%"+f) % val s += fmt return s def mysummary(self): return "" def summary(self, intern=0): found,s = self.payload.summary(intern=1) if s: s = " / "+s ret = "" if not found: ret = self.mysummary() if ret: found = 1 else: ret = self.__class__.__name__ ret = "%s%s" % (ret,s) if intern: return found,ret else: return ret def lastlayer(self,layer=None): return self.payload.lastlayer(self) def decode_payload_as(self,cls): s = str(self.payload) self.payload = cls(s) def libnet(self): print "libnet_build_%s(" % self.__class__.name.lower() det = self.__class__(str(self)) for f in self.fields_desc: val = getattr(det, f.name) if val is None: val = 0 elif type(val) is int: val = str(val) else: val = '"%s"' % str(val) print "\t%s, \t\t/* %s */" % (val,f.name) print ");" class NoPayload(Packet,object): def __new__(cls, *args, **kargs): singl = cls.__dict__.get("__singl__") if singl is None: cls.__singl__ = singl = object.__new__(cls) Packet.__init__(singl, *args, **kargs) return singl def __init__(self, *args, **kargs): pass def add_payload(self, payload): raise Exception("Can't add payload to NoPayload instance") def remove_payload(self): pass def add_underlayer(self,underlayer): pass def remove_underlayer(self,other): pass def copy(self): return self def __repr__(self): return "" def __str__(self): return "" def build(self, internal=0): return "" def __getattr__(self, attr): if attr in self.__dict__: return self.__dict__[attr] elif attr in self.__class__.__dict__: return self.__class__.__dict__[attr] else: raise AttributeError, attr def hide_defaults(self): pass def __iter__(self): return iter([]) def hashret(self): return "" def answers(self, other): return isinstance(other, NoPayload) or isinstance(other, Padding) def haslayer(self, cls): return 0 def haslayer_str(self, cls): return 0 def getlayer(self, cls): return None def show(self, lvl=0): pass def sprintf(self, fmt, relax): if relax: return "??" else: raise Exception("Format not found [%s]"%fmt) def summary(self, intern=0): return 0,"" def lastlayer(self,layer): return layer #################### ## packet classes ## #################### class ChangeDefaultValues(type): def __new__(cls, name, bases, dct): default = dct["new_default_values"] fields = None for b in bases: if hasattr(b,"fields_desc"): fields = b.fields_desc[:] break if fields is None: raise Exception("No fields_desc in superclasses") del(dct["new_default_values"]) new_fields = [] for f in fields: if f in default: f = f.copy() f.default = default[f] new_fields.append(f) dct["fields_desc"] = new_fields return super(ChangeDefaultValues, cls).__new__(cls, name, bases, dct) class Raw(Packet): name = "Raw" fields_desc = [ StrField("load", "") ] def answers(self, other): return 1 # s = str(other) # t = self.load # l = min(len(s), len(t)) # return s[:l] == t[:l] class Padding(Raw): name = "Padding" def build(self, internal=0): if internal: return "" else: return Raw.build(self) class Ether(Packet): name = "Ethernet" fields_desc = [ DestMACField("dst"), SourceMACField("src"), XShortEnumField("type", 0x0000, ETHER_TYPES) ] def hashret(self): return struct.pack("H",self.type)+self.payload.hashret() def answers(self, other): if isinstance(other,Ether): if self.type == other.type: return self.payload.answers(other.payload) return 0 def mysummary(self): return self.sprintf("%Ether.src% > %Ether.dst% (%Ether.type%)") class PPPoE(Packet): name = "PPP over Ethernet" fields_desc = [ BitField("version", 1, 4), BitField("type", 1, 4), ByteEnumField("code", 0, {0:"Session"}), XShortField("sessionid", 0x0), ShortField("len", None) ] def post_build(self,p): if self.len is None: l = len(p)-6 p = p[:4]+struct.pack("!H", l)+p[6:] return p class PPPoED(PPPoE): name = "PPP over Ethernet Discovery" fields_desc = [ BitField("version", 1, 4), BitField("type", 1, 4), ByteEnumField("code", 0x09, {0x09:"PADI",0x07:"PADO",0x19:"PADR",0x65:"PADS",0xa7:"PADT"}), XShortField("sessionid", 0x0), ShortField("len", None) ] class Dot3(Packet): name = "802.3" fields_desc = [ MACField("dst", ETHER_BROADCAST), MACField("src", ETHER_ANY), LenField("len", None, "H") ] def extract_padding(self,s): l = self.len return s[:l],s[l:] def answers(self, other): if isinstance(other,Dot3): return self.payload.answers(other.payload) return 0 def mysummary(self): return "%s > %s" % (self.src, self.dst) class LLC(Packet): name = "LLC" fields_desc = [ XByteField("dsap", 0x00), XByteField("ssap", 0x00), ByteField("ctrl", 0) ] class CookedLinux(Packet): name = "cooked linux" fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast", 4:"sent-by-us"}), #XXX incomplete XShortField("lladdrtype",512), ShortField("lladdrlen",0), StrFixedLenField("src","",8), XShortEnumField("proto",0x800,ETHER_TYPES) ] class SNAP(Packet): name = "SNAP" fields_desc = [ X3BytesField("OUI",0x000000), XShortEnumField("code", 0x000, ETHER_TYPES) ] class Dot1Q(Packet): name = "802.1Q" aliastypes = [ Ether ] fields_desc = [ BitField("prio", 0, 3), BitField("id", 0, 1), BitField("vlan", 1, 12), XShortEnumField("type", 0x0000, ETHER_TYPES) ] def answers(self, other): if isinstance(other,Dot1Q): if ( (self.type == other.type) and (self.vlan == other.vlan) ): return self.payload.answers(other.payload) else: return self.payload.answers(other) return 0 def mysummary(self): if isinstance(self.underlayer, Ether): return self.underlayer.sprintf("802.1q %Ether.src% > %Ether.dst% (%Dot1Q.type%) vlan %Dot1Q.vlan%") else: return self.sprintf("802.1q (%Dot1Q.type%) vlan %Dot1Q.vlan%") class STP(Packet): name = "Spanning Tree Protocol" fields_desc = [ ShortField("proto", 0), ByteField("version", 0), ByteField("bpdutype", 0), ByteField("bpduflags", 0), ShortField("rootid", 0), MACField("rootmac", ETHER_ANY), IntField("pathcost", 0), ShortField("bridgeid", 0), MACField("bridgemac", ETHER_ANY), ShortField("portid", 0), ShortField("age", 1), BCDFloatField("maxage", 20), BCDFloatField("hellotime", 2), BCDFloatField("fwddelay", 15) ] class EAPOL(Packet): name = "EAPOL" fields_desc = [ ByteField("version", 1), ByteEnumField("type", 0, ["EAP_PACKET", "START", "LOGOFF", "KEY", "ASF"]), LenField("len", None, "H") ] EAP_PACKET= 0 START = 1 LOGOFF = 2 KEY = 3 ASF = 4 def extract_padding(self, s): l = self.len return s[:l],s[l:] def hashret(self): return chr(self.type)+self.payload.hashret() def answers(self, other): if isinstance(other,EAPOL): if ( (self.type == self.EAP_PACKET) and (other.type == self.EAP_PACKET) ): return self.payload.answers(other.payload) return 0 def mysummary(self): return self.sprintf("EAPOL %EAPOL.type%") class EAP(Packet): name = "EAP" fields_desc = [ ByteEnumField("code", 4, {1:"REQUEST",2:"RESPONSE",3:"SUCCESS",4:"FAILURE"}), ByteField("id", 0), ByteEnumField("type",0, {1:"ID",4:"MD5"}), ByteField("len",None)] REQUEST = 1 RESPONSE = 2 SUCCESS = 3 FAILURE = 4 TYPE_ID = 1 TYPE_MD5 = 4 def answers(self, other): if isinstance(other,EAP): if self.code == self.REQUEST: return 0 elif self.code == self.RESPONSE: if ( (other.code == self.REQUEST) and (other.type == self.type) ): return 1 elif other.code == self.RESPONSE: return 1 return 0 def build(self,internal=0): l = self.len if self.code in [EAP.SUCCESS, EAP.FAILURE]: if l is None: l = 4 return struct.pack("!BBH", self.code, self.id, l)+str(self.payload) else: payl = str(self.payload) if l is None: l = 5+len(payl) return struct.pack("!BBHB", self.code, self.id, l, self.type)+payl class ARP(Packet): name = "ARP" fields_desc = [ XShortField("hwtype", 0x0001), XShortEnumField("ptype", 0x0800, ETHER_TYPES), ByteField("hwlen", 6), ByteField("plen", 4), ShortEnumField("op", 1, {"who-has":1, "is-at":2, "RARP-req":3, "RARP-rep":4, "Dyn-RARP-req":5, "Dyn-RAR-rep":6, "Dyn-RARP-err":7, "InARP-req":8, "InARP-rep":9}), ARPSourceMACField("hwsrc"), SourceIPField("psrc","pdst"), MACField("hwdst", ETHER_ANY), IPField("pdst", "0.0.0.0") ] who_has = 1 is_at = 2 def answers