在一年前笔者研究过一款嵌入式设备,当时就对其声波配网的逻辑感到十分新奇,但后面专注于漏洞挖掘就没有深入研究
最近在研究一些诸如sub-ghz、蓝牙、射频等无线通信的东西,于是通过声波传递信息的方式又激发了我研究的兴趣,所以就有了这篇文章,简要记录一下我的一些发现和心得
将主控芯片上的散热片去除后,我们可以通过放大镜读取主控芯片上的丝印:
使用的是lngenic t20
芯片的官方网站:Ingenic-北京君正集成电路股份有限公司
音频功放为XPT4890:
具体的音频接收是通过设备上的咪头,咪头接收到声波后产生电信号,然后主控芯片对电信号进行处理
首先采集一段配网时候的音频进行分析
通过aux公对公线,能直接从手机的耳机孔输出音频到电脑上,通过windows自带的录音软件或者OBS就可以采集
查阅资料发现声波配网和声音的频率有很大关系,所以这一步主要是通过脚本提取声波的频率特征
这里使用librosa这个库,但是它不支持m4a,只支持对wav进行处理,所以首先需要将m4a转换为wav
转换使用的是pydub这个库,它对音频的转换要安装FFmpeg:
Windows 10系统下安装FFmpeg教程详解_ffmpeg window10-CSDN博客
下载链接:https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip
把bin目录添加到环境变量就行了:
转换代码:
代码:
效果:
代码:
效果:
当wifi的名称是8个1,密码是32个1的情况
发现有一部分和上面的图片是重叠的,而且这张图片的频率分布确实更平整了,说明确实和频率是有关系的
但仍然看不出什么东西,需要具体逆向分析一下通信过程中传输协议是什么样的
整个通信大体可以概括为:
设备配套的app将wifi的ssid和passwd通过声波这种媒介传递到设备中,设备接收到信息后连接上对应的wifi并访问运营商的云端服务器,并给予用户连接成功/失败的反馈
在固件中能看到一个名为"connect_wifi.sh"的sh脚本,脚本主要的作用是通过wpa_supplicant.conf中的内容去连接wifi
wpa_supplicant.conf的格式如下:
搜索存在"wpa_supplicant.conf"的elf可以发现有如下调用:
在"ipcam"这个elf中可以看到如下内容:
那么可以确定是ipcam这个elf对声波进行识别,并把内容保存到"wpa_supplicant.conf"中,然后在"daemon"中执行"connect_wifi.sh"连接wifi
该函数会被当作参数传递到"vr_setRecognizerListener"中进行处理:
然后就到了c++的魅力时刻:
这里应该是某个结构体的虚表跳转,那么想继续从固件里看出东西的话就很困难了
但其实可以看出有很多地方都有调试的打印,而且这个设备是有UART调试串口的,所以应该可以通过调试打印的信息定位后续处理的位置
遗憾的是,调试口被笔者焊坏掉了(QAQ):
而且从板子上飞线出来也不是很好飞
所以暂时放弃对接收方的研究,去逆向一下发送方(app)的逻辑
对app进行解压,确定不是flutter架构:
Pkid查壳发现是《梆梆》加固:
用jadx直接打开也能看到比较明显的加固特征
通过frida dexdump脱壳试试:
能成功dump下来59个dex,并且每一个dex都可以通过JEB进行查看了:
首先需要从59个dex中定位到哪些是我们需要的
我们可以让app运行到声波配网的页面:
然后通过adb shell执行"dumpsys activity top | grep ACTIVITY"这行命令
就可以列出顶层的activity,也就是需要尤为关注的内容:
通过"Everthing"或者其它字符串搜索工具搜索"SoundWaveGuideActivity",能够定位到如下dex
用JEB对该dex进行简单的逆向可以知道整个流程应该是在"soundWaveGuide.control"中进行的
首先输入的SSID和passwd会被分别存入this.F和this.G:
然后到下面这个地方会对传入的SSID和passwd进行处理然后通过"play"方法进行播放
"play"和"encodeString"方法都不是在这个dex实现的,同样可以通过搜关键字符串的形式进行查找,最后在16.dex里找到了"play"方法的具体实现:
并且可以发现这里用的是一个原生库的函数:
直接检索"play"发现有很多的原生库都能匹配:
好在这个app在运行的时候会有一些关键的log,能够通过"adb logcat"进行打印获取:
通过搜索"play text"最终定位了具体的原生库文件:
原生库文件一样是用c++写的,直接逆向分析也会遇到虚表跳转
好在对于安卓有frida这种神器,可以打印具体的内存信息获取虚表跳转的地址
但是要hook的地方实在是太多了,基本上是一步一hook非常麻烦
好在app的开发者偷懒了,通过在github上搜原生库中的一个函数名"slg_gen"就能直接搜到一个名为"SmartBoy"的项目:
和原生库中的一模一样,而且其相关调用链也高度一致,也能在项目中搜到adb logcat的打印字符串:
项目地址:https://github.com/npnet/SmartBoy/tree/af0403fc7151f22381b0d0da7df3812f64ea5b3a
更为夸张的是,笔者在项目的"main.c"里发现了如下内容:
好家伙,直接不演了,黑盒测试变成白盒了
CSDN链接:http://blog.csdn.net/softlgh
通过CSDN的链接我们能下载下来linux、windows、macos、android这几个主流平台的声波配网的全部demo,这里面显然就包括了android的源码
在源码里我们能看到熟悉的原生库,以及熟悉的代码
只不过demo的baseFreq是16000,而app里的是4000:
至此,我们的逆向工作其实已经可以结束了,因为我们完成了逆向的最高境界——逆出源码(笑)
后续的工作更多的就是对传输的协议(对ssid+passwd字符串的处理)的分析了
分析协议主要是分析"play"的字符串长啥样
官方app提供了设备的日志,从日志里能看见对传入声波的解析后的内容:
主要是ssid psd phone这三个东西,猜测"play"的字符串就和这三个东西有关
在JEB反编译的java代码中可以看见这三个内容经过了两次处理:
第一次处理是对三个字符串的拼接,这里就不赘述
第二次处理是对第一次拼接的字符串进行编码:
hexChar是一个数组:
在encodeData里会把传入的字符串分为8类:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2023-12-5 00:05
被Nameless_a编辑
,原因: