木马核心技术剖析读书笔记之木马反分析技术

常见的恶意软分析技术主要包含静态分析和动态分析两大类

反调试

木马程序使用反调试技术,可以阻止调试器对木马程序的加载;阻止调试器对木马程序的执行控制;阻止对木马程序设置断点;阻止调试器读取被调试进程的内存信息,进而延缓整个分析过程

Windows 系统实现了专门的调试器接口,而大多数调试器都是基于这些调试接口实现的

调试器工作机制

反病毒分析过程中,由于不可能获取到被分析软件的源码,通常使用的都是汇编级调试器

  • 源码级调试器针对各种高级语言,如 Chrome 附带的 JavaScript 源码调试器、专门针对 Java 语言的调试器 JDB、Visaul Studio 开发环境自带的调试器、Windbg、GDB 和 LLDB 等
  • 汇编级调试器主要针对机器语言,如 x86 平台机器指令、ARM 机器指令等,这类调试器比较有名的有 Ollydbg、Windbg、x64dbg、GDB 和 LLDB 等
  • 有些调试器既支持源码级调试,也支持汇编级调试,如 Windgb、GDB、LLDB

使用 Windows 系统调试接口的调试器中,首先创建或者附加被调试程序的进程;然后启动一个调试循环,该循环不断接收各种调试事件,并根据事件的类型读取被调试进程相应的信息进行处理,最后在用户的主动干预下恢复被调试进程

木马核心技术剖析读书笔记之木马反分析技术

通常,调试器主要有软断点、硬件断点和内存断点三种类型的断点

  • 软断点是单字节的机器指令,它在暂停程序执行的同时,将程序的执行控制转交给调试器进程的断点事件处理过程。软断点通过替换设置断点处的1字节内容为O×CC实现,O×CC代表了INT 3中断指令,它通知CPU暂停执行,而引发一个INT 3事件。当调试器捕捉到这个事件并进行处理后,会恢复被设置断点处的1字节内容
  • 硬件断点是在CPU级别实现的,没有修改内存内容,而是使用CPU的调试寄存器DRO~DR7。CPU通常有8个调试寄存器,DRO~DR3被用来保存设置断点的地址,DR4~DR5被保留,DR6保存断点命中后触发的事件类型,DR7决定了硬件断点是否启用,还指明了断点是在内存读、写还是执行的时候触发。由于只能有4个调试器设置断点的地址,故硬件断点同时只能设置4个
  • 内存断点是通过修改的内存区域的访问权限,进而引发异常,使得将控制执行交给调试器的一种断点方式。该类型的断点也同样包括内存读断点、内存写断点和内存执行断点。在汇编级调试器中,单步执行(step by step)就是让应用程序每次执行一条汇编指令。一般是通过设置CPU的单步执行标志。具体的是设置CPU标志寄存器的TF(Trap Flag,陷阱标志位),可以让CPU每执行完一条指令便产生一个调试中断(HVT 1),而调试器可以及时获取到这个中断事件

反调试

通用反调试技术

Windows 系统本身支持检查调试器,提供了 IsDebuggerPresent、CheckRemote、DebuggerPresent 和 NtQueryInformationProcess 等一系列 API

当一个进程被调试器调试之后,在完成某些操作或者发生一些异常的时候,该进程会发送通知给调试器,该通知被称为调试事件,然后该进程将自身挂起,并等待调试器的命令,调试事件包括创建进程、创建线程、加载DLL模块、卸载DLL模块、给调试器发送字符串、发生异常等。调试器通过WaitForDebugEvent函数来获取调试事件,通过ContinueDebugEvent函数来继续被调试进程的执行。DEBUG_EVENT 结构体用来描述调试事件的内容,在木马程序中可以使用 NtSetInformationThread 函数防止调试事件被发往调试器,达到反调试的目的

特定反调试技术

在 Windows 系统中,判断是否存在指定标题名或类名的窗口,主要是使用 FindWindow 函数。木马在实际应用中,一般是将常见调试器的窗口标题和类名全部收集起来,逐一查找对应的窗口是否存在

通过调试器来调试目标程序有两种方式:通过调试器启动程序(open)和调试器附加(attach)到正在运行的目标程序进程。前一种方式,父进程为调试器进程,子进程为目标程序进程;而后一种方式,父进程是启动目标程序进程的进程,而不是调试器进程

OutputDebugString 函数用于向调试器发送一个格式化的字符串,当程序被调试器调试的时候,该字符串会在调试器底部显示,但如果程序没有被调试器调试,OutputDebugString 函数不会影响程序的正常运行。对于 OllyDbg 调试器,在使用 OutputDebugString 函数时存在严重的格式化字符串漏洞。程序被 OllyDbg 调试时向调试器发送一个错误的格式化字符串会造成 OllyDbg 的崩溃

反反汇编

常用反汇编工具主要使用线性和递归下降两种反汇编算法:

  • 线性反汇编算法是从程序中的代码段起始位置开始,以线性方式扫描整个代码块,逐条转换每条指令,直到完成整个代码段。一条指令的结束,就是另一条指令的开始。这种算法并不会通过识别分支等非线性指令来解读程序的控制流。使用线性反汇编算法的工具有 GDB、Windbg 等
  • 递归下降返回算法是根据程序的控制和执行流,即分支、函数调用和返回指定等,确定需要反汇编的指令,能够清晰地区分数据和代码。使用递归下降算法的工具有 IDA、OllyDbg 和 PEBrowse Professional Interactive 等

木马进行反反汇编的技术思路有以下两类:

  • 针对反汇编工具采用的算法缺陷进行攻击,此类方法常见于一些软件保护壳、多态变形引擎中
  • 阻止反汇编工具对被分析程序的导入函数和函数之间的交叉调用分析

