X-Force
X-Force: Force-Executing Binary Programs for Security Applications
Summary
强制执行路径:随机输入/按需分配内存/异常恢复机制等目的都是保证路径可以强制执行,暴露恶意行为。
线性集追踪语义:保证指针更新的一致性,指针变量一般只有加减操作,更简单。
- L-INIT:具有地址值的所有全局变量的SM映射被设置为它自己的地址,这意味着变量最初仅与其自身线性相关
内存错误检测和恢复规则:
路径探索:根据应用场景提出Fitness函数
摘要
本文介绍了一种新颖的二进制分析引擎X-Force。
X-Force可以强制执行二进制文件,无需输入或适当的环境。
它还通过系统地分析一小组条件控制转移指令的分支结果来探索二进制内部的不同执行路径。
X-Force具有无崩溃执行模型,可以检测异常并从异常中恢复。
特别是,它可以通过按需分配内存并将有问题的指针设置为分配的内存来修复无效的内存访问。
我们已将X-Force应用于三个安全应用程序。第一个是构造控制流图和调用剥离的二进制图。第二种是暴露恶意软件的隐藏行为,包括打包和混淆的APT恶意软件。 X-Force能够揭示人工检查遗漏的隐藏的恶意行为。在第三个应用中,X-Force显着改善了剥离二进制的动态类型重建中的分析覆盖。
Intro
二进制分析有许多安全应用程序。例如,给定一个未知的,可能是恶意可执行的二进制分析,有助于构建其人类可观的表示,如控制流图(CFG)和调用图(CG),安全分析师可以使用它来研究其行为。二进制分析还有助于识别和修补COTS二进制文件中的安全漏洞。有价值的信息可以通过二进制分析从可执行文件中进行反向工程。这些信息包括网络协议,输入格式,变量类型和数据结构定义。它们可以支持网络嗅探,漏洞利用生成,VM内省和取证分析。
现有的二元分析可大致分为静态,动态和符号(concolic)分析。静态分析直接分析可执行文件而不执行它;动态分析通过执行二进制获取分析结果;符号(concolic)分析能够生成输入以探索二元的不同路径。这些不同类型的分析具有各自的优势和局限性。静态分析难以处理打包和混淆的二进制文件。已知内存消歧和间接跳跃/调用目标分析对于静态分析非常具有挑战性。
动态二进制分析基于在一组输入上执行二进制文件。它广泛用于分析恶意软件。然而,动态分析本质上是不完整的。分析结果的质量很大程度上取决于测试输入的覆盖范围。此外,现代恶意软件变得非常复杂,为二进制分析带来了许多新的挑战:(1)对于零日二进制恶意软件,我们通常对它没有任何了解,尤其是其输入的本质,使传统的基于执行的分析变得困难; (2)恶意软件二进制文件越来越多地配备了反分析逻辑,因此即使给定有效输入也可能拒绝运行; (3)恶意软件的错误可能包含多阶段,条件保护和环境特定的恶意有效载荷,这使得很难揭示所有有效载荷,即使有人设法执行它们也是如此。
近年来,符号分析取得了很大进展。一些处理二进制程序并且可以探索二进制中的各种路径。但是,将它们缩放到复杂的真实世界二进制文件时存在困难,因为它们通过将单个指令建模为符号约束并使用SMT / SAT求解器来解决生成的约束来进行操作。尽管最近有令人印象深刻的进展,但SMT / SAT仍然很昂贵,虽然可以同时执行符号和混合执行,以便在符号分析遇到困难时具体执行可能有所帮助,但用户需要提供具体的输入,称为种子输入,种子输入的质量对于执行路径至关重要可以探索。由于没有或很少了解恶意软件输入,因此难以创建此类种子输入。此外,许多现有技术无法处理混淆或自修改二进制文件。
在本文中,我们提出了一种名为X-Force的新型实用执行引擎。 X-Force背后的核心启用技术是强制执行,顾名思义,它强制任意二进制文件在不同的路径上执行,无需任何输入或环境设置。更具体地说,X-Force通过动态二进制检测来监视二进制文件的执行,系统地强制可能影响执行路径的一小组指令(例如,谓词和跳转表访问)具有特定值,而不管它们是什么输入值,并在需要输入时提供随机值。因此,可以系统地探索二进制的具体程序状态。例如,通过设置自我保护检查的分支结果,可以强制打包/混淆的恶意软件解压缩/自我混淆,这会在调试器或虚拟机存在时终止执行。 X-Force能够通过执行按需内存分配来容忍无效的内存访问。此外,通过探索二进制的可达状态,X-Force能够探索二进制行为的不同方面或阶段。例如,我们可以暴露恶意软件的数据泄漏操作,而不会使真正的数据成为目标。
与手动检查和静态分析相比,X-Force更准确,因为静态分析的许多困难,例如处理间接跳转/调用和混淆/打包代码,可以通过X-Force的具体执行得到显着减轻。与符号/共同分析相比,X-Force在实用性和可扩展性方面更好,略微牺牲精度。请注意,X-Force可以探索不可行的路径,因为它会强制谓词结果;而符号分析则试图通过约束求解来尊重路径的可行性。 X-Force的本质将在后面的第6节中讨论。此外,X-Force中的执行都是具体的。在不需要建模和解决约束的情况下,X-Force更有可能扩展到大型程序和长时间执行。 X-Force的具体执行使其适用于分析打包和混淆的二进制文件。它还可以轻松地将现有的动态分析移植到X-Force,以利用大量的执行,从而减少动态分析的不完整性。
我们的主要贡献总结如下:
- 我们提出X-Force,这个系统可以强制执行二进制文件,无需输入或任何环境设置。
- 我们开发了一种无崩溃的执行模型,可以正确地检测和恢复异常。 我们还开发了各种执行路径探索算法。
- 我们已经解决了使技术在真实世界二进制文件上工作的技术挑战,包括打包和混淆的恶意软件二进制文件。
- 我们开发了三种X-Force应用程序。 第一个是构建剥离双子的CFG和CG,具有高质量的间接跳跃和呼叫目标识别; 二是研究高级恶意软件的隐藏行为; 第三个是在反向工程变量类型和可执行文件的数据结构定义中应用X-Force。 我们的研究结果表明,X-Force大大提升了先进水平。
Motivation
void main() {
int x=inoutInt(...);
if (C(x))
p=(DNSentry*)malloc(...);
if (x & CODE_RED) {
genName(x,p);
table_put(x,p);
}
...
table_put(...,o); /*o is of type T*/
...
s=table_get(y); /*y==x through execution*/
if (s)
/* redirection for the domain specified by s*/
}
void genName(int x, DNSentry *q) {
inputDictoinary();
*(q->name)=...Lookup(x,date())...;
}
void * table_get(int key) {
.../* i is derived from key*/
if (key==bucket[i])
return bucket[i+4];
}
void table_put(int key, void* value) {
.../* i is derived from key*/
bucket[i]=key;
bucket[i+4]=value;
}
考虑图1中的片段。它显示了一个隐藏的恶意有效负载,它劫持了特定域名(第14行)的名称解析,该域根据当前日期(函数genName()
)而变化。特别是,它在第2行接收到一些整数输入。如果输入满足第3行的条件C,则将分配DNSentry对象。在第5-8行中,如果输入设置了CODE_RED
位,则通过调用genName()
填充对象,并将输入和生成的名称作为(键,值)对存储到哈希表中。在第12-14行中,检索该对并用于指导域名重定向。注意,哈希表用作各种类型对象的通用存储。在第10行中,还将无关对象o插入表中。
这个例子说明了静态和符号/复杂分析所面临的一些挑战。在静态分析中,很难确定在第12行收到的对象是第7行插入的对象,因为抽象域必须精确地建模哈希表put/get操作的行为和条件y == x,这需要上下文敏感和路径敏感的分析,并消除table_get()
和table_put()
中的存储bucket[i]
和bucket[i+4]
的歧义。许多静态分析技术所做的近似通常决定了第12行的对象可能是放在第7行或第10行的对象。
仅在二进制级别执行,这样的分析实际上比这里描述的更具挑战性。在符号/符号分析中,可以将第2行的输入建模为符号变量,使得通过求解对应于路径条件的符号约束,可以达到隐藏的有效载荷。但是,如果文件不可用,则在第21行读取的字典将难以处理。如果文件具有非常重要的格式和大小,则将文件编辑为符号通常会导致可伸缩性问题,因为生成的符号约束通常很复杂,并且用于获取语法正确输入的搜索空间可能非常大。
在X-Force中,首先通过提供随机输入来执行二进制文件。请注意,X-Force不需要事先知道输入格式,因为它的异常恢复机制可以防止任何崩溃/异常。换句话说,随机输入值的提供仅仅是为了允许执行继续进行,而不是沿着不同的路径驱动执行。在第一次正常运行中,假设第3,5和13行的条件的假分支被采用,产生无趣的执行。然后,X-Force将尝试通过执行系统搜索来强制设置少量(例如,1或2)谓词的分支结果。假设第5行的分支结果被强制设置为“true”。恶意有效载荷将被强制激活。请注意,指针p在第6行具有空值,这通常会使第22行的执行崩溃。X-Force通过在第22行之前按需定位内存来容忍此类无效访问。此外,即使第21行字典文件不存在,X-Force将通过提供随机输入值来强制它通过。因此,一些随机整数和域被插入表(第7行)并稍后检索(第12行)。最终,随机域名在第14行被重定向,从而暴露了DNS劫持操作。我们认为,只要隐藏的劫持逻辑被暴露,域名本身并不重要。
3.设计
3.1 强制执行语义
本节介绍单个强制执行如何进行的基础知识。 目标是进行不会崩溃的执行。 为了便于阅读,我们重点介绍如何检测和恢复此子句中的内存错误,然后逐步介绍强制执行的其他方面,如路径探索和处理库和后续部分中的线程。
语言:由于x86指令集的复杂性,我们引入了一种简单的低级语言,它可以模拟x86二进制可执行文件以方便讨论。我们只模拟一个足以说明关键思想的子集。图2显示了语法。
内存读写由R(ra)
和W(ra,rv)
建模,ra保持地址,rv为值。由于它是一种低级语言,我们不会模拟条件或循环语句,而是保护跳跃; malloc()
和free()
表示堆分配和释放。函数调用和返回由call()
和ret
建模。在我们的语言中,堆栈/堆内存地址被建模为一个整数范围,一个特殊值0表示空指针值。程序计数器(或指令地址)由PC集明确建模。观察每条指令都用PC标记,表示其指令地址。直接跳转/调用使用PC值进行参数化,而间接跳转/调用则使用寄存器进行参数化。
自定义语法,模拟x86; 下面是线性集追踪语义
在X-Force中,我们通过按需分配内存来确保执行不可崩溃。但是,当我们用分配的内存替换指向无效地址a的指针时,我们需要更新具有相同地址值的所有其他指针变量或表示与地址的偏移量的值。我们通过线性集跟踪语义来实现这一点,线性集跟踪语义也是强制执行的基本语义。其目标是识别变量集(即二进制级别的存储器位置和寄存器),其值具有线性相关性。在本文中,我们说如果一个变量的值通过加减一个值来计算另一个变量的值,则两个变量是线性相关的。请注意,它比传统的线性相关定义更简单,没有乘除。然而,在这项工作中足够了,因为线性集跟踪的目标是识别相关指针变量,这些变量由地址偏移引起,这些地址偏移仅是加法和减法。
语义如表1所示。相应的定义如图3所示。特别是,线性集LSet
表示一组地址,使得存储在这些地址中的值是线性相关的。映射SR将寄存器映射到LSet的引用。直观地,人们可以解释它将寄存器映射到指向一组地址的指针,使得存储在寄存器中的值和那些地址是线性相关的。两个寄存器映射到相同的引用(LSet)意味着两个寄存器的值也是线性相关的。类似地,映射SM将地址映射到LSet的引用,使得地址中的值和LSet中的所有地址线性相关。线性集跟踪的本质是维护已访问的所有寄存器和地址的SR和SM映射,以便在任何执行点,我们可以查询任何给定变量的线性相关变量集。
在执行之前,具有地址值的所有全局变量的SM映射被设置为它自己的地址,这意味着变量最初仅与其自身线性相关(规则L-INIT
)。函数isAddr(v)
确定值v是否可以是地址。 X-Force监控所有内存分配和映像加载过程。因此,给定一个值,如果X-Force落入静态,堆或堆栈内存区域,则将其视为指针。请注意,我们不需要确定该值确实是一个地址。过近似仅引起一些额外的线性集跟踪。对于存储器读操作,如果SM集存在,则目标寄存器的SR映射指向地址寄存器中值的SM集,这意味着该值是地址,否则设置为nil
(规则L-READ
)。请注意,在规则中我们使用“r”表示r的符号名称,ra表示存储在ra中的值。 \(SR("r")→SM(r_a)\)表示我们将SR("r")
设置为指向SM(ra)
集。对于存储器写入,我们首先从其线性集中消除目标地址。然后,地址被添加到值寄存器的线性集合,因为地址基本上表示新的线性相关变量。最后,更新地址的SM映射(规则L-WRITE
)。请注意,操作“=”表示设置更新,这与“→”不同,表示设置映射更新。对于简单的地址分配,SR集设置为指向空线性集,这与nil
值(规则L-ADDR
)不同。空集本质上是一个LSet对象,可以由多个寄存器指向它们以表示它们的线性关系。零值不能用于此目的。对于线性运算符,目标寄存器的SR映射设置为指向保存地址值的寄存器的SR映射(规则L-LINEAR
)。直觉上,这是因为我们只对地址值之间的线性相关感兴趣(出于内存错误恢复的目的)。对于堆是释放,我们必须从其线性集(规则L-FREE
)中删除每个解除分配的地址。
内存错误检测和恢复规则—按需分配
表2列出了一组内存错误检测和恢复规则。相关定义如图3所示。引入辅助映射accessible()
来表示地址是否已分配且因此可访问。 M-ALLOC
和M-FREE
规则是标准的。在读取或写入不可访问的地址时,X-Force调用函数recover()
,寄存器保持无效地址以执行恢复。在函数中,我们首先获取线性集中所有变量的值,并确定最小值和最大值(第1-6行)。请注意,值可能不同(通过地址偏移操作)。然后,我们根据值的范围和预定义的默认内存块大小按需分配一块内存。然后在第9-12行中,线性集中的变量根据它们在块中的偏移量而更新。我们想指出按需分配可能没有分配足够的空间。但是,当发生越界访问时,将检测到这种不足,并进一步按需重新分配。我们还想指出,正确开发的程序在读取之前首先会写入地址。因此,按需分配通常由第一次写入无效缓冲区触发,以便可以正确写入并稍后读取该值。换句话说,我们不需要在按需分配的缓冲区中恢复值。
在我们的实际实现中,我们还更新所有线性相关的寄存器,这可以通过识别指向同一组的寄存器来确定。此外,规则仅描述了我们如何确保堆内存安全,而X-Force保护关键堆栈地址,如返回地址和参数,我们将在后面讨论。
示例:图4显示了具有线性集跟踪和内存安全语义的示例执行的一部分。该计划来自动机示例(图1)。在执行中,采用第3行的else分支,但强制执行第5行的真分支。因此,指针p在传递给函数genName()
时具有空值,这将在第22行引起异常。在图4中,我们关注第6,22和7行的执行。第二节列显示二进制代码(以我们的简化语言)。第三列显示了相应的线性集计算和内存异常检测和恢复。最初,根据规则L-INIT
,SM(&p = 0x8004c0)
被设置为指向集合{0x8004c0}
。在二进制代码行2处,SR(eax)
被设置为指向SM(&p)
的集合。在第3行,由于该值被进一步复制到堆栈地址0xce0080
,eax
,&p
和堆栈地址都指向包含&p
和堆栈地址的相同线性集。直观地,这些是线性相关的三个变量。在第9行和第10行,edi
进一步指向相同的线性集。在第12行,当程序试图访问由edi = 4表示的地址时,存储器安全组件检测到异常并执行按需分配。根据线性集,&p
和堆栈地址0xce0080
设置为新分配的地址0xd34780
,而edi
根据其偏移量更新为0xd34784
。虽然未在表中显示,但程序进一步初始化新分配的数据结构。结果,当指针p稍后传递给table_put()
时,它指向有效的数据结构。
在项目的早期阶段,我们尝试了一种更为简单的策略,即在发现异常时终止强制执行。但是,我们观察到,由于我们不提供任何实际输入,因此异常非常常见。此外,简单地跳过导致异常的指令也不起作用,因为这会对程序状态的破坏产生连锁效应。最后,提出的不会崩溃的执行模型被证明是最有效的。
X-Force还可以通过跳过导致异常的指令,自动从其他异常中恢复,例如除零。细节被省略。
3.2 X-Force路径探索
X-Force的一个重要功能是探索给定二进制文件的不同执行路径以暴露其行为并获得完整的分析结果。在本小节中,我们将解释路径探索算法和策略。
为了简化讨论,我们首先假设二进制仅通过简单谓词(即具有恒定控制转移目标的谓词)执行控制转移。我们将介绍如何在实际设置中扩展算法,例如,支持在后面的部分中探索间接跳跃。
算法1描述了一种通用路径探索算法,它生成一个强制执行池,该池应该满足由可配置适应度函数指定的目标。它是一种工作清单算法。工作列表存储(强制)执行的列表,可以通过切换更多谓词来进一步探索。每个执行都由一系列整数表示,指定要执行的谓词实例切换。请注意,X-Force仅强制设置一小组谓词实例的分支结果。它允许其他谓词实例像往常一样运行。最初(第1行),工作列表是一个带有零序列的单例集,表示不切换任何谓词的执行。请注意,工作列表最初不为空。在强制执行结束时,我们更新适应度函数,该函数指示要探索的剩余空间(第6行),例如覆盖范围。然后在第7-16行中,我们尝试确定是否有兴趣进一步切换更多谓词实例。第7-9行计算符合切换条件的谓词实例序列。注意,它不能是在开关中指定的最后一个切换谓词之前的谓词,因为切换这样的谓词可能会改变控制流,使得开关中的规范变得无效。在第10-16行中,对于每个符合条件的谓词及其当前分支结果,我们查询适应度函数以确定是否应该进一步切换它以生成新的强制执行。如果是这样,我们将其添加到工作列表并更新适应度函数。请注意,在每次新的强制执行中,我们实质上都会再切换一个谓词。
不同的Fitness Functions。对于真实世界的二进制文件,所有可能路径的搜索空间通常都非常大。不同的应用程序可以定义不同的适应度函数来控制他们想要探索的范围。在下文中,我们介绍了我们使用的三种适应度函数。可以类似地开发其他更复杂的功能。
- 线性搜索。在某些应用中,例如构建控制流程图和动态类型逆向工程(第5节),目标可能只是涵盖每条指令。适应度函数F可以定义为覆盖的映射:谓词×布尔→布尔值,用于确定谓词的分支是否已被覆盖。因此,算法1的第11行中的框中的评估被定义为!覆盖(p,¬b),这意味着如果没有覆盖其他分支,我们将切换谓词。一旦我们决定切换附加谓词,就会更新适应度函数以反映新的覆盖范围(第12行)。因此,所需的执行次数是O(n),其中n是二进制中的数字指令。
- 二次搜索。在识别间接呼叫目标这样的应用中,这是二进制分析中一个非常重要的挑战,仅仅涵盖所有指令可能还不够,我们可能需要涵盖可能导致间接呼叫或产生不同间接的路径呼叫目标。因此,我们将F定义为一组设置,以保持所有探索路径已经发现的间接呼叫站点和潜在间接呼叫目标的集合。因此,第11行的评估是为了测试icall的基数是否随着当前探索的路径而增长。如果是这样,执行被认为是重要的,并且进一步探索执行中所有符合条件的唯一谓词(非实例)。复杂度为O(n2),n指令数。 X-Force还可以限制函数内的二次搜索。
- 指数搜索。如果我们只是设置评估在第12行中,算法执行指数搜索,因为它将探索每个可能的组合。在实践中,我们无法承受这样的搜索。但是,X-Force为用户提供了在二进制的子范围内限制这种指数搜索的能力。
污点分析减少搜索空间。观察是我们不必在低级效用方法中强制设置谓词,因为它们的分支结果通常不受任何输入的影响。因此,在X-Force中,我们使用污点分析来跟踪谓词是否与程序输入相关。 X-Force只会强制那些被污染的谓词的分支结果。由于这是一种标准技术,我们省略了它的细节。
5 Evaluation
5.2 malware analysis
理解未知恶意软件样本行为的一种常见方法是查看它所产生的库调用。这可以通过静态,动态或符号分析来完成;但是,它们都有局限性。
静态分析无法获取动态计算的库调用的参数,并且在打包或混淆样本时不可行。传统的动态分析可以获得参数,并且不受打包和混淆的影响,但是,它只能根据输入和环境来探索一些执行路径。不幸的是,恶意软件的输入通常是未知的。符号分析虽然能够根据路径条件构造输入,但难以处理复杂或打包的二进制文件。
X-Force克服了这些问题,因为传统的动态分析可以建立在X-Force之上,以探索各种执行路径,而无需提供任何输入或环境。在本案例研究中,我们演示了使用我们在X-Force之上构建的库调用分析系统来分析现实世界的恶意软件样本。
当我们在X-Force之上实现库调用分析时,我们稍微调整X-Force以使其适合处理恶意软件:
(1)我们启用大多数库函数的具体执行,包括输出功能,因为许多打包器使用输出函数(例如RtlDecompressBuffer()
)来解包代码。我们继续跳过一些库调用,如Sleep()
和DeleteFile()
;
(2)我们拦截了一些分配内存和更改页面属性的函数,例如VirtualAlloc()
和VirtualProtect()
。这是为了跟踪由于自修改和动态生成的代码而在运行时不断变化的代码和数据的内存区域。
鉴于恶意软件样本,我们使用X-Force来探索其路径。我们使用线性搜索算法(第3.2节),因为它在效率和覆盖范围之间提供了良好的平衡。在每次执行期间,我们记录一系列函数调用。对于库调用,我们还记录参数值。然后将跟踪转换为过程间流程图,该流程图具有控制转移指令,包括跳转和调用,作为其节点,以及控制流/调用边作为其边缘。库调用的参数也在图上注释。多个执行中生成的图形将合并以生成最终图形。然后,我们手动检查最终图表以了解恶意软件行为。
我们在10个真实恶意软件样本上评估我们的系统,这些样本是野生捕获的病毒/木马或[9]中描述的APT样本。由于我们的分析主要集中在库调用上,因此我们选择已识别的库函数的数量及其调用站点的总数作为评估指标5。我们还将我们的结果与IDA-Pro和本地运行进行比较。在IDA中,库函数从导入表中识别出来;通过扫描反汇编来识别调用站点。在本地运行中,我们在没有任何参数的情况下执行恶意软件,并使用PIN工具记录库调用。
结果显示在表7中。我们可以看到,对于打包或混淆的样本,例如dg003.exe
, Win32/PWSteal.F
, APT1.DAIRY
和APT1.BOUNCER
,与X-Force相比,IDA获得的库函数和调用站点更少力。对于未打包或混淆的其他样本,由于可执行文件可以正确解析,因此IDA和X-Force中获得的指标非常接近。但是,即使在这种情况下,静态分析也不足以理解恶意行为,因为它不显示库函数参数的值。与本机运行方法相比,X-Force可以识别更多库函数和调用站点。
接下来,我们提供两个代表性样本的详细分析。
Dg003.exe:这是一个典型的APT恶意软件样本,具有多阶段,条件保护和环境特定的有效负载。在第一阶段,恶意软件提取它作为资源携带的DLL,使用专有算法将DLL打包在内存中,并将打包的DLL写入磁盘。在第二阶段,加载打包的DLL,在内存中解压缩并执行主有效负载。
之前的报告[26]中,分析师使用静态和动态分析来分析此样本。要使用IDA Pro执行静态分析,他们手动提取并解压缩DLL。这需要反汇编算解包算法,这既费时又困难。我们的系统通过具体执行为我们执行拆包的拆包程序避免了这种麻烦。与动态分析相比,X-Force需要大约5个小时才能完成800次执行,以探索恶意软件第一和第二阶段的所有路径。之后,将跟踪转换为包含378个函数的流程图。我们的系统能够发现以前的报告中未提及的一组恶意行为。如图5所示,图中每个突出显示的函数调用对应于之前未经证实的恶意行为。使用相应功能中的库调用来识别每个行为。例如,如图6所示,库调用和函数0x10009b50
中的参数显示它递归枚举和删除从根目录开始的文件和目录,这表明它的行为是删除所有文件。磁盘。
在图5中,我们可以看到所有这些函数调用的共同支配者(以红色突出显示)确定eax寄存器的值是否大于0x196。通过X-Force中的污点分析,我们发现eax寄存器的值与输入有关,该输入是先前recv库函数调用中的缓冲区。这表示它代表C&C服务器发送的命令ID,导致执行不同的恶意行为。因此,我们怀疑之前的分析师错过了一些行为,因为C&C服务器在他们运行恶意软件时只发送了部分可能的命令。我们还发现recv函数调用中的缓冲区使用私有解密算法转换为命令ID,因此符号分析解决约束并构造有效输入是不可行的。我们还想指出,在我们执行分析时,此恶意软件的C&C服务器已经处于非活动状态;如果我们没有使用X-Force,我们将无法发现这些恶意行为。
Win32 / PWSteal.F:在尝试使用X-Force之前,我们首先尝试使用IDA-Pro进行静态分析。令人惊讶的是,此示例不会导入任何可疑的库函数;甚至不是可以执行I / O的功能(例如读/写文件,注册表或网络套接字)。也不导入LoadLibrary()
和GetProcAddress()
函数,这意味着不使用动态加载库的常用方法。可执行文件中的字符串也不包含任何DLL名称或库函数名称。这表明样本配备了先进的API混淆技术以阻止静态分析。
由于静态分析不可行,我们将样本提交给Anubis恶意软件分析平台进行动态分析。结果显示恶意软件确实读取了一些注册表项和文件,但是,它们都不是恶意的。因此,我们将样本提供给我们的系统,希望揭示其真实意图。在探索30条路径后,X-Force实现全覆盖,并生成具有15个函数的图形。通过遍历图表,我们发现此恶意软件旨在窃取受害者计算机中IE和Firefox存储的密码。它枚举了存储IE加密自动完成密码的注册表项,并调用库函数(如CryptUnprotectData()
)来解密存储的密码。这与[1]中提到的攻击非常相似。关于Firefox,它首先从Firefox应用程序数据目录下的profiles.ini获取用户名,然后窃取存储在用户名目录下的signons * .txt中的密码。然后使用文件名[计算机名]将[[IP地址] .txt)密码上传到远程FTP服务器。显然,此示例使用一些混淆技术在运行时查找这些库函数的条目地址。 X-Force允许我们识别恶意行为,而无需花费不必要的时间对API混淆进行逆向工程。
此外,流程图还揭示了Anubis错过恶意行为的原因:恶意软件执行环境检查以确保目标在尝试攻击之前存在。例如,在恶意软件从IE窃取密码的功能中,它将尝试打开包含自动完成密码的注册表项;如果此类条目不存在或为空,则恶意软件将停止其操作并从该函数返回。此外,在尝试窃取Firefox存储的密码之前,它将首先尝试从注册表中查询Firefox的安装目录,以确保系统中存在目标程序。这些“先决条件”不太可能在自动分析系统中实现,因为它们是不可预测的。但是,通过强制执行不同的路径,X-Force能够通过这些检查来揭示恶意软件的真实意图。
6.讨论和未来工作
X-Force旨在成为分析未知(恶意)二进制文件的实用解决方案,无需任何源代码或输入。因此,X-Force牺牲稳定性和完整性以实现实用性;不稳定,因为它可以探索不可行的路径;不完整,因为它无法探索所有路径。图10显示了X-Force如何与静态和动态分析进行比较:Reachable Program State
椭圆表示通过可能的程序输入到达的所有状态 - 程序分析的理想覆盖范围。静态分析通常做出保守的近似,使得它们产生过大的覆盖范围。动态分析分析了许多实际执行情况,从而产生了不充分的结果。 X-Force探索了比动态分析更多的一系列行动。由于X-Force产生不合理的近似值,因此其结果可能无效(即,在理想椭圆之外)。此外,它不完整,因为其结果可能无法涵盖理想的结果。
然而,我们认为X-Force在实践中仍然具有重要意义:(1)有许多安全应用程序的分析结果对路径不敏感,例如本文中的三项研究。因此,路径不可能性可能不会对结果产生太大影响。然而,在路径探索中具有具体状态在这些应用中仍然至关重要,因此像X-Force这样的基于执行的方法是合适的; (2)只有很小比例的谓词在X-Force中被切换(第5.1节)。允许执行在大多数谓词中自然地进行,遵循路径可行性。根据我们的观察,大多数在线性搜索中切换的谓词是检查程序是否已提供所需参数,文件是否正确打开以及是否正确设置某些环境配置的谓词; (3)在X-Force中,污点分析用于识别受输入影响的谓词,只有这些预测符符合切换条件。
此外,X-Force允许用户(1)快速探索任何(未知)二进制的行为,因为它只是简单地执行二进制(不解决约束); (2)处理更广泛范围内的二进制文件(例如,大型,打包或混淆的二进制文件); (3)轻松移植或开发X-Force的动态分析,因为X-Force中的执行与常规符号执行没有区别。
未来工作:我们相信本文只是开发独特类型的程序分析的第一步,不同于传统的静态,动态和符号分析。我们未来的研究议程中有许多任务。
- 虽然X-Force只是强调一些谓词的分支结果而不考虑它们的可行性,但我们怀疑在实践中有可能在许多情况下强制路径确实可行。请注意,如果强制谓词不紧密相关,则不可行性的可能性不高。我们计划使用符号分析引擎来模拟沿强制路径的路径条件,以观察它们不可行的频率。
- 我们在此论文中开发了3种探索算法。根据SPECINT2000程序的评估数据,存在当前探索算法无法很好处理的情况(例如,perlbmk)。将开发更有效的算法,例如,基于建模功能行为和缓存先前的探索选择。
- 通过对其执行进行初始化,我们可以手动处理多线程程序。将来,我们将探索强制真正的并发执行。我们认为这必须与翻转计划决策相结合,这是一种探索并发执行状态的标准技术。如何处理扩大的状态空间和可能引入的不可行线程安排将是新的挑战。
- 当前系统是作为PIN之上的工具实现的。为了构建一个利用X-Force的工具,例如REWARDS,附加工具的实现目前与X-Force混合使用。它们一起编译为单个PIN工具。我们的目标是通过提供类似PIN的界面使X-Force对动态分析开发人员透明。理想情况下,现有的PIN工具可以轻松移植到X-Force,以便从X-Force引擎提供的大量执行中受益。
- 我们还计划将核心X-Force引擎移植到其他平台,如移动和HTML5平台。
7. 相关工作
研究人员提议在[51]中强制分支结果来修补软件故障。提出硬件支持以促进[31]中的路径强制。两者都需要源代码和具体的程序输入。分支结果被迫在[48]中探索二进制程序的路径以构建控制流程图。该技术不会对任何堆行为进行建模。此外,它会跳过所有库调用。提出了类似的技术来揭示Android应用程序中的隐藏行为[22,45]。这些技术随机地决定了每个分支的结果,提出了过度不可行路径的挑战。还提出强制执行来识别内核级rootkit [46]。它完全忽略了执行期间的分支结果,并执行简单的深度优先搜索。这些技术都不会执行异常恢复,而只是在出现异常时终止执行。在[33,6]中,使用约束求解来探索暴露恶意软件行为的执行路径。它们需要具体的输入开始,然后将这些输入放在一起以探索不同的路径。
X-Force与静态二进制分析[21,3,25,42,41],动态二进制分析[30,39,24]和符号二进制分析[10,40]有关。我们已经在第6节讨论了他们与X-Force的差异,这些差异也得到了我们在第5节中的实证结果的支持.X-Force也与失败的遗忘计算[36]和实时异常恢复[34]有关,它们用于容错和调试,需要源代码。