mirror of https://github.com/secdev/scapy.git
10309 lines
346 KiB
Python
Executable File
10309 lines
346 KiB
Python
Executable File
#! /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 <phil@secdev.org> ##
|
|
## ##
|
|
## 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. ##
|
|
## ##
|
|
#############################################################################
|
|
|
|
|
|
from __future__ import generators
|
|
|
|
BASE_VERSION = "1.0.6.1"
|
|
|
|
HG_NODE = "$Node$"
|
|
REVISION = "$Revision$"
|
|
|
|
VERSION = "v%s / %s" % (BASE_VERSION, (REVISION+"--")[11:23])
|
|
|
|
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()
|
|
caller=None
|
|
for f,l,n,c in stk:
|
|
if n == 'warning':
|
|
break
|
|
caller = l
|
|
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, code
|
|
import cPickle, copy, types, gzip, base64, re, zlib, array
|
|
from sets import Set
|
|
from select import select
|
|
from fcntl import ioctl
|
|
import fcntl
|
|
import warnings
|
|
warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__)
|
|
|
|
|
|
try:
|
|
import Gnuplot
|
|
GNUPLOT=1
|
|
except ImportError:
|
|
log_loading.info("did not find python gnuplot wrapper . Won't be able to plot")
|
|
GNUPLOT=0
|
|
|
|
try:
|
|
import pyx
|
|
PYX=1
|
|
except ImportError:
|
|
log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump()")
|
|
PYX=0
|
|
|
|
|
|
LINUX=sys.platform.startswith("linux")
|
|
OPENBSD=sys.platform.startswith("openbsd")
|
|
FREEBSD=sys.platform.startswith("freebsd")
|
|
DARWIN=sys.platform.startswith("darwin")
|
|
BIG_ENDIAN= struct.pack("H",1) == "\x00\x01"
|
|
X86_64 = (os.uname()[4] == 'x86_64')
|
|
SOLARIS=sys.platform.startswith("sunos")
|
|
|
|
|
|
if LINUX:
|
|
DNET=PCAP=0
|
|
else:
|
|
DNET=PCAP=1
|
|
|
|
|
|
if PCAP:
|
|
try:
|
|
import pcap
|
|
PCAP = 1
|
|
except ImportError:
|
|
if LINUX:
|
|
log_loading.warning("did not find pcap module. Fallback to linux primitives")
|
|
PCAP = 0
|
|
else:
|
|
if __name__ == "__main__":
|
|
log_loading.error("did not find pcap module")
|
|
raise SystemExit
|
|
else:
|
|
raise
|
|
|
|
if DNET:
|
|
try:
|
|
import dnet
|
|
DNET = 1
|
|
except ImportError:
|
|
if LINUX:
|
|
log_loading.warning("did not find dnet module. Fallback to linux primitives")
|
|
DNET = 0
|
|
else:
|
|
if __name__ == "__main__":
|
|
log_loading.error("did not find dnet module")
|
|
raise SystemExit
|
|
else:
|
|
raise
|
|
|
|
if not PCAP:
|
|
f = os.popen("tcpdump -V 2> /dev/null")
|
|
if f.close() >> 8 == 0x7f:
|
|
log_loading.warning("Failed to execute tcpdump. Check it is installed and in the PATH")
|
|
TCPDUMP=0
|
|
else:
|
|
TCPDUMP=1
|
|
|
|
|
|
|
|
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
|
|
|
|
inet_ntoa = socket.inet_ntoa
|
|
try:
|
|
inet_ntop = socket.inet_ntop
|
|
inet_pton = socket.inet_pton
|
|
except AttributeError:
|
|
log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present")
|
|
|
|
|
|
if SOLARIS:
|
|
# GRE is missing on Solaris
|
|
socket.IPPROTO_GRE = 47
|
|
|
|
|
|
|
|
############
|
|
## 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
|
|
ARPHDR_TUN = 65534
|
|
|
|
# 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
|
|
RTF_REJECT = 0x0200
|
|
|
|
# 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 /etc/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 /etc/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 /etc/services file (" + l + ")")
|
|
f.close()
|
|
except IOError:
|
|
log_loading.info("Can't open /etc/services file")
|
|
|
|
|
|
|
|
###########
|
|
## Tools ##
|
|
###########
|
|
|
|
def sane_color(x):
|
|
r=""
|
|
for i in x:
|
|
j = ord(i)
|
|
if (j < 32) or (j >= 127):
|
|
r=r+conf.color_theme.not_printable(".")
|
|
else:
|
|
r=r+i
|
|
return r
|
|
|
|
def sane(x):
|
|
r=""
|
|
for i in x:
|
|
j = ord(i)
|
|
if (j < 32) or (j >= 127):
|
|
r=r+"."
|
|
else:
|
|
r=r+i
|
|
return r
|
|
|
|
def hexdump(x):
|
|
x=str(x)
|
|
l = len(x)
|
|
i = 0
|
|
while i < l:
|
|
print "%04x " % i,
|
|
for j in range(16):
|
|
if i+j < l:
|
|
print "%02X" % ord(x[i+j]),
|
|
else:
|
|
print " ",
|
|
if j%16 == 7:
|
|
print "",
|
|
print " ",
|
|
print sane_color(x[i:i+16])
|
|
i += 16
|
|
|
|
def linehexdump(x, onlyasc=0, onlyhex=0):
|
|
x = str(x)
|
|
l = len(x)
|
|
if not onlyasc:
|
|
for i in range(l):
|
|
print "%02X" % ord(x[i]),
|
|
print "",
|
|
if not onlyhex:
|
|
print sane_color(x)
|
|
|
|
def hexstr(x, onlyasc=0, onlyhex=0):
|
|
s = []
|
|
if not onlyasc:
|
|
s.append(" ".join(map(lambda x:"%02x"%ord(x), x)))
|
|
if not onlyhex:
|
|
s.append(sane(x))
|
|
return " ".join(s)
|
|
|
|
if BIG_ENDIAN:
|
|
CRCPOLY=0x04c11db7L
|
|
else:
|
|
CRCPOLY=0xedb88320L
|
|
|
|
crc32 = zlib.crc32
|
|
|
|
|
|
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 inet_ntoa(struct.pack("I", x))
|
|
|
|
def itom(x):
|
|
return socket.ntohl((0xffffffff00000000L>>x)&0xffffffffL)&0xffffffffL
|
|
|
|
def do_graph(graph,prog=None,type="svg",target=None):
|
|
"""do_graph(graph, prog=conf.prog.dot, type="svg",target="| conf.prog.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
|
|
prog: which graphviz program to use"""
|
|
if prog is None:
|
|
prog = conf.prog.dot
|
|
if target is None:
|
|
target = "| %s" % conf.prog.display
|
|
w,r = os.popen2("%s -T %s %s" % (prog,type,target))
|
|
w.write(graph)
|
|
w.close()
|
|
|
|
_TEX_TR = {
|
|
"{":"{\\tt\\char123}",
|
|
"}":"{\\tt\\char125}",
|
|
"\\":"{\\tt\\char92}",
|
|
"^":"\\^{}",
|
|
"$":"\\$",
|
|
"#":"\\#",
|
|
"~":"\\~",
|
|
"_":"\\_",
|
|
"&":"\\&",
|
|
"%":"\\%",
|
|
"|":"{\\tt\\char124}",
|
|
"~":"{\\tt\\char126}",
|
|
"<":"{\\tt\\char60}",
|
|
">":"{\\tt\\char62}",
|
|
}
|
|
|
|
def tex_escape(x):
|
|
s = ""
|
|
for c in x:
|
|
s += _TEX_TR.get(c,c)
|
|
return s
|
|
|
|
def colgen(*lstcol,**kargs):
|
|
"""Returns a generator that mixes provided quantities forever
|
|
trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default"""
|
|
if len(lstcol) < 2:
|
|
lstcol *= 2
|
|
trans = kargs.get("trans", lambda x,y,z: (x,y,z))
|
|
while 1:
|
|
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:
|
|
yield trans(lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)])
|
|
|
|
|
|
|
|
##############################
|
|
## 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,"wb")
|
|
cPickle.dump(to_be_saved, f, pickleProto)
|
|
f.close()
|
|
|
|
def load_session(fname):
|
|
try:
|
|
s = cPickle.load(gzip.open(fname,"rb"))
|
|
except IOError:
|
|
s = cPickle.load(open(fname,"rb"))
|
|
scapy_session.clear()
|
|
scapy_session.update(s)
|
|
|
|
def update_session(fname):
|
|
try:
|
|
s = cPickle.load(gzip.open(fname,"rb"))
|
|
except IOError:
|
|
s = cPickle.load(open(fname,"rb"))
|
|
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,"wb"))
|
|
|
|
def load_object(fname):
|
|
return cPickle.load(gzip.open(fname,"rb"))
|
|
|
|
|
|
#################
|
|
## 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), itom(msk), gw, dev, ifaddr)
|
|
|
|
def add(self, *args, **kargs):
|
|
"""Ex:
|
|
add(net="192.168.1.0/24",gw="1.2.3.4")
|
|
"""
|
|
self.routes.append(self.make_route(*args,**kargs))
|
|
|
|
|
|
def delt(self, *args, **kargs):
|
|
"""delt(host|net, gw|dev)"""
|
|
route = self.make_route(*args,**kargs)
|
|
try:
|
|
i=self.routes.index(route)
|
|
del(self.routes[i])
|
|
except ValueError:
|
|
warning("no matching route found")
|
|
|
|
def ifchange(self, iff, addr):
|
|
the_addr,the_msk = (addr.split("/")+["32"])[:2]
|
|
the_msk = itom(int(the_msk))
|
|
the_rawaddr, = struct.unpack("I",inet_aton(the_addr))
|
|
the_net = the_rawaddr & the_msk
|
|
|
|
|
|
for i in range(len(self.routes)):
|
|
net,msk,gw,iface,addr = self.routes[i]
|
|
if iface != iff:
|
|
continue
|
|
if gw == '0.0.0.0':
|
|
self.routes[i] = (the_net,the_msk,gw,iface,the_addr)
|
|
else:
|
|
self.routes[i] = (net,msk,gw,iface,the_addr)
|
|
for i in arp_cache.keys():
|
|
del(arp_cache[i])
|
|
|
|
|
|
|
|
def ifdel(self, iff):
|
|
new_routes=[]
|
|
for rt in self.routes:
|
|
if rt[3] != iff:
|
|
new_routes.append(rt)
|
|
self.routes=new_routes
|
|
|
|
def ifadd(self, iff, addr):
|
|
the_addr,the_msk = (addr.split("/")+["32"])[:2]
|
|
the_msk = itom(int(the_msk))
|
|
the_rawaddr, = struct.unpack("I",inet_aton(the_addr))
|
|
the_net = the_rawaddr & the_msk
|
|
self.routes.append((the_net,the_msk,'0.0.0.0',iff,the_addr))
|
|
|
|
|
|
def route(self,dst):
|
|
# Transform "192.168.*.1-5" to one IP of the set
|
|
dst = dst.split("/")[0]
|
|
dst = dst.replace("*","0")
|
|
while 1:
|
|
l = dst.find("-")
|
|
if l < 0:
|
|
break
|
|
m = (dst[l:]+".").find(".")
|
|
dst = dst[:l]+dst[l+m:]
|
|
|
|
|
|
try:
|
|
dst=inet_aton(dst)
|
|
except socket.error:
|
|
dst=inet_aton(socket.gethostbyname(dst))
|
|
dst,=struct.unpack("I",dst)
|
|
pathes=[]
|
|
for d,m,gw,i,a in self.routes:
|
|
aa, = struct.unpack("I",inet_aton(a))
|
|
if aa == dst:
|
|
pathes.append((0xffffffffL,("lo",a,"0.0.0.0")))
|
|
if (dst & m) == (d & m):
|
|
pathes.append((m,(i,a,gw)))
|
|
if not pathes:
|
|
warning("No route found (no default route?)")
|
|
return "lo","0.0.0.0","0.0.0.0" #XXX linux specific!
|
|
# Choose the more specific route (greatest netmask).
|
|
# XXX: we don't care about metrics
|
|
pathes.sort()
|
|
return pathes[-1][1]
|
|
|
|
def get_if_bcast(self, iff):
|
|
for net, msk, gw, iface, addr in self.routes:
|
|
if (iff == iface and net != 0L):
|
|
bcast = atol(addr)|(~msk&0xffffffffL); # FIXME: check error in atol()
|
|
return ltoa(bcast);
|
|
warning("No broadcast address found for iface %s\n" % iff);
|
|
|
|
if DNET:
|
|
def get_if_raw_hwaddr(iff):
|
|
if iff[:2] == "lo":
|
|
return (772, '\x00'*6)
|
|
try:
|
|
l = dnet.intf().get(iff)
|
|
l = l["link_addr"]
|
|
except:
|
|
raise Exception("Error in attempting to get hw address for interface [%s]" % iff)
|
|
return l.type,l.data
|
|
def get_if_raw_addr(ifname):
|
|
i = dnet.intf()
|
|
return i.get(ifname)["addr"].data
|
|
else:
|
|
def get_if_raw_hwaddr(iff):
|
|
return struct.unpack("16xh6s8x",get_if(iff,SIOCGIFHWADDR))
|
|
|
|
def get_if_raw_addr(iff):
|
|
try:
|
|
return get_if(iff, SIOCGIFADDR)[20:24]
|
|
except IOError:
|
|
return "\0\0\0\0"
|
|
|
|
|
|
if PCAP:
|
|
def get_if_list():
|
|
# remove 'any' interface
|
|
return map(lambda x:x[0],filter(lambda x:x[1] is None,pcap.findalldevs()))
|
|
def get_working_if():
|
|
try:
|
|
return pcap.lookupdev()
|
|
except pcap.pcapc.EXCEPTION:
|
|
return 'lo'
|
|
|
|
def attach_filter(s, filter):
|
|
warning("attach_filter() should not be called in PCAP mode")
|
|
def set_promisc(s,iff,val=1):
|
|
warning("set_promisc() should not be called in DNET/PCAP mode")
|
|
|
|
else:
|
|
def get_if_list():
|
|
f=open("/proc/net/dev","r")
|
|
lst = []
|
|
f.readline()
|
|
f.readline()
|
|
for l in f:
|
|
lst.append(l.split(":")[0].strip())
|
|
return lst
|
|
def get_working_if():
|
|
for i in get_if_list():
|
|
if i == 'lo':
|
|
continue
|
|
ifflags = struct.unpack("16xH14x",get_if(i,SIOCGIFFLAGS))[0]
|
|
if ifflags & IFF_UP:
|
|
return i
|
|
return "lo"
|
|
def attach_filter(s, filter):
|
|
# XXX We generate the filter on the interface conf.iface
|
|
# because tcpdump open the "any" interface and ppp interfaces
|
|
# in cooked mode. As we use them in raw mode, the filter will not
|
|
# work... one solution could be to use "any" interface and translate
|
|
# the filter from cooked mode to raw mode
|
|
# mode
|
|
if not TCPDUMP:
|
|
return
|
|
try:
|
|
f = os.popen("%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump,conf.iface,filter))
|
|
except OSError,msg:
|
|
log_interactive.warning("Failed to execute tcpdump: (%s)")
|
|
return
|
|
lines = f.readlines()
|
|
if f.close():
|
|
raise Exception("Filter parse error")
|
|
nb = int(lines[0])
|
|
bpf = ""
|
|
for l in lines[1:]:
|
|
bpf += struct.pack("HBBI",*map(long,l.split()))
|
|
|
|
# XXX. Argl! We need to give the kernel a pointer on the BPF,
|
|
# python object header seems to be 20 bytes. 36 bytes for x86 64bits arch.
|
|
if X86_64:
|
|
bpfh = struct.pack("HL", nb, id(bpf)+36)
|
|
else:
|
|
bpfh = struct.pack("HI", nb, id(bpf)+20)
|
|
s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh)
|
|
|
|
def set_promisc(s,iff,val=1):
|
|
mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, "")
|
|
if val:
|
|
cmd = PACKET_ADD_MEMBERSHIP
|
|
else:
|
|
cmd = PACKET_DROP_MEMBERSHIP
|
|
s.setsockopt(SOL_PACKET, cmd, mreq)
|
|
|
|
|
|
if not LINUX:
|
|
|
|
def new_read_routes():
|
|
|
|
rtlst = []
|
|
def addrt(rt,lst):
|
|
dst,gw = rt
|
|
lst.append(rt)
|
|
|
|
r = dnet.route()
|
|
print r.loop(addrt, rtlst)
|
|
return rtlst
|
|
|
|
def read_routes():
|
|
if SOLARIS:
|
|
f=os.popen("netstat -rvn") # -f inet
|
|
else:
|
|
f=os.popen("netstat -rn") # -f inet
|
|
ok = 0
|
|
mtu_present = False
|
|
routes = []
|
|
for l in f.readlines():
|
|
if not l:
|
|
break
|
|
l = l.strip()
|
|
if l.find("----") >= 0: # a separation line
|
|
continue
|
|
if l.find("Destination") >= 0:
|
|
ok = 1
|
|
if l.find("Mtu") >= 0:
|
|
mtu_present = True
|
|
continue
|
|
if ok == 0:
|
|
continue
|
|
if not l:
|
|
break
|
|
if SOLARIS:
|
|
dest,mask,gw,netif,mxfrg,rtt,ref,flg = l.split()[:8]
|
|
else:
|
|
if mtu_present:
|
|
dest,gw,flg,ref,use,mtu,netif = l.split()[:7]
|
|
else:
|
|
dest,gw,flg,ref,use,netif = l.split()[:6]
|
|
if flg.find("Lc") >= 0:
|
|
continue
|
|
if dest == "default":
|
|
dest = 0L
|
|
netmask = 0L
|
|
else:
|
|
if SOLARIS:
|
|
netmask, = struct.unpack("I",inet_aton(mask))
|
|
elif "/" in dest:
|
|
dest,netmask = dest.split("/")
|
|
netmask = itom(int(netmask))
|
|
else:
|
|
netmask = itom((dest.count(".") + 1) * 8)
|
|
dest += ".0"*(3-dest.count("."))
|
|
dest, = struct.unpack("I",inet_aton(dest))
|
|
if not "G" in flg:
|
|
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 = 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 = 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()
|
|
flags = int(flags,16)
|
|
if flags & RTF_UP == 0:
|
|
continue
|
|
if flags & RTF_REJECT:
|
|
continue
|
|
ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",iff))
|
|
addrfamily = struct.unpack("h",ifreq[16:18])[0]
|
|
if addrfamily == socket.AF_INET:
|
|
ifaddr = inet_ntoa(ifreq[20:24])
|
|
else:
|
|
warning("Interface %s: unkownn address family (%i)"%(iff, addrfamily))
|
|
continue
|
|
routes.append((long(dst,16),
|
|
long(msk,16),
|
|
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 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) for interface [%s]" % (addrfamily,iff))
|
|
|
|
|
|
|
|
#####################
|
|
## 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):
|
|
tmp = map(ord, inet_aton(ip))
|
|
if (tmp[0] & 0xf0) == 0xe0: # mcast @
|
|
return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
|
|
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):
|
|
tmp = map(ord, inet_aton(ip))
|
|
if (tmp[0] & 0xf0) == 0xe0: # mcast @
|
|
return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
|
|
iff,a,gw = conf.route.route(ip)
|
|
if ( (iff == "lo") or (ip == conf.route.get_if_bcast(iff)) ):
|
|
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 not timeout or (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,
|
|
nofilter=1)
|
|
if res is not None:
|
|
mac = res.payload.hwsrc
|
|
arp_cache[ip] = (mac,time.time())
|
|
return mac
|
|
return None
|
|
|
|
|
|
####################
|
|
## Random numbers ##
|
|
####################
|
|
|
|
class VolatileValue:
|
|
def __repr__(self):
|
|
return "<%s>" % self.__class__.__name__
|
|
def __getattr__(self, attr):
|
|
return getattr(self._fix(),attr)
|
|
def _fix(self):
|
|
return None
|
|
|
|
|
|
class RandField(VolatileValue):
|
|
pass
|
|
|
|
|
|
class RandNum(RandField):
|
|
min = 0
|
|
max = 0
|
|
def __init__(self, min, max):
|
|
self.min = min
|
|
self.max = max
|
|
def _fix(self):
|
|
# XXX: replace with sth that guarantee unicity
|
|
return random.randrange(self.min, self.max)
|
|
|
|
class RandNumGamma(RandField):
|
|
def __init__(self, alpha, beta):
|
|
self.alpha = alpha
|
|
self.beta = beta
|
|
def _fix(self):
|
|
return int(round(random.gammavariate(self.alpha, self.beta)))
|
|
|
|
class RandNumGauss(RandField):
|
|
def __init__(self, mu, sigma):
|
|
self.mu = mu
|
|
self.sigma = sigma
|
|
def _fix(self):
|
|
return int(round(random.gauss(self.mu, self.sigma)))
|
|
|
|
class RandNumExpo(RandField):
|
|
def __init__(self, lambd):
|
|
self.lambd = lambd
|
|
def _fix(self):
|
|
return int(round(random.expovariate(self.lambd)))
|
|
|
|
class RandByte(RandNum):
|
|
def __init__(self):
|
|
RandNum.__init__(self, 0, 2L**8)
|
|
|
|
class RandShort(RandNum):
|
|
def __init__(self):
|
|
RandNum.__init__(self, 0, 2L**16)
|
|
|
|
class RandInt(RandNum):
|
|
def __init__(self):
|
|
RandNum.__init__(self, 0, 2L**32)
|
|
|
|
class RandSInt(RandNum):
|
|
def __init__(self):
|
|
RandNum.__init__(self, -2L**31, 2L**31)
|
|
|
|
class RandLong(RandNum):
|
|
def __init__(self):
|
|
RandNum.__init__(self, 0, 2L**64)
|
|
|
|
class RandSLong(RandNum):
|
|
def __init__(self):
|
|
RandNum.__init__(self, -2L**63, 2L**63)
|
|
|
|
class RandChoice(RandField):
|
|
def __init__(self, *args):
|
|
self._choice = args
|
|
def _fix(self):
|
|
return random.choice(self._choice)
|
|
|
|
class RandString(RandField):
|
|
def __init__(self, size, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"):
|
|
self.chars = chars
|
|
self.size = size
|
|
def _fix(self):
|
|
s = ""
|
|
for i in range(self.size):
|
|
s += random.choice(self.chars)
|
|
return s
|
|
|
|
class RandBin(RandString):
|
|
def __init__(self, size):
|
|
RandString.__init__(self, size, "".join(map(chr,range(256))))
|
|
|
|
|
|
class RandTermString(RandString):
|
|
def __init__(self, size, term):
|
|
RandString.__init__(self, size, "".join(map(chr,range(1,256))))
|
|
self.term = term
|
|
def _fix(self):
|
|
return RandString._fix(self)+self.term
|
|
|
|
|
|
|
|
class RandIP(RandString):
|
|
def __init__(self, iptemplate="0.0.0.0/0"):
|
|
self.ip = Net(iptemplate)
|
|
def _fix(self):
|
|
return self.ip.choice()
|
|
|
|
class RandMAC(RandString):
|
|
def __init__(self, template="*"):
|
|
template += ":*:*:*:*:*"
|
|
template = template.split(":")
|
|
self.mac = ()
|
|
for i in range(6):
|
|
if template[i] == "*":
|
|
v = RandByte()
|
|
elif "-" in template[i]:
|
|
x,y = template[i].split("-")
|
|
v = RandNum(int(x,16), int(y,16))
|
|
else:
|
|
v = int(template[i],16)
|
|
self.mac += (v,)
|
|
def _fix(self):
|
|
return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac
|
|
|
|
|
|
class RandOID(RandString):
|
|
def __init__(self, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)):
|
|
self.depth = depth
|
|
self.idnum = idnum
|
|
def _fix(self):
|
|
return ".".join(map(str, [self.idnum for i in xrange(1+self.depth)]))
|
|
|
|
|
|
# Automatic timestamp
|
|
|
|
class AutoTime(VolatileValue):
|
|
def __init__(self, base=None):
|
|
if base == None:
|
|
self.diff = 0
|
|
else:
|
|
self.diff = time.time()-base
|
|
def _fix(self):
|
|
return time.time()-self.diff
|
|
|
|
class IntAutoTime(AutoTime):
|
|
def _fix(self):
|
|
return int(AutoTime.current_val(self))
|
|
|
|
|
|
|
|
class DelayedEval(VolatileValue):
|
|
""" Exemple of usage: DelayedEval("time.time()") """
|
|
def __init__(self, expr):
|
|
self.expr = expr
|
|
def _fix(self):
|
|
return eval(self.expr)
|
|
|
|
|
|
class IncrementalValue(VolatileValue):
|
|
def __init__(self, start=0, step=1, restart=-1):
|
|
self.start = self.val = start
|
|
self.step = step
|
|
self.restart = restart
|
|
def _fix(self):
|
|
v = self.val
|
|
if self.val == self.restart :
|
|
self.val = self.start
|
|
else:
|
|
self.val += self.step
|
|
return v
|
|
|
|
def corrupt_bytes(s, p=0.01, n=None):
|
|
s = array.array("B",str(s))
|
|
l = len(s)
|
|
if n is None:
|
|
n = max(1,int(l*p))
|
|
for i in random.sample(xrange(l), n):
|
|
s[i] = random.randint(0,255)
|
|
return s.tostring()
|
|
|
|
def corrupt_bits(s, p=0.01, n=None):
|
|
s = array.array("B",str(s))
|
|
l = len(s)*8
|
|
if n is None:
|
|
n = max(1,int(l*p))
|
|
for i in random.sample(xrange(l), n):
|
|
s[i/8] ^= 1 << (i%8)
|
|
return s.tostring()
|
|
|
|
|
|
class CorruptedBytes(VolatileValue):
|
|
def __init__(self, s, p=0.01, n=None):
|
|
self.s = s
|
|
self.p = p
|
|
self.n = n
|
|
def _fix(self):
|
|
return corrupt_bytes(self.s, self.p, self.n)
|
|
|
|
class CorruptedBits(CorruptedBytes):
|
|
def _fix(self):
|
|
return corrupt_bits(self.s, self.p, self.n)
|
|
|
|
|
|
|
|
|
|
################
|
|
## Generators ##
|
|
################
|
|
|
|
class Gen(object):
|
|
def __iter__(self):
|
|
return iter([])
|
|
|
|
class SetGen(Gen):
|
|
def __init__(self, set, _iterpacket=1):
|
|
self._iterpacket=_iterpacket
|
|
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) and type(i[0]) is int and type(i[1]) is int:
|
|
if (i[0] <= i[1]):
|
|
j=i[0]
|
|
while j <= i[1]:
|
|
yield j
|
|
j += 1
|
|
elif isinstance(i, Gen) and (self._iterpacket or not isinstance(i,Packet)):
|
|
for j in i:
|
|
yield j
|
|
else:
|
|
yield i
|
|
def __repr__(self):
|
|
return "<SetGen %s>" % 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<<netmask) , max(y, (x | (0xffL>>(8-netmask))))+1)
|
|
else:
|
|
a = (int(a) & (0xffL<<netmask),(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 choice(self):
|
|
ip = []
|
|
for v in self.parsed:
|
|
ip.append(str(random.randint(v[0],v[1]-1)))
|
|
return ".".join(ip)
|
|
|
|
def __repr__(self):
|
|
return "Net(%r)" % self.repr
|
|
|
|
|
|
#############
|
|
## Results ##
|
|
#############
|
|
|
|
class PacketList:
|
|
res = []
|
|
def __init__(self, res=None, name="PacketList", stats=None):
|
|
"""create a packet list from a list of packets
|
|
res: the list of packets
|
|
stats: a list of classes that will appear in the stats (defaults to [TCP,UDP,ICMP])"""
|
|
if stats is None:
|
|
stats = [ TCP,UDP,ICMP ]
|
|
self.stats = stats
|
|
if res is None:
|
|
res = []
|
|
if isinstance(res, PacketList):
|
|
res = res.res
|
|
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 = ""
|
|
ct = conf.color_theme
|
|
for p in stats:
|
|
s += " %s%s%s" % (ct.packetlist_proto(p.name),
|
|
ct.punct(":"),
|
|
ct.packetlist_value(stats[p]))
|
|
s += " %s%s%s" % (ct.packetlist_proto("Other"),
|
|
ct.punct(":"),
|
|
ct.packetlist_value(other))
|
|
return "%s%s%s%s%s" % (ct.punct("<"),
|
|
ct.packetlist_name(self.listname),
|
|
ct.punct(":"),
|
|
s,
|
|
ct.punct(">"))
|
|
def __getattr__(self, attr):
|
|
return getattr(self.res, attr)
|
|
def __getitem__(self, item):
|
|
if isinstance(item,type) and issubclass(item,Packet):
|
|
return self.__class__(filter(lambda x: item in self._elt2pkt(x),self.res),
|
|
name="%s from %s"%(item.__name__,self.listname))
|
|
if type(item) is slice:
|
|
return self.__class__(self.res.__getitem__(item),
|
|
name = "mod %s" % self.listname)
|
|
return self.res.__getitem__(item)
|
|
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, lfilter=None):
|
|
"""prints a summary of each packet
|
|
prn: function to apply to each packet instead of lambda x:x.summary()
|
|
lfilter: truth function to apply to each packet to decide whether it will be displayed"""
|
|
for r in self.res:
|
|
if lfilter is not None:
|
|
if not lfilter(r):
|
|
continue
|
|
if prn is None:
|
|
print self._elt2sum(r)
|
|
else:
|
|
print prn(r)
|
|
def nsummary(self,prn=None, lfilter=None):
|
|
"""prints a summary of each packet with the packet's number
|
|
prn: function to apply to each packet instead of lambda x:x.summary()
|
|
lfilter: truth function to apply to each packet to decide whether it will be displayed"""
|
|
for i in range(len(self.res)):
|
|
if lfilter is not None:
|
|
if not lfilter(self.res[i]):
|
|
continue
|
|
print conf.color_theme.id(i,"%04i"),
|
|
if prn is None:
|
|
print self._elt2sum(self.res[i])
|
|
else:
|
|
print prn(self.res[i])
|
|
def display(self): # Deprecated. Use show()
|
|
"""deprecated. is show()"""
|
|
self.show()
|
|
def show(self, *args, **kargs):
|
|
"""Best way to display the packet list. Defaults to nsummary() method"""
|
|
return self.nsummary(*args, **kargs)
|
|
|
|
def filter(self, func):
|
|
"""Returns a packet list filtered by a truth function"""
|
|
return self.__class__(filter(func,self.res),
|
|
name="filtered %s"%self.listname)
|
|
def make_table(self, *args, **kargs):
|
|
"""Prints a table using a function that returs for each packet its head column value, head row value and displayed value
|
|
ex: p.make_table(lambda x:(x[IP].dst, x[TCP].dport, x[TCP].sprintf("%flags%")) """
|
|
return make_table(self.res, *args, **kargs)
|
|
def make_lined_table(self, *args, **kargs):
|
|
"""Same as make_table, but print a table with lines"""
|
|
return make_lined_table(self.res, *args, **kargs)
|
|
def make_tex_table(self, *args, **kargs):
|
|
"""Same as make_table, but print a table with LaTeX syntax"""
|
|
return make_tex_table(self.res, *args, **kargs)
|
|
|
|
def plot(self, f, lfilter=None,**kargs):
|
|
"""Applies a function to each packet to get a value that will be plotted with GnuPlot. A gnuplot object is returned
|
|
lfilter: a truth function that decides whether a packet must be ploted"""
|
|
g=Gnuplot.Gnuplot()
|
|
l = self.res
|
|
if lfilter is not None:
|
|
l = filter(lfilter, l)
|
|
l = map(f,l)
|
|
g.plot(Gnuplot.Data(l, **kargs))
|
|
return g
|
|
|
|
def diffplot(self, f, delay=1, lfilter=None, **kargs):
|
|
"""diffplot(f, delay=1, lfilter=None)
|
|
Applies a function to couples (l[i],l[i+delay])"""
|
|
g = Gnuplot.Gnuplot()
|
|
l = self.res
|
|
if lfilter is not None:
|
|
l = filter(lfilter, l)
|
|
l = map(f,l[:-delay],l[delay:])
|
|
g.plot(Gnuplot.Data(l, **kargs))
|
|
return g
|
|
|
|
def multiplot(self, f, lfilter=None, **kargs):
|
|
"""Uses a function that returns a label and a value for this label, then plots all the values label by label"""
|
|
g=Gnuplot.Gnuplot()
|
|
l = self.res
|
|
if lfilter is not None:
|
|
l = filter(lfilter, l)
|
|
|
|
d={}
|
|
for e in l:
|
|
k,v = f(e)
|
|
if k in d:
|
|
d[k].append(v)
|
|
else:
|
|
d[k] = [v]
|
|
data=[]
|
|
for k in d:
|
|
data.append(Gnuplot.Data(d[k], title=k, **kargs))
|
|
|
|
g.plot(*data)
|
|
return g
|
|
|
|
|
|
def rawhexdump(self):
|
|
"""Prints an hexadecimal dump of each packet in the list"""
|
|
for p in self:
|
|
hexdump(self._elt2pkt(p))
|
|
|
|
def hexraw(self, lfilter=None):
|
|
"""Same as nsummary(), except that if a packet has a Raw layer, it will be hexdumped
|
|
lfilter: a truth function that decides whether a packet must be displayed"""
|
|
for i in range(len(self.res)):
|
|
p = self._elt2pkt(self.res[i])
|
|
if lfilter is not None and not lfilter(p):
|
|
continue
|
|
print "%s %s %s" % (conf.color_theme.id(i,"%04i"),
|
|
p.sprintf("%.time%"),
|
|
self._elt2sum(self.res[i]))
|
|
if p.haslayer(Raw):
|
|
hexdump(p.getlayer(Raw).load)
|
|
|
|
def hexdump(self, lfilter=None):
|
|
"""Same as nsummary(), except that packets are also hexdumped
|
|
lfilter: a truth function that decides whether a packet must be displayed"""
|
|
for i in range(len(self.res)):
|
|
p = self._elt2pkt(self.res[i])
|
|
if lfilter is not None and not lfilter(p):
|
|
continue
|
|
print "%s %s %s" % (conf.color_theme.id(i,"%04i"),
|
|
p.sprintf("%.time%"),
|
|
self._elt2sum(self.res[i]))
|
|
hexdump(p)
|
|
|
|
def padding(self, lfilter=None):
|
|
"""Same as hexraw(), for Padding layer"""
|
|
for i in range(len(self.res)):
|
|
p = self._elt2pkt(self.res[i])
|
|
if p.haslayer(Padding):
|
|
if lfilter is None or lfilter(p):
|
|
print "%s %s %s" % (conf.color_theme.id(i,"%04i"),
|
|
p.sprintf("%.time%"),
|
|
self._elt2sum(self.res[i]))
|
|
hexdump(p.getlayer(Padding).load)
|
|
|
|
def nzpadding(self, lfilter=None):
|
|
"""Same as padding() but only non null padding"""
|
|
for i in range(len(self.res)):
|
|
p = self._elt2pkt(self.res[i])
|
|
if p.haslayer(Padding):
|
|
pad = p.getlayer(Padding).load
|
|
if pad == pad[0]*len(pad):
|
|
continue
|
|
if lfilter is None or lfilter(p):
|
|
print "%s %s %s" % (conf.color_theme.id(i,"%04i"),
|
|
p.sprintf("%.time%"),
|
|
self._elt2sum(self.res[i]))
|
|
hexdump(p.getlayer(Padding).load)
|
|
|
|
|
|
def conversations(self, getsrcdst=None,**kargs):
|
|
"""Graphes a conversations between sources and destinations and display it
|
|
(using graphviz and imagemagick)
|
|
getsrcdst: a function that takes an element of the list and return the source and dest
|
|
by defaults, return source and destination IP
|
|
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
|
|
prog: which graphviz program to use"""
|
|
if getsrcdst is None:
|
|
getsrcdst = lambda x:(x[IP].src, x[IP].dst)
|
|
conv = {}
|
|
for p in self.res:
|
|
p = self._elt2pkt(p)
|
|
try:
|
|
c = getsrcdst(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 afterglow(self, src=None, event=None, dst=None, **kargs):
|
|
"""Experimental clone attempt of http://sourceforge.net/projects/afterglow
|
|
each datum is reduced as src -> event -> dst and the data are graphed.
|
|
by default we have IP.src -> IP.dport -> IP.dst"""
|
|
if src is None:
|
|
src = lambda x: x[IP].src
|
|
if event is None:
|
|
event = lambda x: x[IP].dport
|
|
if dst is None:
|
|
dst = lambda x: x[IP].dst
|
|
sl = {}
|
|
el = {}
|
|
dl = {}
|
|
for i in self.res:
|
|
try:
|
|
s,e,d = src(i),event(i),dst(i)
|
|
if s in sl:
|
|
n,l = sl[s]
|
|
n += 1
|
|
if e not in l:
|
|
l.append(e)
|
|
sl[s] = (n,l)
|
|
else:
|
|
sl[s] = (1,[e])
|
|
if e in el:
|
|
n,l = el[e]
|
|
n+=1
|
|
if d not in l:
|
|
l.append(d)
|
|
el[e] = (n,l)
|
|
else:
|
|
el[e] = (1,[d])
|
|
dl[d] = dl.get(d,0)+1
|
|
except:
|
|
continue
|
|
|
|
import math
|
|
def normalize(n):
|
|
return 2+math.log(n)/4.0
|
|
|
|
def minmax(x):
|
|
m,M = min(x),max(x)
|
|
if m == M:
|
|
m = 0
|
|
if M == 0:
|
|
M = 1
|
|
return m,M
|
|
|
|
mins,maxs = minmax(map(lambda (x,y): x, sl.values()))
|
|
mine,maxe = minmax(map(lambda (x,y): x, el.values()))
|
|
mind,maxd = minmax(dl.values())
|
|
|
|
gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n'
|
|
|
|
gr += "# src nodes\n"
|
|
for s in sl:
|
|
n,l = sl[s]; n = 1+float(n-mins)/(maxs-mins)
|
|
gr += '"src.%s" [label = "%s", shape=box, fillcolor="#FF0000", style=filled, fixedsize=1, height=%.2f,width=%.2f];\n' % (`s`,`s`,n,n)
|
|
gr += "# event nodes\n"
|
|
for e in el:
|
|
n,l = el[e]; n = n = 1+float(n-mine)/(maxe-mine)
|
|
gr += '"evt.%s" [label = "%s", shape=circle, fillcolor="#00FFFF", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`e`,`e`,n,n)
|
|
for d in dl:
|
|
n = dl[d]; n = n = 1+float(n-mind)/(maxd-mind)
|
|
gr += '"dst.%s" [label = "%s", shape=triangle, fillcolor="#0000ff", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`d`,`d`,n,n)
|
|
|
|
gr += "###\n"
|
|
for s in sl:
|
|
n,l = sl[s]
|
|
for e in l:
|
|
gr += ' "src.%s" -> "evt.%s";\n' % (`s`,`e`)
|
|
for e in el:
|
|
n,l = el[e]
|
|
for d in l:
|
|
gr += ' "evt.%s" -> "dst.%s";\n' % (`e`,`d`)
|
|
|
|
gr += "}"
|
|
open("/tmp/aze","w").write(gr)
|
|
do_graph(gr, **kargs)
|
|
|
|
|
|
|
|
def timeskew_graph(self, ip, **kargs):
|
|
"""Tries to graph the timeskew between the timestamps and real time for a given ip"""
|
|
res = map(lambda x: self._elt2pkt(x), self.res)
|
|
b = filter(lambda x:x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), 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]))
|
|
if not c:
|
|
warning("No timestamps found in packet list")
|
|
return
|
|
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
|
|
|
|
def _dump_document(self, **kargs):
|
|
d = pyx.document.document()
|
|
l = len(self.res)
|
|
for i in range(len(self.res)):
|
|
elt = self.res[i]
|
|
c = self._elt2pkt(elt).canvas_dump(**kargs)
|
|
cbb = c.bbox()
|
|
c.text(cbb.left(),cbb.top()+1,r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i,l),[pyx.text.size.LARGE])
|
|
if conf.verb >= 2:
|
|
os.write(1,".")
|
|
d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4,
|
|
margin=1*pyx.unit.t_cm,
|
|
fittosize=1))
|
|
return d
|
|
|
|
|
|
|
|
def psdump(self, filename = None, **kargs):
|
|
"""Creates a multipage poscript file with a psdump of every packet
|
|
filename: name of the file to write to. If empty, a temporary file is used and
|
|
conf.prog.psreader is called"""
|
|
d = self._dump_document(**kargs)
|
|
if filename is None:
|
|
filename = "/tmp/scapy.psd.%i" % os.getpid()
|
|
d.writePSfile(filename)
|
|
os.system("%s %s.ps &" % (conf.prog.psreader,filename))
|
|
else:
|
|
d.writePSfile(filename)
|
|
print
|
|
|
|
def pdfdump(self, filename = None, **kargs):
|
|
"""Creates a PDF file with a psdump of every packet
|
|
filename: name of the file to write to. If empty, a temporary file is used and
|
|
conf.prog.pdfreader is called"""
|
|
d = self._dump_document(**kargs)
|
|
if filename is None:
|
|
filename = "/tmp/scapy.psd.%i" % os.getpid()
|
|
d.writePDFfile(filename)
|
|
os.system("%s %s.pdf &" % (conf.prog.pdfreader,filename))
|
|
else:
|
|
d.writePDFfile(filename)
|
|
print
|
|
|
|
def sr(self,multi=0):
|
|
"""sr([multi=1]) -> (SndRcvList, PacketList)
|
|
Matches packets in the list and return ( (matched couples), (unmatched packets) )"""
|
|
remain = self.res[:]
|
|
sr = []
|
|
i = 0
|
|
while i < len(remain):
|
|
s = remain[i]
|
|
j = i
|
|
while j < len(remain)-1:
|
|
j += 1
|
|
r = remain[j]
|
|
if r.answers(s):
|
|
sr.append((s,r))
|
|
if multi:
|
|
remain[i]._answered=1
|
|
remain[j]._answered=2
|
|
continue
|
|
del(remain[j])
|
|
del(remain[i])
|
|
i -= 1
|
|
break
|
|
i += 1
|
|
if multi:
|
|
remain = filter(lambda x:not hasattr(x,"_answered"), remain)
|
|
return SndRcvList(sr),PacketList(remain)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 show(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.padding = 0
|
|
self.hloc = None
|
|
self.nloc = None
|
|
|
|
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 get_trace(self):
|
|
trace = {}
|
|
for s,r in self.res:
|
|
if IP not in s:
|
|
continue
|
|
d = s[IP].dst
|
|
if d not in trace:
|
|
trace[d] = {}
|
|
trace[d][s[IP].ttl] = r[IP].src, ICMP not in r
|
|
for k in trace.values():
|
|
m = filter(lambda x:k[x][1], k.keys())
|
|
if not m:
|
|
continue
|
|
m = min(m)
|
|
for l in k.keys():
|
|
if l > m:
|
|
del(k[l])
|
|
return trace
|
|
|
|
def trace3D(self):
|
|
"""Give a 3D representation of the traceroute.
|
|
right button: rotate the scene
|
|
middle button: zoom
|
|
left button: move the scene
|
|
left button on a ball: toggle IP displaying
|
|
ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result"""
|
|
trace = self.get_trace()
|
|
import visual
|
|
|
|
class IPsphere(visual.sphere):
|
|
def __init__(self, ip, **kargs):
|
|
visual.sphere.__init__(self, **kargs)
|
|
self.ip=ip
|
|
self.label=None
|
|
self.setlabel(self.ip)
|
|
def setlabel(self, txt,visible=None):
|
|
if self.label is not None:
|
|
if visible is None:
|
|
visible = self.label.visible
|
|
self.label.visible = 0
|
|
elif visible is None:
|
|
visible=0
|
|
self.label=visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible)
|
|
def action(self):
|
|
self.label.visible ^= 1
|
|
|
|
visual.scene = visual.display()
|
|
visual.scene.exit_on_close(0)
|
|
start = visual.box()
|
|
rings={}
|
|
tr3d = {}
|
|
for i in trace:
|
|
tr = trace[i]
|
|
tr3d[i] = []
|
|
ttl = tr.keys()
|
|
for t in range(1,max(ttl)+1):
|
|
if t not in rings:
|
|
rings[t] = []
|
|
if t in tr:
|
|
if tr[t] not in rings[t]:
|
|
rings[t].append(tr[t])
|
|
tr3d[i].append(rings[t].index(tr[t]))
|
|
else:
|
|
rings[t].append(("unk",-1))
|
|
tr3d[i].append(len(rings[t])-1)
|
|
for t in rings:
|
|
r = rings[t]
|
|
l = len(r)
|
|
for i in range(l):
|
|
if r[i][1] == -1:
|
|
col = (0.75,0.75,0.75)
|
|
elif r[i][1]:
|
|
col = visual.color.green
|
|
else:
|
|
col = visual.color.blue
|
|
|
|
s = IPsphere(pos=((l-1)*visual.cos(2*i*visual.pi/l),(l-1)*visual.sin(2*i*visual.pi/l),2*t),
|
|
ip = r[i][0],
|
|
color = col)
|
|
for trlst in tr3d.values():
|
|
if t <= len(trlst):
|
|
if trlst[t-1] == i:
|
|
trlst[t-1] = s
|
|
forecol = colgen(0.625, 0.4375, 0.25, 0.125)
|
|
for trlst in tr3d.values():
|
|
col = forecol.next()
|
|
start = (0,0,0)
|
|
for ip in trlst:
|
|
visual.cylinder(pos=start,axis=ip.pos-start,color=col,radius=0.2)
|
|
start = ip.pos
|
|
|
|
movcenter=None
|
|
while 1:
|
|
if visual.scene.kb.keys:
|
|
k = visual.scene.kb.getkey()
|
|
if k == "esc":
|
|
break
|
|
if visual.scene.mouse.events:
|
|
ev = visual.scene.mouse.getevent()
|
|
if ev.press == "left":
|
|
o = ev.pick
|
|
if o:
|
|
if ev.ctrl:
|
|
if o.ip == "unk":
|
|
continue
|
|
savcolor = o.color
|
|
o.color = (1,0,0)
|
|
a,b=sr(IP(dst=o.ip)/TCP(dport=[21,22,23,25,80,443]),timeout=2)
|
|
o.color = savcolor
|
|
if len(a) == 0:
|
|
txt = "%s:\nno results" % o.ip
|
|
else:
|
|
txt = "%s:\n" % o.ip
|
|
for s,r in a:
|
|
txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n")
|
|
o.setlabel(txt, visible=1)
|
|
else:
|
|
if hasattr(o, "action"):
|
|
o.action()
|
|
elif ev.drag == "left":
|
|
movcenter = ev.pos
|
|
elif ev.drop == "left":
|
|
movcenter = None
|
|
if movcenter:
|
|
visual.scene.center -= visual.scene.mouse.pos-movcenter
|
|
movcenter = visual.scene.mouse.pos
|
|
|
|
|
|
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,padding):
|
|
self.graphASN = ASN
|
|
self.graphpadding = padding
|
|
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("<T%ir,TCP.sport%> %TCP.sport%: %TCP.flags%"))
|
|
trace[s.ttl] = r.sprintf('"%IP.src%":T%ir,TCP.sport%')
|
|
elif r.haslayer(UDP):
|
|
p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%"))
|
|
trace[s.ttl] = r.sprintf('"%IP.src%":U%ir,UDP.sport%')
|
|
elif r.haslayer(ICMP):
|
|
p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%"))
|
|
trace[s.ttl] = r.sprintf('"%IP.src%":I%ir,ICMP.type%')
|
|
else:
|
|
p.append(r.sprintf("<P%ir,IP.proto> 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
|
|
|
|
|
|
backcolorlist=colgen("60","86","ba","ff")
|
|
forecolorlist=colgen("a0","70","40","20")
|
|
|
|
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
|
|
col = backcolorlist.next()
|
|
s += '\t\tcolor="#%s%s%s";' % col
|
|
s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col
|
|
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
|
|
|
|
if padding:
|
|
s += "\n#Padding\n"
|
|
pad={}
|
|
for snd,rcv in self.res:
|
|
if rcv.src not in ports and rcv.haslayer(Padding):
|
|
p = rcv.getlayer(Padding).load
|
|
if p != "\x00"*len(p):
|
|
pad[rcv.src]=None
|
|
for rcv in pad:
|
|
s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv
|
|
|
|
|
|
|
|
s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n"
|
|
|
|
|
|
for rtk in rt:
|
|
s += "#---[%s\n" % `rtk`
|
|
s += '\t\tedge [color="#%s%s%s"];\n' % forecolorlist.next()
|
|
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, padding=0, **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
|
|
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
|
|
prog: which graphviz program to use"""
|
|
if (self.graphdef is None or
|
|
self.graphASN != ASN or
|
|
self.graphpadding != padding):
|
|
self.make_graph(ASN,padding)
|
|
|
|
do_graph(self.graphdef, **kargs)
|
|
|
|
|
|
|
|
|
|
############
|
|
## Fields ##
|
|
############
|
|
|
|
class Field:
|
|
"""For more informations on how this work, please refer to
|
|
http://www.secdev.org/projects/scapy/files/scapydoc.pdf
|
|
chapter ``Adding a New Field''"""
|
|
islist=0
|
|
holds_packets=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 i2len(self, pkt, x):
|
|
"""Convert internal value to a length usable by a FieldLenField"""
|
|
return self.sz
|
|
def h2i(self, pkt, x):
|
|
"""Convert human value to internal value"""
|
|
return x
|
|
def i2h(self, pkt, x):
|
|
"""Convert internal value to human value"""
|
|
return x
|
|
def m2i(self, pkt, x):
|
|
"""Convert machine value to internal value"""
|
|
return x
|
|
def i2m(self, pkt, x):
|
|
"""Convert internal value to machine value"""
|
|
if x is None:
|
|
x = 0
|
|
return x
|
|
def any2i(self, pkt, x):
|
|
"""Try to understand the most input values possible and make an internal value from them"""
|
|
return self.h2i(pkt, x)
|
|
def i2repr(self, pkt, x):
|
|
"""Convert internal value to a nice representation"""
|
|
if x is None:
|
|
x = 0
|
|
return repr(self.i2h(pkt,x))
|
|
def addfield(self, pkt, s, val):
|
|
"""Add an internal value to a string"""
|
|
return s+struct.pack(self.fmt, self.i2m(pkt,val))
|
|
def getfield(self, pkt, s):
|
|
"""Extract an internal value from a string"""
|
|
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)
|
|
def randval(self):
|
|
"""Return a volatile object whose value is both random and suitable for this field"""
|
|
fmtt = self.fmt[-1]
|
|
if fmtt in "BHIQ":
|
|
return {"B":RandByte,"H":RandShort,"I":RandInt, "Q":RandLong}[fmtt]()
|
|
elif fmtt == "s":
|
|
if self.fmt[0] in "0123456789":
|
|
l = int(self.fmt[:-1])
|
|
else:
|
|
l = int(self.fmt[1:-1])
|
|
return RandBin(l)
|
|
else:
|
|
warning("no random class for [%s] (fmt=%s)." % (self.name, self.fmt))
|
|
|
|
|
|
|
|
|
|
class Emph:
|
|
fld = ""
|
|
def __init__(self, fld):
|
|
self.fld = fld
|
|
def __getattr__(self, attr):
|
|
return getattr(self.fld,attr)
|
|
|
|
class ActionField:
|
|
def __init__(self, fld, action_method, **kargs):
|
|
self._fld = fld
|
|
self._action_method = action_method
|
|
self._privdata = kargs
|
|
def any2i(self, pkt, val):
|
|
getattr(pkt, self._action_method)(val, self._fld, **self._privdata)
|
|
return getattr(self._fld, "any2i")(pkt, val)
|
|
def __getattr__(self, attr):
|
|
return getattr(self._fld,attr)
|
|
|
|
|
|
class ConditionalField:
|
|
def __init__(self, fld, fldlst, cond):
|
|
self.fld = fld
|
|
self.fldlst = fldlst
|
|
self.cond = cond
|
|
def _evalcond(self,pkt):
|
|
if type(self.fldlst) is list or type(self.fldlst) is tuple:
|
|
res = map(lambda x,pkt=pkt:getattr(pkt,x), self.fldlst)
|
|
else:
|
|
res = getattr(pkt, self.fldlst)
|
|
return self.cond(res)
|
|
|
|
def getfield(self, pkt, s):
|
|
if self._evalcond(pkt):
|
|
return self.fld.getfield(pkt,s)
|
|
else:
|
|
return s,None
|
|
|
|
def addfield(self, pkt, s, val):
|
|
if self._evalcond(pkt):
|
|
return self.fld.addfield(pkt,s,val)
|
|
else:
|
|
return s
|
|
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):
|
|
if x is None:
|
|
return "\0\0\0\0\0\0"
|
|
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)
|
|
def randval(self):
|
|
return RandMAC()
|
|
|
|
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, IPv6):
|
|
dstip = pkt.payload.dst
|
|
elif isinstance(pkt.payload, IP):
|
|
dstip = pkt.payload.dst
|
|
elif isinstance(pkt.payload, ARP):
|
|
dstip = pkt.payload.pdst
|
|
if isinstance(dstip, Gen):
|
|
dstip = dstip.__iter__().next()
|
|
if dstip is not None:
|
|
if isinstance(pkt.payload, IPv6):
|
|
x = getmacbyip6(dstip)
|
|
else:
|
|
x = getmacbyip(dstip)
|
|
if x is None:
|
|
x = "ff:ff:ff:ff:ff:ff"
|
|
warning("Mac address to reach %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, IPv6):
|
|
dstip = pkt.payload.dst
|
|
elif isinstance(pkt.payload, IP):
|
|
dstip = pkt.payload.dst
|
|
elif isinstance(pkt.payload, ARP):
|
|
dstip = pkt.payload.pdst
|
|
if isinstance(dstip, Gen):
|
|
dstip = dstip.__iter__().next()
|
|
if dstip is not None:
|
|
if isinstance(pkt.payload, IPv6):
|
|
iff,a,nh = conf.route6.route(dstip)
|
|
else:
|
|
iff,a,gw = conf.route.route(dstip)
|
|
try:
|
|
x = get_if_hwaddr(iff)
|
|
except:
|
|
pass
|
|
if x is None:
|
|
x = "00:00:00:00:00:00"
|
|
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):
|
|
dstip = dstip.__iter__().next()
|
|
if dstip is not None:
|
|
iff,a,gw = conf.route.route(dstip)
|
|
try:
|
|
x = get_if_hwaddr(iff)
|
|
except:
|
|
pass
|
|
if x is None:
|
|
x = "00:00:00:00:00:00"
|
|
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 inet_ntoa(x)
|
|
def any2i(self, pkt, x):
|
|
return self.h2i(pkt,x)
|
|
def i2repr(self, pkt, x):
|
|
return self.i2h(pkt, x)
|
|
def randval(self):
|
|
return RandIP()
|
|
|
|
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))[1:4]
|
|
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 SignedIntField(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 LESignedIntField(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, shift=0):
|
|
Field.__init__(self,name,default,fmt)
|
|
self.remain = remain
|
|
self.shift = shift
|
|
def i2len(self, pkt, i):
|
|
return len(i)+self.shift
|
|
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])
|
|
def randval(self):
|
|
return RandBin(RandNum(0,1200))
|
|
|
|
class PacketField(StrField):
|
|
holds_packets=1
|
|
def __init__(self, name, default, cls, remain=0, shift=0):
|
|
StrField.__init__(self, name, default, remain=remain, shift=shift)
|
|
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):
|
|
holds_packets=1
|
|
def __init__(self, name, default, cls, fld, shift=0):
|
|
PacketField.__init__(self, name, default, cls, shift=shift)
|
|
self.fld = fld
|
|
def getfield(self, pkt, s):
|
|
l = getattr(pkt, self.fld)
|
|
l -= self.shift
|
|
i = self.m2i(pkt, s[:l])
|
|
return s[l:],i
|
|
|
|
|
|
class PacketListField(PacketLenField):
|
|
islist = 1
|
|
holds_packets=1
|
|
def do_copy(self, x):
|
|
return map(lambda p:p.copy(), x)
|
|
def getfield(self, pkt, s):
|
|
l = getattr(pkt, self.fld)
|
|
l -= self.shift
|
|
lst = []
|
|
remain = s
|
|
while l>0 and len(remain)>0:
|
|
l -= 1
|
|
p = self.m2i(pkt,remain)
|
|
if Padding in p:
|
|
pad = p[Padding]
|
|
remain = pad.load
|
|
del(pad.underlayer.payload)
|
|
else:
|
|
remain = ""
|
|
lst.append(p)
|
|
return remain,lst
|
|
def addfield(self, pkt, s, val):
|
|
return s+"".join(map(str, val))
|
|
|
|
|
|
class StrFixedLenField(StrField):
|
|
def __init__(self, name, default, length, shift=0):
|
|
StrField.__init__(self, name, default, shift=shift)
|
|
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("%is"%self.length,self.i2m(pkt, val))
|
|
def randval(self):
|
|
return RandBin(self.length)
|
|
|
|
class NetBIOSNameField(StrFixedLenField):
|
|
def __init__(self, name, default, length=31, shift=0):
|
|
StrFixedLenField.__init__(self, name, default, length, shift=shift)
|
|
def i2m(self, pkt, x):
|
|
if x is None:
|
|
x = ""
|
|
x += " "*(self.length/2)
|
|
x = x[:(self.length/2)]
|
|
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, shift=0):
|
|
StrField.__init__(self, name, default, shift=shift)
|
|
self.fld = fld
|
|
def getfield(self, pkt, s):
|
|
l = getattr(pkt, self.fld)
|
|
l -= self.shift
|
|
return s[l:], self.m2i(pkt,s[:l])
|
|
|
|
class FieldListField(Field):
|
|
islist=1
|
|
def __init__(self, name, default, cls, fld, shift=0):
|
|
self.name = name
|
|
self.default = default
|
|
self.cls = cls
|
|
self.fld = fld
|
|
self.shift=shift
|
|
def i2len(self, pkt, val):
|
|
if val is None:
|
|
return self.shift
|
|
else:
|
|
return len(val)+self.shift
|
|
def i2m(self, pkt, val):
|
|
if val is None:
|
|
val = []
|
|
return val
|
|
def addfield(self, pkt, s, val):
|
|
val = self.i2m(pkt, val)
|
|
for v in val:
|
|
s = self.cls.addfield(pkt, s, v)
|
|
return s
|
|
def getfield(self, pkt, s):
|
|
l = getattr(pkt, self.fld)
|
|
# add the shift from the length field
|
|
f = pkt.get_field(self.fld)
|
|
l -= self.shift
|
|
val = []
|
|
for i in range(l):
|
|
s,v = self.cls.getfield(pkt, s)
|
|
val.append(v)
|
|
return s, val
|
|
|
|
class FieldLenField(Field):
|
|
def __init__(self, name, default, fld, fmt = "H"):
|
|
Field.__init__(self, name, default, fmt)
|
|
self.fld = fld
|
|
def i2m(self, pkt, x):
|
|
if x is None:
|
|
f = pkt.get_field(self.fld)
|
|
x = f.i2len(pkt,pkt.getfieldval(self.fld))
|
|
return x
|
|
|
|
# see http://www.iana.org/assignments/ipsec-registry for details
|
|
ISAKMPAttributeTypes= { "Encryption": (1, { "DES-CBC" : 1,
|
|
"IDEA-CBC" : 2,
|
|
"Blowfish-CBC" : 3,
|
|
"RC5-R16-B64-CBC" : 4,
|
|
"3DES-CBC" : 5,
|
|
"CAST-CBC" : 6,
|
|
"AES-CBC" : 7,
|
|
"CAMELLIA-CBC" : 8, }, 0),
|
|
"Hash": (2, { "MD5": 1,
|
|
"SHA": 2,
|
|
"Tiger": 3,
|
|
"SHA2-256": 4,
|
|
"SHA2-384": 5,
|
|
"SHA2-512": 6,}, 0),
|
|
"Authentication":(3, { "PSK": 1,
|
|
"DSS": 2,
|
|
"RSA Sig": 3,
|
|
"RSA Encryption": 4,
|
|
"RSA Encryption Revised": 5,
|
|
"ElGamal Encryption": 6,
|
|
"ElGamal Encryption Revised": 7,
|
|
"ECDSA Sig": 8,
|
|
"HybridInitRSA": 64221,
|
|
"HybridRespRSA": 64222,
|
|
"HybridInitDSS": 64223,
|
|
"HybridRespDSS": 64224,
|
|
"XAUTHInitPreShared": 65001,
|
|
"XAUTHRespPreShared": 65002,
|
|
"XAUTHInitDSS": 65003,
|
|
"XAUTHRespDSS": 65004,
|
|
"XAUTHInitRSA": 65005,
|
|
"XAUTHRespRSA": 65006,
|
|
"XAUTHInitRSAEncryption": 65007,
|
|
"XAUTHRespRSAEncryption": 65008,
|
|
"XAUTHInitRSARevisedEncryption": 65009,
|
|
"XAUTHRespRSARevisedEncryptio": 65010, }, 0),
|
|
"GroupDesc": (4, { "768MODPgr" : 1,
|
|
"1024MODPgr" : 2,
|
|
"EC2Ngr155" : 3,
|
|
"EC2Ngr185" : 4,
|
|
"1536MODPgr" : 5,
|
|
"2048MODPgr" : 14,
|
|
"3072MODPgr" : 15,
|
|
"4096MODPgr" : 16,
|
|
"6144MODPgr" : 17,
|
|
"8192MODPgr" : 18, }, 0),
|
|
"GroupType": (5, {"MODP": 1,
|
|
"ECP": 2,
|
|
"EC2N": 3}, 0),
|
|
"GroupPrime": (6, {}, 1),
|
|
"GroupGenerator1":(7, {}, 1),
|
|
"GroupGenerator2":(8, {}, 1),
|
|
"GroupCurveA": (9, {}, 1),
|
|
"GroupCurveB": (10, {}, 1),
|
|
"LifeType": (11, {"Seconds": 1,
|
|
"Kilobytes": 2, }, 0),
|
|
"LifeDuration": (12, {}, 1),
|
|
"PRF": (13, {}, 0),
|
|
"KeyLength": (14, {}, 0),
|
|
"FieldSize": (15, {}, 0),
|
|
"GroupOrder": (16, {}, 1),
|
|
}
|
|
|
|
# the name 'ISAKMPTransformTypes' is actually a misnomer (since the table
|
|
# holds info for all ISAKMP Attribute types, not just transforms, but we'll
|
|
# keep it for backwards compatibility... for now at least
|
|
ISAKMPTransformTypes = ISAKMPAttributeTypes
|
|
|
|
ISAKMPTransformNum = {}
|
|
for n in ISAKMPTransformTypes:
|
|
val = ISAKMPTransformTypes[n]
|
|
tmp = {}
|
|
for e in val[1]:
|
|
tmp[val[1][e]] = e
|
|
ISAKMPTransformNum[val[0]] = (n,tmp, val[2])
|
|
del(n)
|
|
del(e)
|
|
del(tmp)
|
|
del(val)
|
|
|
|
|
|
class ISAKMPTransformSetField(StrLenField):
|
|
islist=1
|
|
def type2num(self, (typ,val)):
|
|
type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typ, (typ,{},0))
|
|
val = enc_dict.get(val, val)
|
|
s = ""
|
|
if (val & ~0xffff):
|
|
if not tlv:
|
|
warning("%r should not be TLV but is too big => using TLV encoding" % typ)
|
|
n = 0
|
|
while val:
|
|
s = chr(val&0xff)+s
|
|
val >>= 8
|
|
n += 1
|
|
val = n
|
|
else:
|
|
type_val |= 0x8000
|
|
return struct.pack("!HH",type_val, val)+s
|
|
def num2type(self, typ, enc):
|
|
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 "".join(i)
|
|
def m2i(self, pkt, m):
|
|
# I try to ensure that we don't read off the end of our packet based
|
|
# on bad length fields we're provided in the packet. There are still
|
|
# conditions where struct.unpack() may not get enough packet data, but
|
|
# worst case that should result in broken attributes (which would
|
|
# be expected). (wam)
|
|
lst = []
|
|
while len(m) >= 4:
|
|
trans_type, = struct.unpack("!H", m[:2])
|
|
is_tlv = not (trans_type & 0x8000)
|
|
if is_tlv:
|
|
# We should probably check to make sure the attribute type we
|
|
# are looking at is allowed to have a TLV format and issue a
|
|
# warning if we're given an TLV on a basic attribute.
|
|
value_len, = struct.unpack("!H", m[2:4])
|
|
if value_len+4 > len(m):
|
|
warning("Bad length for ISAKMP tranform type=%#6x" % trans_type)
|
|
value = m[4:4+value_len]
|
|
value = reduce(lambda x,y: (x<<8L)|y, struct.unpack("!%s" % ("B"*len(value),), value),0)
|
|
else:
|
|
trans_type &= 0x7fff
|
|
value_len=0
|
|
value, = struct.unpack("!H", m[2:4])
|
|
m=m[4+value_len:]
|
|
lst.append(self.num2type(trans_type, value))
|
|
if len(m) > 0:
|
|
warning("Extra bytes after ISAKMP transform dissection [%r]" % m)
|
|
return lst
|
|
def getfield(self, pkt, s):
|
|
l = getattr(pkt, self.fld)
|
|
l -= self.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])
|
|
def randval(self):
|
|
return RandTermString(RandNum(0,1200),"\x00")
|
|
|
|
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]
|
|
def randval(self):
|
|
return RandTermString(RandNum(0,1200),self.stop)
|
|
|
|
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<<self.size) - 1)
|
|
bitsdone += self.size
|
|
while bitsdone >= 8:
|
|
bitsdone -= 8
|
|
s = s+struct.pack("!B", v >> bitsdone)
|
|
v &= (1L<<bitsdone)-1
|
|
if bitsdone:
|
|
return s,bitsdone,v
|
|
else:
|
|
return s
|
|
def getfield(self, pkt, s):
|
|
if type(s) is tuple:
|
|
s,bn = s
|
|
else:
|
|
bn = 0
|
|
# we don't want to process all the string
|
|
nb_bytes = (self.size+bn-1)/8 + 1
|
|
w = s[:nb_bytes]
|
|
|
|
# split the substring byte by byte
|
|
bytes = struct.unpack('!%dB' % nb_bytes , w)
|
|
|
|
b = 0L
|
|
for c in range(nb_bytes):
|
|
b |= long(bytes[c]) << (nb_bytes-c-1)*8
|
|
|
|
# get rid of high order bits
|
|
b &= (1L << (nb_bytes*8-bn)) - 1
|
|
|
|
# remove low order bits
|
|
b = b >> (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
|
|
def randval(self):
|
|
return RandNum(0,2**self.size-1)
|
|
|
|
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"):
|
|
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
|
|
Field.__init__(self, name, default, fmt)
|
|
def any2i_one(self, pkt, x):
|
|
if type(x) is str:
|
|
x = self.s2i[x]
|
|
return x
|
|
def i2repr_one(self, pkt, x):
|
|
return self.i2s.get(x, repr(x))
|
|
|
|
def any2i(self, pkt, x):
|
|
if type(x) is list:
|
|
return map(lambda z,pkt=pkt:self.any2i_one(pkt,z), x)
|
|
else:
|
|
return self.any2i_one(pkt,x)
|
|
def i2repr(self, pkt, x):
|
|
if type(x) is list:
|
|
return map(lambda z,pkt=pkt:self.i2repr_one(pkt,z), x)
|
|
else:
|
|
return self.i2repr_one(pkt,x)
|
|
|
|
class CharEnumField(EnumField):
|
|
def __init__(self, name, default, enum, fmt = "1s"):
|
|
EnumField.__init__(self, name, default, enum, fmt)
|
|
k = self.i2s.keys()
|
|
if k and len(k[0]) != 1:
|
|
self.i2s,self.s2i = self.s2i,self.i2s
|
|
def any2i_one(self, pkt, x):
|
|
if len(x) != 1:
|
|
x = self.s2i[x]
|
|
return 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_one(self, pkt, x):
|
|
return self.i2s.get(x, hex(x))
|
|
|
|
# Little endian long field
|
|
class LELongField(Field):
|
|
def __init__(self, name, default):
|
|
Field.__init__(self, name, default, "<Q")
|
|
|
|
# Little endian fixed length field
|
|
class LEFieldLenField(FieldLenField):
|
|
def __init__(self, name, default, fld, fmt = "<H"):
|
|
FieldLenField.__init__(self, name, default, fld=fld, fmt=fmt)
|
|
|
|
|
|
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]
|
|
def randval(self):
|
|
return RandBin(RandNum(0,39))
|
|
|
|
|
|
TCPOptions = (
|
|
{ 0 : ("EOL",None),
|
|
1 : ("NOP",None),
|
|
2 : ("MSS","!H"),
|
|
3 : ("WScale","!B"),
|
|
4 : ("SAckOK",None),
|
|
5 : ("SAck","!"),
|
|
8 : ("Timestamp","!II"),
|
|
14 : ("AltChkSum","!BH"),
|
|
15 : ("AltChkSumOpt",None)
|
|
},
|
|
{ "EOL":0,
|
|
"NOP":1,
|
|
"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:
|
|
opt.append(("EOL",None))
|
|
x=x[1:]
|
|
break
|
|
if onum == 1:
|
|
opt.append(("NOP",None))
|
|
x=x[1:]
|
|
continue
|
|
olen = ord(x[1])
|
|
if olen < 2:
|
|
warning("Malformed TCP option (announced length is %i)" % olen)
|
|
olen = 2
|
|
oval = x[2:olen]
|
|
if TCPOptions[0].has_key(onum):
|
|
oname, ofmt = TCPOptions[0][onum]
|
|
if onum == 5: #SAck
|
|
ofmt += "%iI" % (len(oval)/4)
|
|
if ofmt and struct.calcsize(ofmt) == len(oval):
|
|
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 oname == "EOL":
|
|
opt += "\x00"
|
|
continue
|
|
elif TCPOptions[1].has_key(oname):
|
|
onum = TCPOptions[1][oname]
|
|
ofmt = TCPOptions[0][onum][1]
|
|
if onum == 5: #SAck
|
|
ofmt += "%iI" % len(oval)
|
|
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))
|
|
def randval(self):
|
|
return [] # XXX
|
|
|
|
|
|
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):
|
|
holds_packets=1
|
|
def __init__(self, name, default, rr):
|
|
ShortField.__init__(self, name, default)
|
|
self.rr = rr
|
|
def _countRR(self, pkt):
|
|
x = getattr(pkt,self.rr)
|
|
i = 0
|
|
while isinstance(x, DNSRR) or isinstance(x, DNSQR):
|
|
x = x.payload
|
|
i += 1
|
|
return i
|
|
|
|
def i2m(self, pkt, x):
|
|
if x is None:
|
|
x = self._countRR(pkt)
|
|
return x
|
|
def i2h(self, pkt, x):
|
|
if x is None:
|
|
x = self._countRR(pkt)
|
|
return x
|
|
|
|
|
|
def DNSgetstr(s,p):
|
|
name = ""
|
|
q = 0
|
|
jpath = [p]
|
|
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
|
|
if p >= len(s):
|
|
warning("DNS incomplete jump token at (ofs=%i)" % p)
|
|
break
|
|
p = ((l & 0x3f) << 8) + ord(s[p]) - 12
|
|
if p in jpath:
|
|
warning("DNS decompression loop detected")
|
|
break
|
|
jpath.append(p)
|
|
continue
|
|
elif l > 0:
|
|
name += s[p:p+l]+"."
|
|
p += l
|
|
continue
|
|
break
|
|
if q:
|
|
p = q
|
|
return name,p
|
|
|
|
|
|
class DNSRRField(StrField):
|
|
holds_packets=1
|
|
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)
|
|
if c > len(s):
|
|
warning("wrong value: DNS.%s=%i" % (self.countfld,c))
|
|
return s,""
|
|
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):
|
|
holds_packets=1
|
|
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):
|
|
family = None
|
|
if pkt.type == 1:
|
|
family = socket.AF_INET
|
|
elif pkt.type == 28:
|
|
family = socket.AF_INET6
|
|
elif pkt.type == 12:
|
|
s = DNSgetstr(s, 0)[0]
|
|
if family is not None:
|
|
s = inet_ntop(family, s)
|
|
return s
|
|
def i2m(self, pkt, s):
|
|
if pkt.type == 1:
|
|
if s:
|
|
s = inet_aton(s)
|
|
elif pkt.type == 28:
|
|
if s:
|
|
s = inet_pton(socket.AF_INET6, 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
|
|
|
|
class Dot11SCField(LEShortField):
|
|
def is_applicable(self, pkt):
|
|
return pkt.type != 1 # control frame
|
|
def addfield(self, pkt, s, val):
|
|
if self.is_applicable(pkt):
|
|
return LEShortField.addfield(self, pkt, s, val)
|
|
else:
|
|
return s
|
|
def getfield(self, pkt, s):
|
|
if self.is_applicable(pkt):
|
|
return LEShortField.getfield(self, pkt, s)
|
|
else:
|
|
return s,None
|
|
|
|
###########################
|
|
## Packet abstract class ##
|
|
###########################
|
|
|
|
|
|
class Packet(Gen):
|
|
name=None
|
|
|
|
fields_desc = []
|
|
|
|
aliastypes = []
|
|
overload_fields = {}
|
|
|
|
underlayer = None
|
|
|
|
payload_guess = []
|
|
initialized = 0
|
|
show_indent=1
|
|
|
|
def from_hexcap(cls):
|
|
return cls(import_hexcap())
|
|
from_hexcap = classmethod(from_hexcap)
|
|
|
|
|
|
def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields):
|
|
self.time = time.time()
|
|
if self.name is None:
|
|
self.name = self.__class__.__name__
|
|
self.aliastypes = [ self.__class__ ] + self.aliastypes
|
|
self.default_fields = {}
|
|
self.overloaded_fields = {}
|
|
self.fields={}
|
|
self.fieldtype={}
|
|
self.packetfields=[]
|
|
self.__dict__["payload"] = NoPayload()
|
|
self.init_fields()
|
|
self.underlayer = _underlayer
|
|
self.initialized = 1
|
|
if _pkt:
|
|
self.dissect(_pkt)
|
|
if not _internal:
|
|
self.dissection_done(self)
|
|
for f in fields.keys():
|
|
self.fields[f] = self.fieldtype[f].any2i(self,fields[f])
|
|
if type(post_transform) is list:
|
|
self.post_transforms = post_transform
|
|
elif post_transform is None:
|
|
self.post_transforms = []
|
|
else:
|
|
self.post_transforms = [post_transform]
|
|
|
|
def init_fields(self):
|
|
self.do_init_fields(self.fields_desc)
|
|
|
|
def do_init_fields(self, flist):
|
|
for f in flist:
|
|
self.default_fields[f] = f.default
|
|
self.fieldtype[f] = f
|
|
if f.holds_packets:
|
|
self.packetfields.append(f)
|
|
|
|
def dissection_done(self,pkt):
|
|
"""DEV: will be called after a dissection is completed"""
|
|
self.post_dissection(pkt)
|
|
self.payload.dissection_done(pkt)
|
|
|
|
def post_dissection(self, pkt):
|
|
"""DEV: is called after the dissection of the whole packet"""
|
|
pass
|
|
|
|
def get_field(self, fld):
|
|
"""DEV: returns the field instance from the name of the field"""
|
|
return self.fields_desc[self.fields_desc.index(fld)]
|
|
|
|
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):
|
|
"""Returns a deep copy of the instance."""
|
|
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.post_transforms=self.post_transforms[:]
|
|
clone.__dict__["payload"] = self.payload.copy()
|
|
clone.payload.add_underlayer(clone)
|
|
return clone
|
|
|
|
def getfieldval(self, attr):
|
|
for f in self.fields, self.overloaded_fields, self.default_fields:
|
|
if f.has_key(attr):
|
|
return f[attr]
|
|
return self.payload.getfieldval(attr)
|
|
|
|
def __getattr__(self, attr):
|
|
if self.initialized:
|
|
v = self.getfieldval(attr)
|
|
fld = self.fieldtype.get(attr,None)
|
|
if fld is not None:
|
|
return fld.i2h(self, v)
|
|
return v
|
|
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 = ""
|
|
ct = conf.color_theme
|
|
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 = ct.emph_field_name
|
|
vcol = ct.emph_field_value
|
|
else:
|
|
ncol = ct.field_name
|
|
vcol = ct.field_value
|
|
|
|
|
|
s += " %s%s%s" % (ncol(f.name),
|
|
ct.punct("="),
|
|
vcol(val))
|
|
return "%s%s %s %s%s%s"% (ct.punct("<"),
|
|
ct.layer_name(self.__class__.__name__),
|
|
s,
|
|
ct.punct("|"),
|
|
repr(self.payload),
|
|
ct.punct(">"))
|
|
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 __mul__(self, other):
|
|
if type(other) is int:
|
|
return [self]*other
|
|
else:
|
|
raise TypeError
|
|
def __rmul__(self,other):
|
|
return self.__mul__(other)
|
|
|
|
def __nonzero__(self):
|
|
return True
|
|
def __len__(self):
|
|
return len(self.__str__())
|
|
def do_build(self):
|
|
p=""
|
|
for f in self.fields_desc:
|
|
p = f.addfield(self, p, self.getfieldval(f))
|
|
return p
|
|
|
|
def post_build(self, pkt, pay):
|
|
"""DEV: called right after the current layer is build."""
|
|
return pkt+pay
|
|
|
|
def build_payload(self):
|
|
return self.payload.build(internal=1)
|
|
|
|
def build(self,internal=0):
|
|
pkt = self.do_build()
|
|
for t in self.post_transforms:
|
|
pkt = t(pkt)
|
|
pay = self.build_payload()
|
|
try:
|
|
p = self.post_build(pkt,pay)
|
|
except TypeError:
|
|
log_runtime.error("API changed! post_build() now takes 2 arguments. Compatibility is only assured for a short transition time")
|
|
p = self.post_build(pkt+pay)
|
|
if not internal:
|
|
pkt = self
|
|
while pkt.haslayer(Padding):
|
|
pkt = pkt.getlayer(Padding)
|
|
p += pkt.load
|
|
pkt = pkt.payload
|
|
return p
|
|
|
|
def do_build_ps(self):
|
|
p=""
|
|
pl = []
|
|
q=""
|
|
for f in self.fields_desc:
|
|
p = f.addfield(self, p, self.getfieldval(f) )
|
|
if type(p) is str:
|
|
r = p[len(q):]
|
|
q = p
|
|
else:
|
|
r = ""
|
|
pl.append( (f, f.i2repr(self,self.getfieldval(f)), r) )
|
|
|
|
pkt,lst = self.payload.build_ps(internal=1)
|
|
p += pkt
|
|
lst.append( (self, pl) )
|
|
|
|
return p,lst
|
|
|
|
def build_ps(self,internal=0):
|
|
p,lst = self.do_build_ps()
|
|
# if not internal:
|
|
# pkt = self
|
|
# while pkt.haslayer(Padding):
|
|
# pkt = pkt.getlayer(Padding)
|
|
# lst.append( (pkt, [ ("loakjkjd", pkt.load, pkt.load) ] ) )
|
|
# p += pkt.load
|
|
# pkt = pkt.payload
|
|
return p,lst
|
|
|
|
|
|
def psdump(self, filename=None, **kargs):
|
|
"""psdump(filename=None, layer_shift=0, rebuild=1)
|
|
Creates an EPS file describing a packet. If filename is not provided a temporary file is created and gs is called."""
|
|
canvas = self.canvas_dump(**kargs)
|
|
if filename is None:
|
|
fname = "/tmp/scapy.%i"%os.getpid()
|
|
canvas.writeEPSfile(fname)
|
|
os.system("%s '%s.eps' &" % (conf.prog.psreader,fname))
|
|
else:
|
|
canvas.writeEPSfile(filename)
|
|
|
|
def pdfdump(self, filename=None, **kargs):
|
|
"""pdfdump(filename=None, layer_shift=0, rebuild=1)
|
|
Creates a PDF file describing a packet. If filename is not provided a temporary file is created and xpdf is called."""
|
|
canvas = self.canvas_dump(**kargs)
|
|
if filename is None:
|
|
fname = "/tmp/scapy.%i"%os.getpid()
|
|
canvas.writePDFfile(fname)
|
|
os.system("%s '%s.pdf' &" % (conf.prog.pdfreader,fname))
|
|
else:
|
|
canvas.writePDFfile(filename)
|
|
|
|
|
|
def canvas_dump(self, layer_shift=0, rebuild=1):
|
|
canvas = pyx.canvas.canvas()
|
|
if rebuild:
|
|
p,t = self.__class__(str(self)).build_ps()
|
|
else:
|
|
p,t = self.build_ps()
|
|
YTXT=len(t)
|
|
for n,l in t:
|
|
YTXT += len(l)
|
|
YTXT = float(YTXT)
|
|
YDUMP=YTXT
|
|
|
|
XSTART = 1
|
|
XDSTART = 10
|
|
y = 0.0
|
|
yd = 0.0
|
|
xd = 0
|
|
XMUL= 0.55
|
|
YMUL = 0.4
|
|
|
|
backcolor=colgen(0.6, 0.8, 1.0, trans=pyx.color.rgb)
|
|
forecolor=colgen(0.2, 0.5, 0.8, trans=pyx.color.rgb)
|
|
# backcolor=makecol(0.376, 0.729, 0.525, 1.0)
|
|
|
|
|
|
def hexstr(x):
|
|
s = []
|
|
for c in x:
|
|
s.append("%02x" % ord(c))
|
|
return " ".join(s)
|
|
|
|
|
|
def make_dump_txt(x,y,txt):
|
|
return pyx.text.text(XDSTART+x*XMUL, (YDUMP-y)*YMUL, r"\tt{%s}"%hexstr(txt), [pyx.text.size.Large])
|
|
|
|
def make_box(o):
|
|
return pyx.box.rect(o.left(), o.bottom(), o.width(), o.height(), relcenter=(0.5,0.5))
|
|
|
|
def make_frame(lst):
|
|
if len(lst) == 1:
|
|
b = lst[0].bbox()
|
|
b.enlarge(pyx.unit.u_pt)
|
|
return b.path()
|
|
else:
|
|
fb = lst[0].bbox()
|
|
fb.enlarge(pyx.unit.u_pt)
|
|
lb = lst[-1].bbox()
|
|
lb.enlarge(pyx.unit.u_pt)
|
|
if len(lst) == 2 and fb.left() > lb.right():
|
|
return pyx.path.path(pyx.path.moveto(fb.right(), fb.top()),
|
|
pyx.path.lineto(fb.left(), fb.top()),
|
|
pyx.path.lineto(fb.left(), fb.bottom()),
|
|
pyx.path.lineto(fb.right(), fb.bottom()),
|
|
pyx.path.moveto(lb.left(), lb.top()),
|
|
pyx.path.lineto(lb.right(), lb.top()),
|
|
pyx.path.lineto(lb.right(), lb.bottom()),
|
|
pyx.path.lineto(lb.left(), lb.bottom()))
|
|
else:
|
|
# XXX
|
|
gb = lst[1].bbox()
|
|
if gb != lb:
|
|
gb.enlarge(pyx.unit.u_pt)
|
|
kb = lst[-2].bbox()
|
|
if kb != gb and kb != lb:
|
|
kb.enlarge(pyx.unit.u_pt)
|
|
return pyx.path.path(pyx.path.moveto(fb.left(), fb.top()),
|
|
pyx.path.lineto(fb.right(), fb.top()),
|
|
pyx.path.lineto(fb.right(), kb.bottom()),
|
|
pyx.path.lineto(lb.right(), kb.bottom()),
|
|
pyx.path.lineto(lb.right(), lb.bottom()),
|
|
pyx.path.lineto(lb.left(), lb.bottom()),
|
|
pyx.path.lineto(lb.left(), gb.top()),
|
|
pyx.path.lineto(fb.left(), gb.top()),
|
|
pyx.path.closepath(),)
|
|
|
|
|
|
def make_dump(s, shift=0, y=0, col=None, bkcol=None, larg=16):
|
|
c = pyx.canvas.canvas()
|
|
tlist = []
|
|
while s:
|
|
dmp,s = s[:larg-shift],s[larg-shift:]
|
|
txt = make_dump_txt(shift, y, dmp)
|
|
tlist.append(txt)
|
|
shift += len(dmp)
|
|
if shift >= 16:
|
|
shift = 0
|
|
y += 1
|
|
if col is None:
|
|
col = pyx.color.rgb.red
|
|
if bkcol is None:
|
|
col = pyx.color.rgb.white
|
|
c.stroke(make_frame(tlist),[col,pyx.deco.filled([bkcol]),pyx.style.linewidth.Thick])
|
|
for txt in tlist:
|
|
c.insert(txt)
|
|
return c, tlist[-1].bbox(), shift, y
|
|
|
|
|
|
last_shift,last_y=0,0.0
|
|
while t:
|
|
bkcol = backcolor.next()
|
|
proto,fields = t.pop()
|
|
y += 0.5
|
|
pt = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % proto.name, [ pyx.text.size.Large])
|
|
y += 1
|
|
ptbb=pt.bbox()
|
|
ptbb.enlarge(pyx.unit.u_pt*2)
|
|
canvas.stroke(ptbb.path(),[pyx.color.rgb.black, pyx.deco.filled([bkcol])])
|
|
canvas.insert(pt)
|
|
for fname, fval, fdump in fields:
|
|
col = forecolor.next()
|
|
ft = pyx.text.text(XSTART, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fname.name))
|
|
if fval is not None:
|
|
if len(fval) > 18:
|
|
fval = fval[:18]+"[...]"
|
|
else:
|
|
fval=""
|
|
vt = pyx.text.text(XSTART+3, (YTXT-y)*YMUL, r"\font\cmssfont=cmss10\cmssfont{%s}" % tex_escape(fval))
|
|
y += 1.0
|
|
if fdump:
|
|
dt,target,last_shift,last_y = make_dump(fdump, last_shift, last_y, col, bkcol)
|
|
|
|
dtb = dt.bbox()
|
|
dtb=target
|
|
vtb = vt.bbox()
|
|
bxvt = make_box(vtb)
|
|
bxdt = make_box(dtb)
|
|
dtb.enlarge(pyx.unit.u_pt)
|
|
try:
|
|
if yd < 0:
|
|
cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=-90)
|
|
else:
|
|
cnx = pyx.connector.curve(bxvt,bxdt,absangle1=0, absangle2=90)
|
|
except:
|
|
pass
|
|
else:
|
|
canvas.stroke(cnx,[pyx.style.linewidth.thin,pyx.deco.earrow.small,col])
|
|
|
|
canvas.insert(dt)
|
|
|
|
canvas.insert(ft)
|
|
canvas.insert(vt)
|
|
last_y += layer_shift
|
|
|
|
return canvas
|
|
|
|
|
|
|
|
def extract_padding(self, s):
|
|
"""DEV: to be overloaded to extract current layer's padding. Return a couple of strings (actual layer, padding)"""
|
|
return s,None
|
|
|
|
def post_dissect(self, s):
|
|
"""DEV: is called right after the current layer has been dissected"""
|
|
return s
|
|
|
|
def pre_dissect(self, s):
|
|
"""DEV: is called right before the current layer is dissected"""
|
|
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
|
|
|
|
return s
|
|
|
|
def do_dissect_payload(self, s):
|
|
if s:
|
|
cls = self.guess_payload_class(s)
|
|
try:
|
|
p = cls(s, _internal=1, _underlayer=self)
|
|
except:
|
|
if conf.debug_dissector:
|
|
if isinstance(cls,type) and issubclass(cls,Packet):
|
|
log_runtime.error("%s dissector failed" % cls.name)
|
|
else:
|
|
log_runtime.error("%s.guess_payload_class() returned [%s]" % (self.__class__.__name__,repr(cls)))
|
|
if cls is not None:
|
|
raise
|
|
p = Raw(s, _internal=1, _underlayer=self)
|
|
self.add_payload(p)
|
|
|
|
def dissect(self, s):
|
|
s = self.pre_dissect(s)
|
|
|
|
s = self.do_dissect(s)
|
|
|
|
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 guess_payload_class(self, payload):
|
|
"""DEV: Guesses the next payload class from layer bonds. Can be overloaded to use a different mechanism."""
|
|
for t in self.aliastypes:
|
|
for fval, cls in t.payload_guess:
|
|
ok = 1
|
|
for k in fval.keys():
|
|
if not hasattr(self, k) or fval[k] != self.getfieldval(k):
|
|
ok = 0
|
|
break
|
|
if ok:
|
|
return cls
|
|
return self.default_payload_class(payload)
|
|
|
|
def default_payload_class(self, payload):
|
|
"""DEV: Returns the default payload class if nothing has been found by the guess_payload_class() method."""
|
|
return Raw
|
|
|
|
def hide_defaults(self):
|
|
"""Removes fields' values that are the same as default values."""
|
|
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.getfieldval(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], VolatileValue):
|
|
done2[k] = done2[k]._fix()
|
|
pkt = self.__class__()
|
|
pkt.fields = done2
|
|
pkt.time = self.time
|
|
pkt.underlayer = self.underlayer
|
|
pkt.overload_fields = self.overload_fields.copy()
|
|
pkt.post_transforms = self.post_transforms
|
|
if payl is None:
|
|
yield pkt
|
|
else:
|
|
yield pkt/payl
|
|
todo = map(lambda (x,y):x, filter(lambda (x,y):isinstance(y,VolatileValue), self.default_fields.items()))
|
|
todo += map(lambda (x,y):x, filter(lambda (x,y):isinstance(y,VolatileValue), self.overloaded_fields.items()))
|
|
todo += self.fields.keys()
|
|
return loop(map(lambda x:str(x), todo), {})
|
|
|
|
def __gt__(self, other):
|
|
"""True if other is an answer from self (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):
|
|
"""True if self is an answer from other (other ==> self)."""
|
|
if isinstance(other, Packet):
|
|
return self.answers(other)
|
|
elif type(other) is str:
|
|
return 1
|
|
else:
|
|
raise TypeError((self, other))
|
|
|
|
def __eq__(self, other):
|
|
if not isinstance(other, self.__class__):
|
|
return False
|
|
for f in self.fields_desc:
|
|
if f not in other.fields_desc:
|
|
return False
|
|
if self.getfieldval(f.name) != other.getfieldval(f.name):
|
|
return False
|
|
return self.payload == other.payload
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
|
|
def hashret(self):
|
|
"""DEV: returns a string that has the same value for a request and its answer."""
|
|
return self.payload.hashret()
|
|
def answers(self, other):
|
|
"""DEV: true if self is an answer from other"""
|
|
if other.__class__ == self.__class__:
|
|
return self.payload.answers(other.payload)
|
|
return 0
|
|
|
|
def haslayer(self, cls):
|
|
"""true if self has a layer that is an instance of cls. Superseded by "cls in self" syntax."""
|
|
if self.__class__ == cls or self.__class__.__name__ == cls:
|
|
return 1
|
|
for f in self.packetfields:
|
|
fvalue_gen = self.getfieldval(f)
|
|
if fvalue_gen is None:
|
|
continue
|
|
if not f.islist:
|
|
fvalue_gen = SetGen(fvalue_gen,_iterpacket=0)
|
|
for fvalue in fvalue_gen:
|
|
if isinstance(fvalue, Packet):
|
|
ret = fvalue.haslayer(cls)
|
|
if ret:
|
|
return ret
|
|
return self.payload.haslayer(cls)
|
|
def getlayer(self, cls, nb=1, _track=None):
|
|
"""Return the nb^th layer that is an instance of cls."""
|
|
if type(cls) is str and "." in cls:
|
|
ccls,fld = cls.split(".",1)
|
|
else:
|
|
ccls,fld = cls,None
|
|
if self.__class__ == cls or self.__class__.name == ccls:
|
|
if nb == 1:
|
|
if fld is None:
|
|
return self
|
|
else:
|
|
return self.getfieldval(fld)
|
|
else:
|
|
nb -=1
|
|
for f in self.packetfields:
|
|
fvalue_gen = self.getfieldval(f)
|
|
if fvalue_gen is None:
|
|
continue
|
|
if not f.islist:
|
|
fvalue_gen = SetGen(fvalue_gen,_iterpacket=0)
|
|
for fvalue in fvalue_gen:
|
|
if isinstance(fvalue, Packet):
|
|
track=[]
|
|
ret = fvalue.getlayer(cls, nb, _track=track)
|
|
if ret is not None:
|
|
return ret
|
|
nb = track[0]
|
|
return self.payload.getlayer(cls,nb,_track=_track)
|
|
|
|
def __getitem__(self, cls):
|
|
if type(cls) is slice:
|
|
if cls.stop:
|
|
ret = self.getlayer(cls.start, cls.stop)
|
|
else:
|
|
ret = self.getlayer(cls.start)
|
|
if ret is None and cls.step is not None:
|
|
ret = cls.step
|
|
return ret
|
|
else:
|
|
return self.getlayer(cls)
|
|
|
|
def __contains__(self, cls):
|
|
""""cls in self" returns true if self has a layer which is an instance of cls."""
|
|
return self.haslayer(cls)
|
|
|
|
|
|
|
|
def display(self,*args,**kargs): # Deprecated. Use show()
|
|
"""Deprecated. Use show() method."""
|
|
self.show(*args,**kargs)
|
|
def show(self, indent=3, lvl="", label_lvl=""):
|
|
"""Prints a hierarchical view of the packet. "indent" gives the size of indentation for each layer."""
|
|
ct = conf.color_theme
|
|
print "%s%s %s %s" % (label_lvl,
|
|
ct.punct("###["),
|
|
ct.layer_name(self.name),
|
|
ct.punct("]###"))
|
|
for f in self.fields_desc:
|
|
if isinstance(f, Emph):
|
|
ncol = ct.emph_field_name
|
|
vcol = ct.emph_field_value
|
|
else:
|
|
ncol = ct.field_name
|
|
vcol = ct.field_value
|
|
fvalue = self.__getattr__(f)
|
|
if isinstance(fvalue, Packet) or (f.islist and f.holds_packets):
|
|
print "%s \\%-10s\\" % (label_lvl+lvl, ncol(f.name))
|
|
fvalue_gen = SetGen(self.__getattr__(f),_iterpacket=0)
|
|
for fvalue in fvalue_gen:
|
|
fvalue.show(indent=indent, label_lvl=label_lvl+lvl+" |")
|
|
else:
|
|
print "%s %-10s%s %s" % (label_lvl+lvl,
|
|
ncol(f.name),
|
|
ct.punct("="),
|
|
vcol(f.i2repr(self,fvalue)))
|
|
self.payload.show(indent=indent, lvl=lvl+(" "*indent*self.show_indent), label_lvl=label_lvl)
|
|
def show2(self):
|
|
"""Prints a hierarchical view of an assembled version of the packet, so that automatic fields are calculated (checksums, etc.)"""
|
|
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(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
|
|
if "." in clsfld:
|
|
cls,fld = clsfld.split(".")
|
|
else:
|
|
cls = self.__class__.__name__
|
|
fld = clsfld
|
|
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):
|
|
"""DEV: can be overloaded to return a string that summarizes the layer.
|
|
Only one mysummary() is used in a whole packet summary: the one of the upper layer,
|
|
except if a mysummary() also returns (as a couple) a list of layers whose
|
|
mysummary() must be called if they are present."""
|
|
return ""
|
|
|
|
def summary(self, intern=0):
|
|
"""Prints a one line summary of a packet."""
|
|
found,s,needed = self.payload.summary(intern=1)
|
|
if s:
|
|
s = " / "+s
|
|
ret = ""
|
|
if not found or self.__class__ in needed:
|
|
ret = self.mysummary()
|
|
if type(ret) is tuple:
|
|
ret,n = ret
|
|
needed += n
|
|
if ret or needed:
|
|
found = 1
|
|
if not ret:
|
|
ret = self.__class__.__name__
|
|
ret = "%s%s" % (ret,s)
|
|
if intern:
|
|
return found,ret,needed
|
|
else:
|
|
return ret
|
|
|
|
def lastlayer(self,layer=None):
|
|
"""Returns the uppest layer of the packet"""
|
|
return self.payload.lastlayer(self)
|
|
|
|
def decode_payload_as(self,cls):
|
|
"""Reassembles the payload and decode it using another packet class"""
|
|
s = str(self.payload)
|
|
self.payload = cls(s)
|
|
|
|
def libnet(self):
|
|
"""Not ready yet. Should give the necessary C code that interfaces with libnet to recreate the packet"""
|
|
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 ");"
|
|
def command(self):
|
|
"""Returns a string representing the command you have to type to obtain the same packet"""
|
|
f = []
|
|
for fn,fv in self.fields.items():
|
|
if isinstance(fv, Packet):
|
|
fv = fv.command()
|
|
else:
|
|
fv = repr(fv)
|
|
f.append("%s=%s" % (fn, fv))
|
|
c = "%s(%s)" % (self.__class__.__name__, ", ".join(f))
|
|
pc = self.payload.command()
|
|
if pc:
|
|
c += "/"+pc
|
|
return c
|
|
|
|
|
|
|
|
|
|
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 dissection_done(self,pkt):
|
|
return
|
|
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 __nonzero__(self):
|
|
return False
|
|
def build(self, internal=0):
|
|
return ""
|
|
def build_ps(self, internal=0):
|
|
return "",[]
|
|
def getfieldval(self, attr):
|
|
raise AttributeError(attr)
|
|
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 __eq__(self, other):
|
|
if isinstance(other, NoPayload):
|
|
return True
|
|
return False
|
|
def hashret(self):
|
|
return ""
|
|
def answers(self, other):
|
|
return isinstance(other, NoPayload) or isinstance(other, Padding)
|
|
def haslayer(self, cls):
|
|
return 0
|
|
def getlayer(self, cls, nb=1, _track=None):
|
|
if _track is not None:
|
|
_track.append(nb)
|
|
return None
|
|
def show(self, indent=3, lvl="", label_lvl=""):
|
|
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
|
|
def command(self):
|
|
return ""
|
|
|
|
|
|
####################
|
|
## 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)
|
|
|
|
# Metaclass
|
|
class NewDefaultValues(type):
|
|
def __new__(cls, name, bases, dct):
|
|
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")
|
|
|
|
new_fields = []
|
|
for f in fields:
|
|
if f in dct:
|
|
f = f.copy()
|
|
f.default = dct[f]
|
|
del(dct[f])
|
|
new_fields.append(f)
|
|
dct["fields_desc"] = new_fields
|
|
return super(NewDefaultValues, 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("%src% > %dst% (%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, pay):
|
|
p += pay
|
|
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 "802.3 %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 default_payload_class(self, pay):
|
|
if self.type <= 1500:
|
|
return LLC
|
|
return Raw
|
|
def extract_padding(self,s):
|
|
if self.type <= 1500:
|
|
return s[:self.type],s[self.type:]
|
|
return s,None
|
|
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),
|
|
ShortField("len",None),
|
|
ConditionalField(ByteEnumField("type",0, {1:"ID",4:"MD5"}), "code", lambda x:x not in [EAP.SUCCESS, EAP.FAILURE])
|
|
|
|
]
|
|
|
|
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 post_build(self, p, pay):
|
|
if self.len is None:
|
|
l = len(p)+len(pay)
|
|
p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:]
|
|
return p+pay
|
|
|
|
|
|
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(self, other):
|
|
if isinstance(other,ARP):
|
|
if ( (self.op == self.is_at) and
|
|
(other.op == self.who_has) and
|
|
(self.psrc == other.pdst) ):
|
|
return 1
|
|
return 0
|
|
def extract_padding(self, s):
|
|
return "",s
|
|
def mysummary(self):
|
|
if self.op == self.is_at:
|
|
return "ARP is at %s says %s" % (self.hwsrc, self.psrc)
|
|
elif self.op == self.who_has:
|
|
return "ARP who has %s says %s" % (self.pdst, self.psrc)
|
|
else:
|
|
return "ARP %ARP.op% %ARP.psrc% > %ARP.pdst%"
|
|
|
|
|
|
class IP(Packet, IPTools):
|
|
name = "IP"
|
|
fields_desc = [ BitField("version" , 4 , 4),
|
|
BitField("ihl", None, 4),
|
|
XByteField("tos", 0),
|
|
ShortField("len", None),
|
|
ShortField("id", 1),
|
|
FlagsField("flags", 0, 3, ["MF","DF","evil"]),
|
|
BitField("frag", 0, 13),
|
|
ByteField("ttl", 64),
|
|
ByteEnumField("proto", 0, IP_PROTOS),
|
|
XShortField("chksum", None),
|
|
#IPField("src", "127.0.0.1"),
|
|
Emph(SourceIPField("src","dst")),
|
|
Emph(IPField("dst", "127.0.0.1")),
|
|
IPoptionsField("options", "") ]
|
|
def post_build(self, p, pay):
|
|
ihl = self.ihl
|
|
if ihl is None:
|
|
ihl = len(p)/4
|
|
p = chr((self.version<<4) | ihl&0x0f)+p[1:]
|
|
if self.len is None:
|
|
l = len(p)+len(pay)
|
|
p = p[:2]+struct.pack("!H", l)+p[4:]
|
|
if self.chksum is None:
|
|
ck = checksum(p)
|
|
p = p[:10]+chr(ck>>8)+chr(ck&0xff)+p[12:]
|
|
return p+pay
|
|
|
|
def extract_padding(self, s):
|
|
l = self.len - (self.ihl << 2)
|
|
return s[:l],s[l:]
|
|
|
|
def send(self, s, slp=0):
|
|
for p in self:
|
|
try:
|
|
s.sendto(str(p), (p.dst,0))
|
|
except socket.error, msg:
|
|
log_runtime.error(msg)
|
|
if slp:
|
|
time.sleep(slp)
|
|
def hashret(self):
|
|
if ( (self.proto == socket.IPPROTO_ICMP)
|
|
and (isinstance(self.payload, ICMP))
|
|
and (self.payload.type in [3,4,5,11,12]) ):
|
|
return self.payload.payload.hashret()
|
|
else:
|
|
if conf.checkIPsrc and conf.checkIPaddr:
|
|
return strxor(inet_aton(self.src),inet_aton(self.dst))+struct.pack("B",self.proto)+self.payload.hashret()
|
|
else:
|
|
return struct.pack("B", self.proto)+self.payload.hashret()
|
|
def answers(self, other):
|
|
if not isinstance(other,IP):
|
|
return 0
|
|
if conf.checkIPaddr and (self.dst != other.src):
|
|
return 0
|
|
if ( (self.proto == socket.IPPROTO_ICMP) and
|
|
(isinstance(self.payload, ICMP)) and
|
|
(self.payload.type in [3,4,5,11,12]) ):
|
|
# ICMP error message
|
|
return self.payload.payload.answers(other)
|
|
|
|
else:
|
|
if ( (conf.checkIPaddr and (self.src != other.dst)) or
|
|
(self.proto != other.proto) ):
|
|
return 0
|
|
return self.payload.answers(other.payload)
|
|
def mysummary(self):
|
|
s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%")
|
|
if self.frag:
|
|
s += " frag:%i" % self.frag
|
|
return s
|
|
|
|
|
|
|
|
class TCP(Packet):
|
|
name = "TCP"
|
|
fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES),
|
|
ShortEnumField("dport", 80, TCP_SERVICES),
|
|
IntField("seq", 0),
|
|
IntField("ack", 0),
|
|
BitField("dataofs", None, 4),
|
|
BitField("reserved", 0, 4),
|
|
FlagsField("flags", 0x2, 8, "FSRPAUEC"),
|
|
ShortField("window", 8192),
|
|
XShortField("chksum", None),
|
|
ShortField("urgptr", 0),
|
|
TCPOptionsField("options", {}) ]
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
dataofs = self.dataofs
|
|
if dataofs is None:
|
|
dataofs = 5+((len(self.fieldtype["options"].i2m(self,self.options))+3)/4)
|
|
p = p[:12]+chr((dataofs << 4) | ord(p[12])&0x0f)+p[13:]
|
|
if self.chksum is None:
|
|
if isinstance(self.underlayer, IP):
|
|
psdhdr = struct.pack("!4s4sHH",
|
|
inet_aton(self.underlayer.src),
|
|
inet_aton(self.underlayer.dst),
|
|
self.underlayer.proto,
|
|
len(p))
|
|
ck=checksum(psdhdr+p)
|
|
p = p[:16]+struct.pack("!H", ck)+p[18:]
|
|
elif isinstance(self.underlayer, IPv6) or isinstance(self.underlayer, _IPv6OptionHeader):
|
|
ck = in6_chksum(socket.IPPROTO_TCP, self.underlayer, p)
|
|
p = p[:16]+struct.pack("!H", ck)+p[18:]
|
|
else:
|
|
warning("No IP underlayer to compute checksum. Leaving null.")
|
|
return p
|
|
def hashret(self):
|
|
if conf.checkIPsrc:
|
|
return struct.pack("H",self.sport ^ self.dport)+self.payload.hashret()
|
|
else:
|
|
return self.payload.hashret()
|
|
def answers(self, other):
|
|
if not isinstance(other, TCP):
|
|
return 0
|
|
if conf.checkIPsrc:
|
|
if not ((self.sport == other.dport) and
|
|
(self.dport == other.sport)):
|
|
return 0
|
|
if (abs(other.seq-self.ack) > 2+len(other.payload)):
|
|
return 0
|
|
return 1
|
|
def mysummary(self):
|
|
if isinstance(self.underlayer, IP):
|
|
return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%")
|
|
elif isinstance(self.underlayer, IPv6):
|
|
return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%")
|
|
else:
|
|
return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%")
|
|
|
|
class UDP(Packet):
|
|
name = "UDP"
|
|
fields_desc = [ ShortEnumField("sport", 53, UDP_SERVICES),
|
|
ShortEnumField("dport", 53, UDP_SERVICES),
|
|
ShortField("len", None),
|
|
XShortField("chksum", None), ]
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
l = self.len
|
|
if l is None:
|
|
l = len(p)
|
|
p = p[:4]+struct.pack("!H",l)+p[6:]
|
|
if self.chksum is None:
|
|
if isinstance(self.underlayer, IP):
|
|
psdhdr = struct.pack("!4s4sHH",
|
|
inet_aton(self.underlayer.src),
|
|
inet_aton(self.underlayer.dst),
|
|
self.underlayer.proto,
|
|
len(p))
|
|
ck=checksum(psdhdr+p)
|
|
p = p[:6]+struct.pack("!H", ck)+p[8:]
|
|
elif isinstance(self.underlayer, IPv6) or isinstance(self.underlayer, _IPv6OptionHeader):
|
|
ck = in6_chksum(socket.IPPROTO_UDP, self.underlayer, p)
|
|
p = p[:6]+struct.pack("!H", ck)+p[8:]
|
|
else:
|
|
warning("No IP underlayer to compute checksum. Leaving null.")
|
|
return p
|
|
def extract_padding(self, s):
|
|
l = self.len - 8
|
|
return s[:l],s[l:]
|
|
def hashret(self):
|
|
if conf.checkIPsrc:
|
|
return struct.pack("H",self.sport ^ self.dport)+self.payload.hashret()
|
|
else:
|
|
return self.payload.hashret()
|
|
def answers(self, other):
|
|
if not isinstance(other, UDP):
|
|
return 0
|
|
if conf.checkIPsrc:
|
|
if not ((self.sport == other.dport) and
|
|
(self.dport == other.sport)):
|
|
return 0
|
|
return self.payload.answers(other.payload)
|
|
def mysummary(self):
|
|
if isinstance(self.underlayer, IP):
|
|
return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%")
|
|
elif isinstance(self.underlayer, IPv6):
|
|
return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%")
|
|
else:
|
|
return self.sprintf("UDP %UDP.sport% > %UDP.dport%")
|
|
|
|
icmptypes = { 0 : "echo-reply",
|
|
3 : "dest-unreach",
|
|
4 : "source-quench",
|
|
5 : "redirect",
|
|
8 : "echo-request",
|
|
9 : "router-advertisement",
|
|
10 : "router-solicitation",
|
|
11 : "time-exceeded",
|
|
12 : "parameter-problem",
|
|
13 : "timestamp-request",
|
|
14 : "timestamp-reply",
|
|
15 : "information-request",
|
|
16 : "information-response",
|
|
17 : "address-mask-request",
|
|
18 : "address-mask-reply" }
|
|
|
|
class ICMP(Packet):
|
|
name = "ICMP"
|
|
fields_desc = [ ByteEnumField("type",8, icmptypes),
|
|
ByteField("code",0),
|
|
XShortField("chksum", None),
|
|
XShortField("id",0),
|
|
XShortField("seq",0) ]
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
if self.chksum is None:
|
|
ck = checksum(p)
|
|
p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:]
|
|
return p
|
|
|
|
def hashret(self):
|
|
return struct.pack("HH",self.id,self.seq)+self.payload.hashret()
|
|
def answers(self, other):
|
|
if not isinstance(other,ICMP):
|
|
return 0
|
|
if ( (other.type,self.type) in [(8,0),(13,14),(15,16),(17,18)] and
|
|
self.id == other.id and
|
|
self.seq == other.seq ):
|
|
return 1
|
|
return 0
|
|
|
|
def guess_payload_class(self, payload):
|
|
if self.type in [3,4,5,11,12]:
|
|
return IPerror
|
|
else:
|
|
return None
|
|
def mysummary(self):
|
|
if isinstance(self.underlayer, IP):
|
|
return self.underlayer.sprintf("ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%")
|
|
else:
|
|
return self.sprintf("ICMP %ICMP.type% %ICMP.code%")
|
|
|
|
|
|
|
|
|
|
|
|
class IPerror(IP):
|
|
name = "IP in ICMP"
|
|
def answers(self, other):
|
|
if not isinstance(other, IP):
|
|
return 0
|
|
if not ( ((conf.checkIPsrc == 0) or (self.dst == other.dst)) and
|
|
(self.src == other.src) and
|
|
( ((conf.checkIPID == 0)
|
|
or (self.id == other.id)
|
|
or (conf.checkIPID == 1 and self.id == socket.htons(other.id)))) and
|
|
(self.proto == other.proto) ):
|
|
return 0
|
|
return self.payload.answers(other.payload)
|
|
def mysummary(self):
|
|
return Packet.mysummary(self)
|
|
|
|
|
|
class TCPerror(TCP):
|
|
name = "TCP in ICMP"
|
|
def answers(self, other):
|
|
if not isinstance(other, TCP):
|
|
return 0
|
|
if conf.checkIPsrc:
|
|
if not ((self.sport == other.sport) and
|
|
(self.dport == other.dport)):
|
|
return 0
|
|
if conf.check_TCPerror_seqack:
|
|
if self.seq is not None:
|
|
if self.seq != other.seq:
|
|
return 0
|
|
if self.ack is not None:
|
|
if self.ack != other.ack:
|
|
return 0
|
|
return 1
|
|
def mysummary(self):
|
|
return Packet.mysummary(self)
|
|
|
|
|
|
class UDPerror(UDP):
|
|
name = "UDP in ICMP"
|
|
def answers(self, other):
|
|
if not isinstance(other, UDP):
|
|
return 0
|
|
if conf.checkIPsrc:
|
|
if not ((self.sport == other.sport) and
|
|
(self.dport == other.dport)):
|
|
return 0
|
|
return 1
|
|
def mysummary(self):
|
|
return Packet.mysummary(self)
|
|
|
|
|
|
|
|
class ICMPerror(ICMP):
|
|
name = "ICMP in ICMP"
|
|
def answers(self, other):
|
|
if not isinstance(other,ICMP):
|
|
return 0
|
|
if not ((self.type == other.type) and
|
|
(self.code == other.code)):
|
|
return 0
|
|
if self.code in [0,8,13,14,17,18]:
|
|
if (self.id == other.id and
|
|
self.seq == other.seq):
|
|
return 1
|
|
else:
|
|
return 0
|
|
else:
|
|
return 1
|
|
def mysummary(self):
|
|
return Packet.mysummary(self)
|
|
|
|
class IPv6(Packet):
|
|
"""See http://namabiiru.hongo.wide.ad.jp/scapy6"""
|
|
name = "IPv6 not implemented here."
|
|
def __init__(self, *args, **kargs):
|
|
log_interactive.error(self.name)
|
|
def __repr__(self):
|
|
return "<IPv6: ERROR not implemented>"
|
|
|
|
class _IPv6OptionHeader(Packet):
|
|
"""See http://namabiiru.hongo.wide.ad.jp/scapy6"""
|
|
name = "IPv6 not implemented here."
|
|
def __init__(self, *args, **kargs):
|
|
log_interactive.error(self.name)
|
|
def __repr__(self):
|
|
return "<IPv6: ERROR not implemented>"
|
|
|
|
class PPP(Packet):
|
|
name = "PPP Link Layer"
|
|
fields_desc = [ ShortEnumField("proto", 0x0021, {0x0021: "IP",
|
|
0xc021: "LCP"} ) ]
|
|
|
|
|
|
class DNS(Packet):
|
|
name = "DNS"
|
|
fields_desc = [ ShortField("id",0),
|
|
BitField("qr",0, 1),
|
|
BitEnumField("opcode", 0, 4, {0:"QUERY",1:"IQUERY",2:"STATUS"}),
|
|
BitField("aa", 0, 1),
|
|
BitField("tc", 0, 1),
|
|
BitField("rd", 0, 1),
|
|
BitField("ra", 0 ,1),
|
|
BitField("z", 0, 3),
|
|
BitEnumField("rcode", 0, 4, {0:"ok", 1:"format-error", 2:"server-failure", 3:"name-error", 4:"not-implemented", 5:"refused"}),
|
|
DNSRRCountField("qdcount", None, "qd"),
|
|
DNSRRCountField("ancount", None, "an"),
|
|
DNSRRCountField("nscount", None, "ns"),
|
|
DNSRRCountField("arcount", None, "ar"),
|
|
DNSQRField("qd", "qdcount"),
|
|
DNSRRField("an", "ancount"),
|
|
DNSRRField("ns", "nscount"),
|
|
DNSRRField("ar", "arcount",0) ]
|
|
def answers(self, other):
|
|
return (isinstance(other, DNS)
|
|
and self.id == other.id
|
|
and self.qr == 1
|
|
and other.qr == 0)
|
|
|
|
def mysummary(self):
|
|
type = ["Qry","Ans"][self.qr]
|
|
name = ""
|
|
if self.qr:
|
|
type = "Ans"
|
|
if self.ancount > 0 and isinstance(self.an, DNSRR):
|
|
name = ' "%s"' % self.an.rdata
|
|
else:
|
|
type = "Qry"
|
|
if self.qdcount > 0 and isinstance(self.qd, DNSQR):
|
|
name = ' "%s"' % self.qd.qname
|
|
return 'DNS %s%s ' % (type, name)
|
|
|
|
dnstypes = { 0:"ANY", 255:"ALL",
|
|
1:"A", 2:"NS", 3:"MD", 4:"MD", 5:"CNAME", 6:"SOA", 7: "MB", 8:"MG",
|
|
9:"MR",10:"NULL",11:"WKS",12:"PTR",13:"HINFO",14:"MINFO",15:"MX",16:"TXT",
|
|
17:"RP",18:"AFSDB",28:"AAAA", 33:"SRV",38:"A6",39:"DNAME"}
|
|
|
|
dnsqtypes = {251:"IXFR",252:"AXFR",253:"MAILB",254:"MAILA",255:"ALL"}
|
|
dnsqtypes.update(dnstypes)
|
|
dnsclasses = {1: 'IN', 2: 'CS', 3: 'CH', 4: 'HS', 255: 'ANY'}
|
|
|
|
|
|
class DNSQR(Packet):
|
|
name = "DNS Question Record"
|
|
show_indent=0
|
|
fields_desc = [ DNSStrField("qname",""),
|
|
ShortEnumField("qtype", 1, dnsqtypes),
|
|
ShortEnumField("qclass", 1, dnsclasses) ]
|
|
|
|
|
|
|
|
class DNSRR(Packet):
|
|
name = "DNS Resource Record"
|
|
show_indent=0
|
|
fields_desc = [ DNSStrField("rrname",""),
|
|
ShortEnumField("type", 1, dnstypes),
|
|
ShortEnumField("rclass", 1, dnsclasses),
|
|
IntField("ttl", 0),
|
|
RDLenField("rdlen"),
|
|
RDataField("rdata", "", "rdlen") ]
|
|
|
|
dhcpmagic="c\x82Sc"
|
|
|
|
|
|
class BOOTP(Packet):
|
|
name = "BOOTP"
|
|
fields_desc = [ ByteEnumField("op",1, {1:"BOOTREQUEST", 2:"BOOTREPLY"}),
|
|
ByteField("htype",1),
|
|
ByteField("hlen",6),
|
|
ByteField("hops",0),
|
|
IntField("xid",0),
|
|
ShortField("secs",0),
|
|
FlagsField("flags", 0, 16, "???????????????B"),
|
|
IPField("ciaddr","0.0.0.0"),
|
|
IPField("yiaddr","0.0.0.0"),
|
|
IPField("siaddr","0.0.0.0"),
|
|
IPField("giaddr","0.0.0.0"),
|
|
Field("chaddr","", "16s"),
|
|
Field("sname","","64s"),
|
|
Field("file","","128s"),
|
|
StrField("options","") ]
|
|
def guess_payload_class(self, payload):
|
|
if self.options[:len(dhcpmagic)] == dhcpmagic:
|
|
return DHCP
|
|
else:
|
|
return Packet.guess_payload_class(self, payload)
|
|
def extract_padding(self,s):
|
|
if self.options[:len(dhcpmagic)] == dhcpmagic:
|
|
# set BOOTP options to DHCP magic cookie and make rest a payload of DHCP options
|
|
payload = self.options[len(dhcpmagic):]
|
|
self.options = self.options[:len(dhcpmagic)]
|
|
return payload, None
|
|
else:
|
|
return "", None
|
|
def hashret(self):
|
|
return struct.pack("L", self.xid)
|
|
def answers(self, other):
|
|
if not isinstance(other, BOOTP):
|
|
return 0
|
|
return self.xid == other.xid
|
|
|
|
|
|
|
|
#DHCP_UNKNOWN, DHCP_IP, DHCP_IPLIST, DHCP_TYPE \
|
|
#= range(4)
|
|
#
|
|
|
|
DHCPTypes = {
|
|
1: "discover",
|
|
2: "offer",
|
|
3: "request",
|
|
4: "decline",
|
|
5: "ack",
|
|
6: "nak",
|
|
7: "release",
|
|
8: "inform",
|
|
9: "force_renew",
|
|
10:"lease_query",
|
|
11:"lease_unassigned",
|
|
12:"lease_unknown",
|
|
13:"lease_active",
|
|
}
|
|
|
|
DHCPOptions = {
|
|
0: "pad",
|
|
1: IPField("subnet_mask", "0.0.0.0"),
|
|
2: "time_zone",
|
|
3: IPField("router","0.0.0.0"),
|
|
4: IPField("time_server","0.0.0.0"),
|
|
5: IPField("IEN_name_server","0.0.0.0"),
|
|
6: IPField("name_server","0.0.0.0"),
|
|
7: IPField("log_server","0.0.0.0"),
|
|
8: IPField("cookie_server","0.0.0.0"),
|
|
9: IPField("lpr_server","0.0.0.0"),
|
|
12: "hostname",
|
|
14: "dump_path",
|
|
15: "domain",
|
|
17: "root_disk_path",
|
|
22: "max_dgram_reass_size",
|
|
23: "default_ttl",
|
|
24: "pmtu_timeout",
|
|
28: IPField("broadcast_address","0.0.0.0"),
|
|
35: "arp_cache_timeout",
|
|
36: "ether_or_dot3",
|
|
37: "tcp_ttl",
|
|
38: "tcp_keepalive_interval",
|
|
39: "tcp_keepalive_garbage",
|
|
40: "NIS_domain",
|
|
41: IPField("NIS_server","0.0.0.0"),
|
|
42: IPField("NTP_server","0.0.0.0"),
|
|
43: "vendor_specific",
|
|
44: IPField("NetBIOS_server","0.0.0.0"),
|
|
45: IPField("NetBIOS_dist_server","0.0.0.0"),
|
|
50: IPField("requested_addr","0.0.0.0"),
|
|
51: IntField("lease_time", 43200),
|
|
54: IPField("server_id","0.0.0.0"),
|
|
55: "param_req_list",
|
|
57: ShortField("max_dhcp_size", 1500),
|
|
58: IntField("renewal_time", 21600),
|
|
59: IntField("rebinding_time", 37800),
|
|
60: "vendor_class_id",
|
|
61: "client_id",
|
|
|
|
64: "NISplus_domain",
|
|
65: IPField("NISplus_server","0.0.0.0"),
|
|
69: IPField("SMTP_server","0.0.0.0"),
|
|
70: IPField("POP3_server","0.0.0.0"),
|
|
71: IPField("NNTP_server","0.0.0.0"),
|
|
72: IPField("WWW_server","0.0.0.0"),
|
|
73: IPField("Finger_server","0.0.0.0"),
|
|
74: IPField("IRC_server","0.0.0.0"),
|
|
75: IPField("StreetTalk_server","0.0.0.0"),
|
|
76: "StreetTalk_Dir_Assistance",
|
|
82: "relay_agent_Information",
|
|
53: ByteEnumField("message-type", 1, DHCPTypes),
|
|
# 55: DHCPRequestListField("request-list"),
|
|
255: "end"
|
|
}
|
|
|
|
DHCPRevOptions = {}
|
|
|
|
for k,v in DHCPOptions.iteritems():
|
|
if type(v) is str:
|
|
n = v
|
|
v = None
|
|
else:
|
|
n = str(v)
|
|
DHCPRevOptions[n] = (k,v)
|
|
del(n)
|
|
del(v)
|
|
del(k)
|
|
|
|
|
|
|
|
|
|
|
|
class DHCPOptionsField(StrField):
|
|
islist=1
|
|
def i2repr(self,pkt,x):
|
|
s = []
|
|
for v in x:
|
|
if type(v) is tuple and len(v) == 2:
|
|
if DHCPRevOptions.has_key(v[0]) and isinstance(DHCPRevOptions[v[0]][1],Field):
|
|
f = DHCPRevOptions[v[0]][1]
|
|
vv = f.i2repr(pkt,v[1])
|
|
else:
|
|
vv = repr(v[1])
|
|
s.append("%s=%s" % (v[0],vv))
|
|
else:
|
|
s.append(str(v))
|
|
return "[%s]" % (" ".join(s))
|
|
|
|
def getfield(self, pkt, s):
|
|
return "", self.m2i(pkt, s)
|
|
def m2i(self, pkt, x):
|
|
opt = []
|
|
while x:
|
|
o = ord(x[0])
|
|
if o == 255:
|
|
opt.append("end")
|
|
x = x[1:]
|
|
continue
|
|
if o == 0:
|
|
opt.append("pad")
|
|
x = x[1:]
|
|
continue
|
|
if DHCPOptions.has_key(o):
|
|
f = DHCPOptions[o]
|
|
|
|
if isinstance(f, str):
|
|
olen = ord(x[1])
|
|
opt.append( (f,x[2:olen+2]) )
|
|
x = x[olen+2:]
|
|
else:
|
|
olen = ord(x[1])
|
|
left, val = f.getfield(pkt,x[2:olen+2])
|
|
# val = f.m2i(pkt,val)
|
|
# if left:
|
|
# print "m2i data left left=%s" % left
|
|
opt.append((f.name, val))
|
|
x = x[olen+2:]
|
|
else:
|
|
olen = ord(x[1])
|
|
opt.append((o, x[2:olen+2]))
|
|
x = x[olen+2:]
|
|
return opt
|
|
def i2m(self, pkt, x):
|
|
#print "i2m x=%s" % x
|
|
s = ""
|
|
for o in x:
|
|
if type(o) is tuple and len(o) == 2:
|
|
name, val = o
|
|
|
|
if isinstance(name, int):
|
|
onum, oval = name, val
|
|
elif DHCPRevOptions.has_key(name):
|
|
onum, f = DHCPRevOptions[name]
|
|
if f is None:
|
|
oval = val
|
|
else:
|
|
# oval = f.addfield(pkt,"",f.i2m(pkt,f.any2i(pkt,val)))
|
|
oval = f.addfield(pkt,"",f.any2i(pkt,val))
|
|
|
|
else:
|
|
warning("Unknown field option %s" % name)
|
|
continue
|
|
|
|
s += chr(onum)
|
|
s += chr(len(oval))
|
|
s += oval
|
|
|
|
elif (type(o) is str and DHCPRevOptions.has_key(o) and
|
|
DHCPRevOptions[o][1] == None):
|
|
s += chr(DHCPRevOptions[o][0])
|
|
elif type(o) is int:
|
|
s += chr(o)
|
|
else:
|
|
warning("Malformed option %s" % o)
|
|
return s
|
|
|
|
|
|
class DHCP(Packet):
|
|
name = "DHCP options"
|
|
fields_desc = [ DHCPOptionsField("options","") ]
|
|
|
|
|
|
class Dot11(Packet):
|
|
name = "802.11"
|
|
fields_desc = [
|
|
BitField("subtype", 0, 4),
|
|
BitEnumField("type", 0, 2, ["Management", "Control", "Data", "Reserved"]),
|
|
BitField("proto", 0, 2),
|
|
FlagsField("FCfield", 0, 8, ["to-DS", "from-DS", "MF", "retry", "pw-mgt", "MD", "wep", "order"]),
|
|
ShortField("ID",0),
|
|
MACField("addr1", ETHER_ANY),
|
|
Dot11Addr2MACField("addr2", ETHER_ANY),
|
|
Dot11Addr3MACField("addr3", ETHER_ANY),
|
|
Dot11SCField("SC", 0),
|
|
Dot11Addr4MACField("addr4", ETHER_ANY)
|
|
]
|
|
def mysummary(self):
|
|
return self.sprintf("802.11 %Dot11.type% %Dot11.subtype% %Dot11.addr2% > %Dot11.addr1%")
|
|
def guess_payload_class(self, payload):
|
|
if self.FCfield & 0x40:
|
|
return Dot11WEP
|
|
else:
|
|
return Packet.guess_payload_class(self, payload)
|
|
def answers(self, other):
|
|
if isinstance(other,Dot11):
|
|
if self.type == 0: # management
|
|
if self.addr1 != other.addr2: # check resp DA w/ req SA
|
|
return 0
|
|
if (other.subtype,self.subtype) in [(0,1),(2,3),(4,5)]:
|
|
return 1
|
|
if self.subtype == other.subtype == 11: # auth
|
|
return self.payload.answers(other.payload)
|
|
elif self.type == 1: # control
|
|
return 0
|
|
elif self.type == 2: # data
|
|
return self.payload.answers(other.payload)
|
|
elif self.type == 3: # reserved
|
|
return 0
|
|
return 0
|
|
def unwep(self, key=None, warn=1):
|
|
if self.FCfield & 0x40 == 0:
|
|
if warn:
|
|
warning("No WEP to remove")
|
|
return
|
|
if isinstance(self.payload.payload, NoPayload):
|
|
if key or conf.wepkey:
|
|
self.payload.decrypt(key)
|
|
if isinstance(self.payload.payload, NoPayload):
|
|
if warn:
|
|
warning("Dot11 can't be decrypted. Check conf.wepkey.")
|
|
return
|
|
self.FCfield &= ~0x40
|
|
self.payload=self.payload.payload
|
|
|
|
|
|
capability_list = [ "res8", "res9", "short-slot", "res11",
|
|
"res12", "DSSS-OFDM", "res14", "res15",
|
|
"ESS", "IBSS", "CFP", "CFP-req",
|
|
"privacy", "short-preamble", "PBCC", "agility"]
|
|
|
|
reason_code = {0:"reserved",1:"unspec", 2:"auth-expired",
|
|
3:"deauth-ST-leaving",
|
|
4:"inactivity", 5:"AP-full", 6:"class2-from-nonauth",
|
|
7:"class3-from-nonass", 8:"disas-ST-leaving",
|
|
9:"ST-not-auth"}
|
|
|
|
status_code = {0:"success", 1:"failure", 10:"cannot-support-all-cap",
|
|
11:"inexist-asso", 12:"asso-denied", 13:"algo-unsupported",
|
|
14:"bad-seq-num", 15:"challenge-failure",
|
|
16:"timeout", 17:"AP-full",18:"rate-unsupported" }
|
|
|
|
class Dot11Beacon(Packet):
|
|
name = "802.11 Beacon"
|
|
fields_desc = [ LELongField("timestamp", 0),
|
|
LEShortField("beacon_interval", 0x0064),
|
|
FlagsField("cap", 0, 16, capability_list) ]
|
|
|
|
|
|
class Dot11Elt(Packet):
|
|
name = "802.11 Information Element"
|
|
fields_desc = [ ByteEnumField("ID", 0, {0:"SSID", 1:"Rates", 2: "FHset", 3:"DSset", 4:"CFset", 5:"TIM", 6:"IBSSset", 16:"challenge",
|
|
42:"ERPinfo", 47:"ERPinfo", 48:"RSNinfo", 50:"ESRates",221:"vendor",68:"reserved"}),
|
|
FieldLenField("len", None, "info", "B"),
|
|
StrLenField("info", "", "len") ]
|
|
def mysummary(self):
|
|
if self.ID == 0:
|
|
return "SSID=%s"%repr(self.info),[Dot11]
|
|
else:
|
|
return ""
|
|
|
|
class Dot11ATIM(Packet):
|
|
name = "802.11 ATIM"
|
|
|
|
class Dot11Disas(Packet):
|
|
name = "802.11 Disassociation"
|
|
fields_desc = [ LEShortEnumField("reason", 1, reason_code) ]
|
|
|
|
class Dot11AssoReq(Packet):
|
|
name = "802.11 Association Request"
|
|
fields_desc = [ FlagsField("cap", 0, 16, capability_list),
|
|
LEShortField("listen_interval", 0x00c8) ]
|
|
|
|
|
|
class Dot11AssoResp(Packet):
|
|
name = "802.11 Association Response"
|
|
fields_desc = [ FlagsField("cap", 0, 16, capability_list),
|
|
LEShortField("status", 0),
|
|
LEShortField("AID", 0) ]
|
|
|
|
class Dot11ReassoReq(Packet):
|
|
name = "802.11 Reassociation Request"
|
|
fields_desc = [ FlagsField("cap", 0, 16, capability_list),
|
|
MACField("current_AP", ETHER_ANY),
|
|
LEShortField("listen_interval", 0x00c8) ]
|
|
|
|
|
|
class Dot11ReassoResp(Dot11AssoResp):
|
|
name = "802.11 Reassociation Response"
|
|
|
|
class Dot11ProbeReq(Packet):
|
|
name = "802.11 Probe Request"
|
|
|
|
class Dot11ProbeResp(Packet):
|
|
name = "802.11 Probe Response"
|
|
fields_desc = [ LELongField("timestamp", 0),
|
|
LEShortField("beacon_interval", 0x0064),
|
|
FlagsField("cap", 0, 16, capability_list) ]
|
|
|
|
class Dot11Auth(Packet):
|
|
name = "802.11 Authentication"
|
|
fields_desc = [ LEShortEnumField("algo", 0, ["open", "sharedkey"]),
|
|
LEShortField("seqnum", 0),
|
|
LEShortEnumField("status", 0, status_code) ]
|
|
def answers(self, other):
|
|
if self.seqnum == other.seqnum+1:
|
|
return 1
|
|
return 0
|
|
|
|
class Dot11Deauth(Packet):
|
|
name = "802.11 Deauthentication"
|
|
fields_desc = [ LEShortEnumField("reason", 1, reason_code) ]
|
|
|
|
|
|
|
|
class Dot11WEP(Packet):
|
|
name = "802.11 WEP packet"
|
|
fields_desc = [ StrFixedLenField("iv", "\0\0\0", 3),
|
|
ByteField("keyid", 0),
|
|
StrField("wepdata",None,remain=4),
|
|
IntField("icv",None) ]
|
|
|
|
def post_dissect(self, s):
|
|
# self.icv, = struct.unpack("!I",self.wepdata[-4:])
|
|
# self.wepdata = self.wepdata[:-4]
|
|
self.decrypt()
|
|
|
|
def build_payload(self):
|
|
if self.wepdata is None:
|
|
return Packet.build_payload(self)
|
|
return ""
|
|
|
|
def post_build(self, p, pay):
|
|
if self.wepdata is None:
|
|
key = conf.wepkey
|
|
if key:
|
|
if self.icv is None:
|
|
pay += struct.pack("<I",crc32(pay))
|
|
icv = ""
|
|
else:
|
|
icv = p[4:8]
|
|
c = ARC4.new(self.iv+key)
|
|
p = p[:4]+c.encrypt(pay)+icv
|
|
else:
|
|
warning("No WEP key set (conf.wepkey).. strange results expected..")
|
|
return p
|
|
|
|
|
|
def decrypt(self,key=None):
|
|
if key is None:
|
|
key = conf.wepkey
|
|
if key:
|
|
c = ARC4.new(self.iv+key)
|
|
self.add_payload(LLC(c.decrypt(self.wepdata)))
|
|
|
|
|
|
|
|
class PrismHeader(Packet):
|
|
""" iwpriv wlan0 monitor 3 """
|
|
name = "Prism header"
|
|
fields_desc = [ LEIntField("msgcode",68),
|
|
LEIntField("len",144),
|
|
StrFixedLenField("dev","",16),
|
|
LEIntField("hosttime_did",0),
|
|
LEShortField("hosttime_status",0),
|
|
LEShortField("hosttime_len",0),
|
|
LEIntField("hosttime",0),
|
|
LEIntField("mactime_did",0),
|
|
LEShortField("mactime_status",0),
|
|
LEShortField("mactime_len",0),
|
|
LEIntField("mactime",0),
|
|
LEIntField("channel_did",0),
|
|
LEShortField("channel_status",0),
|
|
LEShortField("channel_len",0),
|
|
LEIntField("channel",0),
|
|
LEIntField("rssi_did",0),
|
|
LEShortField("rssi_status",0),
|
|
LEShortField("rssi_len",0),
|
|
LEIntField("rssi",0),
|
|
LEIntField("sq_did",0),
|
|
LEShortField("sq_status",0),
|
|
LEShortField("sq_len",0),
|
|
LEIntField("sq",0),
|
|
LEIntField("signal_did",0),
|
|
LEShortField("signal_status",0),
|
|
LEShortField("signal_len",0),
|
|
LESignedIntField("signal",0),
|
|
LEIntField("noise_did",0),
|
|
LEShortField("noise_status",0),
|
|
LEShortField("noise_len",0),
|
|
LEIntField("noise",0),
|
|
LEIntField("rate_did",0),
|
|
LEShortField("rate_status",0),
|
|
LEShortField("rate_len",0),
|
|
LEIntField("rate",0),
|
|
LEIntField("istx_did",0),
|
|
LEShortField("istx_status",0),
|
|
LEShortField("istx_len",0),
|
|
LEIntField("istx",0),
|
|
LEIntField("frmlen_did",0),
|
|
LEShortField("frmlen_status",0),
|
|
LEShortField("frmlen_len",0),
|
|
LEIntField("frmlen",0),
|
|
]
|
|
|
|
|
|
|
|
class HSRP(Packet):
|
|
name = "HSRP"
|
|
fields_desc = [
|
|
ByteField("version", 0),
|
|
ByteEnumField("opcode", 0, { 0:"Hello"}),
|
|
ByteEnumField("state", 16, { 16:"Active"}),
|
|
ByteField("hellotime", 3),
|
|
ByteField("holdtime", 10),
|
|
ByteField("priority", 120),
|
|
ByteField("group", 1),
|
|
ByteField("reserved", 0),
|
|
StrFixedLenField("auth","cisco",8),
|
|
IPField("virtualIP","192.168.1.1") ]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NTP(Packet):
|
|
# RFC 1769
|
|
name = "NTP"
|
|
fields_desc = [
|
|
BitEnumField('leap', 0, 2,
|
|
{ 0: 'nowarning',
|
|
1: 'longminute',
|
|
2: 'shortminute',
|
|
3: 'notsync'}),
|
|
BitField('version', 3, 3),
|
|
BitEnumField('mode', 3, 3,
|
|
{ 0: 'reserved',
|
|
1: 'sym_active',
|
|
2: 'sym_passive',
|
|
3: 'client',
|
|
4: 'server',
|
|
5: 'broadcast',
|
|
6: 'control',
|
|
7: 'private'}),
|
|
BitField('stratum', 2, 8),
|
|
BitField('poll', 0xa, 8), ### XXX : it's a signed int
|
|
BitField('precision', 0, 8), ### XXX : it's a signed int
|
|
FloatField('delay', 0, 32),
|
|
FloatField('dispersion', 0, 32),
|
|
IPField('id', "127.0.0.1"),
|
|
TimeStampField('ref', 0, 64),
|
|
TimeStampField('orig', -1, 64), # -1 means current time
|
|
TimeStampField('recv', 0, 64),
|
|
TimeStampField('sent', -1, 64)
|
|
]
|
|
def mysummary(self):
|
|
return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%")
|
|
|
|
|
|
class GRE(Packet):
|
|
name = "GRE"
|
|
fields_desc = [ BitField("chksumpresent",0,1),
|
|
BitField("reserved0",0,12),
|
|
BitField("version",0,3),
|
|
XShortEnumField("proto", 0x0000, ETHER_TYPES),
|
|
ConditionalField(XShortField("chksum",None),"chksumpresent",lambda x:x==1),
|
|
ConditionalField(XShortField("reserved1",None),"chksumpresent",lambda x:x==1),
|
|
]
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
if self.chksumpresent and self.chksum is None:
|
|
c = checksum(p)
|
|
p = p[:4]+chr((c>>8)&0xff)+chr(c&0xff)+p[6:]
|
|
return p
|
|
|
|
|
|
class Radius(Packet):
|
|
name = "Radius"
|
|
fields_desc = [ ByteEnumField("code", 1, {1: "Access-Request",
|
|
2: "Access-Accept",
|
|
3: "Access-Reject",
|
|
4: "Accounting-Request",
|
|
5: "Accounting-Accept",
|
|
6: "Accounting-Status",
|
|
7: "Password-Request",
|
|
8: "Password-Ack",
|
|
9: "Password-Reject",
|
|
10: "Accounting-Message",
|
|
11: "Access-Challenge",
|
|
12: "Status-Server",
|
|
13: "Status-Client",
|
|
21: "Resource-Free-Request",
|
|
22: "Resource-Free-Response",
|
|
23: "Resource-Query-Request",
|
|
24: "Resource-Query-Response",
|
|
25: "Alternate-Resource-Reclaim-Request",
|
|
26: "NAS-Reboot-Request",
|
|
27: "NAS-Reboot-Response",
|
|
29: "Next-Passcode",
|
|
30: "New-Pin",
|
|
31: "Terminate-Session",
|
|
32: "Password-Expired",
|
|
33: "Event-Request",
|
|
34: "Event-Response",
|
|
40: "Disconnect-Request",
|
|
41: "Disconnect-ACK",
|
|
42: "Disconnect-NAK",
|
|
43: "CoA-Request",
|
|
44: "CoA-ACK",
|
|
45: "CoA-NAK",
|
|
50: "IP-Address-Allocate",
|
|
51: "IP-Address-Release",
|
|
253: "Experimental-use",
|
|
254: "Reserved",
|
|
255: "Reserved"} ),
|
|
ByteField("id", 0),
|
|
ShortField("len", None),
|
|
StrFixedLenField("authenticator","",16) ]
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
l = self.len
|
|
if l is None:
|
|
l = len(p)
|
|
p = p[:2]+struct.pack("!H",l)+p[4:]
|
|
return p
|
|
|
|
|
|
|
|
|
|
class RIP(Packet):
|
|
name = "RIP header"
|
|
fields_desc = [
|
|
ByteEnumField("command",1,{1:"req",2:"resp",3:"traceOn",4:"traceOff",5:"sun",
|
|
6:"trigReq",7:"trigResp",8:"trigAck",9:"updateReq",
|
|
10:"updateResp",11:"updateAck"}),
|
|
ByteField("version",1),
|
|
ShortField("null",0),
|
|
]
|
|
|
|
class RIPEntry(Packet):
|
|
name = "RIP entry"
|
|
fields_desc = [
|
|
ShortEnumField("AF",2,{2:"IP"}),
|
|
ShortField("RouteTag",0),
|
|
IPField("addr","0.0.0.0"),
|
|
IPField("mask","0.0.0.0"),
|
|
IPField("nextHop","0.0.0.0"),
|
|
IntEnumField("metric",1,{16:"Unreach"}),
|
|
]
|
|
|
|
|
|
|
|
|
|
ISAKMP_payload_type = ["None","SA","Proposal","Transform","KE","ID","CERT","CR","Hash",
|
|
"SIG","Nonce","Notification","Delete","VendorID"]
|
|
|
|
ISAKMP_exchange_type = ["None","base","identity prot.",
|
|
"auth only", "aggressive", "info"]
|
|
|
|
|
|
class ISAKMP_class(Packet):
|
|
def guess_payload_class(self, payload):
|
|
np = self.next_payload
|
|
if np == 0:
|
|
return Raw
|
|
elif np < len(ISAKMP_payload_type):
|
|
pt = ISAKMP_payload_type[np]
|
|
return globals().get("ISAKMP_payload_%s" % pt, ISAKMP_payload)
|
|
else:
|
|
return ISAKMP_payload
|
|
|
|
|
|
class ISAKMP(ISAKMP_class): # rfc2408
|
|
name = "ISAKMP"
|
|
fields_desc = [
|
|
StrFixedLenField("init_cookie","",8),
|
|
StrFixedLenField("resp_cookie","",8),
|
|
ByteEnumField("next_payload",0,ISAKMP_payload_type),
|
|
XByteField("version",0x10),
|
|
ByteEnumField("exch_type",0,ISAKMP_exchange_type),
|
|
FlagsField("flags",0, 8, ["encryption","commit","auth_only","res3","res4","res5","res6","res7"]), # XXX use a Flag field
|
|
IntField("id",0),
|
|
IntField("length",None)
|
|
]
|
|
|
|
def guess_payload_class(self, payload):
|
|
if self.flags & 1:
|
|
return Raw
|
|
return ISAKMP_class.guess_payload_class(self, payload)
|
|
|
|
def answers(self, other):
|
|
if isinstance(other, ISAKMP):
|
|
if other.init_cookie == self.init_cookie:
|
|
return 1
|
|
return 0
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
if self.length is None:
|
|
p = p[:24]+struct.pack("!I",len(p))+p[28:]
|
|
return p
|
|
|
|
|
|
|
|
|
|
class ISAKMP_payload_Transform(ISAKMP_class):
|
|
name = "IKE Transform"
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
# ShortField("len",None),
|
|
ShortField("length",None),
|
|
ByteField("num",None),
|
|
ByteEnumField("id",1,{1:"KEY_IKE"}),
|
|
ShortField("res2",0),
|
|
ISAKMPTransformSetField("transforms",None,"length",shift=8)
|
|
# XIntField("enc",0x80010005L),
|
|
# XIntField("hash",0x80020002L),
|
|
# XIntField("auth",0x80030001L),
|
|
# XIntField("group",0x80040002L),
|
|
# XIntField("life_type",0x800b0001L),
|
|
# XIntField("durationh",0x000c0004L),
|
|
# XIntField("durationl",0x00007080L),
|
|
]
|
|
def post_build(self, p, pay):
|
|
if self.length is None:
|
|
l = len(p)
|
|
p = p[:2]+chr((l>>8)&0xff)+chr(l&0xff)+p[4:]
|
|
p += pay
|
|
return p
|
|
|
|
|
|
|
|
|
|
class ISAKMP_payload_Proposal(ISAKMP_class):
|
|
name = "IKE proposal"
|
|
# ISAKMP_payload_type = 0
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
FieldLenField("length",None,"trans","H"),
|
|
ByteField("proposal",1),
|
|
ByteEnumField("proto",1,{1:"ISAKMP"}),
|
|
FieldLenField("SPIsize",None,"SPI","B"),
|
|
ByteField("trans_nb",None),
|
|
StrLenField("SPI","","SPIsize"),
|
|
PacketLenField("trans",Raw(),ISAKMP_payload_Transform,"length",shift=8),
|
|
]
|
|
|
|
|
|
class ISAKMP_payload_metaclass(type):
|
|
def __new__(cls, name, bases, dct):
|
|
f = dct["fields_desc"]
|
|
f = [ ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
ShortField("length",None),
|
|
] + f
|
|
dct["fields_desc"] = f
|
|
return super(ISAKMP_payload_metaclass, cls).__new__(cls, name, bases, dct)
|
|
|
|
|
|
class ISAKMP_payload(ISAKMP_class):
|
|
name = "ISAKMP payload"
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
FieldLenField("length",None,"load","H"),
|
|
StrLenField("load","","length",shift=4),
|
|
]
|
|
|
|
|
|
class ISAKMP_payload_VendorID(ISAKMP_class):
|
|
name = "ISAKMP Vendor ID"
|
|
overload_fields = { ISAKMP: { "next_payload":13 }}
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
FieldLenField("length",None,"vendorID","H"),
|
|
StrLenField("vendorID","","length",shift=4),
|
|
]
|
|
|
|
class ISAKMP_payload_SA(ISAKMP_class):
|
|
name = "ISAKMP SA"
|
|
overload_fields = { ISAKMP: { "next_payload":1 }}
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
FieldLenField("length",None,"prop","H"),
|
|
IntEnumField("DOI",1,{1:"IPSEC"}),
|
|
IntEnumField("situation",1,{1:"identity"}),
|
|
PacketLenField("prop",Raw(),ISAKMP_payload_Proposal,"length",shift=12),
|
|
]
|
|
|
|
class ISAKMP_payload_Nonce(ISAKMP_class):
|
|
name = "ISAKMP Nonce"
|
|
overload_fields = { ISAKMP: { "next_payload":10 }}
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
FieldLenField("length",None,"load","H"),
|
|
StrLenField("load","","length",shift=4),
|
|
]
|
|
|
|
class ISAKMP_payload_KE(ISAKMP_class):
|
|
name = "ISAKMP Key Exchange"
|
|
overload_fields = { ISAKMP: { "next_payload":4 }}
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
FieldLenField("length",None,"load","H"),
|
|
StrLenField("load","","length",shift=4),
|
|
]
|
|
|
|
class ISAKMP_payload_ID(ISAKMP_class):
|
|
name = "ISAKMP Identification"
|
|
overload_fields = { ISAKMP: { "next_payload":5 }}
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
FieldLenField("length",None,"load","H"),
|
|
ByteEnumField("IDtype",1,{1:"IPv4_addr", 11:"Key"}),
|
|
ByteEnumField("ProtoID",0,{0:"Unused"}),
|
|
ShortEnumField("Port",0,{0:"Unused"}),
|
|
# IPField("IdentData","127.0.0.1"),
|
|
StrLenField("load","","length",shift=8),
|
|
]
|
|
|
|
|
|
|
|
class ISAKMP_payload_Hash(ISAKMP_class):
|
|
name = "ISAKMP Hash"
|
|
overload_fields = { ISAKMP: { "next_payload":8 }}
|
|
fields_desc = [
|
|
ByteEnumField("next_payload",None,ISAKMP_payload_type),
|
|
ByteField("res",0),
|
|
FieldLenField("length",None,"load","H"),
|
|
StrLenField("load","","length",shift=4),
|
|
]
|
|
|
|
|
|
|
|
ISAKMP_payload_type_overload = {}
|
|
for i in range(len(ISAKMP_payload_type)):
|
|
name = "ISAKMP_payload_%s" % ISAKMP_payload_type[i]
|
|
if name in globals():
|
|
ISAKMP_payload_type_overload[globals()[name]] = {"next_payload":i}
|
|
|
|
del(i)
|
|
del(name)
|
|
ISAKMP_class.overload_fields = ISAKMP_payload_type_overload.copy()
|
|
|
|
|
|
|
|
|
|
# Cisco Skinny protocol
|
|
|
|
# shamelessly ripped from Ethereal dissector
|
|
skinny_messages = {
|
|
# Station -> Callmanager
|
|
0x0000: "KeepAliveMessage",
|
|
0x0001: "RegisterMessage",
|
|
0x0002: "IpPortMessage",
|
|
0x0003: "KeypadButtonMessage",
|
|
0x0004: "EnblocCallMessage",
|
|
0x0005: "StimulusMessage",
|
|
0x0006: "OffHookMessage",
|
|
0x0007: "OnHookMessage",
|
|
0x0008: "HookFlashMessage",
|
|
0x0009: "ForwardStatReqMessage",
|
|
0x000A: "SpeedDialStatReqMessage",
|
|
0x000B: "LineStatReqMessage",
|
|
0x000C: "ConfigStatReqMessage",
|
|
0x000D: "TimeDateReqMessage",
|
|
0x000E: "ButtonTemplateReqMessage",
|
|
0x000F: "VersionReqMessage",
|
|
0x0010: "CapabilitiesResMessage",
|
|
0x0011: "MediaPortListMessage",
|
|
0x0012: "ServerReqMessage",
|
|
0x0020: "AlarmMessage",
|
|
0x0021: "MulticastMediaReceptionAck",
|
|
0x0022: "OpenReceiveChannelAck",
|
|
0x0023: "ConnectionStatisticsRes",
|
|
0x0024: "OffHookWithCgpnMessage",
|
|
0x0025: "SoftKeySetReqMessage",
|
|
0x0026: "SoftKeyEventMessage",
|
|
0x0027: "UnregisterMessage",
|
|
0x0028: "SoftKeyTemplateReqMessage",
|
|
0x0029: "RegisterTokenReq",
|
|
0x002A: "MediaTransmissionFailure",
|
|
0x002B: "HeadsetStatusMessage",
|
|
0x002C: "MediaResourceNotification",
|
|
0x002D: "RegisterAvailableLinesMessage",
|
|
0x002E: "DeviceToUserDataMessage",
|
|
0x002F: "DeviceToUserDataResponseMessage",
|
|
0x0030: "UpdateCapabilitiesMessage",
|
|
0x0031: "OpenMultiMediaReceiveChannelAckMessage",
|
|
0x0032: "ClearConferenceMessage",
|
|
0x0033: "ServiceURLStatReqMessage",
|
|
0x0034: "FeatureStatReqMessage",
|
|
0x0035: "CreateConferenceResMessage",
|
|
0x0036: "DeleteConferenceResMessage",
|
|
0x0037: "ModifyConferenceResMessage",
|
|
0x0038: "AddParticipantResMessage",
|
|
0x0039: "AuditConferenceResMessage",
|
|
0x0040: "AuditParticipantResMessage",
|
|
0x0041: "DeviceToUserDataVersion1Message",
|
|
# Callmanager -> Station */
|
|
0x0081: "RegisterAckMessage",
|
|
0x0082: "StartToneMessage",
|
|
0x0083: "StopToneMessage",
|
|
0x0085: "SetRingerMessage",
|
|
0x0086: "SetLampMessage",
|
|
0x0087: "SetHkFDetectMessage",
|
|
0x0088: "SetSpeakerModeMessage",
|
|
0x0089: "SetMicroModeMessage",
|
|
0x008A: "StartMediaTransmission",
|
|
0x008B: "StopMediaTransmission",
|
|
0x008C: "StartMediaReception",
|
|
0x008D: "StopMediaReception",
|
|
0x008F: "CallInfoMessage",
|
|
0x0090: "ForwardStatMessage",
|
|
0x0091: "SpeedDialStatMessage",
|
|
0x0092: "LineStatMessage",
|
|
0x0093: "ConfigStatMessage",
|
|
0x0094: "DefineTimeDate",
|
|
0x0095: "StartSessionTransmission",
|
|
0x0096: "StopSessionTransmission",
|
|
0x0097: "ButtonTemplateMessage",
|
|
0x0098: "VersionMessage",
|
|
0x0099: "DisplayTextMessage",
|
|
0x009A: "ClearDisplay",
|
|
0x009B: "CapabilitiesReqMessage",
|
|
0x009C: "EnunciatorCommandMessage",
|
|
0x009D: "RegisterRejectMessage",
|
|
0x009E: "ServerResMessage",
|
|
0x009F: "Reset",
|
|
0x0100: "KeepAliveAckMessage",
|
|
0x0101: "StartMulticastMediaReception",
|
|
0x0102: "StartMulticastMediaTransmission",
|
|
0x0103: "StopMulticastMediaReception",
|
|
0x0104: "StopMulticastMediaTransmission",
|
|
0x0105: "OpenReceiveChannel",
|
|
0x0106: "CloseReceiveChannel",
|
|
0x0107: "ConnectionStatisticsReq",
|
|
0x0108: "SoftKeyTemplateResMessage",
|
|
0x0109: "SoftKeySetResMessage",
|
|
0x0110: "SelectSoftKeysMessage",
|
|
0x0111: "CallStateMessage",
|
|
0x0112: "DisplayPromptStatusMessage",
|
|
0x0113: "ClearPromptStatusMessage",
|
|
0x0114: "DisplayNotifyMessage",
|
|
0x0115: "ClearNotifyMessage",
|
|
0x0116: "ActivateCallPlaneMessage",
|
|
0x0117: "DeactivateCallPlaneMessage",
|
|
0x0118: "UnregisterAckMessage",
|
|
0x0119: "BackSpaceReqMessage",
|
|
0x011A: "RegisterTokenAck",
|
|
0x011B: "RegisterTokenReject",
|
|
0x0042: "DeviceToUserDataResponseVersion1Message",
|
|
0x011C: "StartMediaFailureDetection",
|
|
0x011D: "DialedNumberMessage",
|
|
0x011E: "UserToDeviceDataMessage",
|
|
0x011F: "FeatureStatMessage",
|
|
0x0120: "DisplayPriNotifyMessage",
|
|
0x0121: "ClearPriNotifyMessage",
|
|
0x0122: "StartAnnouncementMessage",
|
|
0x0123: "StopAnnouncementMessage",
|
|
0x0124: "AnnouncementFinishMessage",
|
|
0x0127: "NotifyDtmfToneMessage",
|
|
0x0128: "SendDtmfToneMessage",
|
|
0x0129: "SubscribeDtmfPayloadReqMessage",
|
|
0x012A: "SubscribeDtmfPayloadResMessage",
|
|
0x012B: "SubscribeDtmfPayloadErrMessage",
|
|
0x012C: "UnSubscribeDtmfPayloadReqMessage",
|
|
0x012D: "UnSubscribeDtmfPayloadResMessage",
|
|
0x012E: "UnSubscribeDtmfPayloadErrMessage",
|
|
0x012F: "ServiceURLStatMessage",
|
|
0x0130: "CallSelectStatMessage",
|
|
0x0131: "OpenMultiMediaChannelMessage",
|
|
0x0132: "StartMultiMediaTransmission",
|
|
0x0133: "StopMultiMediaTransmission",
|
|
0x0134: "MiscellaneousCommandMessage",
|
|
0x0135: "FlowControlCommandMessage",
|
|
0x0136: "CloseMultiMediaReceiveChannel",
|
|
0x0137: "CreateConferenceReqMessage",
|
|
0x0138: "DeleteConferenceReqMessage",
|
|
0x0139: "ModifyConferenceReqMessage",
|
|
0x013A: "AddParticipantReqMessage",
|
|
0x013B: "DropParticipantReqMessage",
|
|
0x013C: "AuditConferenceReqMessage",
|
|
0x013D: "AuditParticipantReqMessage",
|
|
0x013F: "UserToDeviceDataVersion1Message",
|
|
}
|
|
|
|
|
|
|
|
class Skinny(Packet):
|
|
name="Skinny"
|
|
fields_desc = [ LEIntField("len",0),
|
|
LEIntField("res",0),
|
|
LEIntEnumField("msg",0,skinny_messages) ]
|
|
|
|
|
|
|
|
### SEBEK
|
|
|
|
|
|
class SebekHead(Packet):
|
|
name = "Sebek header"
|
|
fields_desc = [ XIntField("magic", 0xd0d0d0),
|
|
ShortField("version", 1),
|
|
ShortEnumField("type", 0, {"read":0, "write":1,
|
|
"socket":2, "open":3}),
|
|
IntField("counter", 0),
|
|
IntField("time_sec", 0),
|
|
IntField("time_usec", 0) ]
|
|
def mysummary(self):
|
|
return self.sprintf("Sebek Header v%SebekHead.version% %SebekHead.type%")
|
|
|
|
# we need this because Sebek headers differ between v1 and v3, and
|
|
# between v3 type socket and v3 others
|
|
|
|
class SebekV1(Packet):
|
|
name = "Sebek v1"
|
|
fields_desc = [ IntField("pid", 0),
|
|
IntField("uid", 0),
|
|
IntField("fd", 0),
|
|
StrFixedLenField("command", "", 12),
|
|
FieldLenField("data_length", None, "data",fmt="I"),
|
|
StrLenField("data", "", "data_length") ]
|
|
def mysummary(self):
|
|
if isinstance(self.underlayer, SebekHead):
|
|
return self.underlayer.sprintf("Sebek v1 %SebekHead.type% (%SebekV1.command%)")
|
|
else:
|
|
return self.sprintf("Sebek v1 (%SebekV1.command%)")
|
|
|
|
class SebekV3(Packet):
|
|
name = "Sebek v3"
|
|
fields_desc = [ IntField("parent_pid", 0),
|
|
IntField("pid", 0),
|
|
IntField("uid", 0),
|
|
IntField("fd", 0),
|
|
IntField("inode", 0),
|
|
StrFixedLenField("command", "", 12),
|
|
FieldLenField("data_length", None, "data",fmt="I"),
|
|
StrLenField("data", "", "data_length") ]
|
|
def mysummary(self):
|
|
if isinstance(self.underlayer, SebekHead):
|
|
return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3.command%)")
|
|
else:
|
|
return self.sprintf("Sebek v3 (%SebekV3.command%)")
|
|
|
|
class SebekV2(SebekV3):
|
|
def mysummary(self):
|
|
if isinstance(self.underlayer, SebekHead):
|
|
return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.command%)")
|
|
else:
|
|
return self.sprintf("Sebek v2 (%SebekV2.command%)")
|
|
|
|
class SebekV3Sock(Packet):
|
|
name = "Sebek v2 socket"
|
|
fields_desc = [ IntField("parent_pid", 0),
|
|
IntField("pid", 0),
|
|
IntField("uid", 0),
|
|
IntField("fd", 0),
|
|
IntField("inode", 0),
|
|
StrFixedLenField("command", "", 12),
|
|
IntField("data_length", 15),
|
|
IPField("dip", "127.0.0.1"),
|
|
ShortField("dport", 0),
|
|
IPField("sip", "127.0.0.1"),
|
|
ShortField("sport", 0),
|
|
ShortEnumField("call", 0, { "bind":2,
|
|
"connect":3, "listen":4,
|
|
"accept":5, "sendmsg":16,
|
|
"recvmsg":17, "sendto":11,
|
|
"recvfrom":12}),
|
|
ByteEnumField("proto", 0, IP_PROTOS) ]
|
|
def mysummary(self):
|
|
if isinstance(self.underlayer, SebekHead):
|
|
return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3Sock.command%)")
|
|
else:
|
|
return self.sprintf("Sebek v3 socket (%SebekV3Sock.command%)")
|
|
|
|
class SebekV2Sock(SebekV3Sock):
|
|
def mysummary(self):
|
|
if isinstance(self.underlayer, SebekHead):
|
|
return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.command%)")
|
|
else:
|
|
return self.sprintf("Sebek v2 socket (%SebekV2Sock.command%)")
|
|
|
|
class MGCP(Packet):
|
|
name = "MGCP"
|
|
longname = "Media Gateway Control Protocol"
|
|
fields_desc = [ StrStopField("verb","AUEP"," ", -1),
|
|
StrFixedLenField("sep1"," ",1),
|
|
StrStopField("transaction_id","1234567"," ", -1),
|
|
StrFixedLenField("sep2"," ",1),
|
|
StrStopField("endpoint","dummy@dummy.net"," ", -1),
|
|
StrFixedLenField("sep3"," ",1),
|
|
StrStopField("version","MGCP 1.0 NCS 1.0","\x0a", -1),
|
|
StrFixedLenField("sep4","\x0a",1),
|
|
]
|
|
|
|
|
|
#class MGCP(Packet):
|
|
# name = "MGCP"
|
|
# longname = "Media Gateway Control Protocol"
|
|
# fields_desc = [ ByteEnumField("type",0, ["request","response","others"]),
|
|
# ByteField("code0",0),
|
|
# ByteField("code1",0),
|
|
# ByteField("code2",0),
|
|
# ByteField("code3",0),
|
|
# ByteField("code4",0),
|
|
# IntField("trasid",0),
|
|
# IntField("req_time",0),
|
|
# ByteField("is_duplicate",0),
|
|
# ByteField("req_available",0) ]
|
|
#
|
|
class GPRS(Packet):
|
|
name = "GPRSdummy"
|
|
fields_desc = [
|
|
StrStopField("dummy","","\x65\x00\x00",1)
|
|
]
|
|
|
|
|
|
class HCI_Hdr(Packet):
|
|
name = "HCI header"
|
|
fields_desc = [ ByteEnumField("type",2,{1:"command",2:"ACLdata",3:"SCOdata",4:"event",5:"vendor"}),]
|
|
|
|
def mysummary(self):
|
|
return self.sprintf("HCI %type%")
|
|
|
|
class HCI_ACL_Hdr(Packet):
|
|
name = "HCI ACL header"
|
|
fields_desc = [ ByteField("handle",0), # Actually, handle is 12 bits and flags is 4.
|
|
ByteField("flags",0), # I wait to write a LEBitField
|
|
LEShortField("len",None), ]
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
if self.len is None:
|
|
l = len(p)-4
|
|
p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:]
|
|
return p
|
|
|
|
|
|
class L2CAP_Hdr(Packet):
|
|
name = "L2CAP header"
|
|
fields_desc = [ LEShortField("len",None),
|
|
LEShortEnumField("cid",0,{1:"control"}),]
|
|
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
if self.len is None:
|
|
l = len(p)-4
|
|
p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:]
|
|
return p
|
|
|
|
|
|
|
|
class L2CAP_CmdHdr(Packet):
|
|
name = "L2CAP command header"
|
|
fields_desc = [
|
|
ByteEnumField("code",8,{1:"rej",2:"conn_req",3:"conn_resp",
|
|
4:"conf_req",5:"conf_resp",6:"disconn_req",
|
|
7:"disconn_resp",8:"echo_req",9:"echo_resp",
|
|
10:"info_req",11:"info_resp"}),
|
|
ByteField("id",0),
|
|
LEShortField("len",None) ]
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
if self.len is None:
|
|
l = len(p)-4
|
|
p = p[:2]+chr(l&0xff)+chr((l>>8)&0xff)+p[4:]
|
|
return p
|
|
def answers(self, other):
|
|
if other.id == self.id:
|
|
if self.code == 1:
|
|
return 1
|
|
if other.code in [2,4,6,8,10] and self.code == other.code+1:
|
|
if other.code == 8:
|
|
return 1
|
|
return self.payload.answers(other.payload)
|
|
return 0
|
|
|
|
class L2CAP_ConnReq(Packet):
|
|
name = "L2CAP Conn Req"
|
|
fields_desc = [ LEShortEnumField("psm",0,{1:"SDP",3:"RFCOMM",5:"telephony control"}),
|
|
LEShortField("scid",0),
|
|
]
|
|
|
|
class L2CAP_ConnResp(Packet):
|
|
name = "L2CAP Conn Resp"
|
|
fields_desc = [ LEShortField("dcid",0),
|
|
LEShortField("scid",0),
|
|
LEShortEnumField("result",0,["no_info","authen_pend","author_pend"]),
|
|
LEShortEnumField("status",0,["success","pend","bad_psm",
|
|
"cr_sec_block","cr_no_mem"]),
|
|
]
|
|
def answers(self, other):
|
|
return self.scid == other.scid
|
|
|
|
class L2CAP_CmdRej(Packet):
|
|
name = "L2CAP Command Rej"
|
|
fields_desc = [ LEShortField("reason",0),
|
|
]
|
|
|
|
|
|
class L2CAP_ConfReq(Packet):
|
|
name = "L2CAP Conf Req"
|
|
fields_desc = [ LEShortField("dcid",0),
|
|
LEShortField("flags",0),
|
|
]
|
|
|
|
class L2CAP_ConfResp(Packet):
|
|
name = "L2CAP Conf Resp"
|
|
fields_desc = [ LEShortField("scid",0),
|
|
LEShortField("flags",0),
|
|
LEShortEnumField("result",0,["success","unaccept","reject","unknown"]),
|
|
]
|
|
def answers(self, other):
|
|
return self.scid == other.scid
|
|
|
|
|
|
class L2CAP_DisconnReq(Packet):
|
|
name = "L2CAP Disconn Req"
|
|
fields_desc = [ LEShortField("dcid",0),
|
|
LEShortField("scid",0), ]
|
|
|
|
class L2CAP_DisconnResp(Packet):
|
|
name = "L2CAP Disconn Resp"
|
|
fields_desc = [ LEShortField("dcid",0),
|
|
LEShortField("scid",0), ]
|
|
def answers(self, other):
|
|
return self.scid == other.scid
|
|
|
|
|
|
|
|
class L2CAP_InfoReq(Packet):
|
|
name = "L2CAP Info Req"
|
|
fields_desc = [ LEShortEnumField("type",0,{1:"CL_MTU",2:"FEAT_MASK"}),
|
|
StrField("data","")
|
|
]
|
|
|
|
|
|
class L2CAP_InfoResp(Packet):
|
|
name = "L2CAP Info Resp"
|
|
fields_desc = [ LEShortField("type",0),
|
|
LEShortEnumField("result",0,["success","not_supp"]),
|
|
StrField("data",""), ]
|
|
def answers(self, other):
|
|
return self.type == other.type
|
|
|
|
|
|
|
|
|
|
class NetBIOS_DS(Packet):
|
|
name = "NetBIOS datagram service"
|
|
fields_desc = [
|
|
ByteEnumField("type",17, {17:"direct_group"}),
|
|
ByteField("flags",0),
|
|
XShortField("id",0),
|
|
IPField("src","127.0.0.1"),
|
|
ShortField("sport",138),
|
|
ShortField("len",None),
|
|
ShortField("ofs",0),
|
|
NetBIOSNameField("srcname",""),
|
|
NetBIOSNameField("dstname",""),
|
|
]
|
|
def post_build(self, p, pay):
|
|
p += pay
|
|
if self.len is None:
|
|
l = len(p)-14
|
|
p = p[:10]+struct.pack("!H", l)+p[12:]
|
|
return p
|
|
|
|
# ShortField("length",0),
|
|
# ShortField("Delimitor",0),
|
|
# ByteField("command",0),
|
|
# ByteField("data1",0),
|
|
# ShortField("data2",0),
|
|
# ShortField("XMIt",0),
|
|
# ShortField("RSPCor",0),
|
|
# StrFixedLenField("dest","",16),
|
|
# StrFixedLenField("source","",16),
|
|
#
|
|
# ]
|
|
#
|
|
|
|
# IR
|
|
|
|
class IrLAPHead(Packet):
|
|
name = "IrDA Link Access Protocol Header"
|
|
fields_desc = [ XBitField("Address", 0x7f, 7),
|
|
BitEnumField("Type", 1, 1, {"Response":0,
|
|
"Command":1})]
|
|
|
|
class IrLAPCommand(Packet):
|
|
name = "IrDA Link Access Protocol Command"
|
|
fields_desc = [ XByteField("Control", 0),
|
|
XByteField("Format identifier", 0),
|
|
XIntField("Source address", 0),
|
|
XIntField("Destination address", 0xffffffffL),
|
|
XByteField("Discovery flags", 0x1),
|
|
ByteEnumField("Slot number", 255, {"final":255}),
|
|
XByteField("Version", 0)]
|
|
|
|
|
|
class IrLMP(Packet):
|
|
name = "IrDA Link Management Protocol"
|
|
fields_desc = [ XShortField("Service hints", 0),
|
|
XByteField("Character set", 0),
|
|
StrField("Device name", "") ]
|
|
|
|
|
|
#NetBIOS
|
|
|
|
|
|
# Name Query Request
|
|
# Node Status Request
|
|
class NBNSQueryRequest(Packet):
|
|
name="NBNS query request"
|
|
fields_desc = [ShortField("NAME_TRN_ID",0),
|
|
ShortField("FLAGS", 0x0110),
|
|
ShortField("QDCOUNT",1),
|
|
ShortField("ANCOUNT",0),
|
|
ShortField("NSCOUNT",0),
|
|
ShortField("ARCOUNT",0),
|
|
NetBIOSNameField("QUESTION_NAME","windows"),
|
|
ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}),
|
|
ByteField("NULL",0),
|
|
ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}),
|
|
ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"})]
|
|
|
|
# Name Registration Request
|
|
# Name Refresh Request
|
|
# Name Release Request or Demand
|
|
class NBNSRequest(Packet):
|
|
name="NBNS request"
|
|
fields_desc = [ShortField("NAME_TRN_ID",0),
|
|
ShortField("FLAGS", 0x2910),
|
|
ShortField("QDCOUNT",1),
|
|
ShortField("ANCOUNT",0),
|
|
ShortField("NSCOUNT",0),
|
|
ShortField("ARCOUNT",1),
|
|
NetBIOSNameField("QUESTION_NAME","windows"),
|
|
ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}),
|
|
ByteField("NULL",0),
|
|
ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}),
|
|
ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}),
|
|
ShortEnumField("RR_NAME",0xC00C,{0xC00C:"Label String Pointer to QUESTION_NAME"}),
|
|
ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}),
|
|
ShortEnumField("RR_CLASS",1,{1:"INTERNET"}),
|
|
IntField("TTL", 0),
|
|
ShortField("RDLENGTH", 6),
|
|
BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}),
|
|
BitEnumField("OWNER NODE TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}),
|
|
BitEnumField("UNUSED",0,13,{0:"Unused"}),
|
|
IPField("NB_ADDRESS", "127.0.0.1")]
|
|
|
|
# Name Query Response
|
|
# Name Registration Response
|
|
class NBNSQueryResponse(Packet):
|
|
name="NBNS query response"
|
|
fields_desc = [ShortField("NAME_TRN_ID",0),
|
|
ShortField("FLAGS", 0x8500),
|
|
ShortField("QDCOUNT",0),
|
|
ShortField("ANCOUNT",1),
|
|
ShortField("NSCOUNT",0),
|
|
ShortField("ARCOUNT",0),
|
|
NetBIOSNameField("RR_NAME","windows"),
|
|
ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}),
|
|
ByteField("NULL",0),
|
|
ShortEnumField("QUESTION_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}),
|
|
ShortEnumField("QUESTION_CLASS",1,{1:"INTERNET"}),
|
|
IntField("TTL", 0x493e0),
|
|
ShortField("RDLENGTH", 6),
|
|
ShortField("NB_FLAGS", 0),
|
|
IPField("NB_ADDRESS", "127.0.0.1")]
|
|
|
|
# Name Query Response (negative)
|
|
# Name Release Response
|
|
class NBNSQueryResponseNegative(Packet):
|
|
name="NBNS query response (negative)"
|
|
fields_desc = [ShortField("NAME_TRN_ID",0),
|
|
ShortField("FLAGS", 0x8506),
|
|
ShortField("QDCOUNT",0),
|
|
ShortField("ANCOUNT",1),
|
|
ShortField("NSCOUNT",0),
|
|
ShortField("ARCOUNT",0),
|
|
NetBIOSNameField("RR_NAME","windows"),
|
|
ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}),
|
|
ByteField("NULL",0),
|
|
ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}),
|
|
ShortEnumField("RR_CLASS",1,{1:"INTERNET"}),
|
|
IntField("TTL",0),
|
|
ShortField("RDLENGTH",6),
|
|
BitEnumField("G",0,1,{0:"Unique name",1:"Group name"}),
|
|
BitEnumField("OWNER NODE TYPE",00,2,{00:"B node",01:"P node",02:"M node",03:"H node"}),
|
|
BitEnumField("UNUSED",0,13,{0:"Unused"}),
|
|
IPField("NB_ADDRESS", "127.0.0.1")]
|
|
|
|
# Node Status Response
|
|
class NBNSNodeStatusResponse(Packet):
|
|
name="NBNS Node Status Response"
|
|
fields_desc = [ShortField("NAME_TRN_ID",0),
|
|
ShortField("FLAGS", 0x8500),
|
|
ShortField("QDCOUNT",0),
|
|
ShortField("ANCOUNT",1),
|
|
ShortField("NSCOUNT",0),
|
|
ShortField("ARCOUNT",0),
|
|
NetBIOSNameField("RR_NAME","windows"),
|
|
ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}),
|
|
ByteField("NULL",0),
|
|
ShortEnumField("RR_TYPE",0x21, {0x20:"NB",0x21:"NBSTAT"}),
|
|
ShortEnumField("RR_CLASS",1,{1:"INTERNET"}),
|
|
IntField("TTL",0),
|
|
ShortField("RDLENGTH",83),
|
|
ByteField("NUM_NAMES",1)]
|
|
|
|
# Service for Node Status Response
|
|
class NBNSNodeStatusResponseService(Packet):
|
|
name="NBNS Node Status Response Service"
|
|
fields_desc = [StrFixedLenField("NETBIOS_NAME","WINDOWS ",15),
|
|
ByteEnumField("SUFFIX",0,{0:"workstation",0x03:"messenger service",0x20:"file server service",0x1b:"domain master browser",0x1c:"domain controller", 0x1e:"browser election service"}),
|
|
ByteField("NAME_FLAGS",0x4),
|
|
ByteEnumField("UNUSED",0,{0:"unused"})]
|
|
|
|
# End of Node Status Response packet
|
|
class NBNSNodeStatusResponseEnd(Packet):
|
|
name="NBNS Node Status Response"
|
|
fields_desc = [SourceMACField("MAC_ADDRESS"),
|
|
BitField("STATISTICS",0,57*8)]
|
|
|
|
# Wait for Acknowledgement Response
|
|
class NBNSWackResponse(Packet):
|
|
name="NBNS Wait for Acknowledgement Response"
|
|
fields_desc = [ShortField("NAME_TRN_ID",0),
|
|
ShortField("FLAGS", 0xBC07),
|
|
ShortField("QDCOUNT",0),
|
|
ShortField("ANCOUNT",1),
|
|
ShortField("NSCOUNT",0),
|
|
ShortField("ARCOUNT",0),
|
|
NetBIOSNameField("RR_NAME","windows"),
|
|
ShortEnumField("SUFFIX",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}),
|
|
ByteField("NULL",0),
|
|
ShortEnumField("RR_TYPE",0x20, {0x20:"NB",0x21:"NBSTAT"}),
|
|
ShortEnumField("RR_CLASS",1,{1:"INTERNET"}),
|
|
IntField("TTL", 2),
|
|
ShortField("RDLENGTH",2),
|
|
BitField("RDATA",10512,16)] #10512=0010100100010000
|
|
|
|
class NBTDatagram(Packet):
|
|
name="NBT Datagram Packet"
|
|
fields_desc= [ByteField("Type", 0x10),
|
|
ByteField("Flags", 0x02),
|
|
ShortField("ID", 0),
|
|
IPField("SourceIP", "127.0.0.1"),
|
|
ShortField("SourcePort", 138),
|
|
ShortField("Length", 272),
|
|
ShortField("Offset", 0),
|
|
NetBIOSNameField("SourceName","windows"),
|
|
ShortEnumField("SUFFIX1",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}),
|
|
ByteField("NULL",0),
|
|
NetBIOSNameField("DestinationName","windows"),
|
|
ShortEnumField("SUFFIX2",0x4141,{0x4141:"workstation",0x4141+0x03:"messenger service",0x4141+0x200:"file server service",0x4141+0x10b:"domain master browser",0x4141+0x10c:"domain controller", 0x4141+0x10e:"browser election service"}),
|
|
ByteField("NULL",0)]
|
|
|
|
|
|
class NBTSession(Packet):
|
|
name="NBT Session Packet"
|
|
fields_desc= [ByteEnumField("TYPE",0,{0x00:"Session Message",0x81:"Session Request",0x82:"Positive Session Response",0x83:"Negative Session Response",0x84:"Retarget Session Response",0x85:"Session Keepalive"}),
|
|
BitField("RESERVED",0x00,7),
|
|
BitField("LENGTH",0,17)]
|
|
|
|
|
|
# SMB NetLogon Response Header
|
|
class SMBNetlogon_Protocol_Response_Header(Packet):
|
|
name="SMBNetlogon Protocol Response Header"
|
|
fields_desc = [StrFixedLenField("Start","\xffSMB",4),
|
|
ByteEnumField("Command",0x25,{0x25:"Trans"}),
|
|
ByteField("Error_Class",0x02),
|
|
ByteField("Reserved",0),
|
|
LEShortField("Error_code",4),
|
|
ByteField("Flags",0),
|
|
LEShortField("Flags2",0x0000),
|
|
LEShortField("PIDHigh",0x0000),
|
|
LELongField("Signature",0x0),
|
|
LEShortField("Unused",0x0),
|
|
LEShortField("TID",0),
|
|
LEShortField("PID",0),
|
|
LEShortField("UID",0),
|
|
LEShortField("MID",0),
|
|
ByteField("WordCount",17),
|
|
LEShortField("TotalParamCount",0),
|
|
LEShortField("TotalDataCount",112),
|
|
LEShortField("MaxParamCount",0),
|
|
LEShortField("MaxDataCount",0),
|
|
ByteField("MaxSetupCount",0),
|
|
ByteField("unused2",0),
|
|
LEShortField("Flags3",0),
|
|
ByteField("TimeOut1",0xe8),
|
|
ByteField("TimeOut2",0x03),
|
|
LEShortField("unused3",0),
|
|
LEShortField("unused4",0),
|
|
LEShortField("ParamCount2",0),
|
|
LEShortField("ParamOffset",0),
|
|
LEShortField("DataCount",112),
|
|
LEShortField("DataOffset",92),
|
|
ByteField("SetupCount", 3),
|
|
ByteField("unused5", 0)]
|
|
|
|
# SMB MailSlot Protocol
|
|
class SMBMailSlot(Packet):
|
|
name = "SMB Mail Slot Protocol"
|
|
fields_desc = [LEShortField("opcode", 1),
|
|
LEShortField("priority", 1),
|
|
LEShortField("class", 2),
|
|
LEShortField("size", 135),
|
|
StrNullField("name","\MAILSLOT\NET\GETDC660")]
|
|
|
|
# SMB NetLogon Protocol Response Tail SAM
|
|
class SMBNetlogon_Protocol_Response_Tail_SAM(Packet):
|
|
name = "SMB Netlogon Protocol Response Tail SAM"
|
|
fields_desc = [ByteEnumField("Command", 0x17, {0x12:"SAM logon request", 0x17:"SAM Active directory Response"}),
|
|
ByteField("unused", 0),
|
|
ShortField("Data1", 0),
|
|
ShortField("Data2", 0xfd01),
|
|
ShortField("Data3", 0),
|
|
ShortField("Data4", 0xacde),
|
|
ShortField("Data5", 0x0fe5),
|
|
ShortField("Data6", 0xd10a),
|
|
ShortField("Data7", 0x374c),
|
|
ShortField("Data8", 0x83e2),
|
|
ShortField("Data9", 0x7dd9),
|
|
ShortField("Data10", 0x3a16),
|
|
ShortField("Data11", 0x73ff),
|
|
ByteField("Data12", 0x04),
|
|
StrFixedLenField("Data13", "rmff", 4),
|
|
ByteField("Data14", 0x0),
|
|
ShortField("Data16", 0xc018),
|
|
ByteField("Data18", 0x0a),
|
|
StrFixedLenField("Data20", "rmff-win2k", 10),
|
|
ByteField("Data21", 0xc0),
|
|
ShortField("Data22", 0x18c0),
|
|
ShortField("Data23", 0x180a),
|
|
StrFixedLenField("Data24", "RMFF-WIN2K", 10),
|
|
ShortField("Data25", 0),
|
|
ByteField("Data26", 0x17),
|
|
StrFixedLenField("Data27", "Default-First-Site-Name", 23),
|
|
ShortField("Data28", 0x00c0),
|
|
ShortField("Data29", 0x3c10),
|
|
ShortField("Data30", 0x00c0),
|
|
ShortField("Data31", 0x0200),
|
|
ShortField("Data32", 0x0),
|
|
ShortField("Data33", 0xac14),
|
|
ShortField("Data34", 0x0064),
|
|
ShortField("Data35", 0x0),
|
|
ShortField("Data36", 0x0),
|
|
ShortField("Data37", 0x0),
|
|
ShortField("Data38", 0x0),
|
|
ShortField("Data39", 0x0d00),
|
|
ShortField("Data40", 0x0),
|
|
ShortField("Data41", 0xffff)]
|
|
|
|
# SMB NetLogon Protocol Response Tail LM2.0
|
|
class SMBNetlogon_Protocol_Response_Tail_LM20(Packet):
|
|
name = "SMB Netlogon Protocol Response Tail LM20"
|
|
fields_desc = [ByteEnumField("Command",0x06,{0x06:"LM 2.0 Response to logon request"}),
|
|
ByteField("unused", 0),
|
|
StrFixedLenField("DblSlash", "\\\\", 2),
|
|
StrNullField("ServerName","WIN"),
|
|
LEShortField("LM20Token", 0xffff)]
|
|
|
|
# SMBNegociate Protocol Request Header
|
|
class SMBNegociate_Protocol_Request_Header(Packet):
|
|
name="SMBNegociate Protocol Request Header"
|
|
fields_desc = [StrFixedLenField("Start","\xffSMB",4),
|
|
ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}),
|
|
ByteField("Error_Class",0),
|
|
ByteField("Reserved",0),
|
|
LEShortField("Error_code",0),
|
|
ByteField("Flags",0x18),
|
|
LEShortField("Flags2",0x0000),
|
|
LEShortField("PIDHigh",0x0000),
|
|
LELongField("Signature",0x0),
|
|
LEShortField("Unused",0x0),
|
|
LEShortField("TID",0),
|
|
LEShortField("PID",1),
|
|
LEShortField("UID",0),
|
|
LEShortField("MID",2),
|
|
ByteField("WordCount",0),
|
|
LEShortField("ByteCount",12)]
|
|
|
|
# SMB Negociate Protocol Request Tail
|
|
class SMBNegociate_Protocol_Request_Tail(Packet):
|
|
name="SMB Negociate Protocol Request Tail"
|
|
fields_desc=[ByteField("BufferFormat",0x02),
|
|
StrNullField("BufferData","NT LM 0.12")]
|
|
|
|
# SMBNegociate Protocol Response Advanced Security
|
|
class SMBNegociate_Protocol_Response_Advanced_Security(Packet):
|
|
name="SMBNegociate Protocol Response Advanced Security"
|
|
fields_desc = [StrFixedLenField("Start","\xffSMB",4),
|
|
ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}),
|
|
ByteField("Error_Class",0),
|
|
ByteField("Reserved",0),
|
|
LEShortField("Error_Code",0),
|
|
ByteField("Flags",0x98),
|
|
LEShortField("Flags2",0x0000),
|
|
LEShortField("PIDHigh",0x0000),
|
|
LELongField("Signature",0x0),
|
|
LEShortField("Unused",0x0),
|
|
LEShortField("TID",0),
|
|
LEShortField("PID",1),
|
|
LEShortField("UID",0),
|
|
LEShortField("MID",2),
|
|
ByteField("WordCount",17),
|
|
LEShortField("DialectIndex",7),
|
|
ByteField("SecurityMode",0x03),
|
|
LEShortField("MaxMpxCount",50),
|
|
LEShortField("MaxNumberVC",1),
|
|
LEIntField("MaxBufferSize",16144),
|
|
LEIntField("MaxRawSize",65536),
|
|
LEIntField("SessionKey",0x0000),
|
|
LEShortField("ServerCapabilities",0xf3f9),
|
|
BitField("UnixExtensions",0,1),
|
|
BitField("Reserved2",0,7),
|
|
BitField("ExtendedSecurity",1,1),
|
|
BitField("CompBulk",0,2),
|
|
BitField("Reserved3",0,5),
|
|
# There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94.
|
|
LEIntField("ServerTimeHigh",0xD6228000L),
|
|
LEIntField("ServerTimeLow",0x1C4EF94),
|
|
LEShortField("ServerTimeZone",0x3c),
|
|
ByteField("EncryptionKeyLength",0),
|
|
LEFieldLenField("ByteCount", None, "SecurityBlob"),
|
|
BitField("GUID",0,128),
|
|
StrLenField("SecurityBlob", "", "ByteCount",shift=-16)]
|
|
|
|
# SMBNegociate Protocol Response No Security
|
|
# When using no security, with EncryptionKeyLength=8, you must have an EncryptionKey before the DomainName
|
|
class SMBNegociate_Protocol_Response_No_Security(Packet):
|
|
name="SMBNegociate Protocol Response No Security"
|
|
fields_desc = [StrFixedLenField("Start","\xffSMB",4),
|
|
ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}),
|
|
ByteField("Error_Class",0),
|
|
ByteField("Reserved",0),
|
|
LEShortField("Error_Code",0),
|
|
ByteField("Flags",0x98),
|
|
LEShortField("Flags2",0x0000),
|
|
LEShortField("PIDHigh",0x0000),
|
|
LELongField("Signature",0x0),
|
|
LEShortField("Unused",0x0),
|
|
LEShortField("TID",0),
|
|
LEShortField("PID",1),
|
|
LEShortField("UID",0),
|
|
LEShortField("MID",2),
|
|
ByteField("WordCount",17),
|
|
LEShortField("DialectIndex",7),
|
|
ByteField("SecurityMode",0x03),
|
|
LEShortField("MaxMpxCount",50),
|
|
LEShortField("MaxNumberVC",1),
|
|
LEIntField("MaxBufferSize",16144),
|
|
LEIntField("MaxRawSize",65536),
|
|
LEIntField("SessionKey",0x0000),
|
|
LEShortField("ServerCapabilities",0xf3f9),
|
|
BitField("UnixExtensions",0,1),
|
|
BitField("Reserved2",0,7),
|
|
BitField("ExtendedSecurity",0,1),
|
|
FlagsField("CompBulk",0,2,"CB"),
|
|
BitField("Reserved3",0,5),
|
|
# There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94.
|
|
LEIntField("ServerTimeHigh",0xD6228000L),
|
|
LEIntField("ServerTimeLow",0x1C4EF94),
|
|
LEShortField("ServerTimeZone",0x3c),
|
|
ByteField("EncryptionKeyLength",8),
|
|
LEShortField("ByteCount",24),
|
|
BitField("EncryptionKey",0,64),
|
|
StrNullField("DomainName","WORKGROUP"),
|
|
StrNullField("ServerName","RMFF1")]
|
|
|
|
# SMBNegociate Protocol Response No Security No Key
|
|
class SMBNegociate_Protocol_Response_No_Security_No_Key(Packet):
|
|
namez="SMBNegociate Protocol Response No Security No Key"
|
|
fields_desc = [StrFixedLenField("Start","\xffSMB",4),
|
|
ByteEnumField("Command",0x72,{0x72:"SMB_COM_NEGOTIATE"}),
|
|
ByteField("Error_Class",0),
|
|
ByteField("Reserved",0),
|
|
LEShortField("Error_Code",0),
|
|
ByteField("Flags",0x98),
|
|
LEShortField("Flags2",0x0000),
|
|
LEShortField("PIDHigh",0x0000),
|
|
LELongField("Signature",0x0),
|
|
LEShortField("Unused",0x0),
|
|
LEShortField("TID",0),
|
|
LEShortField("PID",1),
|
|
LEShortField("UID",0),
|
|
LEShortField("MID",2),
|
|
ByteField("WordCount",17),
|
|
LEShortField("DialectIndex",7),
|
|
ByteField("SecurityMode",0x03),
|
|
LEShortField("MaxMpxCount",50),
|
|
LEShortField("MaxNumberVC",1),
|
|
LEIntField("MaxBufferSize",16144),
|
|
LEIntField("MaxRawSize",65536),
|
|
LEIntField("SessionKey",0x0000),
|
|
LEShortField("ServerCapabilities",0xf3f9),
|
|
BitField("UnixExtensions",0,1),
|
|
BitField("Reserved2",0,7),
|
|
BitField("ExtendedSecurity",0,1),
|
|
FlagsField("CompBulk",0,2,"CB"),
|
|
BitField("Reserved3",0,5),
|
|
# There have been 127490112000000000 tenths of micro-seconds between 1st january 1601 and 1st january 2005. 127490112000000000=0x1C4EF94D6228000, so ServerTimeHigh=0xD6228000 and ServerTimeLow=0x1C4EF94.
|
|
LEIntField("ServerTimeHigh",0xD6228000L),
|
|
LEIntField("ServerTimeLow",0x1C4EF94),
|
|
LEShortField("ServerTimeZone",0x3c),
|
|
ByteField("EncryptionKeyLength",0),
|
|
LEShortField("ByteCount",16),
|
|
StrNullField("DomainName","WORKGROUP"),
|
|
StrNullField("ServerName","RMFF1")]
|
|
|
|
# Session Setup AndX Request
|
|
class SMBSession_Setup_AndX_Request(Packet):
|
|
name="Session Setup AndX Request"
|
|
fields_desc=[StrFixedLenField("Start","\xffSMB",4),
|
|
ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}),
|
|
ByteField("Error_Class",0),
|
|
ByteField("Reserved",0),
|
|
LEShortField("Error_Code",0),
|
|
ByteField("Flags",0x18),
|
|
LEShortField("Flags2",0x0001),
|
|
LEShortField("PIDHigh",0x0000),
|
|
LELongField("Signature",0x0),
|
|
LEShortField("Unused",0x0),
|
|
LEShortField("TID",0),
|
|
LEShortField("PID",1),
|
|
LEShortField("UID",0),
|
|
LEShortField("MID",2),
|
|
ByteField("WordCount",13),
|
|
ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}),
|
|
ByteField("Reserved2",0),
|
|
LEShortField("AndXOffset",96),
|
|
LEShortField("MaxBufferS",2920),
|
|
LEShortField("MaxMPXCount",50),
|
|
LEShortField("VCNumber",0),
|
|
LEIntField("SessionKey",0),
|
|
LEFieldLenField("ANSIPasswordLength",None,"ANSIPassword"),
|
|
LEShortField("UnicodePasswordLength",0),
|
|
LEIntField("Reserved3",0),
|
|
LEShortField("ServerCapabilities",0x05),
|
|
BitField("UnixExtensions",0,1),
|
|
BitField("Reserved4",0,7),
|
|
BitField("ExtendedSecurity",0,1),
|
|
BitField("CompBulk",0,2),
|
|
BitField("Reserved5",0,5),
|
|
LEShortField("ByteCount",35),
|
|
StrLenField("ANSIPassword", "Pass","ANSIPasswordLength"),
|
|
StrNullField("Account","GUEST"),
|
|
StrNullField("PrimaryDomain", ""),
|
|
StrNullField("NativeOS","Windows 4.0"),
|
|
StrNullField("NativeLanManager","Windows 4.0"),
|
|
ByteField("WordCount2",4),
|
|
ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}),
|
|
ByteField("Reserved6",0),
|
|
LEShortField("AndXOffset2",0),
|
|
LEShortField("Flags3",0x2),
|
|
LEShortField("PasswordLength",0x1),
|
|
LEShortField("ByteCount2",18),
|
|
ByteField("Password",0),
|
|
StrNullField("Path","\\\\WIN2K\\IPC$"),
|
|
StrNullField("Service","IPC")]
|
|
|
|
# Session Setup AndX Response
|
|
class SMBSession_Setup_AndX_Response(Packet):
|
|
name="Session Setup AndX Response"
|
|
fields_desc=[StrFixedLenField("Start","\xffSMB",4),
|
|
ByteEnumField("Command",0x73,{0x73:"SMB_COM_SESSION_SETUP_ANDX"}),
|
|
ByteField("Error_Class",0),
|
|
ByteField("Reserved",0),
|
|
LEShortField("Error_Code",0),
|
|
ByteField("Flags",0x90),
|
|
LEShortField("Flags2",0x1001),
|
|
LEShortField("PIDHigh",0x0000),
|
|
LELongField("Signature",0x0),
|
|
LEShortField("Unused",0x0),
|
|
LEShortField("TID",0),
|
|
LEShortField("PID",1),
|
|
LEShortField("UID",0),
|
|
LEShortField("MID",2),
|
|
ByteField("WordCount",3),
|
|
ByteEnumField("AndXCommand",0x75,{0x75:"SMB_COM_TREE_CONNECT_ANDX"}),
|
|
ByteField("Reserved2",0),
|
|
LEShortField("AndXOffset",66),
|
|
LEShortField("Action",0),
|
|
LEShortField("ByteCount",25),
|
|
StrNullField("NativeOS","Windows 4.0"),
|
|
StrNullField("NativeLanManager","Windows 4.0"),
|
|
StrNullField("PrimaryDomain",""),
|
|
ByteField("WordCount2",3),
|
|
ByteEnumField("AndXCommand2",0xFF,{0xFF:"SMB_COM_NONE"}),
|
|
ByteField("Reserved3",0),
|
|
LEShortField("AndXOffset2",80),
|
|
LEShortField("OptionalSupport",0x01),
|
|
LEShortField("ByteCount2",5),
|
|
StrNullField("Service","IPC"),
|
|
StrNullField("NativeFileSystem","")]
|
|
|
|
class MobileIP(Packet):
|
|
name = "Mobile IP (RFC3344)"
|
|
fields_desc = [ ByteEnumField("type", 1, {1:"RRQ", 3:"RRP"}) ]
|
|
|
|
class MobileIPRRQ(Packet):
|
|
name = "Mobile IP Registration Request (RFC3344)"
|
|
fields_desc = [ XByteField("flags", 0),
|
|
ShortField("lifetime", 180),
|
|
IPField("homeaddr", "0.0.0.0"),
|
|
IPField("haaddr", "0.0.0.0"),
|
|
IPField("coaddr", "0.0.0.0"),
|
|
Field("id", "", "64s") ]
|
|
|
|
class MobileIPRRP(Packet):
|
|
name = "Mobile IP Registration Reply (RFC3344)"
|
|
fields_desc = [ ByteField("code", 0),
|
|
ShortField("lifetime", 180),
|
|
IPField("homeaddr", "0.0.0.0"),
|
|
IPField("haaddr", "0.0.0.0"),
|
|
Field("id", "", "64s") ]
|
|
|
|
class MobileIPTunnelData(Packet):
|
|
name = "Mobile IP Tunnel Data Message (RFC3519)"
|
|
fields_desc = [ ByteField("nexthdr", 4),
|
|
ShortField("res", 0) ]
|
|
|
|
|
|
# Cisco Netflow Protocol version 1
|
|
class NetflowHeader(Packet):
|
|
name = "Netflow Header"
|
|
fields_desc = [ ShortField("version", 1) ]
|
|
|
|
class NetflowHeaderV1(Packet):
|
|
name = "Netflow Header V1"
|
|
fields_desc = [ ShortField("count", 0),
|
|
IntField("sysUptime", 0),
|
|
IntField("unixSecs", 0),
|
|
IntField("unixNanoSeconds", 0) ]
|
|
|
|
|
|
class NetflowRecordV1(Packet):
|
|
name = "Netflow Record"
|
|
fields_desc = [ IPField("ipsrc", "0.0.0.0"),
|
|
IPField("ipdst", "0.0.0.0"),
|
|
IPField("nexthop", "0.0.0.0"),
|
|
ShortField("inputIfIndex", 0),
|
|
ShortField("outpuIfIndex", 0),
|
|
IntField("dpkts", 0),
|
|
IntField("dbytes", 0),
|
|
IntField("starttime", 0),
|
|
IntField("endtime", 0),
|
|
ShortField("srcport", 0),
|
|
ShortField("dstport", 0),
|
|
ShortField("padding", 0),
|
|
ByteField("proto", 0),
|
|
ByteField("tos", 0),
|
|
IntField("padding1", 0),
|
|
IntField("padding2", 0) ]
|
|
|
|
|
|
|
|
#################
|
|
## Bind layers ##
|
|
#################
|
|
|
|
|
|
def bind_bottom_up(lower, upper, __fval=None, **fval):
|
|
if __fval is not None:
|
|
fval.update(__fval)
|
|
lower.payload_guess = lower.payload_guess[:]
|
|
lower.payload_guess.append((fval, upper))
|
|
|
|
|
|
def bind_top_down(lower, upper, __fval=None, **fval):
|
|
if __fval is not None:
|
|
fval.update(__fval)
|
|
upper.overload_fields = upper.overload_fields.copy()
|
|
upper.overload_fields[lower] = fval
|
|
|
|
def bind_layers(lower, upper, __fval=None, **fval):
|
|
if __fval is not None:
|
|
fval.update(__fval)
|
|
bind_top_down(lower, upper, **fval)
|
|
bind_bottom_up(lower, upper, **fval)
|
|
|
|
def split_bottom_up(lower, upper, __fval=None, **fval):
|
|
if __fval is not None:
|
|
fval.update(__fval)
|
|
def do_filter((f,u),upper=upper,fval=fval):
|
|
if u != upper:
|
|
return True
|
|
for k in fval:
|
|
if k not in f or f[k] != fval[k]:
|
|
return True
|
|
return False
|
|
lower.payload_guess = filter(do_filter, lower.payload_guess)
|
|
|
|
def split_top_down(lower, upper, __fval=None, **fval):
|
|
if __fval is not None:
|
|
fval.update(__fval)
|
|
if lower in upper.overload_fields:
|
|
ofval = upper.overload_fields[lower]
|
|
for k in fval:
|
|
if k not in ofval or ofval[k] != fval[k]:
|
|
return
|
|
upper.overload_fields = upper.overload_fields.copy()
|
|
del(upper.overload_fields[lower])
|
|
|
|
def split_layers(lower, upper, __fval=None, **fval):
|
|
if __fval is not None:
|
|
fval.update(__fval)
|
|
split_bottom_up(lower, upper, **fval)
|
|
split_top_down(lower, upper, **fval)
|
|
|
|
layer_bonds = [ ( Dot3, LLC, { } ),
|
|
( GPRS, IP, { } ),
|
|
( PrismHeader, Dot11, { }),
|
|
( Dot11, LLC, { "type" : 2 } ),
|
|
( PPP, IP, { "proto" : 0x0021 } ),
|
|
( Ether, LLC, { "type" : 0x007a } ),
|
|
( Ether, Dot1Q, { "type" : 0x8100 } ),
|
|
( Ether, Ether, { "type" : 0x0001 } ),
|
|
( Ether, ARP, { "type" : 0x0806 } ),
|
|
( Ether, IP, { "type" : 0x0800 } ),
|
|
( Ether, EAPOL, { "type" : 0x888e } ),
|
|
( Ether, EAPOL, { "type" : 0x888e, "dst" : "01:80:c2:00:00:03" } ),
|
|
( Ether, PPPoED, { "type" : 0x8863 } ),
|
|
( Ether, PPPoE, { "type" : 0x8864 } ),
|
|
( CookedLinux, LLC, { "proto" : 0x007a } ),
|
|
( CookedLinux, Dot1Q, { "proto" : 0x8100 } ),
|
|
( CookedLinux, Ether, { "proto" : 0x0001 } ),
|
|
( CookedLinux, ARP, { "proto" : 0x0806 } ),
|
|
( CookedLinux, IP, { "proto" : 0x0800 } ),
|
|
( CookedLinux, EAPOL, { "proto" : 0x888e } ),
|
|
( CookedLinux, PPPoED, { "proto" : 0x8863 } ),
|
|
( CookedLinux, PPPoE, { "proto" : 0x8864 } ),
|
|
( GRE, LLC, { "proto" : 0x007a } ),
|
|
( GRE, Dot1Q, { "proto" : 0x8100 } ),
|
|
( GRE, Ether, { "proto" : 0x0001 } ),
|
|
( GRE, ARP, { "proto" : 0x0806 } ),
|
|
( GRE, IP, { "proto" : 0x0800 } ),
|
|
( GRE, EAPOL, { "proto" : 0x888e } ),
|
|
( PPPoE, PPP, { "code" : 0x00 } ),
|
|
( EAPOL, EAP, { "type" : EAPOL.EAP_PACKET } ),
|
|
( LLC, STP, { "dsap" : 0x42 , "ssap" : 0x42, "ctrl":3 } ),
|
|
( LLC, SNAP, { "dsap" : 0xAA , "ssap" : 0xAA, "ctrl":3 } ),
|
|
( SNAP, Dot1Q, { "code" : 0x8100 } ),
|
|
( SNAP, Ether, { "code" : 0x0001 } ),
|
|
( SNAP, ARP, { "code" : 0x0806 } ),
|
|
( SNAP, IP, { "code" : 0x0800 } ),
|
|
( SNAP, EAPOL, { "code" : 0x888e } ),
|
|
( IPerror,IPerror, { "frag" : 0, "proto" : socket.IPPROTO_IP } ),
|
|
( IPerror,ICMPerror,{ "frag" : 0, "proto" : socket.IPPROTO_ICMP } ),
|
|
( IPerror,TCPerror, { "frag" : 0, "proto" : socket.IPPROTO_TCP } ),
|
|
( IPerror,UDPerror, { "frag" : 0, "proto" : socket.IPPROTO_UDP } ),
|
|
( IP, IP, { "frag" : 0, "proto" : socket.IPPROTO_IP } ),
|
|
( IP, ICMP, { "frag" : 0, "proto" : socket.IPPROTO_ICMP } ),
|
|
( IP, TCP, { "frag" : 0, "proto" : socket.IPPROTO_TCP } ),
|
|
( IP, UDP, { "frag" : 0, "proto" : socket.IPPROTO_UDP } ),
|
|
( IP, GRE, { "frag" : 0, "proto" : socket.IPPROTO_GRE } ),
|
|
( UDP, MGCP, { "dport" : 2727 } ),
|
|
( UDP, MGCP, { "sport" : 2727 } ),
|
|
( UDP, DNS, { "dport" : 53 } ),
|
|
( UDP, DNS, { "sport" : 53 } ),
|
|
( UDP, ISAKMP, { "sport" : 500, "dport" : 500 } ),
|
|
( UDP, HSRP, { "sport" : 1985, "dport" : 1985} ),
|
|
( UDP, NTP, { "sport" : 123, "dport" : 123 } ),
|
|
( UDP, BOOTP, { "sport" : 68, "dport" : 67 } ),
|
|
( UDP, BOOTP, { "sport" : 67, "dport" : 68 } ),
|
|
( BOOTP, DHCP, { "options" : dhcpmagic } ),
|
|
( UDP, RIP, { "sport" : 520 } ),
|
|
( UDP, RIP, { "dport" : 520 } ),
|
|
( RIP, RIPEntry, { } ),
|
|
( RIPEntry,RIPEntry,{ } ),
|
|
( Dot11, Dot11AssoReq, { "type" : 0, "subtype" : 0 } ),
|
|
( Dot11, Dot11AssoResp, { "type" : 0, "subtype" : 1 } ),
|
|
( Dot11, Dot11ReassoReq, { "type" : 0, "subtype" : 2 } ),
|
|
( Dot11, Dot11ReassoResp, { "type" : 0, "subtype" : 3 } ),
|
|
( Dot11, Dot11ProbeReq, { "type" : 0, "subtype" : 4 } ),
|
|
( Dot11, Dot11ProbeResp, { "type" : 0, "subtype" : 5 } ),
|
|
( Dot11, Dot11Beacon, { "type" : 0, "subtype" : 8 } ),
|
|
( Dot11, Dot11ATIM , { "type" : 0, "subtype" : 9 } ),
|
|
( Dot11, Dot11Disas , { "type" : 0, "subtype" : 10 } ),
|
|
( Dot11, Dot11Auth, { "type" : 0, "subtype" : 11 } ),
|
|
( Dot11, Dot11Deauth, { "type" : 0, "subtype" : 12 } ),
|
|
( Dot11Beacon, Dot11Elt, {} ),
|
|
( Dot11AssoReq, Dot11Elt, {} ),
|
|
( Dot11AssoResp, Dot11Elt, {} ),
|
|
( Dot11ReassoReq, Dot11Elt, {} ),
|
|
( Dot11ReassoResp, Dot11Elt, {} ),
|
|
( Dot11ProbeReq, Dot11Elt, {} ),
|
|
( Dot11ProbeResp, Dot11Elt, {} ),
|
|
( Dot11Auth, Dot11Elt, {} ),
|
|
( Dot11Elt, Dot11Elt, {} ),
|
|
( TCP, Skinny, { "dport": 2000 } ),
|
|
( TCP, Skinny, { "sport": 2000 } ),
|
|
( UDP, SebekHead, { "sport" : 1101 } ),
|
|
( UDP, SebekHead, { "dport" : 1101 } ),
|
|
( UDP, SebekHead, { "sport" : 1101,
|
|
"dport" : 1101 } ),
|
|
( SebekHead, SebekV1, { "version" : 1 } ),
|
|
( SebekHead, SebekV2Sock, { "version" : 2,
|
|
"type" : 2 } ),
|
|
( SebekHead, SebekV2, { "version" : 2 } ),
|
|
( SebekHead, SebekV3Sock, { "version" : 3,
|
|
"type" : 2 } ),
|
|
( SebekHead, SebekV3, { "version" : 3 } ),
|
|
( CookedLinux, IrLAPHead, { "proto" : 0x0017 } ),
|
|
( IrLAPHead, IrLAPCommand, { "Type" : 1} ),
|
|
( IrLAPCommand, IrLMP, {} ),
|
|
(UDP, NBNSQueryRequest, {"dport" : 137 }),
|
|
(UDP, NBNSRequest, {"dport" : 137 }),
|
|
(UDP, NBNSQueryResponse, {"sport" : 137}),
|
|
(UDP, NBNSQueryResponseNegative, {"sport" : 137}),
|
|
(UDP, NBNSNodeStatusResponse, {"sport" : 137}),
|
|
(NBNSNodeStatusResponse, NBNSNodeStatusResponseService, {}),
|
|
(NBNSNodeStatusResponse, NBNSNodeStatusResponseService, {}),
|
|
(NBNSNodeStatusResponseService, NBNSNodeStatusResponseService, {}),
|
|
(NBNSNodeStatusResponseService, NBNSNodeStatusResponseEnd, {}),
|
|
(UDP, NBNSWackResponse, {"sport" : 137}),
|
|
(UDP,NBTDatagram,{ "dport":138}),
|
|
(TCP,NBTSession,{"dport":139}),
|
|
(NBTSession, SMBNegociate_Protocol_Request_Header,{}),
|
|
(SMBNegociate_Protocol_Request_Header,SMBNegociate_Protocol_Request_Tail,{}),
|
|
(SMBNegociate_Protocol_Request_Tail,SMBNegociate_Protocol_Request_Tail,{}),
|
|
(NBTSession, SMBNegociate_Protocol_Response_Advanced_Security,{"ExtendedSecurity":1}),
|
|
(NBTSession, SMBNegociate_Protocol_Response_No_Security,{"ExtendedSecurity":0,"EncryptionKeyLength":8 }),
|
|
(NBTSession, SMBNegociate_Protocol_Response_No_Security_No_Key,{"ExtendedSecurity":0,"EncryptionKeyLength":0 }),
|
|
(NBTSession, SMBSession_Setup_AndX_Request,{}),
|
|
(NBTSession, SMBSession_Setup_AndX_Response,{}),
|
|
|
|
(HCI_Hdr, HCI_ACL_Hdr, {"type":2}),
|
|
(HCI_Hdr, Raw, {}),
|
|
(HCI_ACL_Hdr, L2CAP_Hdr, {}),
|
|
(L2CAP_Hdr, L2CAP_CmdHdr, {"cid":1}),
|
|
(L2CAP_CmdHdr, L2CAP_CmdRej, {"code":1}),
|
|
(L2CAP_CmdHdr, L2CAP_ConnReq, {"code":2}),
|
|
(L2CAP_CmdHdr, L2CAP_ConnResp, {"code":3}),
|
|
(L2CAP_CmdHdr, L2CAP_ConfReq, {"code":4}),
|
|
(L2CAP_CmdHdr, L2CAP_ConfResp, {"code":5}),
|
|
(L2CAP_CmdHdr, L2CAP_DisconnReq, {"code":6}),
|
|
(L2CAP_CmdHdr, L2CAP_DisconnResp, {"code":7}),
|
|
(L2CAP_CmdHdr, L2CAP_InfoReq, {"code":10}),
|
|
(L2CAP_CmdHdr, L2CAP_InfoResp, {"code":11}),
|
|
( UDP, MobileIP, { "sport" : 434 } ),
|
|
( UDP, MobileIP, { "dport" : 434 } ),
|
|
( MobileIP, MobileIPRRQ, { "type" : 1 } ),
|
|
( MobileIP, MobileIPRRP, { "type" : 3 } ),
|
|
( MobileIP, MobileIPTunnelData, { "type" : 4 } ),
|
|
( MobileIPTunnelData, IP, { "nexthdr" : 4 } ),
|
|
|
|
( NetflowHeader, NetflowHeaderV1, { "version" : 1 } ),
|
|
( NetflowHeaderV1, NetflowRecordV1, {} ),
|
|
|
|
]
|
|
|
|
for l in layer_bonds:
|
|
bind_layers(*l)
|
|
del(l)
|
|
|
|
|
|
###################
|
|
## Fragmentation ##
|
|
###################
|
|
|
|
def fragment(pkt, fragsize=1480):
|
|
fragsize = (fragsize+7)/8*8
|
|
pkt = pkt.copy()
|
|
pkt.flags = "MF"
|
|
lst = []
|
|
for p in pkt:
|
|
s = str(p.payload)
|
|
nb = (len(s)+fragsize-1)/fragsize
|
|
for i in range(nb):
|
|
q = p.copy()
|
|
del(q.payload)
|
|
r = Raw(load=s[i*fragsize:(i+1)*fragsize])
|
|
r.overload_fields = p.payload.overload_fields.copy()
|
|
if i == nb-1:
|
|
q.flags=0
|
|
q.frag = i*fragsize/8
|
|
q.add_payload(r)
|
|
lst.append(q)
|
|
return lst
|
|
|
|
|
|
###################
|
|
## Super sockets ##
|
|
###################
|
|
|
|
def Ether_Dot3_Dispatcher(pkt=None, **kargs):
|
|
if type(pkt) is str and len(pkt) >= 14 and struct.unpack("!H", pkt[12:14])[0] <= 1500:
|
|
return Dot3(pkt, **kargs)
|
|
return Ether(pkt, **kargs)
|
|
|
|
# According to libdnet
|
|
LLTypes = { ARPHDR_ETHER : Ether_Dot3_Dispatcher,
|
|
ARPHDR_METRICOM : Ether_Dot3_Dispatcher,
|
|
ARPHDR_LOOPBACK : Ether_Dot3_Dispatcher,
|
|
12 : IP,
|
|
101 : IP,
|
|
801 : Dot11,
|
|
802 : PrismHeader,
|
|
105 : Dot11,
|
|
113 : CookedLinux,
|
|
119 : PrismHeader, # for atheros
|
|
144 : CookedLinux, # called LINUX_IRDA, similar to CookedLinux
|
|
783 : IrLAPHead,
|
|
0xB1E70073L : HCI_Hdr, # I invented this one
|
|
}
|
|
|
|
LLNumTypes = { Ether : ARPHDR_ETHER,
|
|
IP : 12,
|
|
IP : 101,
|
|
Dot11 : 801,
|
|
PrismHeader : 802,
|
|
Dot11 : 105,
|
|
CookedLinux : 113,
|
|
CookedLinux : 144,
|
|
IrLAPHead : 783
|
|
}
|
|
|
|
L3Types = { ETH_P_IP : IP,
|
|
ETH_P_ARP : ARP,
|
|
ETH_P_ALL : IP
|
|
}
|
|
|
|
|
|
|
|
class SuperSocket:
|
|
closed=0
|
|
def __init__(self, family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0):
|
|
self.ins = socket.socket(family, type, proto)
|
|
self.outs = self.ins
|
|
self.promisc=None
|
|
def send(self, x):
|
|
return self.outs.send(str(x))
|
|
def recv(self, x):
|
|
return Raw(self.ins.recv(x))
|
|
def fileno(self):
|
|
return self.ins.fileno()
|
|
def close(self):
|
|
if self.closed:
|
|
return
|
|
self.closed=1
|
|
if self.ins != self.outs:
|
|
if self.outs and self.outs.fileno() != -1:
|
|
self.outs.close()
|
|
if self.ins and self.ins.fileno() != -1:
|
|
self.ins.close()
|
|
def bind_in(self, addr):
|
|
self.ins.bind(addr)
|
|
def bind_out(self, addr):
|
|
self.outs.bind(addr)
|
|
|
|
|
|
class L3RawSocket(SuperSocket):
|
|
def __init__(self, type = ETH_P_IP, filter=None, iface=None, promisc=None, nofilter=0):
|
|
self.outs = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
|
|
self.outs.setsockopt(socket.SOL_IP, socket.IP_HDRINCL, 1)
|
|
self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
|
|
def recv(self, x):
|
|
return Ether(self.ins.recv(x)).payload
|
|
def send(self, x):
|
|
try:
|
|
self.outs.sendto(str(x),(x.dst,0))
|
|
except socket.error,msg:
|
|
log_runtime.error(msg)
|
|
|
|
|
|
|
|
class L3PacketSocket(SuperSocket):
|
|
def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0):
|
|
self.type = type
|
|
self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
|
|
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0)
|
|
if not nofilter:
|
|
if conf.except_filter:
|
|
if filter:
|
|
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
|
|
else:
|
|
filter = "not (%s)" % conf.except_filter
|
|
if filter is not None:
|
|
attach_filter(self.ins, filter)
|
|
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30)
|
|
self.outs = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
|
|
self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30)
|
|
if promisc is None:
|
|
promisc = conf.promisc
|
|
self.promisc = promisc
|
|
if self.promisc:
|
|
if iface is None:
|
|
self.iff = get_if_list()
|
|
else:
|
|
if iface.__class__ is list:
|
|
self.iff = iface
|
|
else:
|
|
self.iff = [iface]
|
|
for i in self.iff:
|
|
set_promisc(self.ins, i)
|
|
def close(self):
|
|
if self.closed:
|
|
return
|
|
self.closed=1
|
|
if self.promisc:
|
|
for i in self.iff:
|
|
set_promisc(self.ins, i, 0)
|
|
SuperSocket.close(self)
|
|
def recv(self, x):
|
|
pkt, sa_ll = self.ins.recvfrom(x)
|
|
if sa_ll[2] == socket.PACKET_OUTGOING:
|
|
return None
|
|
if LLTypes.has_key(sa_ll[3]):
|
|
cls = LLTypes[sa_ll[3]]
|
|
lvl = 2
|
|
elif L3Types.has_key(sa_ll[1]):
|
|
cls = L3Types[sa_ll[1]]
|
|
lvl = 3
|
|
else:
|
|
warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3]))
|
|
cls = Ether
|
|
lvl = 2
|
|
|
|
try:
|
|
pkt = cls(pkt)
|
|
except:
|
|
if conf.debug_dissector:
|
|
raise
|
|
pkt = Raw(pkt)
|
|
if lvl == 2:
|
|
pkt = pkt.payload
|
|
return pkt
|
|
|
|
def send(self, x):
|
|
if isinstance(x, IPv6):
|
|
iff,a,gw = conf.route6.route(x.dst)
|
|
elif hasattr(x,"dst"):
|
|
iff,a,gw = conf.route.route(x.dst)
|
|
else:
|
|
iff = conf.iface
|
|
sdto = (iff, self.type)
|
|
self.outs.bind(sdto)
|
|
sn = self.outs.getsockname()
|
|
ll = lambda x:x
|
|
if sn[3] in (ARPHDR_PPP,ARPHDR_TUN):
|
|
sdto = (iff, ETH_P_IP)
|
|
if LLTypes.has_key(sn[3]):
|
|
ll = lambda x:LLTypes[sn[3]]()/x
|
|
try:
|
|
self.outs.sendto(str(ll(x)), sdto)
|
|
except socket.error,msg:
|
|
if conf.auto_fragment and msg[0] == 90:
|
|
for p in fragment(x):
|
|
self.outs.sendto(str(ll(p)), sdto)
|
|
else:
|
|
raise
|
|
|
|
|
|
|
|
|
|
class L2Socket(SuperSocket):
|
|
def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0):
|
|
if iface is None:
|
|
iface = conf.iface
|
|
self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
|
|
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0)
|
|
if not nofilter:
|
|
if conf.except_filter:
|
|
if filter:
|
|
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
|
|
else:
|
|
filter = "not (%s)" % conf.except_filter
|
|
if filter is not None:
|
|
attach_filter(self.ins, filter)
|
|
self.ins.bind((iface, type))
|
|
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30)
|
|
self.outs = self.ins
|
|
self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30)
|
|
sa_ll = self.outs.getsockname()
|
|
if LLTypes.has_key(sa_ll[3]):
|
|
self.LL = LLTypes[sa_ll[3]]
|
|
elif L3Types.has_key(sa_ll[1]):
|
|
self.LL = L3Types[sa_ll[1]]
|
|
else:
|
|
warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3]))
|
|
self.LL = Ether
|
|
|
|
def recv(self, x):
|
|
pkt, sa_ll = self.ins.recvfrom(x)
|
|
if sa_ll[2] == socket.PACKET_OUTGOING:
|
|
return None
|
|
try:
|
|
q = self.LL(pkt)
|
|
except:
|
|
if conf.debug_dissector:
|
|
raise
|
|
q = Raw(pkt)
|
|
return q
|
|
|
|
|
|
class L2ListenSocket(SuperSocket):
|
|
def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None, nofilter=0):
|
|
self.type = type
|
|
self.outs = None
|
|
self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type))
|
|
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0)
|
|
if iface is not None:
|
|
self.ins.bind((iface, type))
|
|
if not nofilter:
|
|
if conf.except_filter:
|
|
if filter:
|
|
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
|
|
else:
|
|
filter = "not (%s)" % conf.except_filter
|
|
if filter is not None:
|
|
attach_filter(self.ins, filter)
|
|
if promisc is None:
|
|
promisc = conf.sniff_promisc
|
|
self.promisc = promisc
|
|
if iface is None:
|
|
self.iff = get_if_list()
|
|
else:
|
|
if iface.__class__ is list:
|
|
self.iff = iface
|
|
else:
|
|
self.iff = [iface]
|
|
if self.promisc:
|
|
for i in self.iff:
|
|
set_promisc(self.ins, i)
|
|
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30)
|
|
def close(self):
|
|
if self.promisc:
|
|
for i in self.iff:
|
|
set_promisc(self.ins, i, 0)
|
|
SuperSocket.close(self)
|
|
|
|
def recv(self, x):
|
|
pkt, sa_ll = self.ins.recvfrom(x)
|
|
if LLTypes.has_key(sa_ll[3]):
|
|
cls = LLTypes[sa_ll[3]]
|
|
elif L3Types.has_key(sa_ll[1]):
|
|
cls = L3Types[sa_ll[1]]
|
|
else:
|
|
warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using Ethernet" % (sa_ll[0],sa_ll[1],sa_ll[3]))
|
|
cls = Ether
|
|
|
|
try:
|
|
pkt = cls(pkt)
|
|
except:
|
|
if conf.debug_dissector:
|
|
raise
|
|
pkt = Raw(pkt)
|
|
return pkt
|
|
|
|
def send(self, x):
|
|
raise Exception("Can't send anything with L2ListenSocket")
|
|
|
|
|
|
|
|
class L3dnetSocket(SuperSocket):
|
|
def __init__(self, type = ETH_P_ALL, filter=None, promisc=None, iface=None, nofilter=0):
|
|
self.iflist = {}
|
|
self.ins = pcap.pcapObject()
|
|
if iface is None:
|
|
iface = conf.iface
|
|
self.iface = iface
|
|
self.ins.open_live(iface, 1600, 0, 100)
|
|
try:
|
|
ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1))
|
|
except:
|
|
pass
|
|
if nofilter:
|
|
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap
|
|
filter = "ether proto %i" % type
|
|
else:
|
|
filter = None
|
|
else:
|
|
if conf.except_filter:
|
|
if filter:
|
|
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
|
|
else:
|
|
filter = "not (%s)" % conf.except_filter
|
|
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap
|
|
if filter:
|
|
filter = "(ether proto %i) and (%s)" % (type,filter)
|
|
else:
|
|
filter = "ether proto %i" % type
|
|
if filter:
|
|
self.ins.setfilter(filter, 0, 0)
|
|
def send(self, x):
|
|
if isinstance(x, IPv6):
|
|
iff,a,gw = conf.route6.route(x.dst)
|
|
elif hasattr(x,"dst"):
|
|
iff,a,gw = conf.route.route(x.dst)
|
|
else:
|
|
iff = conf.iface
|
|
ifs = self.iflist.get(iff)
|
|
if ifs is None:
|
|
self.iflist[iff] = ifs = dnet.eth(iff)
|
|
ifs.send(str(Ether()/x))
|
|
def recv(self,x=MTU):
|
|
ll = self.ins.datalink()
|
|
if LLTypes.has_key(ll):
|
|
cls = LLTypes[ll]
|
|
else:
|
|
warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll))
|
|
cls = Ether
|
|
|
|
pkt = self.ins.next()
|
|
if pkt is not None:
|
|
pkt = pkt[1]
|
|
if pkt is None:
|
|
return
|
|
|
|
try:
|
|
pkt = cls(pkt)
|
|
except:
|
|
if conf.debug_dissector:
|
|
raise
|
|
pkt = Raw(pkt)
|
|
return pkt.payload
|
|
|
|
def nonblock_recv(self):
|
|
self.ins.setnonblock(1)
|
|
p = self.recv()
|
|
self.ins.setnonblock(0)
|
|
return p
|
|
|
|
def close(self):
|
|
if hasattr(self, "ins"):
|
|
del(self.ins)
|
|
if hasattr(self, "outs"):
|
|
del(self.outs)
|
|
|
|
class L2dnetSocket(SuperSocket):
|
|
def __init__(self, iface = None, type = ETH_P_ALL, filter=None, nofilter=0):
|
|
if iface is None:
|
|
iface = conf.iface
|
|
self.iface = iface
|
|
self.ins = pcap.pcapObject()
|
|
self.ins.open_live(iface, 1600, 0, 100)
|
|
try:
|
|
ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1))
|
|
except:
|
|
pass
|
|
if nofilter:
|
|
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap
|
|
filter = "ether proto %i" % type
|
|
else:
|
|
filter = None
|
|
else:
|
|
if conf.except_filter:
|
|
if filter:
|
|
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
|
|
else:
|
|
filter = "not (%s)" % conf.except_filter
|
|
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap
|
|
if filter:
|
|
filter = "(ether proto %i) and (%s)" % (type,filter)
|
|
else:
|
|
filter = "ether proto %i" % type
|
|
if filter:
|
|
self.ins.setfilter(filter, 0, 0)
|
|
self.outs = dnet.eth(iface)
|
|
def recv(self,x):
|
|
ll = self.ins.datalink()
|
|
if LLTypes.has_key(ll):
|
|
cls = LLTypes[ll]
|
|
else:
|
|
warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll))
|
|
cls = Ether
|
|
|
|
pkt = self.ins.next()
|
|
if pkt is not None:
|
|
pkt = pkt[1]
|
|
if pkt is None:
|
|
return
|
|
|
|
try:
|
|
pkt = cls(pkt)
|
|
except:
|
|
if conf.debug_dissector:
|
|
raise
|
|
pkt = Raw(pkt)
|
|
return pkt
|
|
|
|
def nonblock_recv(self):
|
|
self.ins.setnonblock(1)
|
|
p = self.recv(MTU)
|
|
self.ins.setnonblock(0)
|
|
return p
|
|
|
|
def close(self):
|
|
if hasattr(self, "ins"):
|
|
del(self.ins)
|
|
if hasattr(self, "outs"):
|
|
del(self.outs)
|
|
|
|
|
|
|
|
|
|
|
|
class L2pcapListenSocket(SuperSocket):
|
|
def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None):
|
|
self.type = type
|
|
self.outs = None
|
|
self.ins = pcap.pcapObject()
|
|
self.iface = iface
|
|
if iface is None:
|
|
iface = conf.iface
|
|
if promisc is None:
|
|
promisc = conf.sniff_promisc
|
|
self.promisc = promisc
|
|
self.ins.open_live(iface, 1600, self.promisc, 100)
|
|
try:
|
|
ioctl(self.ins.fileno(),BIOCIMMEDIATE,struct.pack("I",1))
|
|
except:
|
|
pass
|
|
if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given
|
|
if conf.except_filter:
|
|
if filter:
|
|
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
|
|
else:
|
|
filter = "not (%s)" % conf.except_filter
|
|
if filter:
|
|
self.ins.setfilter(filter, 0, 0)
|
|
|
|
def close(self):
|
|
del(self.ins)
|
|
|
|
def recv(self, x):
|
|
ll = self.ins.datalink()
|
|
if LLTypes.has_key(ll):
|
|
cls = LLTypes[ll]
|
|
else:
|
|
warning("Unable to guess datalink type (interface=%s linktype=%i). Using Ethernet" % (self.iface, ll))
|
|
cls = Ether
|
|
|
|
pkt = None
|
|
while pkt is None:
|
|
pkt = self.ins.next()
|
|
if pkt is not None:
|
|
pkt = pkt[1]
|
|
|
|
try:
|
|
pkt = cls(pkt)
|
|
except:
|
|
if conf.debug_dissector:
|
|
raise
|
|
pkt = Raw(pkt)
|
|
return pkt
|
|
|
|
def send(self, x):
|
|
raise Exception("Can't send anything with L2pcapListenSocket")
|
|
|
|
|
|
class SimpleSocket(SuperSocket):
|
|
def __init__(self, sock):
|
|
self.ins = sock
|
|
self.outs = sock
|
|
|
|
|
|
class StreamSocket(SimpleSocket):
|
|
def __init__(self, sock, basecls=Raw):
|
|
SimpleSocket.__init__(self, sock)
|
|
self.basecls = basecls
|
|
|
|
def recv(self, x=MTU):
|
|
pkt = self.ins.recv(x, socket.MSG_PEEK)
|
|
x = len(pkt)
|
|
pkt = self.basecls(pkt)
|
|
pad = pkt[Padding]
|
|
if pad is not None and pad.underlayer is not None:
|
|
del(pad.underlayer.payload)
|
|
while pad is not None and not isinstance(pad, NoPayload):
|
|
x -= len(pad.load)
|
|
pad = pad.payload
|
|
self.ins.recv(x)
|
|
return pkt
|
|
|
|
|
|
class BluetoothL2CAPSocket(SuperSocket):
|
|
def __init__(self, peer):
|
|
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW,
|
|
socket.BTPROTO_L2CAP)
|
|
s.connect((peer,0))
|
|
|
|
self.ins = self.outs = s
|
|
|
|
def recv(self, x):
|
|
return L2CAP_HdrCmd(self.ins.recv(x))
|
|
|
|
|
|
class BluetoothHCISocket(SuperSocket):
|
|
def __init__(self, iface=0x10000, type=None):
|
|
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
|
|
s.setsockopt(socket.SOL_HCI, socket.HCI_DATA_DIR,1)
|
|
s.setsockopt(socket.SOL_HCI, socket.HCI_TIME_STAMP,1)
|
|
s.setsockopt(socket.SOL_HCI, socket.HCI_FILTER, struct.pack("IIIh2x", 0xffffffffL,0xffffffffL,0xffffffffL,0)) #type mask, event mask, event mask, opcode
|
|
s.bind((iface,))
|
|
self.ins = self.outs = s
|
|
# s.connect((peer,0))
|
|
|
|
|
|
def recv(self, x):
|
|
return HCI_Hdr(self.ins.recv(x))
|
|
|
|
|
|
|
|
####################
|
|
## Send / Receive ##
|
|
####################
|
|
|
|
|
|
|
|
|
|
def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
|
|
if not isinstance(pkt, Gen):
|
|
pkt = SetGen(pkt)
|
|
|
|
if verbose is None:
|
|
verbose = conf.verb
|
|
debug.recv = PacketList([],"Unanswered")
|
|
debug.sent = PacketList([],"Sent")
|
|
debug.match = SndRcvList([])
|
|
nbrecv=0
|
|
ans = []
|
|
# do it here to fix random fields, so that parent and child have the same
|
|
tobesent = [p for p in pkt]
|
|
notans = len(tobesent)
|
|
|
|
hsent={}
|
|
for i in tobesent:
|
|
h = i.hashret()
|
|
if h in hsent:
|
|
hsent[h].append(i)
|
|
else:
|
|
hsent[h] = [i]
|
|
if retry < 0:
|
|
retry = -retry
|
|
autostop=retry
|
|
else:
|
|
autostop=0
|
|
|
|
|
|
while retry >= 0:
|
|
found=0
|
|
|
|
if timeout < 0:
|
|
timeout = None
|
|
|
|
rdpipe,wrpipe = os.pipe()
|
|
rdpipe=os.fdopen(rdpipe)
|
|
wrpipe=os.fdopen(wrpipe,"w")
|
|
|
|
pid = os.fork()
|
|
if pid == 0:
|
|
sys.stdin.close()
|
|
rdpipe.close()
|
|
try:
|
|
i = 0
|
|
if verbose:
|
|
print "Begin emission:"
|
|
for p in tobesent:
|
|
pks.send(p)
|
|
i += 1
|
|
time.sleep(inter)
|
|
if verbose:
|
|
print "Finished to send %i packets." % i
|
|
except SystemExit:
|
|
pass
|
|
except KeyboardInterrupt:
|
|
pass
|
|
except:
|
|
log_runtime.exception("--- Error in child %i" % os.getpid())
|
|
log_runtime.info("--- Error in child %i" % os.getpid())
|
|
os._exit(0)
|
|
else:
|
|
cPickle.dump(arp_cache, wrpipe)
|
|
wrpipe.close()
|
|
os._exit(0)
|
|
elif pid < 0:
|
|
log_runtime.error("fork error")
|
|
else:
|
|
wrpipe.close()
|
|
stoptime = 0
|
|
remaintime = None
|
|
inmask = [rdpipe,pks]
|
|
try:
|
|
while 1:
|
|
if stoptime:
|
|
remaintime = stoptime-time.time()
|
|
if remaintime <= 0:
|
|
break
|
|
r = None
|
|
if FREEBSD or DARWIN:
|
|
inp, out, err = select(inmask,[],[], 0.05)
|
|
if len(inp) == 0 or pks in inp:
|
|
r = pks.nonblock_recv()
|
|
else:
|
|
inp, out, err = select(inmask,[],[], remaintime)
|
|
if len(inp) == 0:
|
|
break
|
|
if pks in inp:
|
|
r = pks.recv(MTU)
|
|
if rdpipe in inp:
|
|
if timeout:
|
|
stoptime = time.time()+timeout
|
|
del(inmask[inmask.index(rdpipe)])
|
|
if r is None:
|
|
continue
|
|
ok = 0
|
|
h = r.hashret()
|
|
if h in hsent:
|
|
hlst = hsent[h]
|
|
for i in range(len(hlst)):
|
|
if r.answers(hlst[i]):
|
|
ans.append((hlst[i],r))
|
|
if verbose > 1:
|
|
os.write(1, "*")
|
|
ok = 1
|
|
if not multi:
|
|
del(hlst[i])
|
|
notans -= 1;
|
|
else:
|
|
if not hasattr(hlst[i], '_answered'):
|
|
notans -= 1;
|
|
hlst[i]._answered = 1;
|
|
break
|
|
if notans == 0 and not multi:
|
|
break
|
|
if not ok:
|
|
if verbose > 1:
|
|
os.write(1, ".")
|
|
nbrecv += 1
|
|
if conf.debug_match:
|
|
debug.recv.append(r)
|
|
except KeyboardInterrupt:
|
|
if chainCC:
|
|
raise KeyboardInterrupt
|
|
|
|
try:
|
|
ac = cPickle.load(rdpipe)
|
|
except EOFError:
|
|
warning("Child died unexpectedly. Packets may have not been sent")
|
|
else:
|
|
arp_cache.update(ac)
|
|
os.waitpid(pid,0)
|
|
|
|
remain = reduce(list.__add__, hsent.values(), [])
|
|
if multi:
|
|
remain = filter(lambda p: not hasattr(p, '_answered'), remain);
|
|
|
|
if autostop and len(remain) > 0 and len(remain) != len(tobesent):
|
|
retry = autostop
|
|
|
|
tobesent = remain
|
|
if len(tobesent) == 0:
|
|
break
|
|
retry -= 1
|
|
|
|
if conf.debug_match:
|
|
debug.sent=PacketList(remain[:],"Sent")
|
|
debug.match=SndRcvList(ans[:])
|
|
|
|
#clean the ans list to delete the field _answered
|
|
if (multi):
|
|
for s,r in ans:
|
|
if hasattr(s, '_answered'):
|
|
del(s._answered)
|
|
|
|
if verbose:
|
|
print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)
|
|
return SndRcvList(ans),PacketList(remain,"Unanswered"),debug.recv
|
|
|
|
|
|
def __gen_send(s, x, inter=0, loop=0, count=None, verbose=None, *args, **kargs):
|
|
if not isinstance(x, Gen):
|
|
x = SetGen(x)
|
|
if verbose is None:
|
|
verbose = conf.verb
|
|
n = 0
|
|
if count is not None:
|
|
loop = -count
|
|
elif not loop:
|
|
loop=-1
|
|
try:
|
|
while loop:
|
|
for p in x:
|
|
s.send(p)
|
|
n += 1
|
|
if verbose:
|
|
os.write(1,".")
|
|
time.sleep(inter)
|
|
if loop < 0:
|
|
loop += 1
|
|
except KeyboardInterrupt:
|
|
pass
|
|
s.close()
|
|
if verbose:
|
|
print "\nSent %i packets." % n
|
|
|
|
def send(x, inter=0, loop=0, count=None, verbose=None, *args, **kargs):
|
|
"""Send packets at layer 3
|
|
send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None"""
|
|
__gen_send(conf.L3socket(*args, **kargs), x, inter=inter, loop=loop, count=count,verbose=verbose)
|
|
|
|
def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, *args, **kargs):
|
|
"""Send packets at layer 2
|
|
send(packets, [inter=0], [loop=0], [verbose=conf.verb]) -> None"""
|
|
if iface is None and iface_hint is not None:
|
|
iface = conf.route.route(iface_hint)[0]
|
|
__gen_send(conf.L2socket(iface=iface, *args, **kargs), x, inter=inter, loop=loop, count=count, verbose=verbose)
|
|
|
|
def sr(x,filter=None, iface=None, *args,**kargs):
|
|
"""Send and receive packets at layer 3
|
|
nofilter: put 1 to avoid use of bpf filters
|
|
retry: if positive, how many times to resend unanswered packets
|
|
if negative, how many times to retry when no more packets are answered
|
|
timeout: how much time to wait after the last packet has been sent
|
|
verbose: set verbosity level
|
|
multi: whether to accept multiple answers for the same stimulus
|
|
filter: provide a BPF filter
|
|
iface: listen answers only on the given interface"""
|
|
if not kargs.has_key("timeout"):
|
|
kargs["timeout"] = -1
|
|
s = conf.L3socket(filter=filter, iface=iface)
|
|
a,b,c=sndrcv(s,x,*args,**kargs)
|
|
s.close()
|
|
return a,b
|
|
|
|
def sr1(x,filter=None,iface=None, nofilter=0, *args,**kargs):
|
|
"""Send packets at layer 3 and return only the first answer
|
|
nofilter: put 1 to avoid use of bpf filters
|
|
retry: if positive, how many times to resend unanswered packets
|
|
if negative, how many times to retry when no more packets are answered
|
|
timeout: how much time to wait after the last packet has been sent
|
|
verbose: set verbosity level
|
|
multi: whether to accept multiple answers for the same stimulus
|
|
filter: provide a BPF filter
|
|
iface: listen answers only on the given interface"""
|
|
if not kargs.has_key("timeout"):
|
|
kargs["timeout"] = -1
|
|
s=conf.L3socket(filter=filter, nofilter=nofilter, iface=iface)
|
|
a,b,c=sndrcv(s,x,*args,**kargs)
|
|
s.close()
|
|
if len(a) > 0:
|
|
return a[0][1]
|
|
else:
|
|
return None
|
|
|
|
def srp(x,iface=None, iface_hint=None, filter=None, nofilter=0, type=ETH_P_ALL, *args,**kargs):
|
|
"""Send and receive packets at layer 2
|
|
nofilter: put 1 to avoid use of bpf filters
|
|
retry: if positive, how many times to resend unanswered packets
|
|
if negative, how many times to retry when no more packets are answered
|
|
timeout: how much time to wait after the last packet has been sent
|
|
verbose: set verbosity level
|
|
multi: whether to accept multiple answers for the same stimulus
|
|
filter: provide a BPF filter
|
|
iface: work only on the given interface"""
|
|
if not kargs.has_key("timeout"):
|
|
kargs["timeout"] = -1
|
|
if iface is None and iface_hint is not None:
|
|
iface = conf.route.route(iface_hint)[0]
|
|
a,b,c=sndrcv(conf.L2socket(iface=iface, filter=filter, nofilter=nofilter, type=type),x,*args,**kargs)
|
|
return a,b
|
|
|
|
def srp1(*args,**kargs):
|
|
"""Send and receive packets at layer 2 and return only the first answer
|
|
nofilter: put 1 to avoid use of bpf filters
|
|
retry: if positive, how many times to resend unanswered packets
|
|
if negative, how many times to retry when no more packets are answered
|
|
timeout: how much time to wait after the last packet has been sent
|
|
verbose: set verbosity level
|
|
multi: whether to accept multiple answers for the same stimulus
|
|
filter: provide a BPF filter
|
|
iface: work only on the given interface"""
|
|
if not kargs.has_key("timeout"):
|
|
kargs["timeout"] = -1
|
|
a,b=srp(*args,**kargs)
|
|
if len(a) > 0:
|
|
return a[0][1]
|
|
else:
|
|
return None
|
|
|
|
def __sr_loop(srfunc, pkts, prn=lambda x:x[1].summary(), prnfail=lambda x:x.summary(), inter=1, timeout=None, count=None, verbose=0, store=1, *args, **kargs):
|
|
n = 0
|
|
r = 0
|
|
ct = conf.color_theme
|
|
parity = 0
|
|
ans=[]
|
|
unans=[]
|
|
if timeout is None:
|
|
timeout = min(2*inter, 5)
|
|
try:
|
|
while 1:
|
|
parity ^= 1
|
|
col = [ct.even,ct.odd][parity]
|
|
if count is not None:
|
|
if count == 0:
|
|
break
|
|
count -= 1
|
|
start = time.time()
|
|
print "\rsend...\r",
|
|
res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=1, *args, **kargs)
|
|
n += len(res[0])+len(res[1])
|
|
r += len(res[0])
|
|
if prn and len(res[0]) > 0:
|
|
msg = "RECV %i:" % len(res[0])
|
|
print "\r"+ct.success(msg),
|
|
for p in res[0]:
|
|
print col(prn(p))
|
|
print " "*len(msg),
|
|
if prnfail and len(res[1]) > 0:
|
|
msg = "fail %i:" % len(res[1])
|
|
print "\r"+ct.fail(msg),
|
|
for p in res[1]:
|
|
print col(prnfail(p))
|
|
print " "*len(msg),
|
|
if not (prn or prnfail):
|
|
print "recv:%i fail:%i" % tuple(map(len, res[:2]))
|
|
if store:
|
|
ans += res[0]
|
|
unans += res[1]
|
|
end=time.time()
|
|
if end-start < inter:
|
|
time.sleep(inter+start-end)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
if n>0:
|
|
print "%s\nSent %i packets, received %i packets. %3.1f%% hits." % (Color.normal,n,r,100.0*r/n)
|
|
|
|
return SndRcvList(ans),PacketList(unans)
|
|
|
|
def srloop(pkts, *args, **kargs):
|
|
"""Send a packet at layer 3 in loop and print the answer each time
|
|
srloop(pkts, [prn], [inter], [count], ...) --> None"""
|
|
return __sr_loop(sr, pkts, *args, **kargs)
|
|
|
|
def srploop(pkts, *args, **kargs):
|
|
"""Send a packet at layer 2 in loop and print the answer each time
|
|
srloop(pkts, [prn], [inter], [count], ...) --> None"""
|
|
return __sr_loop(srp, pkts, *args, **kargs)
|
|
|
|
|
|
## Bluetooth
|
|
|
|
|
|
def srbt(peer, pkts, inter=0.1, *args, **kargs):
|
|
s = conf.BTsocket(peer=peer)
|
|
a,b,c=sndrcv(s,pkts,inter=inter,*args,**kargs)
|
|
s.close()
|
|
return a,b
|
|
|
|
def srbt1(peer, pkts, *args, **kargs):
|
|
a,b = srbt(peer, pkts, *args, **kargs)
|
|
if len(a) > 0:
|
|
return a[0][1]
|
|
|
|
|
|
|
|
|
|
|
|
#############################
|
|
## pcap capture file stuff ##
|
|
#############################
|
|
|
|
def wrpcap(filename, pkt, *args, **kargs):
|
|
"""Write a list of packets to a pcap file
|
|
gz: set to 1 to save a gzipped capture
|
|
linktype: force linktype value
|
|
endianness: "<" or ">", force endianness"""
|
|
PcapWriter(filename, *args, **kargs).write(pkt)
|
|
|
|
def rdpcap(filename, count=-1):
|
|
"""Read a pcap file and return a packet list
|
|
count: read only <count> packets"""
|
|
return PcapReader(filename).read_all(count=count)
|
|
|
|
class PcapReader:
|
|
"""A stateful pcap reader
|
|
|
|
Based entirely on scapy.rdpcap(), this class allows for packets
|
|
to be dispatched without having to be loaded into memory all at
|
|
once
|
|
"""
|
|
|
|
def __init__(self, filename):
|
|
self.filename = filename
|
|
try:
|
|
self.f = gzip.open(filename,"rb")
|
|
magic = self.f.read(4)
|
|
except IOError:
|
|
self.f = open(filename,"rb")
|
|
magic = self.f.read(4)
|
|
if magic == "\xa1\xb2\xc3\xd4": #big endian
|
|
self.endian = ">"
|
|
elif magic == "\xd4\xc3\xb2\xa1": #little endian
|
|
self.endian = "<"
|
|
else:
|
|
raise RuntimeWarning, "Not a pcap capture file (bad magic)"
|
|
hdr = self.f.read(20)
|
|
if len(hdr)<20:
|
|
raise RuntimeWarning, "Invalid pcap file (too short)"
|
|
vermaj,vermin,tz,sig,snaplen,linktype = struct.unpack(self.endian+"HHIIII",hdr)
|
|
self.LLcls = LLTypes.get(linktype, Raw)
|
|
if self.LLcls == Raw:
|
|
warning("PcapReader: unkonwon LL type [%i]/[%#x]. Using Raw packets" % (linktype,linktype))
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def next(self):
|
|
"""impliment the iterator protocol on a set of packets in a
|
|
pcap file
|
|
"""
|
|
pkt = self.read_packet()
|
|
if pkt == None:
|
|
raise StopIteration
|
|
return pkt
|
|
|
|
|
|
def read_packet(self):
|
|
"""return a single packet read from the file
|
|
|
|
returns None when no more packets are available
|
|
"""
|
|
hdr = self.f.read(16)
|
|
if len(hdr) < 16:
|
|
return None
|
|
sec,usec,caplen,olen = struct.unpack(self.endian+"IIII", hdr)
|
|
s = self.f.read(caplen)
|
|
try:
|
|
p = self.LLcls(s)
|
|
except:
|
|
if conf.debug_dissector:
|
|
raise
|
|
p = Raw(s)
|
|
p.time = sec+0.000001*usec
|
|
return p
|
|
|
|
def dispatch(self, callback):
|
|
"""call the specified callback routine for each packet read
|
|
|
|
This is just a convienience function for the main loop
|
|
that allows for easy launching of packet processing in a
|
|
thread.
|
|
"""
|
|
p = self.read_packet()
|
|
while p != None:
|
|
callback(p)
|
|
p = self.read_packet()
|
|
|
|
def read_all(self,count=-1):
|
|
"""return a list of all packets in the pcap file
|
|
"""
|
|
res=[]
|
|
while count != 0:
|
|
count -= 1
|
|
p = self.read_packet()
|
|
if p is None:
|
|
break
|
|
res.append(p)
|
|
return PacketList(res,name = os.path.basename(self.filename))
|
|
|
|
def recv(self, size):
|
|
""" Emulate a socket
|
|
"""
|
|
return self.read_packet()
|
|
|
|
def fileno(self):
|
|
return self.f.fileno()
|
|
|
|
|
|
|
|
class PcapWriter:
|
|
"""A pcap writer with more control than wrpcap()
|
|
|
|
This routine is based entirely on scapy.wrpcap(), but adds capability
|
|
of writing one packet at a time in a streaming manner.
|
|
"""
|
|
def __init__(self, filename, linktype=None, gz=0, endianness=""):
|
|
self.linktype = linktype
|
|
self.header_done = 0
|
|
if gz:
|
|
self.f = gzip.open(filename,"wb")
|
|
else:
|
|
self.f = open(filename,"wb")
|
|
self.endian = endianness
|
|
|
|
def fileno(self):
|
|
return self.f.fileno()
|
|
|
|
def write(self, pkt):
|
|
"""accepts a either a single packet or a list of packets
|
|
to be written to the dumpfile
|
|
"""
|
|
|
|
if self.header_done == 0:
|
|
if self.linktype == None:
|
|
if isinstance(pkt,Packet):
|
|
linktype = LLNumTypes.get(pkt.__class__,1)
|
|
else:
|
|
linktype = LLNumTypes.get(pkt[0].__class__,1)
|
|
|
|
self.f.write(struct.pack(self.endian+"IHHIIII", 0xa1b2c3d4L,
|
|
2, 4, 0, 0, MTU, linktype))
|
|
self.header_done = 1
|
|
|
|
for p in pkt:
|
|
self._write_packet(p)
|
|
|
|
def _write_packet(self, packet):
|
|
"""writes a single packet to the pcap file
|
|
"""
|
|
s = str(packet)
|
|
l = len(s)
|
|
sec = int(packet.time)
|
|
usec = int((packet.time-sec)*1000000)
|
|
self.f.write(struct.pack(self.endian+"IIII", sec, usec, l, l))
|
|
self.f.write(s)
|
|
|
|
re_extract_hexcap = re.compile("^(0x[0-9a-fA-F]{2,}[ :\t]|(0x)?[0-9a-fA-F]{2,}:|(0x)?[0-9a-fA-F]{3,}[: \t]|) *(([0-9a-fA-F]{2} {,2}){,16})")
|
|
|
|
def import_hexcap():
|
|
p = ""
|
|
try:
|
|
while 1:
|
|
l = raw_input().strip()
|
|
try:
|
|
p += re_extract_hexcap.match(l).groups()[3]
|
|
except:
|
|
warning("Parsing error during hexcap")
|
|
continue
|
|
except EOFError:
|
|
pass
|
|
|
|
p = p.replace(" ","")
|
|
p2=""
|
|
for i in range(len(p)/2):
|
|
p2 += chr(int(p[2*i:2*i+2],16))
|
|
return p2
|
|
|
|
|
|
|
|
def wireshark(pktlist):
|
|
f = os.tempnam("scapy")
|
|
wrpcap(f, pktlist)
|
|
os.spawnlp(os.P_NOWAIT, "wireshark", "wireshark", "-r", f)
|
|
|
|
|
|
#####################
|
|
## knowledge bases ##
|
|
#####################
|
|
|
|
class KnowledgeBase:
|
|
def __init__(self, filename):
|
|
self.filename = filename
|
|
self.base = None
|
|
|
|
def lazy_init(self):
|
|
self.base = ""
|
|
|
|
def reload(self, filename = None):
|
|
if filename is not None:
|
|
self.filename = filename
|
|
oldbase = self.base
|
|
self.base = None
|
|
self.lazy_init()
|
|
if self.base is None:
|
|
self.base = oldbase
|
|
|
|
def get_base(self):
|
|
if self.base is None:
|
|
self.lazy_init()
|
|
return self.base
|
|
|
|
|
|
|
|
##########################
|
|
## IP location database ##
|
|
##########################
|
|
|
|
class IPCountryKnowledgeBase(KnowledgeBase):
|
|
"""
|
|
How to generate the base :
|
|
db = []
|
|
for l in open("GeoIPCountryWhois.csv").readlines():
|
|
s,e,c = l.split(",")[2:5]
|
|
db.append((int(s[1:-1]),int(e[1:-1]),c[1:-1]))
|
|
cPickle.dump(gzip.open("xxx","w"),db)
|
|
"""
|
|
def lazy_init(self):
|
|
self.base = load_object(self.filename)
|
|
|
|
|
|
class CountryLocKnowledgeBase(KnowledgeBase):
|
|
def lazy_init(self):
|
|
f=open(self.filename)
|
|
self.base = {}
|
|
while 1:
|
|
l = f.readline()
|
|
if not l:
|
|
break
|
|
l = l.strip().split(",")
|
|
if len(l) != 3:
|
|
continue
|
|
c,lat,long = l
|
|
|
|
self.base[c] = (float(long),float(lat))
|
|
f.close()
|
|
|
|
|
|
|
|
|
|
def locate_ip(ip):
|
|
ip=map(int,ip.split("."))
|
|
ip = ip[3]+(ip[2]<<8L)+(ip[1]<<16L)+(ip[0]<<24L)
|
|
|
|
cloc = country_loc_kdb.get_base()
|
|
db = IP_country_kdb.get_base()
|
|
|
|
d=0
|
|
f=len(db)-1
|
|
while (f-d) > 1:
|
|
guess = (d+f)/2
|
|
if ip > db[guess][0]:
|
|
d = guess
|
|
else:
|
|
f = guess
|
|
s,e,c = db[guess]
|
|
if s <= ip and ip <= e:
|
|
return cloc.get(c,None)
|
|
|
|
|
|
|
|
|
|
###############
|
|
## p0f stuff ##
|
|
###############
|
|
|
|
# File format (according to p0f.fp) :
|
|
#
|
|
# wwww:ttt:D:ss:OOO...:QQ:OS:Details
|
|
#
|
|
# wwww - window size
|
|
# ttt - initial TTL
|
|
# D - don't fragment bit (0=unset, 1=set)
|
|
# ss - overall SYN packet size
|
|
# OOO - option value and order specification
|
|
# QQ - quirks list
|
|
# OS - OS genre
|
|
# details - OS description
|
|
|
|
|
|
|
|
class p0fKnowledgeBase(KnowledgeBase):
|
|
def __init__(self, filename):
|
|
KnowledgeBase.__init__(self, filename)
|
|
#self.ttl_range=[255]
|
|
def lazy_init(self):
|
|
try:
|
|
f=open(self.filename)
|
|
except IOError:
|
|
warning("Can't open base %s" % self.filename)
|
|
return
|
|
try:
|
|
self.base = []
|
|
for l in f:
|
|
if l[0] in ["#","\n"]:
|
|
continue
|
|
l = tuple(l.split(":"))
|
|
if len(l) < 8:
|
|
continue
|
|
li = map(int,l[1:4])
|
|
#if li[0] not in self.ttl_range:
|
|
# self.ttl_range.append(li[0])
|
|
# self.ttl_range.sort()
|
|
self.base.append((l[0], li[0], li[1], li[2], l[4], l[5], l[6], l[7][:-1]))
|
|
except:
|
|
warning("Can't parse p0f database (new p0f version ?)")
|
|
self.base = None
|
|
f.close()
|
|
|
|
|
|
def packet2p0f(pkt):
|
|
while pkt.haslayer(IP) and pkt.haslayer(TCP):
|
|
pkt = pkt.getlayer(IP)
|
|
if isinstance(pkt.payload, TCP):
|
|
break
|
|
pkt = pkt.payload
|
|
|
|
if not isinstance(pkt, IP) or not isinstance(pkt.payload, TCP):
|
|
raise TypeError("Not a TCP/IP packet")
|
|
if pkt.payload.flags & 0x13 != 0x02: #S,!A,!F
|
|
raise TypeError("Not a syn packet")
|
|
|
|
#t = p0f_kdb.ttl_range[:]
|
|
#t += [pkt.ttl]
|
|
#t.sort()
|
|
#ttl=t[t.index(pkt.ttl)+1]
|
|
ttl = pkt.ttl
|
|
|
|
df = (pkt.flags & 2) / 2
|
|
ss = len(pkt)
|
|
# from p0f/config.h : PACKET_BIG = 100
|
|
if ss > 100:
|
|
ss = 0
|
|
|
|
ooo = ""
|
|
mss = -1
|
|
qqT = False
|
|
qqP = False
|
|
#qqBroken = False
|
|
ilen = (pkt[TCP].dataofs << 2) - 20 # from p0f.c
|
|
for option in pkt.payload.options:
|
|
ilen -= 1
|
|
if option[0] == "MSS":
|
|
ooo += "M" + str(option[1]) + ","
|
|
mss = option[1]
|
|
# FIXME: qqBroken
|
|
ilen -= 3
|
|
elif option[0] == "WScale":
|
|
ooo += "W" + str(option[1]) + ","
|
|
# FIXME: qqBroken
|
|
ilen -= 2
|
|
elif option[0] == "Timestamp":
|
|
if option[1][0] == 0:
|
|
ooo += "T0,"
|
|
else:
|
|
ooo += "T,"
|
|
if option[1][1] != 0:
|
|
qqT = True
|
|
ilen -= 9
|
|
elif option[0] == "SAckOK":
|
|
ooo += "S,"
|
|
ilen -= 1
|
|
elif option[0] == "NOP":
|
|
ooo += "N,"
|
|
elif option[0] == "EOL":
|
|
ooo += "E,"
|
|
if ilen > 0:
|
|
qqP = True
|
|
else:
|
|
ooo += "?,"
|
|
# FIXME: ilen
|
|
ooo = ooo[:-1]
|
|
if ooo == "": ooo = "."
|
|
|
|
win = pkt.payload.window
|
|
if mss != -1:
|
|
if win % mss == 0:
|
|
win = "S" + str(win/mss)
|
|
elif win % (mss + 40) == 0:
|
|
win = "T" + str(win/(mss+40))
|
|
win = str(win)
|
|
|
|
qq = ""
|
|
|
|
if qqP:
|
|
qq += "P"
|
|
if pkt[IP].id == 0:
|
|
qq += "Z"
|
|
if pkt[IP].options != '':
|
|
qq += "I"
|
|
if pkt[TCP].urgptr != 0:
|
|
qq += "U"
|
|
if pkt[TCP].reserved != 0:
|
|
qq += "X"
|
|
if pkt[TCP].ack != 0:
|
|
qq += "A"
|
|
if qqT:
|
|
qq += "T"
|
|
if pkt[TCP].flags & 40 != 0:
|
|
# U or P
|
|
qq += "F"
|
|
if not isinstance(pkt[TCP].payload, NoPayload):
|
|
qq += "D"
|
|
# FIXME : "!" - broken options segment
|
|
|
|
if qq == "":
|
|
qq = "."
|
|
|
|
return (win,
|
|
ttl,
|
|
df,
|
|
ss,
|
|
ooo,
|
|
qq)
|
|
|
|
def p0f_correl(x,y):
|
|
d = 0
|
|
# wwww can be "*" or "%nn"
|
|
d += (x[0] == y[0] or y[0] == "*" or (y[0][0] == "%" and x[0].isdigit() and (int(x[0]) % int(y[0][1:])) == 0))
|
|
# ttl
|
|
d += (y[1] >= x[1] and y[1] - x[1] < 32)
|
|
for i in [2, 3, 5]:
|
|
d += (x[i] == y[i])
|
|
xopt = x[4].split(",")
|
|
yopt = y[4].split(",")
|
|
if len(xopt) == len(yopt):
|
|
same = True
|
|
for i in range(len(xopt)):
|
|
if not (xopt[i] == yopt[i] or
|
|
(len(yopt[i]) == 2 and len(xopt[i]) > 1 and
|
|
yopt[i][1] == "*" and xopt[i][0] == yopt[i][0]) or
|
|
(len(yopt[i]) > 2 and len(xopt[i]) > 1 and
|
|
yopt[i][1] == "%" and xopt[i][0] == yopt[i][0] and
|
|
int(xopt[i][1:]) % int(yopt[i][2:]) == 0)):
|
|
same = False
|
|
break
|
|
if same:
|
|
d += len(xopt)
|
|
return d
|
|
|
|
|
|
def p0f(pkt):
|
|
"""Passive OS fingerprinting: which OS emitted this TCP SYN ?
|
|
p0f(packet) -> accuracy, [list of guesses]
|
|
"""
|
|
pb = p0f_kdb.get_base()
|
|
if not pb:
|
|
warning("p0f base empty.")
|
|
return []
|
|
s = len(pb[0][0])
|
|
r = []
|
|
sig = packet2p0f(pkt)
|
|
max = len(sig[4].split(",")) + 5
|
|
for b in pb:
|
|
d = p0f_correl(sig,b)
|
|
if d == max:
|
|
r.append((b[6], b[7], b[1] - pkt[IP].ttl))
|
|
return r
|
|
|
|
|
|
def prnp0f(pkt):
|
|
try:
|
|
r = p0f(pkt)
|
|
except:
|
|
return
|
|
if r == []:
|
|
r = ("UNKNOWN", "[" + ":".join(map(str, packet2p0f(pkt))) + ":?:?]", None)
|
|
else:
|
|
r = r[0]
|
|
uptime = None
|
|
try:
|
|
uptime = pkt2uptime(pkt)
|
|
except:
|
|
pass
|
|
if uptime == 0:
|
|
uptime = None
|
|
res = pkt.sprintf("%IP.src%:%TCP.sport% - " + r[0] + " " + r[1])
|
|
if uptime is not None:
|
|
res += pkt.sprintf(" (up: " + str(uptime/3600) + " hrs)\n -> %IP.dst%:%TCP.dport%")
|
|
else:
|
|
res += pkt.sprintf("\n -> %IP.dst%:%TCP.dport%")
|
|
if r[2] is not None:
|
|
res += " (distance " + str(r[2]) + ")"
|
|
print res
|
|
|
|
|
|
def pkt2uptime(pkt, HZ=100):
|
|
"""Calculate the date the machine which emitted the packet booted using TCP timestamp
|
|
pkt2uptime(pkt, [HZ=100])"""
|
|
if not isinstance(pkt, Packet):
|
|
raise TypeError("Not a TCP packet")
|
|
if isinstance(pkt,NoPayload):
|
|
raise TypeError("Not a TCP packet")
|
|
if not isinstance(pkt, TCP):
|
|
return pkt2uptime(pkt.payload)
|
|
for opt in pkt.options:
|
|
if opt[0] == "Timestamp":
|
|
#t = pkt.time - opt[1][0] * 1.0/HZ
|
|
#return time.ctime(t)
|
|
t = opt[1][0] / HZ
|
|
return t
|
|
raise TypeError("No timestamp option")
|
|
|
|
|
|
|
|
#################
|
|
## Queso stuff ##
|
|
#################
|
|
|
|
|
|
def quesoTCPflags(flags):
|
|
if flags == "-":
|
|
return "-"
|
|
flv = "FSRPAUXY"
|
|
v = 0
|
|
for i in flags:
|
|
v |= 2**flv.index(i)
|
|
return "%x" % v
|
|
|
|
class QuesoKnowledgeBase(KnowledgeBase):
|
|
def lazy_init(self):
|
|
try:
|
|
f = open(self.filename)
|
|
except IOError:
|
|
return
|
|
self.base = {}
|
|
p = None
|
|
try:
|
|
for l in f:
|
|
l = l.strip()
|
|
if not l or l[0] == ';':
|
|
continue
|
|
if l[0] == '*':
|
|
if p is not None:
|
|
p[""] = name
|
|
name = l[1:].strip()
|
|
p = self.base
|
|
continue
|
|
if l[0] not in list("0123456"):
|
|
continue
|
|
res = l[2:].split()
|
|
res[-1] = quesoTCPflags(res[-1])
|
|
res = " ".join(res)
|
|
if not p.has_key(res):
|
|
p[res] = {}
|
|
p = p[res]
|
|
if p is not None:
|
|
p[""] = name
|
|
except:
|
|
self.base = None
|
|
warning("Can't load queso base [%s]", self.filename)
|
|
f.close()
|
|
|
|
|
|
|
|
|
|
def queso_sig(target, dport=80, timeout=3):
|
|
p = queso_kdb.get_base()
|
|
ret = []
|
|
for flags in ["S", "SA", "F", "FA", "SF", "P", "SEC"]:
|
|
ans, unans = sr(IP(dst=target)/TCP(dport=dport,flags=flags,seq=RandInt()),
|
|
timeout=timeout, verbose=0)
|
|
if len(ans) == 0:
|
|
rs = "- - - -"
|
|
else:
|
|
s,r = ans[0]
|
|
rs = "%i" % (r.seq != 0)
|
|
if not r.ack:
|
|
r += " 0"
|
|
elif r.ack-s.seq > 666:
|
|
rs += " R" % 0
|
|
else:
|
|
rs += " +%i" % (r.ack-s.seq)
|
|
rs += " %X" % r.window
|
|
rs += " %x" % r.payload.flags
|
|
ret.append(rs)
|
|
return ret
|
|
|
|
def queso_search(sig):
|
|
p = queso_kdb.get_base()
|
|
sig.reverse()
|
|
ret = []
|
|
try:
|
|
while sig:
|
|
s = sig.pop()
|
|
p = p[s]
|
|
if p.has_key(""):
|
|
ret.append(p[""])
|
|
except KeyError:
|
|
pass
|
|
return ret
|
|
|
|
|
|
def queso(*args,**kargs):
|
|
"""Queso OS fingerprinting
|
|
queso(target, dport=80, timeout=3)"""
|
|
return queso_search(queso_sig(*args, **kargs))
|
|
|
|
|
|
|
|
######################
|
|
## nmap OS fp stuff ##
|
|
######################
|
|
|
|
|
|
class NmapKnowledgeBase(KnowledgeBase):
|
|
def lazy_init(self):
|
|
try:
|
|
f=open(self.filename)
|
|
except IOError:
|
|
return
|
|
|
|
self.base = []
|
|
name = None
|
|
try:
|
|
for l in f:
|
|
l = l.strip()
|
|
if not l or l[0] == "#":
|
|
continue
|
|
if l[:12] == "Fingerprint ":
|
|
if name is not None:
|
|
self.base.append((name,sig))
|
|
name = l[12:].strip()
|
|
sig={}
|
|
p = self.base
|
|
continue
|
|
elif l[:6] == "Class ":
|
|
continue
|
|
op = l.find("(")
|
|
cl = l.find(")")
|
|
if op < 0 or cl < 0:
|
|
warning("error reading nmap os fp base file")
|
|
continue
|
|
test = l[:op]
|
|
s = map(lambda x: x.split("="), l[op+1:cl].split("%"))
|
|
si = {}
|
|
for n,v in s:
|
|
si[n] = v
|
|
sig[test]=si
|
|
if name is not None:
|
|
self.base.append((name,sig))
|
|
except:
|
|
self.base = None
|
|
warning("Can't read nmap database [%s](new nmap version ?)" % self.filename)
|
|
f.close()
|
|
|
|
def TCPflags2str(f):
|
|
fl="FSRPAUEC"
|
|
s=""
|
|
for i in range(len(fl)):
|
|
if f & 1:
|
|
s = fl[i]+s
|
|
f >>= 1
|
|
return s
|
|
|
|
def nmap_tcppacket_sig(pkt):
|
|
r = {}
|
|
if pkt is not None:
|
|
# r["Resp"] = "Y"
|
|
r["DF"] = (pkt.flags & 2) and "Y" or "N"
|
|
r["W"] = "%X" % pkt.window
|
|
r["ACK"] = pkt.ack==2 and "S++" or pkt.ack==1 and "S" or "O"
|
|
r["Flags"] = TCPflags2str(pkt.payload.flags)
|
|
r["Ops"] = "".join(map(lambda x: x[0][0],pkt.payload.options))
|
|
else:
|
|
r["Resp"] = "N"
|
|
return r
|
|
|
|
|
|
def nmap_udppacket_sig(S,T):
|
|
r={}
|
|
if T is None:
|
|
r["Resp"] = "N"
|
|
else:
|
|
r["DF"] = (T.flags & 2) and "Y" or "N"
|
|
r["TOS"] = "%X" % T.tos
|
|
r["IPLEN"] = "%X" % T.len
|
|
r["RIPTL"] = "%X" % T.payload.payload.len
|
|
r["RID"] = S.id == T.payload.payload.id and "E" or "F"
|
|
r["RIPCK"] = S.chksum == T.getlayer(IPerror).chksum and "E" or T.getlayer(IPerror).chksum == 0 and "0" or "F"
|
|
r["UCK"] = S.payload.chksum == T.getlayer(UDPerror).chksum and "E" or T.getlayer(UDPerror).chksum ==0 and "0" or "F"
|
|
r["ULEN"] = "%X" % T.getlayer(UDPerror).len
|
|
r["DAT"] = T.getlayer(Raw) is None and "E" or S.getlayer(Raw).load == T.getlayer(Raw).load and "E" or "F"
|
|
return r
|
|
|
|
|
|
|
|
def nmap_match_one_sig(seen, ref):
|
|
c = 0
|
|
for k in seen.keys():
|
|
if ref.has_key(k):
|
|
if seen[k] in ref[k].split("|"):
|
|
c += 1
|
|
if c == 0 and seen.get("Resp") == "N":
|
|
return 0.7
|
|
else:
|
|
return 1.0*c/len(seen.keys())
|
|
|
|
|
|
|
|
def nmap_sig(target, oport=80, cport=81, ucport=1):
|
|
res = {}
|
|
|
|
tcpopt = [ ("WScale", 10),
|
|
("NOP",None),
|
|
("MSS", 256),
|
|
("Timestamp",(123,0)) ]
|
|
tests = [ IP(dst=target, id=1)/TCP(seq=1, sport=5001, dport=oport, options=tcpopt, flags="CS"),
|
|
IP(dst=target, id=1)/TCP(seq=1, sport=5002, dport=oport, options=tcpopt, flags=0),
|
|
IP(dst=target, id=1)/TCP(seq=1, sport=5003, dport=oport, options=tcpopt, flags="SFUP"),
|
|
IP(dst=target, id=1)/TCP(seq=1, sport=5004, dport=oport, options=tcpopt, flags="A"),
|
|
IP(dst=target, id=1)/TCP(seq=1, sport=5005, dport=cport, options=tcpopt, flags="S"),
|
|
IP(dst=target, id=1)/TCP(seq=1, sport=5006, dport=cport, options=tcpopt, flags="A"),
|
|
IP(dst=target, id=1)/TCP(seq=1, sport=5007, dport=cport, options=tcpopt, flags="FPU"),
|
|
IP(str(IP(dst=target)/UDP(sport=5008,dport=ucport)/(300*"i"))) ]
|
|
|
|
ans, unans = sr(tests, timeout=2)
|
|
ans += map(lambda x: (x,None), unans)
|
|
|
|
for S,T in ans:
|
|
if S.sport == 5008:
|
|
res["PU"] = nmap_udppacket_sig(S,T)
|
|
else:
|
|
t = "T%i" % (S.sport-5000)
|
|
if T is not None and T.haslayer(ICMP):
|
|
warning("Test %s answered by an ICMP" % t)
|
|
T=None
|
|
res[t] = nmap_tcppacket_sig(T)
|
|
|
|
return res
|
|
|
|
def nmap_probes2sig(tests):
|
|
tests=tests.copy()
|
|
res = {}
|
|
if "PU" in tests:
|
|
res["PU"] = nmap_udppacket_sig(*tests["PU"])
|
|
del(tests["PU"])
|
|
for k in tests:
|
|
res[k] = nmap_tcppacket_sig(tests[k])
|
|
return res
|
|
|
|
|
|
def nmap_search(sigs):
|
|
guess = 0,[]
|
|
for os,fp in nmap_kdb.get_base():
|
|
c = 0.0
|
|
for t in sigs.keys():
|
|
if t in fp:
|
|
c += nmap_match_one_sig(sigs[t], fp[t])
|
|
c /= len(sigs.keys())
|
|
if c > guess[0]:
|
|
guess = c,[ os ]
|
|
elif c == guess[0]:
|
|
guess[1].append(os)
|
|
return guess
|
|
|
|
|
|
def nmap_fp(target, oport=80, cport=81):
|
|
"""nmap fingerprinting
|
|
nmap_fp(target, [oport=80,] [cport=81,]) -> list of best guesses with accuracy
|
|
"""
|
|
sigs = nmap_sig(target, oport, cport)
|
|
return nmap_search(sigs)
|
|
|
|
|
|
def nmap_sig2txt(sig):
|
|
torder = ["TSeq","T1","T2","T3","T4","T5","T6","T7","PU"]
|
|
korder = ["Class", "gcd", "SI", "IPID", "TS",
|
|
"Resp", "DF", "W", "ACK", "Flags", "Ops",
|
|
"TOS", "IPLEN", "RIPTL", "RID", "RIPCK", "UCK", "ULEN", "DAT" ]
|
|
txt=[]
|
|
for i in sig.keys():
|
|
if i not in torder:
|
|
torder.append(i)
|
|
for t in torder:
|
|
sl = sig.get(t)
|
|
if sl is None:
|
|
continue
|
|
s = []
|
|
for k in korder:
|
|
v = sl.get(k)
|
|
if v is None:
|
|
continue
|
|
s.append("%s=%s"%(k,v))
|
|
txt.append("%s(%s)" % (t, "%".join(s)))
|
|
return "\n".join(txt)
|
|
|
|
|
|
|
|
|
|
|
|
###################
|
|
## User commands ##
|
|
###################
|
|
|
|
|
|
def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg):
|
|
"""Sniff packets
|
|
sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets
|
|
|
|
count: number of packets to capture. 0 means infinity
|
|
store: wether to store sniffed packets or discard them
|
|
prn: function to apply to each packet. If something is returned,
|
|
it is displayed. Ex:
|
|
ex: prn = lambda x: x.summary()
|
|
lfilter: python function applied to each packet to determine
|
|
if further action may be done
|
|
ex: lfilter = lambda x: x.haslayer(Padding)
|
|
offline: pcap file to read packets from, instead of sniffing them
|
|
timeout: stop sniffing after a given time (default: None)
|
|
L2socket: use the provided L2socket
|
|
"""
|
|
c = 0
|
|
|
|
if offline is None:
|
|
if L2socket is None:
|
|
L2socket = conf.L2listen
|
|
s = L2socket(type=ETH_P_ALL, *arg, **karg)
|
|
else:
|
|
s = PcapReader(offline)
|
|
|
|
lst = []
|
|
if timeout is not None:
|
|
stoptime = time.time()+timeout
|
|
remain = None
|
|
while 1:
|
|
try:
|
|
if timeout is not None:
|
|
remain = stoptime-time.time()
|
|
if remain <= 0:
|
|
break
|
|
sel = select([s],[],[],remain)
|
|
if s in sel[0]:
|
|
p = s.recv(MTU)
|
|
if p is None:
|
|
break
|
|
if lfilter and not lfilter(p):
|
|
continue
|
|
if store:
|
|
lst.append(p)
|
|
c += 1
|
|
if prn:
|
|
r = prn(p)
|
|
if r is not None:
|
|
print r
|
|
if count > 0 and c >= count:
|
|
break
|
|
except KeyboardInterrupt:
|
|
break
|
|
return PacketList(lst,"Sniffed")
|
|
|
|
|
|
|
|
def arpcachepoison(target, victim, interval=60):
|
|
"""Poison target's cache with (your MAC,victim's IP) couple
|
|
arpcachepoison(target, victim, [interval=60]) -> None
|
|
"""
|
|
tmac = getmacbyip(target)
|
|
p = Ether(dst=tmac)/ARP(op="who-has", psrc=victim, pdst=target)
|
|
try:
|
|
while 1:
|
|
sendp(p, iface_hint=target)
|
|
if conf.verb > 1:
|
|
os.write(1,".")
|
|
time.sleep(interval)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4 = None, filter=None, timeout=2, **kargs):
|
|
"""Instant TCP traceroute
|
|
traceroute(target, [maxttl=30], [dport=80], [sport=80]) -> None
|
|
"""
|
|
if filter is None:
|
|
filter="(icmp and icmp[0]=11) or (tcp and (tcp[13] & 0x16 > 0x10))"
|
|
if l4 is None:
|
|
a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/TCP(seq=RandInt(),sport=sport, dport=dport),
|
|
timeout=timeout, filter=filter, **kargs)
|
|
else:
|
|
a,b = sr(IP(dst=target, id=RandShort(), ttl=(minttl,maxttl))/l4,
|
|
timeout=timeout, **kargs)
|
|
|
|
a = TracerouteResult(a.res)
|
|
a.display()
|
|
return a,b
|
|
|
|
|
|
|
|
|
|
def arping(net, timeout=2, cache=0, **kargs):
|
|
"""Send ARP who-has requests to determine which hosts are up
|
|
arping(net, cache=0, iface=conf.iface) -> None
|
|
Set cache=True if you want arping to modify internal ARP-Cache"""
|
|
ans,unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=net),
|
|
filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs)
|
|
ans = ARPingResult(ans.res)
|
|
|
|
if cache and ans is not None:
|
|
for pair in ans:
|
|
arp_cache[pair[1].psrc] = (pair[1].hwsrc, time.time())
|
|
|
|
ans.display()
|
|
return ans,unans
|
|
|
|
def dyndns_add(nameserver, name, rdata, type="A", ttl=10):
|
|
"""Send a DNS add message to a nameserver for "name" to have a new "rdata"
|
|
dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok)
|
|
|
|
example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1")
|
|
RFC2136
|
|
"""
|
|
zone = name[name.find(".")+1:]
|
|
r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5,
|
|
qd=[DNSQR(qname=zone, qtype="SOA")],
|
|
ns=[DNSRR(rrname=name, type="A",
|
|
ttl=ttl, rdata=rdata)]),
|
|
verbose=0, timeout=5)
|
|
if r and r.haslayer(DNS):
|
|
return r.getlayer(DNS).rcode
|
|
else:
|
|
return -1
|
|
|
|
|
|
|
|
|
|
def dyndns_del(nameserver, name, type="ALL", ttl=10):
|
|
"""Send a DNS delete message to a nameserver for "name"
|
|
dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok)
|
|
|
|
example: dyndns_del("ns1.toto.com", "dyn.toto.com")
|
|
RFC2136
|
|
"""
|
|
zone = name[name.find(".")+1:]
|
|
r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5,
|
|
qd=[DNSQR(qname=zone, qtype="SOA")],
|
|
ns=[DNSRR(rrname=name, type=type,
|
|
rclass="ANY", ttl=0, rdata="")]),
|
|
verbose=0, timeout=5)
|
|
if r and r.haslayer(DNS):
|
|
return r.getlayer(DNS).rcode
|
|
else:
|
|
return -1
|
|
|
|
|
|
def is_promisc(ip, fake_bcast="ff:ff:00:00:00:00",**kargs):
|
|
"""Try to guess if target is in Promisc mode. The target is provided by its ip."""
|
|
|
|
responses = srp1(Ether(dst=fake_bcast) / ARP(op="who-has", pdst=ip),type=ETH_P_ARP, iface_hint=ip, timeout=1, verbose=0,**kargs)
|
|
|
|
return responses is not None
|
|
|
|
def promiscping(net, timeout=2, fake_bcast="ff:ff:ff:ff:ff:fe", **kargs):
|
|
"""Send ARP who-has requests to determine which hosts are in promiscuous mode
|
|
promiscping(net, iface=conf.iface)"""
|
|
ans,unans = srp(Ether(dst=fake_bcast)/ARP(pdst=net),
|
|
filter="arp and arp[7] = 2", timeout=timeout, iface_hint=net, **kargs)
|
|
ans = ARPingResult(ans.res, name="PROMISCPing")
|
|
|
|
ans.display()
|
|
return ans,unans
|
|
|
|
def ikescan(ip):
|
|
return sr(IP(dst=ip)/UDP()/ISAKMP(init_cookie=RandString(8),
|
|
exch_type=2)/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal()))
|
|
|
|
|
|
def dhcp_request(iface=None,**kargs):
|
|
if conf.checkIPaddr != 0:
|
|
warning("conf.checkIPaddr is not 0, I may not be able to match the answer")
|
|
if iface is None:
|
|
iface = conf.iface
|
|
fam,hw = get_if_raw_hwaddr(iface)
|
|
return srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)
|
|
/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"]),iface=iface,**kargs)
|
|
|
|
|
|
#####################
|
|
## Reporting stuff ##
|
|
#####################
|
|
|
|
def report_ports(target, ports):
|
|
"""portscan a target and output a LaTeX table
|
|
report_ports(target, ports) -> string"""
|
|
ans,unans = sr(IP(dst=target)/TCP(dport=ports),timeout=5)
|
|
rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n"
|
|
for s,r in ans:
|
|
if not r.haslayer(ICMP):
|
|
if r.payload.flags == 0x12:
|
|
rep += r.sprintf("%TCP.sport% & open & SA \\\\\n")
|
|
rep += "\\hline\n"
|
|
for s,r in ans:
|
|
if r.haslayer(ICMP):
|
|
rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n")
|
|
elif r.payload.flags != 0x12:
|
|
rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n")
|
|
rep += "\\hline\n"
|
|
for i in unans:
|
|
rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n")
|
|
rep += "\\hline\n\\end{tabular}\n"
|
|
return rep
|
|
|
|
|
|
def __make_table(yfmtfunc, fmtfunc, endline, list, fxyz, sortx=None, sorty=None, seplinefunc=None):
|
|
vx = {}
|
|
vy = {}
|
|
vz = {}
|
|
vxf = {}
|
|
vyf = {}
|
|
l = 0
|
|
for e in list:
|
|
xx,yy,zz = map(str, fxyz(e))
|
|
l = max(len(yy),l)
|
|
vx[xx] = max(vx.get(xx,0), len(xx), len(zz))
|
|
vy[yy] = None
|
|
vz[(xx,yy)] = zz
|
|
|
|
vxk = vx.keys()
|
|
vyk = vy.keys()
|
|
if sortx:
|
|
vxk.sort(sortx)
|
|
else:
|
|
try:
|
|
vxk.sort(lambda x,y:int(x)-int(y))
|
|
except:
|
|
try:
|
|
vxk.sort(lambda x,y: cmp(struct.unpack("I", inet_aton(x))[0],struct.unpack("I", inet_aton(y))[0]))
|
|
except:
|
|
vxk.sort()
|
|
if sorty:
|
|
vyk.sort(sorty)
|
|
else:
|
|
try:
|
|
vyk.sort(lambda x,y:int(x)-int(y))
|
|
except:
|
|
try:
|
|
vyk.sort(lambda x,y: cmp(struct.unpack("I", inet_aton(x))[0],struct.unpack("I", inet_aton(y))[0]))
|
|
except:
|
|
vyk.sort()
|
|
|
|
|
|
if seplinefunc:
|
|
sepline = seplinefunc(l, map(lambda x:vx[x],vxk))
|
|
print sepline
|
|
|
|
fmt = yfmtfunc(l)
|
|
print fmt % "",
|
|
for x in vxk:
|
|
vxf[x] = fmtfunc(vx[x])
|
|
print vxf[x] % x,
|
|
print endline
|
|
if seplinefunc:
|
|
print sepline
|
|
for y in vyk:
|
|
print fmt % y,
|
|
for x in vxk:
|
|
print vxf[x] % vz.get((x,y), "-"),
|
|
print endline
|
|
if seplinefunc:
|
|
print sepline
|
|
|
|
def make_table(*args, **kargs):
|
|
__make_table(lambda l:"%%-%is" % l, lambda l:"%%-%is" % l, "", *args, **kargs)
|
|
|
|
def make_lined_table(*args, **kargs):
|
|
__make_table(lambda l:"%%-%is |" % l, lambda l:"%%-%is |" % l, "",
|
|
seplinefunc=lambda a,x:"+".join(map(lambda y:"-"*(y+2), [a-1]+x+[-2])),
|
|
*args, **kargs)
|
|
|
|
def make_tex_table(*args, **kargs):
|
|
__make_table(lambda l: "%s", lambda l: "& %s", "\\\\", seplinefunc=lambda a,x:"\\hline", *args, **kargs)
|
|
|
|
|
|
######################
|
|
## Online doc stuff ##
|
|
######################
|
|
|
|
|
|
def lsc(cmd=None):
|
|
"""List user commands"""
|
|
if cmd is None:
|
|
for c in user_commands:
|
|
doc = "No doc. available"
|
|
if c.__doc__:
|
|
doc = c.__doc__.split("\n")[0]
|
|
|
|
print "%-16s : %s" % (c.__name__, doc)
|
|
else:
|
|
print cmd.__doc__
|
|
|
|
def ls(obj=None):
|
|
"""List available layers, or infos on a given layer"""
|
|
if obj is None:
|
|
import __builtin__
|
|
all = __builtin__.__dict__.copy()
|
|
all.update(globals())
|
|
objlst = filter(lambda (n,o): isinstance(o,type) and issubclass(o,Packet), all.items())
|
|
objlst.sort(lambda x,y:cmp(x[0],y[0]))
|
|
for n,o in objlst:
|
|
print "%-10s : %s" %(n,o.name)
|
|
else:
|
|
if isinstance(obj, type) and issubclass(obj, Packet):
|
|
for f in obj.fields_desc:
|
|
print "%-10s : %-20s = (%s)" % (f.name, f.__class__.__name__, repr(f.default))
|
|
elif isinstance(obj, Packet):
|
|
for f in obj.fields_desc:
|
|
print "%-10s : %-20s = %-15s (%s)" % (f.name, f.__class__.__name__, repr(getattr(obj,f.name)), repr(f.default))
|
|
if not isinstance(obj.payload, NoPayload):
|
|
print "--"
|
|
ls(obj.payload)
|
|
|
|
|
|
else:
|
|
print "Not a packet class. Type 'ls()' to list packet classes."
|
|
|
|
|
|
|
|
|
|
|
|
user_commands = [ sr, sr1, srp, srp1, srloop, srploop, sniff, p0f, arpcachepoison, send, sendp, traceroute, arping, ls, lsc, queso, nmap_fp, report_ports, dyndns_add, dyndns_del, is_promisc, promiscping ]
|
|
|
|
|
|
########################
|
|
## Answering machines ##
|
|
########################
|
|
|
|
class ReferenceAM(type):
|
|
def __new__(cls, name, bases, dct):
|
|
o = super(ReferenceAM, cls).__new__(cls, name, bases, dct)
|
|
if o.function_name:
|
|
globals()[o.function_name] = lambda o=o,*args,**kargs: o(*args,**kargs)()
|
|
return o
|
|
|
|
|
|
class AnsweringMachine(object):
|
|
__metaclass__ = ReferenceAM
|
|
function_name = ""
|
|
filter = None
|
|
sniff_options = { "store":0 }
|
|
sniff_options_list = [ "store", "iface", "count", "promisc", "filter", "type", "prn" ]
|
|
send_options = { "verbose":0 }
|
|
send_options_list = ["iface", "inter", "loop", "verbose"]
|
|
send_function = staticmethod(send)
|
|
|
|
|
|
def __init__(self, **kargs):
|
|
self.mode = 0
|
|
if self.filter:
|
|
kargs.setdefault("filter",self.filter)
|
|
kargs.setdefault("prn", self.reply)
|
|
self.optam1 = {}
|
|
self.optam2 = {}
|
|
self.optam0 = {}
|
|
doptsend,doptsniff = self.parse_all_options(1, kargs)
|
|
self.defoptsend = self.send_options.copy()
|
|
self.defoptsend.update(doptsend)
|
|
self.defoptsniff = self.sniff_options.copy()
|
|
self.defoptsniff.update(doptsniff)
|
|
self.optsend,self.optsniff = [{},{}]
|
|
|
|
def __getattr__(self, attr):
|
|
for d in [self.optam2, self.optam1]:
|
|
if attr in d:
|
|
return d[attr]
|
|
raise AttributeError,attr
|
|
|
|
def __setattr__(self, attr, val):
|
|
mode = self.__dict__.get("mode",0)
|
|
if mode == 0:
|
|
self.__dict__[attr] = val
|
|
else:
|
|
[self.optam1, self.optam2][mode-1][attr] = val
|
|
|
|
def parse_options(self):
|
|
pass
|
|
|
|
def parse_all_options(self, mode, kargs):
|
|
sniffopt = {}
|
|
sendopt = {}
|
|
for k in kargs.keys():
|
|
if k in self.sniff_options_list:
|
|
sniffopt[k] = kargs[k]
|
|
if k in self.send_options_list:
|
|
sendopt[k] = kargs[k]
|
|
if k in self.sniff_options_list+self.send_options_list:
|
|
del(kargs[k])
|
|
if mode != 2 or kargs:
|
|
if mode == 1:
|
|
self.optam0 = kargs
|
|
elif mode == 2 and kargs:
|
|
k = self.optam0.copy()
|
|
k.update(kargs)
|
|
self.parse_options(**k)
|
|
kargs = k
|
|
omode = self.__dict__.get("mode",0)
|
|
self.__dict__["mode"] = mode
|
|
self.parse_options(**kargs)
|
|
self.__dict__["mode"] = omode
|
|
return sendopt,sniffopt
|
|
|
|
def is_request(self, req):
|
|
return 1
|
|
|
|
def make_reply(self, req):
|
|
return req
|
|
|
|
def send_reply(self, reply):
|
|
self.send_function(reply, **self.optsend)
|
|
|
|
def print_reply(self, req, reply):
|
|
print "%s ==> %s" % (req.summary(),reply.summary())
|
|
|
|
def reply(self, pkt):
|
|
if not self.is_request(pkt):
|
|
return
|
|
reply = self.make_reply(pkt)
|
|
self.send_reply(reply)
|
|
if conf.verb >= 0:
|
|
self.print_reply(pkt, reply)
|
|
|
|
def run(self, *args, **kargs):
|
|
log_interactive.warning("run() method deprecated. The intance is now callable")
|
|
self(*args,**kargs)
|
|
|
|
def __call__(self, *args, **kargs):
|
|
optsend,optsniff = self.parse_all_options(2,kargs)
|
|
self.optsend=self.defoptsend.copy()
|
|
self.optsend.update(optsend)
|
|
self.optsniff=self.defoptsniff.copy()
|
|
self.optsniff.update(optsniff)
|
|
|
|
try:
|
|
self.sniff()
|
|
except KeyboardInterrupt:
|
|
print "Interrupted by user"
|
|
|
|
def sniff(self):
|
|
sniff(**self.optsniff)
|
|
|
|
|
|
class BOOTP_am(AnsweringMachine):
|
|
function_name = "bootpd"
|
|
filter = "udp and port 68 and port 67"
|
|
send_function = staticmethod(sendp)
|
|
def parse_options(self, ipset=Net("192.168.1.128/25"),gw="192.168.1.1"):
|
|
if type(ipset) is str:
|
|
ipset = Net(ipset)
|
|
if isinstance(ipset,Gen):
|
|
ipset = [k for k in ipset]
|
|
ipset.reverse()
|
|
if len(ipset) == 1:
|
|
ipset, = ipset
|
|
self.ipset = ipset
|
|
self.gw = gw
|
|
self.leases = {}
|
|
|
|
def is_request(self, req):
|
|
if not req.haslayer(BOOTP):
|
|
return 0
|
|
reqb = req.getlayer(BOOTP)
|
|
if reqb.op != 1:
|
|
return 0
|
|
return 1
|
|
|
|
def print_reply(self, req, reply):
|
|
print "Reply %s to %s" % (reply.getlayer(IP).dst,reply.dst)
|
|
|
|
def make_reply(self, req):
|
|
mac = req.src
|
|
if type(self.ipset) is list:
|
|
if not self.leases.has_key(mac):
|
|
self.leases[mac] = self.ipset.pop()
|
|
ip = self.leases[mac]
|
|
else:
|
|
ip = self.ipset
|
|
|
|
repb = req.getlayer(BOOTP).copy()
|
|
repb.options = ""
|
|
repb.op="BOOTREPLY"
|
|
repb.yiaddr = ip
|
|
repb.siaddr = self.gw
|
|
repb.ciaddr = self.gw
|
|
repb.giaddr = self.gw
|
|
rep=Ether(dst=mac)/IP(dst=ip)/UDP(sport=req.dport,dport=req.sport)/repb
|
|
return rep
|
|
|
|
|
|
class DHCP_am(BOOTP_am):
|
|
function_name="dhcpd"
|
|
def is_request(self, req):
|
|
if not BOOTP_am.is_request(self, req):
|
|
return 0
|
|
if req.getlayer(BOOTP).options[:4] != "'c\x82Sc":
|
|
return 0
|
|
return 1
|
|
def make_reply(self, req):
|
|
dhcprespmap={"\x01":"\x02","\x03":"\x05"}
|
|
resp = BOOTP_am.make_reply(self, req)
|
|
opt = req.getlayer(BOOTP).options
|
|
o = opt[:6]
|
|
if len(opt) > 6:
|
|
o += dhcprespmap.get(opt[6],opt[6])
|
|
if len(opt) > 7:
|
|
o += opt[7:]
|
|
resp.getlayer(BOOTP).options = o
|
|
|
|
|
|
|
|
class DNS_am(AnsweringMachine):
|
|
function_name="dns_spoof"
|
|
filter = "udp port 53"
|
|
|
|
def parse_options(self, joker="192.168.1.1", match=None):
|
|
if match is None:
|
|
self.match = {}
|
|
else:
|
|
self.match = match
|
|
self.joker=joker
|
|
|
|
def is_request(self, req):
|
|
return req.haslayer(DNS) and req.getlayer(DNS).qr == 0
|
|
|
|
def make_reply(self, req):
|
|
ip = req.getlayer(IP)
|
|
dns = req.getlayer(DNS)
|
|
resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport)
|
|
rdata = self.match.get(dns.qd.qname, self.joker)
|
|
resp /= DNS(id=dns.id, qr=1, qd=dns.qd,
|
|
an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata))
|
|
return resp
|
|
|
|
|
|
class WiFi_am(AnsweringMachine):
|
|
"""Before using this, initialize "iffrom" and "ifto" interfaces:
|
|
iwconfig iffrom mode monitor
|
|
iwpriv orig_ifto hostapd 1
|
|
ifconfig ifto up
|
|
note: if ifto=wlan0ap then orig_ifto=wlan0
|
|
note: ifto and iffrom must be set on the same channel
|
|
ex:
|
|
ifconfig eth1 up
|
|
iwconfig eth1 mode monitor
|
|
iwconfig eth1 channel 11
|
|
iwpriv wlan0 hostapd 1
|
|
ifconfig wlan0ap up
|
|
iwconfig wlan0 channel 11
|
|
iwconfig wlan0 essid dontexist
|
|
iwconfig wlan0 mode managed
|
|
"""
|
|
function_name = "airpwn"
|
|
filter = None
|
|
|
|
def parse_options(iffrom, ifto, replace, pattern="", ignorepattern=""):
|
|
self.iffrom = iffrom
|
|
self.ifto = ifto
|
|
ptrn = re.compile(pattern)
|
|
iptrn = re.compile(ignorepattern)
|
|
|
|
def is_request(self, pkt):
|
|
if not isinstance(pkt,Dot11):
|
|
return 0
|
|
if not pkt.FCfield & 1:
|
|
return 0
|
|
if not pkt.haslayer(TCP):
|
|
return 0
|
|
ip = pkt.getlayer(IP)
|
|
tcp = pkt.getlayer(TCP)
|
|
pay = str(tcp.payload)
|
|
if not self.ptrn.match(pay):
|
|
return 0
|
|
if self.iptrn.match(pay):
|
|
return 0
|
|
|
|
def make_reply(self, p):
|
|
ip = p.getlayer(IP)
|
|
tcp = p.getlayer(TCP)
|
|
pay = str(tcp.payload)
|
|
del(p.payload.payload.payload)
|
|
p.FCfield="from-DS"
|
|
p.addr1,p.addr2 = p.addr2,p.addr1
|
|
p /= IP(src=ip.dst,dst=ip.src)
|
|
p /= TCP(sport=tcp.dport, dport=tcp.sport,
|
|
seq=tcp.ack, ack=tcp.seq+len(pay),
|
|
flags="PA")
|
|
q = p.copy()
|
|
p /= self.replace
|
|
q.ID += 1
|
|
q.getlayer(TCP).flags="RA"
|
|
q.getlayer(TCP).seq+=len(replace)
|
|
return [p,q]
|
|
|
|
def print_reply(self):
|
|
print p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%")
|
|
|
|
def send_reply(self, reply):
|
|
sendp(reply, iface=self.ifto, **self.optsend)
|
|
|
|
def sniff(self):
|
|
sniff(iface=self.iffrom, **self.optsniff)
|
|
|
|
|
|
|
|
class ARP_am(AnsweringMachine):
|
|
function_name="farpd"
|
|
filter = "arp"
|
|
send_function = staticmethod(sendp)
|
|
|
|
def parse_options(self, IP_addr=None, iface=None, ARP_addr=None):
|
|
self.IP_addr=IP_addr
|
|
self.iface=iface
|
|
self.ARP_addr=ARP_addr
|
|
|
|
def is_request(self, req):
|
|
return (req.haslayer(ARP) and
|
|
req.getlayer(ARP).op == 1 and
|
|
(self.IP_addr == None or self.IP_addr == req.getlayer(ARP).pdst))
|
|
|
|
def make_reply(self, req):
|
|
ether = req.getlayer(Ether)
|
|
arp = req.getlayer(ARP)
|
|
iff,a,gw = conf.route.route(arp.psrc)
|
|
if self.iface != None:
|
|
iff = iface
|
|
ARP_addr = self.ARP_addr
|
|
IP_addr = arp.pdst
|
|
resp = Ether(dst=ether.src,
|
|
src=ARP_addr)/ARP(op="is-at",
|
|
hwsrc=ARP_addr,
|
|
psrc=IP_addr,
|
|
hwdst=arp.hwsrc,
|
|
pdst=arp.pdst)
|
|
return resp
|
|
|
|
def sniff(self):
|
|
sniff(iface=self.iface, **self.optsniff)
|
|
|
|
|
|
#############
|
|
## Fuzzing ##
|
|
#############
|
|
|
|
|
|
def fuzz(p, _inplace=0):
|
|
if not _inplace:
|
|
p = p.copy()
|
|
q = p
|
|
while not isinstance(q, NoPayload):
|
|
for f in q.fields_desc:
|
|
if isinstance(f, PacketListField):
|
|
for r in getattr(q, f.name):
|
|
print "fuzzing", repr(r)
|
|
fuzz(r, _inplace=1)
|
|
elif f.default is not None:
|
|
rnd = f.randval()
|
|
if rnd is not None:
|
|
q.default_fields[f] = rnd
|
|
q = q.payload
|
|
return p
|
|
|
|
|
|
|
|
|
|
###################
|
|
## Testing stuff ##
|
|
###################
|
|
|
|
|
|
|
|
def merge(x,y):
|
|
if len(x) > len(y):
|
|
y += "\x00"*(len(x)-len(y))
|
|
elif len(x) < len(y):
|
|
x += "\x00"*(len(y)-len(x))
|
|
m = ""
|
|
for i in range(len(x)/ss):
|
|
m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)]
|
|
return m
|
|
# return "".join(map(str.__add__, x, y))
|
|
|
|
|
|
def voip_play(s1,list=None,**kargs):
|
|
FIFO="/tmp/conv1.%i.%%i" % os.getpid()
|
|
FIFO1=FIFO % 1
|
|
FIFO2=FIFO % 2
|
|
|
|
os.mkfifo(FIFO1)
|
|
os.mkfifo(FIFO2)
|
|
try:
|
|
os.system("soxmix -t .ul %s -t .ul %s -t ossdsp /dev/dsp &" % (FIFO1,FIFO2))
|
|
|
|
c1=open(FIFO1,"w", 4096)
|
|
c2=open(FIFO2,"w", 4096)
|
|
fcntl.fcntl(c1.fileno(),fcntl.F_SETFL, os.O_NONBLOCK)
|
|
fcntl.fcntl(c2.fileno(),fcntl.F_SETFL, os.O_NONBLOCK)
|
|
|
|
# dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp")
|
|
def play(pkt,last=[]):
|
|
if not pkt:
|
|
return
|
|
if not pkt.haslayer(UDP):
|
|
return
|
|
ip=pkt.getlayer(IP)
|
|
if s1 in [ip.src, ip.dst]:
|
|
if not last:
|
|
last.append(pkt)
|
|
return
|
|
load=last.pop()
|
|
# x1 = load.load[12:]
|
|
c1.write(load.load[12:])
|
|
if load.getlayer(IP).src == ip.src:
|
|
# x2 = ""
|
|
c2.write("\x00"*len(load.load[12:]))
|
|
last.append(pkt)
|
|
else:
|
|
# x2 = pkt.load[:12]
|
|
c2.write(pkt.load[12:])
|
|
# dsp.write(merge(x1,x2))
|
|
|
|
if list is None:
|
|
sniff(store=0, prn=play, **kargs)
|
|
else:
|
|
for p in list:
|
|
play(p)
|
|
finally:
|
|
os.unlink(FIFO1)
|
|
os.unlink(FIFO2)
|
|
|
|
|
|
|
|
def voip_play1(s1,list=None,**kargs):
|
|
|
|
|
|
dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp")
|
|
def play(pkt):
|
|
if not pkt:
|
|
return
|
|
if not pkt.haslayer(UDP):
|
|
return
|
|
ip=pkt.getlayer(IP)
|
|
if s1 in [ip.src, ip.dst]:
|
|
dsp.write(pkt.getlayer(Raw).load[12:])
|
|
try:
|
|
if list is None:
|
|
sniff(store=0, prn=play, **kargs)
|
|
else:
|
|
for p in list:
|
|
play(p)
|
|
finally:
|
|
dsp.close()
|
|
rd.close()
|
|
|
|
def voip_play2(s1,**kargs):
|
|
dsp,rd = os.popen2("sox -t .ul -c 2 - -t ossdsp /dev/dsp")
|
|
def play(pkt,last=[]):
|
|
if not pkt:
|
|
return
|
|
if not pkt.haslayer(UDP):
|
|
return
|
|
ip=pkt.getlayer(IP)
|
|
if s1 in [ip.src, ip.dst]:
|
|
if not last:
|
|
last.append(pkt)
|
|
return
|
|
load=last.pop()
|
|
x1 = load.load[12:]
|
|
# c1.write(load.load[12:])
|
|
if load.getlayer(IP).src == ip.src:
|
|
x2 = ""
|
|
# c2.write("\x00"*len(load.load[12:]))
|
|
last.append(pkt)
|
|
else:
|
|
x2 = pkt.load[:12]
|
|
# c2.write(pkt.load[12:])
|
|
dsp.write(merge(x1,x2))
|
|
|
|
sniff(store=0, prn=play, **kargs)
|
|
|
|
def voip_play3(lst=None,**kargs):
|
|
dsp,rd = os.popen2("sox -t .ul - -t ossdsp /dev/dsp")
|
|
try:
|
|
def play(pkt, dsp=dsp):
|
|
if pkt and pkt.haslayer(UDP) and pkt.haslayer(Raw):
|
|
dsp.write(pkt.getlayer(Raw).load[12:])
|
|
if lst is None:
|
|
sniff(store=0, prn=play, **kargs)
|
|
else:
|
|
for p in lst:
|
|
play(p)
|
|
finally:
|
|
try:
|
|
dsp.close()
|
|
rd.close()
|
|
except:
|
|
pass
|
|
|
|
|
|
def IPID_count(lst, funcID=lambda x:x[1].id, funcpres=lambda x:x[1].summary()):
|
|
idlst = map(funcID, lst)
|
|
idlst.sort()
|
|
classes = [idlst[0]]+map(lambda x:x[1],filter(lambda (x,y): abs(x-y)>50, map(lambda x,y: (x,y),idlst[:-1], idlst[1:])))
|
|
lst = map(lambda x:(funcID(x), funcpres(x)), lst)
|
|
lst.sort()
|
|
print "Probably %i classes:" % len(classes), classes
|
|
for id,pr in lst:
|
|
print "%5i" % id, pr
|
|
|
|
|
|
|
|
|
|
|
|
last=None
|
|
|
|
|
|
def tethereal(*args,**kargs):
|
|
sniff(prn=lambda x: x.display(),*args,**kargs)
|
|
|
|
|
|
|
|
def fragleak(target,sport=123, dport=123, timeout=0.2, onlyasc=0):
|
|
load = "XXXXYYYYYYYYYY"
|
|
# getmacbyip(target)
|
|
# pkt = IP(dst=target, id=RandShort(), options="\x22"*40)/UDP()/load
|
|
pkt = IP(dst=target, id=RandShort(), options="\x00"*40, flags=1)/UDP(sport=sport, dport=sport)/load
|
|
s=conf.L3socket()
|
|
intr=0
|
|
found={}
|
|
try:
|
|
while 1:
|
|
try:
|
|
if not intr:
|
|
s.send(pkt)
|
|
sin,sout,serr = select([s],[],[],timeout)
|
|
if not sin:
|
|
continue
|
|
ans=s.recv(1600)
|
|
if not isinstance(ans, IP): #TODO: IPv6
|
|
continue
|
|
if not isinstance(ans.payload, ICMP):
|
|
continue
|
|
if not isinstance(ans.payload.payload, IPerror):
|
|
continue
|
|
if ans.payload.payload.dst != target:
|
|
continue
|
|
if ans.src != target:
|
|
print "leak from", ans.src,
|
|
|
|
|
|
# print repr(ans)
|
|
if not ans.haslayer(Padding):
|
|
continue
|
|
|
|
|
|
# print repr(ans.payload.payload.payload.payload)
|
|
|
|
# if not isinstance(ans.payload.payload.payload.payload, Raw):
|
|
# continue
|
|
# leak = ans.payload.payload.payload.payload.load[len(load):]
|
|
leak = ans.getlayer(Padding).load
|
|
if leak not in found:
|
|
found[leak]=None
|
|
linehexdump(leak, onlyasc=onlyasc)
|
|
except KeyboardInterrupt:
|
|
if intr:
|
|
raise KeyboardInterrupt
|
|
intr=1
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
def fragleak2(target, timeout=0.4, onlyasc=0):
|
|
found={}
|
|
try:
|
|
while 1:
|
|
p = sr1(IP(dst=target, options="\x00"*40, proto=200)/"XXXXYYYYYYYYYYYY",timeout=timeout,verbose=0)
|
|
if not p:
|
|
continue
|
|
if Padding in p:
|
|
leak = p[Padding].load
|
|
if leak not in found:
|
|
found[leak]=None
|
|
linehexdump(leak,onlyasc=onlyasc)
|
|
except:
|
|
pass
|
|
|
|
|
|
|
|
plst=[]
|
|
def get_toDS():
|
|
global plst
|
|
while 1:
|
|
p,=sniff(iface="eth1",count=1)
|
|
if not isinstance(p,Dot11):
|
|
continue
|
|
if p.FCfield & 1:
|
|
plst.append(p)
|
|
print "."
|
|
|
|
|
|
# if not ifto.endswith("ap"):
|
|
# print "iwpriv %s hostapd 1" % ifto
|
|
# os.system("iwpriv %s hostapd 1" % ifto)
|
|
# ifto += "ap"
|
|
#
|
|
# os.system("iwconfig %s mode monitor" % iffrom)
|
|
#
|
|
|
|
def airpwn(iffrom, ifto, replace, pattern="", ignorepattern=""):
|
|
"""Before using this, initialize "iffrom" and "ifto" interfaces:
|
|
iwconfig iffrom mode monitor
|
|
iwpriv orig_ifto hostapd 1
|
|
ifconfig ifto up
|
|
note: if ifto=wlan0ap then orig_ifto=wlan0
|
|
note: ifto and iffrom must be set on the same channel
|
|
ex:
|
|
ifconfig eth1 up
|
|
iwconfig eth1 mode monitor
|
|
iwconfig eth1 channel 11
|
|
iwpriv wlan0 hostapd 1
|
|
ifconfig wlan0ap up
|
|
iwconfig wlan0 channel 11
|
|
iwconfig wlan0 essid dontexist
|
|
iwconfig wlan0 mode managed
|
|
"""
|
|
|
|
ptrn = re.compile(pattern)
|
|
iptrn = re.compile(ignorepattern)
|
|
def do_airpwn(p, ifto=ifto, replace=replace, ptrn=ptrn, iptrn=iptrn):
|
|
if not isinstance(p,Dot11):
|
|
return
|
|
if not p.FCfield & 1:
|
|
return
|
|
if not p.haslayer(TCP):
|
|
return
|
|
ip = p.getlayer(IP)
|
|
tcp = p.getlayer(TCP)
|
|
pay = str(tcp.payload)
|
|
# print "got tcp"
|
|
if not ptrn.match(pay):
|
|
return
|
|
# print "match 1"
|
|
if iptrn.match(pay):
|
|
return
|
|
# print "match 2"
|
|
del(p.payload.payload.payload)
|
|
p.FCfield="from-DS"
|
|
p.addr1,p.addr2 = p.addr2,p.addr1
|
|
q = p.copy()
|
|
p /= IP(src=ip.dst,dst=ip.src)
|
|
p /= TCP(sport=tcp.dport, dport=tcp.sport,
|
|
seq=tcp.ack, ack=tcp.seq+len(pay),
|
|
flags="PA")
|
|
q = p.copy()
|
|
p /= replace
|
|
q.ID += 1
|
|
q.getlayer(TCP).flags="RA"
|
|
q.getlayer(TCP).seq+=len(replace)
|
|
|
|
sendp([p,q], iface=ifto, verbose=0)
|
|
# print "send",repr(p)
|
|
# print "send",repr(q)
|
|
print p.sprintf("Sent %IP.src%:%IP.sport% > %IP.dst%:%TCP.dport%")
|
|
|
|
sniff(iface=iffrom,prn=do_airpwn)
|
|
|
|
|
|
|
|
|
|
##################
|
|
## Color themes ##
|
|
##################
|
|
|
|
class Color:
|
|
normal = "\033[0m"
|
|
black = "\033[30m"
|
|
red = "\033[31m"
|
|
green = "\033[32m"
|
|
yellow = "\033[33m"
|
|
blue = "\033[34m"
|
|
purple = "\033[35m"
|
|
cyan = "\033[36m"
|
|
grey = "\033[37m"
|
|
|
|
bold = "\033[1m"
|
|
uline = "\033[4m"
|
|
blink = "\033[5m"
|
|
invert = "\033[7m"
|
|
|
|
|
|
class ColorTheme:
|
|
def __repr__(self):
|
|
return "<%s>" % self.__class__.__name__
|
|
def __getattr__(self, attr):
|
|
return lambda x:x
|
|
|
|
|
|
class NoTheme(ColorTheme):
|
|
pass
|
|
|
|
|
|
class AnsiColorTheme(ColorTheme):
|
|
def __getattr__(self, attr):
|
|
s = "style_%s" % attr
|
|
if s in self.__class__.__dict__:
|
|
before = getattr(self, s)
|
|
after = self.style_normal
|
|
else:
|
|
before = after = ""
|
|
|
|
def do_style(val, fmt=None, before=before, after=after):
|
|
if fmt is None:
|
|
if type(val) is not str:
|
|
val = str(val)
|
|
else:
|
|
val = fmt % val
|
|
return before+val+after
|
|
return do_style
|
|
|
|
|
|
style_normal = ""
|
|
style_prompt = ""
|
|
style_punct = ""
|
|
style_id = ""
|
|
style_not_printable = ""
|
|
style_layer_name = ""
|
|
style_field_name = ""
|
|
style_field_value = ""
|
|
style_emph_field_name = ""
|
|
style_emph_field_value = ""
|
|
style_packetlist_name = ""
|
|
style_packetlist_proto = ""
|
|
style_packetlist_value = ""
|
|
style_fail = ""
|
|
style_success = ""
|
|
style_odd = ""
|
|
style_even = ""
|
|
style_opening = ""
|
|
style_active = ""
|
|
style_closed = ""
|
|
|
|
class BlackAndWhite(AnsiColorTheme):
|
|
pass
|
|
|
|
class DefaultTheme(AnsiColorTheme):
|
|
style_normal = Color.normal
|
|
style_prompt = Color.blue+Color.bold
|
|
style_punct = Color.normal
|
|
style_id = Color.blue+Color.bold
|
|
style_not_printable = Color.grey
|
|
style_layer_name = Color.red+Color.bold
|
|
style_field_name = Color.blue
|
|
style_field_value = Color.purple
|
|
style_emph_field_name = Color.blue+Color.uline+Color.bold
|
|
style_emph_field_value = Color.purple+Color.uline+Color.bold
|
|
style_packetlist_name = Color.red+Color.bold
|
|
style_packetlist_proto = Color.blue
|
|
style_packetlist_value = Color.purple
|
|
style_fail = Color.red+Color.bold
|
|
style_success = Color.blue+Color.bold
|
|
style_even = Color.black+Color.bold
|
|
style_odd = Color.black
|
|
style_opening = Color.yellow
|
|
style_active = Color.black
|
|
style_closed = Color.grey
|
|
|
|
class BrightTheme(AnsiColorTheme):
|
|
style_normal = Color.normal
|
|
style_punct = Color.normal
|
|
style_id = Color.yellow+Color.bold
|
|
style_layer_name = Color.red+Color.bold
|
|
style_field_name = Color.yellow+Color.bold
|
|
style_field_value = Color.purple+Color.bold
|
|
style_emph_field_name = Color.yellow+Color.bold
|
|
style_emph_field_value = Color.green+Color.bold
|
|
style_packetlist_name = Color.red+Color.bold
|
|
style_packetlist_proto = Color.yellow+Color.bold
|
|
style_packetlist_value = Color.purple+Color.bold
|
|
style_fail = Color.red+Color.bold
|
|
style_success = Color.blue+Color.bold
|
|
style_even = Color.black+Color.bold
|
|
style_odd = Color.black
|
|
|
|
|
|
class RastaTheme(AnsiColorTheme):
|
|
style_normal = Color.green+Color.bold
|
|
style_prompt = Color.yellow+Color.bold
|
|
style_punct = Color.red
|
|
style_id = Color.green+Color.bold
|
|
style_not_printable = Color.green
|
|
style_layer_name = Color.red+Color.bold
|
|
style_field_name = Color.yellow+Color.bold
|
|
style_field_value = Color.green+Color.bold
|
|
style_emph_field_name = Color.green
|
|
style_emph_field_value = Color.green
|
|
style_packetlist_name = Color.red+Color.bold
|
|
style_packetlist_proto = Color.yellow+Color.bold
|
|
style_packetlist_value = Color.green+Color.bold
|
|
style_fail = Color.red
|
|
style_success = Color.red+Color.bold
|
|
style_even = Color.yellow
|
|
style_odd = Color.green
|
|
|
|
|
|
class FormatTheme(ColorTheme):
|
|
def __getattr__(self, attr):
|
|
col = self.__class__.__dict__.get("style_%s" % attr, "%s")
|
|
def do_style(val, fmt=None, col=col):
|
|
if fmt is None:
|
|
if type(val) is not str:
|
|
val = str(val)
|
|
else:
|
|
val = fmt % val
|
|
return col % val
|
|
return do_style
|
|
|
|
|
|
class LatexTheme(FormatTheme):
|
|
style_prompt = r"\textcolor{blue}{%s}"
|
|
style_not_printable = r"\textcolor{gray}{%s}"
|
|
style_layer_name = r"\textcolor{red}{\bf %s}"
|
|
style_field_name = r"\textcolor{blue}{%s}"
|
|
style_field_value = r"\textcolor{purple}{%s}"
|
|
style_emph_field_name = r"\textcolor{blue}{\underline{%s}}" #ul
|
|
style_emph_field_value = r"\textcolor{purple}{\underline{%s}}" #ul
|
|
style_packetlist_name = r"\textcolor{red}{\bf %s}"
|
|
style_packetlist_proto = r"\textcolor{blue}{%s}"
|
|
style_packetlist_value = r"\textcolor{purple}{%s}"
|
|
style_fail = r"\textcolor{red}{\bf %s}"
|
|
style_success = r"\textcolor{blue}{\bf %s}"
|
|
# style_even = r"}{\bf "
|
|
# style_odd = ""
|
|
|
|
class LatexTheme2(FormatTheme):
|
|
style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@"
|
|
style_not_printable = r"@`@textcolor@[@gray@]@@[@%s@]@"
|
|
style_layer_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@"
|
|
style_field_name = r"@`@textcolor@[@blue@]@@[@%s@]@"
|
|
style_field_value = r"@`@textcolor@[@purple@]@@[@%s@]@"
|
|
style_emph_field_name = r"@`@textcolor@[@blue@]@@[@@`@underline@[@%s@]@@]@"
|
|
style_emph_field_value = r"@`@textcolor@[@purple@]@@[@@`@underline@[@%s@]@@]@"
|
|
style_packetlist_name = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@"
|
|
style_packetlist_proto = r"@`@textcolor@[@blue@]@@[@%s@]@"
|
|
style_packetlist_value = r"@`@textcolor@[@purple@]@@[@%s@]@"
|
|
style_fail = r"@`@textcolor@[@red@]@@[@@`@bfseries@[@@]@%s@]@"
|
|
style_success = r"@`@textcolor@[@blue@]@@[@@`@bfserices@[@@]@%s@]@"
|
|
style_even = r"@`@textcolor@[@gray@]@@[@@`@bfseries@[@@]@%s@]@"
|
|
# style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@"
|
|
|
|
class HTMLTheme(FormatTheme):
|
|
style_prompt = "<span class=prompt>%s</span>"
|
|
style_not_printable = "<span class=not_printable>%s</span>"
|
|
style_layer_name = "<span class=layer_name>%s</span>"
|
|
style_field_name = "<span class=field_name>%s</span>"
|
|
style_field_value = "<span class=field_value>%s</span>"
|
|
style_emph_field_name = "<span class=emph_field_name>%s</span>"
|
|
style_emph_field_value = "<span class=emph_field_value>%s</span>"
|
|
style_packetlist_name = "<span class=packetlist_name>%s</span>"
|
|
style_packetlist_proto = "<span class=packetlist_proto>%s</span>"
|
|
style_packetlist_value = "<span class=packetlist_value>%s</span>"
|
|
style_fail = "<span class=fail>%s</span>"
|
|
style_success = "<span class=success>%s</span>"
|
|
style_even = "<span class=even>%s</span>"
|
|
style_odd = "<span class=odd>%s</span>"
|
|
|
|
class HTMLTheme2(HTMLTheme):
|
|
style_prompt = "#[#span class=prompt#]#%s#[#/span#]#"
|
|
style_not_printable = "#[#span class=not_printable#]#%s#[#/span#]#"
|
|
style_layer_name = "#[#span class=layer_name#]#%s#[#/span#]#"
|
|
style_field_name = "#[#span class=field_name#]#%s#[#/span#]#"
|
|
style_field_value = "#[#span class=field_value#]#%s#[#/span#]#"
|
|
style_emph_field_name = "#[#span class=emph_field_name#]#%s#[#/span#]#"
|
|
style_emph_field_value = "#[#span class=emph_field_value#]#%s#[#/span#]#"
|
|
style_packetlist_name = "#[#span class=packetlist_name#]#%s#[#/span#]#"
|
|
style_packetlist_proto = "#[#span class=packetlist_proto#]#%s#[#/span#]#"
|
|
style_packetlist_value = "#[#span class=packetlist_value#]#%s#[#/span#]#"
|
|
style_fail = "#[#span class=fail#]#%s#[#/span#]#"
|
|
style_success = "#[#span class=success#]#%s#[#/span#]#"
|
|
style_even = "#[#span class=even#]#%s#[#/span#]#"
|
|
style_odd = "#[#span class=odd#]#%s#[#/span#]#"
|
|
|
|
|
|
class ColorPrompt:
|
|
__prompt = ">>> "
|
|
def __str__(self):
|
|
try:
|
|
ct = conf.color_theme
|
|
if isinstance(ct, AnsiColorTheme):
|
|
## ^A and ^B delimit invisible caracters for readline to count right
|
|
return "\001%s\002" % ct.prompt("\002"+conf.prompt+"\001")
|
|
else:
|
|
return ct.prompt(conf.prompt)
|
|
except:
|
|
return self.__prompt
|
|
|
|
############
|
|
## Config ##
|
|
############
|
|
|
|
class ConfClass:
|
|
def configure(self, cnf):
|
|
self.__dict__ = cnf.__dict__.copy()
|
|
def __repr__(self):
|
|
return str(self)
|
|
def __str__(self):
|
|
s="Version = %s\n" % VERSION
|
|
keys = self.__class__.__dict__.copy()
|
|
keys.update(self.__dict__)
|
|
keys = keys.keys()
|
|
keys.sort()
|
|
for i in keys:
|
|
if i[0] != "_":
|
|
s += "%-10s = %s\n" % (i, repr(getattr(self, i)))
|
|
return s[:-1]
|
|
def reset(self):
|
|
self.__dict__ = {}
|
|
|
|
|
|
class ProgPath(ConfClass):
|
|
pdfreader = "acroread"
|
|
psreader = "gv"
|
|
dot = "dot"
|
|
display = "display"
|
|
tcpdump = "tcpdump"
|
|
|
|
|
|
|
|
class Conf(ConfClass):
|
|
"""This object contains the configuration of scapy.
|
|
session : filename where the session will be saved
|
|
stealth : if 1, prevents any unwanted packet to go out (ARP, DNS, ...)
|
|
checkIPID: if 0, doesn't check that IPID matches between IP sent and ICMP IP citation received
|
|
if 1, checks that they either are equal or byte swapped equals (bug in some IP stacks)
|
|
if 2, strictly checks that they are equals
|
|
checkIPsrc: if 1, checks IP src in IP and ICMP IP citation match (bug in some NAT stacks)
|
|
check_TCPerror_seqack: if 1, also check that TCP seq and ack match the ones in ICMP citation
|
|
iff : selects the default output interface for srp() and sendp(). default:"eth0")
|
|
verb : level of verbosity, from 0 (almost mute) to 3 (verbose)
|
|
promisc : default mode for listening socket (to get answers if you spoof on a lan)
|
|
sniff_promisc : default mode for sniff()
|
|
filter : bpf filter added to every sniffing socket to exclude traffic from analysis
|
|
histfile : history file
|
|
padding : includes padding in desassembled packets
|
|
except_filter : BPF filter for packets to ignore
|
|
debug_match : when 1, store received packet that are not matched into debug.recv
|
|
route : holds the Scapy routing table and provides methods to manipulate it
|
|
warning_threshold : how much time between warnings from the same place
|
|
"""
|
|
session = ""
|
|
stealth = "not implemented"
|
|
iface = get_working_if()
|
|
checkIPID = 0
|
|
checkIPsrc = 1
|
|
checkIPaddr = 1
|
|
check_TCPerror_seqack = 0
|
|
verb = 2
|
|
prompt = ">>> "
|
|
promisc = 1
|
|
sniff_promisc = 1
|
|
L3socket = L3PacketSocket
|
|
L2socket = L2Socket
|
|
L2listen = L2ListenSocket
|
|
BTsocket = BluetoothL2CAPSocket
|
|
histfile = os.path.join(os.environ["HOME"], ".scapy_history")
|
|
padding = 1
|
|
p0f_base ="/etc/p0f/p0f.fp"
|
|
queso_base ="/etc/queso.conf"
|
|
nmap_base ="/usr/share/nmap/nmap-os-fingerprints"
|
|
IPCountry_base = "GeoIPCountry4Scapy.gz"
|
|
countryLoc_base = "countryLoc.csv"
|
|
gnuplot_world = "world.dat"
|
|
except_filter = ""
|
|
debug_match = 0
|
|
route = Route()
|
|
wepkey = ""
|
|
auto_fragment = 1
|
|
debug_dissector = 0
|
|
color_theme = DefaultTheme()
|
|
warning_threshold = 5
|
|
prog = ProgPath()
|
|
|
|
|
|
conf=Conf()
|
|
|
|
if PCAP:
|
|
conf.L2listen=L2pcapListenSocket
|
|
if DNET:
|
|
conf.L3socket=L3dnetSocket
|
|
conf.L2socket=L2dnetSocket
|
|
|
|
|
|
p0f_kdb = p0fKnowledgeBase(conf.p0f_base)
|
|
queso_kdb = QuesoKnowledgeBase(conf.queso_base)
|
|
nmap_kdb = NmapKnowledgeBase(conf.nmap_base)
|
|
IP_country_kdb = IPCountryKnowledgeBase(conf.IPCountry_base)
|
|
country_loc_kdb = CountryLocKnowledgeBase(conf.countryLoc_base)
|
|
|
|
|
|
#########################
|
|
##### Autorun stuff #####
|
|
#########################
|
|
|
|
|
|
class ScapyAutorunInterpreter(code.InteractiveInterpreter):
|
|
def __init__(self, *args, **kargs):
|
|
code.InteractiveInterpreter.__init__(self, *args, **kargs)
|
|
self.error = 0
|
|
def showsyntaxerror(self, *args, **kargs):
|
|
self.error = 1
|
|
return code.InteractiveInterpreter.showsyntaxerror(self, *args, **kargs)
|
|
def showtraceback(self, *args, **kargs):
|
|
self.error = 1
|
|
return code.InteractiveInterpreter.showtraceback(self, *args, **kargs)
|
|
|
|
|
|
def autorun_commands(cmds,my_globals=None,verb=0):
|
|
sv = conf.verb
|
|
import __builtin__
|
|
try:
|
|
if my_globals is None:
|
|
my_globals = globals()
|
|
conf.verb = verb
|
|
interp = ScapyAutorunInterpreter(my_globals)
|
|
cmd = ""
|
|
cmds = cmds.splitlines()
|
|
cmds.append("") # ensure we finish multiline commands
|
|
cmds.reverse()
|
|
__builtin__.__dict__["_"] = None
|
|
while 1:
|
|
if cmd:
|
|
sys.stderr.write(sys.__dict__.get("ps2","... "))
|
|
else:
|
|
sys.stderr.write(str(sys.__dict__.get("ps1",ColorPrompt())))
|
|
|
|
l = cmds.pop()
|
|
print l
|
|
cmd += "\n"+l
|
|
if interp.runsource(cmd):
|
|
continue
|
|
if interp.error:
|
|
return 0
|
|
cmd = ""
|
|
if len(cmds) <= 1:
|
|
break
|
|
finally:
|
|
conf.verb = sv
|
|
return _
|
|
|
|
def autorun_get_interactive_session(cmds, **kargs):
|
|
class StringWriter:
|
|
def __init__(self):
|
|
self.s = ""
|
|
def write(self, x):
|
|
self.s += x
|
|
|
|
sw = StringWriter()
|
|
sstdout,sstderr = sys.stdout,sys.stderr
|
|
try:
|
|
sys.stdout = sys.stderr = sw
|
|
res = autorun_commands(cmds, **kargs)
|
|
finally:
|
|
sys.stdout,sys.stderr = sstdout,sstderr
|
|
return sw.s,res
|
|
|
|
def autorun_get_text_interactive_session(cmds, **kargs):
|
|
ct = conf.color_theme
|
|
try:
|
|
conf.color_theme = NoTheme()
|
|
s,res = autorun_get_interactive_session(cmds, **kargs)
|
|
finally:
|
|
conf.color_theme = ct
|
|
return s,res
|
|
|
|
def autorun_get_ansi_interactive_session(cmds, **kargs):
|
|
ct = conf.color_theme
|
|
try:
|
|
conf.color_theme = DefaultTheme()
|
|
s,res = autorun_get_interactive_session(cmds, **kargs)
|
|
finally:
|
|
conf.color_theme = ct
|
|
return s,res
|
|
|
|
def autorun_get_html_interactive_session(cmds, **kargs):
|
|
ct = conf.color_theme
|
|
try:
|
|
conf.color_theme = HTMLTheme2()
|
|
s,res = autorun_get_interactive_session(cmds, **kargs)
|
|
finally:
|
|
conf.color_theme = ct
|
|
|
|
s = s.replace("<","<").replace(">",">").replace("#[#","<").replace("#]#",">")
|
|
return s,res
|
|
|
|
def autorun_get_latex_interactive_session(cmds, **kargs):
|
|
ct = conf.color_theme
|
|
try:
|
|
conf.color_theme = LatexTheme2()
|
|
s,res = autorun_get_interactive_session(cmds, **kargs)
|
|
finally:
|
|
conf.color_theme = ct
|
|
s = tex_escape(s)
|
|
s = s.replace("@[@","{").replace("@]@","}").replace("@`@","\\")
|
|
return s,res
|
|
|
|
|
|
################
|
|
##### Main #####
|
|
################
|
|
|
|
def scapy_write_history_file(readline):
|
|
if conf.histfile:
|
|
readline.write_history_file(conf.histfile)
|
|
|
|
|
|
def interact(mydict=None,argv=None,mybanner=None,loglevel=1):
|
|
import code,sys,cPickle,types,os,imp,getopt,logging
|
|
|
|
logging.getLogger("scapy").setLevel(loglevel)
|
|
|
|
the_banner = "Welcome to Scapy (%s)"
|
|
if mybanner is not None:
|
|
the_banner += "\n"
|
|
the_banner += mybanner
|
|
|
|
if argv is None:
|
|
argv = sys.argv
|
|
|
|
# scapy_module = argv[0][argv[0].rfind("/")+1:]
|
|
# if not scapy_module:
|
|
# scapy_module = "scapy"
|
|
# else:
|
|
# if scapy_module.endswith(".py"):
|
|
# scapy_module = scapy_module[:-3]
|
|
#
|
|
# scapy=imp.load_module("scapy",*imp.find_module(scapy_module))
|
|
|
|
|
|
import __builtin__
|
|
# __builtin__.__dict__.update(scapy.__dict__)
|
|
__builtin__.__dict__.update(globals())
|
|
if mydict is not None:
|
|
__builtin__.__dict__.update(mydict)
|
|
|
|
|
|
import re, atexit
|
|
try:
|
|
import rlcompleter,readline
|
|
except ImportError:
|
|
log_loading.info("Can't load Python libreadline or completer")
|
|
READLINE=0
|
|
else:
|
|
READLINE=1
|
|
class ScapyCompleter(rlcompleter.Completer):
|
|
def global_matches(self, text):
|
|
matches = []
|
|
n = len(text)
|
|
for lst in [dir(__builtin__), session.keys()]:
|
|
for word in lst:
|
|
if word[:n] == text and word != "__builtins__":
|
|
matches.append(word)
|
|
return matches
|
|
|
|
|
|
def attr_matches(self, text):
|
|
m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
|
|
if not m:
|
|
return
|
|
expr, attr = m.group(1, 3)
|
|
try:
|
|
object = eval(expr)
|
|
except:
|
|
object = eval(expr, session)
|
|
if isinstance(object, Packet):
|
|
words = filter(lambda x: x[0]!="_",dir(object))
|
|
words += map(str, object.fields_desc)
|
|
else:
|
|
words = dir(object)
|
|
if hasattr( object,"__class__" ):
|
|
words = words + rlcompleter.get_class_members(object.__class__)
|
|
matches = []
|
|
n = len(attr)
|
|
for word in words:
|
|
if word[:n] == attr and word != "__builtins__":
|
|
matches.append("%s.%s" % (expr, word))
|
|
return matches
|
|
|
|
readline.set_completer(ScapyCompleter().complete)
|
|
readline.parse_and_bind("C-o: operate-and-get-next")
|
|
readline.parse_and_bind("tab: complete")
|
|
|
|
|
|
session=None
|
|
session_name=""
|
|
|
|
opts=getopt.getopt(argv[1:], "hs:")
|
|
iface = None
|
|
try:
|
|
for opt, parm in opts[0]:
|
|
if opt == "-h":
|
|
usage()
|
|
elif opt == "-s":
|
|
session_name = parm
|
|
|
|
if len(opts[1]) > 0:
|
|
raise getopt.GetoptError("Too many parameters : [%s]" % string.join(opts[1]),None)
|
|
|
|
|
|
except getopt.error, msg:
|
|
log_loading.error(msg)
|
|
sys.exit(1)
|
|
|
|
|
|
if session_name:
|
|
try:
|
|
os.stat(session_name)
|
|
except OSError:
|
|
log_loading.info("New session [%s]" % session_name)
|
|
else:
|
|
try:
|
|
try:
|
|
session = cPickle.load(gzip.open(session_name,"rb"))
|
|
except IOError:
|
|
session = cPickle.load(open(session_name,"rb"))
|
|
log_loading.info("Using session [%s]" % session_name)
|
|
except EOFError:
|
|
log_loading.error("Error opening session [%s]" % session_name)
|
|
except AttributeError:
|
|
log_loading.error("Error opening session [%s]. Attribute missing" % session_name)
|
|
|
|
if session:
|
|
if "conf" in session:
|
|
conf.configure(session["conf"])
|
|
session["conf"] = conf
|
|
else:
|
|
conf.session = session_name
|
|
session={"conf":conf}
|
|
|
|
else:
|
|
session={"conf": conf}
|
|
|
|
__builtin__.__dict__["scapy_session"] = session
|
|
|
|
|
|
if READLINE:
|
|
if conf.histfile:
|
|
try:
|
|
readline.read_history_file(conf.histfile)
|
|
except IOError:
|
|
pass
|
|
atexit.register(scapy_write_history_file,readline)
|
|
|
|
sys.ps1 = ColorPrompt()
|
|
code.interact(banner = the_banner % (VERSION), local=session)
|
|
|
|
if conf.session:
|
|
save_session(conf.session, session)
|
|
|
|
sys.exit()
|
|
|
|
if __name__ == "__main__":
|
|
interact()
|