Skip to content

Commit

Permalink
Merge pull request #32 from SysSec-KAIST/LTESniffer-v2.0.0
Browse files Browse the repository at this point in the history
update version 2.0.0
  • Loading branch information
hdtuanss authored Jul 11, 2023
2 parents a0e07b3 + 1c40759 commit 3a79c3c
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 19 deletions.
71 changes: 71 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"files.associations": {
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"array": "cpp",
"atomic": "cpp",
"strstream": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cfenv": "cpp",
"chrono": "cpp",
"cinttypes": "cpp",
"codecvt": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"cstdint": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"list": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"map": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"set": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"ostream": "cpp",
"shared_mutex": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"bit": "cpp"
}
}
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ Please refer to our [paper][paper] for more details.
## LTESniffer in layman's terms
LTESniffer is a tool that can capture the LTE wireless messages that are sent between a cell tower and smartphones connected to it. LTESniffer supports capturing the messages in both directions, from the tower to the smartphones, and from the smartphones back to the cell tower.

LTESniffer can **NOT DECRYPT** encrypted messages between the cell tower and smartphones. It can be used for analyzing unencrypted parts of the communication between the cell tower and smartphones. For example, for encrypted messages, it can allow the user to analyze unencrypted parts, such as headers in MAC and physical layers. However, those messages sent in plaintext can be completely analyzable. For example, the broadcast messages sent by the cell tower, or the messages at the beginning of the connection are completely visible.
LTESniffer **CANNOT DECRYPT** encrypted messages between the cell tower and smartphones. It can be used for analyzing unencrypted parts of the communication between the cell tower and smartphones. For example, for encrypted messages, it can allow the user to analyze unencrypted parts, such as headers in MAC and physical layers. However, those messages sent in plaintext can be completely analyzable. For example, the broadcast messages sent by the cell tower, or the messages at the beginning of the connection are completely visible.

## Ethical Consideration

The main purpose of LTESniffer is to support security and analysis research on the cellular network. Due to the collection of uplink-downlink user data, any use of LTESniffer must follow the local regulations on sniffing the LTE traffic. We are not responsible for any illegal purposes such as intentionally collecting user privacy-related information.

## Features
### New Update
- Supports two USRP B-series for uplink sniffing mode. Please refer to `LTESniffer-multi-usrp` branch and its [README][multi-readme] for more details.
- Improved the DCI 0 detected in uplink.
- Fixed some bugs.

