我正在使用 ClamAV 的引擎从事一个业余爱好 AV 项目。虽然 ClamAV 是一个很好的开源引擎,但它对检测多态病毒的支持很差。最新的更新版本未能检测到 Virut 和 Sality 的许多实例。商用 AV 如何检测多态病毒?
反病毒厂商如何为多态病毒创建签名?
简短回答:AV 扫描仪不使用多态样本的签名。他们使用通用检测代码。
长答案:多态恶意软件使不同代的代码看起来不同。谈到文件感染者(Sality 和 Virut),当一个新文件被感染时,就被认为是一代。如果样本 A 感染了 B、C 和 D,那么这是第一代。这一代的代码或多或少看起来相似。当样本 B、C、D 感染新文件 E、F 和 G 时,这将是第 2 代,并且第 1 代和第 2 代之间的代码会有很大差异(取决于文件感染者的质量)。这同样适用于后代:新一代将与以前的不同(同样,这取决于文件感染器的质量,但通常是正确的)。
因此,如果您决定使用基于字节流或助记符流的签名,您只会检测到一代,而不是恶意软件本身。除了不太好的多态引擎。
AV 工程师通常编写检测代码(不是签名)并找到恶意软件的证据,而不依赖于多态代码,而是依赖于不会改变和/或语义的特定代码:尽管代码可能看起来非常不同,它总是在做同样的事情。
例如,所使用的证据可以是以下证据:
- 如果入口点在最后一部分(如 Sality/Virut 的情况)。
- 具有相同语义但使用不同寄存器的特定指令(如果文件感染器使用 EPO,则将原始入口点地址推入堆栈)。
- 不同代之间相等的特定值/偏移量。
另一种方法,没有被广泛使用,因为它很慢而且它本身是不够的:
- 对第一个函数进行代码分析并比较控制流图。您可以将其视为基于图形的签名。
另一种广泛使用的方法:
- 使用模拟器(ClamAV 缺乏的一大功能)尝试模拟恶意软件的许多指令,然后找到特定的缓冲区(字节流)或一组特定的指令。在执行了这么多指令之后,希望恶意软件已经在内存中“解包”了。当然,如果它被正确模拟。
因此,如果您想为 Sality 或 Virut 编写 ClamAV 的检测代码,我建议您使用他们所谓的字节码“签名”。
多态病毒有很多种,但通常最常见的解决方案实际上试图解决问题并避免在用户机器上检测未知样本。人们认为很难在可用资源很少的活动机器上实时检测病毒,而不会让用户真正接触到病毒的恶意属性。相反,自动驾驶汽车更喜欢在他们的舒适区完成大部分繁重的工作:内部实验室和沙箱。
通常有几个方向:
- 尝试生成对尽可能多的样本保持有效的签名。即签名字节不是多态的或只有很少的变体。您将需要大量类似的变体。AV 通常有算法来聚类并以这种方式自动生成签名。
- 尝试去除多态层并检测底层样本。UPX 是一个简单的例子,因为它很容易静态解包,一些 XOR 加密方案也是如此。
- 通过恶意活动/API、进程注入等动态分析检测样本。这带来了很多误报,这是 HIPS 系统的一个已知问题。
- 让您的 AV 产品将未知的可疑文件上传到后端,如果需要,将由专有的静态和动态分析机器、聚类算法和手动 RE 分析样本。然后明明是老样子签的。KAV 喜欢对未知文件执行此操作。
许多这些 e 通常结合使用,第 3 种方法用于检测可疑文件的第 4 种。1st 和 4th 通常有相似的引擎和流程,从静态分析开始,因为它更快。因为大部分艰苦的工作都是在 AV 供应商的实验室中完成的,所以这些是一个巨大的瓶颈,所以加速和优先级是游戏的重要组成部分
要检测多态引擎本身 - 正确 - 需要引擎的副本。过去就是这种情况,因为病毒携带引擎是为了产生自己的新副本。针对这一点的明显攻击是服务器端多态性,在这种情况下,我们(“我们”= AV 行业)只能猜测引擎的功能,并且可以随时更改以响应我们的检测。然而,回到实际问题:给定一个可以产生这样的序列的引擎:
mov reg1, offset_of_crypted
[optional garbage from set 1]
[optional garbage from set 2]
[optional garbage from set 3]
mov reg2, key_for_crypted
[optional garbage from set 1]
[optional garbage from set 2]
[optional garbage from set 3]
mov reg3, size_of_crypted
[optional garbage from set 1]
[optional garbage from set 2]
[optional garbage from set 3]
[decrypt]
[optional garbage from set 1]
[optional garbage from set 2]
[optional garbage from set 3]
[adjust reg3]
[optional garbage from set 1]
[optional garbage from set 2]
[optional garbage from set 3]
[branch to decrypt until reg3 completes]
然后我们可以分析可以产生寄存器分配的操作码,我们知道垃圾指令集,解密方法,寄存器调整等。
从那里,我们可以使用状态机来观察真正的指令,而忽略虚假的指令。那个实现细节又长又无聊,不适合在这里作为答案。在大多数情况下,这是一种单引擎单算法关系。
结果,模拟器成为我们对付这种攻击的最有用的工具,让我们本质上“让病毒自己解密”,然后我们就能看到背后的东西(针对它的攻击显然是变形,最简单的实现是在源代码级别而不是编译后)。
因此,简而言之,答案通常是“我们不再这样做了”。也就是说,我们倾向于不再检测多态引擎本身。当然也有例外,但这些日子很少见。