和 Codex 改造 USB 上网卡:从备份固件到网页短信与通话

目录

声明:本文包含 AI 辅助创作。

这次交给 Codex 一个麻烦的任务:把手边的一个 USB 上网卡改成运行在 Linux 主机上的短信收发设备。主力模型用的是 GPT 6.1 Sol。

需求很简单,几张偶尔用来收验证码的卡总得放在某个设备里,扔在手机里费电还得维护,不如做个设备插在 Linux 主机上长期运行。之前看到有人用大疆 4G 模块改过类似设备,正好有些闲置硬件,干脆自己动手。

本来只想问问可行性,结果越聊范围越大:能不能把原固件完整读出来?能不能自己写个小程序放进去?能不能直接编译刷机?

做到现在,固件通过 USB 备份出来了,自己写的 C 程序也成功运行在设备上。先做了 Mac 应用,验证了短信收发和双向通话,后来又做了网页版本,把模块接到 NAS 上。先把这段过程记下来。

先找到的是一台红米 1S

最早看中的其实是抽屉里的红米 1S。这台旧手机早就 root 过,网上能找到不少开源内核和第三方系统,本来打算刷一个轻量 Linux 进去直接管理 SIM 卡。

Codex 当时帮我把整个方案分析了一遍:Linux 内核要改哪些驱动、基带跟系统怎么通信,以及去掉安卓后哪些服务必须保留,还查了远程双向通话音频如何采集和送回。

但很快就发现不行。这台手机要用的 2G 网络,本地早退网了。基带连不上网,改系统也没有用,只能换方案。

找了一圈,总算找到了以前买的一个 USB 上网卡。

拆开外壳,确认芯片型号

这东西本来就是插电脑上,通过网页后台配置流量、提供 Wi-Fi 的。撬开外壳,里面有一个标准的 SIM 卡槽,但插上自己的卡后,行为跟预想的不一样。

我把 PCB 正反面的芯片丝印拍给 Codex,让它根据丝印继续查。板子型号 CSM45L,核心是展锐 US8310AM。核对完资料后有点失望:这块芯片使用的是厂商的定制 RTOS,网上那些把同类上网卡刷成 OpenWrt 的方法完全用不上。

调试时还发现一个异常现象:开机后反复轮询 SIM 卡状态,刚通电时能读到卡,过一会儿状态就变了。单次查询看不出原因,必须写个循环连续打日志才发现这个时序问题。至于底层到底是哪段代码导致的,只能继续分析固件。

没有 SDK,只能自己找参考固件

最简单的办法是通过 USB 端口发 AT 指令,直接调用原固件收发短信。不过我更想自己控制,写一个精简的设备端程序,把短信通道直接提供给主机,替换掉原厂那些多余的逻辑。

底层蜂窝协议栈和基带驱动当然得留着,LTE 鉴权和拨号这部分太复杂,没必要自己实现。我的目标很明确:实现 SIM 卡管理、读写短信,以及和主机的通信。

麻烦在于,展锐这块芯片根本没有公开的 SDK。Codex 一直把「缺乏厂商头文件和工程源码」标为后续的主要风险。好在我没打算放弃,让它继续找相关资料。

后来它真的找到了一个 D-Link 同平台的官方固件包。里面除了刷机包,还带了符号信息和调试产物。我们直接交给 IDA 和分析脚本,对照着手上的板子二进制逐段分析。

虽说没有完整的工程源码,但有了这些带函数名的参考样本,大批原本没有名字的内存地址总算有了名称,函数签名和消息结构也有了线索。

刷机前,先确保能恢复

谁也不想为了备份用热风枪吹 Flash 再上编程器,必须想办法从 USB 把全盘数据读出来。

Codex 找出了固件里留存的诊断读取接口,先读出一部分内存代码进行交叉比对。接着,它基于开源的底层加载器改了一个 NAND 读取工具,把控制器能访问的原始 NAND 数据完整读出来,并处理硬件 ECC 校验。

在参考固件里找到原厂加载器时,我差点直接拿来用。好在 Codex 又检查了一遍反汇编,发现这个加载器在接收主机指令前,会触发自动格式化或重写配置分区。

加载器上电后会执行哪些操作,必须先查清楚。最后我们用了自己写的小程序,严格限定读写范围,完成全盘备份。