LTESniffer is implemented on top of [FALCON][falcon] with the help of [srsRAN][srsran] library. LTESniffer supports:
- Real-time decoding LTE uplink-downlink control-data channels: PDCCH, PDSCH, PUSCH
- LTE Advanced and LTE Advanced Pro, up to 256QAM in both uplink and downlink
Expand All @@ -33,7 +38,7 @@ LTESniffer is implemented on top of [FALCON][falcon] with the help of [srsRAN][s

## Hardware and Software Requirement
### OS Requirement
Currently, LTESniffer works stably on Ubuntu 18.04/20.04.
Currently, LTESniffer works stably on Ubuntu 18.04/20.04/22.04.

### Hardware Requirement
Achieving real-time decoding of LTE traffic requires a high-performance CPU with multiple physical cores. Especially when the base station has many active users during the peak hour. LTESniffer was able to achieve real-time decoding when running on an Intel i7-9700K PC to decode traffic on a base station with 150 active users.
Expand All @@ -43,11 +48,11 @@ Achieving real-time decoding of LTE traffic requires a high-performance CPU with
- At least 16Gb RAM
- 256 Gb SSD storage
### SDR
Currently, LTESniffer requires USRP X310 with 2 daughterboards, especially when sniffing the uplink traffic. This is because sniffing the uplink traffic requires precise time synchronization between uplink and downlink subframes, which can be achieved by using two daughterboards with the same clock source from a single motherboard of USRP X310. Also, the "srsran_rf_set_rx_freq" function used by LTESniffer seems to only support the USRP X310 with 2 daughterboards for simultaneous reception of signals at two different frequencies. The function might not work with USRP X310 equipped with a single TwinRX daughterboard. The USRP X310 should be equipped with GPSDO to maintain stable synchronization. Additionally, two RX antennas are required to enable LTESniffer to decode downlink messages in transmission modes 3 and 4.
Currently, LTESniffer supports a single USRP X310 or two USRP B-series to sniff uplink traffic. For using two USRP B-series, please refer to `LTESniffer-multi-usrp` branch and its [README][multi-readme].

To sniff only downlink traffic from the base station, one can operate LTESniffer with USRP B210 which is connected to PC via a USB 3.0 port. Similarly, USRB B210 should be equipped with GPSDO and two RX antennas to decode downlink messages in transmission modes 3 and 4.
To sniff only downlink traffic from the base station, LTESniffer is compatible with most SDRs that are supported by the srsRAN library (for example, USRP or BladeRF). The SDR should be connected to the PC via a USB 3.0 port. Also, it should be equipped with GPSDO and two RX antennas to decode downlink messages in transmission modes 3 and 4.
## Installation
**Important note: To avoid unexpected errors, please follow the following steps on Ubuntu 18.04/20.04.**
**Important note: To avoid unexpected errors, please follow the following steps on Ubuntu 18.04/20.04/22.04.**

**Dependencies**
- **Important dependency**: [UHD][uhd] library version >= 4.0 must be installed in advance (recommend building from source). The following steps can be used on Ubuntu 18.04. Refer to UHD Manual for full installation guidance.
Expand Down Expand Up @@ -185,8 +190,6 @@ To enable the WireShark to analyze the decoded packets correctly, please refer t
**Note:** The uplink pcap file contains both uplink and downlink messages. On the WireShark, use this filter to monitor only uplink messages: ``mac-lte.direction == 0``; or this filter to monitor only downlink messages: ``mac-lte.direction == 1``.

## Application Note
### Uplink sniffing mode
When sniffing LTE uplink, LTESniffer requires USRP X310 because it needs to listen to two different frequencies at the same time, 1 for uplink and 1 for downlink. The main target of the uplink sniffing function is to decode uplink traffic from nearby smartphones. However, as LTESniffer needs to decode the downlink traffic to obtain uplink-downlink DCI messages, it also supports decoding downlink traffic at the same time. Nevertheless, the downlink sniffing function is limited to decoding messages which use transmission modes 1 and 2, since LTESniffer only has 1 antenna for downlink.
### Distance for uplink sniffing
The effective range for sniffing uplink is limited in LTESniffer due to the capability of the RF front-end of the hardware (i.e. SDR). The uplink signal power from UE is significantly weaker compared to the downlink signal because UE is a handheld device that optimizes battery usage, while the eNB uses sufficient power to cover a large area. To successfully capture the uplink traffic, LTESniffer can increase the strength of the signal power by i) being physically close to the UE, or ii) improving the signal reception capability with specialized hardware, such as a directional antenna, dedicated RF front-end, and signal amplifier.
### The information displayed on the terminal
Expand Down Expand Up @@ -231,9 +234,9 @@ Please refer to our [paper][paper] for more details.
```
## FAQ

**Q:** What kind of SDRs I can use to run LTESniffer? \
<!-- **Q:** What kind of SDRs I can use to run LTESniffer? \
**A:** To sniff only downlink traffic from the base station, LTESniffer works well with USRP B210 with 2 RX antennas.
To sniff the uplink traffic, LTESniffer requires USRP X310 with 2 daughterboards. There are two reasons for this. First, sniffing the uplink traffic requires precise time synchronization between uplink and downlink subframes, which can be simply achieved by using two daughterboards with the same clock source from a single motherboard of USRP X310. Second, the "srsran_rf_set_rx_freq" function used by LTESniffer seems to only support the USRP X310 with 2 daughterboards for simultaneous reception of signals at two different frequencies.
To sniff the uplink traffic, LTESniffer requires USRP X310 with 2 daughterboards. There are two reasons for this. First, sniffing the uplink traffic requires precise time synchronization between uplink and downlink subframes, which can be simply achieved by using two daughterboards with the same clock source from a single motherboard of USRP X310. Second, the "srsran_rf_set_rx_freq" function used by LTESniffer seems to only support the USRP X310 with 2 daughterboards for simultaneous reception of signals at two different frequencies. -->

**Q:** Is it mandatory to use GPSDO with the USRP in order to run LTESniffer? \
**A:** GPSDO is useful for more stable synchronization. However, without GPSDO, LTESniffer still can synchronize with the LTE signal to decode the packets.
Expand All @@ -256,4 +259,5 @@ To sniff the uplink traffic, LTESniffer requires USRP X310 with 2 daughterboards
[paper]: https://syssec.kaist.ac.kr/pub/2023/wisec2023_tuan.pdf
[pcap]: pcap_file_example/README.md
[app]: https://play.google.com/store/apps/details?id=make.more.r2d2.cellular_z&hl=en&gl=US&pli=1
[watching]: https://syssec.kaist.ac.kr/pub/2022/sec22summer_bae.pdf
[watching]: https://syssec.kaist.ac.kr/pub/2022/sec22summer_bae.pdf
[multi-readme] https://github.com/SysSec-KAIST/LTESniffer/tree/LTESniffer-multi-usrp
11 changes: 11 additions & 0 deletions src/include/DCISearch.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
#include "PhyCommon.h"
#include "MetaFormats.h"
#include "falcon/prof/Lifetime.h"

typedef struct SRSRAN_API{
uint16_t rnti = 0;
uint32_t L = -1;
uint32_t ncce = -1;
int format = -1;
dci_candidate_t cand = {};
bool added = false;
} ltesniffer_accepted_dci_t;

class DCISearch {
public:
DCISearch(falcon_ue_dl_t& ue_dl,
Expand Down Expand Up @@ -49,4 +59,5 @@ class DCISearch {
uint32_t sfn;
DCIBlindSearchStats stats;
bool enableShortcutDiscovery;
std::vector<ltesniffer_accepted_dci_t> temp_dci0;
};
4 changes: 2 additions & 2 deletions src/include/ULSchedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ class ULSchedule
void delete_rar_ULSche(uint32_t tti);
std::vector<DCI_UL>* getULSche(uint32_t tti);
std::vector<DCI_UL>* get_rar_ULSche(uint32_t tti);
uint32_t get_ul_tti(uint32_t cur_tti);
uint32_t get_rar_ul_tti(uint32_t cur_tti);
int get_ul_tti(uint32_t cur_tti);
int get_rar_ul_tti(uint32_t cur_tti);

void set_config();
bool get_config() {return config; }
Expand Down
59 changes: 57 additions & 2 deletions src/src/DCISearch.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,27 @@ int DCISearch::inspect_dci_location_recursively(srsran_dci_msg_t *dci_msg,
#ifdef PRINT_ALL_CANDIDATES
printf("Cand. %d (sfn %d.%d, ncce %d, L %d, f_idx %d)\n", &cand[format_idx].rnti, sfn, sf_idx, ncce, L, format_idx);
#endif
if (rntiManager.getActivationReason(cand[format_idx].rnti) == RM_ACT_RAR && cand[format_idx].dci_msg.format == 0){
ltesniffer_accepted_dci_t temp_dci;
temp_dci.rnti = cand[format_idx].rnti;
temp_dci.L = L;
temp_dci.ncce = ncce;
temp_dci.format = cand[format_idx].dci_msg.format;
temp_dci.cand = cand[format_idx];
temp_dci.added = false;
bool add_to_temp_list = true;
for (auto dci_member:temp_dci0)
{
if (dci_member.format == cand[format_idx].dci_msg.format && dci_member.rnti == cand[format_idx].rnti && dci_member.ncce == ncce){
add_to_temp_list = false;
}
}
if (add_to_temp_list){
temp_dci0.push_back(temp_dci);
// printf("[DCI] sfn %d.%d, Accept DCI 0 to cand list. %d , ncce %d, L %d, fm: %d)\n",sfn, sf_idx, cand[format_idx].rnti, ncce, L, cand[format_idx].dci_msg.format);
}
}

if(result != SRSRAN_SUCCESS) {
ERROR("Error calling srsran_pdcch_decode_msg_limit_avg_llr_power\n");
}
Expand Down Expand Up @@ -163,6 +184,10 @@ int DCISearch::inspect_dci_location_recursively(srsran_dci_msg_t *dci_msg,
INFO("Found RA-RNTI: 0x%x\n", cand[format_idx].rnti);
falcon_ue_dl.current_rnti = cand[format_idx].rnti;
}
else if(meta_formats[format_idx]->format==SRSRAN_DCI_FORMAT1C){
// printf("Found 1C RA-RNTI: 0x%x\n", cand[format_idx].rnti); //T_note
falcon_ue_dl.current_rnti = cand[format_idx].rnti;
}
else {
//DEBUG("Dropped DCI cand.: RA-RNTI only with format 1A: 0x%x\n", cand[format_idx].rnti);
//INFO("Dropped DCI cand. %d (format_idx %d) L%d ncce %d (RA-RNTI only format 1A)\n", cand[format_idx].rnti, format_idx, L, ncce);
Expand Down Expand Up @@ -374,9 +399,38 @@ int DCISearch::inspect_dci_location_recursively(srsran_dci_msg_t *dci_msg,
// finally, process the accepted DCI
// adopt new DL Sniffer
cand[hist_max_format_idx].dci_msg.rnti = cand[hist_max_format_idx].rnti;
bool add_dci = true;
if (cand[hist_max_format_idx].rnti != 0){
dciCollection.addCandidate(cand[hist_max_format_idx], srsran_dci_location_t{L_disamb, ncce}, hist_max_format_value,
sf, &ue_dl_cfg->cfg.dci);
if (cand[hist_max_format_idx].dci_msg.format == 0){
for (auto dci0_mem:temp_dci0){
//if adding DCI0 is in temp list, not add in this step, add in step 2
if (dci0_mem.format == cand[hist_max_format_idx].dci_msg.format && dci0_mem.rnti == cand[hist_max_format_idx].rnti && dci0_mem.ncce == ncce){
add_dci = false;
}
}
if (add_dci){
// printf("[DCI1] sfn %d.%d, Accept DCI 0 to cand list. %d , ncce %d, L %d, f_idx %d)\n",sfn, sf_idx, cand[hist_max_format_idx].rnti, ncce, L, cand[hist_max_format_idx].dci_msg.format);
dciCollection.addCandidate(cand[hist_max_format_idx], srsran_dci_location_t{L_disamb, ncce}, hist_max_format_value,
sf, &ue_dl_cfg->cfg.dci);
}
}else{
// printf("[DCI2] sfn %d.%d, Accept DCI 0 to cand list. %d , ncce %d, L %d, f_idx %d)\n",sfn, sf_idx, cand[hist_max_format_idx].rnti, ncce, L, cand[hist_max_format_idx].dci_msg.format);
dciCollection.addCandidate(cand[hist_max_format_idx], srsran_dci_location_t{L_disamb, ncce}, hist_max_format_value,
sf, &ue_dl_cfg->cfg.dci);
}
//step 2: add all DCI0 in temp list to final dci collector
for (auto dci0_mem:temp_dci0){
// printf("nof_dci = %d \n", temp_dci0.size());
// printf("[DCI3] sfn %d.%d, Accept DCI 0 to cand list. %d , ncce %d, L %d, f_idx %d)\n",sfn, sf_idx, dci0_mem.rnti, dci0_mem.ncce, dci0_mem.L, dci0_mem.format);
unsigned int temp_hist_max_format_value = rntiManager.getFrequency(dci0_mem.rnti, dci0_mem.format);
// Prevent adding multiple times
if (dci0_mem.added == false){
dciCollection.addCandidate(dci0_mem.cand, srsran_dci_location_t{dci0_mem.L, dci0_mem.ncce}, temp_hist_max_format_value,
sf, &ue_dl_cfg->cfg.dci);
dci0_mem.added = true;
}
}
temp_dci0.clear(); //clear all member after added
}

// cleanup and return
Expand Down Expand Up @@ -514,6 +568,7 @@ int DCISearch::search() {
float snr_db = falcon_ue_dl.q->chest_res.snr_db;
if (snr_db > 6.0){
//PrintLifetime lt(test_string + "DCI Blind Search: ");
temp_dci0.clear();
recursive_blind_dci_search(&dci_msg, sf->cfi);
ret = SRSRAN_SUCCESS;
}
Expand Down
2 changes: 1 addition & 1 deletion src/src/SubframeWorker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ void SubframeWorker::run_ul_mode(SubframeInfo &subframeInfo, uint32_t tti)
/*Get Uplink and Downlink dci and grant lists*/
std::vector<DCI_UL> dci_ul = subframeInfo.getDCICollection().getULSnifferDCI_UL(); // get UL DCI0 list
std::vector<DL_Sniffer_DCI_DL> dci_dl = subframeInfo.getDCICollection().getDLSnifferDCI_DL(); // get DL DCI list

/*UL grant for RRC Connection Request is sent in RAR response (msg 2),
/ so convert UL grant from msg2 to general UL grant*/
std::vector<DCI_UL> rar_dci_ul_vector;
Expand Down
8 changes: 4 additions & 4 deletions src/src/ULSchedule.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ void ULSchedule::set_SIB2(asn1::rrc::sib_type2_s *sib2_)
lock.unlock();
}

uint32_t ULSchedule::get_ul_tti(uint32_t cur_tti)
int ULSchedule::get_ul_tti(uint32_t cur_tti)
{
uint32_t temp_tti = cur_tti - 4;
int temp_tti = (int)cur_tti - 4;
if (temp_tti >= 0)
{
return temp_tti;
Expand All @@ -123,9 +123,9 @@ uint32_t ULSchedule::get_ul_tti(uint32_t cur_tti)
}
}

uint32_t ULSchedule::get_rar_ul_tti(uint32_t cur_tti)
int ULSchedule::get_rar_ul_tti(uint32_t cur_tti)
{
uint32_t temp_tti = cur_tti - 6;
int temp_tti = (int)cur_tti - 6;
if (temp_tti >= 0)
{
return temp_tti;
Expand Down

0 comments on commit 3a79c3c

Please sign in to comment.