反逆向工程——使恶意软件难以逆向的技术

前言

在攻防对抗的博弈中,恶意软件的作者总是想方设法阻止反病毒公司、安全研究人员对其编写的恶意软件进行检测、分析.在对抗中,技术的进步螺旋式上升,那么编写恶意软件的人都采用了哪些技术来对抗分析?这些技术又是如何发展的?反病毒公司如何应对这种挑战?作者 Bartosz Wójcik 给出了自己的观察。 逆向工程是指不访问其源代码来分析已编译好的程序的方法。 在这篇文章中,我想说明这是使用恶意软件的编写者阻碍病毒和其他恶意软件分析的方法,我也会解释反病毒公司和反病毒软件是怎么处理的。 为了使反病毒公司的分析变得困难,首先你需要知道反病毒公司是如何在实验室中分析恶意软件的。最常用的方法如下: 在虚拟环境中测试恶意软件 在沙盒和模拟其中测试恶意软件 监控恶意软件对系统所做的更改 静态分析 动态分析 生成恶意软件的签名 同时,在用户计算机上运行的反病毒软件可以执行以下检查来帮助判断这个恶意程序的实际意图: 文件和片段的校验和(例如 MD5、SHA1、SHA2、CRC32) 不寻常的文件结构 文件中不寻常的值 文件片段的签名(启发式扫描) 字符串常量 应用程序的行为,被称为行为分析(监控访问系统文件、注册表等) 函数调用(调用哪些函数、提供了什么参数,调用顺序) 实验室和反病毒软件都是这么做的,这中间的每一步,都可能遇到专门设计好来阻止或减慢分析的障碍。

检测虚拟机

99%的情况下,恶意软件会被放在虚拟机中进行测试,例如VMware, VirtualBox,Virtual PC,Parallels 等。这是为了保护分析师自己的机器免受感染,这也是时有发生的! 在2011年,由于反病毒公司 ESET 一个雇员的错误,众所周知的(昂贵的)分析软件 IDA 被盗。

通常在反病毒实验室中,恶意软件存储在没有执行权限的文件夹中,特别要防止感染文件或恶意文件的意外执行。虚拟机的使用也允许了额外工具的使用,例如比较感染后的系统映像和感染前的系统映像的区别,哪怕是对系统文件、注册表、其他系统组件极其微小的改变,都可以轻易发现。

输入图片说明

运行在虚拟机中的恶意软件的网络流量可以被准确地跟踪(通过网络嗅探器,比如 Wireshark),这可以证明恶意软件将会和该恶意程序所属的僵尸网络的控制服务进行通信。 由于这些原因,恶意软件的作者通常希望阻止恶意软件在虚拟机中运行,但是为了做到这一点,恶意软件必须检测到它在虚拟环境中运行了。虚拟机永远不能和真实计算机一样,因此它们具有那些“给予它们”的特性,例如: 具有仅在虚拟机中会存在的特定名称的虚拟硬件设备 对真实机器不完全、有限的仿真(IDT 和 GDT 表) 含糊不清、没有文档记录的 API 在与主机层通信(Virtual PC 使用汇编指令 cmpxchg8b eax 这种魔法一般处理寄存器的方式,就可以确定它的存在) 附加的实用工具,比如 VMware Tools,他们的存在可以通过特定系统对象的名字得知(互斥体、事件、类名、窗口名称等) 清单1-使用 API 接口确定 VWware 环境

