免杀艺术 1: 史上最全的免杀方法汇总

xiaohui 技术 2017年3月16日发布

导语:虽然目前有很多方法可以让恶意软件使用某一技术绕过反病毒检测,但这些显然不是恶意软件免杀的终极目标, 它们的最终目标是实现 “FUD”。

owl-750x410.jpg

从本文开始,我们将分三章来系统的讲述一下有关免杀的各种技术。

虽然目前有很多方法可以让恶意软件使用某一技术绕过反病毒检测,但这些显然不是恶意软件免杀的终极目标, 它们的最终目标是实现 “FUD”, “FUD” 是地下网络论坛的黑话,代表“恶意软件完全不可被检测到”的意思。

本文主要是通过介绍静态免杀、动态免杀和启发式免杀三种方法来实现“FUD”,但不管是哪种实现的技术,其中的一个关键点就是恶意软件的大小一定要足够小。另外,本文还会向大家介绍杀软和Windows操作系统的内部工作原理。

建议读者应该具备一定的C/C++、汇编和PE文件结构的相关知识,否则本文将晦涩难懂。

反杀软检测技术介绍

应该说每一类型的恶意软件所实施的反检测技术都是不一样的(恶意软件可以分为病毒、木马、僵尸程序、流氓软件、勒索软件、广告程序等),虽然本文会对所有相关的反检测技术都进行介绍,但我们还是会将注意力放在stager阶段的meterpreter这种有效载荷的工具上,因为几乎所有的恶意软件的攻击命令的执行都必须依靠 meterpreter来实施攻击,例如: 提权、凭证窃取、进程迁移、注册表操作和分配更多的后续攻击, 另外,MetasploitFramework是一个缓冲区溢出测试使用的辅助工具,也可以说是一个漏洞利用和测试平台,它集成了各种平台上常见的溢出漏洞和流行的shellcode,并且不断更新,使得缓冲区溢出测试变的方便和简单。

专业术语

1.基于签名的检测:

传统的防病毒软件很大程度上依赖于签名来识别恶意软件。工作原理如下:

当恶意软件被杀软公司采集后,杀软后台的研究人员以及动态分析系统便会对这些样本进行分析,一旦确定是恶意软件,后台便会提取恶意文件的标签并将其添加到反病毒软件的签名数据库中。

2. 静态程序分析:

静态程序分析是在不实际运行程序的情况下进行的分析。

大部份的静态程序分析的对象是针对特定版本的源代码,也有些静态程序分析的对象是目标代码。

3. 动态程序分析:

动态程序分析是通过在真实或虚拟处理器上执行程序而执行的分析。为了使动态程序分析真实可信,我们必须要能够对各种目标程序的行为进行测试。

4.沙盒分析技术:

沙盒是一个观察计算机病毒的重要环境,用于为一些来源不可信、具备破坏力或无法判定程序意图的程序提供试验环境。 

5.启发式分析:

启发式分析是许多计算机防病毒软件使用的一种方法,其被设计用于检测未知的计算机病毒,以及新的病毒变体。

启发式分析是基于专家的分析,利用它可以对已知或未知的恶意软件进行各种维度的风险衡量,其中多标准分析(MCA)是其中的方法之一,不过启发式分析不是统计分析而是基于可用的数据或统计。

6.信息熵检测:

每个恶意软件都可以被描述成数值性质的属性(例如:信息熵)或者抽象性质的属性,信息熵就是通过找到最合适的量度来验证并且对比恶意软件的属性。

7.其他常见检测技术

7.1 混淆检测:

病毒由两个部分组成:载荷(payload)和混淆部件(obfuscator),载荷是用来做坏事的代码,而混淆部件则是病毒用来保护自身免于被查杀的,通常恶意软件开发者都会将其代码进行混淆以降低其代码的可读性

所以混淆检测就非常的有针对性。

7.2 加壳检测:

恶意软件一般都会被压缩加壳,因为加壳会将可执行文件进行压缩打包, 并将压缩数据与解压缩代码组合成单个可执行文件的一种手段。 当执行被压缩过的可执行文件时,解压缩代码会在执行之前从压缩数据中重新创建原始代码。所以检测恶意软件是否使用了加壳技术,也是发现的一种重要手段。

