What can you do? Briefly list your capabilities and the kinds of files and tasks you can help with.

Dr.Binary

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).

classes.dex

这是我的安卓解包后的文件,请分析出里边的逻辑

Dr.Binary
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
grep
regex 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 + 广播数据回调给 JS
  • connect / 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,基于 kevinsawicki HttpRequest 库,在线程池中执行
  • 三个由 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/httpsWhitelistPlugin.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 本地存储),原生层只提供能力桥接。

六、下一步建议

如需分析完整的业务逻辑(服务端地址、认证方式、蓝牙协议指令、存储的数据内容),请提供以下任一项:

  1. 完整 APK(可用 apktool/unzip 提取 assets/www/)
  2. 仅 assets/www/ 目录(HTML/JS 文件)

那里的 JS 代码才是真正决定 App 行为的部分。

You stopped this response.

确实是有assets/www的,这里边没有关于lora射频基础配置的内容吗

Dr.Binary

先在现有 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 射频基础配置最可能出现在:

  1. assets/www/js/*.js —— 配置页面、参数表单、下发指令的构造逻辑(频段 433/470/868/915 MHz、扩频因子 SF、带宽 BW、编码率 CR、发射功率、网络 ID/APP Key 等)
  2. assets/www/js/services/ 或类似目录 —— BLE UART/透传协议封装(特征值 UUID、分包、AT 命令集)
  3. assets/www/index.html + 模板 —— 配置界面本身

请把 assets/www 目录上传到工作区(或直接传完整 APK,我可以自行解包提取 www),我就能定位出:

  • LoRa 射频参数的默认值与可配置范围
  • 配置是通过哪条 BLE 特征值/什么协议帧下发的
  • 是否有服务端接口参与配置(结合 HTTP 插件)

文件到位后我会立即开始分析。

我通读了www 貌似没找到lora配置所以寄希望于dex了 ,你消耗token是按照字节数还是什么 我需要评估是解包后只给相关文件还是全给

Dr.Binary

关于 token 消耗

按处理量计,不是按上传字节数。具体规律:

  • 文件传到沙箱只是落盘,不读就不消耗 token——传整个 APK 和只传几个文件,成本差为零
  • 消耗发生在:我读入上下文的内容(文件内容、命令输出)+ 我的分析回复
  • token ≈ 4 字节英文 ≈ 1.5~2 字节中文;一个 200KB 的压缩 JS 全文读完约几万 token,但 grep 定位后只读命中的片段只需零头

结论:不要人工筛选,直接全给。 你人工筛「相关文件」反而有风险——LoRa 配置很可能不在你预判的文件里(见下)。正确做法是全量上传,我用关键词全库扫描定位,只精读命中文件,成本可控且不漏。

关于 LoRa 配置去哪了

DEX 里已经确认没有。但「www 里没找到」大概率是搜索姿势问题,而非不存在,常见坑:

  1. JS 被压缩/打包——vendor.js、main.js 一类打包产物里字符串连成一行,肉眼通读容易漏
  2. 关键词不对——代码里可能没有 "lora" 字样,而是 radio、rf、airRate、sf/bw/cr、channel,甚至中文「射频/扩频/频段/频率/空中速率」
  3. 配置以原始字节下发——JS 里只有 write(Uint8Array([...])) 这种十六进制帧,看不到 "LoRa" 字样
  4. 真不在 www 的可能:APK 可能还有 classes2.dex(multidex)、lib/arm64-v8a/*.so(原生库)、assets 根目录的配置文件、预置 SQLite 库——你如果只通读了 www 的源码目录,这些全没覆盖
  5. 最坏情况:这是 LoRa 设备的 BLE 配套 App,射频参数写死在设备固件里,App 只负责把配置页的值原样转发——那 App 里就只有协议帧格式,没有「配置内容」本身

建议下一步

把完整 APK(或解包后的整个目录:全部 classesN.dex + assets/www + lib/*.so + res 清单)传上来,我按这个顺序排查:

  1. 全库 grep(英文 + 中文关键词 + 十六进制常量 433/470/868/915)
  2. 有命中 → 精读对应文件,还原配置项和下发协议
  3. 无命中 → 检查 .so 符号表和其他 dex,最后给你「配置在设备固件侧」的结论和证据
dongben.apk

这是web桥接为apk,请查阅

Dr.Binary
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(不计量关阀时间)。

三、关键逻辑细节

  1. 频道值来源是服务器:抄表前先调 /AppUser/GetUserMeterList 查表档案,取 user[0].ChannelId;改频道成功后调 /AppUser/UpdateChnValve 回传服务器并写 SaveSetChannelLog 日志
  2. 存在硬编码:一处代码路径放弃服务器值、WriteCH1(deviceId,'50') 强行设 50 频道(注释「强行设置50」),抄表前还固定下发唤醒帧 5581010082AA
  3. 频道→实际频率的映射表不在 App 内,写死在手抄机/表端固件里
  4. 服务器后端(getApiURL 按 site 切换,全部明文 HTTP):东本 egas.tonben.com.cn、东本2 eweb.tonben.com.cn、南通 nant.tonben.com、如东、吴中、相城、临沂

四、顺带发现的问题

  • 全部 API 走 明文 HTTP(httpPostNoAuth),账号密码可被中间人截获
  • acceptAllCerts/白名单全开(上一轮 DEX 分析已述)叠加 debug 构建,WebView 层无 TLS 防护

结论:App 内的「LoRa 射频基础配置」= 频道号读取/写入/扫描/信号检测四组 BLE 指令;SF、带宽、发射功率、频点表等射频参数不在 App 里,在手抄机固件中。如需那部分,需要拿到手抄机或表端固件。

实际上的协议帧 分内外层把 蓝牙层包裹lora层 蓝牙层到手抄机 lora层是给手抄机里的lora与lora从机用的

Dr.Binary

对——你说的这个两层结构和代码完全吻合。我把 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用途数据区
0x01WALK 唤醒手抄机内层帧(表命令 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,逐行印证你的描述)

  1. 先拼内层:array.push(0x68) → 表命令 → 长度 → 表号逐 2 字符倒序转 hex → 参数 → 内层和 vf → 0x16
  2. 再包外层:splice(0,0, handDatalen)(手抄机数据长度 = 内层帧总长)→ splice(0,0, handcommand) → push 手抄机和 hvf → push 0xAA → splice(0,0, 0x55)
  3. 两层校验各自独立:vf 只覆盖内层字段,hvf 覆盖 外层命令+长度+整个内层帧

关键结论

  • 射频基础配置分两处:手抄机本机的频道用外层短命令(0x20/0x21/0x24/0x26)配;表端的频道用内层 0x10 命令经空中下发(CHNSET)——所以 App 能远程改表内 LoRa 模块的频率档位
  • 信道检测应答的 RSSI 换算:parseInt(字节,16) - 180 得 dBm
  • 整个协议无加密、无防重放,校验仅为累加和——BLE 和 LoRa 两层都是明文可伪造

这也解释了为什么全包搜 "lora" 只有营销软文:射频概念在代码里完全以「手抄机命令 / 表通讯命令」两层呈现,与你的描述逐字节吻合。

flash_full.bin

这个看看呢 像是固件

Dr.Binary
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
唤醒信道 SFSF10(打印常量);唤醒发送模式为 SF12/125kHz(TXCLORA MODE %d SF12/125KHz)0x0800E18E / 0x0800DBF2
数据上行 SFSF12(表→手抄机,远距离弱信号方向)0x0800E1B6 movs r1, 0xc
数据下行 SFSF10(手抄机→表)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 覆盖)

Dr.Binary

问得专业——我上一轮只还原了频率/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)频率 HzFrf 0x06/07/08(浮点换算)
+4 (u8)SFMCFG2 0x1E bits7:4(SF6 特判 DetOpt 0x31=0x05/0x37=0x0C)
+5 (u8)BW codeMCFG1 0x1D bits7:4,7 = 125kHz
+6 (u8)CRMCFG1 bits3:1,恒写 1 = 4/5
+7 (u8)CRCMCFG2 0x1E bit2,业务/唤醒路径恒写 0
+8 (u8)报头MCFG1 bit0:0=显式 / 1=隐式
+9 (u8)定长RegPayloadLength 0x22(仅隐式用)
+0xA (i8)功率死代码(见下)
+0xC (u16)前导码RegPreamble 0x20/0x21
(计算)LDRO0x26 bit3,按 SF×BW 表(0x0800A274)

两套实际发送参数(0x0800E2AA 读表下行 / 0x0800E310 唤醒,均为运行时传参):

参数唤醒 TX读表 TX(下行)
频率470.3 + ch×0.4M470.1 + ch×0.4M
SF1010
BW7 (125k)7 (125k)
前导码0x27 = 398
报头隐式显式
定长9 字节运行时(读表帧 14)
CR4/54/5
CRC 位00
IQmode4mode4
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 也写 910隐式报头下定长必须与表端 RX 完全一致,差 1 字节会错位解析——除非你的新唤醒帧格式本就不同,否则改为 9
CRC 位MCFG2 bit2 = 0(所有 TX)W_CRC=R_CRC=1SX127x 该位语义与 SX126x 的 crcOn 不完全对等;若表端按原网关习惯(CRC 关)收帧,你开 CRC 会多出 2 字节。建议先抓原网关空口帧确认 wake 帧是否带 CRC
功率芯片 RFO ~0dBm 进 FEM3dBm差 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 不用调换吗

Dr.Binary

问得好——你这句话其实提供了一个关键的经验性证据:你的 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的立场分析

Dr.Binary

这个纠偏很关键——我确实一直把打印里配对的 (470.3, SF10) 当成了唤醒信道的完整语义,没有追问 SF12 在唤醒信道上的角色。fcn.0800de3c 还有两个我没解的调用者(0x800E370 / 0x800E500),如果其中之一是「唤醒信道 + SF12」的配置,你的模型就坐实了。

drbinary_run_sandbox
You stopped this response.

纠正 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的 ,目前的目的就是想提高通讯成功率

Dr.Binary

双通道模型现在完全对上了——刚解出的两个 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 唤醒帧1039隐式90x0800E32E
470.1+ch×0.4M网关 TX 命令(下行)108显式运行时0x0800E2CA
470.1+ch×0.4M从机回复(上行)→网关 RX128显式上限 0x40=640x0800E370 / 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 IQRX 正常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 两次发射之间的间隔

Dr.Binary

明白,这个语义澄清很关键——那我上一轮对 80ms 的担心(重发撞上表端应答)不成立,撤回。你的实际时序是:

TX#1 ──→ RX 监听 2.5s ──→ 超时 ──→ 80ms(MCU 静止/善后)──→ TX#2 ──→ RX 监听 2.5s ──→ 结束
        ├────── 约 5.08s/轮 ──────┤

80ms 只发生在窗口已经整体超时之后,此时表端要么已在 2.5s 内回完(不会撞),要么根本没收到(还在等唤醒/收听,也不撞)——没有我之前说的「重发砸进表端应答」的问题。这个模型和原固件的会话级重试(发→等满窗→超时→重来)是同构的,只是你的窗口更紧、次数显式化为 1。

修正后仍然成立的建议

  1. CRC 宽松接收(最大的一块收益,与时序无关):原固件对 PayloadCrcError 帧仍读出并交上层用内层 68…16 累加和二次验证。你可以在 RX 窗口内照常收,CRC 坏帧不丢、内层校验通过就采纳——这个和你的重发机制完全正交,纯粹多捞帧。

  2. 2.5s 窗的残余尾部风险很小但存在:若某块表的应答恰好在窗口关闭后才起跳(2.5~2.7s 之间),会被 80ms 后的 TX#2 干扰掉(SF10 命令帧 ToA 约 190ms 的干扰窗)。这个概率很低,不值得为它加长窗口——但有一个例外值得分档:开关阀命令。表端执行机械动作的处理时长和抄表完全不是一个量级,原固件里存在 10s 级的等待阶段(0x2710)。建议给阀控命令单独一档窗口(4~6s),抄表维持 2.5s——两行配置的事,避免阀控类成功率被抄表窗口拖累。

  3. 换频道恢复:你的逐频道找表(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

Dr.Binary
You stopped this response.

坏帧很少,目前就是信号强度,你说旧固件是0 dbm 全靠rfpa 我用ra01sp 带pa的 用的3dbm ;效果很不理想

Dr.Binary

坏帧少、瓶颈在信号强度——那排查重心就从「协议层」转到「射频链路预算」。先确认两个硬件事实: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–10dBRX 前端问题:开关没切到 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 两条命令」是最常见的翻车方式,值得先查。

t1.bin

新的逆向 ,老一代的抄表软件 fsk,频点使用473,请逆向出具体配置和程序逻辑

Dr.Binary
todo_write
run_sandbox
You stopped this response.

?

Dr.Binary

工具调用似乎中断了,重试一下:

run_sandbox
run_sandbox

工作区/t1.bin 传上来了但是空文件(0 字节,md5 = d41d8cd9… 是空文件的特征),应该是上传中断了。

请重新上传一次。收到后我会按老套路逆向:

  1. 识别架构(大概率还是 STM32 裸机固件,Flash 0x08000000 起)
  2. 定位 473 MHz 频点——FSK 模式下找 473 xxx xxx Hz 的 Frf 常量(类似上次 0x1C083560 的算式基址)或三字节寄存器拆分
  3. FSK 配置全解——相较 LoRa,FSK 还多几项:FSK 偏移(deviation)、比特率(bitrate)、同步字(多字节)、whitening/曼彻斯特、packet format(可变长/定长、地址过滤、DC-free)
  4. 程序逻辑——收发状态机、表帧协议(应该还是 68…16 CJ/T 188 内层)、换频/重试策略

另外有个问题可以先告诉我,能省一轮探测:这批老表/老网关的射频芯片是什么型号? 如果是 SX127x 系(FSK/OOK 模式),寄存器映射和上次一样,直接按 0x06-0x08 频率、0x02 Bitrate、0x07 Fdev 顺藤摸瓜;如果是 CC1101/A7139 之类国产或 TI 老芯片,寄存器表完全不同。不知道也没关系,从 SPI 写序列反推也行。

t1.bin

大概率是sx1278 不确定

Dr.Binary
run_sandbox
run_sandbox

还是空的——t1.bin 两次都是 0 字节(md5 d41d8cd9… 是空文件哈希),第二次上传并没有把内容写进来,本地时间戳还停留在第一次的 12:16。

这不是固件本身的问题(比如加密或擦除),而是上传通道没把字节送进来。请按下面顺序排查:

  1. 先确认源文件没传错:在你电脑上看一下 t1.bin 的大小——如果它本身就是 0 KB,说明读出来的时候就失败了(有些烧录器/读保护芯片会给出空文件;SWD 读保护开着的芯片读出来就是全空或全 0xFF,但要区分:全 0xFF 文件大小不会是 0)
  2. 换文件名重传:改个名比如 t1_fw.bin 再上传一次——同名覆盖有时会撞到占位文件不更新的问题
  3. 如果源文件确实是空的:说明是读取环节的问题,重新 dump 一次(校验 dump 工具的日志),注意读保护的芯片需要先解除 RDP

传上来后我立刻开工。如果是全 0xFF 也发过来,那也是有信息量的(说明读保护开了)。

t1 - 副本

我再试试 我把后缀文件名去掉了

Dr.Binary
run_sandbox

还是 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 步。

t1 - 副本.bin

好吧 备份的不是0kb了

Dr.Binary
run_sandbox
run_sandbox
run_sandbox
run_sandbox
run_sandbox
run_sandbox

这次大小传到了(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 的频率常量搜索脚本都在等这个文件。

t1.zip

试试

Dr.Binary
run_sandbox

zip 完好,解压成功——t1_out/t1.bin 164,832 字节(2020-12-16 编译/打包)。开始分析:

run_sandbox

文件到手,内容完好(非零字节 14 万)。开头就不是 STM32 那套——05 00 00 EA 是 ARM32(A32/ARM 态)跳转指令,紧跟魔数 0x5AA51234,还有指向 0x0202xxxx 的指针。先做基础侦察:

run_sandbox

头部直接给了答案:镜像基址 0x02000000(0x0008 处 0x02000000 是加载地址,0x02027BD8/0x020283E0 是数据段指针,全部落在镜像内)。这是 ARM A32(ARM 态,非 Thumb)固件。先挖字符串:

run_sandbox

老固件特征明显:几乎没有调试字符串(109 行里大半是 LCD 字库位图),只挖到 2009051611111(版本戳 2009-05-16)和 1234567890abcde。走纯指令路线——先按基址 0x02000000 反汇编引导头,再扫 SPI 写序列和频率常量:

run_sandbox

没有直接命中频率编码(频率大概率是运行时算的,跟上次一样),那些 0x1Cxxxxxx 也只是字库数据。换路线——扫外设基址定位芯片平台,再顺藤摸 SPI 驱动:

run_sandbox

锚点找到了:0xE002C000(LPC2000 PINSEL)在 0x0200EFEC——这是 LPC21xx/ARM7 平台的特征。反汇编这个初始化区域:

run_sandbox

0xE002C000 是误报——那是 AND ip, r2, r0 的指令编码。重新扫描,扩大外设地址范围:

run_sandbox

全是误报(那些都是指令编码:分支、除法魔数、AND 指令)——说明这个固件的硬件访问方式很特别。直接从入口处跟着走:

run_sandbox

平台判定成功——这是三星 S3C44B0X(ARM7TDMI):

  • 入口先写 0x01D30000=0 关看门狗(WTCON)、0x01E0000C=0x07FFFFFF 屏蔽全部中断(INTMSK)——S3C44 系标准启动序列
  • 镜像被引导程序搬到 RAM 0x02000000 运行(栈顶 0x0207FC00,BSS 清零到 0x02061729)
  • main 在 0x0200011C(大栈帧 + 主循环特征)

现在定位 SPI/射频驱动——扫 SFR 字面量:

run_sandbox

SFR 字面量少而杂——驱动多半是运行时算寄存器地址。换战术:直接搜 473 MHz 的各种存储形态(Hz 值、10kHz/25kHz 单位值、BCD/ASCII 显示串):

run_sandbox
run_sandbox

只有 0x01C00000 是真字面量(5 处,集中在 0x02010B7C 附近)。反汇编这个区域看它是什么:

run_sandbox

这是 Flash/EEPROM 写函数群(关中断→写半字→轮询直到稳定)。把 SFR 范围内所有精确字面量完整列出来:

run_sandbox
run_sandbox

这批函数的池子同时有 0x01C00000(SYSCFG)、0xAAA/0x554/0xCCCC/0x3333 和 UTXH1(0x01D04020)——像是往 UART1 发命令序列。精确解一个完整函数看指令与寄存器的真实用法:

run_sandbox

解出函数语义了:SYSCFG=0(关缓存)→ 往 0xAAA/0x554 写 0xCCCC/0x3333 → 写数据 → 轮询读回相等——这是 Bank0 上 NOR Flash 的烧写/擦除命令序列(0xAAA=半字槽 0x555,0x554=半字槽 0x2AA)。这是 bootloader 层的 Flash 驱动,先放一边。关键线索是 UTXH1(UART1)——老一代设备的射频模块多半挂在串口上。定位所有用 UART1 的函数:

run_sandbox

手工追池子效率太低,让 rizin 全量分析建立函数图,再用交叉引用说话:

run_sandbox

Info.DBF——这是带数据库文件的老式掌上抄表器(dBASE 格式台账)。dump 全部字符串(含 GB2312 中文)并追 UART1 交叉引用:

run_sandbox

字符串情报丰富:FLOG_V20/FFAT_V20 自制文件系统、Info.DBF 台账、拼音输入法表(qian/qiao/zhang...)——典型的掌上抄表器 PDA。现在精确找 UART1 的使用点:

run_sandbox
[stopped: the account is out of credit]
Out of credits — this response was cut short.