卡西欧词典格式还原Huffman解码,缺运行时literal table,求真机 RAM Dump!

我在研究 CASIO EX-word 电子词典扩展内容。

ZH008
Japanese-Chinese Dictionary
日漢大辞典 / 日汉大辞典
内部编号:cz303

目标是从以下文件中完整导出词条文本:

W_CMP.CJD
W_HEAD.CJD
W_TREE.CJD
W_WCT.CJD
W_DBADD.CJT
ZH008D.D01

这是 CASIO 自己的词典格式。

已确认的格式

目前已经逆向出大部分解码流程:

  1. CPU/程序为 SH-4。
  2. ZH008D.D01 加载地址为 0x70000000,入口为 0x70000080
  3. D01 通过 syscall 0x2f 调用固件中的词典解压器。
  4. W_DBADD.CJT 是 24 位大端偏移表。
  5. W_TREE.CJD 包含两棵 Huffman tree。
  6. W_HEAD.CJD 是共用短语库。
  7. W_WCT.CJD 是 96 项字符/代码转换表。
  8. W_CMP.CJD 是主要压缩正文。

W_DBADD.CJT 中共有约:

149,642 个压缩记录偏移

已经能正常提取的文本

下面是从 W_HEAD.CJD 解码并清除控制码后的真实内容:

【アーチ】 arch / アーチ

弓形;弧形;

白球が〜を描く /
(棒球,高尔夫球的)白球画了个弧形;

拱门;门,进出口处和窗的上部用石、砖等做成弓形或半圆形;
有马蹄形拱门、尖顶拱门等;

弧形结构;

〜式のダム /
弧形水库;拱坝;

彩楼;牌楼;
用杉树、扁柏等绿叶装饰而成的门;

另一个片段:

【アーロン】 Henry Arron / アーロン

阿龙(1934- );
美国职业棒球联赛中亚特兰大勇士队的外场手;
1974年打破贝弗・路斯的终生本垒打纪录714球;
1976年退役,终生纪录为755球;

这说明:

  • Huffman tree 的读取基本正确;
  • HEAD 引用位置和长度正确;
  • WCT 输出过滤器基本正确;
  • 日文 Shift-JIS、中文 GB/GBK 和 ASCII 混合文本可以还原。

现在卡住的地方

固件解压器处理 Huffman leaf >16 时,会访问一张 239 项的 literal table:

table_ptr = *(uint32_t *)0x8c7316ec;
pair = table_ptr[(leaf - 17) * 2];

关键地址:

指针槽:0x8c7316ec
表长度:239 × 2 = 478 字节

但静态分析发现:

  • 应用启动 CRT 会清零 0x8c7316ec
  • 固件中能找到的普通赋值点只在 custom WCT/TREE 分支;
  • custom 分支要求 WCT 文件大小 >= 0x161(353);
  • ZH008 的 WCT 是 192 字节,因此不会进入 custom 分支;
  • 完整检查 CY436 NAND 中 294 个 WCT*.CJD,全部小于353字节;
  • 因此 default/fixed literal table 很可能来自运行时、boot ROM、kernel 或文件服务兼容层,而不是普通 NOR/NAND 文件。

我也测试过:

  • 多种推测的239项表;
  • Shift-JIS标点/假名表;
  • 从 W_HEAD 统计高频字符;
  • byte bigram/trigram/4-gram;
  • 从旧款 CY102/CY113 提取的230张历史 literal table。

都能让局部字符“看起来像文字”,但上下文仍然错误,不能用于正式导出。

希望获得的帮助

最直接的办法是请有兼容真机的朋友做一次 RAM dump。

最好是:

CASIO EX-word E-D800
固件/平台:CY436

其他能运行 ZH008 的相近机型也可以尝试。

操作步骤

  1. 将 ZH008 安装到词典。
  2. 开机后进入 ZH008。
  3. 打开几个实际词条,让解码器完成初始化。
  4. 不要重启机器。
  5. 运行 MDUMP
  6. 取得生成的:
EXTERNAL.BIN

CY436 上应该是从 0x8c000000 开始的16MB RAM。

MDUMP 不是我编写的,来自开源项目 dictscript

dictscript/
  softwares/
    run-on-dictionary/
      MEM_dumper/
        build/cn/MDUMP/
          mdump.d01

GitHub:

https://github.com/brijohn/libdataplus
https://github.com/dictdump/dictdump

如果愿意提供整个 EXTERNAL.BIN,我可以直接分析。

如果有人研究过 CASIO sys2f、D01、CY436 内存布局,或者手上有 EXTERNAL.BIN、boot ROM dump,也欢迎联系。

这个厉害了,我之前想提取拉鲁斯来着,然后加载到ida然后发现ida没有这个架构的反编译器然后这个汇编我也不熟就先搁置了。这个因为有书可以对比文本,那通过文本来反推表格不知道是否可行。

都是人才啊 :laughing:

应该是不可以…

是不是因为你写错了, 才会卡住?
根据你写的指针槽:0x8c7316ec,不过我不懂指针槽是什么,猜测应该是跟指针相关的。

  1. 如果0x8c7316ec 存放的是一个指针, 你想读取地址 0x8c7316ec 处的 4 字节,将其作为表的首地址,那么应该写 uint32_t *table_ptr = *(uint32_t **)0x8c7316ec; 因为你后面写了table_ptr[(leaf - 17) * 2];

  2. 如果0x8c7316ec 本身就是表地址,那么应该写uint32_t * table_ptr = (uint32_t *)0x8c7316ec;

也就这2种情况了。

我怀疑 table_ptr = *(uint32_t )0x8c7316ec; 是一个错误点,在你写了 pair = table_ptr[(leaf - 17) * 2]; 的情况下,我不理解你为什么这样写 table_ptr = *(uint32_t *)0x8c7316ec;

就是写错了。
已经解决了。