7.3 加密检测:

恶意软件使用加密对其二进制程序进行加密,以免被逆向分析。加密存在于恶意软件的构建器和存根中,当恶意软件需要解密时,不会用恶意代码常用的正常方法执行它。为了隐藏进程,恶意软件使用了一个有名的RunPE的技术,代码会以挂起的方式执行一个干净的进程(比如iexplorer.exe或者explorer.exe),然后把内存内容修改成恶意代码后再执行。所以检测RunPE的运行,就可以很容易的检测到恶意软件了。

1.png

加密与加壳检测技术的深度分析

由于目前杀毒软件的检测技术也来越高,不仅能对恶意软件签名和攻击行为分析,还能够对使用的加密与加壳技术进行探测。由于对加密与加壳的检测相对容易,所以目前杀软公司都侧重于对加密与加壳行为进行检测,但凡出现这些可疑行为,例如,PE文件在内存进行加密和解密加载时,都会进行深入分析。

为了完全理解PE映像的内存执行过程,我们先要了解一下Windows是如何加载PE文件的。一般在编译PE文件时,编译器会将主模块的地址设置为0x00400000,而编译过程中所有的地址指针和长跳转指令地址都是依据主模块的地址来计算,在编译过程结束时,编译器会在 PE 文件中创建一个重定位分区表,重定位PE文件所有的地址指针和长跳转指令地址。

在执行PE映像时,Windows会检查PE文件要映射到的进程地址空间是否可用,如果空间不可用,则Windows会会在启动之前将PE映像加载到内存上的绝对地址,再利用重定位分区表修正所有地址。 所有这种机制称为 “地址空间布局随机化(ASLR) ”。

为了在内存密码器上执行PE映像,需要对PE文件头进行解析并重定位绝对地址,如果出现模拟系统加载器这种可疑的行为就证明该运行很可能是恶意运行。在我们对所有使用C 语言或更高级语言编写的加密程序进行分析时,总能看到“NtUnmapViewOfSection”和“ZwUnmapViewOfSection”这样的Windows API函数,这些函数会简单地从主体进程的虚拟地址空间中取消映射视图, 以上这个操作过程就是RunPE,几乎90%的加密都会使用到这一技术。

2.png

当然杀软产品不能仅仅根据是否使用了Windows API 函数就判断该程序是恶意的,但是可以根据该函数的使用顺序来提高检测的准确率。

鉴于杀软产品已经开始对加密与加壳技术进行探测,目前已有一部分恶意软件的开发者在使用密码器进行汇编时,不使用这些Windows API 函数和手动执行重定位,因为他们知道,没有哪一个正常的程序会刻意的进行模仿系统的加载。

另外由于在不断加载的过程中,恶意软件会产生很多文件,而这些文件所自带的信息熵增加到一定程度时,就会被杀软产品标记为可疑行为进而进行持续的分析。

3.png

完美的免杀方法

到目前为止,要实现恶意软件的“FUD”,加密恶意代码被认为是个不错的选择,不过有几点要注意:

1.恶意程序在解密时,应当也进行代码混淆

2.当恶意文件在内存中运行解密代码时,我们必须要保证在不重定位绝对地址的情况下进行

3.恶意软件是否在沙箱环境中运行,如果是,则立马停止恶意文件的解密

4.应当只对 PE 文件中的 shellcode 或 只有二进制文件的.text部分进行加密,而不是对整个 PE 文件进行,以便把信息熵和降到最低

以下是恶意软件流程图。

4.png

我们的“杀软检测”功能将检测恶意软件是否正在沙箱中被动态分析,如果功能检测到AV扫描器的任何迹象,则它将再次调用主函数或者仅当 “AV Detect” 函数来用。如果没有发现AV扫描器的任何迹象,它会调用 “解密Shellcode” 的功能。

以下就是 meterpreter 反汇编 shellcode 的原始格式。

5.png

