本帖最后由 B100D1E55 于 2017-5-30 01:32 编辑
安软的测试真的很简单吗?从表面上来看这类测试无非就是收集一系列恶意程序样本,然后进行扫描并统计结果。但概念上的简单不代表实际操作的容易,在这里结合一些资料来介绍安软测试的一些要点,以及可能出现的盲区和谬误
传统安软测试无非就是:
- 收集恶意程序样本
- 让待测产品检测这些样本
- 整理统计结果
接下来谈谈这些环节 1. 收集大量恶意程序样本 到底需要收集多少样本?测试的样本数量不应远小于真实世界中活跃的恶意程序数量(VB100之类的特殊测试例外)。大部分情况下收集恶意程序样本也不算太难,一般可以使用没打补丁的系统主动访问恶意网站等等来收集样本(mi-guan法),当然一些大机构也可以通过安全厂商的样本共享网络实现样本收集。后文会讨论到样本数量对于检测率置信度的影响。 有一些测试机构觉得从安全厂商那里获得的样本可能已经被筛选处理过,因此他们可能会选择对样本进行修改(例如加壳)或者设计一些所谓的病毒模拟器来避免不公正的测试结果。对于前者这种做法,很久以前就有过讨论(将在系列3提及)。而病毒模拟器则普遍不被认为是合理的测试方法,例如(http://vxheaven.org/vx.php?id=sr00)是早年比较典型的代表,仅考察杀软对特定二进制特征的检测,完全没有正确理解杀软的工作机制 撇开上文的问题不谈,收集到的样本真的全都是恶意样本吗?事实上大部分恶意样本来源都包含了不少的无效样本(损坏的PE,已经不工作/不展现行为的,灰色样本,甚至是白样本)。理想情况下收集的样本应当保证都是有效的恶意程序,然而针对海量样本做精准的检测需要测试人员有很高的专业素养,讽刺的是这些专业人员往往都在安全厂商而不是在测试机构工作…… 因此,这个问题在现实中往往是这样解决的:首先先收集一系列样本(其中包含无效样本),这些样本先拿去测试安全软件,剩下未检出的样本则交给相应的安全厂商。安全厂商需要帮助鉴定剩下的这些样本中的无效样本来给自己产品的漏检进行辩护……逻辑是“如果你没法证明这个漏检文件不是恶意的,那么这就是你和你产品的失职”。这种方法对于测试者来说自然很轻松方便,但对于安全厂商来说是一个很大的负担:越来越多的测试带来的送检样本就像DDoS攻击一样。而且因为这些样本往往需要人工分析(毕竟如果能自动化检出的话早就本地检出或者云鉴定了),安软厂商不得不耗费大量人力物力应对这些来自测试机构的样本。 2.测试结果说明了什么 不详细介绍测试流程和具体结果的行为是耍流氓。之前我曾经在论坛上吐槽过性能测试的问题(http://bbs.kafan.cn/thread-2073505-1-1.html)。很多用户觉得一些评测机构的性能测试结果和自己使用经历大相径庭。这可能源于测试方法不当,例如测试的情景无法反映用户的实机使用情况;也可能源于操作不当(比如性能测试却未考虑缓存问题)。除了测试方法的问题外,测试结果和预期的偏差也可能是因为缺乏对测试结果的合理解读。假设性能测试中A产品对网页加载拖累1ms,而B产品拖累了10ms,你可以说B产品比A慢了10倍,但这种“慢”在实际使用中是否会被用户察觉呢?对于普通人来说,简明扼要的“星级评分”“梯队评分”最直观最容易解读,然而这些粗颗粒度的结果底下可能掩盖了更多关键的细节。对于爱好者来说,学会看详细的评测报告至关重要。
理想的测试应当是怎样的? “检测率”这个概念可能比不少人想象的复杂得多。它包含时间因素(安软对新威胁的响应),也包含用户因素(取决于用户的习惯和需求,例如服务器不太可能中网页病毒,而Mac用户不太可能中Windows病毒,等等)。为了说明理想的测试模型,我们定义U为用户的集合,定义P为待测产品的集合,定义A为恶意攻击的集合。在这里,恶意攻击和用户的使用习惯是相关的:对于使用Chrome的用户A来说,针对IE漏洞的攻击并不算恶意攻击,而能保护这类攻击的安软也并不会被额外加分。此外,我们定义一个离散的时间序列t,用来代表一段时间T内的时间点集合 我们定义两个指示函数(事件Incident函数,侦测Detect函数):
这里假定一个安全产品在所有用户机器上工作状态都一致,比如假定在时间点t上,若产品p在用户A的的机器上能检测攻击a,那么同一时刻在用户B的机器上也能检测攻击a(事实上就算这点在实际情况中都不一定成立,例如某些云安全软件的不稳定性)
有了以上两个指示函数,我们就可以定义一个威胁的Prevalence(广度)为:
这个数值越高,对应的威胁a影响就越广泛。当然,这个数值会随着时间变化而变化,这也是为什么这个函数的参数之一是时间t。
相应的,安全软件的检出成功率可以定义为:
平均成功率则是:
第一个成功率的公式代表对特定用户u来说,使用产品p能阻挡的威胁的比率 第二个平均成功率的公式代表对整体用户的影响。这种算术平均带来的结果是:假定用户A遭受了8次攻击而p阻挡了6次(个体成功率75%),而用户B遭受了2次攻击而p都无法阻挡(个体成功率0%),那么算数平均后的平均成功率仅有37.5%,而不是6/10=60%。之所以这样做的原因在于,比起假定恶意威胁是平均分布的,假定用户和其对应习惯平均分布更具合理性。相反,如果恶意威胁真的是平均分布的,那么这个算术平均出来的结果和其他传统方法结果也不会相差多少。
当然,有很多现实问题使上述这种理想的测试模式难以实现。首当其冲的就是Incident这个指示函数的悖论,道理很简单:如果我们知道有/没有一个攻击,那么我们一定已经依靠某种程序准确侦测出来了,而世界上没有一个程序能百分百准确侦测出是否有攻击……其次,收集用户群体的使用习惯、常用软件、系统漏洞分布也基本上难以实现。
-------------------------- 题外话: 没有任何程序能完全准确判断出一个程序/文件是否为恶意,这是所有图灵机的理论局限。图灵曾证明“停机问题”是图灵机不可判定的。也就是说,不可能存在一个计算机程序能判断某个程序是否一定能终止(收敛)。基于这个理论,我们简单构造一种情况就可以证明计算机恶意程序也是一个图灵不可判定问题: 假设我有一个程序包含两个函数foo()和bar(),在主函数中是这样的:
其中foo()是正常代码,而bar()是包含恶意行为的代码。这个程序可能执行到foo()就永远循环而不会执行bar()(严格来说这样它就不是恶意程序),也可能正确返回后继续执行恶意的函数bar()(此时它是恶意程序)。根据停机问题的结论,任何图灵机都不可能判断foo()是否会执行结束,也就无法判断一个程序到底是不是真的会展现出恶意行为。 当然,以上只是一个理论构造,现实中的安软可以通过很多非精确的方式来逼近这个理论壁垒。例如,对于很多靠特征或者静态启发的安软,只要静态扫描到bar()的恶意内容就会报警(可以是特定的二进制特征,也可能是一些区段的异常熵值,等等)。动态启发则通过模拟执行程序来判断,但foo()可以运行很长时间以至于超过检测时间限制,这无法被虚拟机准确甄别出来。
总体而言,安软的查杀都只能旁敲侧击逼近100%检测率,而且不可避免带来误报,永远不可能做到100%准确的侦测 --------------------------
大部分测试机构在实际测试中,都对以上这个理想模型进行了大幅简化。第一个简化就是假定所有收集的样本对用户来说是均匀分布的。这等效于假定有一个用户,他/她会遭受所有可能的攻击。因此,上文的平均成功率就被简化为:
上式中仍然有一个大难题,那就是如何获取广度(Prevalence)信息。因此不少测试假定广度为1,于是式子就变成了 这个式子就变成了传统的 检出数/样本数 这种测试方法……
简化模型带来的问题: 论坛中总有用户表示:现实中不怎么可能遇到毒区那些威胁,卡饭群众大多对恶意程序都有点反应过激了——其实这点正好对应了上文理想测试中“恶意攻击的指示函数要绑定用户使用习惯”这点。对于平时只进行简单文书处理的用户来说,一个查杀脚本病毒是弱项的杀软也可能帮其阻挡99%的威胁,而在大多测试由于割离了用户群和威胁的关联,测试结果也难以体现出针对普通用户群的查杀能力。
而把广度这个参数简化带来的问题更加明显:假定某个时段某病毒A疯狂传播,感染了60%的电脑,而测试者收集了100个样本,A只占了一个。那么就算某产品漏检了A,它的检出率仍旧有99%——这在现实生活中有代表性吗?因此,很多大型评测机构已经针对这点对样本的组成比率进行了针对性修正(印象中早年PCSL有这么做过) 从我个人经验来看,这个谬误在论坛测试中很常见:很多人认为ESET杀ransom很弱,而我之前的锁库测试却显示锁库的ESET都能查杀一两个月后大部分勒索,这似乎很不科学。实际上仔细想一想,毒区样本中每日的ransom以cerber/locky居多,ESET面对新ransom查杀不利是事实,但对于已知ransom的变种/混淆查杀能力却很强。普通用户是遇到cerber变种可能性大呢?还是遇到新ransom的可能性大呢?这就要实际情况实际讨论了。
---------------------------------- 系列二 回到之前提的另一个问题:我们到底需要收集多少样本? 检出率,抽样数,和结果置信度 假定有一个恶意威胁集合N,我们随机从中抽样作为测试集合,那么使用简单的排列组合和初等概率就可以算出对应的标准差。这里略过理论的推导,给出更直观的计算机模拟的结果: 总数代表假定的网络中总恶意威胁的数量,检测率为某产品p的实际检测率,抽样数为总威胁集合中随机抽取样本的数量。平均、最小、最大是在抽样模拟中这个产品在不同样本集中的检测结果 总数 | 检测率 | 抽样数 | 平均 | 最小 | 最大 | 标准差 | 1 000 000 | 80 % | 1 000 | 80.03% | 75.70% | 83.70% | 1.25% | 1 000 000 | 80 % | 10 000 | 79.97% | 78.81% | 81.24% | 0.39% | 1 000 000 | 80% | 100 000 | 80.00% | 79.65% | 80.37% | 0.12% | 1 000 000 | 97% | 1 000 | 97.00% | 95.30% | 98.60% | 0.54% | 1 000 000 | 97% | 10 000 | 97.00% | 96.39% | 97.47% | 0.16% | 1 000 000 | 97% | 100 000 | 97.00% | 96.83% | 97.16% | 0.05% | | | | | | | |
总的来说,从上面的数据可以看到:1)抽样数相同的情况下,产品检出率越高,其抽样检出结果的浮动范围越小 2)同样检出率的情况下,抽样数量越大,结果的浮动范围越小越可靠
取样数量和检测率的关联
要取样多少才能让测试结果达到某种精度?基础统计公式变换后可以得到如下结论:
其中M代表取样数量,D代表产品的检测率,N代表总威胁数,E代表想要达到的标准差
例如,如果我们想让测试结果精度达到小数点后四位(百分比小数点后两位),我们可以将E设为0.0001/2 (即有95%以上概率能达到这个精度),计算结果如下:
总威胁数 | 检测率 | 目标精度 | 需要取样的数量(95%置信度) | 1 000 000 | 80% | 0.001/2 | 390 000 | 10 000 000 | 80% | 0.001/2 | 600 000 | 100 000 000 | 80% | 0.001/2 | 640 000 | 10 000 000 | 95% | 0.001/2 | 190 000 | 100 000 000 | 95% | 0.001/2 | 190 000 | 从上面的表格可以看到:1)产品检测率越高,达到相同精度需要的取样数越少 2)取样数收敛速度很快,例如检测率95%的时候,就算威胁总量从一千万上升到一亿,需要取样的数量也基本不变(仍旧是19万)其实通过求极限也可以看到,当N趋近于无穷大的时候,分母实际上是由E^2/(D-D^2)主导的
如果在上述各情况中使用更多的样本来测试,测试结果也不会有多少改进。不过这里需要注意的是,以上全是基于威胁池的随机抽样。现实生活中做到这点并不容易。
现在来看看一些机构的取样数量:
AV-C在去年九月份的文件侦测测试中使用了122856个样本
当时的漏检率统计:
AV-C在今年四月的真实世界测试中使用了448个样本
当时的检出率情况:
AV-TEST在今年四月win10家庭用户测试中,真实世界测试使用了175个样本,传统侦测测试使用了12169个样本(下图包含了这个产品的检出情况(AhnLab))
或许看到这里你对他们的各类测试有了新的认识:)
反转!还是反转!
到目前为止,我们还都仅仅在讨论测试单个产品的精度问题。当多个产品并列横向测试的时候,他们之间的排位则更加耐人寻味。
我们模拟一下当两个产品检测率非常接近的时候,取样数量对排位的影响
在下表中,假定产品A检测率为97.00%,假定产品B检测率为96.90%,威胁池大小为一百万,“反转率”代表随机采样后的测试结果和实际检测率不符的概率
产品A检测率 | 产品B检测率 | 取样数 | 反转率 | 97.00% | 96.90% | 1 000 | 44.90% | 97.00% | 96.90% | 10 000 | 36.30% | 97.00% | 96.90% | 100 000 | 8.20% | 97.00% | 96.90% | 500 000 | 0.00% | 模拟结果挺惊人的不是吗?当两款产品检测率不相上下的时候,就算取样数量达到了总威胁池的1/10,仍然有不小的概率会发生排位上的反转。如果需要95%的置信度,一般需要320 000个样本才能精确把97%和96.9%区分开,而当两者检测率分别为97.0%和96.8%的时候,也需要120 000个样本。
作为本章总结:当第一梯队产品检测率差别极小的时候,需要用大量样本才能正确测得它们的排位。若样本数量太小,他们的排位则有可能在现实中反转。
无效样本和误检——垃圾样本进去、垃圾结果出来
到目前为止还没讨论过无效样本给测试结果带来的影响。有的产品的检出基于“无罪推定原则”,即“如果我没法证明你有罪,那么你是无辜的”;有些激进的产品则相反(某些产品计算待测文件和正常文件特征的距离,这个距离超过某个阈值则判毒,这本质上算“有罪推定”)
在实际测试中,这种产品设计哲学的不同也会反映在测试结果上。我们在这里建立一个简单的模型:
我们定义J(p)为产品p对垃圾文件的检出率(大量实际测试表明每个产品的这个数值基本比较稳定,因此在这里看作一个常数)
定义R(p)为产品p对实际恶意样本的检出率
定义B为测试集中无效样本的比率
R'(p)则代表在掺有无效样本的测试集中最后测得的检出率
现在我们来假设两个产品p1和p2:
p1对有效样本的实际检出率为99%,无效样本检出率为10%
p2对有效样本的实际检出率为95%,无效样本检出率为80%
假定测试集中无效样本占比8%,那么两款产品的测试结果分别为p1:91.88%和p2:93.80%
这个结果十分惊人!检测集中若有8%的无效样本,两款产品的测试结果也相应反转,而且差距还不小。本当比p2优秀的p1内牛满面……上述样例中,至少需要将无效样本控制在6%以下才不会导致结果反转
01年vb conf上就有人指出:
一个经常被忽略的问题是:当测试人员发现某款安软漏检时首先怀疑的是安软而不是样本本身。从历史情况来看,如果安软漏检某个样本,应该先怀疑样本是否是有效的,而不是怀疑那款安软检出率行不行。
在实际收集样本的时候,专业机构通常需要对样本进行大量来验证其有效性。例如对病毒来说,需要构造环境验证它的确能自我复制,并需要在那个环境下测试。对其他恶意程序家族来说,验证方法也不尽相同。最可怕的误区之一在于用多引擎扫描结果来鉴毒(例如大于x款产品报毒即认为某个样本是毒),这会让整个测试陷入一个恶性循环——错的检出导致后续错上加错。一些初创企业喜欢用VT标榜自己的检出率(我们检出了多少样本,而其他引擎却只检出多少)。这种做法普遍受到老厂和VT的抨击,原因之一也在于此。
现在我们换个角度来看,从测试取得的检出率来推导实际检出率(对上面的公式进行简单变换即可)。 假定有三个产品X,Y和Z。如果我们知道这些产品的垃圾样本检出率,同时知道测试集中包含了20%的无效样本,那么可以求得下表的结果(可以看到X代表遵循“无罪推定原则”的产品,Z代表遵循“有罪推定原则”的产品,Y则处在中间)
产品 | 测试结果 | 垃圾检出率 | 真正的有效样本检出率 | X | 77% | 5% | 95.00% | Y | 81% | 40% | 91.25% | Z | 70% | 75% | 68.75% | 可以看到,X的有效样本检出率最高且误检率最低,但由于测试样本集的掺杂,在测试结果中成绩却比Y来的差。讽刺的是,由于测试集中掺杂了20%的垃圾样本,严格来说检测率最高应该是80%,但产品Y在测试结果中居然达到了81%。囧囧囧囧囧
计算机反病毒先驱Alan Solomon曾经说:
1.如果某样东西有着极高的病毒检出率但其误报也不少,那么这种产品就没有意义
2.任何需要用户来人工决策一些难以抉择的问题的产品也没有任何意义
3.一些杀软或许的确能做到其宣传中的效果,但这些宣传的效果必然没有什么实际价值
安软评测意在考察安软的水平,但谁来考察这些安软评测的水平?
-----------------------------
刚好趁放假把压箱底的文章发了,以下是预定的续集(不为填坑负责)
(完结)系列一:测试常见谬误,何为理想测试,实际测试对理想模型的简化及其相应影响 (完结)系列二:样本数量对测试结果的影响,误检、垃圾样本对测试结果的影响 系列三:测试需要注意的其他问题&修改样本进行测试合理吗? 系列四:其他一些有趣的东西,待定……
|