这个有趣的 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> |
把上面代码复制粘贴到浏览器,然后前往。
从上图可以看出:真实内容为你女朋友‮口一你了亲还‭撤回了一条消息
,显示出来却变成了你女朋友撤回了一条消息还亲了你一口
,这就是‮
和‭
控制字符作用的结果。
因此只要复制 你女朋友撤回了一条消息还亲了你一口 这段内容(如上所述,真实内容中有‮
和‭
这两个控制字符),将其修改为群昵称,就会出现最开始题图的那种效果了。
蛤蛤~好好玩哦~
不过呢,装逼要趁早。大腾讯的程序猿们在后台检测到部分用户用此bug撩妹,人家可能中午饭都没来得及吃就封了这个bug。
中午过后,据群友反映,大多数机型尝试用此法修改昵称时微信会弹出:可能包含<>/等特殊符号,请修改后重试。
的失败信息。
道高一尺,魔高一丈。后来,网友另辟蹊径,这个 bug 貌似还没完全解决,大家继续玩得很 high,感兴趣的 iPhone 用户可以参考这篇文章:http://t.cn/RqC6dvq 试下。