分析重装系统也无法清除的鬼影病毒

声明:本文由【MS509 Team】成员expsky原创,仅用于技术交流分享,禁止将相关技术应用到不当途径。

整理电脑的时候找到自已以前分析的一个鬼影病毒的资料,当时兼容市面上主要的windows系统(XP, win7,包含x86和x64系统)样本来自国外,有不少亮点,当时花了不少时间把所有原理分析出来并重新用汇编和C++实现了出来。

以前的一些资料简单整理了下,分享出来,也给自己以前的工作留个记录。

该木马样本通过感染MBR达到早于系统得到执行。整个样本以非文件形式存在,直接写人磁盘扇区。包括感染的MBR以及保存在磁盘末尾非文件系统的payload。

木马执行流程从MBR开始,通过hook和注册回调函数的机制,将所有的木马程序在windows启动过程中逐级得到执行, windows启动完毕后最终将下载者dll注入到指定的系统进程中,下载者代码循环执行而告终。

木马亮点有:

绕过windows的PatchGuard保护 兼容XP, win7(x86,x64)(bootkit型底层木马兼容不同系统是挺有挑战的) 自保护功能:感染木马后通过WinHex等工具查看到的MBR是正常的,而且无法修改被感染的MBR(不是简单的修改失败,而是会呈现MBR被修改成功的假象) 从内核态向用户态的指定系统进程注入下载者DLL(根据系统是32位还是64位,会注入相应的32位和64位版本的DLL) 启动过程中关闭指定进程

木马工作原理

1)木马植入程序运行后感染MBR和磁盘末尾未分区部分 (无文件,直接写入磁盘扇区)

感染的数据主要包括6个部分:

感染的MBR;

启动部分代码;

x86驱动代码;x64驱动代码;

x86下载者DLL;x64下载者DLL

(前两部分未压缩,其他4部分进行了aPLib压缩)

2)重启电脑后感染的MBR接管执行:

a) 加载末尾20个扇区的前18个扇区(未压缩数据)到常规内存并执行 b) 加载第19个扇区(原始MBR)到0x7C00 c) 加载第20个扇区(配置信息:驱动、下载者代码的大小、payload所在扇区位置数据)到常规内存 d) 调用Int15扫描常规内存,保存ARD结构体用于后续常规内存的分页扫描(查找ntoskrnl.exe映像) e) hook IVT表中int13向量(用于监控后续磁盘读操作) f) 跳转到0x7C00控制权转交原始MBR,系统继续启动

ARD: AddressRange Descriptor Structure+0:BaseAddrLow:基地址的低32位+4:BaseAddrHigh:基地址的高32位+8:LengthLow:长度(字节)的低32位+12:LengthHigh:长度(字节)的高32位+16:Type:这个地址范围的地址类型(1:AddressRangeMemory;2:?AddressRangeReserved;Other:Undefined)

3)Int13钩子监视系统启动过程的读磁盘操作,当读取到kdcom.dll时接管执行(绕过PatchGuard的时机)

a)int13钩子根据读取文件的前0×200字节的校验值和PE特征码识别kdcom.dll,根据kdcom.dll PE的mechine字段判断系统为32位还是64位(32位和64位部分hook函数不同,整体流程一致) b)根据前面保存的ARD结构体扫描常规内存,找到ntoskrnl.exe基地址(根据文件hash值),再根据ntoskrnl PE导出表hook函数IoCreateDriver(根据函数名hash值,64位系统hook MmMapIoSapce) c)控制权继续转交系统

4)IoCreateDriver钩子函数接管执行

a)先恢复IoCreateDriver钩子 b)同前机制,根据ntoskrnl.exe基地址和PE导出表找到函数PsSetLoadImageNotifyRoutine c)调用PsSetLoadImageNotifyRoutine,注册回调函数 d)控制权继续转交系统

5)系统加载模块时,上步PsSetLoadImageNotifyRoutine注册的回调函数接管执行

a)如前机制,得到NtReadFile,NtClose,NtOpenFile,ExAllocatePool函数指针 b)申请8K内存,再打开Device\Harddisk0\Patition0,将驱动部分压缩数据读入其中 c)申请0×3600字节内存,解压驱动数据到其中 d)释放8K压缩数据内存,转入驱动代码部分执行

