1. 珊瑚码定义
这种二维码是在某金融机构打码机上使用的一种形似二维码但是又不遵循二维码标准的图形码。其形状在正方形四个角上有回型图案,中间为一个大型回字图案。
在生物学上来看比较像簇生珊瑚,故暂时命名为珊瑚码。
按照逆向结果,此码可以最大为65x65黑白块,但是在该机构只使用了带ECC的27x27大小图形码,下文专门以此规格进行介绍。
如果下文中内容涉及侵权,请联系作者。因无法联系打码机软件作者,本文仅从学术讨论记录重构过程,并不涉及商业使用。
谢绝转载,特别是CSDN之流。本文发布在CNBLOGS博客园。作者cnblogs.com/charset
1.1 逆向过程
本文作者拿到的DLL可以分为两类,加码和解码。软件开发平台以MFC为主,并携带了如CImage.dll和ReedSolomon.dll等辅助动态库,完成图象格式转换和ECC码计算。
本身逆向使用了IDA Pro,使用过程中也发现了伪代码生成逻辑不正确的情况,只能从原始汇编代码手动跟踪。但是大多数场景下伪代码模式可以使用,因为从GDI函数和RS函数入口可分析代码需要做和正在做的事情,从而将整个软件行为串接起来,分析主要的加码和解码函数入手即可。
1.2 得与失
本文作者之前的逆向仅做过一些小型软件,本次从汇编以及OpenCV现学现卖,收货很大,并且按照要求重构为Java8工程。
可惜在逆向和重构过程中,牙龈感染去看医生被诊断为牙隐裂,可能牙齿保不住了。
2. 数据结构说明
2.1 图像示例
#####11111111111111111##### # #11111111111111111# # # # #55555555555555555# # # # #11111111111111111# # #####11111111111111111##### 111111111111111111111111111 111111111111111111111111111 1111111 1111111 1111111 ########### 1111111 1111111 # # 1111111 1111111 # ####### # 1111111 1111111 # # # # 1111111 1111111 # # # # 1111111 1111111 # # # # 1111111 1111111 # # # # 1111111 1111111 # # # # 1111111 1111111 # ####### # 1111111 1111111 # # 1111111 1111111 ########### 1111111 1111111 1111111 111111111111111111111111111 111111111111111111111111111 #####11111111111111111##### # #11111111111111111# # # # #11111111111111111# # # # #11111111111111111# # #####11111111111111111#####
2.2 图像规范
| 符号 | 规律 |
|---|---|
| # | 黑色 |
| 空格 | 白色 |
| 1 | 数据域 |
| 5 | 明暗相间识别域 |
数据域按照比特的值是1置为黑色,0置为白色。
明暗相间识别域以白-黑的间隔进行填充。这个域是代表此面向上,用于图形的旋转校正。
实际上使用的#以>=3的数字替代,空格以0替代。
3 加码过程
3.1 比特流存储器
假设所有的数据域操作都通过比特流存取器完成。下文以BitStream来表示,并给出操作原语。
3.1.1 Write方法
void Write(byte b, int len);
Write方法将b从高位向BitStream写入len个位。len应当在1至8之间。
同时写指针位置向后移动len,如果该操作需要扩充比特流存取器则自动扩充1个字节。
3.1.2 Read方法
byte Read(int len);
Read方法将len个位写入byte类型,从低往高填充。len应当在1至8之间。
同时读指针位置向后移动len。
3.1.3 ToArray方法
int[] ToArray();
byte[] ToArray();
ToArray方法将当前存放的比特流按照顺序按比特写入数组。提供int[]的返回值主要对接Reed Solomon的容错API。
3.2 加码算法
加码算法使用3种加码策略:纯数字模式、字典模式、字符模式。对于数字和字符来说,这几种模式加码使用的位数不同,算法使用了带回溯的动态规划算法,使得加码所需的位数最少。(原公司仅使用例如最大/最小连续数字等判断方式,计算的加码模式不一定是最优。)
所有模式均以4个位的模式码开头,并写入每个模式可对应的内容长度。如果模式为0b0000,则加码器结束。
3.2.1 纯数字模式
纯数字模式所需的位最小,优先以纯数字加码。
3.2.2 字典模式
当组内字符串仅包含数字、大写英文字母、空格、$%*+-./:时使用字典模式。
字典模式并未在场景中使用,因为转账支票的类型都是小写字母。故可省略。
3.2.3 字符模式
当组内字符串无法满足纯数字模式和字典模式,只能使用字符模式。
以每个字符的ASCII值写入。
3.2.4 停止模式
加码结束后,写入mode = 0b0000,并填充完成当前所占字节。
3.2.5 ECC
容错校验算法使用Reed Solomon算法。
伽罗华域配置为0x11d多项式生成,256域大小,0多项式生成因子。
代码从zxing的Java实现中拷贝,不采用整个包。
该金融机构该场景中使用21个字节生成34个容错码。
3.2.6 图像绘制
加码完成后,使用BitStream中按位,在SmallMatrix中从左到右,从上到下进行遍历,仅填充当前是1的单元。
图像的位有一个特殊的转置算法,并不像其他算法那样填充。
4. 解码
解码过程是加码的逆过程,不在此展开赘述。
5. 识别
原识别算法采用的纯GDI分析,重构后的识别算法使用OpenCV库编写的算法达到快速效果。
读取图片后的灰度化、区域识别、去噪等均是普遍做法,这里略过。
5.1 栅格化数据
将近似正方形区域标记出来后,将宽和高都除以该场景所用的值,对每个区域计算其黑度,即黑色像素占区域的大小。一开始使用的矩形区域,计算出来由于打印机扫描件等原因不是很理想,所以换成圆区域。
区域黑度的阈值从30%-70%依次进行检测,每次递进10%。
首先判断间隔带(即在SmallMatrix中单元为5的格子),如果间隔带不存在则将矩阵逆时针转动90度再进行判断,转动3次还未能定位间隔带,则报错这个图案不是珊瑚码。
5.2 解码
不赘述。根据BitStream中的内容结合ECC进行解码,解码函数提供了一个预期正则,可辅助判断是否解码成功。