MBR感染病毒分析报告
基本信息
样本名称 | 1.exe11.vir |
---|---|
样本类型 | MBR感染型病毒 |
样本大小 | 236 KB (242,176 字节) |
MD5 | 8cfa512ba62399f135c03505a93533f3 |
简介
该样本为MBR感染型病毒,其通过修改原始MBR,从而实现在开机运行操作系统之前,执行修改后的MBR中的指令,其对系统进行了3次Hook,最终再开始时加载其写入的驱动文件来实施攻击。经过分析该样本只针对于XP操作系统进行攻击,攻击手段十分隐蔽,并且攻击十分底层,一般情况下很难发现该恶意样本的恶意源头。
流程图
简单行为分析
文件写入行为
运行样本后,该样本进行了3次文件写入行为,分别为:
1.往磁盘DR0中写入数据;
2.在样本目录中释放与样本同名的dll文件;
3.在临时目录中写入临时文件
00000218.tmp
运行样本后,该样本通过调用regsvr32.exe
,将释放出来的样本同名的dll文件注册为系统服务。执行的参数如下:
1 | regsvr32 /s "C:\Documents and Settings\Administrator\桌面\8cfa512ba62399f135c03505a93533f3_1.dll" |
分析样本文件本身
首先通过查壳工具,发现该样本文件是ASPack
壳,如图:
然后通过ESP定律将其脱去。脱壳中值得注意的是,该样本有2层ASPack
壳,脱壳时需要脱两次。
然后将其拖入IDA和OD中分析,该样本主要行为有:
- 往临时文件
C:\Windows\temp\00000218.tmp
中,写入执行日志。 - 将控制代码
IOCTL_VOLUME_LOGICAL_TO_PHYSICAL
发送到\\.\C:,来获取物理磁盘号和物理偏移量相对应的逻辑偏移量。 - 然后根据内存地址
MEMORY[0x7FFE0500]
中的值是否等于0xEE00A121,来判断当前操作系统是否已经被感染,若被感染执行相应操作。 - 创建互斥体==Global\7BC8413E-DEF5-4BF6-9530-9EAD7F45338B==。
- 修改MBR中的数据,并将原始MBR数据存储在62扇区中。另外还从当前文件中读取两个512字节的数据写入到60和61扇区中,还写入了一个PE文件。
- 重启后删除自身文件,拷贝自身文件到当前目录,并伪装成一个dll文件。然后再将该dll文件注册为系统服务。
- 强制重启计算机。从而执行感染后的MBR。
主函数代码如图:
写入执行日志
通过调用函数WriteFile_401B81
,将传进来的记录日志的字符串,写入”C:\Windows\temp\00000218.tmp”文件中。从而记录该样本执行过程的相关日志,函数WriteFile_401B81
代码如下:
获取逻辑偏移量
通过调用函数DeviceIoControl
,将控制代码IOCTL_VOLUME_LOGICAL_TO_PHYSICAL
发送到\\.\C:中,从而来获取物理磁盘号和物理偏移量相对应的==逻辑偏移量==。 其中函数DeviceIoControl_402BF9
代码如下:
判断是否被感染
根据内存地址MEMORY[0x7FFE0500]
中的值是否等于0xEE00A121,来进行判断当前操作系统是否已经被感染,若被感染执行相应操作。感染前改地址处的值为0,当被感染后改地址的值将被置为0xEE00A121,然后根据判断结果执行不同的操作,如图:
创建互斥体
通过调用函数CreateMutex_402B93
,来进行判断互斥体”Global\7BC8413E-DEF5-4BF6-9530-9EAD7F45338B”是否存在,若不存在则创建该互斥体,若存在直接退出当前进程。如图:
修改MBR往扇区中写入数据
通过调用函数AlterMBR_402407
,修改原始MBR中的数据,并将原始MBR数据存储在62扇区中。另外还从当前文件中读取两个512字节的数据写入到60和61扇区中。
读取原始MBR
先通过函数CreateFileA
,获取\.\physicalDrive0文件的句柄hDevice。
再通过调用函数DeviceIoControl
,来检索有关物理磁盘的信息,然后再从DR0中偏移00位置处读取原始MBR
到指定Buffer_init00MBR中。
写入数据
然后连续调用5次WriteFile
,分别写入:
- 往偏移
0xE0FFF00000
处,写入大小为0x3BC00大小的PE文件 - 往偏移
0x7800(60扇区)
处,写入大小为512字节的数据 - 往偏移
0x7A00(61扇区)
处,写入经过修改的大小为512字节的数据 - 往偏移
0x7C00(62扇区)
处,写入之前读取到的原始MBR数据 - 往偏移为
0x0(MBR)
处,写入修改后的MBR数据,从而实现感染MBR
拷贝自身文件并注册系统服务
- 通过调用函数
MoveFileExA
将自身文件拷贝到当前目录,并将文件后缀名重命名为.dll,然后设置参数为MOVEFILE_DELAY_UNTIL_REBOOT
,重启后删除自身文件。 - 通过调用函数
AlterCharacteristic_403422
修改复制后的.dll文件的characteristics
,来将该EXE文件的属性修改为DLL文件 - 通过函数
CreateService_40316F
调用regsvr32
,执行命令regsvr32 /s “C:\Users\\Desktop\dump.dll”,来将复制后的dll文件注册为系统服务
强制重新启动
先获取特权SeShutdownPrivilege
,再调用函数InitiateSystemShutdownA
,强制关闭应用程序,并重新启动本地计算机,从而执行感染后的MBR,代码如图:
分析修改后的MBR
首先修改虚拟机的VMX
文件,再通过强大的IDA来调试虚拟机中的系统MBR。
并在MBR载入内存的地方==7C00==处下断,然后运行到此处。由于MBR
是运行在==实模式==下的,所以这里还需要将IDA区段属性修改为16位汇编模式。然后就能看到==7C00==处解析出的16位汇编代码,如下图:
修改后的MBR主要实现了功能有:
- 申请2KB大小的内存空间,将修改后的
MBR数据
写入申请的内存空间中 - 读取60和61扇区中的数据到申请的内存空间中
- Hook
int 13
中断表中的跳转的地址,将要跳转的地址修改为申请的内存空间 - 恢复原始MBR,通过调用被Hook的
int 13
,将磁盘62扇区中的原始MBR数据读取到内存0x7C00,再跳转到0x7C00执行原本MBR指令。
将修改后的MBR写入申请的内存空间中
申请内存空间
偏移[0x413]
中记录了bios内存可用区域,通过将偏移[0X413]
的数值减2,来申请大小为2KB的内存空间,如图:
将修改后的MBR写入申请的内存空间中
首先通过寄存器ax
左移6位,计算出分配的内存的段起始地址。然后再将当前内存地址为[0x7C00]
也就是当前执行的修改后的MBR指令,拷贝到分配的段起始位置ES:00(9F000)
中,如图:
拷贝后内存地址0x9F000
上的数据,如图:
读取60和61扇中的数据到申请的内存空间中
通过调用int 13h
中断,跳转到地址ECBE2
执行指令,来读取磁盘==0磁道60扇区(7800)和61扇区(7A00)==大小为2个扇区的数据到ES:BX(9F00:200)(9F200)
,紧挨着上一步写入的修改后的MBR
数据后面。其中60和61扇区中的数据是之前分析过的样本==恶意写入磁盘中的两段数据==,汇编指 令如图:
int 13
中断处的跳转的地址为ECBE2
,跳到该地址查看汇编指令。由于此时的int 13
中断表跳转的地址是由系统加载的,并没有被修改,所要执行的的指令都是正常的,因此这里也就不需要跟下去分析了,ECBE2
汇编指令如图:
直接查看拷贝后的数据 ,查看申请的内存地址0x9EE00
中存储的之前样本恶意写入==60和61扇区==中的数据,拷贝后内存地址0x9EE00
上的数据如图:
此时内存空间中数据与样本写入磁盘的数据对应关系,如下表:
开辟的内存地址 | 数据来源 |
---|---|
9F000 | 修改后的MBR数据 |
9F200 | 样本写入磁盘60扇区中的数据 |
9F400 | 样本写入磁盘61扇区中的数据 |
Hook int 13中断表
Hookint 13
中断表,修改int 13
中断表中原本要跳转的地址ECBE2
修改为申请的内存空间地址9F066
,并将原本int 13中断要跳转的地址存储在ES:0x73(9F073)
中,后面恢复时会用到,从而实现Hook。
然后将9F00
和004D
压进栈,通过retf
指令跳转到内存地址9F04D
去执行。由于之前已经将修改后的MBR
写入到了申请的内存空间9F000
中,所以这里跳到9F04D
去执行,其实就等同于执行修改后MBR
数据+偏移0x4D
的处的指令。Hook int 13 中断表和跳转指令如图:
被Hook前后的 int 13
要跳转的地址,如图:
恢复原始MBR
在地址9F04D
中,通过调用被Hook的int 13
中断,将==0扇道62扇区(7C00)==中的原始MBR
的数据写入到内存地址7C00
处。写入完成后,再通过跳转到内存地址7C00
中,去执行原始的MBR,从而实现恢复执行原本的正常开机操作,指令如图:
将原始MBR拷贝到0x7C00,0x7C00处的指令前后对比如图:
分析被Hook的int 13中断
由于上一步恢复原始MBR时,会调用被Hook的int 13
中断来实现。当调用被Hook的int 13
中断时,会使其跳转到申请的内存地址9FC66
中去执行。接下来将主要分析要执行的Hook函数。
Hook int 13的hook函数进行的操作有:
- 根据判断调用int 13中断的不同方式,恢复原本int 13要执行的操作。
- 遍历匹配
ntldr
寻找特征序列码:==8B F0 85 F6 74 21 80 3D== - Hook 找到的特征码指令,将其指令修改为
call 9F1FC
,但实际call的地址是9F200
,也就是执行之前拷贝到内存空间的==第60扇区中的数据==,这样就又回到了病毒代码。
恢复原本int 13要执行的操作
在之前Hook int 13中断时,将原本int 13要跳转的地址存在了CS:0x73的偏移处(可看之前hook int 13有分析),
然后首先通过对比ah的值来判断调用int 13的方式,分为扩展int 13调用读方式和非扩展int 13调用读方式,当不是这两种时,便会跳转到原本int 13应该跳转的地址ECBE2
,执行原本的int 13应该执行的操作;若是这两种调用方式,便跳转到9F077
去执行,然而在9F07E处,又重新call 了原本int 13的地址,代码如图:
遍历匹配特征序列码
通过汇编指令repne scasb
来遍历ntldr
匹配特征序列:8B F0 85 F6 74 21 80 3D,来寻找要Hook的地方,指令如图:
匹配到的特征序列(要被Hook的指令)对应的反汇编,如图:
Hook 匹配到的特征码的指令
Hook 找到的特征码指令,将其指令mov si,ax
修改为call 9F1FC
,当执行这条指令时,便会跳转到地址9F200
中去执行,也就是执行之前拷贝到内存空间的==第60扇区中的数据==,这样就又回到了病毒的代码区域。
Hook前后的指令,对比如下图:
分析被Hook的ntldr
通过上一步通过匹配特征Hook了ntldr中的地址46C3C
中的指令,当执行到地址46C3C
时,便会执行指令call off_9F1FC(实际call到内存地址0x9F200上去执行)
,重新回到之前拷贝到申请的内存中的60扇区中的指令去执行,接下来将详细分析拷贝到申请内存中的60扇区中的指令。
写入60扇区中的指令主要操作有:
- 通过遍历匹配特征,定位到
BlLoaderBlock
结构,再根据层层结果遍历得到ntoskrnl.exe
的模块基址。 - 遍历模块
ntoskrnl.exe
,匹配特征6A 4B 6A 19 E8 和 E8 xx xx xx xx xx 84 C0,找到要Hook的地址,也就是找到函数IoInitSystem
。 - 将61扇区中大小为512字节的指令,拷贝至该
ntoskrnl.exe
模块的最后512字节的位置中,并记下拷贝后的起始地址。 - Hook之前匹配的执行函数
IoInitSystem
处的地址,将其指令修改为跳转到拷贝后的起始位置去执行。
获取ntoskrnl.exe模块基址
通过遍历匹配特征 ==C7 46 34 00 40==,定位到 BlLoaderBlock
结构,再根据BlLoaderBlock->LoadOrderListHead->nt!_LDR_DATA_TABLE_ENTRY->ntoskrnl.exe,遍历得到ntoskrnl.exe
的模块基址,指令如图:
匹配特征找到要Hook的地址
遍历获取到的ntoskrnl.exe
的模块,先根据特征值==6A 4B 6A 19 E8==再根据==E8 xx xx xx xx xx 84 C0==匹配到要Hook的地址0x8069A3CF
(执行函数IoInitSystem
的地址),并将执行函数IoInitSystem
的地址保存至0x9F400(61扇区)+0x4的偏移处,用来恢复被Hook的IoInitSystem
指令,然后将要Hook的地址压进栈中保存。指令如图:
匹配到特征的地址处的指令(Hook前的原始指令),如图:
拷贝61扇区中的数据至文件末尾
遍历文件ntoskrnl.exe
,找到该文件末尾512字节地址处,并将样本写入61扇区中的数据拷贝到该位置处,并记录下拷贝后的起始地址。
文件末尾512字节(0x806E5E00)拷贝前后对比图:
Hook 匹配到特征的地址
首先将之前通过匹配特征码找到的函数IoInitSystem
执行的地址,从栈中取出来,然后将执行函数IoInitSystem
的指令修改为执行拷贝61扇区到最后512字节的地址0x806E5E00处,此时hook后就会call到拷贝61扇区数据后的地址处去执行,就能执行回样本写入的指令处,实现Hook。
Hook前后的指令对比图:
分析被Hook的ntoskrnl
当操作系统启动时,便会执行ntoskrnl
中被Hook的函数,便会跳转到文件末尾的病毒代码空间中去执行。也就是病毒写入61扇区中的指令,其主要功能是实现加载并运行之前写入的驱动程序。
写入ntoskrnl
中的病毒代码(61扇区)主要实现了:
- 调用函数
ExAllocatePool
,开辟内存空间,然后将(61扇区中)剩下的未执行的指令拷贝至开辟的内存空间中,并跳转到内存空间中执行。 - 执行之前被Hook 的函数IoInitSystem,初始化操作系统。
- 将恶意样本之前写入的驱动文件加载至内存中,将其展开并运行该驱动程序。
开辟内存空间拷贝数据并执行
首先根据hash值3707E062h,调用函数SearchKernelFunctionAddress
,来获取函数ExAllocatePool
的地址。然后再执行函数ExAllocatePool
开辟大小为1ABh的内存空间,将(61扇区拷贝至ntoskrnl中的指令)剩下还未执行的指令拷贝到虚拟内存空间中执行。代码如图:
执行之前被Hook的函数IoInitSystem
通过将之前压进栈的函数IoInitSystem的地址取出,然后Call去调用之前被Hook的函数IoInitSystem
,来初始化操作系统。指令如图:
### 加载恶意驱动文件并执行
首先获取一些内核函数,如ntOpenFile,ntReadFile,ntClose,ExAllocatepool。然后再将之前样本写入磁盘的驱动程序加载到内存中,将其展开并跳转到OEP处去运行,指令如下图:
读取到样本写入的驱动文件:
将其再内存中展开:
跳转到驱动程序的OEP处执行:
恶意驱动程序有待进一步分析
由于驱动相关方面的知识还很匮乏,这里就先不继续进行分析了。以后有相关知识的累积再进一步进行分析。
样本溯源
经过分析可知,该样本属于Bootkit类型病毒, Bootkit是更高级的Rootkit。经过查找相关指令应该是早些年爆发的==鬼影病毒==,利用感染MBR,加载恶意驱动程序来实施攻击。
相关文件信息
FileName | FileSize | MD5 |
---|---|---|
1.exe11.vir | 236 KB (242,176 字节) | 8CFA512BA62399F135C03505A93533F3 |
1.exe11.dll | 236 KB (242,176 字节) | 8CFA512BA62399F135C03505A93533F3 |
恶意驱动文件 | 239 KB (244,736 字节) | D00123F9C4C3226B8800A623C9999D70 |
00000218.tmp | 根据系统环境不定 | 根据系统环境不定 |
查杀方案
恢复原始MBR
由于该样本修改了原始的MBR,执行修改后的MBR来实施攻击,只需要将原始的MBR还原就可以阻止该攻击了,具体操作为:
- 删除修改后的MBR中的数据,将存储在62扇区中的原本的MBR数据还原回MBR中,就可以达到恢复的效果了。
删除注册的服务
由于该样本将自身修改为一个dll文件,并注册为了系统服务。在恢复时还需要将该服务进行删除,执行如下指令即可:
1 | regsvr32 /u "<SamplePath>.dll" |
删除恶意写入的数据
恢复过MBR后,还需要将恶意样本写入到操作系统中的数据删除,具体操作为:
- 删除60,61,62扇区中的数据
- 删除写入磁盘中的驱动文件
- 删除释放在样本当前目录下的dll文件
- 删除释放在临时目录下的00000218.tmp文件
总结
该恶意样本通过感染MBR实现早于系统执行自己的恶意代码来将写入磁盘中的驱动文件加载起来运行。整个样本以非文件形式存在,直接写入磁盘扇区。并且是对MBR进行的感染,在很大程度上能隐蔽自身,并在一定程度上逃避杀软的检测。因此建议在平时使用计算机时,不要随意下载不正常文件,不接受不明来源的文件,以避免感染病毒。