6)驱动部分压缩数据解压后的执行

a)根据实际加载的内存地址,对驱动部分的常量进行重定位 b)InstallProcessNotifyCallback注册回调函数,回调函数监控当创建的进程如包含在屏蔽进程列表中时,直接return c)PsSetLoadImageNotifyRoutine注册回调函数,当创建进程包含在注入进程列表中时注入下载者DLL d)Hook NtReadFile, NtWriteFile, 当尝试读写感染的MBR或磁盘末尾未分区处,都返回正常值,隐藏被感染迹象。同样写入数据到此部分也会做相应保护,不会真正写入,但会呈现已被写入的假象

内核向用户态进程注入代码原理

通过KeInitializeApc、KeInsertQueueApc(未文档化函数)插入内核APC回调函数、ExQueueWorkItem插入WorkItem回调函数,附加用户进程,将注入代码复制到用户进程内存空间,最后通过一个用户APC指向注入代码,并异步得到执行。

详细流程如下:

1)PsSetLoadImageNotifyRoutine注册回调函数

2)任意进程加载模块时,上步注册的回调函数得到执行

调用FsRtlIsNameInExpression,判断加载的是kernel32.dll,是则继续执行,否则返回。(The FsRtlIsNameInExpressionroutine determines whether a Unicode string matches the specified pattern.)

FsRtlAllocatePool申请0×34字节内核非分页内存,用于后续APC对象及回调参数。

将kernel32.dll基址存入0×34字节的最后一个DWORD中(用于APC回调函数入参)

调用KeGetCurrentThread得到当前线程对象(用于APC初始化的入参)

调用KeInitializeApc、KeInsertQueueApc(未文档化函数)插入内核APC回调函数(执行后续流程)

3)上步插入的内核APC函数异步执行

调用ExFreePool释放APC结构体内存

FsRtlAllocatePool申请0×40字节非分页内核内存(用于后续EVENT对象、WORKITEM对象及其参数)

调用KeGetCurrentThread、PsGetCurrentProcess、PsGetCurrentThreadProcessId得到进程对象,线程对象,PID(存入上面申请的内存中,作为参数传给ExQueueWorkItem插入的回调函数)

调用KeInitializeEvent,用于同步,等待WorkItem执行完毕

调用ExQueueWorkItem插入后续流程的回调函数

调用KeWaitForMutexObject等待上面的WorkItem回调函数执行完毕

调用ExFreePool释放上面申请的0×40字节内核内存

4)上步ExQueueWorkItem插入的回调函数执行

调用PsLookupProcessByProcessId得到EPROCESS

调用PsGetProcessImageFileName通过EPROCESS得到进程文件名

计算进程文件名的hash值与想要注入代码的进程列表做对比(包含在内继续执行,否则返回)

调用KeStackAttachProcess将当前线程附加到目标进程用户态地址空间

配置入参数ClientId, ObjectAttributes后,调用ZwOpenProcess打开目标进程

调用ZwAllocateVirtualMemory申请目标进程用户态内存

mov指令将内核中的注入代码拷贝到上步申请的用户态进程空间

调用FsRtlAllocatePool申请0×30字节内核非分页内存用于APC对象

调用KeInitializeApc,KeInsertQueueApc插入用户APC(注入代码在用户态进程执行)

调用ZwClose,KeUnstackDetachProcess,ObDereferenceObject释放资源

末尾调用KeSetEvent设置事件,用于同步,通知WorkItem执行完毕

5)上步插入的用户APC异步得到执行(注入的代码在目标进程执行)

至此,完成了内核态向目标用户进程注入代码并得到执行

自保护原理

木马自保护功能:当木马正常工作后,用WinHex等磁盘查看工具是看不到MBR及磁盘末尾被感染的迹象的,并且用工具对MBR或磁盘末尾进行修改后,呈现被改动的假象,但实际并未修改成功。木马代码依然存在在磁盘相应位置。

通过hook NtReadFile和NtWriteFile两个函数实现的自保护功能