为了让信息熵保持适当的大小,我们需要将shellcode注入方法改为使用多字节键的xor加密,xor加密标准不同于RC4或blowfish。因为恶意软件根本就用不到像RC4或blowfish这样的强加密,所以杀软产品也不会企图去解密信息熵中的shellcode,因此我们在编写恶意软件时,只需保证shellcode具备不可读和不可检测的静态字符串就足够了,如果使用xor,那解密过程更快,加密库中的代码量也会少很多。

同一个 meterpreter 代码使用 XOR 加密的前后对比

6.png

启发式引擎

启发式引擎是基于统计和规则的分析机制。他们主要的目的是检测事先未知的病毒,同时根据预定义的标准给出威胁等级,甚至当一个正常运行的程序超出其威胁等级时,也会被认定为恶意软件。

由于启发式引擎是杀软产品中最核心的部分,每个公司都会基于自己不同的判断规则和行为分析,因此没有正式的威胁等级标准让我们参考,所以我们只能自行总结出常见的一些判断标准,来供大家参考:

循环解密行为
读取运行设备名称
读取加密器的 GUID
连接随机域名
读取 Windows 安装日期
删除可执行文件
在二进制文件内存中搜索可用的IP地址
修改代理设置
在运行的进程中安装HOOKS或PATCHES
往浏览器中注入代码
注入远程进程
查询进程信息
设置过程错误模式以覆盖错误窗口
异常熵的出现
检测杀软的存在
对特定的注册表项的变动情况进行监控
包含提权的能力
修改软件的相关限制策略
读取系统/视频 BIOS 版本
PE文件头中的结束字节异常
创建受保护的内存区域
创建大量进程
企图进行长时间休眠
不常用的字节
读取 Windows产品ID
包含循环解密
包含启动或操作设备的驱动程序的能力
包含阻止用户进行干扰的能力
……

所以,当我们在编写免杀的恶意软件和解密shellcode模块时,一定要避免上面这些坑。

解密Shellcode

混淆解密机制是实现免杀的另一个重要手段,由于PE文件内部的解密循环能够被启发式引擎检测到,目前大部分杀软已经针对这个解密过程进行了针对性的检测。比如,一些扫描器会等到ECX寄存器为0时,再分析解密后的内容。

下面是解密shellcode的代码。

7.1.png

7.2.png

可以看出,这是用一个for循环来完成的xor操作,而xor操作的前后汇编代码块只是一些进行一些随机的xor操作和跳转。这些对于混淆解密机制来说已经足够了。

调试模式

动态分析检测:

另外在编写沙盒检测机制的时候我们也需要混淆编写函数,如果我们所使用的反汇编方法被启发式引擎检测到了,那就算免杀目的失败。

第一种检测机制是检查我们所编写的恶意进程是否被调试。

用Windows用户态调试器来确定调用过程是否正在由用户模式调试器调试,但是我们不会用这个方法进行编写,

因为大部分杀软都会监控对用户态调试进行监控。

我们可以通过PEB结构中的“BeingDebuged”字段来判断。

8.png

这些内联汇编中的代码指向地都是 PEB结构中的 “BeingDebuged”字段的指针,如果调试器存在,它将再次检查,直到堆栈中发生溢出,当溢出发生时,堆栈保护(stack canaries)将触发异常并且关闭进程, 这是退出程序的最快方法。

虽然可以手动检查“BeingDebuged”字段可以绕过大量的杀软,但是还是有一些杀软能够对这种情况进行检测,因此我们需要混淆上述代码,以避免杀软进行静态字符串分析。

9.png

请注意我们会在所有混淆操作之后都添加一些跳转指令,避免被检测到,不过这不会影响免杀的实现。

加载虚拟dll库:

如果我们尝试加载虚拟dll,正常情况下,返回值是NULL,一些杀软的动态分析机制就会利用这个返回值来进一步观察程序的运行情况。以便做出更准确的判断。

10.png

获取Tick Count(毫秒计时器)值的检测:

在这种方法中我们将利用 杀软产品的时间截止日期。 在大多数情况下,杀软 产品都被设计为用户友好型,为了不影响用户的其他操作,杀软 产品用来扫描文件的时间都很短。最初恶意软件开发人员会使用 “sleep()” 函数来避开这种快速的扫描,然后等扫描过后,再开始运行。但现在这个杀软漏洞已经被堵死了,因为所有杀软 产品都会跳过 sleep 功能。

