我们来玩个找茬儿,看看下面这段代码,找找 md5 函数的定义在哪里:
<html>
<head>
<title></title>
<meta charset="utf-8">
</head>
<body>
<script>
Function("".replace(/.{8}/g,function(u)
{return String.fromCharCode(parseInt(u.
replace(/\u200c/g,1).replace(/\u200d/g,
0),2))}))();
</script>
<script>
alert( md5( "hello" ) );
</script>
</body>
</html>
太棒了,我知道难不倒你,10 秒钟就找出来了是不是?好吧,可能我举了一个不是很好的例子,因为它一点用处都没有。
标题里的“短”字加了引号,只是因为它看起来短,实际并不短,因为字节还是在的,在 unicode 里有一种神奇的字符叫零宽空白,它的特点是字型的显示宽度为 0,无论堆了多少个零宽字符,你都看不见它。
就像上面我写的例子中,Function("<这里>".repla...
藏了大量的零宽字符,实际看起来就好像是一个空字符串 ""
,这个“空”字串即是 md5 的函数定义经过编码转换后得到的全零宽字符串,此创意最初源自一个叫 z.js 的库。
每个字符都有一个唯一的编码,将编码以 2 进制表示得到 01.. 的字串,把 1 替换成 U+200C,把 0 替换成 U+200D 就得到一个全零宽空白的字符串,每 8 位零宽字符可用于表示 1 个 ascii 字符,所以例子当中,理论上是变长的,不算解码程序的 129 个字符,仅空白就占了原文 8 倍的体积,如果出现中文,那就更不止了,因为中文已经超过了 ascii 的范围,需要先转成纯 ascii (如以 \uxxxx 表示)后再处理。
在 unicode 里,至少有 U+200B, U+200C, U+200D 和 U+FEFF 四个零宽字符,如果把这 4 个字符全用上,上面的例子又可以减少 1 半的体积,感兴趣的同学自己折腾下。
最后,隐藏代码是不科学的,零宽字符有其它更多更有意义的使用场景,比如用于本地存贮的敏感数据等,游戏里面应该会有需求,就看你的发挥了。
附上一时心血来潮写的 Code Hider v0.1alpha。