diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ae1ebf8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,5 @@ +[submodule "thirdparty/srsRAN"] + path = thirdparty/srsLTE + url = https://github.com/FlUxIuS/srsRAN_4G.git + branch = modmobmap + update = merge diff --git a/README.md b/README.md index 58d6b41..ff920bf 100644 --- a/README.md +++ b/README.md @@ -1,247 +1,469 @@ -Modmobmap -========== +# 📡 Modmobmap -Modmobmap is a tool aimed to retrieve information of cellular networks. -As shown in the first [presentation made at BeeRump 2018](https://www.rump.beer/2018/slides/modmobmap.pdf), this tool is able to retrieve information of 2G, 3G, 4G and more cellular network types with minimum requierement: only a phone with ServiceMode. +
-For the moment, the tool has only been tested and developped for the following devices: -- Samsung Galaxy S3 via [xgoldmon (Modmobmap's edition)](https://github.com/FlUxIuS/xgoldmon); -- Samsung Galaxy S4; -- Samsung Galaxy S5; -- Samsung Galaxy Note 2 with LTE; +**Mobile Network Intelligence & Mapping Tool** -Moreover, as it's compatible for XGold via Modmobmap's forked of *xgoldmon*, this tools should also be able to work with devices supported by *xgoldmon* as well: -- Samsung Galaxy S4 GT-I9500 (this is the version without LTE!) -- Samsung Galaxy Nexus GT-I9250 (has to be rooted!) -- Samsung Galaxy S2 GT-I9100 -- Samsung Galaxy Note 2 GT-N7100 +*Retrieve comprehensive information on 2G/3G/4G/and soon 5G cellular networks with minimal equipment* -Note that all devices should be rooted. In any other case, you will have to use the DFR technique by hand! +[![Python](https://img.shields.io/badge/Python-3.x-blue.svg)](https://www.python.org/) +[![License](https://img.shields.io/badge/License-BEER--WARE-yellow.svg)](LICENSE) +[![Docker](https://img.shields.io/badge/Docker-Available-2496ED?logo=docker)](https://hub.docker.com/r/penthertz/modmobmap) +[![Platform](https://img.shields.io/badge/Platform-Linux-lightgrey.svg)](https://www.linux.org/) -Also: Patches, or engines, for other devices are very much welcomed! ;) +[Features](#-key-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-usage-examples) • [Docker](#-docker-deployment) -Requirements -------------- +
-Here are the following requirements: -- Python 2 or 3; -- Last Android SDK to run ADB: https://developer.android.com/studio/#downloads; -- A compatible mobile phone; -- A valid/unvalid SIM card (just in case to provide an IMSI number). +--- -How to use ----------- +## 🎯 Overview -The tool is provided with a quick help that shows you the required argument as follows: +Modmobmap is a comprehensive cellular network reconnaissance tool designed for security researchers, RF engineers, and penetration testers. Originally presented at [BeeRump 2018](https://www.rump.beer/2018/slides/modmobmap.pdf), it combines multiple techniques to map and analyze mobile networks using both commercial smartphones and Software-Defined Radio (SDR) devices. +### 🔥 Key Features + + + + + + + + + + +
+ +**📱 Smartphone-Based Scanning** +- Samsung Galaxy devices (S3-S20) +- ServiceMode interface support +- XGold modem compatibility +- Real-time cell information + + + +**📻 SDR Integration** +- GNU Radio GSM scanning +- srsRAN LTE/NB-IoT support +- Multi-band analysis +- Passive reconnaissance + +
+ +**🌐 Network Coverage** +- 2G/GSM networks +- 3G/UMTS systems +- 4G/LTE cells +- NB-IoT detection + + + +**💾 Data Management** +- JSON export format +- Real-time logging +- Cell database +- Operator caching + +
+ +--- + +## 🛠️ Supported Hardware + +### 📱 Mobile Devices (Rooted Required) + +
+✅ Tested & Confirmed + +- **Samsung Galaxy S3** (via [xgoldmon](https://github.com/FlUxIuS/xgoldmon)) +- **Samsung Galaxy S4** (GT-I9500) +- **Samsung Galaxy S5** +- **Samsung Galaxy Note 2** (LTE version) +- **Samsung Galaxy S20** +- **Samsung Galaxy Nexus** (GT-I9250, rooted) +- **Samsung Galaxy S2** (GT-I9100) + +> 💡 **Note:** All devices must be rooted. For non-rooted devices, manual DFR technique required. + +
+ +### 📻 SDR Devices (via gr-osmosdr & UHD) + +
+🎛️ Compatible SDR Hardware + +| Device | Driver | GSM | LTE | Notes | +|--------|--------|-----|-----|-------| +| **RTL-SDR** | rtlsdr/soapy | ✅ | ✅ | Budget option | +| **HackRF One** | hackrf | ✅ | ✅ | Wide frequency range | +| **BladeRF** | bladerf | ✅ | ✅ | High performance | +| **USRP (all models)** | uhd | ✅ | ✅ | Professional grade | +| **ANTSDR E200** | uhd | ✅ | ✅ | Requires proper UHD setup | +| **AirSpy** | airspy | ✅ | ❌ | GSM only | +| **SDRplay RSP** | sdrplay | ✅ | ❌ | GSM only | +| **FunCube Dongle** | fcd | ✅ | ❌ | GSM only | + +
+ +--- + +## 🚀 Installation + +### Prerequisites + +```bash +# Core dependencies +- Python 3.x +- Android SDK (for ADB) +- GNU Radio 3.10+ with gr-gsm +- Valid/invalid SIM card (for IMSI) +``` + +### 🐧 Ubuntu 24.04 Quick Install + +```bash +# Clone repository with submodules +git clone --recursive https://github.com/FlUxIuS/modmobmap.git +cd modmobmap + +# Run automated installation script +sudo ./install_all-Ubuntu_24.04.sh + +# Update submodules (if needed) +git submodule update --init --recursive --remote ``` -python modmobmap.py -h -usage: modmobmap.py [-h] [-m MODULE] [-n NETWORKS] [-o] [-s ANDROIDSDK] - [-a ATMODE] [-f FILE] -Mobile network mapping tool with cheap equipments +### 🔧 Manual Installation -optional arguments: - -h, --help show this help message and exit - -m MODULE, --module MODULE - Module to use (e.g: "servicemode" by default) - -n NETWORKS, --networks NETWORKS - Networks in MCCMNC format splitted with commas - -o, --cached_operator - Use operator in cache to speed up the passive scan - -s ANDROIDSDK, --sdk ANDROIDSDK - Android SDK path - -a ATMODE, --at ATMODE - AT access mode. If host put something like - "/dev/ttyUSBxx. By default it uses ADB." - -f FILE, --file FILE File to parse. For the moment it could be used in - combination with AT mode host. +
+Click to expand manual installation steps + +```bash +# 1. Install Python dependencies +pip3 install -r requirements.txt + +# 2. Install Android SDK +# Download from: https://developer.android.com/studio/#downloads +export ANDROID_SDK_ROOT=/path/to/android-sdk + +# 3. Install GNU Radio & gr-gsm +sudo apt-get install gnuradio gr-gsm + +# 4. Build srsRAN (for LTE scanning) +cd thirdparty/srsLTE +mkdir build && cd build +cmake ../ +make -j$(nproc) +sudo make install ``` -Assuming the Android SDK is installed in */opt/Android*, the tool can be quickly started as follows: +
+ +--- + +## 🎯 Quick Start + +### 🔰 Basic Scanning (Smartphone) + +```bash +# Auto-detect operators and scan all available networks +sudo python3 modmobmap.py +# Specify Android SDK location (if not in default path) +sudo python3 modmobmap.py -s /opt/Android/sdk +``` + +**Expected Output:** ``` -$ sudo python modmobmap.py => Requesting a list of MCC/MNC. Please wait, it may take a while... Found 2 operator(s) -{u'20810': u'F SFR', u'20820': u'F-Bouygues Telecom'} -[+] Unregistered from current PLMN -[+] New cell detected [CellID/PCI-DL_freq (4XXX-81)] +{'20810': 'F SFR', '20820': 'F-Bouygues Telecom'} + +[+] New cell detected [CellID/PCI-DL_freq (4XXX-81)] Network type=2G PLMN=208-10 ARFCN=81 -[+] New cell detected [CellID/PCI-DL_freq (6XXXXXX-2950)] - Network type=3G - PLMN=208-20 - Band=8 - Downlink UARFCN=2950 - Uplink UARFCN=2725 -[+] New cell detected [CellID/PCI-DL_freq (3XX-6300)] + +[+] New cell detected [CellID/PCI-DL_freq (3XX-6300)] Network type=4G PLMN=208-10 Band=20 Downlink EARFCN=6300 -[+] New cell detected [CellID/PCI-DL_freq (3XX-2825)] - Network type=4G - PLMN=208-10 - Band=7 - Downlink EARFCN=2825 -[+] New cell detected [CellID/PCI-DL_freq (3XX-1675)] - Network type=4G - PLMN=208-10 - Band=3 - Downlink EARFCN=1675 -[...] ``` -Note: If the Android SDK is installed anywhere else, you can use the *-s* parameter to specify its directory. - -Speed-up the passive scan ---------------------------- +### ⚡ Speed Up Scanning (Cached Operators) -When looking for operators, an AT command is sent to the modem. If you want to speed-up the scanning, you can hardcoded the operators to the following file `cache/operators.json`: - -``` +Create `cache/operators.json`: +```json { "20801": "Orange", - "20810": "F SFR", + "20810": "F SFR", "20815": "Free", "20820": "F-Bouygues Telecom" } ``` -Only the MCC/MNC codes are inmportant. Then you can re-launch the tool as follows: +Run with cache: +```bash +sudo python3 modmobmap.py -o +``` + +### 🎯 Target Specific Operators + +```bash +# Focus on Orange (MCC/MNC: 20801) +sudo python3 modmobmap.py -n 20801 +# Multiple operators +sudo python3 modmobmap.py -n 20801,20810,20815 ``` -$ sudo python modmobmap.py -o -=> Requesting a list of MCC/MNC. Please wait, it may take a while... -Found 4 operators in cache, you choose to reuse them. -Found 4 operator(s) -{u'20810': u'F SFR', u'20820': u'F-Bouygues Telecom', u'20815': u'Free', u'20801': u'Orange'} -[+] Unregistered from current PLMN -[+] New cell detected [CellID/PCI-DL_freq (XXXX-10614)] - Network type=3G - PLMN=208-10 - Band=1 - Downlink UARFCN=10614 - Uplink UARFCN=9664 -[...] -[+] New cell detected [CellID/PCI-DL_freq (XXX-3501)] - Network type=4G - PLMN=208-20 - Band=8 - Downlink EARFCN=3501 -[...] -[+] Unregistered from current PLMN -=> Changing MCC/MNC for: 20815 -[+] New cell detected [CellID/PCI-DL_freq (XXX-2825)] - Network type=4G - PLMN=208-15 - Band=7 - Downlink EARFCN=2825 -[...] -=> Changing MCC/MNC for: 20801 -[+] New cell detected [CellID/PCI-DL_freq (XXXXX-3011)] - Network type=3G - PLMN=208-1 - Band=8 - Downlink UARFCN=3011 - Uplink UARFCN=2786 -[...] + +> 📚 **MCC/MNC Lookup:** [Wikipedia Mobile Country Codes](https://en.wikipedia.org/wiki/Mobile_country_code) + +--- + +## 📖 Usage Examples + +### 🔵 Method 1: ServiceMode (Samsung Devices) + +The default and most straightforward method: + +```bash +sudo python3 modmobmap.py -m servicemode ``` -Note we have been able to detect other cells the AT command *AT+COPS* did not returned. +### 🟢 Method 2: XGoldmon (XGold Modems) + +For devices with XGold chipsets (S3, S4, Nexus): -A complet list of MCC and MNC codes could be retrieved anywhere on internet and in Wikipedia: https://en.wikipedia.org/wiki/Mobile_country_code +**Terminal 1 - Start xgoldmon:** +```bash +cd /path/to/xgoldmon +sudo ./xgoldmon -t s3 -m /dev/ttyACM1 +# Creates celllog.fifo +``` + +**Terminal 2 - Run Modmobmap:** +```bash +sudo python3 modmobmap.py \ + -f /path/to/xgoldmon/celllog.fifo \ + -m xgoldmod \ + -a /dev/ttyACM0 \ + -o +``` -Focusing some operators ------------------------- +### 🟡 Method 3: GSM Scanning (SDR - gr-gsm) -It is possible to tell *Modmobmap* to focus only on specific operators with the *-m* argument: +Scan GSM bands with Software-Defined Radio: +```bash +# RTL-SDR scanning GSM-R and GSM900 +python3 modmobmap.py -m grgsm -b GSM-R,GSM900 -g rtlsdr + +# BladeRF scanning multiple bands +python3 modmobmap.py -m grgsm -b GSM850,GSM900,DCS1800 -g bladerf + +# HackRF with custom gain +python3 modmobmap.py -m grgsm -b GSM900 -g "driver=hackrf,gain=40" ``` -$ sudo python modmobmap.py -n 20801 -=> Manual MCC/MNC processing... -Found 1 operator(s) -{'20801': '20801'} -[...] -=> Changing MCC/MNC for: 20801 -[+] New cell detected [CellID/PCI-DL_freq (XXX-1675)] - Network type=4G - PLMN=208-01 - Band=3 - Downlink EARFCN=1675 -[+] New cell detected [CellID/PCI-DL_freq (XXXXX-3011)] - Network type=3G - PLMN=208-1 - Band=8 - Downlink UARFCN=3011 - Uplink UARFCN=2786 -=> Changing network type for 3G only -[+] New cell detected [CellID/PCI-DL_freq (XXXXX-2950)] - Network type=3G - PLMN=208-1 - Band=8 - Downlink UARFCN=2950 - Uplink UARFCN=2725 + +**Available GSM Bands:** +- `GSM850` (824-849 MHz) +- `GSM-R` (876-880 MHz, Railway) +- `GSM900` (890-915 MHz) +- `DCS1800` (1710-1785 MHz) +- `PCS1900` (1850-1910 MHz) + +### 🔴 Method 4: LTE Scanning (SDR - srsRAN) + +Scan LTE cells using srsRAN: + +```bash +# USRP scanning LTE Band 28 +python3 modmobmap.py -m srslte_pss -b 28 -g 'driver=usrp' + +# BladeRF scanning Band 7 +python3 modmobmap.py -m srslte_pss -b 7 -g 'driver=bladerf' + +# RTL-SDR via Soapy (specify device ID!) +python3 modmobmap.py -m srslte_pss -b 20 -g 'soapy:id=1' + +# Multiple bands +python3 modmobmap.py -m srslte_pss -b 3,7,20,28 -g 'driver=usrp' ``` -Using Modmobmap with xgoldmon ------------------------------- +### 🟣 Method 5: NB-IoT Scanning -With XGold modems, the use of xgoldmon will be required. But for now, only the fork for *Modmobmap* works to retrieve exact information of cells via the DIAG interface, and could be downloaded at: https://github.com/FlUxIuS/xgoldmon +Detect Narrowband IoT cells: -Then after compiling, the tool *xgoldmon* could be started using the *-m* parameter like this: +```bash +sudo python3 modmobmap.py -m srslte_npss -b 20 -g 'soapy:id=1' +``` +--- + +## 🔍 Finding Your SDR Device + +### List Soapy Devices +```bash +SoapySDRUtil --find ``` -sudo ./xgoldmon -t s3 -m /dev/ttyACM1 + +**Example Output:** +``` +Found device 0 + driver = rtlsdr + label = Generic RTL2832U OEM :: 00000001 + serial = 00000001 + +Found device 1 + driver = bladerf + label = BladeRF #0 [bd7fffbf..d5958b06] + serial = bd7fffbf8efb4de4ba08d94bd5958b06 +``` + +### List UHD Devices +```bash +uhd_find_devices ``` -This will create a FIFO file that will be requested by Modmobmap later: +--- +## 💾 Data Export + +Results are automatically saved when you stop the scan (`Ctrl+C`): + +```bash +[+] Cells save as cells_1595446203.json ``` -$ ls -celllog.fifo Makefile screenshot-mtsms-while-in-a-call.png xgoldmon + +### JSON Output Format + +```json +{ + "3XX-6300": { + "PLMN": "208-10", + "band": 20, + "bandwidth": "10MHz", + "eARFCN": 6300, + "PCI": "3XX", + "TAC": "XXXX", + "type": "4G" + } +} ``` -Then we can start running *Modmobmap* as follows precising the AT serial interface (*/dev/ttyACM0*) and the fifo file created by *xgoldmon* (* 🔐 **Security Note:** `--privileged` flag required for USB/hardware access + +--- + +## 📊 Command Reference + +```bash +python3 modmobmap.py [OPTIONS] + +Required Options: + -m, --module MODULE Scanning module: servicemode|xgoldmod|grgsm|srslte_pss|srslte_npss + +Optional Parameters: + -n, --networks NETWORKS Target MCC/MNC codes (comma-separated) + -o, --cached_operator Use cached operators for faster scanning + -s, --sdk PATH Android SDK path (default: auto-detect) + -a, --at DEVICE AT serial device (e.g., /dev/ttyUSB0) + -f, --file FILE FIFO file for xgoldmon integration + -b, --bands BANDS Frequency bands to scan (SDR modes) + -g, --args ARGS SDR device arguments (driver, gain, etc.) + +Examples: + # Basic smartphone scan + sudo python3 modmobmap.py + + # GSM scan with RTL-SDR + python3 modmobmap.py -m grgsm -b GSM900 -g rtlsdr + + # LTE scan targeting Band 7 + python3 modmobmap.py -m srslte_pss -b 7 -g 'driver=usrp' + + # Cached operators, specific network + sudo python3 modmobmap.py -n 20810 -o ``` -$ sudo python3 modmobmap.py -f //celllog.fifo -m xgoldmod -a /dev/ttyACM0 -o -=> Requesting a list of MCC/MNC. Please wait, it may take a while... -Found 4 operators in cache, you choose to reuse them. -Found 4 operator(s) -{'20801': 'Orange', '20810': 'F SFR', '20815': 'Free', '20820': 'F-Bouygues Telecom'} -[+] New cell detected [CellID/PCI-DL_freq (0x7XXXX-65535)] - Network type=3G - PLMN=208-1 - Downlink UARFCN=65535 - Uplink UARFCN=2850 -[+] Unregistered from current PLMN -[+] New cell detected [CellID/PCI-DL_freq (0x7XXXX-3011)] - Network type=3G - PLMN=208-1 - Downlink UARFCN=3011 - Uplink UARFCN=2786 -[...] -[+] Unregistered from current PLMN -=> Changing MCC/MNC for: 20810 -[+] New cell detected [CellID/PCI-DL_freq (0x3XXXXX-3075)] - Network type=3G - PLMN=208-10 - Downlink UARFCN=3075 - Uplink UARFCN=2850 -[...] - ``` -Note that retrieving results from AT+COPS command could take a lot of time and sometime would need to restart the tool. If the tool is blocked on the operator retrieving step, please use cached or targeted operators features instead. +--- -Saving results ---------------- +## 🤝 Contributing -The process could be stopped any time when killing the process with a keyboard interrupt signal. Then results will be automatically save in a JSON file as follows: +We welcome contributions! Here's how you can help: + +- 🐛 **Bug Reports:** Open an issue with details +- 🔧 **Device Support:** Add parsers for new phones +- 📡 **SDR Engines:** Integrate new hardware/software +- 📚 **Documentation:** Improve guides and examples + +--- + +## 📞 Support & Community + +- 🐦 **Twitter:** [@Penthertz](https://twitter.com/penthertz) +- 🌐 **Website:** [penthertz.com](https://www.penthertz.com) + +--- + +## 📜 License ``` -[...] -^C[+] Cells save as cells_1528738901.json +---------------------------------------------------------------------------- +"THE BEER-WARE LICENSE" (Revision 42): + wrote this file. As long as you retain +this notice you can do whatever you want with this stuff. If we meet some +day, and you think this stuff is worth it, you can buy me a beer in return. + FlUxIuS ;) +---------------------------------------------------------------------------- ``` + +--- + +## 🙏 Acknowledgments + +- Original presentation: [BeeRump 2018](https://www.rump.beer/2018/slides/modmobmap.pdf) +- [gr-gsm](https://github.com/ptrkrysik/gr-gsm) by Piotr Krysik +- [srsRAN](https://github.com/srsran/srsRAN) by Software Radio Systems +- [xgoldmon](https://github.com/2b-as/xgoldmon) by 2b-as + +--- + +
+ +**Made with ☕ & 📡 by the Penthertz Team** + +⭐ Star us on GitHub if this project helped you! + +
diff --git a/__init__.py b/__init__.py index e69de29..fe0868c 100644 --- a/__init__.py +++ b/__init__.py @@ -0,0 +1,6 @@ +# ---------------------------------------------------------------------------- +# "THE BEER-WARE LICENSE" (Revision 42): +# )penthertz.com> wrote this file. As long as you retain this notice you +# can do whatever you want with this stuff. If we meet some day, and you think +# this stuff is worth it, you can buy me a beer in return FlUxIuS ;) +# ---------------------------------------------------------------------------- diff --git a/cache/operators.json b/cache/operators.json index 94f8921..9fb70c2 100644 --- a/cache/operators.json +++ b/cache/operators.json @@ -3,4 +3,4 @@ "20810": "F SFR", "20815": "Free", "20820": "F-Bouygues Telecom" -} \ No newline at end of file +} diff --git a/core/__init__.py b/core/__init__.py index d13e961..1232884 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) -# ---------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/core/mKB.py b/core/mKB.py index 71ab956..f60f39c 100644 --- a/core/mKB.py +++ b/core/mKB.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/core/mLog.py b/core/mLog.py index 96406a8..1a3e728 100755 --- a/core/mLog.py +++ b/core/mLog.py @@ -3,7 +3,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- @@ -27,18 +27,19 @@ def wrapped(*args, **kwargs): if id_ not in kb.data['SM_cells'] and id_ is not None: kb.data['SM_cells'][id_] = v if kb.config['verbose'] == True: - string2print = "[+] New cell detected [CellID/PCI-DL_freq (%s)]" % id_ - string2print += "\n\r Network type=%s" % v['type'] - string2print += "\n\r PLMN=%s" % v['PLMN'] + string2print = "[+] New cell detected [CellID/PCI-DL_freq ({})]".format(id_) + string2print += "\n\r Network type={}".format(v['type']) + string2print += "\n\r PLMN={}".format(v['PLMN']) if 'band' in v: - string2print += "\n\r Band=%i" % v['band'] + string2print += "\n\r Band={}".format(v['band']) if '4G' in v['type']: - string2print += "\n\r Downlink EARFCN=%i" % v['eARFCN'] + string2print += "\n\r Downlink EARFCN={}".format(v['eARFCN']) elif '3G' in v['type']: - string2print += "\n\r Downlink UARFCN=%i" % v['RX'] - string2print += "\n\r Uplink UARFCN=%i" % v['TX'] + string2print += "\n\r Downlink UARFCN={}".format(v['RX']) + if 'TX' in v: + string2print += "\n\r Uplink UARFCN={}".format(v['TX']) elif '2G' in v['type']: - string2print += "\n\r ARFCN=%i" % v['arfcn'] + string2print += "\n\r ARFCN={}".format(v['arfcn']) print (bcolors.OKGREEN+string2print+bcolors.ENDC) return result return wrapped diff --git a/engines/__init__.py b/engines/__init__.py index d13e961..8df667a 100644 --- a/engines/__init__.py +++ b/engines/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/engines/android/__init__.py b/engines/android/__init__.py index d13e961..8df667a 100644 --- a/engines/android/__init__.py +++ b/engines/android/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/engines/android/generic/ADBshell.py b/engines/android/generic/ADBshell.py index e299ac1..53b9893 100644 --- a/engines/android/generic/ADBshell.py +++ b/engines/android/generic/ADBshell.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- @@ -71,7 +71,7 @@ def _buildcommand(self, command): adbpath = None if self.androidsdkpath is not None: if 'linux' in sys.platform.lower(): - adbpath = self.androidsdkpath + '/platform-tools/adb' + adbpath = self.androidsdkpath + '/adb' if adbpath is None: raise ADBError('platform') commandstring = [adbpath, 'shell'] @@ -90,7 +90,7 @@ def getDevfile(self): process = self.run_adbcmdshell('getprop rild.libargs') devfile = process.stdout.readline().split(b'/dev/') if len(devfile) >= 2: - m = re.match(b'([\d\w]+)', devfile[1]) + m = re.match(rb'([\d\w]+)', devfile[1]) if m is not None: devfile = b'/dev/' + devfile[1].replace(b'\r\n', b'') else: diff --git a/engines/android/generic/RILd.py b/engines/android/generic/RILd.py index 31200c7..96c7a25 100644 --- a/engines/android/generic/RILd.py +++ b/engines/android/generic/RILd.py @@ -3,7 +3,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/engines/android/generic/__init__.py b/engines/android/generic/__init__.py index d13e961..8df667a 100644 --- a/engines/android/generic/__init__.py +++ b/engines/android/generic/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/engines/android/samsung/ServiceMode.py b/engines/android/samsung/ServiceMode.py index da141bc..ed255a4 100755 --- a/engines/android/samsung/ServiceMode.py +++ b/engines/android/samsung/ServiceMode.py @@ -3,7 +3,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- @@ -35,20 +35,20 @@ def parse4Gcell(self, string): for s in string.split(b'\r\n'): if b'LTE' in s: if b'Band' in s: - band = re.match(b'^.*Band?:?\s?(\d+)', s).group(1) + band = re.match(rb'^.*Band?:?\s?(\d+)', s).group(1) elif b'BAND' in s and b'BW' in s: - band, bandwidth = re.match(b'^.*BAND:?\s?(\d+)\sBW:?\s?([\d+\w]+)?\s?\_$', s).groups() + band, bandwidth = re.match(rb'^.*BAND:?\s?(\d+)\sBW:?\s?([\d+\w]+)?\s?\_$', s).groups() if b'MCC-MNC' in s: if b'TAC' in s: - plmn, tac = re.match(b'^.*MCC-MNC?\s?:?\s?([\d\-]+)\,?\s?TAC:?\s?(\d+)?\s?\_$', s).groups() + plmn, tac = re.match(rb'^.*MCC-MNC?\s?:?\s?([\d\-]+)\,?\s?TAC:?\s?(\d+)?\s?\_$', s).groups() elif b'MeG': - plmn = re.match(b'^.*MCC-MNC?\s?:?\s?([\d\-\s]+)', s).group(1) + plmn = re.match(rb'^.*MCC-MNC?\s?:?\s?([\d\-\s]+)', s).group(1) if b'Earfcn_dl:' in s: - earfcn, pci = re.match(b'^.*Earfcn_dl:?\s?(\d+),?\s?PCI:?\s?(\d+)?\s?\_', s).groups() + earfcn, pci = re.match(rb'^.*Earfcn_dl:?\s?(\d+),?\s?PCI:?\s?(\d+)?\s?\_', s).groups() if b'LTE DL BW' in s: - bandwidth = re.match(b'^.*BW?\s?:?\s?([\d\w]+)', s).group(1) + bandwidth = re.match(rb'^.*BW?\s?:?\s?([\d\w]+)', s).group(1) if tac is None and b'TAC' in s: - tac = re.match(b'^.*TAC?\s?:?\s?([\d]+)', s).group(1) + tac = re.match(rb'^.*TAC?\s?:?\s?([\d]+)', s).group(1) if None not in [plmn, tac, earfcn, band, bandwidth, pci]: tac = tac.decode('utf-8') plmn = plmn.decode('utf-8').replace(' ','') @@ -65,6 +65,80 @@ def parse4Gcell(self, string): } return cell + + @Cellslogger + def parse4Gcell_sgs20like(self, string): + ''' + Parse 4G cells information for SGS 20 phone like + in(1): string returned by logcat + out: dict infos + ''' + plmn = None + tac = None + earfcn = None + band = None + bandwidth = None + cid = None + pci = None + cell = {} + for s in string.split(b'\n'): + if b'Band' in s and b'BW' in s: + band, bandwidth = re.match(rb'^.*Band:?\s?(\d+)\sBW:?\s?([\d+\w]+)?\s?\_$', s).groups() + bandwidth = bandwidth.replace(b'MHz', b'') + if b'Registered' in s: + plmn = re.match(rb'^.*PLMN?\s?:?\s?([\d\-\s]+)', s).group(1).replace(b' ', b'') + if b'DL & UL Frequency' in s: + earfcn = re.match(rb'^.*Frequency:?\s?([\d+\w]+)', s).group(1) + if tac is None and b'TAC' in s: + tac = re.match(rb'^.*TAC?\s?:?\s?([\d]+)', s).group(1) + if b'CellID' in s and b'PCI' in s: + cid, pci = re.match(rb'^.*ServingCellID:?\s?([\w\d]+)PCI:?\s?([\w\d]+)?\s?\_$', s).groups() + if None not in [plmn, tac, earfcn, band, bandwidth]: + tac = tac.decode('utf-8') + plmn = plmn.decode('utf-8').replace(' ','') + bandwidth = bandwidth.decode('utf-8') + cid2 = "%s-%i" % (cid, int(pci)) + cell[cid2] = { 'PLMN' : plmn, + 'band' : int(band), + 'bandwidth' : bandwidth, + 'eARFCN': int(earfcn), + 'TAC' : tac, + 'type' : '4G', + } + return cell + + + @Cellslogger + def parse3Gcell_sgs20like(self, string): + ''' + Parse 3G cells information SGS20 like + in(1): string returned by logcat + out: dict infos + ''' + plmn = None + cid = None + rx = None + cell = {} + for s in string.split(b'\n'): + if b'MCC' in s: + mcc, mnc = re.match(rb'^.*MCC?\s?:?\s?(\d+)?\s?MNC?\s?:?\s?(\d+)?\s?\_$', s).groups() + plmn = mcc+mnc + if b'CellId' in s: + cid = re.match(rb'^.*CellId?\s?:?\s?([\w\d]+)', s).group(1) + if b'uarfcn:' in s: + rx = re.match(rb'^.*uarfcn:?\s?(\d+)', s).group(1) + if None not in [rx, cid, plmn]: + cid = cid.decode('utf-8').replace('_','') + plmn = plmn.decode("utf-8") + cid2 = "%s-%i" % (cid, int(rx)) + cell[cid2] = { 'PLMN' : plmn, + 'RX' : int(rx), + 'band': -1, + 'type' : '3G', + } + return cell + + @Cellslogger def parse3Gcell_sgs3like(self, string): ''' @@ -80,15 +154,15 @@ def parse3Gcell_sgs3like(self, string): cell = {} for s in string.split(b'\r\n'): if b'Band' in s: - band = re.match(b'^.*Band?:?\s?(\d+)', s).group(1) + band = re.match(rb'^.*Band?:?\s?(\d+)', s).group(1) if b'Reg PLMN' in s: - plmn = re.match(b'^.*Reg PLMN?\s?([\d\-]+)', s).group(1) + plmn = re.match(rb'^.*Reg PLMN?\s?([\d\-]+)', s).group(1) if b'CELL_ID' in s: - cid = re.match(b'^.*CELL_ID:?\s?(\S+)', s).group(1) + cid = re.match(rb'^.*CELL_ID:?\s?(\S+)', s).group(1) if b'CH DL:' in s: - tx = re.match(b'^.*CH DL:?\s?(\d+)', s).group(1) + tx = re.match(rb'^.*CH DL:?\s?(\d+)', s).group(1) if b', UL:' in s: - rx = re.match(b'^.*\,\sUL:?\s?(\d+)', s).group(1) + rx = re.match(rb'^.*\,\sUL:?\s?(\d+)', s).group(1) if None not in [tx, rx, cid, plmn, band]: cid = cid.decode('utf-8').replace('_','') plmn = plmn.decode("utf-8").replace(' ', '') @@ -116,17 +190,20 @@ def parse3Gcell(self, string): cell = {} for s in string.split(b'\r\n'): if b'Band' in s: - band = re.match(b'^.*Band?:?\s?(\d+)', s).group(1) + band = re.match(rb'^.*Band?:?\s?(\d+)', s).group(1) if b'PLMN:' in s: - plmn = re.match(b'^.*PLMN:?\s?([\d\-]+)?\s?\_$', s).group(1) + plmn = re.match(rb'^.*PLMN:?\s?([\d\-]+)?\s?\_$', s).group(1) elif b'MCC-MNC :' in s: - plmn = re.match(b'^.*MCC-MNC\s:?\s?([\d\-\s]+)?\s?\_$', s).group(1) + try: + plmn = re.match(rb'^.*MCC-MNC\s:?\s?([\d\-\s]+)?\s?\_$', s).group(1) + except: + plmn = None if b'CID:' in s: - cid = re.match(b'^.*CID:?\s?(\S+)?\s?_$', s).group(1) + cid = re.match(rb'^.*CID:?\s?(\S+)?\s?_$', s).group(1) if b' TX:' in s: - tx = re.match(b'^.*TX:?\s?(\d+)', s).group(1) + tx = re.match(rb'^.*TX:?\s?(\d+)', s).group(1) if b' RX:' in s: - rx = re.match(b'^.*RX:?\s?(\d+)', s).group(1) + rx = re.match(rb'^.*RX:?\s?(\d+)', s).group(1) if None not in [tx, rx, cid, plmn, band]: cid = cid.decode('utf-8') plmn = plmn.decode("utf-8").replace(' ', '') @@ -139,6 +216,38 @@ def parse3Gcell(self, string): } return cell + + @Cellslogger + def parse2Gcell_sgs20like(self, string): + ''' + Parse 2G cells information SGS20 like structs + in(1): string returned by logcat + out: dict infos + ''' + plmn = None + cid = None + arfcn = None + cell = {} + for s in string.split(b'\n'): + if b'MCC' in s: + mcc, mnc = re.match(rb'^.*MCC?\s?:?\s?(\d+)?\s?MNC?\s?:?\s?(\d+)?\s?\_$', s).groups() + plmn = mcc+mnc + if b'CellId' in s: + cid = re.match(rb'^.*CellId?\s?:?\s?([\w\d]+)', s).group(1) + if b'arfcn' in s: + arfcn = re.match(rb'^.*arfcn?\s?:?\s?(\d+)', s).group(1) + if None not in [arfcn, cid, plmn]: + cid = cid.decode('utf-8') + plmn = plmn.decode("utf-8").replace(' ', '') + cid2 = "%s-%i" % (cid, int(arfcn)) + cell[cid2] = { 'PLMN' : plmn, + 'arfcn' : int(arfcn), + 'type' : '2G', + 'cid' : cid, + } + return cell + + @Cellslogger def parse2Gcell(self, string): ''' @@ -152,13 +261,13 @@ def parse2Gcell(self, string): cell = {} for s in string.split(b'\r\n'): if b'PLMN:' in s: - plmn = re.match(b'^.*PLMN:?\s?([\d\-]+)?\s?\_$', s).group(1) + plmn = re.match(rb'^.*PLMN:?\s?([\d\-]+)?\s?\_$', s).group(1) elif b'MCC-MNC :' in s: - plmn = re.match(b'^.*MCC-MNC\s:?\s?([\d\-\s]+)?\s?\_$', s).group(1) + plmn = re.match(rb'^.*MCC-MNC\s:?\s?([\d\-\s]+)?\s?\_$', s).group(1) if b'CID:' in s: - cid = re.match(b'^.*CID:?\s?(\S+)?\s?_$', s).group(1) + cid = re.match(rb'^.*CID:?\s?(\S+)?\s?_$', s).group(1) if b' Tra:' in s: - arfcn = re.match(b'^.*Tra:?\s?(\d+)', s).group(1) + arfcn = re.match(rb'^.*Tra:?\s?(\d+)', s).group(1) if None not in [arfcn, cid, plmn]: cid = cid.decode('utf-8') plmn = plmn.decode("utf-8").replace(' ', '') @@ -190,17 +299,27 @@ def grablogcat(self): if b'Update!' in line: if b'LTE RRC:' in capture: self.parse4Gcell(capture) + elif b'LTE-BASIC' in capture: + self.parse4Gcell_sgs20like(capture) elif b'UMTS :' in capture: self.parse3Gcell_sgs3like(capture) - if b'GSM' in capture: - self.parse2Gcell(capture) + elif b'Serving Cell Info' in capture: + self.parse3Gcell_sgs20like(capture) + elif b'GSM' in capture: + if b'2G-BASIC' in capture: + self.parse2Gcell_sgs20like(capture) + else: + self.parse2Gcell(capture) else: - self.parse3Gcell(capture) + if b'LTE' not in capture: + self.parse3Gcell(capture) capture = b'' capture += line except (KeyboardInterrupt, SystemExit): stop = True stdout_reader.stop() + #except Exception as e: + # print (e) stdout_reader.stop() process.stdout.close() diff --git a/engines/android/samsung/__init__.py b/engines/android/samsung/__init__.py index d13e961..1232884 100644 --- a/engines/android/samsung/__init__.py +++ b/engines/android/samsung/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) -# ---------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/engines/host/__init__.py b/engines/host/__init__.py index d13e961..8df667a 100644 --- a/engines/host/__init__.py +++ b/engines/host/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/engines/host/diag/__init__.py b/engines/host/diag/__init__.py index d13e961..8df667a 100644 --- a/engines/host/diag/__init__.py +++ b/engines/host/diag/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/engines/host/diag/xgoldmod.py b/engines/host/diag/xgoldmod.py index 9457af7..030159a 100644 --- a/engines/host/diag/xgoldmod.py +++ b/engines/host/diag/xgoldmod.py @@ -3,7 +3,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/engines/host/serial/AT.py b/engines/host/serial/AT.py index 7158ae2..0a4f211 100644 --- a/engines/host/serial/AT.py +++ b/engines/host/serial/AT.py @@ -1,4 +1,12 @@ # -*- coding: utf-8 -*- + +# ---------------------------------------------------------------------------- +# "THE BEER-WARE LICENSE" (Revision 42): +# )penthertz.com> wrote this file. As long as you retain this notice you +# can do whatever you want with this stuff. If we meet some day, and you think +# this stuff is worth it, you can buy me a beer in return FlUxIuS ;) +# ---------------------------------------------------------------------------- + from __future__ import print_function import serial diff --git a/engines/host/serial/__init__.py b/engines/host/serial/__init__.py index d13e961..1232884 100644 --- a/engines/host/serial/__init__.py +++ b/engines/host/serial/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) -# ---------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/engines/sdr/__init__.py b/engines/sdr/__init__.py new file mode 100644 index 0000000..fe0868c --- /dev/null +++ b/engines/sdr/__init__.py @@ -0,0 +1,6 @@ +# ---------------------------------------------------------------------------- +# "THE BEER-WARE LICENSE" (Revision 42): +# )penthertz.com> wrote this file. As long as you retain this notice you +# can do whatever you want with this stuff. If we meet some day, and you think +# this stuff is worth it, you can buy me a beer in return FlUxIuS ;) +# ---------------------------------------------------------------------------- diff --git a/engines/sdr/grgsm_scanner.py b/engines/sdr/grgsm_scanner.py new file mode 100644 index 0000000..5fb2f7b --- /dev/null +++ b/engines/sdr/grgsm_scanner.py @@ -0,0 +1,811 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# @file +# @author (C) 2015 by Piotr Krysik +# @author (C) 2015 by Roman Khassraf +# @section LICENSE +# +# Gr-gsm is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# Gr-gsm 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. +# +# You should have received a copy of the GNU General Public License +# along with gr-gsm; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# +# +from core.mLog import * + +from gnuradio import blocks +from gnuradio import network +from gnuradio import gr +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.filter import firdes +from gnuradio.filter import pfb +from math import pi +from optparse import OptionParser + +import math +from gnuradio import gsm +import numpy +import os +import osmosdr +import pmt +import time +import sys +import gnuradio.gsm.arfcn as arfcn +import gc + + +class multichannel_receiver(gr.hier_block2): + def __init__(self, arfcns=[], center_freq=935e6, osr=4, tseq=1, wide_samp_rate=2e6): + gr.hier_block2.__init__( + self, "Multi Arfcns GSM Receiver", + gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), + gr.io_signaturev(len(arfcns), len(arfcns), list(map(lambda x: gr.sizeof_gr_complex * 1, arfcns))), + ) + self.message_port_register_hier_out("out_c0") + self.message_port_register_hier_out("out_cx") + + ################################################## + # Parameters + ################################################## + self.arfcns = arfcns + self.center_freq = center_freq + self.osr = osr + self.tseq = tseq + self.wide_samp_rate = wide_samp_rate + ################################################## + # Variables + ################################################## + self.samp_rate = samp_rate = 1e6 + self.fcs = fcs = list(map(lambda x: arfcn.arfcn2downlink(x), arfcns)) + + ################################################## + # Blocks + ################################################## + self.gsm_receiver = gsm.receiver(osr, arfcns, [tseq], False) + self.gsm_clock_offset_control = gsm.clock_offset_control(fcs[0], samp_rate, osr) + self.pfb_arb_resamplers_xxx = [] + self.gsm_inputs = [] + self.blocks_rotators_cc = [] + + for i in range(len(self.fcs)): + resampler = pfb.arb_resampler_ccf( + samp_rate / wide_samp_rate, + taps=None, + flt_size=32) + resampler.declare_sample_delay(0) + self.pfb_arb_resamplers_xxx.append(resampler) + + gsm_input = gsm.gsm_input( + ppm=0, + osr=osr, + fc=fcs[i], + samp_rate_in=samp_rate, + ) + self.gsm_inputs.append(gsm_input) + + blocks_rotator_cc = blocks.rotator_cc(-2 * pi * (fcs[i] - center_freq) / wide_samp_rate) + self.blocks_rotators_cc.append(blocks_rotator_cc) + + ################################################## + # Connections + ################################################## + self.msg_connect((self.gsm_clock_offset_control, 'ctrl'), (gsm_input, 'ctrl_in')) + self.connect((blocks_rotator_cc, 0), (resampler, 0)) + self.connect((gsm_input, 0), (self.gsm_receiver, i)) + self.connect((gsm_input, 0), (self, i)) + self.connect((self, 0), (blocks_rotator_cc, 0)) + self.connect((resampler, 0), (gsm_input, 0)) + + self.msg_connect((self.gsm_receiver, 'measurements'), (self.gsm_clock_offset_control, 'measurements')) + self.msg_connect((self.gsm_receiver, 'C0'), (self, 'out_c0')) + self.msg_connect((self.gsm_receiver, 'CX'), (self, 'out_cx')) + + +class sdcch8_decoder_hopping(gr.top_block): + def __init__(self, arfcns_list=[], ccch_index=2, hsn=5, maio=0, osr=4, ts=1, tseq=0, reclen=5, ppm=0, args=""): + gr.top_block.__init__(self, "Multy arfncs sdcch8 decoder") + + ################################################## + # Parameters + ################################################## + self.arfcns_list = arfcns_list + self.ccch_index = ccch_index + self.hsn = hsn + self.maio = maio + self.osr = osr + self.ts = ts + self.tseq = tseq + + ################################################## + # Variables + ################################################## + self.arfcns = arfcns = list(arfcns_list) + self.fcs = fcs = list(map(arfcn.arfcn2downlink, arfcns)) + self.wide_samp_rate = wide_samp_rate = max(1e6, int(math.ceil((max(fcs) - min(fcs)) * 1.1 / 1e6)) * 1e6) + self.center_freq = center_freq = ((max(fcs) + min(fcs)) / 2) + snippets_main_after_init(self) + ################################################## + # Blocks + ################################################## + self.osmosdr_source_0 = osmosdr.source(args="numchan=" + str(1) + " " + + str(gsm.device.get_default_args(args))) + + self.osmosdr_source_0.set_sample_rate(wide_samp_rate) + self.osmosdr_source_0.set_center_freq(center_freq, 0) + self.osmosdr_source_0.set_freq_corr(ppm, 0) + + self.osmosdr_source_0.set_dc_offset_mode(2, 0) + self.osmosdr_source_0.set_iq_balance_mode(0, 0) + self.osmosdr_source_0.set_gain_mode(True, 0) + self.osmosdr_source_0.set_bandwidth(0, 0) + self.osmosdr_source_0.set_gain(14, 0) + self.osmosdr_source_0.set_if_gain(40, 0) + self.osmosdr_source_0.set_bb_gain(32, 0) + self.gsm_sdcch8_demapper_0 = gsm.gsm_sdcch8_demapper( + timeslot_nr=ts, + ) + self.gsm_multiarfcns_receiver_0 = multichannel_receiver( + arfcns=[arfcns[ccch_index]] + arfcns[0:ccch_index] + arfcns[(ccch_index + 1):len(arfcns)], + center_freq=center_freq, + osr=osr, + tseq=tseq, + wide_samp_rate=wide_samp_rate, + ) + + self.gsm_extract_cmc_0 = gsm.extract_cmc() + self.gsm_cx_channel_hopper_0 = gsm.cx_channel_hopper(arfcns, maio, hsn) + self.gsm_control_channels_decoder_0 = gsm.control_channels_decoder() + self.blocks_head_0 = blocks.head(gr.sizeof_gr_complex * 1, int(wide_samp_rate * reclen)) + + self.blocks_socket_pdu_0 = network.socket_pdu('UDP_CLIENT', '127.0.0.1', '4729', 10000, False) + + ################################################## + # Connections + ################################################## + self.msg_connect((self.gsm_control_channels_decoder_0, 'msgs'), (self.blocks_socket_pdu_0, 'pdus')) + self.msg_connect((self.gsm_control_channels_decoder_0, 'msgs'), (self.gsm_extract_cmc_0, 'msgs')) + self.msg_connect((self.gsm_cx_channel_hopper_0, 'bursts'), (self.gsm_sdcch8_demapper_0, 'bursts')) + self.msg_connect((self.gsm_multiarfcns_receiver_0, 'out_c0'), (self.gsm_cx_channel_hopper_0, 'CX')) + self.msg_connect((self.gsm_multiarfcns_receiver_0, 'out_cx'), (self.gsm_cx_channel_hopper_0, 'CX')) + self.msg_connect((self.gsm_sdcch8_demapper_0, 'bursts'), (self.gsm_control_channels_decoder_0, 'bursts')) + self.connect((self.blocks_head_0, 0), (self.gsm_multiarfcns_receiver_0, 0)) + self.connect((self.osmosdr_source_0, 0), (self.blocks_head_0, 0)) + + def get_arfcns_list(self): + return self.arfcns_list + + def set_arfcns_list(self, arfcns_list): + self.arfcns_list = arfcns_list + + def get_ccch_index(self): + return self.ccch_index + + def set_ccch_index(self, ccch_index): + self.ccch_index = ccch_index + + def get_hsn(self): + return self.hsn + + def set_hsn(self, hsn): + self.hsn = hsn + + def get_maio(self): + return self.maio + + def set_maio(self, maio): + self.maio = maio + + def get_osr(self): + return self.osr + + def set_osr(self, osr): + self.osr = osr + self.gsm_multiarfcns_receiver_0.set_osr(self.osr) + + def get_ts(self): + return self.ts + + def set_ts(self, ts): + self.ts = ts + self.gsm_sdcch8_demapper_0.set_timeslot_nr(self.ts) + + def get_tseq(self): + return self.tseq + + def set_tseq(self, tseq): + self.tseq = tseq + self.gsm_multiarfcns_receiver_0.set_tseq(self.tseq) + + def get_arfcns(self): + return self.arfcns + + def set_arfcns(self, arfcns): + self.arfcns = arfcns + self.set_fcs(list(map(arfcn.arfcn2downlink, self.arfcns))) + + def get_fcs(self): + return self.fcs + + def set_fcs(self, fcs): + self.fcs = fcs + self.set_center_freq(((max(self.fcs) + min(self.fcs)) / 2)) + self.set_wide_samp_rate(max(1e6, int(math.ceil((max(self.fcs) - min(self.fcs)) * 1.1 / 1e6)) * 1e6)) + + def get_wide_samp_rate(self): + return self.wide_samp_rate + + def set_wide_samp_rate(self, wide_samp_rate): + self.wide_samp_rate = wide_samp_rate + self.blocks_head_0.set_length(int(self.wide_samp_rate * 30)) + self.gsm_multiarfcns_receiver_0.set_wide_samp_rate(self.wide_samp_rate) + self.osmosdr_source_0.set_sample_rate(self.wide_samp_rate) + + def get_center_freq(self): + return self.center_freq + + def set_center_freq(self, center_freq): + self.center_freq = center_freq + self.gsm_multiarfcns_receiver_0.set_center_freq(self.center_freq) + self.osmosdr_source_0.set_center_freq(self.center_freq, 0) + + +def snipfcn_check_bandwidth(self): + if (self.wide_samp_rate > 20e6): # for hackrf + print("Very wide bandwidth for this arfcns: " + str(self.wide_samp_rate / 1e6) + " MHz") + self.stop() + else: + print("Wide bandwidth: " + str(self.wide_samp_rate / 1e6) + " MHz") + + +def snippets_main_after_init(tb): + snipfcn_check_bandwidth(tb) + + +# from wideband_receiver import * + +class receiver_with_decoder(gr.hier_block2): + def __init__(self, OSR=4, chan_num=0, fc=939.4e6, ppm=0, samp_rate=0.2e6): + gr.hier_block2.__init__( + self, "Receiver With Decoder", + gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), + gr.io_signature(0, 0, 0), + ) + self.message_port_register_hier_out("bursts") + self.message_port_register_hier_out("msgs") + + ################################################## + # Parameters + ################################################## + self.OSR = OSR + self.chan_num = chan_num + self.fc = fc + self.ppm = ppm + self.samp_rate = samp_rate + + ################################################## + # Variables + ################################################## + self.samp_rate_out = samp_rate_out = 1625000.0 / 6.0 * OSR + + ################################################## + # Blocks + ################################################## + self.gsm_receiver_0 = gsm.receiver(OSR, ([chan_num]), ([])) + self.gsm_input_0 = gsm.gsm_input( + ppm=ppm, + osr=OSR, + fc=fc, + samp_rate_in=samp_rate, + ) + self.gsm_control_channels_decoder_0 = gsm.control_channels_decoder() + self.gsm_clock_offset_control_0 = gsm.clock_offset_control(fc, samp_rate, osr=4) + self.gsm_bcch_ccch_demapper_0 = gsm.gsm_bcch_ccch_demapper(0) + + ################################################## + # Connections + ################################################## + self.msg_connect(self.gsm_bcch_ccch_demapper_0, 'bursts', self, 'bursts') + self.msg_connect(self.gsm_bcch_ccch_demapper_0, 'bursts', self.gsm_control_channels_decoder_0, 'bursts') + self.msg_connect(self.gsm_clock_offset_control_0, 'ctrl', self.gsm_input_0, 'ctrl_in') + self.msg_connect(self.gsm_control_channels_decoder_0, 'msgs', self, 'msgs') + self.msg_connect(self.gsm_receiver_0, 'C0', self.gsm_bcch_ccch_demapper_0, 'bursts') + self.msg_connect(self.gsm_receiver_0, 'measurements', self.gsm_clock_offset_control_0, 'measurements') + self.connect((self.gsm_input_0, 0), (self.gsm_receiver_0, 0)) + self.connect((self, 0), (self.gsm_input_0, 0)) + + def get_OSR(self): + return self.OSR + + def set_OSR(self, OSR): + self.OSR = OSR + self.set_samp_rate_out(1625000.0 / 6.0 * self.OSR) + self.gsm_input_0.set_osr(self.OSR) + + def get_chan_num(self): + return self.chan_num + + def set_chan_num(self, chan_num): + self.chan_num = chan_num + + def get_fc(self): + return self.fc + + def set_fc(self, fc): + self.fc = fc + self.gsm_input_0.set_fc(self.fc) + + def get_ppm(self): + return self.ppm + + def set_ppm(self, ppm): + self.ppm = ppm + self.gsm_input_0.set_ppm(self.ppm) + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.gsm_input_0.set_samp_rate_in(self.samp_rate) + + def get_samp_rate_out(self): + return self.samp_rate_out + + def set_samp_rate_out(self, samp_rate_out): + self.samp_rate_out = samp_rate_out + + +class wideband_receiver(gr.hier_block2): + def __init__(self, OSR=4, fc=939.4e6, samp_rate=0.4e6): + gr.hier_block2.__init__( + self, "Wideband receiver", + gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), + gr.io_signature(0, 0, 0), + ) + self.message_port_register_hier_out("bursts") + self.message_port_register_hier_out("msgs") + self.__init(OSR, fc, samp_rate) + + def __init(self, OSR=4, fc=939.4e6, samp_rate=0.4e6): + ################################################## + # Parameters + ################################################## + self.OSR = OSR + self.fc = fc + self.samp_rate = samp_rate + self.channels_num = int(samp_rate / 0.2e6) + self.OSR_PFB = 2 + + ################################################## + # Blocks + ################################################## + self.pfb_channelizer_ccf_0 = pfb.channelizer_ccf( + self.channels_num, + (), + self.OSR_PFB, + 100) + self.pfb_channelizer_ccf_0.set_channel_map(([])) + self.create_receivers() + + ################################################## + # Connections + ################################################## + self.connect((self, 0), (self.pfb_channelizer_ccf_0, 0)) + for chan in range(0, self.channels_num): + self.connect((self.pfb_channelizer_ccf_0, chan), (self.receivers_with_decoders[chan], 0)) + self.msg_connect(self.receivers_with_decoders[chan], 'bursts', self, 'bursts') + self.msg_connect(self.receivers_with_decoders[chan], 'msgs', self, 'msgs') + + def create_receivers(self): + self.receivers_with_decoders = {} + for chan in range(0, self.channels_num): + self.receivers_with_decoders[chan] = receiver_with_decoder(fc=self.fc, OSR=self.OSR, chan_num=chan, + samp_rate=self.OSR_PFB * 0.2e6) + + def get_OSR(self): + return self.OSR + + def set_OSR(self, OSR): + self.OSR = OSR + self.create_receivers() + + def get_fc(self): + return self.fc + + def set_fc(self, fc): + self.fc = fc + self.create_receivers() + + def get_samp_rate(self): + return self.samp_rate + + +class wideband_scanner(gr.top_block): + def __init__(self, rec_len=3, sample_rate=2e6, carrier_frequency=939e6, gain=40, ppm=0, args=""): + gr.top_block.__init__(self, "Wideband Scanner") + + self.rec_len = rec_len + self.sample_rate = sample_rate + self.carrier_frequency = carrier_frequency + self.ppm = ppm + + # if no file name is given process data from rtl_sdr source + print("Args=", args) + self.rtlsdr_source = osmosdr.source(args="numchan=" + str(1) + " " + + str(gsm.device.get_default_args(args))) + # self.rtlsdr_source.set_min_output_buffer(int(sample_rate*rec_len)) #this line causes segfaults on HackRF + self.rtlsdr_source.set_sample_rate(sample_rate) + + # capture half of GSM channel lower than channel center (-0.1MHz) + # this is needed when even number of channels is captured in order to process full captured bandwidth + + self.rtlsdr_source.set_center_freq(carrier_frequency - 0.1e6, 0) + + # correction of central frequency + # if the receiver has large frequency offset + # the value of this variable should be set close to that offset in ppm + self.rtlsdr_source.set_freq_corr(ppm, 0) + + self.rtlsdr_source.set_dc_offset_mode(2, 0) + self.rtlsdr_source.set_iq_balance_mode(0, 0) + self.rtlsdr_source.set_gain_mode(True, 0) + self.rtlsdr_source.set_bandwidth(sample_rate, 0) + self.rtlsdr_source.set_gain(gain, 0) + self.rtlsdr_source.set_if_gain(40, 0) + self.rtlsdr_source.set_bb_gain(32, 0) + self.head = blocks.head(gr.sizeof_gr_complex * 1, int(rec_len * sample_rate)) + + # shift again by -0.1MHz in order to align channel center in 0Hz + self.blocks_rotator_cc = blocks.rotator_cc(-2 * pi * 0.1e6 / sample_rate) + + self.wideband_receiver = wideband_receiver(OSR=4, fc=carrier_frequency, samp_rate=sample_rate) + self.extract_immediate_assignment = gsm.extract_immediate_assignment(False, True, True) + self.gsm_extract_system_info = gsm.extract_system_info() + + self.connect((self.rtlsdr_source, 0), (self.head, 0)) + self.connect((self.head, 0), (self.blocks_rotator_cc, 0)) + self.connect((self.blocks_rotator_cc, 0), (self.wideband_receiver, 0)) + self.msg_connect(self.wideband_receiver, 'msgs', self.gsm_extract_system_info, 'msgs') + self.msg_connect(self.wideband_receiver, 'msgs', self.extract_immediate_assignment, 'msgs') + + def set_carrier_frequency(self, carrier_frequency): + self.carrier_frequency = carrier_frequency + self.rtlsdr_source.set_center_freq(carrier_frequency - 0.1e6, 0) + + +class assignment_info(object): + def __init__(self, arfcn, channel, timeslot, tseq, maio, hsn, a5_vers=[]): + self.arfcn = arfcn + self.assignment_channel = channel + self.timeslot = timeslot + self.tseq = tseq + self.maio = maio + self.hsn = hsn + self.a5_vers = a5_vers + + def __str__(self): + return str(self.assignment_channel) + ", Timeslot: " + str(self.timeslot) + ", Training Sequence: " + str( + self.tseq) + ", MAIO: " + str(self.maio) + ", HSN: " + str(self.hsn) + ", A5/1 Version: " + ", ".join( + map(str, list(set(self.a5_vers)))) + + +class channel_info(object): + def __init__(self, arfcn, freq, cid, lac, mcc, mnc, ccch_conf, power, neighbours, cell_arfcns, assignments): + self.arfcn = arfcn + self.freq = freq + self.cid = cid + self.lac = lac + self.mcc = mcc + self.mnc = mnc + self.ccch_conf = ccch_conf + self.power = power + self.neighbours = neighbours + self.cell_arfcns = cell_arfcns + self.assignments = assignments + + def __lt__(self, other): + return self.arfcn < other.arfcn + + def get_verbose_info(self): + v = " |---- Configuration: %s\n" % self.get_ccch_conf() + v += " |---- Cell ARFCNs: " + ", ".join(map(str, self.cell_arfcns)) + "\n" + if len(self.assignments) > 0: + v += " |---- DCCHs:\n" + for i in range(0, len(self.assignments)): + v += " |-------- " + "#" + str(i + 1) + " " + str(self.assignments[i]) + "\n" + + v += " |---- Neighbour Cells: " + ", ".join(map(str, self.neighbours)) + "\n" + + return v + + def get_ccch_conf(self): + if self.ccch_conf == 0: + return "1 CCCH, not combined" + elif self.ccch_conf == 1: + return "1 CCCH, combined" + elif self.ccch_conf == 2: + return "2 CCCH, not combined" + elif self.ccch_conf == 4: + return "3 CCCH, not combined" + elif self.ccch_conf == 6: + return "4 CCCH, not combined" + else: + return "Unknown" + + def getKey(self): + return self.arfcn + + + @Cellslogger + def attr2dic(self): + cell = {} + cid2 = str(self.cid) + '_' + str(self.arfcn) + cell[cid2] = {'PLMN' : str(self.mcc)+str(self.mnc), + 'arfcn' : int(self.arfcn), + 'type' : '2G', + 'cid' : str(self.cid)} + return cell + + + def __cmp__(self, other): + if hasattr(other, 'getKey'): + return self.getKey().__cmp__(other.getKey()) + + def __repr__(self): + return "%s(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % ( + self.__class__, self.arfcn, self.freq, self.cid, self.lac, + self.mcc, self.mnc, self.ccch_conf, self.power, + self.neighbours, self.cell_arfcns) + + def __str__(self): + return "ARFCN: %4u, Freq: %6.1fM, CID: %5u, LAC: %5u, MCC: %3u, MNC: %3u, Pwr: %3i" % ( + self.arfcn, self.freq / 1e6, self.cid, self.lac, self.mcc, self.mnc, self.power) + + +def do_scan(samp_rate, band, speed, ppm, gain, args, prn=None, debug=False): + signallist = [] + server = network.socket_pdu('UDP_SERVER', '127.0.0.1', '4729', 10000, False) + channels_num = int(samp_rate / 0.2e6) + for arfcn_range in gsm.arfcn.get_arfcn_ranges(band): + first_arfcn = arfcn_range[0] + last_arfcn = arfcn_range[1] + print("Try scan CCCH on " + str(first_arfcn) + "-" + str(last_arfcn) + " arfcn`s:") + last_center_arfcn = last_arfcn - int((channels_num / 2) - 1) + + start_freq = gsm.arfcn.arfcn2downlink(first_arfcn + int(channels_num / 2) - 1) + current_freq = start_freq + last_freq = gsm.arfcn.arfcn2downlink(last_center_arfcn) + stop_freq = last_freq + 0.2e6 * channels_num + + while current_freq < stop_freq: + print("Scanning: {percent:.2f}% done..".format( + percent=(current_freq - start_freq) / (stop_freq - start_freq) * 100)) + if not debug: + # silence rtl_sdr output: + # open 2 fds + null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)] + # save the current file descriptors to a tuple + save = os.dup(1), os.dup(2) + # put /dev/null fds on 1 and 2 + os.dup2(null_fds[0], 1) + os.dup2(null_fds[1], 2) + # instantiate scanner and processor + scanner = wideband_scanner(rec_len=30 - speed, + sample_rate=samp_rate, + carrier_frequency=current_freq, + ppm=ppm, gain=gain, args=args) + # start recording + scanner.start() + scanner.wait() + scanner.stop() + if not debug: + # restore file descriptors so we can print the results + os.dup2(save[0], 1) + os.dup2(save[1], 2) + # close the temporary fds + os.close(null_fds[0]) + os.close(null_fds[1]) + + freq_offsets = numpy.fft.ifftshift( + numpy.array(range(int(-numpy.floor(channels_num / 2)), int(numpy.floor((channels_num + 1) / 2)))) * 2e5) + detected_c0_channels = scanner.gsm_extract_system_info.get_chans() + + found_list = [] + + if detected_c0_channels: + chans = numpy.array(scanner.gsm_extract_system_info.get_chans()) + found_freqs = current_freq + freq_offsets[(chans)] + + cell_ids = numpy.array(scanner.gsm_extract_system_info.get_cell_id()) + lacs = numpy.array(scanner.gsm_extract_system_info.get_lac()) + mccs = numpy.array(scanner.gsm_extract_system_info.get_mcc()) + mncs = numpy.array(scanner.gsm_extract_system_info.get_mnc()) + ccch_confs = numpy.array(scanner.gsm_extract_system_info.get_ccch_conf()) + powers = numpy.array(scanner.gsm_extract_system_info.get_pwrs()) + assignment_arfcns = numpy.array(scanner.extract_immediate_assignment.get_arfcn_ids()) + assignment_tseqs = numpy.array(scanner.extract_immediate_assignment.get_tseqs()) + assignment_maios = numpy.array(scanner.extract_immediate_assignment.get_maios()) + assignment_hsns = numpy.array(scanner.extract_immediate_assignment.get_hsns()) + assignment_timeslots = numpy.array(scanner.extract_immediate_assignment.get_timeslots()) + assignment_channels = numpy.array(scanner.extract_immediate_assignment.get_channel_types()) + cell_arfcn_lists = [] + neighbour_lists = [] + for i in range(0, len(chans)): + cell_arfcn_lists.append(scanner.gsm_extract_system_info.get_cell_arfcns(chans[i])) + neighbour_lists.append(scanner.gsm_extract_system_info.get_neighbours(chans[i])) + scanner = None + gc.collect() + for i in range(0, len(chans)): + cell_arfcn_list = cell_arfcn_lists[i] + neighbour_list = neighbour_lists[i] + found_arfcn = gsm.arfcn.downlink2arfcn(found_freqs[i]) + print("\nFound CCCH arfcn: " + str(found_arfcn)) + found_arfcn_assignments = [] + + for a in range(0, len(assignment_arfcns)): + + # print("Assigments: " + str(assignment_arfcns[a]) + "<->" + str(chans[i])) + if assignment_arfcns[a] == chans[i]: + dubl = False + for f in found_arfcn_assignments: + if f.assignment_channel == assignment_channels[a] and \ + f.timeslot == assignment_timeslots[a] and \ + f.tseq == assignment_tseqs[a] and \ + f.maio == assignment_maios[a] and \ + f.hsn == assignment_hsns[a]: + dubl = True + break + if not dubl: + a5_versions = [] + if len(cell_arfcn_list) > 0: + fcs = list(map(arfcn.arfcn2downlink, cell_arfcn_list)) + wide_samp_rate = max(1e6, int(math.ceil((max(fcs) - min(fcs)) * 1.1 / 1e6)) * 1e6) + if wide_samp_rate > 20e6: + # print("Very wide bandwidth for this hopping SDCCH/8 arfcns: " + str(wide_samp_rate/1e6) + " MHz, skip SDCCH/8 scan...") + a5_versions = [ + "?, arfcn`s range too wide: " + str(wide_samp_rate / 1e6) + " MHz"] + else: + try: + # print("Wide bandwidth: " + str(wide_samp_rate/1e6) + " MHz") + # print(", ".join(map(str, cell_arfcn_list))) + # print(found_arfcn) + ccch_index = tuple(cell_arfcn_list).index(found_arfcn) + print("Start SDCCH/8 scanning on " + str(found_arfcn) + " arfcn...") + for t in range(0, 6): + print(str(t + 1) + "`th try..") + if not debug: + # silence rtl_sdr output: + # open 2 fds + null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)] + # save the current file descriptors to a tuple + save = os.dup(1), os.dup(2) + # put /dev/null fds on 1 and 2 + os.dup2(null_fds[0], 1) + os.dup2(null_fds[1], 2) + sdcch8_scanner = sdcch8_decoder_hopping(cell_arfcn_list, ccch_index, + assignment_hsns[a], + assignment_maios[a], 4, + assignment_timeslots[a], + assignment_tseqs[a], 30, ppm, + args) + if not debug: + os.dup2(save[0], 1) + os.dup2(save[1], 2) + # close the temporary fds + os.close(null_fds[0]) + os.close(null_fds[1]) + sdcch8_scanner.start() + sdcch8_scanner.wait() + sdcch8_scanner.stop() + a5_versions = sdcch8_scanner.gsm_extract_cmc_0.get_a5_versions() + if len(a5_versions) == 0: + a5_versions = ["?, not found CMC"] + else: + break + # print(", ".join(map(str, a5_versions))) + sdcch8_scanner = None + gc.collect() + except ValueError: + # print("Found arfcn not present in arfcns hopping list, skip SDCCH/8 scan...") + a5_versions = [ + "?, this CCCH is not present in hopping list | possible signal imposition"] + else: + print( + "!! Immidiate assigment got, but SDCCH/8 Channel Description not capture, skip SDCCH/8 scan...") + a5_versions = ["?, SDCCH/8 channel description not capture"] + found_arfcn_assignments.append(assignment_info(found_arfcn, assignment_channels[a], + assignment_timeslots[a], + assignment_tseqs[a], + assignment_maios[a], assignment_hsns[a], + a5_versions)) + if len(found_arfcn_assignments) == 0: + print("Dont capture immediate assignments, skip extract SDCCH/8 info and scan...") + info = channel_info(found_arfcn, found_freqs[i], + cell_ids[i], lacs[i], mccs[i], mncs[i], ccch_confs[i], powers[i], + neighbour_list, cell_arfcn_list, found_arfcn_assignments) + + print(info) + print(info.get_verbose_info()) + + found_list.append(info) + + scanner = None + + if prn: + prn(found_list) + signallist.extend(found_list) + + current_freq += channels_num * 0.2e6 + server = None + return signallist + + +def argument_parser(): + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + bands_list = ", ".join(gsm.arfcn.get_bands()) + parser.add_option("-b", "--band", dest="band", default="GSM900", + help="Specify the GSM band for the frequency.\nAvailable bands are: " + bands_list) + parser.add_option("-s", "--samp-rate", dest="samp_rate", type="float", default=2e6, + help="Set sample rate [default=%default] - allowed values even_number*0.2e6") + parser.add_option("-p", "--ppm", dest="ppm", type="intx", default=0, + help="Set frequency correction in ppm [default=%default]") + parser.add_option("-g", "--gain", dest="gain", type="eng_float", default=24.0, + help="Set gain [default=%default]") + parser.add_option("", "--args", dest="args", type="string", default="", + help="Set device arguments [default=%default]." + " Use --list-devices the view the available devices") + parser.add_option("-l", "--list-devices", action="store_true", + help="List available SDR devices, use --args to specify hints") + parser.add_option("--speed", dest="speed", type="intx", default=15, + help="Scan speed [default=%default]. Value range 0-29.") + parser.add_option("-v", "--verbose", action="store_true", + help="If set, verbose information output is printed: ccch configuration, cell ARFCN's, neighbour ARFCN's") + parser.add_option("-d", "--debug", action="store_true", + help="Print additional debug messages") + + """ + Dont forget: sudo sysctl kernel.shmmni=32000 + """ + return parser + + +def main(options=None): + if options is None: + (options, args) = argument_parser().parse_args() + + if options.list_devices: + gsm.device.print_devices(options.args) + sys.exit(0) + + if options.band not in gsm.arfcn.get_bands(): + parser.error("Invalid GSM band\n") + + if options.speed < 0 or options.speed > 29: + parser.error("Invalid scan speed.\n") + + if (options.samp_rate / 0.2e6) % 2 != 0: + parser.error("Invalid sample rate. Sample rate must be an even numer * 0.2e6") + + # def printfunc(found_list): + # for info in sorted(found_list): + # print(info) + # if options.verbose: + # print(info.get_verbose_info()) + print("") + do_scan(options.samp_rate, options.band, options.speed, + options.ppm, options.gain, options.args, prn=None, debug=options.debug) + + +if __name__ == '__main__': + main() diff --git a/engines/sdr/srslte_pss.py b/engines/sdr/srslte_pss.py new file mode 100644 index 0000000..c298ade --- /dev/null +++ b/engines/sdr/srslte_pss.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# ---------------------------------------------------------------------------- +# "THE BEER-WARE LICENSE" (Revision 42): +# )penthertz.com> wrote this file. As long as you retain this notice you +# can do whatever you want with this stuff. If we meet some day, and you think +# this stuff is worth it, you can buy me a beer in return FlUxIuS ;) +# +# We'd like to thank our contributors for maintening this code: +# - @h0rac (Grzegorz Wypych) from PWNSec.pl +# ---------------------------------------------------------------------------- + +from __future__ import print_function +from core.mLog import Cellslogger +from core.mKB import * +import os + +class srslte_pss(object): + @Cellslogger + def go2logs(self, cell): + return cell + + def parseFifo(self): + kb = mKB() + FIFO = kb.config['file'] + if os.path.isfile(FIFO) == False: + try: + os.mkfifo(FIFO) + except: + pass + if 'SM_cells' not in kb.data: + kb.data['SM_cells'] = {} + while True: + with open(FIFO) as fifo: + while True: + data = fifo.read() + if len(data) == 0: + break + infos = data.split(':')[1] + isplit = infos.split(';') + tmpcell = {} + tmpcell2 = {} + for cell in isplit: + pcell = cell.split('=') + tmpcell[pcell[0]] = pcell[1] + cid = tmpcell['CID'] + '-' + tmpcell['DL_EARFCN'] + tmpcell2[cid] = { 'FREQ' : tmpcell['FREQ'], + 'PLMN' : "-1", + 'type' : "4G", + 'band' : tmpcell['BAND'], + 'eARFCN' : int(tmpcell['DL_EARFCN']), + 'POWER' : tmpcell['POWER'].split("\n")[0], + } + self.go2logs(tmpcell2) + + diff --git a/install_all-Ubuntu_24.04.sh b/install_all-Ubuntu_24.04.sh new file mode 100755 index 0000000..5ed4a67 --- /dev/null +++ b/install_all-Ubuntu_24.04.sh @@ -0,0 +1,104 @@ +#!/usr/bin/bash + +echo "[+] Retrieving submodules" +git submodule update --init --recursive + +echo "[+] Installing Python3 requirements" +sudo apt install -y python3-pip + +echo "[+] Installing GNU Radio dependencies" +install_antsdr_uhd() { + sudo apt-get install autoconf automake build-essential ccache cmake cpufrequtils doxygen ethtool \ + g++ git inetutils-tools libboost-all-dev libncurses5 libncurses5-dev libusb-1.0-0 libusb-1.0-0-dev \ + libusb-dev python3-dev python3-mako python3-numpy python3-requests python3-scipy python3-setuptools \ + python3-ruamel.yaml + git clone https://github.com/MicroPhase/antsdr_uhd.git + cd antsdr_uhd + cd host/ + mkdir build + cd build + cmake ../ + make -j$(nproc) + sudo make install + sudo ldconifg + cd ../../ +} + +sudo apt-get install gnuradio gnuradio-dev -y + +sudo apt install -y git cmake g++ libboost-all-dev libgmp-dev swig python3-numpy \ +python3-mako python3-sphinx python3-lxml doxygen libfftw3-dev \ +libsdl1.2-dev libgsl-dev libqwt-qt5-dev libqt5opengl5-dev python3-pyqt5 \ +liblog4cpp5-dev libzmq3-dev python3-yaml python3-click python3-click-plugins \ +python3-zmq python3-scipy python3-gi python3-gi-cairo gobject-introspection gir1.2-gtk-3.0 \ +libsndfile-dev libtalloc-dev libpcsclite-dev wget unzip python3-pip libbladerf-dev \ +libuhd-dev uhd-host librtlsdr-dev + +while true; do + read -p "Do you which to install the UHD version of ANTSDR (YES), or the original one [NO]? " yn + case $yn in + [Yy]* ) install_antsdr_uhd; break;; + [Nn]* ) sudo apt install libuhd-dev uhd-host librtlsdr-dev; break;; + * ) echo "Please answer [Y]yes or [N]no.";; + esac +done + +sudo pip3 install pyserial --break-system-packages + +echo "[+] Installing osmocore" +sudo apt -y install build-essential libosmocore libosmocore-dev libtool libtalloc-dev libsctp-dev shtool autoconf automake git-core pkg-config make gcc gnutls-dev libusb-1.0-0-dev libmnl-dev +cd thirdparty +REMPATH=`pwd` +#sudo apt install gr-osmosdr +git clone https://github.com/PentHertz/gr-osmosdr.git +cd gr-osmosdr +mkdir build +cd build +cmake -DCMAKE_INSTALL_PREFIX=/usr/local ../ +make -j$(nproc) +sudo make install +cd $REMPATH +echo "[+] Downloading gr-gsm for Python3 and GNU Radio 3.10" +git clone https://github.com/FlUxIuS/gr-gsm.git +echo "[+] Building and installing gr-gsm for GNU Radio 3.10" +cd gr-gsm +mkdir build +cd build +cmake -DCMAKE_INSTALL_PREFIX=/usr/local ../ +make -j$(nproc) +sudo make install +cd ../../ +echo "[+] Installing SDK tools" +sudo apt install -y openjdk-25-jdk +wget https://dl.google.com/android/repository/platform-tools-latest-linux.zip +unzip platform-tools-latest-linux.zip +sudo mkdir -p /opt/Android +sudo cp -R platform-tools +echo "[+] Installing dependencies for srsLTE" +sudo apt-get install cmake libfftw3-dev libmbedtls-dev libboost-program-options-dev libconfig++-dev libsctp-dev +sudo apt install libsoapysdr-dev +#osmo sdr support: +sudo apt-get install osmo-sdr soapysdr-module-osmosdr +#rtl sdr support: +sudo apt-get install rtl-sdr soapysdr-module-rtlsdr +#blade rf support: +sudo apt-get install bladerf soapysdr-module-bladerf +#hack rf support: +sudo apt-get install hackrf soapysdr-module-hackrf +#usrp support: +sudo apt-get install uhd-soapysdr soapysdr-module-uhd +#miri SDR support: +sudo apt-get install miri-sdr soapysdr-module-mirisdr +#rf space support: +sudo apt-get install soapysdr-module-rfspace +#airspy support: +sudo apt-get install airspy soapysdr-module-airspy +echo "[+] Installaing srsLTE for Modmobmap" +cd $REMPATH +ls +cd srsLTE +mkdir build +cd build +cmake ../ +make -j$(nproc) +echo "====END====" diff --git a/modmobmap.py b/modmobmap.py index 5136de4..91dcb9f 100755 --- a/modmobmap.py +++ b/modmobmap.py @@ -3,7 +3,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- @@ -11,36 +11,22 @@ from __future__ import print_function from utils.logprocess import * +SRSLTE_PATH = "thirdparty/srsLTE/" # thirdparty project -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Mobile network mapping tool with cheap equipments') - parser.add_argument('-m', '--module', dest='module', required=False, default='servicemode', - help='Module to use (e.g: "servicemode" by default)') - parser.add_argument('-n', '--networks', dest='networks', required=False, default=None, - help='Networks in MCCMNC format splitted with commas') - parser.add_argument('-o', '--cached_operator', dest='operators', required=False, default=False, action='store_true', - help='Use operator in cache to speed up the passive scan') - parser.add_argument('-s', '--sdk', dest='androidsdk', required=False, default='/opt/Android', - help='Android SDK path') - parser.add_argument('-a', '--at', dest='atmode', required=False, default=None, - help='AT access mode. If host put something like "/dev/ttyUSBxx. By default it uses ADB."') - parser.add_argument('-f', '--file', dest='file', required=False, default=None, - help='File to parse. For the moment it could be used in combination with AT mode host.') - args = parser.parse_args() - sm = ADBshell() - kb = mKB() - kb.config['androidsdk'] = args.androidsdk - sm.androidsdkpath = args.androidsdk - if args.file is not None: - kb.config['file'] = args.file - if args.module == "xgoldmod": - startXgoldmodCollect() - else: - startServiceModeCollect() + +FILES_TO_REMOVE = ["celllog.fifo"] + +def cleaning_file(): + import os + for f in FILES_TO_REMOVE: + os.remove(f) + + +def phone_actions(args): cops = None if args.networks is not None: printInfo('=> Manual MCC/MNC processing...') - cops = processManualMCCMN(args.networks) + cops = processManualMCCMN(args.networks) else: printInfo('=> Requesting a list of MCC/MNC. Please wait, it may take a while...') operators = load_operators() @@ -49,6 +35,8 @@ cops = operators if cops is None: if args.atmode is None: + sm = ADBshell() + sm.androidsdkpath = args.androidsdk cops = sm.getCOPSfromRIL() else: at = AT(args.atmode) @@ -65,3 +53,55 @@ else: kb.config['tty_file'] = args.atmode processOperatorAT(operators) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Mobile network mapping tool with cheap equipments') + parser.add_argument('-m', '--module', dest='module', required=False, default='servicemode', + help='Module to use (e.g: "servicemode" by default).') + parser.add_argument('-b', '--bands', dest='bands', required=False, default=None, + help='Bands to use for SDR engines (for GSM: GSM900, DCS1800, GSM850, PCS1900, GSM450, GSM480, GSM-R. For LTE provide band indexes such as 28 for B28 at 700 MHz, etc.). A list can be provided separated with commas.') + parser.add_argument('-n', '--networks', dest='networks', required=False, default=None, + help='Networks in MCCMNC format splitted with commas') + parser.add_argument('-o', '--cached_operator', dest='operators', required=False, default=False, action='store_true', + help='Use operator in cache to speed up the passive scan.') + parser.add_argument('-s', '--sdk', dest='androidsdk', required=False, default='thirdparty/platform-tools', + help='Android SDK path') + parser.add_argument('-a', '--at', dest='atmode', required=False, default=None, + help='AT access mode. If host put something like "/dev/ttyUSBxx. By default it uses ADB."') + parser.add_argument('-g', '--args', dest='dargs', required=False, default=None, + help='Device name arg for SDR engines (e.g: soapy:id=0))') + parser.add_argument('-f', '--file', dest='file', required=False, default=None, + help='File to parse. For the moment it could be used in combination with AT mode host.') + args = parser.parse_args() + + phoneinteract = False + + kb = mKB() + kb.config['androidsdk'] = args.androidsdk + kb.config['device_args'] = args.dargs + if args.file is not None: + kb.config['file'] = args.file + if args.module == "xgoldmod": + startXgoldmodCollect() + phoneinteract = True + elif args.module == "srslte_pss" or args.module == "srslte_npss": + kb.config['SRSLTETOOLS_PATH'] = SRSLTE_PATH + "build/lib/examples/" + kb.config['file'] = "celllog.fifo" + if args.file is not None: + kb.config['file'] = args.file + + if args.bands is not None: + kb.config['bands'] = args.bands + else: + print ("Bands argument not set! Using band 7 by default instead.") + kb.config['bands'] = "7" + startSrsLTEPSS() + elif args.module == "grgsm": + processGRGSM(args.bands) + else: + startServiceModeCollect() + phoneinteract = True + + if phoneinteract is True: + phone_actions(args) diff --git a/requirements.txt b/requirements.txt index b6be06a..f6c1a1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -serial +pyserial diff --git a/samples/cells_1537879258.json b/samples/cells_1537879258.json new file mode 100644 index 0000000..cc3873c --- /dev/null +++ b/samples/cells_1537879258.json @@ -0,0 +1,11 @@ +{ + "346-1501": { + "PCI": "346", + "PLMN": "208-10", + "TAC": "58104", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1501, + "type": "4G" + } +} \ No newline at end of file diff --git a/samples/cells_1537879363.json b/samples/cells_1537879363.json new file mode 100644 index 0000000..cc3873c --- /dev/null +++ b/samples/cells_1537879363.json @@ -0,0 +1,11 @@ +{ + "346-1501": { + "PCI": "346", + "PLMN": "208-10", + "TAC": "58104", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1501, + "type": "4G" + } +} \ No newline at end of file diff --git a/samples/cells_1537879796.json b/samples/cells_1537879796.json new file mode 100644 index 0000000..cc3873c --- /dev/null +++ b/samples/cells_1537879796.json @@ -0,0 +1,11 @@ +{ + "346-1501": { + "PCI": "346", + "PLMN": "208-10", + "TAC": "58104", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1501, + "type": "4G" + } +} \ No newline at end of file diff --git a/samples/cells_1537880475.json b/samples/cells_1537880475.json new file mode 100644 index 0000000..e85d316 --- /dev/null +++ b/samples/cells_1537880475.json @@ -0,0 +1,323 @@ +{ + "11ee-9": { + "PLMN": "208-01", + "arfcn": 9, + "cid": "11ee", + "type": "2G" + }, + "1882-10787": { + "PLMN": "208-01", + "RX": 10787, + "TX": 9837, + "band": 1, + "type": "3G" + }, + "1882b-10787": { + "PLMN": "208-01", + "RX": 10787, + "TX": 9837, + "band": 1, + "type": "3G" + }, + "1882b-10812": { + "PLMN": "208-01", + "RX": 10812, + "TX": 9862, + "band": 1, + "type": "3G" + }, + "188ec3-3011": { + "PLMN": "208-01", + "RX": 3011, + "TX": 2786, + "band": 8, + "type": "3G" + }, + "1ba-78": { + "PLMN": "208-10", + "arfcn": 78, + "cid": "1ba", + "type": "2G" + }, + "1ba8-78": { + "PLMN": "208-10", + "arfcn": 78, + "cid": "1ba8", + "type": "2G" + }, + "27-1019": { + "PLMN": "208-20", + "arfcn": 1019, + "cid": "27", + "type": "2G" + }, + "2ca4-10737": { + "PLMN": "208-15", + "RX": 10737, + "TX": 9787, + "band": 1, + "type": "3G" + }, + "346-1501": { + "PCI": "346", + "PLMN": "208-10", + "TAC": "58104", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1501, + "type": "4G" + }, + "346-6300": { + "PCI": "346", + "PLMN": "208-10", + "TAC": "0", + "band": 20, + "bandwidth": "20MHz_", + "eARFCN": 6300, + "type": "4G" + }, + "351-1300": { + "PCI": "351", + "PLMN": "208-01", + "TAC": "0", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1300, + "type": "4G" + }, + "351-1501": { + "PCI": "351", + "PLMN": "208-10", + "TAC": "0", + "band": 3, + "bandwidth": "10MHz_", + "eARFCN": 1501, + "type": "4G" + }, + "351-1850": { + "PCI": "351", + "PLMN": "208-20", + "TAC": "58104", + "band": 3, + "bandwidth": "15MHz_", + "eARFCN": 1850, + "type": "4G" + }, + "351-3350": { + "PCI": "351", + "PLMN": "208-01", + "TAC": "0", + "band": 7, + "bandwidth": "20MHz_", + "eARFCN": 3350, + "type": "4G" + }, + "353-1501": { + "PCI": "353", + "PLMN": "208-10", + "TAC": "58104", + "band": 20, + "bandwidth": "20MHz_", + "eARFCN": 1501, + "type": "4G" + }, + "353-1850": { + "PCI": "353", + "PLMN": "208-20", + "TAC": "58104", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1850, + "type": "4G" + }, + "353-3000": { + "PCI": "353", + "PLMN": "208-01", + "TAC": "0", + "band": 7, + "bandwidth": "20MHz_", + "eARFCN": 3000, + "type": "4G" + }, + "353-3350": { + "PCI": "353", + "PLMN": "208-10", + "TAC": "58104", + "band": 7, + "bandwidth": "20MHz_", + "eARFCN": 3350, + "type": "4G" + }, + "353-6300": { + "PCI": "353", + "PLMN": "208-10", + "TAC": "58104", + "band": 20, + "bandwidth": "10MHz_", + "eARFCN": 6300, + "type": "4G" + }, + "391-6300": { + "PCI": "391", + "PLMN": "208-10", + "TAC": "58104", + "band": 20, + "bandwidth": "10MHz_", + "eARFCN": 6300, + "type": "4G" + }, + "435-1675": { + "PCI": "435", + "PLMN": "208-15", + "TAC": "6603", + "band": 3, + "bandwidth": "15MHz_", + "eARFCN": 1675, + "type": "4G" + }, + "435-3350": { + "PCI": "435", + "PLMN": "208-15", + "TAC": "6603", + "band": 7, + "bandwidth": "20MHz_", + "eARFCN": 3350, + "type": "4G" + }, + "56-1850": { + "PCI": "56", + "PLMN": "208-20", + "TAC": "0", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1850, + "type": "4G" + }, + "5f28-8": { + "PLMN": "208-01", + "arfcn": 8, + "cid": "5f28", + "type": "2G" + }, + "65-6200": { + "PCI": "65", + "PLMN": "208-20", + "TAC": "0", + "band": 20, + "bandwidth": "10MHz_", + "eARFCN": 6200, + "type": "4G" + }, + "65-6300": { + "PCI": "65", + "PLMN": "208-20", + "TAC": "0", + "band": 20, + "bandwidth": "10MHz_", + "eARFCN": 6300, + "type": "4G" + }, + "8fc3-10664": { + "PLMN": "208-20", + "RX": 10664, + "TX": 9714, + "band": 1, + "type": "3G" + }, + "8fc30-10664": { + "PLMN": "208-20", + "RX": 10664, + "TX": 9714, + "band": 1, + "type": "3G" + }, + "8fc6e-3075": { + "PLMN": "208-10", + "RX": 3075, + "TX": 2850, + "band": 8, + "type": "3G" + }, + "8fc6e5-3075": { + "PLMN": "208-10", + "RX": 3075, + "TX": 2850, + "band": 8, + "type": "3G" + }, + "8fc7-10564": { + "PLMN": "208-10", + "RX": 10564, + "TX": 9614, + "band": 1, + "type": "3G" + }, + "8fc77-10564": { + "PLMN": "208-10", + "RX": 10564, + "TX": 9614, + "band": 1, + "type": "3G" + }, + "8fcb-10589": { + "PLMN": "208-10", + "RX": 10589, + "TX": 9639, + "band": 1, + "type": "3G" + }, + "8fcb2-10589": { + "PLMN": "208-10", + "RX": 10589, + "TX": 9639, + "band": 1, + "type": "3G" + }, + "fbc8-9": { + "PLMN": "208-01", + "arfcn": 9, + "cid": "fbc8", + "type": "2G" + }, + "fffff-10589": { + "PLMN": "208-10", + "RX": 10589, + "TX": 9639, + "band": 1, + "type": "3G" + }, + "fffff-10664": { + "PLMN": "208-20", + "RX": 10664, + "TX": 9714, + "band": 1, + "type": "3G" + }, + "fffff-10737": { + "PLMN": "208-15", + "RX": 10737, + "TX": 9787, + "band": 1, + "type": "3G" + }, + "fffff-10812": { + "PLMN": "208-01", + "RX": 10812, + "TX": 9862, + "band": 1, + "type": "3G" + }, + "ffffff-3075": { + "PLMN": "208-10", + "RX": 3075, + "TX": 2850, + "band": 8, + "type": "3G" + }, + "ffffff-78": { + "PLMN": "208-10", + "arfcn": 78, + "cid": "ffffff", + "type": "2G" + } +} \ No newline at end of file diff --git a/samples/cells_1537880525.json b/samples/cells_1537880525.json new file mode 100644 index 0000000..a9daac8 --- /dev/null +++ b/samples/cells_1537880525.json @@ -0,0 +1,248 @@ +{ + "11ee-9": { + "PLMN": "208-01", + "arfcn": 9, + "cid": "11ee", + "type": "2G" + }, + "1882-10787": { + "PLMN": "208-01", + "RX": 10787, + "TX": 9837, + "band": 1, + "type": "3G" + }, + "1882b-10787": { + "PLMN": "208-01", + "RX": 10787, + "TX": 9837, + "band": 1, + "type": "3G" + }, + "1882b-10812": { + "PLMN": "208-01", + "RX": 10812, + "TX": 9862, + "band": 1, + "type": "3G" + }, + "188ec3-3011": { + "PLMN": "208-01", + "RX": 3011, + "TX": 2786, + "band": 8, + "type": "3G" + }, + "1ba8-78": { + "PLMN": "208-10", + "arfcn": 78, + "cid": "1ba8", + "type": "2G" + }, + "27-1019": { + "PLMN": "208-20", + "arfcn": 1019, + "cid": "27", + "type": "2G" + }, + "2ca4-10737": { + "PLMN": "208-15", + "RX": 10737, + "TX": 9787, + "band": 1, + "type": "3G" + }, + "346-1501": { + "PCI": "346", + "PLMN": "208-10", + "TAC": "58104", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1501, + "type": "4G" + }, + "351-1300": { + "PCI": "351", + "PLMN": "208-01", + "TAC": "0", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1300, + "type": "4G" + }, + "351-1501": { + "PCI": "351", + "PLMN": "208-10", + "TAC": "0", + "band": 3, + "bandwidth": "10MHz_", + "eARFCN": 1501, + "type": "4G" + }, + "351-1850": { + "PCI": "351", + "PLMN": "208-20", + "TAC": "58104", + "band": 3, + "bandwidth": "15MHz_", + "eARFCN": 1850, + "type": "4G" + }, + "351-3350": { + "PCI": "351", + "PLMN": "208-01", + "TAC": "0", + "band": 7, + "bandwidth": "20MHz_", + "eARFCN": 3350, + "type": "4G" + }, + "353-1501": { + "PCI": "353", + "PLMN": "208-10", + "TAC": "58104", + "band": 20, + "bandwidth": "20MHz_", + "eARFCN": 1501, + "type": "4G" + }, + "353-1850": { + "PCI": "353", + "PLMN": "208-20", + "TAC": "58104", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1850, + "type": "4G" + }, + "353-3000": { + "PCI": "353", + "PLMN": "208-01", + "TAC": "0", + "band": 7, + "bandwidth": "20MHz_", + "eARFCN": 3000, + "type": "4G" + }, + "353-3350": { + "PCI": "353", + "PLMN": "208-10", + "TAC": "58104", + "band": 7, + "bandwidth": "20MHz_", + "eARFCN": 3350, + "type": "4G" + }, + "353-6300": { + "PCI": "353", + "PLMN": "208-10", + "TAC": "58104", + "band": 20, + "bandwidth": "10MHz_", + "eARFCN": 6300, + "type": "4G" + }, + "391-6300": { + "PCI": "391", + "PLMN": "208-10", + "TAC": "58104", + "band": 20, + "bandwidth": "10MHz_", + "eARFCN": 6300, + "type": "4G" + }, + "435-1675": { + "PCI": "435", + "PLMN": "208-15", + "TAC": "6603", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1675, + "type": "4G" + }, + "435-3350": { + "PCI": "435", + "PLMN": "208-15", + "TAC": "6603", + "band": 7, + "bandwidth": "20MHz_", + "eARFCN": 3350, + "type": "4G" + }, + "56-1850": { + "PCI": "56", + "PLMN": "208-20", + "TAC": "0", + "band": 3, + "bandwidth": "20MHz_", + "eARFCN": 1850, + "type": "4G" + }, + "5f28-8": { + "PLMN": "208-01", + "arfcn": 8, + "cid": "5f28", + "type": "2G" + }, + "8fc30-10664": { + "PLMN": "208-20", + "RX": 10664, + "TX": 9714, + "band": 1, + "type": "3G" + }, + "8fc6e-3075": { + "PLMN": "208-10", + "RX": 3075, + "TX": 2850, + "band": 8, + "type": "3G" + }, + "8fc6e5-3075": { + "PLMN": "208-10", + "RX": 3075, + "TX": 2850, + "band": 8, + "type": "3G" + }, + "fbc8-9": { + "PLMN": "208-01", + "arfcn": 9, + "cid": "fbc8", + "type": "2G" + }, + "fffff-10664": { + "PLMN": "208-20", + "RX": 10664, + "TX": 9714, + "band": 1, + "type": "3G" + }, + "fffff-10737": { + "PLMN": "208-15", + "RX": 10737, + "TX": 9787, + "band": 1, + "type": "3G" + }, + "fffff-10812": { + "PLMN": "208-01", + "RX": 10812, + "TX": 9862, + "band": 1, + "type": "3G" + }, + "ffffff-3075": { + "PLMN": "208-10", + "RX": 3075, + "TX": 2850, + "band": 8, + "type": "3G" + }, + "ffffff-78": { + "PLMN": "208-10", + "arfcn": 78, + "cid": "ffffff", + "type": "2G" + } +} \ No newline at end of file diff --git a/thirdparty/srsLTE b/thirdparty/srsLTE new file mode 160000 index 0000000..b5b7110 --- /dev/null +++ b/thirdparty/srsLTE @@ -0,0 +1 @@ +Subproject commit b5b7110d42d0be32398fe6ae9e627a69863887b4 diff --git a/utils/__init__.py b/utils/__init__.py index d13e961..8df667a 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -2,7 +2,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- diff --git a/utils/eu_arfcn_calc.py b/utils/eu_arfcn_calc.py index 157a721..4c79643 100755 --- a/utils/eu_arfcn_calc.py +++ b/utils/eu_arfcn_calc.py @@ -3,7 +3,7 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) # ---------------------------------------------------------------------------- @@ -23,7 +23,7 @@ 'DL_range' : (0,599), 'FUL_Low' : 1920, 'NUL_Offset' : 18000, - 'UP_range' : (1800,18599), + 'UP_range' : (18000,18599), }, 2 : { 'FDL_Low' : 1930, 'NDL_Offset' : 600, @@ -214,6 +214,14 @@ 'NUL_Offset' : 27760, 'UP_range' : (27760,27809), }, + 38 : { 'FDL_Low' : 2570, + 'NDL_Offset' : 37750, + 'DL_range' : (37750,38249), + }, + 40 : { 'FDL_Low' : 2300, + 'NDL_Offset' : 38650, + 'DL_range' : (38650,39649), + }, 65 : { 'FDL_Low' : 2110, 'NDL_Offset' : 65536, 'DL_range' : (65536,66435), @@ -455,10 +463,15 @@ def earfcn2freq(band, dl_earfcn=None, ul_earfcn=None): out: tuple (float downlink_freq, float uplink_freq) ''' NDL_Offset = table_earfcn[band]['NDL_Offset'] - NUL_Offset = table_earfcn[band]['NUL_Offset'] - duplex_spacing = abs(table_earfcn[band]['FDL_Low']-table_earfcn[band]['FUL_Low']) + if table_earfcn[band].__contains__('NUL_Offset'): + NUL_Offset = table_earfcn[band]['NUL_Offset'] + if table_earfcn[band].__contains__('FUL_Low'): + ful_low = table_earfcn[band]['FUL_Low'] + else: + ful_low = 0 + duplex_spacing = abs(table_earfcn[band]['FDL_Low']-ful_low) FDL_Low = table_earfcn[band]['FDL_Low'] - FUL_Low = table_earfcn[band]['FUL_Low'] + FUL_Low = ful_low downlink_freq = uplink_freq = None if dl_earfcn is not None: downlink_freq = FDL_Low + 0.1 * (dl_earfcn-NDL_Offset) @@ -468,6 +481,10 @@ def earfcn2freq(band, dl_earfcn=None, ul_earfcn=None): uplink_freq = downlink_freq - duplex_spacing elif downlink_freq is None and uplink_freq is not None: downlink_freq = downlink_freq + duplex_spacing + + if FUL_Low == 0: + uplink_freq = 0 + return (downlink_freq, uplink_freq) if __name__ == "__main__": diff --git a/utils/logprocess.py b/utils/logprocess.py index 3118165..20185d2 100644 --- a/utils/logprocess.py +++ b/utils/logprocess.py @@ -3,14 +3,16 @@ # # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): -# )synacktiv.com> wrote this file. As long as you retain this notice you +# )penthertz.com> wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return FlUxIuS ;) +# ---------------------------------------------------------------------------- from __future__ import print_function from engines.android.generic.ADBshell import * from engines.android.samsung.ServiceMode import * from engines.host.diag.xgoldmod import * +from engines.sdr.srslte_pss import * from engines.host.serial.AT import AT from utils.colors import * from core.mKB import * @@ -18,9 +20,20 @@ from threading import Thread import argparse import json +import signal kb = mKB() +def signal_handler(sig, frame): + global th, state + print('Keyboard interrupt received, stopping cellmapping and saving results...') + state = False + cells = kb.data['SM_cells'] + saveCells(cells) + sys.exit(0) + +signal.signal(signal.SIGINT, signal_handler) + def statesmv(func, msg=None, wait=10, arg=None): if msg is not None: print (bcolors.OKBLUE+msg+bcolors.ENDC) @@ -30,22 +43,106 @@ def statesmv(func, msg=None, wait=10, arg=None): func() time.sleep(wait) + def bringTestMode(): sm = ADBshell() sm.androidsdkpath = mKB.config['androidsdk'] statesmv(sm.pushsecretcode, arg='4636', wait=2) + def bringServiceMode(): sm = ADBshell() sm.androidsdkpath = mKB.config['androidsdk'] statesmv(sm.pushsecretcode, arg='0011', wait=2) + def startXgoldmodCollect(): xg = xgoldmod() th = Thread(target=xg.parseFifo) th.daemon = True th.start() + +def startSrsLTExPSSProcess(cmdprog): + global state + import subprocess + state = True + bands = mKB.config['bands'].split(",") + while state: + try: + for band in bands: + commandstring = [mKB.config['SRSLTETOOLS_PATH']+cmdprog, "-b", band] + if mKB.config['device_args'] is not None: + if 'soapy' in mKB.config['device_args']: + splitted_arg = mKB.config['device_args'].split(':') + commandstring.append("-d") + commandstring.append(splitted_arg[0]) + commandstring.append("-a") + commandstring.append(splitted_arg[1]) + else: # TODO handle id of multiple devices connected on USB + commandstring.append("-a") + commandstring.append(mKB.config['device_args']) + p = subprocess.run(commandstring) + except (KeyboardInterrupt, SystemExit): + state = False + cells = kb.data['SM_cells'] + saveCells(cells) + + +def startSrsLTENPSSProcess(): + startSrsLTExPSSProcess("cell_search_nbiot_modmobmap") + + +def startSrsLTEPSSProcess(): + startSrsLTExPSSProcess("cell_search_modmobmap") + + +def startSrsLTEPSSCollect(): + print("STARTED COLLECT") + srs = srslte_pss() + th = Thread(target=srs.parseFifo) + th.daemon = True + th.start() + + +def startSrsLTENPSS(): + th = Thread(target=startSrsLTENPSSProcess) + th.daemon = True + th.start() + startSrsLTENPSSCollect() + state = True + th.join() + while state: + try: + pass + except (KeyboardInterrupt, SystemExit): + state = False + cells = kb.data['SM_cells'] + saveCells(cells) + +def startSrsLTEPSS(): + #global th, state + th = Thread(target=startSrsLTEPSSProcess) + th.daemon = True + th.start() + startSrsLTEPSSCollect() + state=True + th.join() + while state: + try: + pass + except (KeyboardInterrupt, SystemExit): + state = False + cells = kb.data['SM_cells'] + saveCells(cells) + +def startSrsLTEPSSCollect(): + srs = srslte_pss() + th = Thread(target=srs.parseFifo) + th.daemon = True + th.start() + + def startServiceModeCollect(): sm = ServiceMode() sm.androidsdkpath = mKB.config['androidsdk'] @@ -55,9 +152,11 @@ def startServiceModeCollect(): th.daemon = True th.start() + def printInfo(string): print (bcolors.OKBLUE+string+bcolors.ENDC) + def saveCells(obj): import time jscells = json.dumps(obj, indent=4, sort_keys=True) @@ -87,8 +186,10 @@ def processOperatorAT(operators): "=> Switching back to auto-mode", arg=2) except (KeyboardInterrupt, SystemExit): state = False - cells = kb.data['SM_cells'] - saveCells(cells) + break + cells = kb.data['SM_cells'] + saveCells(cells) + def processOperatorADB(operators): sm = ADBshell() @@ -109,11 +210,51 @@ def processOperatorADB(operators): "=> Switching back to auto-mode", arg=2) except (KeyboardInterrupt, SystemExit): state = False - kb = mKB() - cells = kb.data['SM_cells'] - saveCells(cells) + break + cells = kb.data['SM_cells'] + saveCells(cells) process = sm.grablogcat() + +def scanGRGSM(band): + from engines.sdr.grgsm_scanner import do_scan + def trigfunc(found_list): + for info in sorted(found_list): + info.attr2dic() # trigger log + device_args = "" + if mKB.config['device_args'] is not None: + device_args = mKB.config['device_args'] + do_scan(2e6, band, 4, 0, 30.0, device_args, trigfunc, False) + + +def processGRGSM(bands): + state = True + cbands_list = ["GSM900", + "DCS1800", + "GSM850", + "PCS1900", + "GSM450", + "GSM480", + "GSM-R"] + + if bands is None: + bands_list = cbands_list + else: + bands_list = bands.split(',') + + while state: + try: + for band in bands_list: + statesmv(scanGRGSM, + "=> Switching to %s band" % band, arg=band) + except (KeyboardInterrupt, SystemExit): + state = False + break + kb = mKB() + cells = kb.data['SM_cells'] + saveCells(cells) + + def processManualMCCMN(string): dic_ = {} splitted = string.replace(' ','').split(',') @@ -121,6 +262,7 @@ def processManualMCCMN(string): dic_[code] = code return dic_ + def load_operators(): try: f = open('cache/operators.json', 'r') @@ -129,12 +271,13 @@ def load_operators(): if len(operators) > 0 or operators is not None: print (bcolors.WARNING+"Found %i operators in cache, do you want to reuse them?:\n\t%s"+bcolors.ENDC % (len(operators), str(operators))) answ = input('(Y)es or (N)o?') - if answ.lower() is 'y': + if answ.lower() == 'y': return operators f.close() except: return None - + + def saveMCCMNC(obj): jscache = json.dumps(obj, indent=4, sort_keys=True) f = open('cache/operators.json', 'w+')