☰
Python+RTL-SDR实现GSM短信嗅探:环境搭建与解码实战
2026/10/15 11:47:42 网站建设 项目流程

简介:面向无线通信安全研究与Python-SDR开发者的GSM短信嗅探项目包,演示如何借助软件定义无线电设备捕获并解析GSM短信,揭示国内GSM网络因加密算法被破解而存在的隐私泄露风险。资源共11个文件,涵盖4个Python脚本、1个SQL数据库文件、1份Markdown说明及5张运行与截图图片,压缩包仅544KB,轻量且代码结构清晰。已有1165人学习,适合具备基础Python知识、对蜂窝网络协议或无线电安全感兴趣的读者。通过阅读源码和画面演示,可掌握从SDR信号接收、下变频解调、帧同步到A5算法解密、短信内容解析的完整链路,理解gr-gsm等开源工具的实际用法;项目还提供了短信入库与转发示例,可快速改造用于教学实验或无线通信安全测试,是入门无线嗅探的实用参考资料。

1. GSM短信嗅探是什么:为什么这个Python工具值得一试

做无线电安全这些年,我拆过不少“很玄学”的抓包工具,但第一次跑通Python-gsmsmssdr从空中抓到一条完整GSM短信的时候,还是有点小激动。原因很简单:GSM网络里的短信在多数场景下并没有做端到端加密,空口只靠A5/1这种老算法保护,而在某些地区甚至直接以明文发射。用一台几十块的RTL-SDR配合这套Python工具,就能把基站下发的短信在PC上解出来。这不是黑客电影特效,而是真实可复现的无线电实验。它适合三类人:好奇无线电协议实现的学生、做检测系统验证的安全从业者、以及想低成本入门软件无线电的开发者。下面这套操作流程,能让你在本地实验环境里,从装驱动到解出第一条短信完整走通。

2. 搭建软件无线电环境:从RTL-SDR到gr-gsm依赖

2.1 硬件选型:RTL-SDR与HackRF的取舍

跑通GSM嗅探的第一步不是装软件,而是选对硬件。市面上最便宜的是RTL-SDR,也就是常说的“电视棒”,价格通常在60到120元区间。它覆盖频率范围大概从24MHz到1.7GHz,正好落在GSM900和GSM1800的频段内。带宽最大2.4MHz,这对单信道解调足够,但如果你想扫一段较宽的频谱,就有点吃力了。

HackRF One价格高几倍,胜在带宽能到20MHz,能同时录制多个频点。可对gsmsmssdr这个工具来说,它本身按单频点处理,RTL-SDR的2.4MHz带宽已经够用。我一般建议新手先用RTL-SDR,等确认对GSM空口有兴趣了再考虑升级。还有一点要注意:RTL-SDR最好选带TCXO温补晶振的版本,否则频偏会让你怀疑人生。

2.2 安装依赖:常见发行版的gr-gsm、gnuradio安装步骤

gsmsmssdr本质上是一个围绕gnuradio和gr-gsm封装出来的Python驱动脚本。所以先得把这两层装好。以Ubuntu 22.04为例,推荐用build-gnuradio脚本源码编译,虽然慢点但版本可控。下面是核心依赖安装指令:

# 更新系统并安装基础构建工具 sudo apt update sudo apt install git cmake g++ libboost-all-dev libgmp-dev swig python3-numpy \ python3-mako python3-sphinx python3-lxml doxygen libfftw3-dev \ libcomedi-dev libvolk2-dev libgflags-dev libglog-dev libprotobuf-dev \ libhdf5-dev libqt5opengl6-dev libqwt-qt5-dev # 安装 gr-osmosdr,用于对接 RTL-SDR 硬件 git clone https://github.com/osmocom/gr-osmosdr.git cd gr-osmosdr mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr make -j4 sudo make install

