-
-
[原创]吉他能听见你的话吗?——某无线吉他拾音器空中协议逆向(一)射频与扰码
-
发表于: 19小时前 35
-
无线音频的传输安全性,长久以来都是个人隐私保护的一个关键议题。当我们看到“无线音频安全”这个词的时候,我们可能想到的是蓝牙耳机,可能是智能家居的音箱设备,乃至特工电影里一些发着红光的小圆柱体。然而在不依赖用户失误或厂商为节约成本做到极致的情况下,想要正面攻击这些链路(它们通常在标准实现下也配置椭圆曲线密码交换与TRNG),是极具困难的。
然而在另一边,一些同样在进行无线音频的,替代有线而生的设备被划分为低端的,默认为点对点的,无需强加密即可传输的等级,毕竟谁能指望一个吉他拾音器泄露什么非音乐之外的内容呢。抑或是通过某些私密的实现,使其无法被标准工具所接收,它们的链路安全性如何?它们传输的内容完全不担心泄露吗?本文中笔者研究了闲置的一个吉他无线拾音器,对其空中链路,帧结构,加解扰逻辑与种子派生等过程进行了分析,最终实现了对于此拾音器及其所用芯片系列的解调/任意内容调制能力。
本系列将依据内容分为2-3篇完成,后续将介绍手工接收板,调试IIC和PCM遇到的一些问题,搭建SDR发射机对codec进行黑盒测试的流程,对软解codec做的一些尝试(尽管失败了),使用接收板作为codec解码预言机实现音频流解码,接收机做成UAC设备直连PC,以及拾音器的打板麦克风拾取环境音的演示。
笔者最初只是希望为放了很久的,原厂接收器丢了的吉他拾音器(下图琴孔处打码的黑色部分)重新配一个自制的接收机,使其可以直接内录到PC,但是研究过程中发现这一拾音器带有集成的麦克风,且传输保密程度出人意料的脆弱,因此写此文章记录一下。(你可以直接跳转到感兴趣的章节或等待后续系列文章,而不必从射频开始读起
受害者↓
此外十分高兴看到有朋友喜欢某Arm9裸机楼宇对讲系统室内机黑盒逆向(一)那篇,然而由于我并非这一专业方向的人员,无法对这一硬件品类中后续发现的漏洞进行有效的负责任披露,因此那一系列只能断更了,向大家说声抱歉。
声明:本文所做的所有测试均在私有设备上进行,以学习和研究为目的。本文有关射频发射的部分均以同轴线直连,或在射频暗箱中完成。本文以强化无线音频安全意识为宗旨,不透露任何与公开参数有关,或能关联到特定产品与特定实体的内容。本文仅进行原理性说明,将隐去部分关键性参数。
某具备无线传输能力的有源吉他拾音器(pickup,用于将吉他的弦震动与打板转换为电声传输,这款是用于民谣吉他的),于U段上进行射频音频传输,并附有一接收器用于射频音频接收,为吉他音箱或效果器输入。其中含有一U/V段的无线音频传输芯片,主打低成本,低延时。
所需的硬件设备包括:闲鱼魔改版pluto-SDR(带AD9363射频前端),DSlogic U3Pro16逻辑分析仪,安捷伦E3631A电源,FT232/CH341USB转TTL模块,PicoProbe调试探针,一些其他的核心板,焊接工具等。

因为笔者实在不舍得拆了这个拾音器,因此本次并没有拆机观察环节,而是在拾音器的官网上找到了其宣传材料中所使用的射频芯片系列,并想办法找到了它的数据手册。
数据手册宣称其支持U/V段,高保真的48kHz音频传输。使用sdrpp观察其射频状况,发现同其数据手册所说一致,在(笔者这台设备)上是680MHz左右的,一个带宽为300kHz的平台。

数据手册宣称其使用私有的协议,DQPSK调制,对于低端消费级设备来说,这是常见的,因为不能指望发射或者接收任意一端使用哪怕TCXO,且整个射频基带和codec全部封装在硅逻辑里。以其价格来看,复杂的均衡,信道估计,FEC什么的是很难有的,DQPSK无需严苛的锁相,无需专利授权,作为一个私有的,低延迟的实现十分适合。
它的帧结构大致为:长度为24Bit的同步字(用于粗定时捕捉,对于这类芯片等效于定时同步),?Bit的用户数据段,378Bit的音频段,16Bit的CRC,帧间填充单音段。音频段以48kHz采样,每样本经过codec后7bit,每帧可携带54个音频采样点。
在硬件接口方面,这一芯片系列使用一个魔改版的IIC(频率最高可达数MHz),与MCU进行通信,同时通过一I2S接口输入(或输出)音频流,在更多场景中,音频流直接通过发射机片内ADC/接收机片内DAC直接以电压信号输出。
GNU Radio是一款开源的,深受SDR社区喜欢的软件,他将常用的功能模块与算法封装起来,使得用户可以用数据流图的方式编辑,快速的用SDR研究标准或非标准的信号。
为了更具体的从数据层上研究这一拾音器的链路实现,笔者首先搭建了用于将空中帧解调为位流的流图。设备方面,笔者使用了pluto-SDR的兼容版(便宜版),其可以支持70M-6GHz频段的射频接收与发射。

将其本振调整至处于发射状态的吉他拾音器的频段中心684.405MHz,调整ad9363的内置滤波器到1MHz,输出4.096Msps的原始IQ流到PC端处理,在流图中再进行一次150kHz的低通滤波,并过软件AGC使其变为数据段幅度大致为1.0,随后再过一次alpha=0.4的RRC成型滤波器(此参数来自数据手册),接下来的操作都在其上进行。

因为DQPSK的解调不用锁相,所以省去了很多调costas环等的麻烦(当然因为本文做的是粗糙的接收机,如果希望用软件实现比原厂接收机更好的效果,锁相也是推荐的)。因此帧分割和一些同步就成为了主要的工作。
首先是一次粗同步,(这里已经假设有信号,因此没有做信号存在检测的逻辑),粗同步用的是Schmidl-Cox法,在此处是延时一个符号周期自相关,因为笔者最初看见信号的时候还没拿到数据手册,一度以为帧间填充段(也就是前导段)是一个有规律的重复。最后发现是单音,其实这里更推荐L&R法的,但是不影响用也就没有改。

帧间填充段也就是如下图↓所示的,(这是已经频偏补偿过的了)。自相关后取滑窗均值,除整个帧间填充的能量做归一化,大致反映出的是在帧间填充长度的区间内,符号间的平均自相关性,该值用于识别帧间填充段。超过一个阈值后作为粗同步标记corr_frame。

**接收机必须先进行粗同步,因为商品晶振的频偏实在是太大了,飘出去几十ppm都有可能,如果这里不进行频偏补偿的话,同步字与模板互相关的时候就会淹没互相关峰。**Schmidl-Cox从平均自相关中就能得出一系列频偏估计:
Δf̂ = Fs / (2πL) · arg P(d),其中 P(d) = Σₖ₌₀ᴸ⁻¹ r*(d+k) · r(d+k+L)
即延时自相关 P(d) 的辐角乘以 Fs/(2πL)。此处 L 为自相关延迟,折合一个符号周期的采样点数。实现中共轭取在另一侧,则整体符号取反。
下面是一个相当草率的实现,用于取出位于前导范围内的逐点频偏估计。这一段频偏进入一个胡乱调参的Kalman滤波器,用于跨帧估计本振和发射机之间的频偏。
在pluto使用TCXO的情况下,这一频偏实测大约是44500Hz左右,65ppm左右。这实际是不太理想的,理论值应该是在25ppm以内,可能是我的天线匹配并不比插个铁棍好多少,好在链路还有足够的冗余。
粗同步过程中计算出的频偏值取负后调制,乘在输入IQ流上,用于补偿发射机晶振的频偏。补偿后的IQ流送入匹配滤波器,与一个同步字模板进行互相关,以更精确的确定帧起始位置。

这一互相关模板,最初是直接从抓到的一段原始IQ流中截取的。后面则按照同步字生成原始IQ,过一次RC成型滤波,获得互相关模板。
此芯片所使用的同步字非常短,仅12符号长,如果只使用同步字本身做模板的话在数据区有相当多的误判发生,需要进行过滤。为了省去更麻烦的帧长度记忆和搜索,因此使用{帧间单音填充段+同步字}整段作为互相关模板,尽管这会增加一些旁瓣,并可能降低定时精度。
下图展示了互相关模板的生成,图1,2的左半边为帧间填充段,右半边为同步字,图3则是此模板在一段真实空中数据上进行匹配滤波的结果。

在流图中,这一功能是由CorrelationEstimator块完成的,其对频偏补偿后IQ输入进行匹配滤波,在数据段第一采样点位置打corr_est标签。
考虑到这一芯片的数据部分只有200符号以内,1.125ms以内的短帧,假设晶振的频偏是100ppm,即最后一个符号的判决点飘移在0.113us以内,然而一个符号长度有4.88us,这一判决点漂移对于定时并不是致命的,因此此处不做额外的定时调整。
同时因此芯片使用了DQPSK,差分解调可以相对容忍相位的缓慢飘移,如下图所示,差分后的数据具有显著更清晰的单音段。因此在匹配滤波给出同步字标记corr_est后,使用一个小的脚本块,寻找相邻的corr_frame粗同步标记与corr_est同步字标记,视作一有效帧起点,随后直接选取corr_est及其后202符号(数据段总长)用于判决。

直接选取判决点后的星座图如下图所示,其EVM约为16.7% (-15.5 dB),尽管不理想,但对于DQPSK来说可以直接判决,本文就不再进一步调优。

分帧并按照pi/2 DQPSK判决后,获得逐帧的二进制流。考虑到此芯片的数据手册上并没有说使用AES等加密方式,而是说“有一个32位的加密号寄存器”,并且通过配置此寄存器,可以对设备进行分组,防止串音。按照此类芯片的一般实现,猜测此寄存器中的值用于驱动一个LFSR扰码器生成伪随机序列,这一序列直接异或到数据流上或经过一些非线性环节异或到数据流上。
线性反馈移位寄存器(LFSR)如下图所示,常常被用于生成伪随机序列,给定特定的位数,种子与抽头,即可生成一个循环的伪随机数序列。然而其并不具备真正的随机性,在给出种子与抽头情况下,它的序列是可预测的。

对于音频codec来说,当持续收到近似静音的采样点,其输出常常会变为0,或特定值,或变的具有规律。这一有规律的数据流与加扰序列异或以后,加扰序列本身的性质将显现出来。因此将吉他拾音器的音量旋钮调整到最小,使其发出近似静音的帧。
抓取35000帧静音帧,对其进行逐位统计每一bit是否0或1均匀出现。如果一位的值在所有帧中有80%都是1或都是0,那么图例就会标注为80%。统计结果如下,可以观察到前三字节是确定的同步头,第4与第5字节前段是帧标记与确定的用户数据段,随后直到Byte051均为加扰后的音频段,他们每一位大约80%的概率都是某一值,而不是均匀分布。
这一统计特征强烈的提示每帧使用了固定的加扰序列+codec输出在近似静音输入下大部分时间输出同一个值,(因为静音通过模拟旋钮静音,codec输入仍有噪声)。
笔者假设codec对于静音状态输出的概率最高的码字为0,那么每一位都取概率最高所得到的即为加扰序列本身(加扰序列XOR0=加扰序列)。
假设加扰序列以最简单的形式生成,即LFSR直接生成。判断这一点的经典方法是计算此序列的线性复杂度,即此序列可以由最小多少位的LFSR生成,Berlekamp–Massey法即可完成这一点,具体可以阅读:938K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2L8W2)9J5k6i4N6A6K9$3W2H3k6h3c8A6j5g2)9J5k6h3!0J5k6#2)9J5c8Y4N6A6K9$3W2Q4x3V1k6n7k6i4u0D9k6h3E0S2L8i4m8Q4x3U0g2q4x3W2)9J5y4e0R3H3i4K6t1#2z5e0y4y4j5i4y4K6k6i4W2Q4y4h3k6S2L8r3N6G2M7X3W2@1K9r3@1`.
计算线性复杂度的同时还可以顺便检查字节序,笔者并不知道星座图四个区域分别代表着什么含义,比如1+0j究竟应该解码为00,01,10还是11,以及传输的时候会不会LSB先发,这一点在私有实现中并没有确定。因此刚刚得到的逐位概率最高序列,可能比特交换,或格雷码编一层或解一层才是芯片中扰码器输出的序列。因此可以生成各种变体,如果扰码序列真的由LFSR生成,那么只有与芯片中扰码器输出相同的那个序列线性复杂度最低。
从计算得到的线性复杂度可以看到,整个加扰序列表现出了强烈的线性性质,同时最初的解码很可能是错误的,每个符号的两位交换得到的才是芯片内的比特序列。
同时扰码器的LFSR结构也被一并探测出,其长度为39,生成多项式也可解出。
在上一章节,我们已经知道了扰码器是一个长度为39的LFSR,在捕获的静音数据中,能够得到其种子S0,长度为39的比特序列。
**然而这一种子在配对中发生变化,配对时MCU会更改配置寄存器Sid,一个32位的寄存器,这一寄存器派生为39位的S0。**为了配置接收或发射芯片,使其成为兼容的发射或接收机,需要搞清Sid与S0间的关系。
鉴于芯片的实现相对简单,猜测Sid与S0关系是线性的,即假设S0=M·Sid+F。试Sid=0x12345678与Sid=0xEDCBA987,发现当Sid位反时,S0也位反,可得M·????₃₂ = ????₃₉
直观上来说,就是S0的每一位由Sid选出奇数个位异或得到(M的每一行都有奇数个1)。因此直接猜M是选择矩阵,因为这样硬件连线最简单,而且没有什么额外的必要在这里做的复杂。即M每行只有一个1,S0[i]=Sid[j]。
因此在这个假设下我们只要逐一翻转Sid的每个位,然后测S0的哪个位发生翻转,即可获得这一选择矩阵M。当Sid=0,S0=F可直接获得F=????????。当然我们不用发32次,而是可以掩码化,也就是发"AAAAAAAA","CCCCCCCC","F0F0F0F0","FF00FF00","FFFF0000","00000001"这几个就行,原理是当我们关注Sid的第j位时,确定其对应的是S0的哪几位,我们只需要让除了j以外的位在另外的试验中全部翻转过至少一次,此时未翻转的一个或多个S0位就是j位所对应的。
通过一STM32板,连接此芯片家族的发射机,将Sid寄存器改为待测值逐一试验,同时在SDR上捕捉静音帧,通过上面的方法求解得到:
S0为Sid的高30位,填入S0[00]到S0[29],再低9位填入S0[30]到S0[38]。然后随机选取另外两个Sid对此关系进行检验,以确定选择矩阵的假设正确。由此,我们可以从配置寄存器获得扰码种子,或反解配置寄存器。
考虑到芯片不会使用太偏门的CRC实现,因此直接试验一些常见的CRC算法。因CRC的线性性质Cr(Data, Init) = Cr(Data, 0) ^ Cr(0, Init),即使初值错误,不同帧XOR这个错误的CRC计算之后能得到一个确定的Cr(0, Init),在未知初值的时候可判定一个CRC多项式是否正确。
最初试了一些libscrc自带的多项式如CRC-16-CCITT,CRC-16-IBM等,但是libscrc是带padding的,考虑到芯片用的是门电路实现,因此大概率使用了一个不带padding的版本。因此试CCITT 0x1021多项式(嵌入式常用),移位寄存器实现的CRC,位数可以不对齐,这一版本最终被测试为正确。
[招生]科锐逆向工程师培训(2026年11月13日实地,远程教学同时开班, 第57期)