当写数据到MBR或磁盘末尾处时,写入的数据存入内存而不真正写入磁盘,当读取磁盘MBR或末尾时,不真实读取,而用之前暂存的数据代替,呈现数据被写入的假象,实现自我保护

内存0×20000(128k)

0×0

磁盘末尾最后一个扇区数据(配置信息)

0×200

原始MBR

0×400

保存被修改过的MBR(假MBR)

0×600

感染后的MBR

0×800

磁盘末尾0xFC00字节(126个扇区,63K)

0×10400 0×20000

保存被修改后的磁盘末尾0xFC00字节数据(126个扇区,63K),假的磁盘末尾数据

(1)NtWriteFile钩子函数

a)确认写入的数据是否在MBR处或磁盘末尾0xFC00处 b)当向MBR写入数据时:将写入的数据保存到上表内存中0×400处(假MBR),以及更新0×200(原始MBR)和0×600(染后后MBR)的末尾0x4C的数据信息(只更新MBR中分区表等数据信息,而不改变MBR中的代码);修改磁盘MBR处后0x4C的分区表等数据,修改磁盘倒数第2个扇区(备份的原始MBR)的后0x4C数据部分 c)当向磁盘末尾0xFC00写入数据时:计算写入数据与末尾0xFC00的交叉情况,保存写入的数据到0×10400处(假的磁盘末尾数据)

(2)NtReadFile钩子函数

a)确认读取的数据是否在MBR处或磁盘末尾0xFC00处 b)当读取MBR时,用上表中0×400处的假MBR代替 c)当读取磁盘末尾时,用上表中0×10400处假数据代

结语

这是几年前逆向分析的基于MBR的bootkit型样本(来自国外),运行相当稳定(也尝试过改造来从BIOS启动,但主板芯片的差异,以及植入BIOS的程序兼容性很难保证,最终稳定性不好)该样本当时未在国内传播,逆向老外的样本过程中发现各种细节处理的非常到位,师夷长技以自强 。^_^

*本文原创作者:expsky,本文属FreeBuf原创奖励计划,未经许可禁止转载

原文发布于微信公众号 - FreeBuf(freebuf)

原文发表时间:2016-07-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员互动联盟

【高级编程】linux进程间通信总结

1. 概览 本文记录经典的IPC:pipes, FIFOs, message queues, semaphores, and shared memory。 2....

57070
来自专栏安恒信息

干货分享 | GoAhead服务器 远程命令执行漏洞(CVE-2017-17562) 分析报告

安全通告 1 GoAhead Web Server是为嵌入式实时操作系统(RTOS)量身定制的开源Web服务器。很多国际一线大厂商,包括IBM、HP、Oracl...

370120
来自专栏我的博客

xhprof使用说明

if (mt_rand(1, 10000) == 1) { //采集请求的万分之一 //xhprof_enable(XHPROF_FLAGS_MEMOR...

39260
来自专栏Java架构师学习

Java程序员必知的并发编程艺术——并发机制的底层原理实现

Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。 volatile借助Java内存模型...

391110
来自专栏张善友的专栏

Microsoft Avro介绍

Microsoft发布了他们自己对Apache Avro通信协议的实现。Avro被描述为“紧凑的二进制数据序列化格式,类似于Thrift或者Protocol B...

199100
来自专栏不止是前端

Node下RabbitMQ的使用

509190
来自专栏信安之路

一个病毒样本分析的全过程

SHA1: 3f738735bb0c5c95792c21d618eca8c0d5624717

21900
来自专栏祝威廉

ElasticSearch Rest/RPC 接口解析

早先ES的HTTP协议支持还是依赖Jetty的,现在不管是Rest还是RPC都是直接基于Netty了。

20440
来自专栏博岩Java大讲堂

Java日志体系(log4j)

889110
来自专栏企鹅号快讯

GoAhead服务器 远程命令执行漏洞 分析报告

安全通告 1 GoAhead Web Server是为嵌入式实时操作系统(RTOS)量身定制的开源Web服务器。很多国际一线大厂商,包括IBM、HP、Oracl...

271100

扫码关注云+社区

领取腾讯云代金券