_extract\embedded_1_42MB.bin(MainForm.exe 内嵌的 C++ exe) · 2026-09-27 · 全静态反汇编,未运行程序、未连接设备不是「芯片里存着一个余量数字」那种读法。芯片/读头这一侧只提供一个号码。 软件干的是两件事:
sub),
而且串口一重连就被清零(0x44CDA7 / 0x44CDB3)。所以「墨水空」的真正含义是:软件这本账掉到 0 了,而不是芯片在报数。
但关键是:这本账的写入动作,就发生在处理读卡结果的那个函数里(0x440B60)。 而且顺序是两道关卡:
所以准确说法是:「空」由 RFID 读卡的成败驱动,但不是 RFID 芯片报出来的余量。
这一点已被现场实测验证 —— 关掉虚拟读头时软件报的是「无法通讯」,开了之后就变成「墨水空 + 溶剂空 都报」,
说明「无法通讯」和「空」是先后两道关卡,中间隔着的就是「读到的编码是不是 0」。
两个耗材同时报空,也是因为两路的校验共用了同一块内存(+0x1FC28),
编码区整片为 0 时两路必然一起失败。
| 方向 | 字节 | 值(实测) | 含义 | 确定度 |
|---|---|---|---|---|
| 软件 → 读头 命令帧 8 字节 | 0 | 01 | 帧头 | 确定 |
| 1 | 08 | 长度 = 8 | 确定 | |
| 2 | A3 | 固定标识 | 确定 | |
| 3 | 30 / 31 | 地址:'0'=墨水、'1'=溶剂 | 确定 | |
| 4 | 3C / 3D / 01 | 命令码(读状态 / 读另一路 / 心跳) | 确定 | |
| 5 | 01 | 固定 | 确定 | |
| 6 | 00 | 固定 | 确定 | |
| 7 | 58 / 65 | 校验 = 前 7 字节异或取反 | 确定 | |
| 读头 → 软件 应答帧 22 字节 | 0–2 | 01 16 A3 | 帧头 + 长度 22 + 固定标识 | 确定 |
| 3 | 30 / 31 | 地址回显 | 确定 | |
| 4 | 00 | 固定 | 确定 | |
| 5 | 64 / 00 | 全帧唯一会变的字节,量纲 0…100,本报告判定为「余量」 | 较可信 | |
| 6–20 | 00 × 15 | 「编码区」。真机采集里从头到尾全是 0 | 含义未知 | |
| 21 | 1F / 7B 等 | 校验 = 前 21 字节异或取反 | 确定 | |
软件内部把读到的「码」按 三个 8 字节块(+0x1FC18 / +0x1FC20 / +0x1FC28)保存,
并用 %C×8 显示到界面。这说明软件期望的是一段十几到二十几字节的编码,
而不是一个短编号。 | 确定 | |||
0x04240D1C 至今没在任何一帧里出现过。
要补上这一段,只能做一件事:把芯片贴在读头上、在有芯片的状态下再采一次。
[全局对象+0x1FC50](墨水)与 [全局对象+0x1FC54](溶剂)——
它们被 RFID 读头读到的那一帧数值写入。粘度缸探针长时间… 那四条走的是下位机上报的探针位,虚拟读头确实管不了,见下方边界说明。)
[0x7114D8]+0x1E4E8 / +0x1E4EC 这两个状态字
(「墨水缸液位低」的读点在 0x412D1A 与 0x415692,条件同样是 < 1),
只是不同界面位置挂了不同文案。粘度缸探针长时间为空 / 长时间有高液位 /
长时间满缸 / 长时间流不满 这四条(提示语在 0x4B25B4 / 0x4B25EC / 0x4B2630 / 0x4B2660,
选取点在 0x428EE5 / 0x428F2F / 0x428F79 / 0x428FBB)。这条也查清了,结论是
它是真的硬件探针,虚拟读头管不了:[0x7114D8]+0x1E4F4 选择,而该状态码在
0x45457D~0x4545B3 处由一个字节的位域解出:
0x45457D mov dl, byte ptr [0x711677] ; ← 下位机上报的 I/O 位字节
0x454585 and al, 0x18 ; 取 bit3 / bit4
0x454587 cmp al, 8 → [0x1E4F4] = 0
0x454593 cmp al, 0x18 → [0x1E4F4] = 2 ; 满缸
0x4545A5 cmp al, 0x10 → 另一分支
[0x711677] 是下位机(主板)经串口上报的位域变量,不是软件账本 ——
所以粘度缸探针报警只能靠下位机侧或改二进制解决,与 ChipSim 无关。
(注:0x43AC8F~0x43ACB2 那一段一字排开的写 0 是初始化清零,
不是解析,别当成数据来源。)
内嵌 exe 是 32 位 PE,imagebase 0x400000,节区表:
| 节 | VirtualAddress | PointerToRawData | 偏移差 |
|---|---|---|---|
| .text | 0x1000 | 0x400 | +0xC00 |
| .rdata | 0xA4000 | 0xA3000 | +0x1000 |
| .data | 0xDD000 | 0xDBE00 | +0x2000 |
| .rsrc | 0x34B000 | 0x127200 | +0x223E00 |
| .reloc | 0x366000 | 0x142200 | +0x223E00 |
结论:文件偏移与 RVA 之间有一个随节区变化的偏移差,不存在通用的
「file offset = RVA」关系。《交接文档》第 2 节给的公式(只加 0x400000)在 .rdata 上恰好
少了 0x1000,它举的那个例子(0xB4814 → 0x4B5814)结果是对的,但公式本身不成立,
照搬到 .data / .rsrc 上会全错。
本报告所有地址均按 VA = imagebase + (文件偏移 − PointerToRawData + VirtualAddress)
换算,并用「在该 VA 处读出的字符串内容是否与预期一致」逐条自校验(下表的字符串全部读出成功)。
| 提示语 | 字符串 VA | 被引用的代码地址 |
|---|---|---|
| 墨水空 | 0x4B20F8 | 0x4282D3、0x42A5B1、0x42B593 |
| 溶剂空 | 0x4B2110 | 0x4282A7、0x42A461、0x42B556 |
| 溶剂容量少,请及时更换 | 0x4B283C | 0x42A4CC |
| 液位小于1时,下位机启动补墨.不允许稀释,此次忽略! | 0x4B9EBE | 0x45672E |
| 墨水缸液位低 | 0x4AD55C | 0x412D28、0x4136AB、0x41391B、0x413C79、0x4156A0、0x4159E0、0x4161B0(与「墨水空」同源) |
| 缺墨,无法供墨,请添加后再试! | 0x4B3138 | 0x42CA30 |
| 墨水RFID无法通讯,请联系厂家解决 | 0x4B7D38 | 0x440DCD |
| 溶剂RFID无法通讯,请联系厂家解决 | 0x4B7DE8 | 0x440D94 |
| 粘度缸探针长时间为空 / 有高液位… / 满缸 / 流不满 | 0x4B25B4 / 0x4B25EC / 0x4B2630 / 0x4B2660 | 0x428FBB / 0x428F79 / 0x428F2F / 0x428EE5(探针通路,本次未展开) |
软件里和「空」有关的判定分成三层,全部读完,链路是闭合的:
0x4282800x428280 push ebp / mov ebp,esp
0x428283 cmp [ebp+8], 1 ; 参数 == 1 ?
0x42828A jne 0x4282B8
; --- 溶剂分支 ---
0x42828C push 0x4B2118 ; "NoticeV"
0x428297 call [0x4A437C] ; USER32!FindWindowExW(已存在就不重复弹)
0x4282A1 push eax / push 0x4B2118 / push 0x4B2110 ; ← "溶剂空"
0x4282AE call 0x49AEC0 ; 通知窗口助手(157 处调用,是通用弹窗封装)
0x4282B8 ; --- 墨水分支(参数 ≠ 1)---
0x4282CD push eax / push 0x4B2100 / push 0x4B20F8 ; ← "墨水空"
这个函数只负责「弹哪一个通知窗」,它自己不做判断。调用它的是主对话框刷新流程:
0x4168E9 push 1 ; 1 = 溶剂
0x4168EA lea ecx, [eax + 0x10E90]
0x4168F0 call 0x428280 ; → 弹「溶剂空」
0x416906 push 2 ; 2 = 墨水
0x41690E call 0x428280 ; → 弹「墨水空」
0x4168C1~0x41690E0x4168C1 mov eax, [0x7114D8] ; 全局单例对象指针
0x4168DC mov edi, 1
0x4168E1 cmp [eax + 0x1E4EC], edi ; 溶剂状态 < 1 ?
0x4168E7 jge 0x4168FA ; 否 → 跳过
0x4168F0 call 0x428280 (push 1) ; 是 → 报「溶剂空」
0x4168FA cmp [eax + 0x1E4E8], edi ; 墨水状态 < 1 ?
0x416900 jge 0x416583
0x41690E call 0x428280 (push 2) ; 是 → 报「墨水空」
[0x7114D8] + 0x1E4E8
溶剂状态[0x7114D8] + 0x1E4EC0x42A390(余量 → 状态 的映射)这是决定「状态字写 0 还是写 1」的地方。逻辑如下(墨水、溶剂两路对称):
0x42A3CB cmp [eax + 0x1FC54], 0x64 ; 溶剂余量 > 100 ?
0x42A3D5 jg 0x42A6BD ; 是 → 整个判定不成立,直接返回
0x42A3DB cmp [eax + 0x1FC50], 0x64 ; 墨水余量 > 100 ? 同上
0x42A3EA cmp [eax + 0x1FC80], 0 ; 标志位为 0 → 返回
0x42A3F6 cmp [eax + 0x1FC7C], 0 ; 标志位为 0 → 返回
; ===== 溶剂路 =====
0x42A40D cmp ecx, 1 ; 溶剂余量 vs 1
0x42A40F jge 0x42A479 ; ≥1 → 走「非空」分支
0x42A411 mov [eax + 0x1E4EC], ebx ; 余量 = 0 → 溶剂状态 := 0 ← 报警点
... 0x42A44A 弹 NoticeV + "溶剂空"
0x42A479 jle 0x42A4EC ; 余量 = 1
0x42A47B cmp ecx, 5
0x42A47E jg 0x42A4EC
0x42A480 mov [eax + 0x1E4EC], esi ; 1 ≤ 余量 ≤ 5 → 溶剂状态 := 1
0x42A4CC push 0x4B283C ; 并提示 "溶剂容量少,请及时更换"
; ===== 墨水路(同构)=====
0x42A55A jge 0x42A5F9
0x42A560 mov [eax + 0x1E4E8], ebx ; 墨水余量 = 0 → 墨水状态 := 0 ← 报警点
0x42A5B1 push 0x4B20F8 ; 弹 NoticeI + "墨水空"
0x440B60(表驱动的读卡结果处理函数)这是整条链的入口。它的地址在整个文件里只出现一次,位于
.rdata 的 0x4B7AF8 —— 也就是说它是被 MFC 的消息表/函数指针表
间接调用的,没有直接 call。函数取三个参数,按第一个参数(数据类别码)
分派,第二个是事件码,第三个是数值:
0x440B60 函数入口(SEH 框架,__thiscall,this 在 ecx)
0x440B8A eax = [ebp+8] ; 参数1 = 数据类别码(0x468 / 0x469 / … / 0x4C9)
0x440B8D ebx = [ebp+0x10] ; 参数3 = 数值
0x440BBD ecx = [ebp+0xC] ; 参数2 = 事件码
; —— 类别码 0x468 分支:把参数 2、3 共 8 个字节拆开显示并保存 ——
0x440C14 push 0x4B7E98 ; " %C%C%C%C%C%C%C%C" ← 8 个字节逐个 %C 格式化
0x440C1E call [0x4A4AD0] ; 输出到 [esi+0x1E760](界面上的 UID / 编码栏)
0x440C39 mov [edx + 0x1FC18], ecx ; ┐ 组装成 64 位整数存入
0x440C47 mov [eax + 0x1FC1C], edi ; ┘
; 类别码 0x469 分支结构相同,写另一组(+0x1FC20)
; —— 事件码分派 ——
0x440D60 cmp ecx, 6 ; 6 = 通讯失败
0x440D6A cmp ebx, 1 → 0x440D94 push 0x4B7DE8 ; "溶剂RFID无法通讯,请联系厂家解决"
0x440D9E cmp ebx, 2 → 0x440DBD push 0x4B7D60 ; "Ink RFID cannot communicate, plz contact the supplier…"
并配 0x4B7D38 "墨水RFID无法通讯,请联系厂家解决"
0x440E41 cmp ecx, 8 ; 8 = 另一类结果
0x440E51 cmp ecx, 0xB ; 11 = 墨水结果
0x440EEF cmp ecx, 0xC ; 12 = 溶剂结果
0x440F8A cmp eax, 0x4C9 ; 另一类别码
+0x1FC18(8 字节) ← 类别码 0x468 写入,同时被 %C×8 格式化显示
+0x1FC20(8 字节) ← 类别码 0x469 写入
+0x1FC28(8 字节) ← 第三块
墨水结果校验:+0x1FC18 块 与 +0x1FC28 块 都非零
溶剂结果校验:+0x1FC28 块 与 +0x1FC20 块 都非零
两块的比较对象都是 0,没有任何白名单 / 查表 / 比对
(0x440E62、0x440E6C、0x440E74、0x440E7E 四条 cmp …, edi(=0))。
换句话说:软件对读到的编码只做「有没有」的判断,不做「对不对」的判断 ——
这与《芯片识别机制与设备激活》里「校验的是长度 + 两次一致」的说法吻合,也是本报告第 7 节
「A 路线」可行的根本原因。
+0x1FC7C,与清 0 无关。| 地址 | 指令 | 含义 |
|---|---|---|
| 0x429CF0 | sub [ecx+0x1FC50], eax | 墨水余量递减(用量扣减) |
| 0x429D16 | cmp [eax+0x1FC50], 0 / jge | 递减后判负 |
| 0x429D1F | mov [eax+0x1FC50], 0 | 负数归零(不会变成负值) |
| 0x414FF0 | mov [eax+0x1FC54], 0 | 溶剂余量置 0(清零路径) |
| 0x43ECEA / 0x43ECF0 | mov [esi+0x1FC50], ebx / [esi+0x1FC54], ebx | 两个余量一起初始化 |
| 0x44CDA7 / 0x44CDB3 | mov [edx+0x1FC50], ebx / [ecx+0x1FC54], ebx | 串口初始化流程里清 0 |
注意 0x44CDA7 落在串口初始化函数内(同函数里紧挨着
SetCallbackFunc4(0x453940) 与 OpenSerial4 的调用点 0x44CC4A / 0x44CC38),
说明每次串口重连都会把余量账本清 0。这一条现场值得留意:拔插串口线之后
重新读表,余量会回到 0,看起来就像「突然没墨了」。
墨水/溶剂RFID无法通讯。墨水空 与 溶剂空 两个都报。「两个都报」这一点反过来印证了本报告推断的内存布局:墨水那路的校验是
+0x1FC18 块 + +0x1FC28 块,溶剂那路是 +0x1FC28 块 + +0x1FC20 块 ——
两路共享 +0x1FC28 这一个块。所以当编码区整片是 0 时,三个块全为 0,两路必然同时判定失败、同时报空,与现场现象完全一致。
+0x1FC20 块保持 0、另外两块非零,则应当
只有「溶剂空」出现、墨水不再报空(墨水校 0x1FC18+0x1FC28,两块都过;
溶剂校 0x1FC28+0x1FC20,后一块为 0 → 失败)。RFReader.exe 那一层。要打通这一层,需要在有芯片的机器上采一次真实数据做比对。
这条现象独立验证了本报告的判定链,并且把「空」的触发点精确落到了编码校验上:
| 阶段 | 报什么 | 对应的代码位置 | 触发条件 |
|---|---|---|---|
| 关卡 1 | 墨水/溶剂 RFID 无法通讯 |
0x440D60 cmp ecx, 6 → 0x440D94 / 0x440DBD |
压根没读到东西(读头不应答 / 串口不通) |
| 关卡 2 | 墨水空 / 溶剂空 |
0x440E51 / 0x440EEF → 校验失败落到 0x440EE4 写 0 | 通讯通了,但读到的「码」是 0 |
两个提示是先后两道关卡,不是互相替代:ChipSim 把第一道打通了(有应答了), 于是软件往下走,撞上了第二道 —— 而 ChipSim 默认档正好过不了第二道。原因是软件要的 不是「有个 8 字节数就行」,而是「几个 8 字节块都不能是 0」:
| 软件校验的内存块 | ChipSim 各档实际填的内容 | 结果 |
|---|---|---|
墨水需 +0x1FC18 块(8B)与 +0x1FC28 块(8B)都非零溶剂需 +0x1FC28 块(8B)与 +0x1FC20 块(8B)都非零→ 合计 +0x1FC18 ~ +0x1FC2F 共 24 字节全部非零 |
-Profile auto(默认):编码区 全 0 |
必然报空 |
-Profile chip:编码区 04 24 0D 1C + 11 个 0 |
必然报空 | |
-Code "…" 填满 15 字节非零 |
具备通过条件 |
ChipSim.cmd -Port COM12,COM13 -Profile auto -Code "04 24 0D 1C 04 24 0D 1C 04 24 0D 1C 04 24 0D" -Seconds 0
-Code 按传入长度逐字节填进应答帧的
15 字节编码区(ChipSim.ps1 第 86–95 行,for ($i=0; $i -lt 15; $i++),
传几个填几个、剩下的保持 0)。上面这串是 04 24 0D 1C 重复 3 次再加 04 24 0D,
正好 15 字节、全非零。-Seconds 0 是一直运行到 Ctrl+C(默认值也是 0),避免它中途自己退出。cmp [esi+0x1FC9C], 1、0x440E93 cmp [ecx+0x1FC48], 1;
任一不满足就跳去失败路径写 0):
| 标志 | 偏移 | 谁把它置 1 |
|---|---|---|
| 标志 1 | +0x1FC9C | 0x440E1E(本处理函数的事件 2 分支)、0x44CDEF(串口初始化) |
| 标志 2 | +0x1FC48 | 0x428224(读头状态判断函数内) |
RFReader.exe 那一侧在收到应答后设置。所以如果编码区填满仍不通,
问题就落在「RFReader 有没有把这次读取当成一次成功读取」上。
RFID 读头 ──串口──▶ VSNScd.dll ──回调 0x453940──▶ 消息分派 0x440B60
│
事件 0xB / 0xC + 数值 + 8 字节编码校验
▼
[0x7114D8] + 0x1FC50(墨水余量)
+ 0x1FC54(溶剂余量)
│
余量 → 状态 映射 0x42A390
▼
[0x7114D8] + 0x1E4E8(墨水状态 0/1)
+ 0x1E4EC(溶剂状态 0/1)
│
主对话框刷新 0x4168C1
▼
0x428280(int) → 弹 NoticeI / NoticeV
"墨水空" / "溶剂空"
| 原文位置 | 原结论 | 本次结论 |
|---|---|---|
| 第 6 节 「液位传感器 ≠ 芯片」 |
「墨水空/溶剂空」来自物理探针,与 RFID 芯片无关,虚拟读头管不了 | 需修正「墨水空 / 溶剂空 / 墨水缸液位低」三条提示的判定源都是
一对余量计数器(+0x1FC50 / +0x1FC54),
而计数器的值由 RFID 读取结果写入(0x440EAD / 0x440F4B)。
虚拟读头在这一层可以影响结果。 |
| 第 6 节 | 把「墨水缸液位低」与「墨水空」当作两条独立来源(探针 / 计数) | 需修正两者同源:引用点 0x412D1A、0x415692
读的都是墨水状态字 +0x1E4E8,条件同样是 < 1。
所谓「墨水缸液位低」只是同一状态在另一处界面上的另一种文案。 |
| 第 3.5 节 | 「没有用量配额」——芯片只存 4 字节 UID | 需补充芯片数据区确实不等于配额, 但读头回帧里的状态字节(0x64=100)就是余量,软件拿它当耗材余量用。 芯片「无配额」与「余量由读头给出」可以同时成立,但后者是现场会实际触发报警的那一条。 |
| 第 2 节 地址换算 |
VA = file offset + 0x400000 | 公式需补各节区另有偏移差(.rdata 差 0x1000、.data 差 0x2000), 报告里举的 0xB4814 例子的结果是对的,但公式照搬会错。 |
《ASM_实测数据解读》里有一个当时无法定性的问题:应答帧第 6 字节在
0x64(100) 与 0x00(0) 之间取值,并列了「A. 耗材余量百分比」与
「B. 读头检测状态」两种假设,未判定。
0 … 100(>100 直接跳过判定,1~5 判「容量少」,0 判「空」),
而读头回帧的状态字节取值正好是 0x64 = 100 与 0x00 = 0。
两个独立来源的数值域完全重合,且 100 被当作满量基准 —— 假设 B(0/1 状态位)
无法解释「为什么值域是 0…100」。
顺带解释了一个现场现象:《ASM_实测对比_v3》记录到「两次采集相隔 24 分钟数据一字未变」, 如果状态字节是余量,那么当时那支耗材的余量一直是 100,说明余量不随软件内的 用量扣减而变——它只在读头重新读数时被整体覆盖。这与 0x429CF0 的单向递减 (只减、由读头重置)是一致的。
| # | 路径 | 做法 | 风险 / 代价 |
|---|---|---|---|
| A | 不改代码,靠 ChipSim 推荐先试 |
ChipSim 用 -Code 把应答帧的 15 字节编码区填满非零
(软件要 +0x1FC18~+0x1FC2F 共 24 字节全非零,只填 4 字节卡号不够):-Port COM12,COM13 -Profile auto -Code "04 24 0D 1C 04 24 0D 1C 04 24 0D 1C 04 24 0D" -Seconds 0 |
无。已由现场实测确认「通讯能通、卡在编码校验」这一步。 |
| B | 用软件自带功能 | 工程师模式下的「耗材归零」对应 0x429D1F / 0x414FF0 的置 0 逻辑, 也可以反向利用:把余量重新写成满量。 | 低。但需要在工程师模式下操作,且只能事后补救、无法自动。 |
| C | 改二进制 最后手段 | 把 0x42A390 里的两处「写 0」改掉:0x42A411 mov [eax+0x1E4EC], ebx → 写 1000x42A560 mov [eax+0x1E4E8], ebx → 写 100(两条都是 6 字节 89 98/90 ?? ?? ?? ??,可原地替换成
mov dword ptr [reg+off], 0x64,长度刚好) |
高。要改原始 exe,破坏文件校验与可维护性,且必须先在测试机上验证。 仅在 A/B 都不通时使用。 |
ChipSim.cmd -Profile chip(编码区带卡号、状态 100)。sim.log 与软件界面截图留档后再继续查。| 结论 | 强度 | 依据 |
|---|---|---|
| 两个状态字偏移 0x1E4E8 / 0x1E4EC 及「<1 即报空」 | 确证 | 0x4168E1 / 0x4168FA 直接可读 |
| 阈值 100 / 5 / 1 与余量区间 0…100 | 确证 | 0x42A3CB / 0x42A40D / 0x42A47B 直接可读 |
| 余量由 RFID 结果消息写入 | 确证 | 0x440EAD / 0x440F4B 与同函数内 RFID 通讯失败提示并列 |
| 读到的码只做「非零」校验,无白名单 / 查表比对 | 确证 | 0x440E62 / 0x440E6C / 0x440E74 / 0x440E7E 四条 cmp …, 0,之后直接置标志,全程无查找 |
| 校验不过时余量当场被写 0(无需累计) | 确证 | 0x440EBE → 0x440EC3 → 0x440EE4 顺序执行;「累计 5 次」只用于置 +0x1FC7C 标志。已由用户现场实测交叉验证 |
| 「通讯失败」与「读空编码」是两道先后关卡 | 确证 | 0x440D60(事件 6,通讯失败)在前,0x440E51 / 0x440EEF(事件 0xB / 0xC,结果处理)在后;用户现场实测:关掉 ChipSim 报「无法通讯」,开 ChipSim 后墨水空与溶剂空两个都出现 |
两路校验共享 +0x1FC28 块,故全 0 时必然同时报空 | 确证 | 墨水校 0x1FC18/0x1FC28 块,溶剂校 0x1FC28/0x1FC20 块;与用户实测「两个都报」一致 |
| 余量写入还要求两个前置标志为 1 | 确证 | 0x440E8B cmp [esi+0x1FC9C], 1、0x440E93 cmp [ecx+0x1FC48], 1,不满足同样走失败路径 |
| 余量数值 = 该处理函数的第 3 个参数 | 确证 | 0x440E9B 暂存 → 0x440EAD / 0x440F4B 写入 |
| 该参数对应应答帧里的哪一个字节 | 未确认 | 处理函数由 .rdata 消息表(0x4B7AF8)间接调用,静态看不到调用方传参;需动态调试或按取值域反推 |
| 软件按三个 8 字节块保存读到的码(共 24 字节) | 确证 | 0x440C39 / 0x440C47 写 +0x1FC18 块;0x469 分支写 +0x1FC20 块;校验另引用 +0x1FC28 块 |
| 事件码 0x0B = 墨水、0x0C = 溶剂 | 推断 | 由两分支写入的变量(0x1FC50 vs 0x1FC54)与提示语配对推得,未动态验证 |
| 「墨水缸液位低」与「墨水空」同源 | 确证 | 0x412D1A 与 0x415692 均读 [..+0x1E4E8] 并与 1 比较后弹该提示 |
| 粘度缸探针四条与余量通路相互独立 | 确证 | 状态码 +0x1E4F4 只由 0x428Exx 区读、由 0x45458B/0x454597/0x4545B3 写;写入源是 [0x711677] & 0x18 位域,与 0x1FC50/0x1FC54 无任何交叉引用 |
| 粘度缸探针状态来自下位机上报而非软件账本 | 推断 | [0x711677] 无静态初值来源、按位域语义解读,符合下位机帧字段特征;未定位到具体的帧解析语句 |
本次未做:没有运行任何程序、没有动态调试、没有连接设备。
所有结论都可由 _extract\embedded_1_42MB.bin 静态复核。
| 用途 | VA | 说明 |
|---|---|---|
| 全局单例对象指针 | 0x7114D8 | 几乎所有状态都在它的偏移上 |
| 墨水状态字 | +0x1E4E8 | 0 = 报墨水空 |
| 溶剂状态字 | +0x1E4EC | 0 = 报溶剂空 |
| 墨水余量 | +0x1FC50 | 0…100,0 = 空 |
| 溶剂余量 | +0x1FC54 | 0…100,0 = 空 |
| 读到的「码」——第 1 块 | +0x1FC18(8 字节) | 类别码 0x468 写入;同时被 %C×8 格式化显示到 +0x1E760 |
| 读到的「码」——第 2 块 | +0x1FC20(8 字节) | 类别码 0x469 写入 |
| 读到的「码」——第 3 块 | +0x1FC28(8 字节) | 墨水与溶剂的有效性校验都要用到它 |
| 前置标志 1 | +0x1FC9C | 置 1 于 0x440E1E;须为 1 余量才会正常写入 |
| 前置标志 2 | +0x1FC48 | 置 1 于 0x428224;须为 1 余量才会正常写入 |
| 界面 UID / 编码显示串 | +0x1E760 | 由 0x440C14 的 " %C%C%C%C%C%C%C%C" 生成 |
| 余量 → 状态 映射函数 | 0x42A390 | 两处「写 0」在 0x42A411 / 0x42A560 |
| RFID 消息处理函数 | 0x440B60 | 事件码分派;余量写入口 0x440EAD / 0x440F4B |
| 串口回调 | 0x453940 | 由 SetCallbackFunc4 注册 |
| 串口发送封装 | 0x453830 | 内部调 SendDataToSerial4,固定发 2 字节 |
| 通知窗口显示函数 | 0x428280 | 参数 1 = 溶剂、其余 = 墨水 |
| 主对话框刷新流程 | 0x4168C1~0x41690E | 读状态字并决定是否弹窗 |
| 粘度缸探针状态码 | +0x1E4F4 | 由 0x45458B / 0x454597 / 0x4545B3 写入,源为 [0x711677] & 0x18 |
| 下位机 I/O 位域字节 | 0x711677 | 粘度缸探针 bit3/bit4;与 RFID 通路无关 |
| 粘度缸探针提示选取 | 0x428EE5 / 0x428F2F / 0x428F79 / 0x428FBB | 四条「粘度缸探针长时间…」提示 |
# 依赖:capstone、pefile
python -m pip install capstone pefile
# 1) 正确换算文件偏移 → VA
# VA = 0x400000 + (off - RawOfSection + VAOfSection)
# .text (0x400→0x1000) .rdata (0xA3000→0xA4000) .data (0xDBE00→0xDD000)
# 2) 取字符串:在目标 VA 处读 UTF-16LE 到 \x00\x00
# 例:0x4B20F8 → "墨水空"
# 3) 找引用:在 .text 里搜 struct.pack('