先给题主一个小思路吧,假设这个二维码完好无损,我以四个汉字编为二维码为例(最小尺寸)。
由于在知乎二维码会自动转换成文字,因此原图见该链接:
https:// pic4.zhimg.com/v2-07f7d b414934b822c49552c113224cb9_b.png
不想点链接的,可以看技术处理过的图:
注意事项:
我想大家初次接触二维码时最深的印象肯定是三个角落的正方形,因为非常显眼,它们叫寻象图形。事实上,里面可能还暗藏小正方形(最小尺寸的二维码没有),叫校正图形。此外,连接两个相邻的寻象图形的内侧边缘处正好有黑白相间的线条,叫定位图形。最后,三个寻象图形的旁边会有一些格式信息(左上寻象图形的两侧、右上寻象图形的下侧、左下寻象图形的右侧各一排):
而我们真正要破译的部分,则是剩下的浅绿色的区域,当然不是全部,因为其中只有一部分能得到我们想要的数据,剩下的部分是纠错码字(有了它,可以在二维码里贴图徽而仍能扫出)。
值得一提的是,尺寸再大一点的二维码,即校正图形有 6 个及以上时,在右上寻象图形左侧三排、左下寻象图形上侧三排,还会有版本信息这东西,在破译时也要避开这些位置:
最后稍微了解下二维码的解码顺序,整体而言是从右边起,向上向下交错:
好,了解这些区域在破译时要避开后,我们现在可以开始实战演练了!
可能有些人阅读下文后会有人问:去掩码的原理简单,但笔算(甚至心算)起来要非常细心。那么掩码存在的意义是什么?可以见这篇答案:二维码生成时为什么要再加一个掩码图案计算呢?
我们要看左上角的寻象图形正下方的这三块找到相应的掩码:
然后我们不难发现,是下表中的 ((i*j)%3+1+j)%2=0 的款式:
选好款式之后,我们先将掩码铺盖好:
二维码中要去掩码的部分为如下红色区域(即数据与纠错码字区域,由于是最小的二维码,因此没有校正图形):
以白为 0,以黑为 1,去掩码则是将原二维码与掩码同一坐标的数据做半加法(即 XOR,同色得白,异色得黑):
当然也可以交给 Photoshop 处理,建立二维码与掩码图案两个图层(掩码图层在上,二维码图层在下),然后掩码反相,最后两者用差值混合图层(因为差值做的是减法,要反相)。
去掉掩码后如此图(未被上上图红色部分覆盖的部分则原封不动,同时注意此时已扫不出来):
我们现在可以开始解码了,不过先不急着破译。我们要先看的是右下用的 4 块:
编码的顺序为 Z 字型(从右下开始曲折往上),即右下、左下、右上、左上。白(浅)记 0,黑(深)记 1,得二进制数 0100:
常见的有 0001 表示「纯数字」,0010 表示「字母数字」(但字母不区分大小写),0100 表示「8 位字节编码(UTF-8)」(通常使用这种,即使是纯英文文章或网址),1000 表示「日本汉字(要经过计算后转换为 Shift_JIS 编码)」。
接着再往上看 8 块(蓝色框内):
同样我们按照 Z 字形的顺序,破译出 0000 1100,转成十进制数为 12:
在不同的编码类别(红框)下,对编码长度的理解不尽相同,这里用的是字节编码模式,编码长度表示的是字节数,即本文有 12 字节。了解这点非常重要,可以告诉你到哪里破译结束。
值得一提的是,二维码大到一定尺寸时,表示编码长度的区域可能为 8 位以上(视编码类别而定,比如可能扩充为 16 位来表示编码长度),这里不展开讨论。
前面两关过了后,我们关心的破译数据就要开始了。我们知道,一个字节有 8 位,则以 8 位为单位一个个破译下来。首先继续往上看 8 块:
按图示的解码顺序,得第一字节为 1110 0100。
按照顺序继续破译第二字节,不过要注意最上面一排有格式信息的部分,不要编进去,而要转弯向下:
第二字节为 1011 1000。
我们依次破译下去(红色数字表示字节次序):
我们得到:
这是 UTF-8 编码,有关 UTF-8 可以见UTF-8_百度百科,这里不展开讨论。
我们的目的是将 UTF-8 编码转换成 Unicode 编码然后查找对应字符。由于编码的内容为汉字,在 UTF-8 里每个汉字需用 3 个字节来表示,得前三个字节:
转换成 Unicode 时要去掉这三个字节前面 1110、10、10(上面加下划线的部分),剩下的组合在一起得到:
最后转换为十六进制的 4E1C,在电脑中打开字符映射表,查到 U+4E1C 对应「东」字:
(这里只能查表了,除非你能把 Unicode 码表背得滚瓜烂熟。)
同样,后面三个字节:
0101 0011 0101 0111→U+5357→「南」
处理完这 12 个字节后得到「东南西北」四个字,此时破译结束。
总结图表如下:
(上图中的黑色部分为纠错码字部分,一般情况下没有破译的必要,但如果在当中插了图徽或者缺损的二维码,则有可能要利用剩余的纠错模块破译,此时算法较为复杂,本人暂时无法讲解。)
应大家的要求,我增添了相对实用的情况,即二维码的网址的破解方法。如果是纯字母的那还相对容易,如果是混有数字的就有一点难度了。
比如本文的链接为:https://www.zhihu.com/question/65253283/answer/229499103
需要注意的是斜杠「/」,它在 0010(字母数字模式)下也有,在 0100(字节模式 UTF-8)下也有,使用哪一种取决于节省编码长度的方式。
原二维码(原图:https://pic3.zhimg.com/80/v2-8780867e920ca4a5b5c1aa0c5f01f54b_hd.jpg):
去除掩码后:
破译细节举例:
附一:字节模式(0100)下的十进制编码表(UTF-8 或 ASCII 码表)
附二:字母数字模式(0010)下的十进制编码表(注意:不适用网址的小写字母,其中 43 代表斜杠)
原二维码见此图:https://pic1.zhimg.com/v2-6118213d8137444052d30e4b11dd043b_b.png
技术处理图:
去掉掩码后:
这个时候我们仍将视线从右下角看起:
此时 Enc 的编码为 1000,属于日本汉字编码之类;Len 为 0010 0111,十进制为 39,注意在日本汉字模式下表示字符数量而非字节数量,即本文有 39 个字符。
然后比较变态的操作开始了,是每 13 位为一组,比如第一组(图中的数字表示顺序,1 到 13 对应权 到 ):
得到 13 位二进制数:0101101000110,换成十六进制为 B46。
还没完,还要进行一波操作:
我们将原先的 B46 转化为到四位十六进制数 9046,对应 Shift_JIS 中的「色」字(注意要将字符集切换成日语,查的是 0x 起头的编码):
同样,我们继续破译紧接着的 13 位二进制数(蓝色部分,从 1 到 13 的顺序):
0000101001101→14D→
→82CD→「は」
依此类推,我们一个个破译得出:
色は匂へど散りぬるを我が世誰ぞ常ならむ有為の奥山今日越えて浅き夢見じ酔ひもせず
(共 39 字)