aboutsummaryrefslogtreecommitdiffstats
path: root/viensamoi/listen_target.py
blob: 38920d82403d1af1bb5f6fa07d5b60cb090a5305 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#!/usr/bin/env python

# 2012 Laurent Ghigonis <laurent@p1sec.com>
# 2012 Pierre-Olivier Vauboin <po@p1sec.com>

# TODO
# * [TOTEST] detect AP only if they send more bea than other
# * display UTF* or other charset bssid
# * display hex value when bssid is non-printable
# * Network View (who is conn, has ap ...)
# * store in redis Nodes and Networks
# * read pcap file
# * better UI
#   * implement a time to forget about nodes
#   * forget nodes on demand

import sys
import datetime
import getopt
import pdb #pdb.set_trace()
import pprint
import netaddr
from scapy.all import *

node_list = {}
# Dot11 Type and Subtype put to hex and concatenated
dot11_types = {
    "00": ["arq", "Association request"],
    "01": ["ars", "Association response"],
    "04": ["prq", "Probe request"],
    "05": ["prs", "Probe response"],
    "08": ["bea", "Beacon frame"],
    "0b": ["aut", "Authentication"],
    "0c": ["dea", "Deauthentication"],
    "0d": ["act", "Action"],
    "19": ["bac", "Block Ack"],
    "1b": ["cts", "Clear to Send"],
    "1d": ["ack", "Acknowledgement"],
    "20": ["dat", "Data"],
    "24": ["nul", "Null function"],
    "28": ["qda", "QoS Data"],
}
packet_counter=0
start_time = datetime.datetime.now()
last_time = 0
option_ap = False
option_debug = False
option_iface = "wlan0"
option_update_time = 3
option_mac_target = ""

def usage():
  print "usage: %s [-ad] [-m mac_target] iface" % sys.argv[0]

def dot11_type_name(num):
  if num in dot11_types:
    return dot11_types[num][0]
  else:
    return "%s " % num

def dump_(obj, classkey=None):
  if isinstance(obj, dict):
      for k in obj.keys():
          obj[k] = dump_(obj[k], classkey)
      return obj
  elif hasattr(obj, "__iter__"):
      return [dump_(v, classkey) for v in obj]
  elif hasattr(obj, "__dict__"):
      data = dict([(key, dump_(value, classkey)) 
          for key, value in obj.__dict__.iteritems() 
          if not callable(value) and not key.startswith('_')])
      if classkey is not None and hasattr(obj, "__class__"):
          data[classkey] = obj.__class__.__name__
      return data
  else:
      return obj
def dump(obj, classkey=None):
  pprint.pprint(dump_(obj, classkey))

def mac_lookup(mac):
  mac_prefix = '-'.join(mac.replace(':', '-').split('-')[0:3])
  try:
    oui = netaddr.OUI(mac_prefix)
    if oui.records:
      vendor = oui.records[0]['org']
    else:
      vendor = 'Unknown'
  except netaddr.NotRegisteredError:
    vendor = 'Unknown'
  return vendor

class Node:
  def __init__(self, mac):
    self.mac = mac
    self.types = {}
    self.pkt_recv_count = 0
  def addpkt(self, pkt):
    self.pkt_recv_count += 1
    type1 = pkt[Dot11].type
    type2 = pkt[Dot11].subtype
    ftype = "%x%x" % (type1, type2)
    if ftype not in self.types:
      self.types[ftype] = {"count": 0}
    self.types[ftype]["count"] += 1
    if ftype in ["00", "04", "05", "08"]:
      if "ssid" not in self.types[ftype]:
        self.types[ftype]["ssid"] = set()
      ssid = pkt[Dot11Elt].info if Dot11Elt in pkt else "#unknown"
      if (pkt.addr2):
        if (pkt.addr3):
          Network.update(ssid, pkt.addr3)
        else:
          Network.update(ssid, pkt.addr2)
      self.types[ftype]["ssid"].add(ssid)
    else:
      bssid = pkt[Dot11].addr3
      if bssid:
        if "bssid" not in self.types[ftype]:
          self.types[ftype]["bssid"] = set()
        self.types[ftype]["bssid"].add(bssid)
  def is_ap(self):
    if '08' in self.types.keys():
      na = len(self.types['08'])
      for t in self.types.keys():
        if len(self.types[t]) > na:
          return False
      return True
    return False
  def __str__(self):
    s = ""
    s += self.mac + " %s (%s)" % (mac_lookup(self.mac), self.pkt_recv_count)
    l = sorted(self.types.keys(), key=lambda x: self.types[x]["count"], reverse=True)
    for t in l:
      s += "\n"
      s += "  " + str(self.types[t]["count"]) + "\t" + dot11_type_name(t) + " "
      for i in self.types[t]:
        if i == "count": continue
        if i == "bssid":
          s += ','.join(map(Network.resolv, self.types[t][i]))
        else:
          s += ','.join(self.types[t][i])
    return s

class Network:
  _nets = {}
  @classmethod
  def resolv(cls, bssid):
    for n in cls._nets.values():
      if bssid in n.bssid:
        return n.name
    return bssid
  @classmethod
  def update(cls, name, bssid):
    if name in cls._nets.keys():
      cls._nets[name].bssid.add(bssid)
    else:
      n = Network(name)
      n.addbssid(bssid)
      cls._nets[name] = n
  def __init__(self, name):
    self.name = name
    self.bssid = set()
    Network._nets[name] = self
  def addbssid(self, bssid):
    self.bssid.add(bssid)

def analyse_packet(pkt):
  # pkt.addr1 : bssid
  # pkt.addr2 : station
  # pkt.addr3 : 
  global packet_counter
  global start_time, last_time
  packet_counter+=1
  try:
    if pkt.addr2 is None:
      if option_debug:
        print "source None !!!"
      return
  except:
    if option_debug:
      print "no addr2 !!!"
    return
  if option_mac_target == "" or option_mac_target == pkt.addr2:
    if pkt.addr2 not in node_list:
      n = Node(pkt.addr2)
      node_list[pkt.addr2] = n
    node_list[pkt.addr2].addpkt(pkt)
  #pdb.set_trace()
  if packet_counter % 20 == 0:
    time = datetime.datetime.now()
    if time.second != last_time:
      delta = time - start_time
      if delta.seconds % option_update_time == 0:
        last_time = time.second
        print_stats()

def print_stats():
  print "==================================== %d pkts, %d nodes" % (packet_counter, len(node_list))
  l = sorted(node_list.keys(), key=lambda x: node_list[x].pkt_recv_count, reverse=True)
  for cli in l:
    if not option_ap and node_list[cli].is_ap():
      continue
    print node_list[cli]

if __name__ == '__main__':
  try:
    opts, args = getopt.getopt(sys.argv[1:], "adm:")
  except getopt.GetoptError, err:
    usage()
    sys.exit(1)
  for o, a in opts:
    if o == "-a": option_ap=True
    elif o == "-d": option_debug=True
    elif o == "-m": option_mac_target=a
    else:
      print "unknown option %s" % o
      sys.exit(1)
  if len(args) < 1:
    usage()
    sys.exit(1)

  option_iface = args[0]
  print "Be sure that %s is in monitor mode." % option_iface
  print "Use airodump at the same time if you want channel hopping. hop hop !"
  sniff(iface=option_iface, prn=analyse_packet, store=0)