相同目标地址的跳转指令

两条紧邻的条件跳转指令跳转到同一目标地址

通过函数指针反汇编

木马经常会使用动态 API 函数调用,动态调用获取该函数地址时,将 DLL 导出表的函数名称计算为哈希值,再与要调用的函数名称的哈希值一一对比,从而找到该函数的地址

反虚拟机

针对虚拟机的检测手段可分为以下四类:

  • 搜索虚拟环境中的进程、文件系统、注册表
  • 搜索虚拟环境中的内存
  • 搜索虚拟环境中特定虚拟硬件
  • 搜索虚拟环境中的特定处理器指令和功能

通过执行特权指令检测虚拟机

VMware 为真实主机与虚拟机之间提供了相互沟通的通信机制,使用 IN 指令来读取特定端口的数据以进行两机通信,但由于 IN 指令属于特权指令,在处于保护模式下的真实主机上执行此指令时,除非权限允许,否则会触发异常,而在虚拟机中并不会发生异常

利用 IDT 基址检测虚拟机

中断描述符表(IDT)用于查找处理中断时所用的函数,由256项组成的数据表,其中每一项对应一种类型的中断处理函数

通过 SIDT 指令读取 IDTR,由于系统只存在一个 IDTR,而存在两个操作系统,为了防止发生冲突,VMM 必须更改熏鸡中的 IDT 地址

利用 LDT 和 GDT 检测虚拟机

在保护模式下,所有内存访问都需要通过全局描述表 GDT 或者局部描述表 LDT 才能进行。这两张表包含了段描述符的调用入口,若想访问段中某一字节,必须同时提供一个段选择器和一个偏移量。位于 GDT 和 LDT 中的段选择器为段提供了可访问的段描述符地址

与 IDT 相同,虚拟机必须使其中的 GDT 和 LDT 与真实主机存在差异

基于 STR 检测虚拟机

STR 指令是将任务寄存器(TR)中的段选择器存储到目标操作数,而目标操作数可以是通用寄存器或内存位置。使用 STR 指令可以将当前正在运行的任务状态段(TSS)存储在目标操作数中。而虚拟机和真实主机,通过 STR 读取的任务状态段地址是不同的,地址等于 ox0040xxxx 时,环境处于虚拟机中,否则为真机

基于注册表检测虚拟机

Windows 虚拟机中常常安装有 VMware Tools 和其他虚拟硬件,它们都会创建任何程序都可以读取的 Windows 注册表项

基于进程检测虚拟机

虚拟机环境中有多个虚拟机的进程,如 VMware 的 VMwareService.exe、VMwareTray.exe 等,安装了 VMware Tools 后,也会启动一些有明显特征的进程

基于虚拟硬件指纹检测虚拟机

VMware 默认网卡的 MAC 地址前缀为 00-05-69 或 00-0C-29 或 00-50-56

反沙盒

沙盒原理

沙盒更为详细的主要功能有:

  • 分析不同类型的病毒文件
  • 跟踪 API 调用和文件的行为
  • 分析程序的网络传输
  • 针对被感染的沙盒客户机进行高级的内存分析

常见的用于病毒分析的沙盒有 Cuckoo、Anubis、Thread Expert、Sandboxie 和 CWSSandbox 等

沙盒检测

计算机名检测

常见的沙盒计算机名称包括 USER、ANDY、COMPUTERNAME、CUCKOO、SANDBOX、NMSDBOX、XXXX-OS、CWSX、WILBERT-SC、XPAMASTC 等,使用 GetComputerName 函数读取计算机名称进行对比

命名管道检测

沙盒通常会在虚拟环境中黄金一个全局的命名管道,以 Cuckoo 为例:\\.\pipe\cuckoo

CPU 核数目检测

目前计算机以及很少有单核 CPU 存在,而沙盒使用的虚拟环境往往 CPU 数目都为 1

模块检测

沙盒一般会在被运行程序的进程中加载自身的模块,这些模块的名称一般情况下是固定的

木马核心技术剖析读书笔记之木马反分析技术

端口检测

有些沙盒为了与 HOST 通信,开放了一些固定的端口

反内存扫描

Anti-Rootkit 的内存扫描,是通过将组件对应的文件加载进内存,与当前系统加载的组件镜像进行扫描对比,识别系统加载组件镜像的内存是否被修改。以Ntoskrnl.exe
为例,具体过程如下:
1)读取Ntosktxrl.exe文件的PE格式,获取该镜像所需要内存的大小,并分配足够大小的非分页内存
2)将Ntoskml.exe文件的按照PE格式填充到分配的内存中
3)根据当前系统加载Ntoskrnl.exe镜像基址,对步骤2)中映射的自加载镜像进行重定位,这一步保证了自加载镜像的内存和系统加载的镜像内存内容一致
4)修复自加载Ntoskrnl.exe镜像导入表
5)使用MDL以页为单位,读取系统加载的Ntoskrnl.exe镜像,与自加载的镜像进行比对,如果有不相同内容,则认为该内存地址被修改。

通过上述分析可以看出,在 Anti-Rootkit 扫描内存时的一个重要环节,是使用MDL读取系统加载的镜像内存。使用MDL访问内存的时候,通常是使用Windows系统提供的一组封装函数进行操作。如果对这组函数使用进行Hook,当发现是Anti-Rootkit工具试图读取被木马修改的内核内存时,则返回内存被换页或无效的结果,导致Anti-Rootkit工具无法进行对该内存地址的扫描比对,从而达到隐藏Hook和防止内存扫描的目的