预估稿费:250RMB(不服你也来投稿啊!
)
投稿办法:发送邮件至linwei#360.cn,或上岸网页版在线投稿
序言

在攻防对抗的博弈中,恶意软件的作者总是费尽心机阻挡反病毒公司、安全研究职员对其编写的恶意软件进行检测、剖析.在对抗中,技能的进步螺旋式上升,那么编写恶意软件的人都采取了哪些技能来对抗剖析?这些技能又是如何发展的?反病毒公司如何应对这种寻衅?作者 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 环境
1234567891011121314151617181920212223242526BOOL 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 注册表判断虚拟环境
123456789101112131415161718192021222324BOOL 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
12345678
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
1234567891011121314151617181920BOOL 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 在检测到正在虚拟环境中实行时谢绝播放节目。出于兴趣,我查看了一下这个播放器利用的库文件,不过在调试器的帮助下,禁用这个卖力检测虚拟环境的功能是很随意马虎的。
一些软件发布商也阻挡他们的运用程序在虚拟机中运行,这是一个非常实际的缘故原由——盗版。他们将软件的运行限定在某台机器上(例如通过机器硬件配置文件天生容许证密钥)。当软件安装在虚拟机中时,可以很随意马虎的将虚拟机映像复制到所须要的多个物理打算机上,并且这些打算机都会拥有相同的硬件配置文件,从而许可软件在多台打算机中运行了。
沙盒
沙盒便是一个和外部天下独立的安全环境,你可以在个中运行恶意程序并监视它们的活动。沙盒可以作为和主机系统相互独立地存在。最有名的是:
Cuckoo Sandbox
Anubis Sandbox
Norman Sandbox
Joe Sandbox
VIPRE ThreatAnalyzer
Buster Sandbox Analyzer
这些都是可以运行任何软件的虚拟环境,由于内置了监视工具,它们可以供应软件在启动后对系统所做变动的详细日志。常日他们基于仿真的 Windows 环境中,而且你可以很随意马虎地检测到它们这些特性。在地下论坛上,你可以很随意马虎的看到这种例子:
清单5-检测 Norman 沙盒
123456789101112131415161718192021222324252627; 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 环境
12345678910BOOL 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 中利用加密标志的例子
1234567891011121314151617#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(\"大众\n\nPress 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,将须要进行商业保护系统保护的每一个文件进行标记,并带有购买人或者公司的署名。有这样标记的文件将不再被反病毒程序标记为病毒,如果合法的保护方案被以某种办法运用在保护恶意程序中(例如恶意软件作者利用被盗的信用卡详细信息来购买保护方案),客户的署名就会被公开放在黑名单中,并且和该用户署名干系的所有文件都会被标记为潜在的恶意程序。
调试器检测
如果恶意程序较为繁芜,像 IDA(反汇编)或 HexRays(反编译)此类的代码静态剖析工具不能供应剖析师判断恶意程序做了什么的信息,下一个剖析师可以用的工具是调试器 - 许可逐步跟踪程序实行的工具。
个中最盛行的调试工具:内置于商业 IDA 反汇编器(32位和64位支持)的调试器,免费的 WinDbg,以及免费的(据我所知最盛行的调试器)OllyDbg ,它许可在用户态下跟踪运用程序(OllyDbg 的的一个缺陷是,它被限定为32位运用程序)。
OllyDbg 的盛行产生了一系列应对反调试器功能的改进和扩展,这些让各种运用程序的剖析都变得随意马虎。比如引入一种脚本措辞,它可以自动完成调试器能做的事情,这让自动化脱所有已知种类的壳变得非常轻松。
另一方面,又有很多种方法可以检测这个调试器,并且恶意软件的作者都很乐意利用它们,由于这样会使得他们的代码更加难以剖析。
清单8-检测 OllyDby(作者Walied Assar)
1234567891011121314151617181920212223242526int __cdecl Hhandler(EXCEPTION_RECORD pRec,void,unsigned char pContext,void)
{
if (pRec->ExceptionCode==EXCEPTION_BREAKPOINT)
{
((unsigned long)(pContext+0xB8))++;
MessageBox(0,\"大众Expected\"大众,\"大众waliedassar\公众,0);
ExitProcess(0);
}
return ExceptionContinueSearch;
}
void main()
{
__asm
{
push offset Hhandler
push dword ptr fs:[0]
mov dword ptr fs:[0],esp
}
RaiseException(EXCEPTION_BREAKPOINT,0,1,0);
__asm
{
pop dword ptr fs:[0]
pop eax
}
MessageBox(0,\公众OllyDbg Detected\公众,\"大众waliedassar\公众,0);
}
几年前,有一个著名的调试器叫做 SoftICE,它是一个别系调试器(它可以跟踪用户模式的运用程序和系统驱动程序)。现在它已经被 WinDbg 取代了,虽然已经坚持了很多年的开拓和升级。系统调试器(或者说内核模式调试器)可以在运用程序利用系统驱动时调用。比如一个 Rootkit,它紧张的目的便是把负载埋进操作系统中(常日以用户模式开始),以此来暗藏恶意软件的进程、对文件系统构造的毁坏、隐蔽网络通信。
迭代更新
你可以利用非常多的方法来检测恶意软件,无论是校验和、文件片段、特色字符串、函数调用顺序、利用非常用函数、行为剖析、高等署名等。这些特性会勾引反病毒软件将其分为恶意还是正常。
这样是建立在假设恶意软件每次都表现出相同的特色的根本上的,修正这些特色中的某一部分就可以躲避检测,在不修正恶意软件源代码的情形下,怎么可以达到这种效果呢?
改换编译器
对编译选项的变动(比如打开或者关闭优化或者变动默认调用约定)会立即变动输出文件的构造
频繁更新和编译选项的变动相结合,将会导致在二进制级别上完备不同的天生文件,但他们可以表现出相同的功能
函数更换
俗话说:“条条大路通罗马”。在编写恶意软件时,有很多方法可以达到相同的效果。例如,为了获取对文件的访问,可以有很多 Windows 系统函数和附加库函数利用。
还有其他几十个库也供应类似的功能,可以用作替代品。
清单9-几种读文件的办法
1234567891011121314151617181920212223#include <windows.h>
#include <stdio.h>
// mode used to open a file
#define FILE_MODE 3
int main()
{
#if FILE_MODE == 1
HANDLE hFile = CreateFileA(\"大众notepad.exe\"大众, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
#elif FILE_MODE == 2
HANDLE hFile = CreateFileW(L\"大众notepad.exe\"大众, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
#elif FILE_MODE == 3
FILE hFile = fopen(\"大众notepad.exe\"大众, \"大众rb+\"大众);
#else
//...
#endif
// close file handle
#if FILE_MODE < 3
CloseHandle(hFile);
#else
fclose(hFile);
#endif
return 0;
}
在特定函数被利用的时候,大概会被标记为“危险”,但是这种办法仍旧创造了全新的代码构造,可以帮助恶意软件躲避检测。
脚本代替程序
某些任务可以由脚本措辞,像 VBScript 或者 JScript,来实行,避开调用系统函数。恶意软件有时候也会包含脚本措辞的阐明器,比如 Flame 中的 Lua、IronPython 中的 Python、乃至是 PHP。一旦源代码被隔离,剖析脚本总好过分析编译好的程序,但是恶意软件作者可选择的大量不同的措辞为病毒剖析师和反病毒公司造成了巨大困扰。
模块化设计
恶意软件并不总是立即安装在传染主机上,常日来说传染模块的规模相称小,传染后会连接到掌握做事器来下载附加模块。此设计还帮助恶意软件作者可以轻松地添加新功能或修复 Bug。对付剖析师来说,当恶意软件都在一个程序中时,事情反而变得随意马虎了。当恶意软件分裂身分歧的模块时,剖析会变慢,特殊是用不用的措辞编写的不同模块,并且每个模块都是用不同的方法来进行保护。
小众措辞
恶意软件每每会利用盛行的措辞来编写,在我看来这是由于:一、这种软件可以在许多版本的 Windows 上不须要其他系统组件就运行;二、有大量的教程和与其措辞干系的源码实例,纵然是新手也可以很随意马虎的创建恶意程序。
某些恶意软件作者更加高明,意识到利用盛行的措辞将会使他们开拓的程序易于剖析,这些措辞都有现成的工具可以利用。病毒剖析师每天都要处理这些盛行的措辞,他们配备并闇练利用干系工具,把这些程序分解成基本模块轻而易举。
另一方面,用小众措辞编写的程序例如函数式措辞或者面向业务型措辞,对剖析师来说是个巨大的寻衅。比如:
Haskell
Lisp
VisualFox Pro
Fortran
COBOL
非标准的 Visual Basic 编译器(像 DarkBasic、PureBasic)
这样的程序常日有着相称繁芜的构造,在许多情形下,他们的全体代码以一种字节码的形式存在,或者是须要剖析许多额外的库、都不用提有多耗时。也有一些为这些小众的措辞编写的反编译工具,比如 VisualFox Pro 运用程序反编译可以利用 ReFox,但是这样的工具在这个领域内是极其罕见的,由于需求太少了。
著名的 Stuxnet 病毒是利用 C 措辞和一个不起眼的面向工具框架写成的,剖析师花了很永劫光才搞清楚,它是用什么写成的,为什么有如此不屈常的构造。
在这篇文章的末端,你可以找到一个 Crackme,是用 Haskell 措辞写成的,只管这只是一个大略用来检讨容许证密钥有效性的算法,但是剖析起来仍旧特殊困难。
自动化软件
市场上有一些软件包许可利用者只须要少量的技能能力就可以创建一个能够完备访问系统文件、从网络上加载组件、访问 Windows 注册表的程序。例如:
AutoIt
Winbatch
Macro Express
利用这些工具创建的运用程序不太可能被检测到,由于他们的代码常日采取中间措辞的形式,而且须要特定的反编译器来剖析它的行为。
源码修正
源码修恰是创建输出文件最高级的方法之一,那么若何修正源码才能产生不同的输出文件呢?
使得源码突变,比如通过利用模版
在源文件中重新排序函数
改变各个功能的优化选项
将伪参数引入函数并假造他们的用法(编译器可以优化删除未利用的参数)
在代码行之间引入垃圾(比如实行不必要的任务的垃圾指令、彼此跳转的指令、函数参数的不必要考验、对本地变量不必要的引用)
非线性实行代码(比如 switch)
构造定义的改变,即数据构造成员的随机排列或引入虚拟字段
所有这些变动都能在出书文件中引发重大变革。
清单10-在 Delphi 代码中增加垃圾指令
123456789101112131415161718192021222324252627282930procedure TForm1.FormCreate(Sender: TObject);
begin
// junk instructions (these instructions
// do not have any impact on the behaviour
// of the application)
asm
db 0EBh,02h,0Fh,078h
db 0EBh,02h,0CDh,20h
db 0EBh,02h,0Bh,059h
db 0EBh,02h,038h,045h
db 0E8h,01h,00h,00h,00h,0BAh,8Dh,64h,24h,004h
db 07Eh,03h,07Fh,01h,0EEh
db 0E8h,01h,00h,00h,00h,03Ah,8Dh,64h,24h,004h
db 0EBh,02h,03Eh,0B8h
end;
// This line will be unreadable by
// a disassembler, thanks to
// the junk instructions
Form1.Caption := 'Hello world';
asm
db 070h,03h,071h,01h,0Ch
db 0EBh,02h,0Fh,037h
db 072h,03h,073h,01h,080h
db 0EBh,02h,0CDh,20h
db 0EBh,02h,0Fh,0BBh
db 078h,03h,079h,01h,0B7h
db 0EBh,02h,094h,05Ch
db 0C1h,0F0h,00h
end;
end;
我见过修正源码最前辈的工具是由 Syncrosoft 开拓的 MCFACT 系统,用于保护有名音响制造公司 Steinberg 的 Cubase 套件。该系统会首先剖析源码,然后将其转换为受保护的形式。
清单11-经由 MCFACT 系统后的 C++ 代码
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849//MCFACT_PROTECTED
unsigned int findInverse(unsigned int n)
{
unsigned int test = 1;
unsigned int result = 0;
unsigned int mask = 1;
while (test != 0)
{
if (mask & test)
{
result |= mask;
//MCFACT_AUTHORIZED
test -= n;
}
mask <<= 1;
n <<= 1;
}
return result;
}
// code after protection
unsigned int findInverse(unsigned int n)
{
...
class _calculations_cpp_test_0 test=((_init0_0::_init0)());
class _calculations_cpp_result_0 result=((_init1_0::_init1)());
class _calculations_cpp_mask_0 mask=((_init2_0::_init2)());
_cycle1:{
signed char tmp8;
((tmp8)=((IsNotEqual)((test), (_calculations_cpp_c_0_0))));
if ((tmp8)) {
{
unsigned int tmp7;
((And)((mask), (test), (tmp7)));
if ((tmp7)) {
((Or)((result), (mask), (result)));
((Sub)((test), (n), (test)));
}
((ShiftLeftOne)((mask), (mask)));
((n)<<=(1U));
}
goto _cycle1;
}
}
{
unsigned int tmp9;
((Copy)((result), (tmp9)));
return(tmp9);
}
}
剖析这样的代码是相称困难的,这个保护终极被 Team-AiR 攻破,根据其 NFO 文件显示,他们花了近 4000 小时,将近一年事情的一半。在此期间,软件发行商可以连续发卖软件而不用考虑盗版的问题。Cloakware 现在是 Lrdeto 公司也在开拓类似的保护系统,但是该项目被放弃了。我相信如果这种保护系统与前辈的虚拟化技能相结合,对付用户和反病毒软件都是灾害性的。幸运的是,构建这种系统非常困难而且不常见。
数据和字符串加密
这是隐蔽敏感信息时最常用的办法,加密常日用于包含那些传染文件的名字、掌握做事器地址、乃至是 FTP 或者邮箱的密码。恶意软件与其掌握做事之间的通信也常常是加密的,以担保其内容是秘密的。
Rootkit 样本 Rustock 利用了 RC4 加密算法(这是恶意软件中最常见的一个),以保护自己的模块可供剖析,在传染时,从打算机取得的硬件 ID 将作为其病毒的加密密钥,同时这个程序将不会在其他打算机上事情(由于硬件 ID 不匹配),同一韶光在另一台打算机上进行剖析也是不可能的(例如病毒被发送到了反病毒公司进行剖析),必须得到被传染打算机的硬件 ID 才能将病毒代码解密。
公认的加密算法 AES 利用固定的数据表,软件(就像 PEiD 这种扫描器)剖析可以立即检测到这种算法存在于可实行程序中,这会给剖析师提醒,有什么东西加密了躲在代码中,值得调查。出于这个缘故原由,恶意软件作者常常利用动态天生的加密算法,这样不会引起疑惑,例如:
清单12-动态天生的得到利用StringEncrypt做事的解密代码
1234567891011121314151617181920212223242526// encrypted with https://www.stringencrypt.com (v1.1.0) [C/C++]
// wszLabel = \"大众C/C++ String Encryption\"大众
wchar_t wszLabel[24] = { 0x2976, 0x2AF9, 0x289C, 0x2B9F, 0x2BA2, 0x2D05, 0x2688, 0x316B,
0x336E, 0x33D1, 0x3214, 0x3337, 0x2CDA, 0x277D, 0x3200, 0x34A3,
0x32C6, 0x3269, 0x32AC, 0x312F, 0x3392, 0x3255, 0x3258, 0x609B };
for (unsigned int Qqhvj = 0, JWqBw = 0; Qqhvj < 24; Qqhvj++)
{
JWqBw = wszLabel[Qqhvj];
JWqBw -= 0xA50D;
JWqBw += Qqhvj;
JWqBw = (((JWqBw & 0xFFFF) >> 15) | (JWqBw << 1)) & 0xFFFF;
JWqBw ^= 0xB0D1;
JWqBw ++;
JWqBw = (((JWqBw & 0xFFFF) >> 3) | (JWqBw << 13)) & 0xFFFF;
JWqBw = ~JWqBw;
JWqBw += 0xAF02;
JWqBw ^= Qqhvj;
JWqBw ^= 0x9FC1;
JWqBw -= 0xA9E0;
JWqBw = ~JWqBw;
JWqBw ++;
JWqBw = (((JWqBw & 0xFFFF) >> 3) | (JWqBw << 13)) & 0xFFFF;
JWqBw --;
wszLabel[Qqhvj] = JWqBw;
}
wprintf(wszLabel);
定时炸弹
有时候恶意软件被设计在一定韶光段后激活,例如它可以检讨本地韶光然后在一个精确的日期后操作,这样的恶意软件有可能会避开反病毒软件(由于没有不雅观察到系统变动)。因此,如果初步剖析表明这个程序不是完备安全的,进一步不雅观察是明确的做法。
清单13-一个特定的日期后激活程序
12345678910111213141516171819202122#include <windows.h>
void MrMalware(void)
{
// malicious code
MessageBox(NULL, \"大众I'm a virus!\"大众, \"大众Boo!\"大众, MB_ICONWARNING);
return;
}
int main()
{
SYSTEMTIME stLocalTime = { 0 };
// obtain the current time
GetLocalTime(&stLocalTime);
// activate the malicious code
// only after this date
if (stLocalTime.wYear >= 2013 && \
stLocalTime.wMonth >= 5 && \
stLocalTime.wDay >= 2)
{
MrMalware();
}
return 0;
}
当我们疑惑一个程序中包含定时炸弹时怎么办?最直接的办理方案是向前或者向后调度系统韶光,并利用监控工具监控系统的变革。只管很大略,但是对创造定时炸弹很有效。
延迟实行
除了定时炸弹,有些恶意程序会采取延迟实行,就像定时器一样。基本思路是,程序启动后不会发生恶意行为,在设定的韶光后就会发生。这是双重效果,首先可以误导剖析师,在启动过程中没有任何反应,程序该当是安全的,阻挡了其进一步进行剖析。其二,延时实行可以欺骗反病毒软件的仿真器,仿真器都受到韶光的限定,由于用户启动程序并等待了一个半小时后触发了加密代码的实行,对仿真器来说这已经毫无意义了。一个良好的仿真器会检测韶光延迟(比如 sleep 函数、延迟循环等),但不可能将所有的韶光延迟都考虑在内,Windows 中多种度量韶光的办法意味着利用韶光延迟对动态代码剖析是一种好的防御方法。
清单14-在一定韶光周期后激活的恶意代码
123456789101112131415161718#include <windows.h>
DWORD dwTimerId = 0;
const DWORD dwTimerEventId = 666;
// callback function activated after a designated time
VOID CALLBACK MrMalware(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
// malicious code
MessageBox(NULL, \"大众I'm a virus!\"大众, \公众Boo!\"大众, MB_ICONWARNING);
return;
}
int main()
{
// activate malicious code 5 seconds
// after the program is launched
dwTimerId = SetTimer(NULL, dwTimerEventId, 5 1000, MrMalware);
MessageBox(NULL, \"大众I'm a friendly program\"大众, \公众Hi!\"大众, MB_ICONINFORMATION);
return 0;
}
加密署名
加密署名常日用来署名运用程序,以此担保拥有数字署名的运用程序来自受信赖的来源。某些防病毒软件会放行那些有数字署名的运用程序。为了得到数字证书,个人和企业的信息都必须在证书中,必须被验证过。如果没有可以证明的文件(比如银行对账单、身份证的扫描件)是不可能得到合法机构的证书的。然而,数字证书确实会被黑客盗取,然后用于签署恶意软件。这样的例子并不多见,但这样会让那些为所有具有数字证书放行的反病毒软件造成问题。
在线扫描
恶意软件的作者一定非常想知道他的恶意软件被反病毒软件检测的情形,安装所有的反病毒软件不仅费事费时,它们之间还并不兼容。以是恶意软件的作者每每会利用在线做事来检测。如果你第一个想到的是 VirusToal(最近被 Google 收购),你只理解了一点点。恶意软件的作者是不会利用这些网站(又例如 Jotti)的,提交给他们的所有文件都与反病毒公司共享,用来提高其反病毒产品的检测精度。恶意软件作者会利用像 NoVirusThanks 或 NoDistribute,它们都以反面反病毒公司互助而著称。
结论
从我的履历不雅观察,恶意软件作者和剖析师的战斗永久不会停滞。有趣的是战役两边,一边是好的,像反病毒公司、为了合法版权保护的公司,另一边都是坏的,那些试图打破软件保护的人、那些写出可以被恶意软件利用的软件的人。从文章开始到结束,我们纵不雅观工具随着韶光的演化,我们可以推测出接下来会发生什么吗?我的意见是统统都会向代码虚拟化的方向提高,但是会比当前的方案更前辈。这从当前市场上对原生运用程序保护工具以及 .NET 运用程序虚拟化工具的涌现就可以看出。反病毒公司会如何处理呢?一如既往地,要依赖卓越的员工的精彩事情。