What can you do? Briefly list your capabilities and the kinds of files and tasks you can help with.
I'm Dr. Binary, a binary analysis and security research assistant. Here's what I can do:
Core Capabilities
Reverse Engineering & Decompilation
- Analyze ELF, PE, Mach-O, and raw firmware binaries
- Lightweight triage (imports, exports, sections, strings, symbols) via Rizin
- Full decompilation and suspicious-function triage via Ghidra
- Semantic search over decompiled functions (e.g., "find the function that parses HTTP headers")
Firmware Analysis
- Extract firmware images (SquashFS, UBI, UBIFS, JFFS2, cramfs, nested archives) with binwalk/unblob
- Map attack surface: network endpoints, CGI handlers, IPC channels, NVRAM keys
- Build cross-binary dependency graphs (DT_NEEDED, imported/exported symbols)
- ELF header/manifest analysis across the whole extracted tree
Vulnerability Research
- Backward taint analysis (command injection, format string, unsafe memcpy) from sinks to attacker-controllable inputs
- Known-vulnerability (n-day) matching against an offline OSV CVE snapshot for userland components
- CVE deep-dives: affected versions, bug-inducing commits, fix patches, branch presence
- Kernel CVE bisection by branch, date, or AOSP version (Android kernels)
- Call-graph reachability ranking to prioritize findings
Patch & Diff Analysis
- Binary diffing (whole-binary and per-function, statement-level PDG diffing)
- Backdoor/vulnerability hunting in patch deltas
- Patch-gap triage (is the fix present in this build?)
Malware & CTF
- Malware static analysis and variant comparison
- CTF challenge solving (reverse engineering, pwn, crypto binaries)
Dynamic & Shell Work
- Sandbox execution of arbitrary commands (strace via qemu, emulators, Python with angr/pwntools/qiling)
- File carving, entropy analysis, format identification
Android
- Fine-grained CWE classification of Android user-level bugs and kernel commits (34 CWEs across memory, concurrency, pointer, input-control, resource categories)
Typical Tasks
- "Analyze this firmware for backdoors and vulnerabilities"
- "Does this binary contain known CVEs?"
- "Diff these two binaries and tell me what the patch changed"
- "Find command injection in this router's httpd"
- "Which CVEs affect an Android 13 5.10 kernel built on this date?"
Just point me at a file or describe the task — for large jobs I'll plan with a todo list and may delegate to specialized subagents (firmware, malware, vulnerability, CTF analysts).
这是我的安卓解包后的文件,请分析出里边的逻辑
todowrite
drbinary_run_sandbox
文件已验证:Dalvik DEX v035, 372 KB。现在开始进行反编译。
drbinary_run_sandbox
drbinary_run_sandbox
Decompilation succeeded (89 Java source files). Mapping the structure now.
drbinary_run_sandbox
这是一个 Cordova/Ionic 混合应用。该 DEX 包含原生框架、插件和主 Activity —— 业务逻辑很可能位于 HTML/JS 资源中。让我深入分析一下关键部分:入口点、插件和网络层。
read
read
grepregex parse error: https?://[^\"]+ ^^ error: unrecognized escape sequence
grep
grep
Key finding already: the whitelist is fully open (http://*/*, https://*/*). Now reading the functional plugins — BLE, HTTP, and the whitelist config.
read
read
drbinary_run_sandbox
No hardcoded secrets in the DEX. Now reading the BLE plugin (the app's core native functionality) and the TLS/HTTP security layer.
read
read
The BLE plugin is stock cordova-plugin-ble-central. Checking the WebView SSL handling and remaining security-relevant pieces.
grep
read
read
read
All stock Cordova so far. Scanning for anything non-standard: dynamic loading, exec, reflection, and checking remaining plugins.
grep
read
drbinary_run_sandbox
todowrite
分析完成。以下是 classes.dex(372 KB, Dalvik v035, MD5 10dbc0eba217ede7f40e98a3924c08b8)的完整逻辑分析。
一、应用概况
| 项目 | 值 |
|---|---|
| 包名 | io.ionic.starter2 |
| 版本 | 0.0.2 (versionCode 2) |
| 构建类型 | debug (BuildConfig.DEBUG = true) |
| 架构 | Cordova/Ionic 混合应用(HTML+JS 界面 + 原生桥接层) |
这是一个典型的 Ionic 混合应用,DEX 中只包含 Cordova 框架 + 插件原生层。真正的业务逻辑(界面、流程、网络请求内容)在 APK 的 assets/www/ 下的 HTML/JS 里,不在此 DEX 中。
二、启动流程
MainActivity.onCreate()
├─ 读取 Intent extra "cdvStartInBackground" → 若为 true 则 moveTaskToBack(true)(支持后台启动)
└─ loadUrl(launchUrl) // 启动地址来自 config.xml(assets 内),默认 file:///android_asset/www/index.html
└─ SystemWebViewEngine 初始化 WebView
├─ setJavaScriptEnabled(true)
├─ setAllowUniversalAccessFromFileURLs(true) ← 标准 Cordova 配置
├─ DOM 存储 / 地理位置已启用
└─ debug 包启用远程调试(chrome://inspect 可连)
三、核心功能:JS→原生插件桥
DEX 中暴露的原生能力全部通过 cordova.exec(service, action, ...) 调用,共 8 组插件:
1. BLE 蓝牙(核心功能)— com.megster.cordova.ble.central.BLECentralPlugin(cordova-plugin-ble-central)
scan / startScan / startScanWithOptions / stopScan:扫描 BLE 外设(按 service UUID 过滤,需ACCESS_COARSE_LOCATION权限),结果以 MAC 地址 + RSSI + 广播数据回调给 JSconnect / disconnect / isConnected:管理外设连接read / write / writeWithoutResponse:对指定 service/characteristic UUID 读写数据startNotification / stopNotification:订阅特征值通知readRSSI / enable / isEnabled / showBluetoothSettings / startStateNotifications:辅助功能- 这是整个应用最主要的原生能力,说明这是一个 BLE 外设配套 App(如蓝牙设备/IoT 控制端)
2. HTTP 网络层 — com.synconset.CordovaHttpPlugin(cordova-plugin-advanced-http)
get / head / post、uploadFile / downloadFile,基于 kevinsawickiHttpRequest库,在线程池中执行- 三个由 JS 控制的安全开关:
enableSSLPinning(从 assets 加载.cer证书)、acceptAllCerts、validateDomainName
3. SQLite 数据库 — io.sqlc.SQLitePlugin + io.liteglue.*(cordova-sqlite-storage)
- 本地 SQLite 存储,通过
System.loadLibrary("sqlc-native-driver")加载原生驱动
4. 框架插件:File(文件系统读写,内部/外部存储)、Device(设备信息:android_id/型号/序列号等)、SplashScreen、StatusBar、IonicKeyboard、Whitelist、CoreAndroid(App 生命周期)
四、安全相关发现
| 发现 | 位置 | 影响 |
|---|---|---|
| Debug 构建且忽略 WebView SSL 错误 | SystemWebViewClient.onReceivedSslError:(appInfo.flags & 2) != 0 → handler.proceed() | 此包为 debug 构建,WebView 中所有自签名/无效证书均被放行,可被 MITM;且可远程调试 |
| 允许 JS 关闭所有 TLS 校验 | CordovaHttp.acceptAllCerts(true) / validateDomainName(false) → trustAllCerts() / trustAllHosts() | 若 www 层 JS 调用了这些开关,HTTP 插件将接受任意证书/主机名(需看 JS 才能确认) |
| 白名单逻辑放行全部 http/https | WhitelistPlugin.CustomConfigXmlParser:config.xml 中 <access origin="*"/> 或 <allow-navigation href="*"/> 映射为放行一切导航与请求 | 配置宽松(具体取值在 assets/config.xml,DEX 内无法确认) |
| 遗留 TLS 协议 | TLSSocketFactory 启用 TLSv1/1.1/1.2 | 允许降级到弱协议 |
| 无硬编码 URL/密钥/证书 | 全 DEX 字符串池扫描 | 服务端地址在 JS 层 |
| 无动态加载/无 shell 执行 | 无 DexClassLoader、Runtime.exec、ProcessBuilder | 原生层干净,未发现后门或混淆隐藏代码 |
五、结论
这个 DEX 是一个未做定制修改的 Cordova 原生壳:框架类均为 Apache Cordova 官方代码,插件均为官方开源插件原样打包,无自研加壳、无混淆痕迹、无后门。应用的实际功能逻辑是一个「BLE 蓝牙设备 App」:JS 层负责 UI 与业务(连接蓝牙设备收发数据、通过 HTTP 插件与服务器交互、SQLite 本地存储),原生层只提供能力桥接。
六、下一步建议
如需分析完整的业务逻辑(服务端地址、认证方式、蓝牙协议指令、存储的数据内容),请提供以下任一项:
- 完整 APK(可用
apktool/unzip提取assets/www/) - 仅
assets/www/目录(HTML/JS 文件)
那里的 JS 代码才是真正决定 App 行为的部分。
确实是有assets/www的,这里边没有关于lora射频基础配置的内容吗
先在现有 DEX 里查一下 LoRa/射频相关的痕迹,同时确认 www 目录是否已在工作区。
drbinary_run_sandbox
在当前 classes.dex 里没有任何 LoRa/射频相关内容。我对 DEX 字符串池做了针对性扫描(lora / lorawan / freq / 433 / 470 / 868 / 915 / SF / BW / AT指令 / sx127x / rfm9x / uart / baud 等),除匹配到 Factory、Interface 这类无关子串外,零命中。
这和之前的结论一致:DEX 只是 Cordova 原生壳。而且从架构上讲也不该有——BLE 插件只提供通用的「向指定 UUID 特征值写二进制数据」能力,具体写什么指令(比如 LoRa 参数配置帧或 AT 命令)完全由 www 目录下的 JS 决定。LoRa 射频基础配置最可能出现在:
assets/www/js/*.js—— 配置页面、参数表单、下发指令的构造逻辑(频段 433/470/868/915 MHz、扩频因子 SF、带宽 BW、编码率 CR、发射功率、网络 ID/APP Key 等)assets/www/js/services/或类似目录 —— BLE UART/透传协议封装(特征值 UUID、分包、AT 命令集)assets/www/index.html+ 模板 —— 配置界面本身
请把 assets/www 目录上传到工作区(或直接传完整 APK,我可以自行解包提取 www),我就能定位出:
- LoRa 射频参数的默认值与可配置范围
- 配置是通过哪条 BLE 特征值/什么协议帧下发的
- 是否有服务端接口参与配置(结合 HTTP 插件)
文件到位后我会立即开始分析。
我通读了www 貌似没找到lora配置所以寄希望于dex了 ,你消耗token是按照字节数还是什么 我需要评估是解包后只给相关文件还是全给
关于 token 消耗
按处理量计,不是按上传字节数。具体规律:
- 文件传到沙箱只是落盘,不读就不消耗 token——传整个 APK 和只传几个文件,成本差为零
- 消耗发生在:我读入上下文的内容(文件内容、命令输出)+ 我的分析回复
- token ≈ 4 字节英文 ≈ 1.5~2 字节中文;一个 200KB 的压缩 JS 全文读完约几万 token,但 grep 定位后只读命中的片段只需零头
结论:不要人工筛选,直接全给。 你人工筛「相关文件」反而有风险——LoRa 配置很可能不在你预判的文件里(见下)。正确做法是全量上传,我用关键词全库扫描定位,只精读命中文件,成本可控且不漏。
关于 LoRa 配置去哪了
DEX 里已经确认没有。但「www 里没找到」大概率是搜索姿势问题,而非不存在,常见坑:
- JS 被压缩/打包——
vendor.js、main.js一类打包产物里字符串连成一行,肉眼通读容易漏 - 关键词不对——代码里可能没有 "lora" 字样,而是
radio、rf、airRate、sf/bw/cr、channel,甚至中文「射频/扩频/频段/频率/空中速率」 - 配置以原始字节下发——JS 里只有
write(Uint8Array([...]))这种十六进制帧,看不到 "LoRa" 字样 - 真不在 www 的可能:APK 可能还有
classes2.dex(multidex)、lib/arm64-v8a/*.so(原生库)、assets根目录的配置文件、预置 SQLite 库——你如果只通读了 www 的源码目录,这些全没覆盖 - 最坏情况:这是 LoRa 设备的 BLE 配套 App,射频参数写死在设备固件里,App 只负责把配置页的值原样转发——那 App 里就只有协议帧格式,没有「配置内容」本身
建议下一步
把完整 APK(或解包后的整个目录:全部 classesN.dex + assets/www + lib/*.so + res 清单)传上来,我按这个顺序排查:
- 全库 grep(英文 + 中文关键词 + 十六进制常量 433/470/868/915)
- 有命中 → 精读对应文件,还原配置项和下发协议
- 无命中 → 检查
.so符号表和其他 dex,最后给你「配置在设备固件侧」的结论和证据
这是web桥接为apk,请查阅
todowrite
drbinary_run_sandbox
drbinary_run_sandbox
APK 解包完成。关键事实:单 classes.dex(与之前那份完全一致,372308 字节)、无原生 .so 库、业务代码全部在 assets/www/build/(main.js 4.9MB 压缩包 + 6 个懒加载 chunk + sourcemap)。现在做全库 LoRa 关键词扫描(含中文 unicode 转义形式)。
drbinary_run_sandbox
lora 只在 main.js 出现 2 次,中文射频词零命中。看看这两处命中的上下文,以及中文语言包内容。
drbinary_run_sandbox
两处 lora 命中只是公司新闻页的营销文章(LoRa vs NB-IoT 行业软文),不是配置。中文语言包几乎为空。转向分析 BLE 通信协议——找服务/特征 UUID 和写入的数据帧。
drbinary_run_sandbox
命中核心线索:特征值 FF01/FF02/FF12 + 自定义封装 ble.WriteCH、ble.chklocalBle。提取 main.js 中的 BLE 协议代码。
drbinary_run_sandbox
drbinary_run_sandbox
找到了 BLE 服务层:FF12 服务 / FF01 通知 / FF02 写入,还有 WriteCH1(设置频道)、wakeCommand(唤醒)、SingleReadLoop8 等函数,看起来是抄表类应用。现在我来提取完整的协议构造逻辑。
drbinary_run_sandbox
这是一套完整的抄表控制协议(有 openValve/closeValve 开关阀、信道扫描、CRC 校验)。提取关键协议函数的代码。
drbinary_run_sandbox
帧格式已明确:55 | 命令 | 长度 | 数据 | CRC | AA。继续提取写信道和帧构造逻辑。
drbinary_run_sandbox
关键突破:WriteCH1 上方注释就是 「设置频率」——信道号即射频频率选择。现在查 UI 页面中信道/频率的选项值和响应解析。
drbinary_run_sandbox
UI 文案证实了完整的射频配置功能(读/写频道、信道扫描显示空闲/占用、信号强度检测)。查 470/868 的上下文和服务器端 ChannelId 的来源。
drbinary_run_sandbox
470/868 均为框架代码误匹配,App 内没有频率表。最后确认:服务器接口(UploadChn/ChannelId 来源)、唤醒指令帧、以及页面清单。
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
todowrite
分析完成。你之前没找到是正常的——"LoRa" 这个词在业务代码里几乎不存在,射频配置全部以「频道(信道)」的形式实现。以下是完整结论。
一、APK 结构确认
| 项 | 内容 |
|---|---|
| dex | 仅 classes.dex(与你之前给的完全相同,无 multidex) |
| 原生库 | 无 lib/*.so |
| 业务代码 | assets/www/build/main.js(4.9MB, Ionic/Angular4 打包)+ 6 个 2KB 空壳 chunk |
| 应用性质 | 东本燃气(tonben.com.cn)LoRa 无线抄表系统手持端,BLE 手抄机配套 App |
二、LoRa 射频基础配置:找到了,但形式和你预期的不同
App 不含 SF/BW/功率/频率表(搜遍全包,470/868 均为框架代码误匹配)。射频配置就是信道号(ChannelId)的读写与检测,全部在 BleServiceProvider 中,通过 BLE 下发给「手抄机」(LoRa 手持采集器):
BLE 链路:服务 FF12,通知 FF01,写入 FF02
帧格式:0x55 | CMD | LEN | DATA | CRC(累加和%256) | 0xAA
射频配置指令集(这就是你要的“射频基础配置”):
| 功能 | 函数 | 指令帧 | 说明 |
|---|---|---|---|
| 读当前频道 | ReadCH() | 55 21 00 21 AA | 返回 mm[3] 十六进制频道号,UI 显示“手抄机当前频道:N” |
| 写频道(设频率) | WriteCH/WriteCH1(CH) | 55 20 01 CH CRC AA | 函数注释明确写着「设置频率」——频道号 = 频率档位 |
| 单表设频率 | CHNSET(no, chn) | datacommand 0x10 | 按表号下发频道变更 |
| 信道扫描 | ScanCH() | 55 26 00 26 AA | 返回位图,逐位显示「频道k 空闲/被占用」 |
| 信道检测 | ChkCH(CH) | 55 24 01 CH CRC AA | 返回该频道的数据信道 RSSI + 唤醒信道 RSSI |
| 停止检测 | ChkCHStop() | — |
业务下发指令(经手抄机 LoRa 转发给表端):SINGLEREAD(0x01 抄表)、OPENVALVE/CLOSEVALVE(0x02 开/关阀)、WAKE(0x1B 唤醒周期)、AUTO(自动上传周期:月/日/时/分)、CALC(不计量关阀时间)。
三、关键逻辑细节
- 频道值来源是服务器:抄表前先调
/AppUser/GetUserMeterList查表档案,取user[0].ChannelId;改频道成功后调/AppUser/UpdateChnValve回传服务器并写SaveSetChannelLog日志 - 存在硬编码:一处代码路径放弃服务器值、
WriteCH1(deviceId,'50')强行设 50 频道(注释「强行设置50」),抄表前还固定下发唤醒帧5581010082AA - 频道→实际频率的映射表不在 App 内,写死在手抄机/表端固件里
- 服务器后端(
getApiURL按 site 切换,全部明文 HTTP):东本egas.tonben.com.cn、东本2eweb.tonben.com.cn、南通nant.tonben.com、如东、吴中、相城、临沂
四、顺带发现的问题
- 全部 API 走 明文 HTTP(
httpPostNoAuth),账号密码可被中间人截获 acceptAllCerts/白名单全开(上一轮 DEX 分析已述)叠加 debug 构建,WebView 层无 TLS 防护
结论:App 内的「LoRa 射频基础配置」= 频道号读取/写入/扫描/信号检测四组 BLE 指令;SF、带宽、发射功率、频点表等射频参数不在 App 里,在手抄机固件中。如需那部分,需要拿到手抄机或表端固件。
实际上的协议帧 分内外层把 蓝牙层包裹lora层 蓝牙层到手抄机 lora层是给手抄机里的lora与lora从机用的
对——你说的这个两层结构和代码完全吻合。我把 calcCommand 的帧组装后半段(上轮被截断)和响应解析 GetMsg 提取出来,用代码验证内外层边界。
drbinary_run_sandbox
calcCommand 后半段完全证实了你的两层模型——代码里就是先拼「表通讯帧」,再整体包进「手抄机帧」。补齐最后的收尾字节和内层帧头/帧尾常量,以及响应方向 GetMsg 的解包逻辑。
drbinary_run_sandbox
内层帧头 0x68、帧尾 0x16——典型的 CJ/T 188 类仪表协议封装。最后看响应方向:OpenNotice 通知回调里外层帧怎么拆、内层表应答怎么解。
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
完全正确,你的两层模型就是代码的实际结构。所有字节级证据都已提取完毕,整理如下。
双层协议结构(代码级验证)
手机 App (Ionic/JS)
│ BLE 写 FF02 ← 外层帧 → BLE 通知 FF01
▼
手抄机 MCU ──识别外层命令──┬─ 本机射频命令:直接操作自带 LoRa 模块(0x20/0x21/0x24/0x26)
│ └─ 转发命令:剥掉外层,把内层帧交给 LoRa 模块
▼ │ 空中 LoRa
LoRa 从机(燃气表)◄──────────┘ ← 内层帧 68…16
外层:BLE 帧(手机 ↔ 手抄机)
帧头 0x55 / 帧尾 0xAA,校验 = 累加和 % 256(handCommandHead=0x55 //手抄机数据头,handCommandEnd=0xAA //手抄机数据尾)
55 | HAND_CMD | LEN | [内层帧 或 本机参数] | SUM | AA
| HAND_CMD | 用途 | 数据区 |
|---|---|---|
0x01 | WALK 唤醒手抄机 | 内层帧(表命令 0x40) |
0x02 | 转发表命令 | 内层帧(LoRa 层整帧) |
0x20 | 写频道(本机) | 1 字节频道号 55 20 01 CH CRC AA |
0x21 | 读频道(本机) | 无数据 55 21 00 21 AA |
0x24 | 信道检测(本机) | 频道号;应答带 2 字节 RSSI |
0x26 | 信道扫描(本机) | 无数据;应答为占用位图 |
应答方向:短 ACK 直接处理(如唤醒应答 55 81 01 00 82 AA,0x81 = 对 0x01 的响应);长应答跨 BLE 包分片,OpenNotice 回调里 mm2.length != 6 时持续累积进 mm1 直到收满一帧。
内层:LoRa 帧(手抄机 LoRA 模块 ↔ 表,仅 0x01/0x02 转发时携带)
帧头 0x68 / 帧尾 0x16,独立累加和(tableCommandHead=0x68 //表通讯数据头,tableCommandEnd=0x16 //表通讯数据尾)——典型 CJ/T 188 类仪表帧封装。
68 | 表命令 | LEN | 表号(BCD,倒序) | [参数] | SUM | 16
| 表命令 | 含义 | 参数 |
|---|---|---|
0x01 | 单抄 | 0x05(时标) |
0x02 | 开阀 / 关阀 | 0x00 / 0x01 |
0x10 | 对表设频率(CHNSET) | 频道号 |
0x19 | 自动上传周期 | 值 + 单位(月0/日1/时2/分3) |
0x1B | 唤醒周期 | 频道号同时写入 |
0x25 | 不计量关阀时间 | 值 + 单位(0x26 为读取) |
0x40 / 0x41 | 走马唤醒 / 休眠 | — |
代码组装顺序(calcCommand,逐行印证你的描述)
- 先拼内层:
array.push(0x68)→ 表命令 → 长度 → 表号逐 2 字符倒序转 hex → 参数 → 内层和vf→0x16 - 再包外层:
splice(0,0, handDatalen)(手抄机数据长度 = 内层帧总长)→splice(0,0, handcommand)→ push 手抄机和hvf→ push0xAA→splice(0,0, 0x55) - 两层校验各自独立:
vf只覆盖内层字段,hvf覆盖 外层命令+长度+整个内层帧
关键结论
- 射频基础配置分两处:手抄机本机的频道用外层短命令(0x20/0x21/0x24/0x26)配;表端的频道用内层 0x10 命令经空中下发(CHNSET)——所以 App 能远程改表内 LoRa 模块的频率档位
- 信道检测应答的 RSSI 换算:
parseInt(字节,16) - 180得 dBm - 整个协议无加密、无防重放,校验仅为累加和——BLE 和 LoRa 两层都是明文可伪造
这也解释了为什么全包搜 "lora" 只有营销软文:射频概念在代码里完全以「手抄机命令 / 表通讯命令」两层呈现,与你的描述逐字节吻合。
这个看看呢 像是固件
todowrite
drbinary_run_sandbox
file 直接识别出:ARM Cortex-M 裸机固件,初始 SP 0x20001168、复位向量 0x08011508——STM32 系映射(Flash 基址 0x08000000),512KB 整,无文件系统(binwalk 零签名)。这正是我们预判存有射频配置的手抄机固件。先扫字符串和协议常量,再用 LoRa 频率寄存器换算反查信道-频率表。
drbinary_run_sandbox
命中要害:TXCLORA MODE %d SF12/125KHz——LoRa 模式字符串;还有 4G 模组 AT 框架、硬编码服务器 IP 47.101.44.61、版本串 Jul 24 2019。字符串只有 262 条,全量拉出来,并统计 GBK 中文串。
drbinary_run_sandbox
字符串直接命中射频配置核心:data downlink channel: %2d, %9d, SF%d——打印格式就是「信道号 + 9 位 Hz 频率 + SF」,说明固件里有一张信道→频率表。直接全镜像扫描 uint32 频率值(400–960 MHz)定位这张表。
drbinary_run_sandbox
前 0x500 字节是向量表区域,那些“频率”是重叠读取的噪声——频率表不是以 uint32 Hz 直存的。改用反汇编路线:定位 data downlink channel / TXCLORA 等调试串的代码引用,从打印处反推信道→频率的计算或表基址。
drbinary_run_sandbox
反汇编信道表打印函数(0x0800EA00–0x0800EC80,字面量池在 0xEC70)和 TX/RX 模式打印函数(0x0800DD80–0x0800DE40)。
drbinary_run_sandbox
输出太花哨且偏移不对。关掉颜色,精确反汇编引用信道表字符串的函数体(0x0800E980–0x0800EC70)和 TXCLORA 打印函数(0x0800DD80–0x0800DE40)。
drbinary_run_sandbox
这是信道扫描逻辑:全局结构 0x20004128,每信道 8 字节记录(base + ch*8),上限 100 信道(cmp r0, 0x64)。现在用 Python 精确解码Thumb LDR-literal 指令,定位真正使用信道表字符串的函数。
drbinary_run_sandbox
T1 编码只命中 TXCLORA/RXMODE。补 T2 宽编码解码,并直接反汇编这两个命中的函数。
drbinary_run_sandbox
TXCLORA 函数已解析出关键结构:模式 0=SLEEP、1=TXCW(20dBm)、2=TXCLORA(SF12/125kHz)、3=RX。栈上配置结构含功率 20(0x14)。反汇编 0xEC9E 之后的代码找信道表打印函数。
drbinary_run_sandbox
0x0800ED84 起是外层帧解析器(校验 0x55 帧头)——外层协议在固件侧坐实。信道表打印引用改用 capstone 全量线性反汇编扫描 movw/movt 对来定位。
drbinary_run_sandbox
drbinary_run_sandbox
定位成功:信道表打印函数是 fcn.0800e0a0。完整反汇编该函数,看信道→频率→SF 的数据来源。
drbinary_run_sandbox
抓到核心了。信道配置打印逻辑:唤醒信道 cfg[0x75]、数据信道 cfg[0x76],频率由 fcn.0800de18/de24 查询,SF 为固定常量(唤醒 SF10、上行 SF12、下行 SF10)。反汇编这两个频率查询函数看映射公式。
drbinary_run_sandbox
频率映射公式已从指令中提取。用 Python 精确核算常量,然后汇总报告。
drbinary_run_sandbox
常量全部整值命中——公式验证成立。汇总报告。
todowrite
手抄机固件分析报告(flash_full.bin)
这就是 App 通信对端的手抄机固件,你要的 LoRa 射频基础配置全部找到了,而且是精确公式,不是查表。
一、固件概况
| 项 | 值 |
|---|---|
| 架构 | ARM Cortex-M 裸机(STM32 系,Flash 基址 0x08000000,SP 0x20001168) |
| 容量 | 512 KB 整,无文件系统、无加密(熵分析混合态) |
| 功能栈 | BLE(对接 App)+ LoRa(对接表端)+ 4G(MYNETCON/APN/TCP 上报)+ AT 命令框架 |
| 硬编码 | 4G 上报服务器 47.101.44.61(阿里云),版本串 %d.%d.%d, Jul 24 2019 |
二、LoRa 射频基础配置(核心发现)
频率映射是运行时算术(0x0800DE18 / 0x0800DE24 两兄弟函数,各 6 条指令):
唤醒信道频率(Hz) = 470,300,000 + 信道号 × 400,000 ; 0x1C083560 + ch × 0x61A80
数据信道频率(Hz) = 470,100,000 + 信道号 × 400,000 ; 0x1C052820 + ch × 0x61A80
| 参数 | 值 | 证据 |
|---|---|---|
| 频段 | CN470 国标抄表频段(470.1 – 509.9 MHz) | 公式基址+步长 |
| 信道间隔 | 400 kHz,共 100 信道(扫描循环 cmp r0, 0x64) | 0x0800EA14 |
| 唤醒信道(wake) | 470.3 MHz 起,ch 号来自配置 cfg[0x75] | 打印串 wake up channel: %2d, %9d, SF%d |
| 数据信道(data) | 470.1 MHz 起,ch 号来自配置 cfg[0x76],上行与下行同频 | 打印串 data uplink/downlink channel |
| 唤醒信道 SF | SF10(打印常量);唤醒发送模式为 SF12/125kHz(TXCLORA MODE %d SF12/125KHz) | 0x0800E18E / 0x0800DBF2 |
| 数据上行 SF | SF12(表→手抄机,远距离弱信号方向) | 0x0800E1B6 movs r1, 0xc |
| 数据下行 SF | SF10(手抄机→表) | 0x0800E1E0 movs r1, 0xa |
| 调制 | LoRa(cmp r6,1 选 LoRa/FSK 分支,另有 TXCW MODE 20dBm 连续波测试模式) | 0x0800DE7E |
| 发射功率 | 20 dBm(配置结构 sp+0xA = 0x14,与 TXCW 字符串一致) | 0x0800DC2A |
| 前导码 | 8(配置结构 sp+0xC = 8) | 0x0800DC32 |
信道号→频率实例(与 App 侧完全对上):
- App 强制下发的
'50'频道 → 唤醒 490.3 MHz / 数据 490.1 MHz - 信道 0 → 470.3/470.1,信道 99 → 509.9/509.7 MHz
三、与 App 协议的闭环印证
- 外层帧解析器在 0x0800ED84:逐字节校验
0x55帧头进入状态机——App 的55 20 01 CH CRC AA(写频道)由这里接收 - 内层帧解析器在 0x0800DE9C:校验
0x68(CJ/T 188 表帧头)——你说的“LoRa 层给手抄机内 LoRa 与 LoRa 从机用”在此代码坐实 - 写入的信道号存入全局配置结构
0x20004128偏移0x75(唤醒)/0x76(数据),由fcn.080081B4从配置区读出 - 信道扫描结果表:结构体偏移
0x19C起,每信道 8 字节 × 100(uint32 计数 + RSSI 等字节),App 侧-180换算 dBm
四、完整图景(三个环节全部打通)
App(JS): 频道号 '50'
│ BLE 55 20 01 32 CRC AA (外层/手抄机帧)
▼
手抄机固件: 配置 cfg[0x75/0x76] = 50,持久化
│ 频率计算: wake 490.3MHz / data 490.1MHz, 上行SF12 下行SF10, 20dBm, BW125k
▼ LoRa 空中接口 (CN470)
表端: 68 … 16 内层帧 (CJ/T 188: 抄表0x01/开关阀0x02/设频0x10/唤醒0x1B…)
结论:LoRa 射频基础配置不在 App 也不在查表里,而是手抄机固件中三条指令就能还原的线性公式——基址 470.3/470.1 MHz、步进 400 kHz、100 信道、SF10/12/10(唤醒/上行/下行)、20 dBm、125 kHz。信道号是唯一可配置项,经 BLE 外层帧下发。
如需继续,可以深挖:配置区在 flash 中的具体页位置与校验方式(改信道掉电保存逻辑)、SX127x 寄存器级驱动(0x0800A49A 一族)、或 4G 上报协议(47.101.44.61 的包格式)。
可是完整的lora配置远不止这些呀 ,纠错率,同步字等等,手抄机有rfpa 功率是写死的20dbm吗 (附上我重写的固件配置参数)
========== 唤醒 TX 参数 (沿用 v1 已验证值) ==========
W_SF = 10 W_BW = 4 W_CR = 1 W_SYNC = 0x12 W_PREAMBLE = 38 W_CRC = 1 W_IQ = 1 W_HEADER = 1 # 隐式报头 W_PAYLOAD = 10 # 唤醒帧固定长度 10 字节 W_POWER = 3 # 发射功率 dBm (默认; FEM 输入上限 3dBm, rf 发送时由运行时参数 tx_power 覆盖)
========== 读表 TX 参数 (沿用 v1 已验证值) ==========
R_SF = 10 R_BW = 4 R_CR = 1 R_SYNC = 0x12 R_PREAMBLE = 8 # 业务帧(读表/休眠/设频)前导码: 表端约定 8 符号, 曾用 16 收不到(2026-08 现场实测); 唤醒另用 W_PREAMBLE=38 R_CRC = 1 R_IQ = 1 R_HEADER = 0 # 显式报头 R_PAYLOAD = 14 # 读表帧最大长度 14 字节 R_POWER = 3 # 发射功率 dBm (默认; rf 发送时由运行时参数 tx_power 覆盖)
问得专业——我上一轮只还原了频率/SF/功率三个维度,寄存器级的 CR、同步字、IQ、报头、前导码确实还没挖。现在把射频驱动层完整解开,顺便回答 RFPA/20dBm 是否写死的问题。
todowrite
drbinary_run_sandbox
0x0800A49A = SPI 寄存器写(reg|0x80,带 CS 控制)——SX127x 式 8 位寄存器映射确认。现在解开核心的 fcn.08009EC8(配置结构体→寄存器装载)和读函数。
drbinary_run_sandbox
0x08009EC8 是字段分发器:+0→频率、+4→SF、+5→?、+6→?、+7→?、+8/9→?,还写 RegLna(0x0C)=0x23(LNA 增益提升)。注意 +0xA(=20) 并未被此函数使用——功率在别处设置。批量解开各字段 setter 和 TX 函数。
drbinary_run_sandbox
过滤太狠了,直接完整输出这几个小函数。
drbinary_run_sandbox
SX127x 寄存器映射全部坐实:0x06/07/08=Frf、0x1E=SF(含 SF6 特判 0x31/0x37)、0x1D bits7:4=BW code 7 = 125kHz、bit1:0=CR、bit0=报头、0x22=PayloadLength、0x0C=LNA。注意测试模式的 +0xA(20)/+0xC(8) 尚未被 setup 消费——看 setup 函数后半段和 LDRO 表函数,并全固件扫描所有寄存器写。
drbinary_run_sandbox
0x08009F28=TX 启动(写 0x22 长度、0x0E/0D FIFO、0x11 中断掩码、0x40 DIO0=TxDone);0x0800a274=LDRO 判定表(SF×BW → RegModemConfig3)。现在全固件扫描所有 SPI 寄存器写的 (reg,val) 对,重点找 0x33(IQ)、0x39(同步字)、0x09(PaConfig)、0x4D(PaDac)。
drbinary_run_sandbox
capstone 输出格式问题(movs r0, 0x1e 无 # 号)。修正后重扫。
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
寄存器全景出来了,几个关键点:0x39(同步字)全固件零写入→保持默认 0x12(与你 v1 的 W_SYNC=0x12 一致);0x33 有 InvertIQ 读写函数;0x4D(PaDac) 在 0x080052A0 里被改——这个函数在每次 setup 时以频率参数调用,很可能就是 PA 增益路径。解开它和 PaConfig 写入点。
drbinary_run_sandbox
0x080052A0 是标准 Semtech 风格 SetTxPower:RFO 路径钳位 0–15、PA_BOOST 路径 ≥18dBm 时 PaDac=0x87 增益提升并 OutputPower=p-5、上限 20dBm。现在看它的收尾、0x08005344(PA 引脚判定)、PaConfig 写入点、InvertIQ 函数,以及 fcn.0800de3c 的调用者传参——这直接回答功率是否运行时可变。
drbinary_run_sandbox
这函数是标准 Semtech SetTxPower 结构(PaDac 0x84/0x87、p-5、钳位),但 setup 传给它的参数疑似频率——必须精确核实。完整反汇编 setup 函数与 0x08009E60(图像校准+PaConfig)函数,解 InvertIQ 设置器。
drbinary_run_sandbox
关键分歧点:0x080052AC 处 movs r5, r0 是把功率覆盖成 0(功率写死)还是我读错(实为 movs r5, r1 功率来自参数)——直接看原始字节。同时查 InvertIQ 函数(0x0800A300)和数据配置函数(0x0800DE3C)的调用者。
drbinary_run_sandbox
原始字节确认:0x000D=movs r5, r1(0x52A6 加载功率)→ 但 0x0005=movs r5, r0(0x52AC 又被 r0=0 覆盖)——功率参数是死代码,PaConfig 恒为 0x70(RFO, MaxPower=7, OutputPower=0, PaDac=0x84)。这就是 RFPA 架构的证据。最后看业务发送配置函数的调用者传参(区分唤醒帧/读表帧的 SF/报头/前导码)。
drbinary_run_sandbox
业务/唤醒两套 TX 配置的实参全部拿到(业务:前导8/SF10/BW7/显式;唤醒:前导0x27/SF10/BW7/隐式/定长9)。最后补 IQ 模式 4/5 的寄存器位定义。
drbinary_run_sandbox
todowrite
你说得对,之前只挖了三层。这轮把射频驱动整个解开了(SPI 写函数 0x0800A49A = reg|0x80,RMW 函数 0x0800A4D0,配置装载器 0x08009EC8,业务发送 0x0800DE3C)。以下是与你的重写参数逐项核对的结果,有几处差异需要你注意。
一、原固件完整 LoRa 配置(寄存器级还原)
芯片:SX127x 系(8 位 SPI 寄存器,Frf@0x06-08、MCFG1@0x1D、MCFG2@0x1E、PaConfig@0x09、PaDac@0x4D、ImageCal@0x3B 带启动位轮询)。
配置结构体布局(fcn.08009EC8 参数,fcn.0800DE3C 填充):
| 偏移 | 字段 | 装载到 |
|---|---|---|
| +0 (u32) | 频率 Hz | Frf 0x06/07/08(浮点换算) |
| +4 (u8) | SF | MCFG2 0x1E bits7:4(SF6 特判 DetOpt 0x31=0x05/0x37=0x0C) |
| +5 (u8) | BW code | MCFG1 0x1D bits7:4,7 = 125kHz |
| +6 (u8) | CR | MCFG1 bits3:1,恒写 1 = 4/5 |
| +7 (u8) | CRC | MCFG2 0x1E bit2,业务/唤醒路径恒写 0 |
| +8 (u8) | 报头 | MCFG1 bit0:0=显式 / 1=隐式 |
| +9 (u8) | 定长 | RegPayloadLength 0x22(仅隐式用) |
| +0xA (i8) | 功率 | 死代码(见下) |
| +0xC (u16) | 前导码 | RegPreamble 0x20/0x21 |
| (计算) | LDRO | 0x26 bit3,按 SF×BW 表(0x0800A274) |
两套实际发送参数(0x0800E2AA 读表下行 / 0x0800E310 唤醒,均为运行时传参):
| 参数 | 唤醒 TX | 读表 TX(下行) |
|---|---|---|
| 频率 | 470.3 + ch×0.4M | 470.1 + ch×0.4M |
| SF | 10 | 10 |
| BW | 7 (125k) | 7 (125k) |
| 前导码 | 0x27 = 39 | 8 |
| 报头 | 隐式 | 显式 |
| 定长 | 9 字节 | 运行时(读表帧 14) |
| CR | 4/5 | 4/5 |
| CRC 位 | 0 | 0 |
| IQ | mode4 | mode4 |
| LDRO | 关 | 关 |
注意:TXCLORA MODE SF12/125KHz 是产测模式(模式分发 0=SLEEP/1=TXCW/2=TXCLORA/3=RX),不是业务配置——真正业务的 SF 是 10,和你的 W_SF/R_SF=10 一致。
同步字:全固件对 0x39 零写入 → 保持默认 0x12(私有网络字),与你 W_SYNC=0x12 一致。
IQ(0x33):mode4 = (0x33 & ~0xBE) | 0x40 = bit6=1(TX 反转)、bit0=0(RX 正常),两条发送路径都用 mode4——网关角色标准配法,与你的 W_IQ/R_IQ=1 等价。
LDRO:SF10+BW≥7 → 关。这不符合 Semtech 官方规则(SF10@125k 符号宽 16.38ms 应开)——原厂就是这么选的,你重写时 SX126x 侧也必须关,否则表端收不到,这点你的参数表里没列。
二、RFPA 与功率:回答你的问题
是写死的,而且写死的方式比较特别——0x080052A0(SetTxPower(freq, power))开头有两条死指令:
0x52A6: movs r5, r1 ; r5 = 传入功率 (0x14=20, 来自 cfg+0xA)
0x52AC: movs r5, r0 ; r0=0 → 功率被无条件覆盖成 0(编译器死存储)
后续钳位(0–15 / ≥18 加 PaDac=0x87、p-5)全部作用于 0,最终每次发射:
PaConfig = 0x70 ; PaSelect=0 → RFO 引脚, MaxPower=7, OutputPower=0
PaDac = 0x84 ; 无 +20dBm boost
SX127x RFO 路径输出 Pout = 10.8 + 0.6×MaxPower - (15-OutputPower) = 15-15+0 ≈ **0 dBm**。
结论:芯片引脚只出 ~0dBm,20dBm 是外置 RFPA/FEM 的系统级指标(配置里的 0x14=20 从未被消费,字符串里的 "20dBm" 指系统输出)。这也解释了你 v1 注释「FEM 输入上限 3dBm」——整个架构就是低功率芯片 + 外放。
三、与你的重写配置的差异清单(互通风险点)
| 项 | 原固件 | 你的 v1 | 风险 |
|---|---|---|---|
| 唤醒前导码 | RegPreamble = 39 (0x27) | 38 | 差 1。TX 侧前导码偏短一般不影响 RX 同步(RX 只要求 ≥ 自身设定),但你既然实测过 16 收不到,建议确认表端 RX 阈值后再定 |
| 唤醒定长 | RegPayloadLength = 9,TX 也写 9 | 10 | 隐式报头下定长必须与表端 RX 完全一致,差 1 字节会错位解析——除非你的新唤醒帧格式本就不同,否则改为 9 |
| CRC 位 | MCFG2 bit2 = 0(所有 TX) | W_CRC=R_CRC=1 | SX127x 该位语义与 SX126x 的 crcOn 不完全对等;若表端按原网关习惯(CRC 关)收帧,你开 CRC 会多出 2 字节。建议先抓原网关空口帧确认 wake 帧是否带 CRC |
| 功率 | 芯片 RFO ~0dBm 进 FEM | 3dBm | 差 3dB 进 FEM;若 FEM 是固定增益则链路预算差 3dB——可接受但建议核对 FEM 输入规格 |
| LDRO | 关(SF10/125k) | 未列 | 必须显式关 |
| SF/BW/CR/SYNC/IQ/报头 | 10 / 125k / 4/5 / 0x12 / TX反RX正 / 唤醒隐式+业务显式 | 一致 | ✅ |
一句话:你重写的配置在调制参数上与原固件对齐,必须修正的是唤醒定长(9 vs 10)和 LDRO(显式关闭),建议复核的是唤醒前导码(39 vs 38)与 CRC。功率架构确认了你的判断:RFPA 承担实际发射功率,芯片侧原厂锁死在 RFO/0dBm,不可运行时调节。
目前我提供给你的这个配置能跟从机通讯是不是说明crc和ldro 不用调换吗
问得好——你这句话其实提供了一个关键的经验性证据:你的 v1 配置已经和从机通上,说明空口参数(包括 CRC 的有无、LDRO 的实际状态)已经和表端匹配了。不过回答前我得先修正上一轮一个极性不确定点——把 CRC 设置函数 0x0800a252 的完整分支解出来。
drbinary_run_sandbox
是的——实测结果就是空口层的最终裁判,两项都不用换。 但这里有个值得说透的逻辑,顺便修正我上一轮的一个错误。
一、先修正上一轮的 CRC 极性错误
上一轮我写「CRC 位业务路径恒写 0(CRC 关)」——极性读反了。刚补解了完整函数 0x0800a252:
cfg+7 == 0 → RMW(0x1E, mask 0xFB, value 4) = 置位 bit2
cfg+7 != 0 → RMW(0x1E, mask 0xFB, value 0) = 清零 bit2
业务/唤醒路径传的是 cfg+7=0 → 该位实际是置 1 的,不是清零。另外位语义也要修正:TXCW 测试路径对同一寄存器置 bit3(=TxContinuousMode,连续波测试的标配),印证这是 SX1276 式映射——bit2 更可能是 AgcAutoOn 而非 CRC 位,真正的 TX CRC 行为在隐式模式下由芯片侧决定,我之前把它当成 CRC 开关是过度解读。
二、为什么“能通讯”就等于“不用换”
从机(表端)是固定设备,它的 RX 参数不可协商。你的配置能和从机完成双向通讯,意味着:
- CRC:你 TX 附加的 2 字节 CRC 与表端 RX 的校验预期一致(CRC 不匹配 = 帧长错位或校验失败,根本通不了)
- LDRO:你的实际运行状态与表端解码预期一致(LDRO 不匹配 = 符号边界错乱,同样是完全解不出)
实测兼容 > 一切静态推断。固件逆向的价值是还原“原厂怎么配的”,但原厂配置只是“能通”的一种实现,不是唯一实现——你的参数已是另一组被现场验证的兼容值。
三、唯一的建议:LDRO 显式落盘为 0
不用改成开,但建议把 LDRO = 0 显式写进你的配置文件并注释原因:
- SF10 @ 125kHz 符号宽 16.38ms,正好压在 Semtech 官方 16ms 阈值线上——不同 SX126x 库的自动规则不同(有的 SF≥10 就开,有的 SF≥11 才开)
- 原网关固件在此组合下是明确关闭的(
0x0800a274:SF10 且 BW≥7 → 0) - 你现在能通说明你的代码当前实际也是关;但如果哪天换库或升级驱动,默认规则可能静默翻转它,届时故障形态是“突然全断”,很难联想到这里
CRC=1 同理——它已经过验证,锁死即可,无需再对齐固件里的任何位。
结论
| 项 | 动作 |
|---|---|
| CRC = 1 | 保持,不动(实测验证 + 原网关该位实为置 1,双向吻合) |
| LDRO | 实际值不用换(=0),建议显式写入配置,注释“SF10/125k 原厂即关,勿按库默认开” |
剩下真正需要盯的差异还是之前那两条:唤醒定长(你 10 / 原厂 9)和唤醒前导码(你 38 / 原厂 39)——既然你实测能通,说明表端容忍或你的帧格式本就是新约定,同样以实测为准,但建议在配置注释里记下与原厂的偏移,便于以后抓空口帧比对。
逆向者理解有偏差,470.3 / sf12是从机回复使用的配置(双通道 ) ,但逆向者一直在以sf10的立场分析
这个纠偏很关键——我确实一直把打印里配对的 (470.3, SF10) 当成了唤醒信道的完整语义,没有追问 SF12 在唤醒信道上的角色。fcn.0800de3c 还有两个我没解的调用者(0x800E370 / 0x800E500),如果其中之一是「唤醒信道 + SF12」的配置,你的模型就坐实了。
drbinary_run_sandbox
纠正 470.3确实是唤醒 但是sf是sf10 ; 我指的是从机回复 用的是470.1, sf12
========== 唤醒 TX 参数 (沿用 v1 已验证值) ==========
W_SF = 10 W_BW = 4 W_CR = 1 W_SYNC = 0x12 W_PREAMBLE = 39 W_CRC = 1 W_IQ = 1 W_HEADER = 1 # 隐式报头 W_PAYLOAD = 10 # 唤醒帧固定长度 10 字节 W_POWER = 3 # 发射功率 dBm (默认; FEM 输入上限 3dBm, rf 发送时由运行时参数 tx_power 覆盖)
========== 读表 TX 参数 (沿用 v1 已验证值) ==========
R_SF = 10 R_BW = 4 R_CR = 1 R_SYNC = 0x12 R_PREAMBLE = 8 # 业务帧(读表/休眠/设频)前导码: 表端约定 8 符号, 曾用 16 收不到(2026-08 现场实测); 唤醒另用 W_PREAMBLE=38 R_CRC = 1 R_IQ = 1 R_HEADER = 0 # 显式报头 R_PAYLOAD = 14 # 读表帧最大长度 14 字节 R_POWER = 3 # 发射功率 dBm (默认; rf 发送时由运行时参数 tx_power 覆盖)
========== RX 参数 (燃气表 SF12 回复) ==========
RX_SF = 12 RX_BW = 4 RX_CR = 1 RX_SYNC = 0x12 RX_PREAMBLE = 8 RX_CRC = 1 RX_IQ = 1 RX_TIMEOUT_SEC = 5 # 扫描流程 RX 监听超时 (秒) - 逐频道找表用, 保持5s余量 RX_WINDOW_SEC = 2.5 # 读表 RX 监听窗口 (秒): 应答ToA≈1.3s + 表端处理 + 余量, 替换原固定5s RX_RETRY_COUNT = 1 # 读表帧重发次数上限 (0=不重发; 1=最多2次机会) RX_RETRY_INTERVAL_MS = 80 # 重发间隔 (ms): 错开多径衰落凹陷
你再分析固件里的关于rx的 ,目前的目的就是想提高通讯成功率
双通道模型现在完全对上了——刚解出的两个 RX 配置调用者正是证据:0x0800E35A 和 0x0800E4EA 都是数据信道 470.1(fcn.0800de24)+ SF12(r2=0xc)+ 显式报头 + 最大定长 0x40,随后调 0x08009f76 进 RX。你的 RX_SF=12 与固件完全一致。
现在围绕「提高通讯成功率」深挖固件 RX 侧的四个关键机制:RX 窗口时长、CRC 错误处理、重发逻辑(字符串 gw ack retry)、以及唤醒→收听切换时序。
drbinary_run_sandbox
IRQ 分发逻辑已明确:RxDone→回调3、PayloadCrcError→回调4(CRC 错误帧仍带上传处理——宽松接收策略)、各事件置标志 0x200057a7。现在定位 gw ack retry 的重发逻辑。
drbinary_run_sandbox
drbinary_run_sandbox
这段是 4G 任务代码混在同一个池区。用 T1/T2 字面量扫描精确定位引用 gw ack retry(池 0xFD5C)的指令。
drbinary_run_sandbox
drbinary_run_sandbox
drbinary_run_sandbox
last frame cs error 的引用在 0x0800EE74——重发状态机多半就在这个函数里。反汇编该区域。
drbinary_run_sandbox
drbinary_run_sandbox
todowrite
双通道模型已被固件代码完全证实,你的 RX 参数与原网关逐项吻合。以下是围绕「提高通讯成功率」的固件 RX 侧分析结论。
一、修正后的完整双通道模型(全部有代码坐实)
| 信道 | 方向 | SF | 前导码 | 报头 | 定长 | 代码位置 |
|---|---|---|---|---|---|---|
| 470.3+ch×0.4M | 网关 TX 唤醒帧 | 10 | 39 | 隐式 | 9 | 0x0800E32E |
| 470.1+ch×0.4M | 网关 TX 命令(下行) | 10 | 8 | 显式 | 运行时 | 0x0800E2CA |
| 470.1+ch×0.4M | 从机回复(上行)→网关 RX | 12 | 8 | 显式 | 上限 0x40=64 | 0x0800E370 / 0x0800E500 |
RX 配置证据:两处 RX 都是 freq=fcn.0800de24(cfg[0x76])(数据信道)+ r2=0xc(SF12) + r3=7(125k) + [sp]=0(显式) + [sp+4]=0x40(最大 64 字节),随后 fcn.08009f76(1,0) 进 RX 连续模式。你的 RX_SF=12 / RX_PREAMBLE=8 / RX_BW=4(=125k) / 显式 —— 全部一致。
二、固件 RX 策略的三个关键机制(对你重写最有价值)
1. CRC 错误帧不丢弃——宽松接收 + 内层校验兜底
IRQ 分发器(0x0800A700 区)中 PayloadCrcError 分支:清标志后仍读出 FIFO 数据并回调上层(回调类型 4)。原理:内层 CJ/T188 帧(68…16)自带累加和校验,外层 LoRa CRC 挂掉但内层完好的帧可以救回。
→ 可直接借鉴的成功率优化:SX126x 报 CRC error 时不要直接丢帧,读出 payload 后用内层累加和二次验证,通过则接受。弱信号场景(表在信号边缘)这能挽回相当比例的帧。
2. RX 连续模式 + 任务级关窗,而非芯片单次 RX
fcn.08009f76(1,0):清 FIFO、清 IRQ(0xE0)、开掩码(0x0F)、DIO0 映射 RxDone,然后进 RX Continuous(r1=0 → mode 5)。窗口由上层定时器关闭:数据信道应答窗 6 秒(movw 0x1770 @0x0800E50C),唤醒后阶段 10 秒(0x2710 @0x0800E348)。SymbTimeout=0x40(64 符号) 只对 RX single 生效,连续模式下形同虚设。
→ 你的 RX_WINDOW_SEC=2.5s 偏紧。SF12 应答 ToA≈0.9–1.3s 没问题,但主要变量是表端从 WOR 醒来后的处理延迟(RTC 恢复、晶体稳定、执行抄表/开关阀)。原厂给 6s/10s 说明现场见过慢应答。建议首应答窗 ≥4s,或学原固件用「连续 RX + 静默超时」模型,比固定短窗更稳。
3. 会话级重试,而非快速连发
固件日志串 gw ack retry / gw data retry 表明重试是完整周期级的:发命令 → 等满 RX 窗口 → 超时才重发。
→ 你的 RX_RETRY_INTERVAL_MS=80 有一个具体风险:若首帧其实已被表端收到,80ms 后的重发会撞上表端的应答发射(表端收到命令后立即切 TX,SF12 应答 ToA 约 1 秒,这个窗口内表端不收帧)。建议:重发只在 RX 窗口整体超时后进行;或把间隔拉到 > 应答窗。你的 RX_RETRY_COUNT=1 本身没问题。
三、成功率还可以从固件这两个能力借力
1. 逐频道找表(对应你的 RX_TIMEOUT_SEC=5):固件的扫描流程是 100 信道逐个试(每信道 8 字节记录,0x20004128+0x19C 起),且 0x0800E4D0 处显示 wake/data 频道号在运行时从状态变量刷新——即原厂支持跑飞后换频道恢复。你的逐频道找表流程(5s/频道)方向正确;配合固件自带的 ChkCH(信道 RSSI 检测,-180 换算 dBm)可以优先扫信号最好的频道。
2. IQ 方向确认:网关侧固定「TX 反转 / RX 正常」(0x33 bit6=1, bit0=0)。你的 RX_IQ=1 按你自己的约定若表示「RX 不反转、对端正常 IQ」则正确——既然实测能收到回复,就是对的;只需在注释里写明,防止误读为「RX 反转」。
四、汇总:参数对照 + 行动清单
| 项 | 原固件 | 你的 v1 | 动作 |
|---|---|---|---|
| RX 频道/SF/BW/CR/报头/前导 | 470.1 / 12 / 125k / 4/5 / 显式 / 8 | 一致 | ✅ 不动 |
| RX IQ | RX 正常 | RX_IQ=1(约定等效) | ✅ 注释写明 |
| CRC 错误处理 | 宽松接收+内层校验兜底 | 未提及 | ★ 增加:CRC 坏帧二次校验后接受 |
| RX 窗口 | 6s(连续 RX+任务关窗) | 2.5s | ★ 放宽到 ≥4s 或改静默超时模型 |
| 重发间隔 | 会话级(窗口超时才重发) | 80ms 连发 | ★ 改为窗口超时后重发 |
| 换频道恢复 | 支持(运行时刷频道号+扫描) | 5s/频道逐扫 | ✅ 保留,可加 RSSI 优先排序 |
这三项 ★ 都是把「帧收不到」的概率分摊到不同失效模式上:宽松接收救误码,长窗口救慢应答,会话级重试救碰撞——正是原厂在 6s/10s 窗口 + CRC 宽松处理里体现的现场经验。
RX_RETRY_INTERVAL_MS 是rx接收超时后的静止时间留给mcu的,并不是从rx开始接收计算
参数 值 作用 RX_WINDOW_SEC 2.5s 每次发完帧后 监听回复的时长 RX_RETRY_COUNT 1 最多重发几次 (1 = 首发 + 重发 1 次 = 共 2 次机会) RX_RETRY_INTERVAL_MS 80ms 两次发射之间的间隔
明白,这个语义澄清很关键——那我上一轮对 80ms 的担心(重发撞上表端应答)不成立,撤回。你的实际时序是:
TX#1 ──→ RX 监听 2.5s ──→ 超时 ──→ 80ms(MCU 静止/善后)──→ TX#2 ──→ RX 监听 2.5s ──→ 结束
├────── 约 5.08s/轮 ──────┤
80ms 只发生在窗口已经整体超时之后,此时表端要么已在 2.5s 内回完(不会撞),要么根本没收到(还在等唤醒/收听,也不撞)——没有我之前说的「重发砸进表端应答」的问题。这个模型和原固件的会话级重试(发→等满窗→超时→重来)是同构的,只是你的窗口更紧、次数显式化为 1。
修正后仍然成立的建议
-
CRC 宽松接收(最大的一块收益,与时序无关):原固件对
PayloadCrcError帧仍读出并交上层用内层 68…16 累加和二次验证。你可以在 RX 窗口内照常收,CRC 坏帧不丢、内层校验通过就采纳——这个和你的重发机制完全正交,纯粹多捞帧。 -
2.5s 窗的残余尾部风险很小但存在:若某块表的应答恰好在窗口关闭后才起跳(2.5~2.7s 之间),会被 80ms 后的 TX#2 干扰掉(SF10 命令帧 ToA 约 190ms 的干扰窗)。这个概率很低,不值得为它加长窗口——但有一个例外值得分档:开关阀命令。表端执行机械动作的处理时长和抄表完全不是一个量级,原固件里存在 10s 级的等待阶段(
0x2710)。建议给阀控命令单独一档窗口(4~6s),抄表维持 2.5s——两行配置的事,避免阀控类成功率被抄表窗口拖累。 -
换频道恢复:你的逐频道找表(5s/频道)方向和原固件一致,保留即可;有余力再加「按 ChkCH 式 RSSI 排序频道」。
更新后的结论表
| 项 | 判定 |
|---|---|
| RX_RETRY_INTERVAL_MS=80 | ✅ 语义合理(窗口后 MCU 静止),无碰撞问题 |
| RX_RETRY_COUNT=1(共 2 次机会) | ✅ 合理,与原固件会话级重试同构 |
| RX_WINDOW_SEC=2.5(抄表) | ✅ 保留;★ 阀控命令建议单独放宽到 4~6s |
| CRC 宽松接收 + 内层校验 | ★ 仍是最值得加的一项,与现有重发机制互补 |
| RX 参数(SF12/470.1/125k/显式/前导8) | ✅ 与固件完全一致 |
一句话:你的重发时序设计没问题,剩下能实打实提升成功率的就两件事——CRC 坏帧二次校验和阀控命令的独立长窗口。
坏帧很少,目前就是信号强度,你说旧固件是0 dbm 全靠rfpa 我用ra01sp 带pa的 用的3dbm fem
坏帧很少,目前就是信号强度,你说旧固件是0 dbm 全靠rfpa 我用ra01sp 带pa的 用的3dbm ;效果很不理想
坏帧少、瓶颈在信号强度——那排查重心就从「协议层」转到「射频链路预算」。先确认两个硬件事实:RA-01SP 的主控芯片身份,和它对 SF12@125k 的支持情况(这直接决定接收灵敏度极限)。
websearch
RA-01S-P 的身份查清了,这直接改变排查方向——你的功率架构没有问题,问题几乎可以肯定在集成细节上。
一、模块身份与官方推荐值
RA-01S-P = SX1268 + 板载 FEM(PA+LNA)+ RF 开关,关键规格:
- SX1268 完整支持 SF5–SF12 @ 125kHz(官方文档明确列出与 LLCC68 的区别——你不用担心 SF12@125k 的兼容性)
- 官方原文:FEM 输入不得超过 +3dBm,建议 SX1268 输出配置 0–3dBm ——你设 3dBm 正是官方推荐值
- 带板载 FEM 后最大 +29dBm、灵敏度 -137dBm@SF10/125k
也就是说:你的 3dBm→FEM 链路 = 原手抄机 0dBm→RFPA 的同款架构,且模块标称能力(+29dBm)远超原机(~20dBm)。功率链路理论上只强不弱。「效果很不理想」说明 FEM/开关/校准层没有正常工作。
二、按嫌疑度排序的检查单(SX126x 专属命令码)
1. RF 开关控制(头号嫌疑)
RA-01S-P 的 TX/RX 收发切换需要控制:要么 SetDio2AsRfSwitchCtrl(0x9D, 1) 让芯片 DIO2 自动驱动开关,要么 MCU 手动控 GPIO。开关没切对 = 发射功率没上天线 / 接收不走 LNA——两个方向同时拉胯,症状完全吻合。RadioLib 等成熟库默认会调它,自写驱动极易漏。
2. TCXO + 校准(SF12 头号杀手)
SetDIO3AsTcxoCtrl(0x93):模块用 DIO3 供 TCXO,电压参数按模块(1.6/1.7/1.8V)配错或漏配 → 频偏- 随后必须
Calibrate(0x89, 0x7F)(STANDBY_RC 下)+CalibrateImage(0x98, 0x75, 0x81)(470–510MHz 段专用参数) - 频偏对 SF12 最致命(符号最窄,容频偏 ±~2kHz 级),SF10 还能勉强解、SF12 直接解不出——如果表现为「收得到帧但远距离 SF12 应答大量丢失」,这是头号原因
3. RX 增益模式
SetRxGain(0x96, 0x94) = boosted 增益(灵敏度 +2~3dB),0x95 是省电模式。自写驱动默认常落在省电档。
4. 供电 带 FEM 发射时模块要 >700mA、建议按 1A 供电。3.3V 轨弱、去耦不足 → 发射瞬间电压塌陷 → 实际输出远低于标称。测一下发射瞬间电流:若只有百来 mA,FEM 根本没开足。
5. 天线 确认是 470–510MHz 段天线(不是 433 的),IPEX/焊点/馈线无损。
三、一次 A/B 定位测试(把问题钉死在哪一段)
同一位置、同一块表,原手抄机 vs 你的网关各跑一轮抄表,记录每帧的 RSSI/SNR(SX126x GetPacketStatus):
| 现象 | 结论 |
|---|---|
| 你的 RSSI 比原机低 5–10dB | RX 前端问题:开关没切到 LNA / LNA 未使能 / 天线链路 |
| RSSI 接近,但解码成功率差 | 频偏/校准问题(TCXO 配置)→ 查第 2 项 |
| 表端对原机有应答、对你的网关无应答 | TX 问题:开关/供电 → 查第 1、4 项,或有频谱仪直接对比两机发射 |
| 两者都差 | 电磁环境/频道被占 → 用原固件的信道扫描思路换频 |
另外用 GetDeviceErrors(0x17) 读一次错误状态,XOSC/PLL 启动失败会直接报在这里。
结论
3dBm 不是问题——它是 RA-01S-P 的官方推荐驱动值,模块本身的能力(+29dBm、SF12@125k 全支持)绰绰有余地覆盖原手抄机。效果差的重点排查顺序:RF 开关控制 → TCXO/频偏校准 → RX 增益模式 → 供电,然后用 RSSI/SNR 对照测试把故障段钉死。这类模块「参数都对但忘了 0x9D 和 0x93 两条命令」是最常见的翻车方式,值得先查。
新的逆向 ,老一代的抄表软件 fsk,频点使用473,请逆向出具体配置和程序逻辑
?
工具调用似乎中断了,重试一下:
工作区/t1.bin 传上来了但是空文件(0 字节,md5 = d41d8cd9… 是空文件的特征),应该是上传中断了。
请重新上传一次。收到后我会按老套路逆向:
- 识别架构(大概率还是 STM32 裸机固件,Flash 0x08000000 起)
- 定位 473 MHz 频点——FSK 模式下找 473 xxx xxx Hz 的 Frf 常量(类似上次
0x1C083560的算式基址)或三字节寄存器拆分 - FSK 配置全解——相较 LoRa,FSK 还多几项:FSK 偏移(deviation)、比特率(bitrate)、同步字(多字节)、whitening/曼彻斯特、packet format(可变长/定长、地址过滤、DC-free)
- 程序逻辑——收发状态机、表帧协议(应该还是 68…16 CJ/T 188 内层)、换频/重试策略
另外有个问题可以先告诉我,能省一轮探测:这批老表/老网关的射频芯片是什么型号? 如果是 SX127x 系(FSK/OOK 模式),寄存器映射和上次一样,直接按 0x06-0x08 频率、0x02 Bitrate、0x07 Fdev 顺藤摸瓜;如果是 CC1101/A7139 之类国产或 TI 老芯片,寄存器表完全不同。不知道也没关系,从 SPI 写序列反推也行。
大概率是sx1278 不确定
还是空的——t1.bin 两次都是 0 字节(md5 d41d8cd9… 是空文件哈希),第二次上传并没有把内容写进来,本地时间戳还停留在第一次的 12:16。
这不是固件本身的问题(比如加密或擦除),而是上传通道没把字节送进来。请按下面顺序排查:
- 先确认源文件没传错:在你电脑上看一下
t1.bin的大小——如果它本身就是 0 KB,说明读出来的时候就失败了(有些烧录器/读保护芯片会给出空文件;SWD 读保护开着的芯片读出来就是全空或全 0xFF,但要区分:全 0xFF 文件大小不会是 0) - 换文件名重传:改个名比如
t1_fw.bin再上传一次——同名覆盖有时会撞到占位文件不更新的问题 - 如果源文件确实是空的:说明是读取环节的问题,重新 dump 一次(校验 dump 工具的日志),注意读保护的芯片需要先解除 RDP
传上来后我立刻开工。如果是全 0xFF 也发过来,那也是有信息量的(说明读保护开了)。
我再试试 我把后缀文件名去掉了
还是 0 字节——t1 - 副本(上传后名字变成了 t1+-+副本)也是空的,时间戳 12:33。这已经是第三次空文件了,基本可以排除偶然。
两种可能,用这个办法一次区分开:
1. 先在你电脑上确认源文件本身的大小
- Windows:右键文件 → 属性,看「大小」是不是 0 KB
- macOS/Linux:
ls -la t1* - 如果源文件就是 0 KB → 问题在 dump 环节,重新读一次芯片(顺便留意读保护 RDP 是否开着)
2. 如果源文件有正常大小(几百 KB),那就是上传通道的问题,试这几个变通:
- 把它压成 zip 再传(
t1.zip)——上传器对无扩展名/无 MIME 的裸 .bin 文件最容易失败,zip 能改变文件类型识别 - 换个浏览器或换网络环境再传
- 传个任意小文件测试通道(比如一张图片),确认是不是所有文件都传不进来
顺带说明:你之前传的 flash_full.bin(512KB)当时是完整的,说明这条通道曾经工作正常——所以现在第一个要怀疑的就是你手里这个 t1.bin 本身是空的。先查第 1 步。
好吧 备份的不是0kb了
这次大小传到了(164,832 字节),但内容还是没到——读出来是全零(python 能读,16 万字节里非零字节数 = 0),普通 cat 直接报 I/O 错误。也就是说上传通道这次把「文件大小」写进来了,但「文件内容」没写进去,是服务端落盘坏了,或者你本地文件本身就是全零。
用一个办法就能同时验证两头:在你电脑上把 t1 压缩成 zip,然后传 zip 上来。
- 如果你本地的 t1 是正常固件(有真实数据)→ zip 应该有几百 KB,且压缩后内容非零,我这边能解开直接开工
- 如果你本地的 t1 本身就是全零/空 → zip 会非常小(全零数据压缩率极高,几 KB 都不到),一看便知是 dump 环节的问题
另外 zip 自带 CRC32 校验,解压不报错就能保证我拿到的和你手里的逐字节一致,顺便把「通道损坏」这个变量也排除了。
顺带记录:如果还想单独验证通道,可以传任意一个你确定完好的文件(flash_full.bin 在我这边校验值是 c2b9d818f91d5bab2e0863fee7749b21,重新传它对比 md5 也行)。
传 zip 上来我就开干——准备工作已就绪:SX1278 的 FSK 模式寄存器映射(0x02 Bitrate、0x06-0x08 Frf、0x07 Fdev、0x39 SyncWord、0x1D-0x1F 包格式)和 473 MHz 的频率常量搜索脚本都在等这个文件。
试试
zip 完好,解压成功——t1_out/t1.bin 164,832 字节(2020-12-16 编译/打包)。开始分析:
文件到手,内容完好(非零字节 14 万)。开头就不是 STM32 那套——05 00 00 EA 是 ARM32(A32/ARM 态)跳转指令,紧跟魔数 0x5AA51234,还有指向 0x0202xxxx 的指针。先做基础侦察:
头部直接给了答案:镜像基址 0x02000000(0x0008 处 0x02000000 是加载地址,0x02027BD8/0x020283E0 是数据段指针,全部落在镜像内)。这是 ARM A32(ARM 态,非 Thumb)固件。先挖字符串:
老固件特征明显:几乎没有调试字符串(109 行里大半是 LCD 字库位图),只挖到 2009051611111(版本戳 2009-05-16)和 1234567890abcde。走纯指令路线——先按基址 0x02000000 反汇编引导头,再扫 SPI 写序列和频率常量:
没有直接命中频率编码(频率大概率是运行时算的,跟上次一样),那些 0x1Cxxxxxx 也只是字库数据。换路线——扫外设基址定位芯片平台,再顺藤摸 SPI 驱动:
锚点找到了:0xE002C000(LPC2000 PINSEL)在 0x0200EFEC——这是 LPC21xx/ARM7 平台的特征。反汇编这个初始化区域:
0xE002C000 是误报——那是 AND ip, r2, r0 的指令编码。重新扫描,扩大外设地址范围:
全是误报(那些都是指令编码:分支、除法魔数、AND 指令)——说明这个固件的硬件访问方式很特别。直接从入口处跟着走:
平台判定成功——这是三星 S3C44B0X(ARM7TDMI):
- 入口先写
0x01D30000=0 关看门狗(WTCON)、0x01E0000C=0x07FFFFFF 屏蔽全部中断(INTMSK)——S3C44 系标准启动序列 - 镜像被引导程序搬到 RAM 0x02000000 运行(栈顶 0x0207FC00,BSS 清零到 0x02061729)
main在0x0200011C(大栈帧 + 主循环特征)
现在定位 SPI/射频驱动——扫 SFR 字面量:
SFR 字面量少而杂——驱动多半是运行时算寄存器地址。换战术:直接搜 473 MHz 的各种存储形态(Hz 值、10kHz/25kHz 单位值、BCD/ASCII 显示串):
只有 0x01C00000 是真字面量(5 处,集中在 0x02010B7C 附近)。反汇编这个区域看它是什么:
这是 Flash/EEPROM 写函数群(关中断→写半字→轮询直到稳定)。把 SFR 范围内所有精确字面量完整列出来:
这批函数的池子同时有 0x01C00000(SYSCFG)、0xAAA/0x554/0xCCCC/0x3333 和 UTXH1(0x01D04020)——像是往 UART1 发命令序列。精确解一个完整函数看指令与寄存器的真实用法:
解出函数语义了:SYSCFG=0(关缓存)→ 往 0xAAA/0x554 写 0xCCCC/0x3333 → 写数据 → 轮询读回相等——这是 Bank0 上 NOR Flash 的烧写/擦除命令序列(0xAAA=半字槽 0x555,0x554=半字槽 0x2AA)。这是 bootloader 层的 Flash 驱动,先放一边。关键线索是 UTXH1(UART1)——老一代设备的射频模块多半挂在串口上。定位所有用 UART1 的函数:
手工追池子效率太低,让 rizin 全量分析建立函数图,再用交叉引用说话:
Info.DBF——这是带数据库文件的老式掌上抄表器(dBASE 格式台账)。dump 全部字符串(含 GB2312 中文)并追 UART1 交叉引用:
字符串情报丰富:FLOG_V20/FFAT_V20 自制文件系统、Info.DBF 台账、拼音输入法表(qian/qiao/zhang...)——典型的掌上抄表器 PDA。现在精确找 UART1 的使用点:
[stopped: the account is out of credit]
