· 8 years ago · Aug 15, 2018, 11:00 PM
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3# Author: Keegan
4
5"""
6This program shows you IMSI numbers of cellphones around you.
7
8
9
10
11What you need :
121 PC
131 USB DVB-T key (RTL2832U) with antenna (less than 15$) or a OsmocomBB phone or HackRf
14
15
16Setup :
17
18sudo apt install python-numpy python-scipy python-scapy
19
20sudo add-apt-repository -y ppa:ptrkrysik/gr-gsm
21sudo apt update
22sudo apt install gr-gsm
23
24If gr-gsm failled to setup. Try this setup : https://github.com/ptrkrysik/gr-gsm/wiki/Installation
25
26Run :
27
28# Open 2 terminals.
29# In terminal 1
30sudo python simple_IMSI-catcher.py
31
32# In terminal 2
33airprobe_rtlsdr.py
34# Now, change the frequency and stop it when you have output like :
35# 15 06 21 00 01 f0 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
36# 25 06 21 00 05 f4 f8 68 03 26 23 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b
37# 49 06 1b 95 cc 02 f8 02 01 9c c8 03 1e 57 a5 01 79 00 00 1c 13 2b 2b
38# ...
39#
40# Now, watch terminal 1 and wait. IMSI numbers should appear :-)
41# If nothing appears after 1 min, change the frequency.
42#
43# Doc : https://fr.wikipedia.org/wiki/Global_System_for_Mobile_Communications
44# Example of frequency : 9.288e+08 Bouygues
45
46# You can watch GSM packet with
47sudo wireshark -k -Y '!icmp && gsmtap' -i lo
48
49
50Links :
51
52Setup of Gr-Gsm : http://blog.nikseetharaman.com/gsm-network-characterization-using-software-defined-radio/
53Frequency : https://fr.wikipedia.org/wiki/Global_System_for_Mobile_Communications
54Scapy : http://secdev.org/projects/scapy/doc/usage.html
55IMSI : https://fr.wikipedia.org/wiki/IMSI
56Realtek RTL2832U : http://doc.ubuntu-fr.org/rtl2832u and http://doc.ubuntu-fr.org/rtl-sdr
57"""
58
59import ctypes
60import json
61from optparse import OptionParser
62import datetime
63import io
64import socket
65
66imsitracker = None
67
68class tracker:
69 imsistate = {}
70 # phones
71 imsis=[] # [IMSI,...]
72 tmsis={} # {TMSI:IMSI,...}
73 nb_IMSI=0 # count the number of IMSI
74
75 mcc=""
76 mnc=""
77 lac=""
78 cell=""
79 country=""
80 brand=""
81 operator=""
82
83 show_all_tmsi = False
84 mcc_codes = None
85 sqlcon = None
86
87 ouput_function=None
88
89 def __init__(self):
90 self.load_mcc_codes()
91 self.track_this_imsi("")
92 self.ouput_function=self.ouput
93
94 def set_ouput_function(self, new_ouput_function):
95 # New ouput function need this field :
96 # cpt, tmsi1, tmsi2, imsi, imsicountry, imsibrand, imsioperator, mcc, mnc, lac, cell, packet=None
97 self.ouput_function=new_ouput_function
98
99 def track_this_imsi(self, imsi_to_track):
100 self.imsi_to_track = imsi_to_track
101 self.imsi_to_track_len=len(imsi_to_track)
102
103 # return something like '0xd9605460'
104 def str_tmsi(self, tmsi):
105 if tmsi != "":
106 new_tmsi="0x"
107 for a in tmsi:
108 c=hex(ord(a))
109 if len(c)==4:
110 new_tmsi+=str(c[2])+str(c[3])
111 else:
112 new_tmsi+="0"+str(c[2])
113 return new_tmsi
114 else:
115 return ""
116
117 def decode_imsi(self, imsi):
118 new_imsi=''
119 for a in imsi:
120 c=hex(ord(a))
121 if len(c)==4:
122 new_imsi+=str(c[3])+str(c[2])
123 else:
124 new_imsi+=str(c[2])+"0"
125
126 mcc=new_imsi[1:4]
127 mnc=new_imsi[4:6]
128 return new_imsi, mcc, mnc
129
130 # return something like
131 # '208 20 1752XXXXXX', 'France', 'Bouygues', 'Bouygues Telecom'
132 def str_imsi(self, imsi, packet=""):
133 new_imsi, mcc, mnc = self.decode_imsi(imsi)
134 country=""
135 brand=""
136 operator=""
137 if mcc in self.mcc_codes:
138 if mnc in self.mcc_codes[mcc]['MNC']:
139 country=self.mcc_codes[mcc]['c'][0]
140 brand=self.mcc_codes[mcc]['MNC'][mnc][0]
141 operator=self.mcc_codes[mcc]['MNC'][mnc][1]
142 new_imsi=mcc+" "+mnc+" "+new_imsi[6:]
143 elif mnc+new_imsi[6:7] in self.mcc_codes[mcc]['MNC']:
144 mnc+=new_imsi[6:7]
145 country=self.mcc_codes[mcc]['c'][0]
146 brand=self.mcc_codes[mcc]['MNC'][mnc][0]
147 operator=self.mcc_codes[mcc]['MNC'][mnc][1]
148 new_imsi=mcc+" "+mnc+" "+new_imsi[7:]
149 else:
150 country=self.mcc_codes[mcc]['c'][0]
151 brand="Unknown MNC {}".format(mnc)
152 operator="Unknown MNC {}".format(mnc)
153 new_imsi=mcc+" "+mnc+" "+new_imsi[6:]
154
155 try:
156 return new_imsi, country, brand, operator
157 except:
158 m=""
159 print("Error", packet, new_imsi, country, brand, operator)
160 return "", "", "", ""
161
162 def load_mcc_codes(self):
163 # mcc codes form https://en.wikipedia.org/wiki/Mobile_Network_Code
164 with io.open('mcc-mnc/mcc_codes.json', 'r', encoding='utf8') as file:
165 self.mcc_codes = json.load(file)
166
167 def current_cell(self, mcc, mnc, lac, cell):
168 brand=""
169 operator=""
170 country = ""
171 if mcc in self.mcc_codes:
172 if mnc in self.mcc_codes[mcc]['MNC']:
173 country=self.mcc_codes[mcc]['c'][0]
174 brand=self.mcc_codes[mcc]['MNC'][mnc][0]
175 operator=self.mcc_codes[mcc]['MNC'][mnc][1]
176 else:
177 country=self.mcc_codes[mcc]['c'][0]
178 brand="Unknown MNC {}".format(mnc)
179 operator="Unknown MNC {}".format(mnc)
180 else:
181 country="Unknown MCC {}".format(mcc)
182 brand="Unknown MNC {}".format(mnc)
183 operator="Unknown MNC {}".format(mnc)
184 self.mcc=str(mcc)
185 self.mnc=str(mnc)
186 self.lac=str(lac)
187 self.cell=str(cell)
188 self.country=country
189 self.brand=brand
190 self.operator=operator
191
192 def sqlite_file(self, filename):
193 import sqlite3 # Avoid pulling in sqlite3 when not saving
194 print("Saving to SQLite database in %s" % filename)
195 self.sqlcon = sqlite3.connect(filename)
196 # FIXME Figure out proper SQL type for each attribute
197 self.sqlcon.execute("CREATE TABLE IF NOT EXISTS observations(stamp datetime, tmsi1 text, tmsi2 text, imsi text, imsicountry text, imsibrand text, imsioperator text, mcc integer, mnc integer, lac integer, cell integer);")
198
199 def ouput(self, cpt, tmsi1, tmsi2, imsi, imsicountry, imsibrand, imsioperator, mcc, mnc, lac, cell, packet=None):
200 print((u"{:7s} ; {:10s} ; {:10s} ; {:17s} ; {:12s} ; {:10s} ; {:21s} ; {:4s} ; {:5s} ; {:6s} ; {:6s}".format(str(cpt), tmsi1, tmsi2, imsi, imsicountry, imsibrand, imsioperator, str(mcc), str(mnc), str(lac), str(cell))).encode("utf-8"))
201
202 def pfields(self, cpt, tmsi1, tmsi2, imsi, mcc, mnc, lac, cell, packet=None):
203 imsicountry=""
204 imsibrand=""
205 imsioperator=""
206 if imsi:
207 imsi, imsicountry, imsibrand, imsioperator = self.str_imsi(imsi, packet)
208 else:
209 imsi=""
210 self.ouput_function(cpt, tmsi1, tmsi2, imsi, imsicountry, imsibrand, imsioperator, mcc, mnc, lac, cell, packet)
211 if self.sqlcon:
212 now = datetime.datetime.now()
213 if tmsi1 == "":
214 tmsi1 = None
215 if tmsi2 == "":
216 tmsi2 = None
217 self.sqlcon.execute(u"INSERT INTO observations (stamp, tmsi1, tmsi2, imsi, imsicountry, imsibrand, imsioperator, mcc, mnc, lac, cell) "+
218 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
219 (now, tmsi1, tmsi2, imsi, imsicountry, imsibrand, imsioperator,
220 mcc, mnc, lac, cell))
221 self.sqlcon.commit()
222
223 def header(self):
224 print("{:7s} ; {:10s} ; {:10s} ; {:17s} ; {:12s} ; {:10s} ; {:21s} ; {:4s} ; {:5s} ; {:6s} ; {:6s}".format("Nb IMSI", "TMSI-1", "TMSI-2", "IMSI", "country", "brand", "operator", "MCC", "MNC", "LAC", "CellId"))
225
226 # print "Nb IMSI", "TMSI-1", "TMSI-2", "IMSI", "country", "brand", "operator", "MCC", "MNC", "LAC", "CellId"
227 def register_imsi(self, arfcn, imsi1="", imsi2="", tmsi1="", tmsi2="", p=""):
228 do_print=False
229 n=''
230 if imsi1: self.imsi_seen(imsi1, arfcn)
231 if imsi2: self.imsi_seen(imsi2, arfcn)
232 if imsi1 and (not self.imsi_to_track or imsi1[:self.imsi_to_track_len] == self.imsi_to_track):
233 if imsi1 not in self.imsis:
234 # new IMSI
235 do_print=True
236 self.imsis.append(imsi1)
237 self.nb_IMSI+=1
238 n=self.nb_IMSI
239 if tmsi1 and (tmsi1 not in self.tmsis or self.tmsis[tmsi1] != imsi1):
240 # new TMSI to an ISMI
241 do_print=True
242 self.tmsis[tmsi1]=imsi1
243 if tmsi2 and (tmsi2 not in self.tmsis or self.tmsis[tmsi2] != imsi1):
244 # new TMSI to an ISMI
245 do_print=True
246 self.tmsis[tmsi2]=imsi1
247
248 if imsi2 and (not self.imsi_to_track or imsi2[:self.imsi_to_track_len] == self.imsi_to_track):
249 if imsi2 not in self.imsis:
250 # new IMSI
251 do_print=True
252 self.imsis.append(imsi2)
253 self.nb_IMSI+=1
254 n=self.nb_IMSI
255 if tmsi1 and (tmsi1 not in self.tmsis or self.tmsis[tmsi1] != imsi2):
256 # new TMSI to an ISMI
257 do_print=True
258 self.tmsis[tmsi1]=imsi2
259 if tmsi2 and (tmsi2 not in self.tmsis or self.tmsis[tmsi2] != imsi2):
260 # new TMSI to an ISMI
261 do_print=True
262 self.tmsis[tmsi2]=imsi2
263
264 if not imsi1 and not imsi2 and tmsi1 and tmsi2:
265 if tmsi2 in self.tmsis:
266 # switch the TMSI
267 do_print=True
268 imsi1=self.tmsis[tmsi2]
269 self.tmsis[tmsi1]=imsi1
270 del self.tmsis[tmsi2]
271
272 if do_print:
273 if imsi1:
274 self.pfields(str(n), self.str_tmsi(tmsi1), self.str_tmsi(tmsi2), imsi1, str(self.mcc), str(self.mnc), str(self.lac), str(self.cell), p)
275 if imsi2:
276 self.pfields(str(n), self.str_tmsi(tmsi1), self.str_tmsi(tmsi2), imsi2, str(self.mcc), str(self.mnc), str(self.lac), str(self.cell), p)
277
278 if not imsi1 and not imsi2:
279 # Register IMSI as seen if a TMSI believed to
280 # belong to the IMSI is seen.
281 if tmsi1 and tmsi1 in self.tmsis \
282 and ""!= self.tmsis[tmsi1]:
283 self.imsi_seen(self.tmsis[tmsi1], arfcn)
284 if self.show_all_tmsi:
285 do_print=False
286 if tmsi1 and tmsi1 not in self.tmsis:
287 do_print=True
288 self.tmsis[tmsi1]=""
289 if tmsi1 and tmsi1 not in self.tmsis:
290 do_print=True
291 self.tmsis[tmsi2]=""
292 if do_print:
293 self.pfields(str(n), self.str_tmsi(tmsi1), self.str_tmsi(tmsi2), None, str(self.mcc), str(self.mnc), str(self.lac), str(self.cell), p)
294 def imsi_seen(self, imsi, arfcn):
295 now = datetime.datetime.utcnow().replace(microsecond=0)
296 imsi, mcc, mnc = self.decode_imsi(imsi)
297 if imsi in self.imsistate:
298 self.imsistate[imsi]["lastseen"] = now
299 else:
300 self.imsistate[imsi] = {
301 "firstseen" : now,
302 "lastseen" : now,
303 "imsi" : imsi,
304 "arfcn" : arfcn,
305 }
306 self.imsi_purge_old()
307 def imsi_purge_old(self):
308 now = datetime.datetime.utcnow().replace(microsecond=0)
309 maxage = datetime.timedelta(minutes=10)
310 limit = now - maxage
311 for imsi in self.imsistate.keys():
312 if limit > self.imsistate[imsi]["lastseen"]:
313 del self.imsistate[imsi]
314
315class gsmtap_hdr(ctypes.BigEndianStructure):
316 _pack_ = 1
317 # Based on gsmtap_hdr structure in <grgsm/gsmtap.h> from gr-gsm
318 _fields_ = [
319 ("version", ctypes.c_ubyte),
320 ("hdr_len", ctypes.c_ubyte),
321 ("type", ctypes.c_ubyte),
322 ("timeslot", ctypes.c_ubyte),
323 ("arfcn", ctypes.c_uint16),
324 ("signal_dbm", ctypes.c_ubyte),
325 ("snr_db", ctypes.c_ubyte),
326 ("frame_number", ctypes.c_uint32),
327 ("sub_type", ctypes.c_ubyte),
328 ("antenna_nr", ctypes.c_ubyte),
329 ("sub_slot", ctypes.c_ubyte),
330 ("res", ctypes.c_ubyte),
331 ]
332 def __repr__(self):
333 return "%s(version=%d, hdr_len=%d, type=%d, timeslot=%d, arfcn=%d, signal_dbm=%d, snr_db=%d, frame_number=%d, sub_type=%d, antenna_nr=%d, sub_slot=%d, res=%d)" % (
334 self.__class__, self.version, self.hdr_len, self.type,
335 self.timeslot, self.arfcn, self.signal_dbm, self.snr_db,
336 self.frame_number, self.sub_type, self.antenna_nr, self.sub_slot,
337 self.res,
338 )
339
340# return mcc mnc, lac, cell, country, brand, operator
341def find_cell(gsm, udpdata, t = None):
342 # find_cell() update all following variables
343 global mcc
344 global mnc
345 global lac
346 global cell
347 global country
348 global brand
349 global operator
350
351 """
352 Dump of a packet from wireshark
353
354 /!\ there are an offset of 0x2a
355 0x12 (from the code) + 0x2a (offset) == 0x3c (in documentation's dump)
356
357 0 1 2 3 4 5 6 7 8 9 a b c d e f
358 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00
359 0010 00 43 9a 6b 40 00 40 11 a2 3c 7f 00 00 01 7f 00
360 0020 00 01 ed d1 12 79 00 2f fe 42 02 04 01 00 00 00
361 0030 cc 00 00 07 9b 2c 01 00 00 00 49 06 1b 61 9d 02
362 0040 f8 02 01 9c c8 03 1e 53 a5 07 79 00 00 80 01 40
363 0050 db
364
365 Channel Type: BCCH (1)
366 6
367 0030 01
368
369 0x36 - 0x2a = position p[0x0c]
370
371
372 Message Type: System Information Type 3
373 c
374 0030 1b
375
376 0x3c - 0x2a = position p[0x12]
377
378 Cell CI: 0x619d (24989)
379 d e
380 0030 61 9d
381
382 0x3d - 0x2a = position p[0x13]
383 0x3e - 0x2a = position p[0x14]
384
385 Location Area Identification (LAI) - 208/20/412
386 Mobile Country Code (MCC): France (208) 0x02f8
387 Mobile Network Code (MNC): Bouygues Telecom (20) 0xf802
388 Location Area Code (LAC): 0x019c (412)
389 0 1 2 3 4 5 6 7 8 9 a b c d e f
390 0030 02
391 0040 f8 02 01 9c
392 """
393 if gsm.sub_type == 0x01: # Channel Type == BCCH (0)
394 p=udpdata
395 if ord(p[0x12]) == 0x1b: # (0x12 + 0x2a = 0x3c) Message Type: System Information Type 3
396 # FIXME
397 m=hex(ord(p[0x15]))
398 if len(m)<4:
399 mcc=m[2]+'0'
400 else:
401 mcc=m[3]+m[2]
402 mcc+=str(ord(p[0x16]) & 0x0f)
403
404 # FIXME not works with mnc like 005 or 490
405 m=hex(ord(p[0x17]))
406 if len(m)<4:
407 mnc=m[2]+'0'
408 else:
409 mnc=m[3]+m[2]
410
411 lac=ord(p[0x18])*256+ord(p[0x19])
412 cell=ord(p[0x13])*256+ord(p[0x14])
413 t.current_cell(mcc, mnc, lac, cell)
414
415def find_imsi(udpdata, t=None):
416 if t is None:
417 t = imsitracker
418
419 # Create object representing gsmtap header in UDP payload
420 gsm = gsmtap_hdr.from_buffer_copy(udpdata)
421 #print gsm
422
423 if gsm.sub_type == 0x1: # Channel Type == BCCH (0)
424 # Update global cell info if found in package
425 # FIXME : when you change the frequency, this informations is
426 # not immediately updated. So you could have wrong values when
427 # printing IMSI :-/
428 find_cell(gsm, udpdata, t=t)
429 else: # Channel Type != BCCH (0)
430 p=udpdata
431 tmsi1=""
432 tmsi2=""
433 imsi1=""
434 imsi2=""
435 if ord(p[0x12]) == 0x21: # Message Type: Paging Request Type 1
436 if ord(p[0x14]) == 0x08 and (ord(p[0x15]) & 0x1) == 0x1: # Channel 1: TCH/F (Full rate) (2)
437 # Mobile Identity 1 Type: IMSI (1)
438 """
439 0 1 2 3 4 5 6 7 8 9 a b c d e f
440 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00
441 0010 00 43 1c d4 40 00 40 11 1f d4 7f 00 00 01 7f 00
442 0020 00 01 c2 e4 12 79 00 2f fe 42 02 04 01 00 00 00
443 0030 c9 00 00 16 21 26 02 00 07 00 31 06 21 00 08 XX
444 0040 XX XX XX XX XX XX XX 2b 2b 2b 2b 2b 2b 2b 2b 2b
445 0050 2b
446 XX XX XX XX XX XX XX XX = IMSI
447 """
448 imsi1=p[0x15:][:8]
449 # ord(p[0x10]) == 0x59 = l2 pseudo length value: 22
450 if ord(p[0x10]) == 0x59 and ord(p[0x1E]) == 0x08 and (ord(p[0x1F]) & 0x1) == 0x1: # Channel 2: TCH/F (Full rate) (2)
451 # Mobile Identity 2 Type: IMSI (1)
452 """
453 0 1 2 3 4 5 6 7 8 9 a b c d e f
454 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00
455 0010 00 43 90 95 40 00 40 11 ac 12 7f 00 00 01 7f 00
456 0020 00 01 b4 1c 12 79 00 2f fe 42 02 04 01 00 00 00
457 0030 c8 00 00 16 51 c6 02 00 08 00 59 06 21 00 08 YY
458 0040 YY YY YY YY YY YY YY 17 08 XX XX XX XX XX XX XX
459 0050 XX
460 YY YY YY YY YY YY YY YY = IMSI 1
461 XX XX XX XX XX XX XX XX = IMSI 2
462 """
463 imsi2=p[0x1F:][:8]
464 elif ord(p[0x10]) == 0x59 and ord(p[0x1E]) == 0x08 and (ord(p[0x1F]) & 0x1) == 0x1: # Channel 2: TCH/F (Full rate) (2)
465 # Mobile Identity - Mobile Identity 2 - IMSI
466 """
467 0 1 2 3 4 5 6 7 8 9 a b c d e f
468 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00
469 0010 00 43 f6 92 40 00 40 11 46 15 7f 00 00 01 7f 00
470 0020 00 01 ab c1 12 79 00 2f fe 42 02 04 01 00 00 00
471 0030 d8 00 00 23 3e be 02 00 05 00 4d 06 21 a0 08 YY
472 0040 YY YY YY YY YY YY YY 17 05 f4 XX XX XX XX 2b 2b
473 0050 2b
474 YY YY YY YY YY YY YY YY = IMSI 1
475 XX XX XX XX = TMSI
476 """
477 tmsi1=p[0x20:][:4]
478
479 t.register_imsi(gsm.arfcn, imsi1, imsi2, tmsi1, tmsi2, p)
480
481 elif ord(p[0x1B]) == 0x08 and (ord(p[0x1C]) & 0x1) == 0x1: # Channel 2: TCH/F (Full rate) (2)
482 # Mobile Identity 2 Type: IMSI (1)
483 """
484 0 1 2 3 4 5 6 7 8 9 a b c d e f
485 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00
486 0010 00 43 57 8e 40 00 40 11 e5 19 7f 00 00 01 7f 00
487 0020 00 01 99 d4 12 79 00 2f fe 42 02 04 01 00 00 00
488 0030 c7 00 00 11 05 99 02 00 03 00 4d 06 21 00 05 f4
489 0040 yy yy yy yy 17 08 XX XX XX XX XX XX XX XX 2b 2b
490 0050 2b
491 yy yy yy yy = TMSI/P-TMSI - Mobile Identity 1
492 XX XX XX XX XX XX XX XX = IMSI
493 """
494 tmsi1=p[0x16:][:4]
495 imsi2=p[0x1C:][:8]
496 t.register_imsi(gsm.arfcn, imsi1, imsi2, tmsi1, tmsi2, p)
497
498 elif ord(p[0x14]) == 0x05 and (ord(p[0x15]) & 0x07) == 4: # Mobile Identity - Mobile Identity 1 - TMSI/P-TMSI
499 """
500 0 1 2 3 4 5 6 7 8 9 a b c d e f
501 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00
502 0010 00 43 b3 f7 40 00 40 11 88 b0 7f 00 00 01 7f 00
503 0020 00 01 ce 50 12 79 00 2f fe 42 02 04 01 00 03 fd
504 0030 d1 00 00 1b 03 5e 05 00 00 00 41 06 21 00 05 f4
505 0040 XX XX XX XX 17 05 f4 YY YY YY YY 2b 2b 2b 2b 2b
506 0050 2b
507 XX XX XX XX = TMSI/P-TMSI - Mobile Identity 1
508 YY YY YY YY = TMSI/P-TMSI - Mobile Identity 2
509 """
510 tmsi1=p[0x16:][:4]
511 if ord(p[0x1B]) == 0x05 and (ord(p[0x1C]) & 0x07) == 4: # Mobile Identity - Mobile Identity 2 - TMSI/P-TMSI
512 tmsi2=p[0x1D:][:4]
513 else:
514 tmsi2=""
515
516 t.register_imsi(gsm.arfcn, imsi1, imsi2, tmsi1, tmsi2, p)
517
518 elif ord(p[0x12]) == 0x22: # Message Type: Paging Request Type 2
519 if ord(p[0x1D]) == 0x08 and (ord(p[0x1E]) & 0x1) == 0x1: # Mobile Identity 3 Type: IMSI (1)
520 """
521 0 1 2 3 4 5 6 7 8 9 a b c d e f
522 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00
523 0010 00 43 1c a6 40 00 40 11 20 02 7f 00 00 01 7f 00
524 0020 00 01 c2 e4 12 79 00 2f fe 42 02 04 01 00 00 00
525 0030 c9 00 00 16 20 e3 02 00 04 00 55 06 22 00 yy yy
526 0040 yy yy zz zz zz 4e 17 08 XX XX XX XX XX XX XX XX
527 0050 8b
528 yy yy yy yy = TMSI/P-TMSI - Mobile Identity 1
529 zz zz zz zz = TMSI/P-TMSI - Mobile Identity 2
530 XX XX XX XX XX XX XX XX = IMSI
531 """
532 tmsi1=p[0x14:][:4]
533 tmsi2=p[0x18:][:4]
534 imsi2=p[0x1E:][:8]
535 t.register_imsi(gsm.arfcn, imsi1, imsi2, tmsi1, tmsi2, p)
536
537def udpserver(port, prn):
538 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
539 server_address = ('localhost', port)
540 sock.bind(server_address)
541 while True:
542 udpdata, address = sock.recvfrom(4096)
543 if prn:
544 prn(udpdata)
545
546def find_imsi_from_pkg(p):
547 udpdata = str(p[UDP].payload)
548 find_imsi(udpdata)
549
550if __name__ == "__main__":
551 imsitracker = tracker()
552 parser = OptionParser(usage="%prog: [options]")
553 parser.add_option("-a", "--alltmsi", action="store_true", dest="show_all_tmsi", help="Show TMSI who haven't got IMSI (default : false)")
554 parser.add_option("-i", "--iface", dest="iface", default="lo", help="Interface (default : lo)")
555 parser.add_option("-m", "--imsi", dest="imsi", default="", type="string", help='IMSI to track (default : None, Example: 123456789101112 or "123 45 6789101112")')
556 parser.add_option("-p", "--port", dest="port", default="4729", type="int", help="Port (default : 4729)")
557 parser.add_option("-s", "--sniff", action="store_true", dest="sniff", help="sniff on interface instead of listening on port (require root/suid access)")
558 parser.add_option("-w", "--sqlite", dest="sqlite", default=None, type="string", help="Save observed IMSI values to specified SQLite file")
559 (options, args) = parser.parse_args()
560
561 if options.sqlite:
562 imsitracker.sqlite_file(options.sqlite)
563
564 imsitracker.show_all_tmsi=options.show_all_tmsi
565 imsi_to_track=""
566 if options.imsi:
567 imsi="9"+options.imsi.replace(" ", "")
568 imsi_to_track_len=len(imsi)
569 if imsi_to_track_len%2 == 0 and imsi_to_track_len > 0 and imsi_to_track_len <17:
570 for i in range(0, imsi_to_track_len-1, 2):
571 imsi_to_track+=chr(int(imsi[i+1])*16+int(imsi[i]))
572 imsi_to_track_len=len(imsi_to_track)
573 else:
574 print("Wrong size for the IMSI to track!")
575 print("Valid sizes :")
576 print("123456789101112")
577 print("1234567891011")
578 print("12345678910")
579 print("123456789")
580 print("1234567")
581 print("12345")
582 print("123")
583 exit(1)
584 imsitracker.track_this_imsi(imsi_to_track)
585 if options.sniff:
586 from scapy.all import sniff, UDP
587 imsitracker.header()
588 sniff(iface=options.iface, filter="port {} and not icmp and udp".format(options.port), prn=find_imsi_from_pkg, store=0)
589 else:
590 imsitracker.header()
591 udpserver(port=options.port, prn=find_imsi)