antikythera package

Submodules

antikythera.Logfile module

antikythera.antikythera module

antikythera.py

The main program manager.

class antikythera.antikythera.Anti(num_processes, headless, interface=None, capturefile=None, max_qsize=100000, *args, **kwargs)[source]

Bases: multiprocessing.context.Process

Start and monitor the worker processes.

exit_process(p)[source]
run()[source]
shutdown()[source]
wait()[source]
antikythera.antikythera.create_parser()[source]

Parse command line parameters.

Returns:command line parameters as argparse.Namespace
Parameters:args ([str]) – List of strings representing the command line arguments.
Returns:Simple object with a readable string representation of the argument list.
Return type:argparse.Namespace

antikythera.capture module

capture.py

Interface to the radio and Pcap files.

class antikythera.capture.Capture(process_id, q, *args, interface=None, capturefile=None, delay=None, **kwargs)[source]

Bases: multiprocessing.context.Process

Grab the packets from the radio interface.

flush_queue()[source]

Empty Queue quickly.

pcap_capture()[source]

Capture from a Pcap file.

radio_capture()[source]

Live packet capture from GNU Radio.

run()[source]

Main process loop.

shutdown()[source]

antikythera.cli module

cli.py

This is the Antikythera console script. It provides the entry points that are used to run the program when it is installed. To install the program run python setup.py install and it will be installed on the system.

antikythera.cli.main(args)[source]

Main entry point allowing external calls.

Collects command line arguments, sets up the logs, and logs information about them. Then it starts the program loop.

Parameters:args ([str]) – command line parameter list
antikythera.cli.run()[source]

Entry point for console_scripts.

antikythera.cli.setup_logs(loglevel)[source]

Set up logger to be used between all modules.

Set logging root and file handler configuration to default to INFO and write output to main.log. Set console handler to default to INFO.

Parameters:str (loglevel) – A string of one of the Default Python logging levels. See the Python logging level documentation for more information.
antikythera.cli.trace(self, message, *args, **kws)[source]

Define trace logging level

Yes, logger takes its ‘*args’ as ‘args’.

antikythera.conditions module

Pull variables from db, if any variable contains data then set corresponding variable equal to one. follow through till all

antikythera.conditions.check_defcon(TMSI, IMSI, LAC, CID, MCC, MNC, IMEISV)[source]
antikythera.conditions.print_defcon(defcon)[source]
antikythera.conditions.start()[source]

antikythera.decoder module

decoder.py

Unwrap the pyshark packets and put the needed data into the database.

Abbreviations:

RXLEV: Reception Level (GSM).

NCELL: neighboring cell.

BSIC-NCELL: Abbreviation for Base Station Identity Code of
an adjacent CELL. Identifies and decode the BCCH (Broadcast Control Channel) of neighbouring cells so that the MS (Mobile Station) may take measuring reports to facilitate handover, or to allow the MS to make cell selection and reselection calculations.
NO-NCELL-M: No neighbour cell measurement result. One byte
unsigned integer representing the number of neighbour cells.
FULL vs. SUB Values:

In GSM, there are two types of values presented for RxQual, namely RxQual Full and RxQual Sub. RxLev, the parameter representing the signal strength, also has similar Full and Sub values. The FULL values are based upon all frames on the SACCH multiframe, whether they have been transmitted from the base station or not. This means that if DTX DL has been used, the FULL values will be invalid for that period since they include bit-error measurements at periods when nothing has been sent resulting in very high BER. In total, 100 bursts (25 blocks) will be used for the FULL values.

The SUB values are based on the mandatory frames on the SACCH multiframe. These frames must always be transmitted. There are two frames fulfilling that criteria and that is the SACCH block (A bursts in Figure 7) and the block holding the SID frame. If DTX DL is not in use, the SID frame will contain an ordinary speech frame and then this is included instead. In total, 12 bursts (two blocks) will be used for the SUB values (four bursts SACCH and eight half bursts [or speech] information).

Further Reference:

class antikythera.decoder.Decoder(process_id, queue, *args, **kwargs)[source]

Bases: multiprocessing.context.Process

Decode and store the packets for analysis.

The run() function is the process main loop. It first sets up the tables needed by the PacketManager to sort the packets into with create_tables(). Then until the Multiprocessing event exit is set by the manager process it continually pulls packets out of the shared queue and hands them off to the PacketManager for processing.