为了能够继续使用这个漏洞,我们将使用 “GetThickCount()” 的 Windows API 函数,我们使用它来获取从操作系统启动后经过的时间,然后尝试 sleep 1秒 sleep 后,通过比较两个 GetTickCout() 值来检查sleep 功能是否被跳过。如果时间差小于1秒,则被检测。

11.png

内核数检测:

这个方法非常简单,就是检查系统的处理器核心数。因为杀软产品不会占用太多主机系统的资源。大部分沙盒系统只会分配1个处理器核心。

12.png

大内存分配:

这个方法与刚刚的获取Tick Count值的道理是一样的,同样是利用杀软的扫描截止时间,简单的分配100Mb的内存,用0填充,最后释放。

我们会给所编写的恶意软件分配近 100 Mb 的内存,然后往其中添加一个 NULL 字节,然后再进行内存释放。

13.png

当程序内存在运行时增大到一定程度时,就会触发杀软结束扫描,以免在一个文件上消耗太多时间。

陷阱标志检测:

陷阱标志用于跟踪恶意程序的,如果此标志被改动,则会触发每个指令产生“SINGLE_STEP” 异常。我们可以用下面的代码来对陷阱标志进行的跟踪属性进行禁用设置

14.png

互斥触发WinExec:

这个方法在未来的免杀技术中,应该用途非常广。另外,它使用起来也非常简单,如下所示,我们会创建一个条件来判断互斥对象是否已经存在于系统上。

15.png

如果“CreateMutex”函数没有把已经存在的错误进行返回,我们将再次执行恶意程序,因为大部分杀软产品不会让正在分析的程序启动新进程和访问沙盒以外的文件。当出现错误运行时,恶意软件就可以开始执行解密了。

运行Shellcodes的正确姿势

从windows vista开始,微软引入了数据执行保护或DEP的安全机制,来监控恶意程序。如果出现任何异常的运行,都将会触发DEP并通知用户关闭该程。这意味着我们不能把字节序列放入字符数组中执行,而是需要分配一块可读写可执行的内存。

微软实现内存分配的方式有很多种,大多数常见的恶意软件在字段中使用会使用“VirtualAlloc”函数,不过现在大多数杀软产品都会对其进行检测,我们建议还是使用其他内存操作函数。

下面是一些内存操作函数的用法:

HeapCreate和 HeapAlloc:

Windows允许创建可读写可执行的堆区域。

16.png

LoadLibrary和 GetProcAddress:

LoadLibrary和GetProcAddress win api的函数组合不但允许我们使用所有其他的win api函数,而且这种组合又不直接调用内存分配函数。

17.png

GetModuleHandle 和GetProcAddress:

这个方法甚至不使用 LoadLibrary 函数,而是充分利用已经加载的kernel32.dll,GetModuleHandle能够从已经返回的dll中加载模块句柄,这个方法是运行shellcode最隐蔽的方式。

18.png

多线程:

不管是对杀软产品还是对免杀程序来说,逆向分析多线程的PE文件都非常困难。多线程的方法不单单适用于 shellcode,还 可以用在上述所有的方法中。我们可以在执行 shellcode 的同时继续执行 “AV Detect” 功能

19.png

利用上述代码可以创建新线程并运行shellcode,只是在创建线程后有一个无限whlie循环来绕过杀软检测,可以说多线程方法是一种非常完美的免杀方法,既绕过了沙盒系统又绕过了动态分析,同时还能躲避启发式引擎的检测。

总结

虽然本文已经列举了很多免杀的方法,但其实还有很多我们没有列举到,比如编译恶意程序时需要开启堆栈保护,降低文件大小可以加大逆向工程分析的难度,内联汇编的语法推荐用visual studio进行编译等。

总之我们通过本文的方法所编写的恶意程序能够绕过35个杀软产品。

20.png

本文参考来源于 pentest ,如若转载,请注明来源于嘶吼: http://www.4hou.com/technology/3853.html
点赞 7
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

    llopppp 2017-03-16 10:58

    国产软件safe…不对,小红伞!