你女朋友撤回了一条消息还亲了你一口

这个有趣的 bug 再次体现了网友对微信这个社交神器细致入微的研究精神,bug 复现的场景是这样的:

在聊天窗口,当你撤回一条消息时,你看到的是:你撤回了一条消息,对方(单聊)或其他人(群聊)接收到的是 xxx 撤回了一条消息,其中 xxx 是你的昵称。

作为一个技术嗅觉敏锐的程序员,玩了一下这个小伎俩后,我关心的是这个 bug 是如何产生的。

可以猜想,问题的根源出现在群昵称上,在群聊时,用户可以自定义在群里显示给其他人的昵称。

给大家来推演下「你女朋友撤回了一条消息还亲了你一口」是如何实现的。

排除掉微信后台遭攻击,客户端发来的消息被恶意篡改这一可能性,我们把关注点放在客户端上面。

就这一条消息来说,微信客户端涉及到字符串的拼接和显示,拼接方式:[你的昵称] + 撤回了一条消息,这个是微信的产品经理张小龙定好的规矩,谁也改不了,只要是系统能够显示出来的字符,显示出来都是这个顺序。

类似于 MySql 注入漏洞,有关字符串拼接后的结果,要是验证逻辑处理不当,也会出问题。

在猥琐的技术人眼中,这个问题转化为:[你的昵称]+撤回了一条消息 怎么才能显示成 你女朋友撤回了一条消息还亲了你一口?

这里涉及到计算机世界里面的一个冷门知识:UNICODE 控制字符

我们浏览网页,文字的显示方向默认是从左到右,但是还有部分阿拉拍语言国家的文字是从右往左读的。如何控制文字的显示方向呢, UNICODE 里面有具体的规定,在一段文字前面,加上某个特殊控制字符,就可以改变它们的显示方向。

比如说:

  • RLO 字符,它的代号是 ‮,强制字符显示方向为 从右到左
  • LRO 字符,它的代号是 ‭,强制字符显示方向为 从左到右

由于这类控制字符无法直接用键盘打出来的,所以正常情况下我们看不到。但是无法打出来不代表不能用,可以硬编码出来,再复制黏贴过去,微信的这个 bug 就栽在这里,他们的程序猿忘记对昵称进行特殊字符过滤了。

比如你的群昵称为:你女朋友RLO口一你了亲还LRO (RLO 和 LRO 不会真实显示出来,当成空白看待就好)

当在微信中撤回一条消息时,其他人的微信客户端将收到提示信息:

你女朋友RLO口一你了亲还LRO撤回了一条消息

注意,这个信息是存在内存中还没有显示到微信界面的。现在我们来逐字模拟当微信在显示这段提示信息的时候发生了什么……

由于系统默认从左往右小时,当微信处理到时,当前光标位置在后面,接下来是RLO控制字符,它后面的字符全部会改变成从右至左的显示顺序。

下个要处理的字符是“口”,变成:

你女朋友口

然后处理`一`:

你女朋友一口

由于是从右到左显示,口一变成 一口,然后处理

你女朋友你一口

……

依此类推,直到处理 ,此时已经显示的字符为:

你女朋友还亲了你一口

光标位置仍然还在后面,现在是 LRO 控制字符,它后面的字符又改成从左至右的显示顺序:

接下来,处理

你女朋友撤还亲了你一口

……

依此类推,直到处理到

这样,最终显示到微信界面上就是:

你女朋友撤回了一条消息还亲了你一口

比如说:

1
data:text/html;charset=utf8,<script>document.write('你女朋友'+String.fromCharCode(8238)+'口一你了亲还'+String.fromCharCode(8237)+'撤回了一条消息')</script>

把上面代码复制粘贴到浏览器,然后前往。

从上图可以看出:真实内容为你女朋友&#8238;口一你了亲还&#8237;撤回了一条消息,显示出来却变成了你女朋友撤回了一条消息还亲了你一口,这就是&#8238;&#8237;控制字符作用的结果。

因此只要复制 你女朋友撤回了一条消息还亲了你一口 这段内容(如上所述,真实内容中有&#8238;&#8237;这两个控制字符),将其修改为群昵称,就会出现最开始题图的那种效果了。

蛤蛤~好好玩哦~

不过呢,装逼要趁早。大腾讯的程序猿们在后台检测到部分用户用此bug撩妹,人家可能中午饭都没来得及吃就封了这个bug。

中午过后,据群友反映,大多数机型尝试用此法修改昵称时微信会弹出:可能包含<>/等特殊符号,请修改后重试。的失败信息。

道高一尺,魔高一丈。后来,网友另辟蹊径,这个 bug 貌似还没完全解决,大家继续玩得很 high,感兴趣的 iPhone 用户可以参考这篇文章:http://t.cn/RqC6dvq 试下。