BOOL IsVMware()
{
    BOOL bDetected = FALSE;
    __try
    {
        // check for the presence of VMware
        __asm
        {
            mov    ecx,0Ah
            mov    eax,'VMXh'
            mov    dx,'VX'
            in    eax,dx
            cmp    ebx,'VMXh'
            sete    al
            movzx   eax,al
            mov    bDetected,eax
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        // if an exception occurs
        // return FALSE
        return FALSE;
    }
    return bDetected;
}

清单2-通过 Windows 注册表判断虚拟环境

BOOL IsVM()
{
    HKEY hKey;
    int i;
    char szBuffer[64];
    char *szProducts[] = { "*VMWARE*", "*VBOX*", "*VIRTUAL*" };
    DWORD dwSize = sizeof(szBuffer) - 1;
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\ControlSet001\Services\Disk\Enum", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
    {
        if (RegQueryValueEx(hKey, "0", NULL, NULL, (unsigned char *)szBuffer, &dwSize ) == ERROR_SUCCESS)
        {
            for (i = 0; i < _countof(szProducts); i++)
            {
                if (strstr(szBuffer, szProduct[i]))
                {
                    RegCloseKey(hKey);
                    return TRUE;
                }
        }
    }
    RegCloseKey(hKey);
}
  return FALSE;
}

清单3-检测 Virutal PC

sub eax,eax     ; prepare magic values
    sub edx,edx     ; in registers
    sub ebx,ebx
    sub ecx,ecx
    db  0Fh, 0C7h, 0C8h ; 'cmpxchg8b eax' instruction
; if Virtual PC is present, after performing cmpxchg8b eax
; register EAX = 1 and there will be no exception
; otherwise an exception will occur

清单4-检测 VirutalBox

BOOL IsVirtualBox()
{
    BOOL bDetected = FALSE;
    // is the VirtualBox helper library
    // installed in the system?
    if (LoadLibrary("VBoxHook.dll") != NULL)
    {
        bDetected = TRUE;
    }
    // is the VirtualBox support device
    // installed in the system?
    if (CreateFile("\\.\VBoxMiniRdrDN", GENERIC_READ, 
                   FILE_SHARE_READ, NULL, OPEN_EXISTING, 
                   FILE_ATTRIBUTE_NORMAL, NULL) 
                   != INVALID_HANDLE_VALUE)
    {
        bDetected = TRUE;
    }
    return bDetected;
}

讽刺的是,如果有检测虚拟环境存在的功能会提醒分析师这很可能就是一个恶意程序,他正在试图避免在一个安全的环境内被分析。不过,这种函数通常也被版权保护和内容保护软件使用。例如,现在常见的多媒体平台 Ipla.tv 在检测到正在虚拟环境中执行时拒绝播放节目。出于兴趣,我查看了一下这个播放器使用的库文件,不过在调试器的帮助下,禁用这个负责检测虚拟环境的功能是很容易的。 一些软件发布商也阻止他们的应用程序在虚拟机中运行,这是一个非常实际的原因——盗版。他们将软件的运行限定在某台机器上(例如通过机器硬件配置文件生成许可证密钥)。当软件安装在虚拟机中时,可以很容易的将虚拟机映像复制到所需要的多个物理计算机上,并且这些计算机都会拥有相同的硬件配置文件,从而允许软件在多台计算机中运行了。

扫描二维码关注公众号,回复: 958263 查看本文章

沙盒

沙盒就是一个和外部世界独立的安全环境,你可以在其中运行恶意程序并监视它们的活动。沙盒可以作为和主机系统相互独立地存在。最知名的是:

  • Cuckoo Sandbox
  • Anubis Sandbox
  • Norman Sandbox
  • Joe Sandbox
  • VIPRE ThreatAnalyzer
  • Buster Sandbox Analyzer

这些都是可以运行任何软件的虚拟环境,由于内置了监视工具,它们可以提供软件在启动后对系统所做更改的详细日志。通常他们基于仿真的 Windows 环境中,而且你可以很容易地检测到它们这些特性。在地下论坛上,你可以很容易的看到这种例子:

清单5-检测 Norman 沙盒

; load the address of the function DecodePointer(), which
; in a Norman Sandbox environment contains
; a different list of instructions
; to a real Windows system
; DecodePointer:
;.7C80644E| C8 00 00 00 enter   0,0
;.7C806452: 8B 45 08    mov eax,[ebp][18]
;.7C806455: 0F C8   bswap   eax
;.7C806457: C9      leave
;.7C806458: C2 04 00    retn    4
; load the address of DecodePointer
    mov eax,DecodePointer
    test    eax,eax
    je  _not_detected
; verify the byte signature
    cmp dword ptr[eax],000000C8h
    jne _not_detected
    cmp dword ptr[eax+4],0F08458Bh
    jne _not_detected
    cmp dword ptr[eax+8],04C2C9C8h
    jne _not_detected
; the presence of the Norman Sandbox environment has been detected
; terminate the application
    push    0
    call    ExitProcess
; continue the application
_not_detected:

清单中的沙盒原来大多数都是免费的,随着几个商业化后,只有少数几个继续免费,如 Anubis 沙盒。 沙盒不需要模拟完整的系统,将沙盒集成到反病毒软件中,或者像 Sandboxie 这种特定应用程序的沙盒正在变得越来越受欢迎。它们允许在安全的操作环境中执行任何应用程序,以保护敏感的系统组件受到修改。

输入图片说明

Sandboxie 不会将修改写入磁盘,也不会修改任何系统文件,只会产生分析时便于使用的外部文件。这种系统的运行依赖于系统底层的钩子(重写底层操作系统函数)以及使用额外的驱动程序、控制应用程序的行为。你可能已经猜到了,这些机制可以被恶意软件作者用来检测沙盒工具。

清单6-检测 Sandboxie 环境

BOOL IsSandboxie()
{
    // check if the Sandboxie helper library
    // is loaded in our process
    if (GetModuleHandle("SbieDll.dll") != NULL)
    {
        return TRUE;
    }
    return FALSE;
}

阻止代码分

使用像 IDA 反汇编器和 HexRays 反编译器来分析可疑文件是业界通用的惯例。这些工具允许对编译好的程序进行汇编级别的分析,如果可能还可以还原成更高级别的语言。

输入图片说明

为了让这些程序执行静态分析,就必须访问未加密的可执行文件,恶意软件的作者可以基于此来让分析变得困难,甚至不可能,致使分析师必须在分析前做额外的工作。

加密

加密是最基本的手段,用来加密整个可执行文件。这些加密工具都有同一个目的,防止恶意软件被反病毒程序检测。这些加密工具在地下论坛中卖数十美元,并且每个客户会使用唯一的副本以避免被先用的反病毒程序的签名数据库检测到。 加密工具的基础是: 加密整个可执行程序 将其附加到一个装载程序中,作为一个嵌入的资源,或者放在在文件的结尾(也就是所谓的叠加) 运行一个以这种方式准备好的程序后,可能发生两种情况,取决于恶意软件作者怎么做:简单情况下,文件被解密,解包到临时目录,并且从此处运行。更高级的做法是加载程序的代码段和数据段被取消内存映射,用解密后的可执行文件替换,之后开始运行。

加壳

UPX, FSG, MEW, ASpack, and .netshrink 这些壳都可以被用于减少可执行文件的大小。它们通常在可执行文件中插入一段结构,将一种压缩算法放在里面,制作一个装载程序。装载程序通常很小,常常用汇编语言编写。当这样一个打包好的程序运行时,由装载程序控制解压缩代码和数据、恢复可执行程序的文件结构,例如从导入表加载函数。然后从没打包时的原始程序入口点开始执行代码。

输入图片说明

这种压缩不会对真正的代码分析造成任何困难,它唯一能实现的事情就是在反病毒程序的潜在签名被改变了。现在可以自动脱壳大多数流行的加壳程序,无论是专用的脱壳模块还是通过仿真使用,并可以分析原始文件中的签名。

压缩

虽然加壳只能有助于减少可执行文件的大小,但是其受欢迎程度已经导致那些想使用加壳程序的人,还想让程序具有更难得到原始可执行代码的优势。因此混淆出现了,它们可以“争先恐后”地操作已经压缩好的程序,比如它们可能会更改节的名称、添加新的代码,然后启动解压缩程序。例如UPX-SCRAMBLER 和 UPolyX 以及非常流行的 UPX 压缩壳。

综合保护

演变的下一个阶段就是像 PELock, ASProtect, ExeCryptor 和 Armadillo 这种保护器。它们不仅能压缩可执行程序,还能添加代码来检测调试工具的存在,并且破坏或隐藏可执行程序的真实结构,使得恢复原始可执行程序的内容更加困难。保护器的一个重要部分是它们内置的强大加密算法,像 ECC(椭圆曲线加密)或 RSA 加密。 通常对特定区域进行加密,这段区域以特殊标记指明。当程序运行,指定标记间区域的代码在没有正确的许可密钥时无法被解密,甚至直到其需要运行前仍然是加密的。运行时被暂时解密,一旦运行完毕就会返回加密状态。

清单7-在 PELock 中使用加密标志的例子

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include "pelock.h"
int main(int argc, char *argv[])
{
    // code between the markers DEMO_START and DEMO_END
    // will be encrypted in the protected file
    // and will not be accessible (or executed) without
    // the correct licence key
    DEMO_START
    printf("Welcome to the full version of my application!");
    DEMO_END
    printf("nnPress any key to continue...");
    getch();
    return 0;
}

这种程度的复杂保护对于破解者是一个现实的问题。虽然这对于软件作者来说一般是好的,但是这使得病毒分析师难以生存,因为这允许了恶意软件作者使用高级加密技术,这使恶意程序的行为分析特别困难。在这种系统中,你经常会发现多态的加密算法(受保护的程序每次都重新生成唯一的加密算法)、代码突变(一系列的程序集替换,这些替换更加复杂但是等效)、代码虚拟化(原生代码被字节码替换),随着将反调试招数都整合在一起,我们已经很难对其代码进行分析、了解它们是如何工作的了。

保护的措施有:

  • 重定向导入表-混淆使用的函数的真实地址-更难从受保护的程序中重建导入表
  • 将文件的映像重定位到内存中的随机区域-阻止其从内存中直接将解密后的程序 dump 下来
  • 检测调试和系统监控工具
  • 以发现代码更改为目的主动监控引用程序代码
  • 检测虚拟环境和沙盒
  • 检测仿真器
  • 通过模拟某些用来访问功能的函数来隐藏资源等结构
  • 结合许多应用程序文件和库到一个可执行程序中,并且使用函数访问钩子来模拟存在

虚拟化

CodeVirtualizer 和 VMProtect 都是以上技术的接班人,这些工具可以让应用程序的原生代码与字节码进行替换,以这种方式保护的应用程序必须配备有执行字节码的虚拟机。 分析那些已经替换过的字节码是非常困难的,因为他们不能直接被 IDA 和 OllyDbg 这种标准工具进行分析,这些工具不能得到中间代码的格式。更高级的虚拟机能够在每次保护可执行文件时生成唯一的字节码,从而进一步使分析受保护的应用程序变得更加复杂。 在这些情况下,必须编写特殊的模块来将中间代码转换成 x86 代码。这是一个艰苦而又耗时的任务,而且上面提到的全部保护措施都可以与虚拟化结合起来,让分析变得更加不可能。

混淆

混淆的目的是让编译好的应用程序或者源代码都尽量变得不可能分析。 混淆大多数用在那些被编译成字节码的应用程序中,比如 Java 和 .NET 应用程序,也有像 Pythia 这种为 Delphi 应用程序开发的混淆器。但是这些工具大多数都用于 .NET 应用程序。用 Visual Basic 编写的程序可以多达六个版本,从原生的指令到需要额外库和虚拟运行时的字节码。而 Visual Basic 的规格是不公开的,分析其应用程序是有挑战性的。然而,通过试错,创建了一些非官方工具来对这样的应用程序进行反编译。 随着 C# 和 VB.NET 的出现,微软发布了虚拟机的规范。原来反编译这样程序的字节码是如此简单!知名的工具像 .NET Reflector 的出现,可以实现应用程序到源码的一键转换。这让软件作者很是头疼,没过多久,市场就出现了几十个工具,这些工具都能对 .NET 应用程序中的字节码(中间语言)进行混淆。这些混淆器一般都采用了如下技术: 修改指令序列执行顺序(从线性到无条件跳转的非线性) .NET 代码的动态加密 加密 .NET 应用程序的资源 加密字符串值 编译好代码的额外虚拟化层 .NET 应用程序文件结构的蓄意破坏 现在存在的大量保护程序,大多采用类似的保护机制。混淆工具的泛滥导致许多去混淆工具的产生,以 2011 年 de4dot 的产生为终点,这是一个通用的 .NET 应用程序通用去混淆工具。它可以去除超过二十种最流行的混淆方案,像 SmartAssembly、.NET Reactor、Dotfuscator、Eazfuscator 和许多其他的方案,这是给软件安全行业一记响亮的耳光, de4dot 不断开发和维护以支持最新的软件保护方案。 有趣的是,混淆器的价格要远比那些保护原生应用程序的软件要贵,在 .NET 平台上,复杂的 EXE 保护方案和代码虚拟化可以取得比混淆器更好的效果,最好的例子就是前面提到的 de4dot,仅此一点就可以扭转应用在 .NET 应用程序上的诸多保护措施。但是对于原生应用程序就没有如此通用的工具存在。

利益冲突

计算机程序保护的问题在保护系统提供商和反病毒公司之间已经成为了一种巨大的问题:合法的版权保护措施会被反病毒软件报告为病毒。保护系统的作者会因此失去他们的潜在客户,而看似反病毒公司和保护系统的斗争其实是与恶意程序的斗争。IEEE 协会试图规范保护系统制造者和反病毒公司之间的信息交流。该系统被称为 TAGGANT,将需要进行商业保护系统保护的每一个文件进行标记,并带有购买人或者公司的签名。有这样标记的文件将不再被反病毒程序标记为病毒,如果合法的保护方案被以某种方式应用在保护恶意程序中(例如恶意软件作者使用被盗的信用卡详细信息来购买保护方案),客户的签名就会被公开放在黑名单中,并且和该用户签名相关的所有文件都会被标记为潜在的恶意程序。

猜你喜欢

转载自my.oschina.net/u/3763247/blog/1789393