这里cmake指定安装前缀为/usr,是为了让后续gr-gsm能正确找到osmosdr模块。如果你不开这个参数,默认装到/usr/local,很多时候动态库路径会出问题,运行时报ModuleNotFoundError。我一般习惯把这两者的前缀保持一致,省去设置PYTHONPATH的麻烦。

接下来编译gr-gsm。它是专门处理GSM空中接口的gnuradio模块,负责完成信道估计、解调、突发脉冲切割等底层操作:

git clone https://github.com/osmocom/gr-gsm.git cd gr-gsm mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr make -j4 sudo make install

装完后执行gnuradio-companion,在模块列表里能看到GSM相关的块,就代表gr-gsm注册成功。需要注意的是,gr-gsm对gnuradio版本敏感,官方支持3.10和3.8,如果你系统里是旧版3.7,基本编译不过去。上升到这个环节,与其折腾旧版本,不如直接装新版系统。

2.3 校验设备:使用osmocom_fft验证SDR工作状态

装完驱动,先别急着跑嗅探。用osmocom_fft看一眼频谱,确认SDR在正常工作。命令参数很直白:

# 列出设备,确认 RTL-SDR 被系统识别 rtl_test # 打开频谱仪,观察 GSM 频段信号 osmocom_fft -f 935M -s 2M

-f 935M指定中心频率为935MHz,这是GSM900下行频段的典型起点;-s 2M表示采样带宽2MHz。运行后如果频谱图上能看到一个个窄带峰值,那多半就是BCCH广播信道。如果屏幕一片平或者全是噪声,先检查天线是否接好,再确认频率是否正确。还有一个常见问题:某些山寨RTL-SDR的驱动芯片不是RTL2832U,而是FC0012,频率上限只有948MHz,覆盖不全GSM900,这种情况只能换棒子。

3. 使用Python-gsmsmssdr抓取GSM广播信道:配置与实战

3.1 理解GSM帧结构与广播信道

要跑通这个工具,得先大概知道GSM空口是啥结构。GSM下行链路每51帧构成一个复帧,BCCH信道占用第0个时隙,每帧里会广播系统消息SIB。gsmsmssdr的核心思路很简单:锁定某一载波的BCCH,等它广播系统消息时,从中提取邻近小区描述和公共控制信道配置,然后再跳到SDCCH或TCH去解短信。它不需要破解基站,只是作为接收机被动解调。

这里有一个值得记住的细节:短信在下行方向通常通过SDCCH(独立专用控制信道)发送,当手机空闲时寻呼消息到了,基站会分配一条SDCCH传短信。gsmsmssdr会自动跟踪这些信道跳频,所以用户不用手动切频点,它比手动抓包的gr-gsm示例要省心很多。

3.2 配置gsmsmssdr参数:频率、增益与采样率

拿到源码后,直接看它的示例配置文件或命令行入口。工具是用Python写的,入口一般长这样:

python3 gsmsmssdr.py -f 935.2M -g 35 -s 2M -o output.pcap

参数含义如下:

  • -f:中心频率,需要设置到实际服务小区的BCCH频点。不知道的话用手机工程模式或scanner工具先扫一遍。
  • -g:接收增益,单位是dB。RTL-SDR建议从30到40之间试,过高会产生交调干扰。
  • -s:采样率,默认2M就够解一个GSM信道,再高会增加CPU负担。
  • -o:输出pcap文件名,方便用wireshark回放。

第一次运行,我建议把增益调低一点,比如25dB。RTL-SDR增益太高时,强信号会让前端饱和,反而是噪音变大。这个值不是越大越好,经典的“玄学调试”就是来回拧增益看结果。

3.3 运行嗅探脚本:第一个解出的短信

按下回车后,终端会刷出当前锁定的小区和接收到的寻呼消息。等个十几秒,如果附近有手机亮屏或者发短信,就能看到类似这样的输出:

[*] Sync found on ARFCN 80, MCC 460, MNC 00 [*] Channel: SDCCH, SAPI 3 [+] SMS: Hello from test phone

