一、 背景情况
5月17日起,在各个微信群中(zhōng)流传着一个天線(xiàn)宝宝的GIF表情。在iOS版的微信上,只要打开了包含这个GIF表情的聊天窗口,就会造成微信闪退。
在进行具(jù)體(tǐ)分(fēn)析之前,对崩溃原因进行了猜测:(1)iOS系统自带GIF解析功能(néng)存在问题;(2)微信自己实现GIF解析的功能(néng),由于对输入数据的校验不严格,导致异常。经过测试,发现iOS版QQ不受影响,因此可(kě)以排除iOS的GIF解析问题。
二、 原因分(fēn)析
1. 样本精(jīng)简
初始的GIF样本有(yǒu)1MB之多(duō),不利于定位引起问题的具(jù)體(tǐ)数据,因此我们需要对样本进行精(jīng)简。通过010 Editor打开原始样本GIF,利用(yòng)GIF模板解析,发生解析异常,这就表示样本GIF的格式存在问题。
从模板解析的情况显示,在38帧正常的图片数据后,出现了异常的数据。如图所示,因此我们将正常数据部分(fēn)移除,仅保留异常数据,进行下一步精(jīng)简。
经过不断的排除后,发现异常的数据就在下图的紫色部分(fēn)中(zhōng)。只要带有(yǒu)GIF的图像数据部分(fēn)带有(yǒu)这些异常数据,就会导致iOS微信闪退。
2. 调试分(fēn)析
经过样本精(jīng)简,我们已经发现了引起异常的数据位置。那么,现在就需要结合实际的调试,来确定实际引起异常的数据。以iOS微信6.5.7版為(wèi)例,崩溃发生时的调用(yòng)栈如下,崩溃发生于微信内部,说明是微信自身的GIF解析功能(néng)存在问题。
经过对相关函数的逆向分(fēn)析,最终确定了引起异常的数据。首先来观察sub_100B6CBF0这个函数,对于GIF中(zhōng)的数据进行循环查找,如果存在0x21和0xF9,那么当前数据就表示是一个GraphicControlExtension结构,并接着对GraphicControlExtension数据进行解析。
如果当前查找到的数据為(wèi)0x2C,就表示搜索到了一个ImageDescriptor,跳出while循环,进行实际图片数据的解析。这里也就是异常数据的起始位置!
正常的帧数据的ImageDescriptor数据如下:
而引起异常的数据中(zhōng),恰好存在0x2C这个关键的分(fēn)隔符,导致下述红框中(zhōng)的数据被解析成了一个ImageDescriptor。可(kě)以看到,ImageWidth属性為(wèi)0,ImageHeight属性為(wèi)0x100。
接下来,这部分(fēn)异常的数据就会进入sub_100B6CE90函数进行解析。由于ImageWidth為(wèi)0,导致与ImageHeight相乘后等于0,在new buffer时,传入的大小(xiǎo)参数為(wèi)0,这是第一个问题,但这并不会导致闪退,仍然可(kě)以分(fēn)配一个很(hěn)小(xiǎo)的堆块。
引起崩溃的代码如下,在else block中(zhōng),sub_100B6C4F0的作(zuò)用(yòng)没有(yǒu)具(jù)體(tǐ)跟踪,猜测是进行lzw解压缩,并返回解压缩后的数据長(cháng)度v21。由于v10 = 0x0000010000000000,截断成unsigned int后為(wèi)0,这就导致 v10 – 1 – v21 為(wèi)负数,作(zuò)為(wèi)memset第三个参数,相应的unsigned int形式就是一个很(hěn)大的正数。在memset时,就会导致崩溃,这是第二个问题。
这个问题的根本原因是由于微信实现了自己的GIF解析功能(néng),但由于对输入数据的校验不严格,导致异常的数据被解析,引起崩溃。
在5月17日当天,这个GIF开始流传后,微信似乎在服務(wù)器端做了屏蔽,使得这个GIF无法被正常接收。但我们只要随便修改一下GIF中(zhōng)的任意一个字节,就能(néng)绕过这个屏蔽措施。同时由于iOS应用(yòng)上架需要经过苹果审核,需要额外耗费一定时间。这就使得这个Bug即使修复后,所有(yǒu)用(yòng)户也无法立刻更新(xīn)。目前最新(xīn)的iOS微信6.5.8版本仍然存在崩溃