备份做完,回写测试也必不可少:划定应用区,擦除,写回,再读出来对比,最后重启。确认原厂固件还能正常启动,才敢进行下一步修改。

第一版固件:返回版本号

测试用的第一版功能很少,只写了一个极小的 C 程序:上电后接管指定位置,通过 USB 返回一个自定义的版本字符串。

没有 SDK 的后果就是硬编码地址:保留原厂的底层和驱动,把编译好的二进制写入分析好的空闲函数区,重新计算校验和并打包。

第一次刷完,我满怀期待地重启。USB 直接没响应了,拔插好几次也没有反应,刷坏了。好在 PCB 上留了复位短接点,按住按键重新上电,Codex 在终端等待,用下载模式把原厂固件写了回去。

失败原因没法只靠分析完全查清,调整了内存重定位和启动标记后,第二版总算通过了。插上电脑,终端返回一行字:

sim-pool app/0.1

把内存读出来核对无误,再刷回原厂固件确认硬件没有损坏。到这里,编译、刷入、执行这三个环节都验证通过了。

终于读出短信内容

到了最重要的短信环节,又遇到了问题。

Codex 找到了平台层处理短信的回调函数,写好了注册逻辑。结果发了条测试短信后,终端没有反应。查了很久才发现,回调拿到的只是底层裸事件,部分关键消息头要在后续的分发流程里才会被填充。我们拦截得太早,把消息当成垃圾过滤掉了。

修正逻辑后,自研的固件终于通过 USB 输出了两条真实的短信内容。在 Mac 上运行脚本解析:一条是测试用的英文,一条是中文短信,反复读很多次,数据非常稳定。

读出旧短信后,继续把 SIM 和短信初始化补上,LTE 注册也正常了。接着从 Mac 通过自己的 USB 接口发了一条测试短信,手机确实收到了;用手机回一条,设备也读到了,发送人、时间和正文都能解析。到这里,短信的收发也验证通过了。

原厂那些多余的逻辑还没全删掉,目前只是接管了一部分初始化和 USB 交互。还没有达到稳定托管设备的程度。

从脚本到 Mac 应用

短信能收发后,每次还在终端里输入命令也不太方便,于是接着做了个 SwiftUI 的 Mac 应用,叫 SIM Pool。应用直接通过 USB 操作模块,左侧切换电话和短信,查看会话、搜索和回复都在窗口里完成,运行时不用另开 Python 服务。

短信存进本机 SQLite 数据库,断开设备也能查看。收到的短信先归档并读回核对,再从模块里清理,免得设备存储被验证码占满。运营商发回来的中文短信也试过了,两段长短信能在应用里拼成一条。

加上电话功能

接着又想试试:既然模块本身支持语音,能不能直接使用 Mac 的麦克风和扬声器打电话?

这部分继续用平台原有的 IMS 通话协议栈。没有为通话再刷一版固件,Mac 在连接时把一小段 C 扩展加载进模块 RAM,调用原有的网络和通话函数。语音帧通过 USB 在模块和电脑之间传输,Mac 端用 AMR 编解码库处理,并通过系统音频接口采集和播放。

先从 Mac 拨出去,手机能收到也能接听,但听到的全是噪音,应用还报 Core Audio 错误。查日志发现,Mac 端采集和播放的音频格式不一致,先把这部分修复,再继续查来电。

来电更麻烦:另一部手机拨进来,应用没有反应,对方最后被转到了运营商智能助理。一开始连来电请求都看不到,后来抓到网络包,才发现 IMS 消息被拆成了 IPv6 分片,扩展漏掉了接收重组后的解密处理。接收修好后能看到来电请求了,模块却又返回 580 Precondition Failure。继续查,发现较大的 SIP 回复在发送前也被分片了,这些分片跳过了加密。

最后复用平台已有的分片重组和发送函数,把上下行处理补齐。再用另一部手机拨入,这次应用终于弹出来电,接听后手机和 Mac 的双向语音都正常,之前的噪音和异常挂断也没再出现。

外拨后来又遇到接听瞬间断线。查到最后,模块重复触发读取通知,第二次读取返回 EWOULDBLOCK,原有 SIP 代码就把 TCP 连接关了。调整通知时序,再修正跨缓冲区的解密数据处理后,外拨接听和双向语音也通过了实测。