create_tables()[source]

Create packet tables.

Create all tables required by the PacketManager if they do not exist. Data that is in the GSMTAP layer of the packets is common between all packets and is therefore present in all tables. It includes:

  • HASH
  • UnixTime
  • PeopleTime
  • CHANNEL
  • DBM
  • ARFCN
  • FrameNumber

Everything else is information that is specific to the Packet(s) that are inserted in the given table. Tables are created for each group of packets data important to a given query.

run()[source]

Process main function, program loop.

Perform initial setup by creating tables and the PacketManager, then execute the run loop to pass packets from the queue to be inserted into the database until shutdown() is called.

shutdown()[source]

When manager process requests exit run loop and terminate.

class antikythera.decoder.LACPacket(packet)[source]

Bases: antikythera.decoder.Packet

Location Area Code (LAC) Packet.

A packet with LAC, CID, and ARFCN data.

store(database)[source]

Store packets with LAC and CID information.

class antikythera.decoder.Packet(packet)[source]

Bases: object

Gather packet generic GSMTAP information.

store(database)[source]

Store unimplemented packets which only have GSMTAP information.

class antikythera.decoder.PacketFactory[source]

Bases: object

Create packets.

static create(packet, type_, subtype)[source]

Create a packet of the given type.

class antikythera.decoder.PacketManager(process_id, data_dir)[source]

Bases: object

Manage operations on packets.

process_id

str – Process identifier.

data_dir

str – Where the database is stored.

packet_types

dict – Implemented packet types. Keys are packet major types and value is a list of implemented subtypes.

packet_metrics

dict – Returns the table the packet should be sorted into given (MAJOR_TYPE + _ + MINOR_TYPE) for example, “GSM_A.DTAP_30” representing the system message subtype 30 which contains LAC and CID information.

get_packet_info(data_layer, _type, _subtype, implemented)[source]

Attempt to get a brief description of the packet.

Parameters:
  • data_layer – the highest layer of a GSMTAP format packet.
  • _type (str) – If a type is found a string representation, else None.
  • _subtype (str) – If a subtype is found a string representation, else None.
Returns:

A brief description of the contents

of the packet. Empty string if not available.

Return type:

packet_info (str)

get_packet_type()[source]

Get the packet type and subtype information.

_type

str – The name of the unique packet data layer.

data_layer

The actual packet data layer object.

attributes

list – A list of strings with the names of all valid attributes and methods for the data layer object.

index

int – The zero indexed location of the packet. For a live capture it would represent the number of packets received. For a .pcap file it is the index that the packet is found at if the file is read into a Pyshark object using FileCapture.

no_type

str – An error message used when the packet type is not implemented.

no_subtype

str – An error message used when the packet type is implemented but not the subtype.

Returns: packet type string and subtype int tuple.

insert_packet(packet)[source]

Insert only needed data into the database for analysis.

is_implemented(type_, subtype)[source]

Return true if packet type/subtype is implemented.

store(packet, type_, subtype)[source]

Store packets.

Choose the function to store a given packet type.

class antikythera.decoder.PagePacket(packet)[source]

Bases: antikythera.decoder.Packet

Location Area Code (LAC) Packet.

A packet with LAC data.

store(database)[source]

Store GSM_A.CCCH packets.

Unique Inserts:
  • id_type
  • msg_type
  • mode
  • chan_req_ch1
  • chan_req_ch2

antikythera.gui module

antikythera.kivylogs module

kivylogs.py

Fix Kivy’s logger so it is compatible with modules using the standard python logger, this must remain in the same order and be at the top of the entry point module for the application.

This code is extremely fragile and must be declared and executed in this exact order.

antikythera.kivylogs.trace(self, message, *args, **kws)[source]

Define trace logging level

Yes, logger takes its ‘*args’ as ‘args’.

antikythera.metrics module

metrics.py

Implementation of the metrics that detect IMSI Catchers

Acronyms:

ARFCN: Absolute Radio Frequency Channel Number, a unique number
given to each radio channel in GSM. The ARFCN can be used to calculate the exact frequency of the radio channel.

IMSI: International Subscriber Identity LAC: Location Area Code CID: Cell Identification Code N-CELL-LAC: Neighboring Cell Location Area Code

Reference:

