微信小程序码私有编码协议分析(继续中)

微信小程序码私有编码协议分析(继续中)

摘要

本文试着分析了微信小程序码的私有编码协议,当前已经可以从小程序码中提取有效信息。需要说明,当前所能提取的信息也仅仅是与wx.scanCode接口返回的result一致,其内容疑似是加密的数据,只在特定情形下有少量未加密数据。

本文主要参考了以下两篇官方文章所透露的一部分小程序码协议,并参考了QR码(即常见的二维码)的编码标准

[1] 【小程序码设计篇】菊花绽放

[2] 小程序码是如何「绽放」的

[3] QR_code

小程序码版本及数据点分布

根据文献[2],小程序码的版本分为36线、54线、72线,每条线上共计13个可识别的黑白数据点。同时,文献[2]给出了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……

看起来似乎是加密的信息。

目前还未完成分析的部分

以上只是对小程序码协议一部分的分析,整个协议还有很大一部分尚未分析透彻。特别是涉及到元信息解析的部分,包括掩码图案如何获得、纠错码长度如何获得、是否存在其他数据解码/解密方式等等。

元信息解析我做了一些尝试,由于目前官方并不支持让用户选择纠错等级和小程序码版本,很难通过输入不同的数据遍历所有的元信息,当前我测试得到的信息如下。

  1. 目前我已经见过156种不同的元信息比特,都已能正常解码。
  2. 这些元信息比特应该是采用了某个线性分组码(未知) + 中心对称重复两遍。
  3. 目前已经采集到的元信息向量所张成的GF2上的线性空间一共有512个元素(已排除可能的元信息掩码产生的影响),对应9比特有效信息,与文档[1]给出的信息(5比特掩码 + 2比特纠错等级 + 2比特版本)吻合,但尚未发现明确的对应关系。
  4. 32个掩码图案已经初步分析完毕,但无法与元数据中的5个比特对应上,疑似不是简单的32图案对应5比特的方式。36、54、72线及纠错能力也无法与具体的2个比特对应上,疑似是个混合的编码。
编辑于 2022-08-12 12:31