小程序码生成技术分析
阅读之前
异或操作
在此之前,我们先来学习一下逻辑异或(XOR,符号是⊕ )的基本知识,请看下面这个表格。
A ⊕ B ,当 AB 不等时值为1,AB 相等时值为0。A和同一个 B 经过两次异或操作,结果跟原码相同(A ⊕ B ⊕ B = A)。
背景
QRCode
QR码/图码 (英语:Quick Response Code;全称为 快速响应矩阵图码 )是二维码的一种,于1994年由日本汽车零组件大厂电装公司的原昌宏所发明 。
异形码
小程序码属于异形码,异形二维码并不是微信的首创,Facebook、Snap 等公司都已经推出过类似的异形码:
小程序VS传统二维码
传统二维码往往以有下几个缺点:
- 扫码预期:每张二维码的背后可能代表一个文件,一个页面、又或者是一个应用
- 安全性:二维码由于其开放性,很容易成为木马病毒的温床,很多人会担心扫码之后可能使自己的手机感染病毒而放弃扫码
- 品牌宣传:无法满足小程序的品牌宣传需求
小程序码的优点:
- 观赏性:小程序码与普通二维码相比,看起来更美观
- 扫码预期:扫码前能明确知道扫码之后将会体验到一个小程序
- 安全性:小程序码目前只能通过微信产生,并且只能通过微信识别,安全性更高
- 品牌宣传:每个小程序码右下角都是固定的微信小程序 Logo,每见到一次小程序码,大家就能多 一次联想到微信小程序
- 高容错性:当一张二维码图片中间嵌有某些 Logo 图片时,其实相当于是把最中间部分有用的编码信息挖掉,再贴一张 Logo 图片上去。而小程序码不同,中间的 Logo 区并不包含数据编码的部分,因此小程序码拥有更高的容错性
小程序码分析
版本与数据分布
目前小程序码一共支持 3 种容量,分别是 36 射线、54 射线和 72 射线。
36线、54线、72线小程序码数据点用途。黑色是固定图案,黄色可绘制广告、头像等。棕色不存储数据(固定填充为黑、白或跟随临近点颜色);绿色存储元数据,表示该码的版本、纠错等级、掩码图案等;剩余的灰色点存储有效数据。
对于三个不同的版本,元数据(绿色)区域的位置是一致的,这是因为在解码元数据之前不一定能知道小程序码的版本,需要在不知道小程序码版本的情况下识别元数据。
通过目测可以发现,上述三个版本数据区域的总数据点数分别为304、416、528个点(都是16的倍数),分别对应304、416、528个比特。
数据点与数据比特映射关系
根据一些测试,输入比特与图案中黑白点的映射关系如下。
以72线为例,首先将朝向左侧(即9点钟方向)的线记为0号线,顺时针依次是1, 2, ..., 71号线,于是18号线位于12点方向,36号线位于3点方向,以此类推。
然后,比特映射的顺序是,将第1个比特映射至0号线由内往外的第1个有效数据点,第k个比特映射至0号线由内往外的第k个有效数据点。0号线映射完毕后继续按照由内往外的方式映射1, 2, ..., 71号线直至映射完毕。
对于36线和54线,数据映射关系也可以参照上述流程,对于不存在的线跳过,继续映射下一线即可。
举个例子,对于文献[1]开头的那张小程序码,目测它的版本是36线。通过上一节的数据点分布及这节所述流程,可以读到它的元数据为:
01110111001……
它的有效承载数据为(304比特):
100011010111……
掩码图案以及去掩码图案
根据文献[1],小程序生成的最后会将数据点与32个掩码图案之一进行异或。在解码时,可以通过元数据知道该小程序生成时具体用了32个掩码图案中的哪个。
具体掩码图案的获得方 式此处先略去,假设我们已经知道了该小程序具体使用了哪个掩码图案。于是,上述数据经过去掩码(实际的流程是先去掩码,再依次读取数据点)后将得到去掩码以后的数据比特:
0010000011001110011……
纠错编码以及数据编码
经过前几步去掩码、数据比特读取,文献[2]开头的那张二维码已近被转化成一串304位二进制比特。之后的纠错编码与数据编码经过实验发现与QR码完全一致。具体如下。
通过元数据,我们可以知道(具体如何知道的晚些时候再说)上述信息中纠错码的有效数据长度为160比特 = 20字节,纠错码校验位的长度为144比特 = 18字节。
通过对照QR码的校验,巧合的发现,如果使用QR码的纠错码对前20个字节生成校验位,恰好就是后18个字节。当然,这是因为做实验用的小程序码完全没有经过污损,每个比特都是对的。于是,从这个结果可以知道,小程序码的纠错用的是和QR码完全一致的编码,即GF256上的RS码,具体编码及解码方式网上有较多公开文档,此处略去。
将纠错码产出的校验位舍去后可以得到如下有效信息比特载荷:
001000001100111……
我再一次掏出了QR码的编码标准,尝试解码上述有效信息比特载荷,发现数据又是合法的。于是此处又可以省略一大段说明,得到解码后的数据如下。顺便补充说明一下,QR码在编码的过程中会在有效荷载之后依次填充0xEC和0x11两个字节, 直至填充满纠错码的数据位长度,小程序码的填充方式也完全一致。
L+SKD……
通过一些观测,这并不是小程序码承载的真实信息,疑似只是小程序码在生成过程中为了将数据喂给QR码的数据编码而产出的数据。
经过大量测试,我发现上述数据产出过程经过了Base82 -> Base256 -> Base45两次转换。于是,解码过程即首先将上述字符串重新转化成QR码Alphanumeric模式的Base45数组,然后通过一个非标准进制转换将其转换成Base256的数组,再转换成Base82的数组。
需要说明一下,为什么此处想到是Base82,这是因为在这篇 小程序码生成文档 中,scene的参数选取可以是数字、大小写字母及文档中给出的20个字符,加起来就是82个字符。
经过上述转换后,最终就得到了文献[2]开头的小程序码中存储的信息:
k1;/~z……
看起来似乎是加密的信息。
小程序码的生成与识别
定位点
首先确定 3 个定位点和右下角的官方 logo 区,经过第一步小程序码的大小也随着确定。