class antikythera.metrics.Metrics(process_id, sharedMemory=None, *args, **kwargs)[source]

Bases: multiprocessing.context.Process

The metrics

imposter_cell()[source]

Same LAC/CID on different ARFCNs

A cell is received on different ARFCNs (frequencies) within a short time.

Rationale:

To avoid leaving traces of a new, non-existent cell, an IMSI catcher may choose to reuse the cell ID and LAC of an existing cell in an area, but using a different frequency. The IMSI catcher must have a location area different from the current serving cell, such that the MS performs a location update once it close enough. The use of the cell ID on different frequencies may be detected by the analysis if system information of the original cell was received earlier.

False Positives:

A cell may be reconfigured to use a different frequency, but this should happen very rarely.

Example

A simple example would be a single two identical CID and LAC pairs advertising different ARFCNs.

  • Good Cell
    • LAC 1
    • CID 7
    • ARFCN 42
  • Evil Cell
    • LAC 1
    • CID 7
    • ARFCN 1337

The evil cell is pretending to be in the location area code to not trigger a lonely LAC metric, and to not leave a trace of a strange cell wandering all over town.

Reference:

The following SQL query will find packets that share the same Location Area Code and Cell ID, but have different ARFCNs.

inconsistent_lac()[source]

Inconsistent LAC

The LAC of the current base station differs from the LAC of many neighboring cells.

Rationale:

A mobile will only perform a normal location update when changing to a different area, i.e. a base station with a different LAC. An IMSI catcher needs to force a location update to be able to interact with the phone and derive the desired information. Therefore, it must span a cell with a LAC different to all neighboring cells, but with a much better signal strength than the other cells. For an IMSI catcher announcing realistic neighboring cells, this difference between the LAC of the serving cell and all neighboring cell can be detected.

False Positives:

Femto cells may or may not announce a LAC different from all their neighboring cells. Their may be other special situations, like in-house cells where this is the case.

Example

A simple example would be a LAC being the only observed LAC.

  • Evil IMSI Catcher Reports:
    • CID 1337
    • LAC 13
  • All neighboring cells reported by Evil IMSI Catcher are:
    • LAC 7

The evil cell is pretending to be in the location area code to not trigger a lonely LAC metric, and to not leave a trace of a strange cell wandering all over town. It is detected by all cells it advertises having a different LAC.

The information must be obtained form the Evil Cells reporting of neighboring cells.

Reference:

The following sql query pull the area code that differs by datetime minus 5 minutes and places it into a seperate table INCONSISTENT_AREA_CODE.

lonely_cell_id()[source]

Lonesome LAC.

A cell is the only cell observed in its location area.

Rationale:

A mobile will only perform a normal location update when changing to a different area, i.e. a base station with a different LAC. An IMSI catcher needs to force a location update to be able to interact with the phone and derive the desired information. Therefore, it must span a cell with a LAC different to all neighboring cells, but with a much better signal strength than the other cells. An IMSI catcher creating a new LAC for its fake cell will be the only cell operating in this location area. The lack of system information for other cells of this location area can be detected.

False Positives:

When traveling at high speeds or in areas with poor coverage the mobile may record system information for only a single cell of location area.

“Unexpected neighbors also do happen often with subway cells. In some cases the BTS is in a central place, and the RF heads are far away, connected with optical fiber. In these cases cell IDs and LACs are carried over many kilometers into places where they usually do not belong, and often not all neighbors are set correctly, due to restrictions in neighbor list size. I can imagine that such circumstances could trigger a false positive.”

Example

A simple example would be a group or two of cells sharing a LAC and another LAC detected with only a single CID belonging to it.

  • LAC 1 contains CIDs: 1, 2, 3
  • LAC 2 contains CIDs: 4, 5, 6, 6, 8
  • LAC 3 contains CIDs: 9

Here LAC 3, containing only a single CID is suspicious.

Reference:

run()[source]

Main process loop.

shutdown()[source]

Trigger shutdown and exit of run() when called by manager.

antikythera.pysharkpatch module

pysharkpatch.py

Patch the pyshark class LayerFieldsContainer using inheritance to override its __new__ method so that it is pickleable.

class antikythera.pysharkpatch.LayerFieldsContainer[source]

Bases: pyshark.packet.fields.LayerFieldsContainer

Module contents