看到Sync found说明BCCH锁定了,后面的MCC和MNC能告诉你这是哪家运营商网络。然后[+] SMS后面跟的就是明文短信内容。当时我看到这条消息时,第一反应是“这就解出来了?”确实,在无加密网络里,整个过程就这么直接。不过要注意,很多环境里短信被A5/1加密,工具解出来的会是乱码,后面我会讲怎么处理。

4. 解密与解码:从比特流到可读短信

4.1 A5/1加密与“无加密”场景的判断

不是所有GSM短信都直接明文,所以得先判断当前网络是否加密。gsmsmssdr在锁定BCCH后,会通过系统消息里的加密指示字段告诉你答案。在终端日志里找一句话:

[INFO] Ciphering: A5/1

如果是A5/1,空口的数据会被加密,gsmsmssdr拿不到明文短信。但有个例外:很多地区的运营商在SDCCH上根本不启用加密,只对业务信道加密,或者在某些老旧的2G网络上,A5/1从未启用,即Ciphering: A5/0。A5/0就是明文,工具可以直接还原。你在中国大陆用一些虚拟运营商网络时,偶尔就能碰到A5/0的情况,这也是为什么不少人说“GSM在中国是没有加密的”的原因之一。但严格讲,不是全部网络都这样,只能说是部分。

4.2 gsmsmssdr的解码流程与输出格式

工具内置了一个轻量解码器,流程是先把突发脉冲还原成比特流,然后按照GSM 04.11协议去拆SMS TPDU。它输出pcap文件同时,也会在终端打印摘要。如果要看完整内容,把pcap拖进Wireshark:

sudo apt install wireshark wireshark output.pcap

Wireshark里对GSM短信的支持很成熟,过滤gsm_sms能看到每条消息的源号码、目的号码、内容、时间戳。我一般用这个步骤来做结果复核,防止终端打印的字段有截断。pcap这个保存形式还有个好处:你可以离线反复分析,不用一直开着电台接收。

4.3 常见编码问题:PDU与7-bit默认字符集

GSM短信正文用的是7-bit默认字母表,不是ASCII。如果看到乱码,别急着怀疑解密失败,先检查字符集。gsmsmssdr在解码时默认按7-bit解,但有些中文短信用UCS2编码,也就是16-bit。工具对UCS2的解码支持不算好,经常输出一堆Unicode转义符。这种情况,我一般直接用Wireshark看TPDU的user data头,里面有个data coding scheme字段,如果看到0x08就是UCS2,0x00是默认7-bit。后续可以自己写个脚本把UCS2的Unicode码点转成中文。

# 简单的UCS2短信解码示例 tpdu_hex = "4F60597D" bytes_list = bytes.fromhex(tpdu_hex) decoded = bytes_list.decode('utf-16-be') print(decoded) # 输出例如“你好”

这里的思路是按UTF-16BE解码,因为GSM的UCS2就是大端序。遇到混合编码,要先把分割符处理掉,再按段解码。编码这块是深坑,建议第一次实验之前,先用已知短信内容验证一下工具的解码链路。

5. 避坑指南:信号盲区、频偏与误码的三个排查记录

5.1 现象:抓不到任何BCCH,终端一直waiting for sync

原因:最常见的是频偏。RTL-SDR的晶振精度不够,实际接收频率和设置值之间可能差几千赫兹。GSM信道带宽只有200kHz,差个20kHz就可能解不出同步字。解决方法是先用kalibrate做校准:

# 安装 kalibrate 并扫描 GSM 频段 kal -s GSM900 -g 35 # 输出会给出 base station 的 offset 值,比如 -3523 Hz kal -s GSM900 -f 935M -g 35 -O -3523Hz

校准后把偏移量补偿到gsmsmssdr的频率参数里,比如原来设935M,校准后设935M-3523Hz。如果你的SDR是TCXO版本,偏移通常小很多,基本不用校准。从这以后,我每次换环境都会先跑一遍kal,已经形成习惯。