把模块接到 NAS 上

先做的 Mac 应用已经能收发短信、接打电话。后来用了一段时间,还是觉得网页更灵活,换台电脑或用手机也能访问。于是又做了网页版本,把 USB 模块插到 NAS 上,也就不用占用 Mac 的 USB 端口了。服务用 Docker 常驻运行,源码在 NAS 本地构建。

Python 服务独占 USB,负责设备连接、短信归档和通话控制,短信存在 NAS 的 SQLite 数据库里。浏览器通过 WebRTC 使用当前电脑或手机的麦克风和扬声器,NAS 本身不用接音频设备。网页通话需要 HTTPS,服务外面加了一层反向代理。

迁移时最费时间的还是音频。固定音调测试明明通过了,实际通话却有强噪音,最后查到旧版 AMR-WB 解码库处理静音帧有问题,换到 0.1.6 后才恢复正常。另一次断续则跟模块的静音检测有关:它减少了发包,主机排队的音频却还在持续增加。主机接管音频时关闭这项检测、退出时恢复,来电连续通话和外拨复测才都正常。

电脑网页和手机 H5

界面统一用了 Material Design,主导航只留电话、短信和设置。桌面上分栏查看会话和正文,设备状态与诊断放进设置;手机 H5 则用底部导航,打开会话后看全屏详情。刷新后能回到之前打开的内容,短信草稿和滚动位置也保留着。

下面三组拼图包含八张页面截图,号码、短信、通话记录和设置均为模拟数据。

桌面 Web:通话详情与录音、短信会话与编辑区

桌面设置:消息推送、设备与诊断

手机 H5:电话、短信、设置、来电与留言

收到短信和来电后,服务可以发送 Bark、Web Push 或 Webhook 通知,点击通知能定位到对应内容。iPhone 上可以把页面加到主屏幕,打开后没有浏览器地址栏;Web Push 在页面关闭后也能收到。通知失败会按间隔重试,短信在别的设备上读过之后,还没发出的提醒会取消。

通话录音和超时自动接听也已完成。录音在音频就绪后开始,通话详情里可以播放、下载和删除。自动接听默认等待 30 秒,然后播放提前合成好的问候语和滴声,提示对方留言;留言有默认时长上限,也可以改问候语和音色。

后来补上的几个功能

短信功能加了一个本地分类模型。模型是从一个 iOS 短信过滤应用里提取的,权重和词表直接打包进服务,分类在 NAS 上完成,不把短信发给外部接口。它先把短信分成正常、垃圾和交易三类,交易短信再细分金融、运营商、天气、订单物流等。垃圾短信仍然保存并显示在会话里,只是不生成提醒;已有短信在服务启动后会补上分类结果。

来电识别也加上了。呼入号码和短信发送号码会查询公开的号码信息接口,显示骚扰或诈骗标记、公司名,以及普通号码的归属地和运营商。结果会出现在通话页、顶部来电提示、历史记录、短信会话和通知里。查过的号码按类型缓存一段时间,历史通话和短信在服务启动后补识别。通知最多延迟 1.5 秒,超时就先按号码发送,通话本身不受影响。

识别到骚扰号码后,可以选转到留言或直接挂断,默认转到留言。转到留言需要先开启留言信箱,没开启时会直接挂断。只有号码标记明确命中骚扰、诈骗、推销、中介、贷款、催收等短语时才会处理,公司名里带类似字样不会触发。号码标记本身可能误判,所以处理方式留给了用户。

通话状态也补全了:拨号、对方振铃、接通、保持和结束原因会显示中文,外呼的 SIP 响应和 Q.850 网络原因会翻译成常见含义。音频可以在 WebRTC 和 WebSocket 之间切换;浏览器音频断了,通话不会因此挂断,换一台设备可以接管音频。

下面两组拼图用模拟数据展示这些识别结果:桌面端的来电页、短信页和设置,以及手机端的来电、短信列表和短信详情。

桌面:来电识别、短信分类与设置

手机:来电识别、短信分类列表与详情

功能清单

NAS 网页版目前有这些功能:

电话

  • 呼入、呼出、接听、拒接和挂断,显示拨号、振铃、接通、保持等阶段
  • 通话录音,详情页播放、下载和删除
  • 超时自动接听和留言信箱,可改问候语、音色、等待时间和留言上限
  • 来电识别:骚扰标记、公司名、归属地、运营商;骚扰来电可转到留言或直接挂断
  • 呼叫失败时显示 SIP 响应和网络原因的中文说明
  • WebRTC / WebSocket 两种音频传输;音频断开后保留通话,可换设备接管
  • 通话历史和 JSON 导出

短信

  • 收发、长短信拼接、会话列表、搜索和导出
  • 垃圾短信和交易短信本地分类,垃圾短信不推送提醒
  • 发送人识别,显示标记、公司名和归属地
  • 已读状态同步,已读短信取消未发出的提醒
  • 自动清理模块和 SIM 卡里的已接收短信,默认关闭

通知

  • Bark、Web Push 和 Webhook,可同时配置多个目标
  • 来电、短信提醒,失败重试和发送记录
  • 点击通知打开对应通话或短信

设备与页面

  • 通话、短信和设置三个栏目,桌面分栏、手机底部导航
  • 页面可添加到主屏幕,支持深色主题和横屏
  • 刷新后保留所在栏目、滚动位置和短信草稿
  • 设备诊断、USB 拔插恢复和手动重连
  • 登录保护、更新提醒和页面缓存清理

这次改造的几点体会

这次 Codex(配合 GPT 6.1 Sol)帮忙最多的,是把查芯片、对照同平台符号和写 USB 工具这些工作连续做了下来。没有公开 SDK,很多接口只能从固件里确认,结果还得拿到设备上试。

整个过程最有效的还是一次只验证一件事:先确认能不能读、写坏后能不能恢复,再输出版本号、调短信接口。到了通话,能接通之后还有音质和连续性要查。固定音调能正常播放,遇上真实通话的静音帧仍然会出问题,这次算是记住了。

型号、丝印和原厂版本

把这次查到的型号和版本号集中列在下面,方便使用同款板子的朋友搜索。

包装生产企业:深圳前海聚火科技有限公司
销售型号:UX21
入网型号:JH-MW-B1
PCB 丝印:CSM45L_MB_V2.0_20211029
屏蔽罩标签:CSM45L-33A / YYC20230329-01 / 20230404

主芯片丝印:UNISOC US8310AM RGRW01.00A MPL795000E AGABB06206
Wi-Fi 芯片丝印:SV6158 THC2246 01H38M
存储芯片丝印(完整料号仍待核对):1405KLE ZP1205 CHINA TC58NY G1S8H BA16
平台相关名称:UIS8310 / UMS9117 / Cortex-A7 / ThreadX / MOCOR
PCB 测试点:RXD / TXD / VBAT / VBUS / DM / DP / GND / KEY / PBINT
NAND ID:98 BA 90 55 76 16
芯片 ID:0x98180001
USB VID:PID(正常模式):1782:000b
USB VID:PID(下载模式):1782:4d00
BootROM 握手:SPRD3

原厂 AT+CGMI:Spreadtrum Communication CO.
原厂 AT+CGMM:V1.0.1-B7
Platform Version: STONEOIM_uis8310vs_csm45l-03-34a_jh_pl_ssv_cc_64mb_lte_4m-cs_vsim-oldbase_user_20230415113142_Release
Software Version: 0.1902.16.00
Hardware Version: vsim-csm45l-jh
Modem Model: modem_csm45l-03a
Flash type: P
Platform OEM: stoneoim
FOTA Version: V1.0
Platform Name: vsim-csm45l-jh
BASE Version: FM_BASE_THIN_MODEM_TRUNK_W21.05.3
HW Version: UMS9117
buildtime: 04-15-2023 11:34:23

同平台参考资料(另一块板子):
D-Link DWR-930M / CSM18-01 / FW 2.07ME
UMS9117_uis8310_128MB_M2_halo_rel.axf
UMS9117_uis8310_128MB_M2_halo_rel.map
UMS9117_uis8310_128MB_M2_halo_rel.pac
uis8310_csm18-01_weijia_m1c_pl_cc_64mb_lte_4m-cs_halo-stable_user_20230216211708
评论区加载中…