- 逆向:Ghidra/IDA 全量反编译 CoreSDKLib.dll/ThermalSDK.dll/libthermalSDK.so/ libcoresdk.so(ARM64)/libmagcore.so,导出 analysis/ida/export/ - 解码官方渲染管线:DDT 校准表加载->快门端点选择->Q12 插值->ref(4x type1 帧 均值)->NUC 查表->盲元补偿->窗口->LUT1024 重建->2x 升采样->调色板 - 逐像素验证:NUC+盲元 0/19200、插值 0 误差、2x 0/76800、窗口一致 - demo3 v5:完整复刻官方管线(含 DDT 解析、FFC 状态机、快门温度驱动), 修复 load_ddt 表错位导致的零像素问题 - 鬼影根因分析写入 analysis/reverse_20260813_full.md - 心跳/恢复机制:analysis/session_state.md + tools/resume_rev.ps1 - 新增 tsdk_pair3 增强采集工具;历史工具归档 csdk/tools/legacy/; 根目录抓帧残留删除,历史文档归档 analysis/history/ - csdk/README.md 完整使用文档;.gitignore/.gitattributes 补 LFS 规则
229 lines
11 KiB
Markdown
229 lines
11 KiB
Markdown
# MAG160C 官方管线完整逆向结果(Ghidra 全量,2026-08-13)
|
||
|
||
> 本文档记录 CoreSDKLib.dll 官方渲染管线的完整反编译结论、逐像素验证结果,
|
||
> 以及 demo3 旧版"鬼影"问题的根因分析。所有结论均有 Ghidra 反编译代码和
|
||
> 官方活体抓帧数据的双重证据,不是推测。
|
||
>
|
||
> 工具链:Ghidra 12.1.2(headless 批处理反编译)+ IDA Free 8.4(交互确认)。
|
||
> 反编译产物:`analysis/ida/export/ghidra_dump/`(keyfuncs / callers / exports)。
|
||
|
||
---
|
||
|
||
## 1. 官方管线(Ghidra 反编译确认)
|
||
|
||
```
|
||
USB 帧 (type=0, 0x1bb1b11b 标记)
|
||
→ 帧解析器 0x18001d930
|
||
验证: [0x08]=len, [0x1c+len]=0x1bb1b11c, type∈{0,1}
|
||
快门: width=160 时 = 帧尾字 frame[len+0x24]
|
||
metadata 0x4c 字节: {0, counter, len, 1, 0, w, dev54, 8, 0, -50000,
|
||
shutter-500, shutter, 0, 0, type, 0, 0}
|
||
→ 帧调度 0x180009ee0(FFC 状态机 + ref/表重建时序, 见 §3)
|
||
→ 主渲染 0x18000ca30
|
||
→ 0x18000c950: f20 平滑器 0x41f10 (mode=1: 直通) → NUC
|
||
→ NUC 查表 0x180017200:
|
||
d2 = (f20[i] - ref[i]) >> 1
|
||
seg: 每像素 (nsegs-1) 个 signed 阈值顺序比较
|
||
out = off + ((gain * d2) >> shift), shift=0x11c=12
|
||
clamp [0, 65535]
|
||
→ 盲元补偿 0x180017330:
|
||
固定记录表 dev+0x41878, 数量 dev+0x41654[sel]
|
||
每条 40 字节: {u32 target, u32 type(3..8=邻域数), u32 neigh[8]}
|
||
out[target] = mean(neigh) (type 8/4 用移位, 其余用除法)
|
||
→ temporal filter 0x18001e920: 仅 dev+0x41fe0>1 时启用, 当前=1 跳过
|
||
→ 统计 0x180010780: min/max/mean/std → dev+0x42010..0x42024
|
||
→ 窗口 0x1800109c0 + 半宽 0x1800108a0:
|
||
half = max(128, dev24*1000 >> dev4c_shift) / 2 (live: 5*1000>>3 /2 = 312)
|
||
lo = min(fmin, mean-half) >= 0
|
||
hi = max(fmax, mean+half) <= 65535
|
||
→ LUT1024 重建 0x180011950 + 0x180011ee0(直方图均衡, 见 §5)
|
||
→ 灰度: gray = LUT[(nuc-lo) * 0xffc00000/(hi-lo) >> 22]
|
||
→ 2x 升采样 0x180019740(2-tap 双线性 + 边缘外推, 见 §6)
|
||
→ 调色板 dev+0xb18 (256×4 BGR)
|
||
```
|
||
|
||
## 2. DDT 校准文件(表的数据源)
|
||
|
||
- 位置:`%TEMP%\Core<序列号>`(无扩展名),由 app 用 MAG_SaveDDT 保存、
|
||
MAG_StartProcessImage → 0x18000f130 在启动时加载;文件路径存 dev+0x4134c。
|
||
- 本机文件已复制:`build-artifacts/mag160c_official.ddt`(1,856,416 字节,v3)。
|
||
|
||
```
|
||
v3 格式 (magic 0x5aa50003):
|
||
+0 u32 magic
|
||
+4 u32 width (160)
|
||
+8 u32 height (120)
|
||
+12 u32 端点数量 (0x41558, live=6)
|
||
+16 u32 nsegs (0x41554, live=3)
|
||
+20 u32 0x47944 (8, 2 的幂)
|
||
+24 u32 0x47948 (-50000)
|
||
+28 u32 0x4155c (1024)
|
||
+32 u32 0x41560 (0)
|
||
+36 i32 T[count] 端点温度: [8304, 18390, 28495, 33637, 38730, 47664]
|
||
+.. u32 0x415b4[count] (偏移基准)
|
||
+.. u32 0x41604[count]
|
||
+.. u32 0x41654[count-1] 每端点盲元记录数 (live: 32×5)
|
||
+128 端点表块 × count:
|
||
thr (nsegs-1)*npix + 0x4155c/2 个 int16 (像素主序)
|
||
gain nsegs*npix*2 个 uint16 (seg 主序: [seg][pix].gain/.off)
|
||
+.. 盲元记录 × count (每端点 40 字节 × 数量)
|
||
+.. trailer: 0x6bb60001, "lens f6.5"
|
||
```
|
||
|
||
## 3. 端点选择 + 表插值 + ref 时序(0x180009ee0 / 0x180016ae0)
|
||
|
||
### 3.1 快门温度 = 设备温度代理
|
||
- 快门 = 帧尾字(width=160 时 frame[len+0x24]),每帧进入 metadata。
|
||
- dev+0x5c = 当前快门,dev+0x54 = 上次 FFC(0) 时的快门。
|
||
- FFC 条件:`idx >= N0+1800`(周期)或 `(|dev54 - 快门累加| > 250 && idx >= 冷却)`。
|
||
|
||
### 3.2 端点选择(实测验证)
|
||
```
|
||
sel = 0
|
||
while (sel < count-2 && 快门 > T[sel+1]) sel++
|
||
```
|
||
live 实测:快门 29289 → sel=2(T[2]=28495 < 29289 ≤ T[3]=33637)✓
|
||
|
||
### 3.3 Q12 插值(0x180016dd0 / 0x180016f10)
|
||
```
|
||
t = ((快门 - T[sel]) << 12) / (T[sel+1] - T[sel]) clamp ±0x3fff
|
||
work[i] = a[i] + ((b[i] - a[i]) * t >> 12)
|
||
```
|
||
验证:阈值 0/38400 误差,gain 0/115200 误差。
|
||
|
||
### 3.4 ref 采集时序(关键!)
|
||
```
|
||
FFC(0) 后帧计数复位:
|
||
idx 1..5 : 表重建(分块)
|
||
idx 6..9 : 采集 ref —— 4 个 type=1 校准帧的均值(累加 >> 2)
|
||
(idx 6 复位平滑器,idx 9 发 FFC(1))
|
||
idx 10..13 : 表重建
|
||
idx 14+ : 正常渲染(ref 冻结,直到下次 FFC)
|
||
```
|
||
- **ref = 4 个 type=1 帧的均值**,不是场景帧!
|
||
- type=1 帧均值实测 ~15000-16300,type=0 场景帧 ~11700-13700,
|
||
因此官方 d = f20 - ref ≈ **-2200 ~ -2700,恒 ≤ 0**。
|
||
- ref 在每次 FFC 对之后重新采集;FFC 之间完全冻结。
|
||
|
||
## 4. 逐像素验证(官方抓帧 vs 反推重建)
|
||
|
||
用官方同帧抓帧(`analysis/pairs_verify_20260813/`)+ DDT 离线复算:
|
||
|
||
| 环节 | 结果 |
|
||
|---|---|
|
||
| 阈值插值 (t 由快门算) | diff 0/38400 |
|
||
| gain/off 插值 | diff 0/115200 |
|
||
| NUC 查表 | diff 30/19200(MAE 0.118)|
|
||
| NUC + 盲元补偿 | **diff 0/19200(MAE 0.0000)** |
|
||
| 窗口 [lo,hi] | 与官方完全一致 |
|
||
| 灰度 (LUT+idx) | 160 层 19200/19200 |
|
||
| 2x 升采样 | **diff 0/76800** |
|
||
| demo3 C 实现离线复算官方数据 | NUC/窗口/灰度全部 0 误差 |
|
||
|
||
LUT 填充公式:在正确 center 下 834/1024 精确,其余 ±1~±58 为
|
||
跨帧捕获偏差(h1024/cdf/lut 不同帧)+ center 时域平滑所致。
|
||
|
||
## 5. LUT1024 重建算法(0x180011950 + 0x180011ee0)
|
||
|
||
1. 1024-bin 直方图:bin = ((nuc-lo) * 0xffc00000/(hi-lo)) >> 22(u32 算术)
|
||
2. 3-tap 就地平滑(bin 0..1022)
|
||
3. 双向 CDF(从 mean-bin 向两侧累积):
|
||
- 向下:每累积 u21(=total>>12) 个计数 +0x100
|
||
- 向上:<8*u21 时每 u21 个 +0x100,>=8*u21 时每 bin +0x200
|
||
4. 对比度曲线 curve[k] = max(1, (0x300000 + k*0x800)>>13)
|
||
5. 中心值二分搜索(fmin/fmax 对应 LUT 端约束)
|
||
6. center 时域平滑 + 重填
|
||
|
||
## 6. 2x 升采样(0x180019740,逐像素验证)
|
||
|
||
```
|
||
内部: out[2y][2x]=a out[2y][2x+1]=(a+b)>>1 out[2y][2x+2]=b out[2y][2x+3]=(c+b)>>1
|
||
out[2y+1][2x]=(d+a)>>1 out[2y+1][2x+1]=(d+e+a+b)>>2
|
||
out[2y+1][2x+2]=(e+b)>>1 out[2y+1][2x+3]=(f+e+c+b)>>2
|
||
右缘: out[2y][2W-1] = (3a>>2)+(l>>2)(外推)
|
||
底缘: out[2H-1][2x] = (3a>>2)+(u>>2)
|
||
角落: (3a+l)>>2 / (3a+u)>>2 / 三者均值
|
||
```
|
||
|
||
## 7. 鬼影根因分析(为什么旧 demo3 有鬼影)
|
||
|
||
旧 demo3(v4,SBNUC 版)与官方的差异,每一条都有上面的证据:
|
||
|
||
### 7.1 ref 语义完全不同(最根本)
|
||
| | 官方 | 旧 demo3 |
|
||
|---|---|---|
|
||
| ref 来源 | FFC 窗口 4 个 **type=1 校准帧**均值 | 启动场景 12 帧中值 |
|
||
| ref 值域 | 场景 + ~2200 counts(d 恒负)| ≈ 场景(d ≈ 0)|
|
||
| 刷新时机 | **每次 FFC 对之后** | 仅启动一次(FFC 后只 rebase 全局偏置)|
|
||
| FFC 之间 | 冻结 | 冻结 + SBNUC 自愈 |
|
||
|
||
后果:
|
||
- 官方 NUC 输出 ≈ 9800(off 段配合 d 恒负),旧 demo3 输出 ≈ 10900+(d≈0),
|
||
两者对同一场景的画面等级都不同,更谈不上残影行为一致。
|
||
- 旧 demo3 启动采集一次 ref 后永不刷新 → 场景基线漂移(如设备升温、
|
||
环境温度变化)后 ref 失配 → 旧位置出现"黑洞/黑拖影",且只能等手动 FFC。
|
||
- 官方每次 FFC 重采 ref → 残影最多持续到下次 FFC(周期 120s 或快门漂移 250 触发),
|
||
且表也按新快门重建,匹配始终成立。
|
||
|
||
### 7.2 NUC 表不是静态的
|
||
- 官方:表 = DDT 端点按快门温度 Q12 插值,FFC 后重建。
|
||
- 旧 demo3:静态抓取表(某次会话的插值结果),温度漂移后失配,
|
||
表现为"小温差残影擦除慢"——本质是表与当前温度不匹配造成的残差,
|
||
不是真正的显示层鬼影。
|
||
|
||
### 7.3 SBNUC 是非官方 hack,引入次级鬼影
|
||
- 旧 demo3 的"曾热变冷快速吸收/低温静止不吸收"状态机,会把低温物体
|
||
排除在 ref 之外 → 物体离开后留下亮残影;把曾热像素写回 ref →
|
||
改变局部基线 → 新黑洞。
|
||
- 官方没有 SBNUC:ref 冻结 + 定期 FFC 刷新是官方唯一的"去鬼影"机制。
|
||
|
||
### 7.4 盲元补偿缺失
|
||
- 官方有 32 条固定盲元记录(邻域平均),旧 demo3 完全没接 → 死像素
|
||
直接显示(0 值黑点),曾用动态 badmap 尝试 → 反而把场景结构钉在
|
||
屏幕坐标(BADMAP_ENABLE=0 后残留黑点)。
|
||
|
||
### 7.5 窗口/灰度差异
|
||
- 官方窗口 = [min(fmin, mean-312), max(fmax, mean+312)];
|
||
旧 demo3 用 P2..P98 自适应 → 对比度行为不同,但不是鬼影主因。
|
||
|
||
### 7.6 结论
|
||
旧 demo3 的"鬼影"是 ref 语义错误 + ref/表不随 FFC 刷新 + 静态表 +
|
||
非官方 SBNUC 共同造成的,与官方"冻结 ref 的轻微旧位置偏暗"不是
|
||
同一个现象。修复 = 完整复刻官方管线(demo3 v5 已实现,见下)。
|
||
|
||
## 8. demo3 v5(官方复刻)实现状态
|
||
|
||
- DDT 加载(v3 解析,含端点表/盲元记录)
|
||
- 端点选择 + Q12 插值(快门驱动,FFC 后重建)
|
||
- ref = FFC 窗口 4 个 type=1 帧均值(u32 累加器)
|
||
- NUC + 盲元补偿(32 条固定记录)
|
||
- 窗口/统计(官方公式)
|
||
- LUT1024 重建(直方图均衡 + center 平滑)
|
||
- 灰度 + 2x 升采样 + 官方调色板
|
||
- FFC:周期 1800 / 快门漂移 250 / 13 帧隐藏周期
|
||
- 实机运行:sel=2、FFC 自动触发正常、画面统计接近官方
|
||
(demo3 mean 9377 vs 官方 9640)
|
||
|
||
### 遗留问题
|
||
1. ~~demo3 NUC 输出有 2 个零像素~~ **已修复(2026-08-13 晚)**:
|
||
根因是 demo3 `load_ddt` 读取布局错误——blind 记录区在文件所有端点块之后,
|
||
代码却在每个端点块内读了 blind,导致 EP1+ 表整体错位 1280 字节。
|
||
错位表在死像素列附近 seg0 gain=35321(应为 ~4000),d2 踩中后 NUC clamp 到 0。
|
||
修复后全帧 0 个零像素,(65,25) nuc=9814,gain=4000 正常值。
|
||
2. LUT 重建的跨帧一致性:算法已按反编译实现并实机运行,同场景 gray 直方图
|
||
与官方相关性 0.9197(跨会话),剩余差异来自会话间 ref/快门温度差异,
|
||
属官方物理机制,不是 bug。
|
||
|
||
## 9. 关键文件
|
||
|
||
- 反编译全量:`analysis/ida/export/ghidra_dump/`
|
||
- `keyfuncs_decomp_CoreSDKLib.dll.txt`(核心函数伪代码)
|
||
- `callers_decomp_CoreSDKLib.dll.txt`(调用关系 + 主管线)
|
||
- `exports_decomp_CoreSDKLib.dll.txt`(全部导出函数)
|
||
- `exports_decomp_ThermalSDK.dll.txt`
|
||
- `all_functions_decomp_libcoresdk_arm64.so_00100000.txt`(ARM64 21.6MB)
|
||
- DDT 文件:`build-artifacts/mag160c_official.ddt`、`analysis/ida/Core160043865.ddt`
|
||
- 官方抓帧:`analysis/pairs_verify_20260813/`、`analysis/pairs_final_20260813/`
|
||
- demo3 v5:`csdk/tools/mag160c_demo3.c`
|
||
- 采集工具:`csdk/tools/tsdk_pair3.c`(含 DDT 复制、表/盲元/快门/窗口抓取)
|