5.2 现象:短信解出来了全是乱码

原因:不一定是加密,可能是采样率参数不对。gsmsmssdr默认按2M采样率去匹配符号速率,如果你用2.4M或者1.6M,虽然理论上能跑,但符号定时恢复可能锁不准,导致突发脉冲里的比特翻转。解决方法是固定用2M,除非你用HackRF做整段频谱录制。另一个容易忽略的是USB线质量,劣质USB线会导致丢包,也会出乱码,换一根带屏蔽层的短口线能解决不少问题。

5.3 现象:程序运行十分钟后崩溃,报Input/output error

原因:RTL-SDR的USB传输模式是bulk transfer,当系统USB控制器有瓶颈时,驱动会返回I/O错误。这个问题在VMware或VirtualBox里跑Linux时特别明显,因为虚拟机对USB控制器的模拟不够稳定。解决方法是直接在真机装Linux,或者给虚拟机加一张USB 3.0控制器,并把SDR插到独立的USB端口上。另外,检查日志里有没有lost USB interrupts字样,有的话就降低采样率,从2M降到1.6M,牺牲一点解调余量换取稳定。

6. 进阶玩法:自动化持续监听与多频段扫描

6.1 多通道扫描:遍历所有GSM频点

手动指定频点太慢,真正要把GSM短信嗅探变成工具化流程,需要先自动找频点。可以用gr-gsm自带的scanner脚本,也可以自己在Python里写频率扫描逻辑。核心思路是遍历某个频段内的所有信道,用能量检测判断哪些频点是BCCH,然后把这些频点列表存下来:

import subprocess start_freq = 935_000_000 channel_spacing = 200_000 for i in range(124): freq = start_freq + i * channel_spacing # 用短时FFT能量估计判断该频点是否有BCCH cmd = f"osmocom_fft -f {freq} -s 200k -T 0.5 --no-gui -q" result = subprocess.run(cmd, shell=True, capture_output=True) # 简单的输出解析,判断是否存在信号峰值

这段代码只是个框架,实际使用时需要解析osmocom_fft的输出。或者更简单的方法,用kal -s GSM900直接拿到基站频点列表,再生成gsmsmssdr的调用参数。扫出频点后,写个循环逐一点跑,每条信道跑三分钟,把结果保存到带频率后缀的pcap里。

6.2 结果自动化处理:过滤目标号码短信

持续监听已经跑起来了,剩下的问题是从一堆短信里找到你要的。写个Python脚本读pcap并过滤号码,是我建议每个做抓包实验的人都会做的事。用pyshark库可以快速解析:

import pyshark cap = pyshark.FileCapture('output.pcap', display_filter='gsm_sms') target_number = "13800138000" for pkt in cap: if hasattr(pkt, 'gsm_sms'): sms = pkt.gsm_sms # 号码字段在 gsm_sms.sms_oa 或 gsm_sms.sms_da 中 oa = sms.get_field_value('sms_oa') da = sms.get_field_value('sms_da') if target_number in [oa, da]: print(f"[Match] From {oa} to {da}: {sms.sms_text}")

实际解析时字段名会因Wireshark版本略有差别,可以先在Wireshark里点开一条短信,看协议树里的字段名再改脚本。pyshark本质上是调用tshark解析,所以性能不算快,处理大pcap时建议先过滤再扫,或者直接在tshark命令行里用-Y过滤。碰到Wireshark版本和pyshark不兼容时,最省事的办法是直接用tshark输出json,然后交给Python解析。

这个监听流程跑顺之后,我养成了一个习惯:每次换天线或换频点,都会先跑一次伪基站模拟器发生器做个自检,确认接收链路自己没坏。说实话,GSM空口分析看起来并不难,坑都在细节里。希望这次拆解能帮你少走点弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询