Files
qianbian/docs/firmware-analysis.md

284 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# firmware-PP_da14585_4.2_CH.img 逆向分析报告
> 目标文件:`app/firmware-PP_da14585_4.2_CH.img`,72228 字节
> 平台:Dialog DA14585(ARM Cortex-M0,仅 Thumb),SysRAM 96KB @ 0x07FC0000–0x07FD8000
> 工具:`analysis/firmware/fw_parse.py`(头解析+CRC)、`analysis/firmware/disasm.py`(capstone 线性扫描)、`analysis/firmware/find_cmds.py`(命令立即数扫描)、`analysis/firmware/fw.asm`(全量反汇编,32098 指令)
> 地址约定:文件偏移 = 映射地址 - 0x07FC0000 + 0x40(映射地址 = 文件偏移 - 0x40 + 0x07FC0000)
## 0. 结论速览
| 问题 | 结论 |
|---|---|
| 固件是否可控 | **完全可控**。BLE 命令处理无任何鉴权/加密门槛,全部命令(图像、刷新、模式、时间)连接即可用;激活只是云端生意,固件侧不锁功能 |
| 如何实现 OTA | 固件内置完整 **SPOTA 协议栈**(自定义任务 0xFC,MEM_DEV/GPIO_MAP/MEM_INFO/PATCH_DATA 四消息),接收方校验 `70 51`+`AA` 头后写入 SPI Flash 0x38000 备bank,可直接复用本镜像格式做 OTA |
| 命令解析在哪 | EPD 通道分发 @ 0x07FCBC64(handle 2/7/0xB);RXTX 命令分发器 @ **0x07FCBDE4**(0xDD–0xEF 跳表 @ 0x07FCBEEA + 0x14/0x15/0x23/0x88/0x99/0xCC/0xFA/0xFB/0xFF 直比链) |
| 墨水屏驱动 | **SSD1683 兼容**(主路径,cmd 0x01 传 0x2B 0x01=300 行、0x44/0x45 设 50×300、0x24/0x26 双平面)+ 备选 **UC8151/IL0373 兼容** 路径(state[0x1c] 切换);波形用驱动 IC 内部 OTP LUT,固件无 LUT 表 |
| 激活机制 | **本地弱哈希,可离线算出**。激活码 = 6 字节,`code[i] = rol2((MAC[i]<<1 & 0xFF) ^ 0xEF) % 101`,MAC 即设备 BD 地址(BLE 广播可见 / EFEF 应答的 14 字符设备 ID 可完整还原) |
---
## 1. 镜像格式(任务1)
### 1.1 头结构(64 字节,Dialog SUOTA/multi-image 格式)
| 偏移 | 字段 | 值 | 验证 |
|---|---|---|---|
| 0x00 | magic | `70 51`('pQ',LE 0x5170 = IMAGE_VALID) | ✓ |
| 0x02 | signature | `AA 01`(OTA 接收方校验首字节==0xAA,见 0x07FCB4F2 处 `cmp r0,#0xaa`) | ✓ |
| 0x04 | code_size | `E4 19 01 00` = 0x119E4 = **72164 = 文件长 72228 - 64** | ✓ 精确匹配 |
| 0x08 | CRC32 | `10 4D 89 6C` = 0x6C894D10 | ✓ = **标准 zlib CRC32**(poly 0xEDB88320,init/xorout 0xFFFFFFFF)over 数据区 72164 字节(fw_parse.py 实测 match=True;BZIP2/MPEG-2/POSIX 变体均不匹配) |
| 0x0C–0x3F | reserved | 0xFF,仅 0x20 处一字节为 0x00 | ✓ |
### 1.2 镜像布局图
```
文件偏移 映射地址 内容
0x000000 - 64B 镜像头('pQ' + len + CRC32)
0x000040 0x07FC0000 向量表:SP=0x07FD2B58, Reset=0x07FC0525, SysTick=0x07FC522B,
IRQ0=0x07FC221D, IRQ1=0x07FCA975(BLE), IRQ2=0x07F0271D(ROM), ...
0x0000C0~ 0x07FC0080~ 代码(Thumb)+ 内嵌字库/模板数据
~0x00C800 ~0x07FCC800 点阵字库区(大量 16px 字形位图,如 f0x00de30 起)
~0x00F900 ~0x07FCF8C0 GATT 描述符/UUID 表/消息handler表/DIS 属性表
0x010000~ 0x07FD0000~ 初始化数据区:DIS 字符串、22 个祝福词、adv 数据模板、TLV 记录
0x0119E4 0x07FD19E4 代码区结束(= code_size)
- 0x07FD19E8 BSS:全局状态结构体 state(激活码/设备ID/时间/显示参数)
- 0x07FD2B58 初始栈顶
```
SPI Flash 布局(由固件内常量推得,芯片 ≥ 512KB):
| Flash 偏移 | 用途 | 证据 |
|---|---|---|
| 0x00000 | 主固件 bank(boot ROM 加载到 SysRAM) | 标准 DA14585 boot |
| 0x2E000–0x35FFF | 显示帧缓存(8×4KB sector 擦除后顺序写,整图双平面 30000B) | cmd 03 处理 0x07FCBD0C:`movs r0,#0x17; lsls r5,r0,#0xd`=0x2E000,循环 8 次 `bl 0x07FCAB74`(sector 擦除),写函数 0x07FCAE4C |
| 0x38000 | **OTA 备 bank**:+0x00 产品头(校验 `70 52` 'pR'),+0x40 镜像头(校验 `70 51`+`AA`) | OTA 接收 0x07FCB4AA:`bl 0x07FC5AC4` 读 0xC 字节后 `cmp #0x70`/`cmp #0x52`;0x07FCB4F8 `cmp #0x70`/`cmp #0x51`、0x07FCB4F2 `cmp #0xaa` |
| 0x3F0EF+sum(MAC[0:3]) / 0x3F1DE+sum(MAC[0:6]) | 激活码写入槽(6B×2,地址由 MAC 求和混淆) | 0x07FC7C18:`ldr r1,=0x0003F0EF`(literal @0x07FC7C58);0x07FC7C28 `adds r1,#0xef`;存于 state[0x3C]/state[0x40] |
## 2. GATT 表(任务3)
### 2.1 UUID 出现位置(均小端存储)
| UUID | 文件偏移 | 映射地址 | 说明 |
|---|---|---|---|
| EPD Service `13187b10-…-83d3217d9a38` | 0x00FF0C | 0x07FCFECC | 128-bit 原始 16 字节 |
| EPD Char `4b646063-…-e65356ea82fe` | 0x00F972 | 0x07FCF932 | 128-bit,被 0x07FCFD7C 处描述符引用 |
| RXTX Service 0x1F10 | 0x00FF28(16B 项起于 0x00FF20) | 0x07FCFEE8 | 以 128-bit 蓝牙基 UUID 形式 `FB 34 9B 5F 80 00 00 80 00 10 00 00 10 1F 00 00` |
| RXTX Char 0x1F1F | 0x00F98E(项起于 0x00F982) | 0x07FCF94E | 同上形式 |
| 第三 Service 0x221F | 0x00FF38(项起于 0x00FF30) | 0x07FCFEF8 | web 端声明但未用——**疑似 SUOTA 通道** |
| Char 0x331F | 0x00F99E(项起于 0x00F992) | 0x07FCF95E | 0x1F10/0x221F 服务下的另一特征 |
| DIS 属性表(0x2800/0x2803 + 0x2A29/24/25/27/26/23/2A/50) | 0x00FF3C 起 | 0x07FCFEFC 起 | 12 字节/项 {uuid32, perm, aux},被 0x07FC7094 处注册代码引用 |
| GAP/BAS 表(0x2A00/0x2A01/0x2A19 电量 + 0x2902 CCC) | 0x00FBB0 起 | 0x07FCFB70 起 | BASS 任务消息 0x2400/0x2402 @0x07FCFBC4 |
EPD svc UUID 被 0x07FCFD64 引用、EPD char UUID 被 0x07FCFD7C 引用(GATT DB 描述符簇 0x07FCFD54–0x07FCFE68,三个服务各一簇)。
**SPOTA/SUOTA UUID(0xFEF6–0xFEFB)在镜像中不存在**(搜 f6fe/f7fe/…/fbfe 仅命中 BL 指令编码,无相邻簇),但:
- 广播数据模板含完整 16-bit 服务列表 **0x180A(DIS)+0xFEF5(SPOTA)**:0x07FD094A `05 03 0a 18 f5 fe`(证据:文件 0x01098A)
- SUOTA 功能以自定义任务 0xFC + 自定义 service(疑似 0x221F)实现,见 §5
### 2.2 应用消息 handler 表(ke_msg_handler {u32 id, u32 func})
- GAPM 表 @ 0x07FCFE68:0x0C13→0x07FC7EE5、0x0C15→0x07FC80C1、0x0C17→0x07FC7C6D、0x0C00→0x07FC7D19…
- 显示/应用任务 0xFD 表:0xFD06→0x07FC6B9D、0xFD03→0x07FC6C1D、0xFD08→0x07FC6B25、0xFD0E→0x07FC6591、0xFD05→0x07FC6C41(@0x07FCFE88 起)
- GATTC 表 @ 0x07FCFB00:0x0E08→0x07FC79F1、0x0E13→0x07FC7619、0x0E15→0x07FC7529、0x0E17→0x07FC7939、0x0E19→0x07FC7921、0x0D15→0x07FC7A51、0x0D21/0x0D22→0x07FC7C69/0x07FC7C65
- **SUOTA 任务 0xFC 表 @ 0x07FCFB50**:`FC00→0x07FCB6F9`、`FC01→0x07FCAEED`、`FC02→0x07FCB6B9`、`FC03→0x07FCB3F1`(文件 0x00FB90–0x00FBAF)
## 3. 命令解析(任务4)
### 3.1 写入口
GATT 写总入口 **0x07FCBC64**(4 个 BL 调用方:0x07FC58B6/0x07FC757E/0x07FC7784/0x07FC7AB8,均为 GAP/GATT 事件包装)。按消息 id(0xFD04/0xFD0D/0xFD0A,literal @0x07FCBDB8–0x07FCBDC0)分流;0xFD0A 按 handle 分流:
| handle | 去向 | 证据 |
|---|---|---|
| 2 | EPD 数据通道(0xAA/03/04/01) | 0x07FCBC84 `cmp r0,#2` |
| 7 | RXTX 命令分发器 0x07FCBDE4 | 0x07FCBC80 `cmp r0,#7` |
| 0x0B | 同上(第二命令特征,疑 0x331F) | 0x07FCBC7C `cmp r0,#0xb` |
写参数结构:`[r4,#2]`=handle,`[r4,#4]`=len,`[r4,#6]`=opcode,`[r4,#7..]`=payload。
### 3.2 EPD 通道(图像数据)
@ 0x07FCBC94:`ldrb r0,[r4,#6]` 后依次比 `0xAA / 3 / 4 / 1`:
| opcode | 处理 | 证据 |
|---|---|---|
| `03` | **整图**:首包擦除 Flash 0x2E000 起 8 sector,随后按 state[0x2A] 累计偏移经 0x07FCAE4C 顺序写 Flash(忽略包内 offset 与平面字节),长度=len-4 | 0x07FCBD0C–0x07FCBDB6 |
| `04` | **日历小图**:按包内 BE offset 写 RAM:`[7]==0xFF`→0x07FD4B18+off(无边界检查!);`[7]==0x00`→0x07FD0A0A+off(边界 0xAA9);数据=r4+0xA,len-4 | 0x07FCBD3C–0x07FCBD80;literal @0x07FCBDC8/0x07FCBDD8/0x07FCBDDC |
| `01` | 整图结束:state 清零 + `bl 0x07FC6E98`(Flash 落盘/刷屏准备) | 0x07FCBCA2–0x07FCBCAE |
| `AA` | 小图结束:置标志 + `bl 0x07FCA880(0)` 触发刷新编排 | 0x07FCBD82–0x07FCBD8E |
| `00` | 忽略(web 的 clearScreen 第一步是固件侧 no-op) | 0x07FCBC9A 链无匹配落 0x07FCBD02 |
### 3.3 RXTX 命令分发器 0x07FCBDE4(文件 0x00BDA4)
前置:opcode 0–4 且 len≥0x1F → 数据写(拷贝 (op&7)×32 字节到 0x07FD17BD,op 记入 0x07FD09D0);opcode 5–8 且 len≥0x1F → 写 0x07FD183D。这覆盖 web 的 `04{128B}` 倒计时前缀位图(op 4→128B ✓)。
随后 `subs r1,#0xdd; cmp r1,#0x12; bls` → **跳表 @ 0x07FCBEEA**(19 项,target=0x07FCBEEE+entry×2,表体文件 0x00BF2C):
| opcode | target | 语义(与 web 对照) |
|---|---|---|
| `DD` | 0x07FCBF12 | 设时间:LE 4 字节 unix → state[0x78],并换算;只用前 4 字节(web 的年月日周字节被忽略)✓ |
| `E1` | 0x07FCBF78 | 模式:[7]==0→图模式(0x07FCBF8C,state[2]=0+刷新);==1→日历(0x07FCC10A,循环/可指定,state[0x14]=0);==2→时钟(0x07FCC12E,state[0x14]=1)✓ |
| `E2` | 0x07FCBF64 | 提交:查显示状态后 → 0x07FCC0D0 → `bl 0x07FCA880(1)` 刷新 ✓ |
| `E3` | 0x07FCC0D0 | 反色 → `bl 0x07FCA880(0)` ✓ |
| `E4` | 0x07FCBF98 | 时钟字体 state[0x25] 0/1 循环 ✓ |
| `E5` | 0x07FCBF58 | 旋转 180°:state[0x11] 翻转 ✓ |
| `E6` | 0x07FCBF70 | LUT/VCOM 校准字节 → 显示参数结构 0x07FD09D0+6,然后刷新。平面写函数 0x07FC8648 @0x07FC86EA 读该字节并按 **0x10 分界**(<0x10 红平面 / ≥0x10 黑平面)——与 web 文档"01–0f 设红、10–f0 设黑"精确一致 ✓ |
| `EF` | 0x07FCBFAA | 激活/取设备 ID,见 §4 |
| DE–E0, E7–EE | 0x07FCC0D6 | **空操作**(含 web 在用的 `E9` 轮播间隔、`EA` 槽位——本固件忽略)✗差异 |
跳表外直比链(0x07FCBE54–0x07FCBE88):
| opcode | target | 语义 |
|---|---|---|
| `14` | 0x07FCC004 | 12/24h:state[0x23] 翻转 ✓ |
| `15` | 0x07FCC0A0 | 时钟颜色:state[0x19]^=1 ✓ |
| `23` | 0x07FCC058 | 三色/黑白:state[0x18]=[7]&1;并写参数[6]=0x0C/0x80 ✓ |
| `88` | 0x07FCC092 | 置标志位 0x8000(web 未用) |
| `99` | 0x07FCC068 | 日历倒计时文字:len≥0x1F 时按 [7]×0x48 拷贝 **72 字节/字符** 到 0x07FD18BD 基缓冲,[7]+1 记字符数 ✓(与 web `990{j}{72B}` 一致) |
| `CC` | 0x07FCBFEE | 参数[5]:>0xE0 或 ≤0x47 时钳到 0x48(web 未用) |
| `FA` | 0x07FCC04A | 关闭倒计时:state[0x24]=1,参数[2]=2 ✓ |
| `FB` | 0x07FCC010 | 休眠时段:BCD 解析 [7]=开关、[8]=HH、[9]=MM → state[0x13]/参数+3/+4 ✓ |
| `FF` | 0x07FCBE8A | len==6 时:`FF 00`→0x07FCC152 时钟倒计时日期(BCD 年×100+月+日 → 结构+0x10/14/18,state[0xD]=1);`FF FF`→0x07FCBEA0 日历倒计时日期(→ +0x1C/20/24)✓ |
**web 有而固件无**:`E9`/`EA`(no-op)、ASCII `WIFI_SSID:...`(固件无此字符串、无解析路径,直接忽略)——本固件为纯 BLE 版,WiFi/天气属其他 SKU 或云端能力。
## 4. 激活验证逻辑(任务6)——可离线绕过
### 4.1 EF 处理路径(0x07FCBFAA)
```
[r4,#7]==0xEF 且 len==2 → "EFEF":bl 0x07FCA22C 生成设备ID,
循环 r5=0x14..0x21 把 14 字节经 ke_msg 发回(notify 应答)✓ 与 web "14B 设备ID"一致
否则 → 比较循环 @0x07FCC0AA:r4[7..12](收的 6 字节) vs state[0x44..0x49]
全部相等 → 0x07FCC0DE:state+0x4A(6B)写 Flash[state[0x40]]、state+0x44(6B)写 Flash[state[0x3C]]
(写函数 0x07FCAE4C,与图像写 Flash 同一函数,证实为 SPI Flash 写)
不等 → 0x07FCC0D6 直接返回(无任何惩罚/锁定)
```
### 4.2 比较目标 state[0x44] 的来源 = MAC 哈希(关键发现)
启动初始化函数 **0x07FC23D0**(被 0x07FC7C30/0x07FC877E 调用)尾部 0x07FC24C8–0x07FC24F4,对 i=0..5 循环:
```
t = ((MAC[i]<<1) & 0xFF) ^ 0xEF ; ldrb→lsls#1→eors ~0x10→uxtb
code1[i] = ((t<<6)+(t>>2)) & 0xFF % 101 ; = rol2(t) % 0x65,经 __aeabi_uidivmod(0x07F1DFB8)
→ 存入 state[0x44+i]
```
- MAC = state[0x4A..0x4F] = 设备 BD 地址,由工厂函数 0x07FC7B94 经 NVDS/TLV 读取(0x07FCA2DC,tag 1 = Dialog NVDS BD_ADDRESS;缺省记录 @0x07FD04BA:`14 00 06 00 70 ca ea 80 d0 07…`)
- 设备 ID(EFEF 应答)生成 @ 0x07FCA22C:`snprintf(state+0x14, "%02X"*7 @0x07FCA298, |MAC4-0x99|, |MAC5-0x99|, |MAC3-0x99|, |MAC2-0x99|, |MAC1-0x99|, |MAC0-0x99|, flags)`;flags=state[0x10] = 各 MAC 字节 ≥0x9A 的位掩码(0x07FC23D4–0x07FC23F4 计算)——**14 个十六进制字符 + 掩码可无损还原 6 字节 MAC**
- state[0x3C]/[0x40](激活码 Flash 写地址)= `0x3F0EF + MAC[0]+MAC[1]+MAC[2]` 与 `0x3F1DE + sum(MAC[0..5])`(0x07FC7C0C–0x07FC7C2C,常数 literal @0x07FC7C58 = 0x0003F0EF)——按 MAC 求和混淆存放
- 全镜像**不存在从 Flash 读回激活码的代码**(Flash 读函数 0x07FC5AC4/0x07FC5A2C 仅 OTA 路径调用);0x07FC4CDC 每次刷新都会把这两个槽**清零**并按 RAM 码非零置 state[0xE]/[0xF](显示合成开关,非门锁)
### 4.3 绕过结论
1. 激活码是纯本地函数:`code[i] = rol2(((MAC[i]<<1)&0xFF)^0xEF) % 101`(i=0..5),**无需厂商服务器**。
2. MAC 获取:BLE 广播地址直接可见;或发 `EFEF` 收 14 字符 ID 按上式反解。
3. 固件侧 EF 成功/失败均不门禁任何后续命令(03/04 图传、E1/E2 刷新等在未激活状态同样解析)——**激活仅是厂商云端商业模式,本地使用可直接无视**;如需让屏幕不再显示激活页,发送一次正确 EF 即可(或直接 patch §8 所述字节)。
## 5. OTA / SUOTA(任务3+可行性)
证据链:
1. **任务 0xFC 四消息**(表 @0x07FCFB50):FC00=MEM_DEV(0x07FCB6F8)、FC01=GPIO_MAP(0x07FCAEED)、FC02=MEM_INFO(0x07FCB6B8)、FC03=PATCH_DATA(0x07FCB3F0)——与 Dialog SPOTA 语义一一对应。
2. **写转发器 0x07FC8340**:按服务内 handle 偏移(`[r4,#6]`=起始 handle,偏移 2–13 跳表 @0x07FC8392,12 项=6 特征×2)把特征写打包成 0xFC00–0xFC03 消息(0x07FC83CC:`movs r0,#0x3f;lsls r0,#0xa`=0xFC00;literal 池 @0x07FC8520–0x07FC8528 = FC02/FC03/FC01)。6 特征 = SPOTA 标准集(mem_dev/gpio_map/mem_info/patch_len/patch_data/serv_status)。
3. **接收写盘 0x07FCB3F0**:state[0x1C] 状态机(0x10–0x13);0x200 字节块缓冲(0x07FCB424 `lsls r1,#9`);XOR 校验和累计(0x07FCB448–0x07FCB458,存 state[0x24]);收满后校验缓冲头 `70 51`(0x07FCB486–0x07FCB492)→ 写 Flash;并回读 0x38000 校验产品头 `70 52`(0x07FCB4BC/0x07FCB4C4)与镜像头 `70 51 AA`(0x07FCB4F2–0x07FCB502)。
4. 广播数据声明 SPOTA 0xFEF5(0x07FD094A)。
**OTA 实施建议**:按本镜像格式(64B 'pQ' 头 + zlib CRC32)打包 → 连接后走 SUOTA 服务(web 端声明但未用的 **0x221F**,待实机确认句柄;可先 `btgatttool` 枚举含 6 特征的服务)→ MEM_DEV 写 SPI flash 基址、PATCH_DATA 分块(块内无需应用层 ACK,结束由接收方校验头+长度+XOR)→ 完成后设备复位,boot ROM 按产品头切换 bank。**风险**:bank 切换依赖 boot ROM 读 0x38000 产品头,首次实机前建议先用一份仅改版本字符串的镜像验证。
## 6. 墨水屏驱动(任务5)
低层原语:发命令 **0x07FC3646**(CS/DC=cmd)、发数据 **0x07FC33D0**、SPI 读 **0x07FC31B4**、GPIO 配置 0x07FC3658(P0/P1 基 0x50003000,literal @0x07FC3694)、SPI1 基 0x50001200(literal @0x07FC32B8 等)、延时 0x07FCB8EC。
### 6.1 驱动 IC 识别 —— 双面板支持,state[0x1C] 选择
**主路径 state[0x1C]==0:SSD1683 兼容**(面板初始化 0x07FC3440,证据 @0x07FC34C8–0x07FC3562):
```
12 ; SWRESET,延时 200ms(0x07FC34CE bl 0x07FC3154, r0=0xC8)
74 / 54 ; 模拟块控制
7E / 3B ; 数字块控制
2B / 04 63 ; VCOM/ACVCOM
0C / 8B 9C 96 0F ; Booster soft start(GDEY042 系列特征序列)
11 / 01 ; data entry mode
44 / 00 31 ; RAM X: 0–49 → 50 字节 = 400 列 ✓
45 / 2C 01 00 00 ; RAM Y: start 0x012C=300, end 0(反向寻址) ✓ 300 行
3C / 01 ; border
22 / B1, 20 ; 装载温度+主激活
4E / 00, 4F / 2C 01 ; RAM 计数器
```
另有 0x07FC3344 发 `01 / 2B 01 / (1-state[0x20])` = Driver Output Control,mux 0x012B=299 → **300 行**(400×300 三色 4.2" 实锤)。温度读取 @0x07FC32BC:`18/80`、`22/B1`、`20`、`1B` 读 2 字节 >>4 带符号(bit 0x800)→ state[0x28]。
**备选路径 state[0x1C]!=0:UC8151/IL0373 兼容**(0x07FC3476 起):
```
01 / 03 00 26 26 (0F|03) ; Power Setting(VGH/VSH/VSL/VDHR)
06 / 17 17 17 ; Booster Soft Start
50 / (97|51) 60 ; VCOM & Data Interval
22 / 04, 延时 200
```
面板类型 state[0x1C] 由 TLV 记录(tag 0x40–0x44,读取器 0x07FCA2DC 跳表 @0x07FCA2F4)配置。
### 6.2 刷新编排 0x07FCA880
读温度→选波形参数(0x07FC3440)→ 平面写 **0x07FC8648** 两次:`(0x24|0x10, 0)` 与 `(0x26|0x13, 2)`——0x24/0x26 = SSD168x 写黑/红 RAM;0x10/0x13 = UC8151 写 RAM1/RAM2,按面板标志 state[0x1E]/[0x1F] 选择 → ADC 读电池(0x07FC6374,state[0x2C]=百分比)→ 计数 state[0x2B]。
### 6.3 LUT
**固件内无 LUT 表**:全程未发 `0x32`(Write LUT),波形用驱动 IC 内部 OTP LUT;`E6` 命令仅存校准字节到 0x07FD09D0+6,平面写时按 <0x10/≥0x10 分红/黑平面生效(0x07FC86EA)。两段"LUT 候选"(0x07FD06F8/0x07FD187D)实为清零/0xFF 填充的间隙区,非 LUT。
## 7. 字符串交叉引用(任务7)
| 字符串 | 位置 | 引用点 | 用途 |
|---|---|---|---|
| `TEW MI YAO:` | 0x07FC2508 (f0x002548) | `adr` @0x07FC2414 | 开机/未激活屏第一行标题(经 0x07FCC1C4 snprintf 到显存,随 0x07FC23D0 绘制) |
| `H:mspppclock.tech` | 0x07FC251C | `adr` @0x07FC244A | 激活屏:厂商主机名(仅显示用,固件无联网代码) |
| `V:PP_da14585_4.2` | 0x07FC2530 | `adr` @0x07FC2464 | 激活屏:固件版本 |
| `WX:2549294317` | 0x07FC2544 | `adr` @0x07FC247C | 激活屏:微信联系方式 |
| `MAC:%02X%02X…` | 0x07FC2554 | `adr` @0x07FC24A0 | 激活屏:显示 BD 地址 |
| `%02X%02X%02X%02X%02X%02X%02X` | 0x07FCA298 | `adr` @0x07FCA286 | 设备 ID(14 hex)格式串,0x07FCA22C |
| `%d %02d` / `%d %02d %02d` / `%s %s` 等 | 0x07FC2D88/90/58/60 | `adr` @0x07FC2D1E/22/C4/C8 | 时钟/日历屏文本排版 |
| `Happiness`…`Motive`(22 词) | 0x07FD034C–0x07FD04B8 | 指针表 @0x07FC9708 | 日历模式每日一词 |
| `DA14585` / `PP_da14585_4.2` / `1.0.0.0-LE` / `Dialog Semi` | 0x07FD02C4/0x07FD08B9/0x07FD08D0/0x07FD08F6 | DIS 表 | DIS 0x2A24/0x2A26/0x2A27/0x2A29 值 |
| `NRF-XXXXXX` | 0x07FD0956 | 0x07FCA210、0x07FCBC4C | 广播名模板(X 由 MAC 填充;web 端 `namePrefix:'NRF-'` ✓) |
| `0123456789abcdef`/`ABCDEF` | 0x07FCC638/0x07FCC64C | hex 工具 | 通用 |
`Sun/Mon/Tues/Wednes/Thurs/Fri/Satu`(0x07FD0482 起,定长 8B)与 `中华人民共和国`(0x07FD0920,UTF-8)为日历显示素材。
## 8. 固件修改/重打包建议
1. **重打包**:改完代码区后用 `fw_parse.py` 同算法重算 zlib CRC32 写回头 0x08,长度不变则无需改 0x04;经 §5 SUOTA 通道或 UART boot 烧录。
2. **免激活 patch**:将 0x07FCC0AA 比较循环的首分支 `beq`(0x07FCC0BC 处 `beq #0x7fcc0ac` 的 fall-through `b #0x7fcc0d6`)改为直接跳成功分支,或把 EF 处理改成总写 Flash——但更简单的做法仍是按 §4.3 公式算码发送。
3. **去激活开机屏**:0x07FC23D0 的 r0 判分支(0x07FC23F6 `cmp r0,#0; beq`)强制走 0x07FC24C4 即可跳过 TEW MI YAO/MAC/WX 屏。
4. **RAM 无边界写注意**:cmd 04 的 0xFF 平面分支(0x07FCBD48)无长度检查,自研上位机时不要越界(会踩 0x07FD4B18 之后的 BSS)。
5. **手柄速查**:EPD 数据 handle 2;命令 handle 7 与 0x0B(同一解析器);SUOTA 服务=含 6 特征(12 句柄)的服务(枚举确认)。
6. **排产注意**:固件忽略 E9/EA/WIFI 命令——自研上位机不必实现;倒计时文字位图经 opcode 0–8 数据通道(len≥0x1F)而非 0x99 之外的格式。
## 附:关键地址速查
| 名称 | 映射地址 | 文件偏移 |
|---|---|---|
| 镜像头 | - | 0x000000 |
| Reset handler | 0x07FC0525 | 0x0004E5 |
| GATT 写总入口 | 0x07FCBC64 | 0x00BC24 |
| RXTX 命令分发器 | 0x07FCBDE4 | 0x00BDA4 |
| 0xDD–0xEF 跳表 | 0x07FCBEEA(表体) | 0x00BF2C |
| EF 激活处理 | 0x07FCBFAA | 0x00BF6A |
| 激活码 keygen(boot) | 0x07FC24C8 | 0x002488 |
| 设备 ID 生成 | 0x07FCA22C | 0x00A1EC |
| SUOTA PATCH_DATA 接收 | 0x07FCB3F0 | 0x00B3B0 |
| SUOTA 写转发器 | 0x07FC8340 | 0x008300 |
| 面板初始化(SSD1683/UC8151) | 0x07FC3440 | 0x003400 |
| 刷新编排 | 0x07FCA880 | 0x00A840 |
| 平面写(0x24/0x26/0x10/0x13) | 0x07FC8648 | 0x008608 |
| Flash 写(页) | 0x07FCAE4C | 0x00AE0C |
| Flash 读 | 0x07FC5AC4 / 0x07FC5A2C | 0x005A84 / 0x0059EC |
| Flash sector 擦除 | 0x07FC58E0 / 0x07FCAE8C | 0x0058A0 / 0x00AE4C |
| 全局状态 state | 0x07FD19E8(BSS) | - |
| 显示参数结构(E6 字节 +6) | 0x07FD09D0 | 0x010990 |
| cmd04 RAM 缓冲 0xFF/0x00 | 0x07FD4B18 / 0x07FD0A0A | - |
| 文字位图缓冲(op0–8/99) | 0x07FD17BD / 0x07FD183D / 0x07FD18BD | - |