从Blue Pill、硬件虚拟化谈安全防护完备性上的一个小原则
这篇博客说说一个安全防护系统完备性上所需的一个小原则:防护系统与被防护对象所用资源的完全隔离,包括防护系统自身工作所依赖的资源不能依赖被防护对象。听起来是原理很简单的一个点,实现中往往很复杂。例如操作系统隔离应用程序、虚拟机监视器隔离客户机,需要考虑方方面面、还需要处理器/芯片组硬件的大量支持。分析、设计系统时遵循许多这类小原则是必要的。
记得好像是06年的时候吧,Joanna团队在Blackhat上推出了一个基于硬件虚拟化的rootkit样本,取名Blue Pill,07年又出了改进的新版New Blue Pill。这个样本并没有任何实质的恶意行为,仅仅是一个最有名气的利用硬件虚拟化支持的原型系统。这回的话题就拿它来做例子说说,从相反的方向介绍这个原则的必要性。
New Blue Pill作为一个驱动程序加载后,沉淀下去,利用硬件虚拟化支持将原本的操作系统置入客户机中运行,其自身则以虚拟机监视器的形态存在。它的目的是展示一个利用硬件虚拟化技术的rootkit,和一些利用了硬件虚拟化支持的程序(如某些调试器)一样,它是不完备的,我们可以较为简单的检测它、脱离它的控制。阅读过new blue pill源代码的同学们很容易回忆起来,这个虚拟机监视器工作所需的不少资源都没有有效保护、客户机软件可随意访问修改,其中一个很重要的就是物理内存资源。BluePill的内存保护仅仅是虚拟内存隐藏,原理见Joanna所绘图示(图1)。其虚拟机监视器有自己的私有页表,同时将客户机操作系统所用页表中对自己的映射处理掉,因此客户机中的软件想直接访问虚拟机监视器的虚存页面自然不行。
这样的虚拟内存保护有多大作用呢,因为客户机和虚拟机监视器的物理内存资源并没有有效隔离,不符合原则,所以效果只能说是聊胜于无。客户机内的软件可以轻松访问虚拟机监视器的物理内存,篡改虚拟机监视器代码数据乃至完全突破使客户机原操作系统重回Host环境(如VMX Root)运行。下面的内容阅读前需要预先熟悉一些Intel64体系结构、Windows内核上的一些知识。
先设计一个非常简单的物理内存访问库(工作于Win7 X64系统),原理为修改事先分配的NonPagedPool页所对应的PTE,使该线性地址可以用来依次映射我们指定的物理内存页面。注意x64系统如此分配一般获得一个在大页面中的线性地址,所以需要重新映射一下,以便得到可供修改的4K页对应的PTE;另外要注意的是这个映射方案是演示用的、很粗糙的,不应随意用该PTE去映射已被以NonCached等类型映射的物理空间,如外设的IO映射地址空间。
001
|
typedef struct _MAP_STRUCT {
|
002
|
PVOID OrigPage;
|
003
|
PVOID MapPage;
|
004
|
PMDL Mdl;
|
005
|
PHYSICAL_ADDRESS MapPagePhys;
|
006
|
} MAP_STRUCT, *PMAP_STRUCT;
|
007
|
008
|
#define VIRTUAL_ADDRESS_BITS 48
|
009
|
#define VIRTUAL_ADDRESS_MASK ((((ULONG_PTR)1) << VIRTUAL_ADDRESS_BITS) - 1)
|
010
|
011
|
#define PTE_BASE 0xFFFFF68000000000UI64
|
012
|
013
|
#define PTI_SHIFT 12
|
014
|
#define PDI_SHIFT 21
|
015
|
#define PPI_SHIFT 30
|
016
|
#define PXI_SHIFT 39
|
017
|
018
|
#define PTE_SHIFT 3
|
019
|
020
|
#define _HARDWARE_PTE_WORKING_SET_BITS 11
|
021
|
022
|
typedef struct _MMPTE {
|
023
|
ULONGLONG Valid : 1;
|
024
|
ULONGLONG Writable : 1; // changed for MP version
|
025
|
ULONGLONG Owner : 1;
|
026
|
ULONGLONG WriteThrough : 1;
|
027
|
ULONGLONG CacheDisable : 1;
|
028
|
ULONGLONG Accessed : 1;
|
029
|
ULONGLONG Dirty : 1;
|
030
|
ULONGLONG LargePage : 1;
|
031
|
ULONGLONG Global : 1;
|
032
|
ULONGLONG CopyOnWrite : 1; // software field
|
033
|
ULONGLONG Prototype : 1; // software field
|
034
|
ULONGLONG Write : 1; // software field - MP change
|
035
|
ULONGLONG PageFrameNumber : 28;
|
036
|
ULONG64 reserved1 : 24 - (_HARDWARE_PTE_WORKING_SET_BITS+1);
|
037
|
ULONGLONG SoftwareWsIndex : _HARDWARE_PTE_WORKING_SET_BITS;
|
038
|
ULONG64 NoExecute : 1;
|
039
|
} MMPTE, *PMMPTE;
|
040
|
041
|
#define MiGetPteAddress(va) \
|
042
|
((PMMPTE)((((( ULONG_PTR )(va) & VIRTUAL_ADDRESS_MASK) >> PTI_SHIFT) << PTE_SHIFT) + PTE_BASE))
|
043
|
044
|
NTSTATUS
|
045
|
InitMapPage(
|
046
|
OUT PMAP_STRUCT MapHandle
|
047
|
)
|
048
|
{
|
049
|
NTSTATUS Status = STATUS_SUCCESS;
|
050
|
PMMPTE pte;
|
051
|
052
|
RtlZeroMemory(MapHandle, sizeof (*MapHandle));
|
053
|
054
|
try {
|
055
|
056
|
MapHandle->OrigPage = ExAllocatePool(NonPagedPool,
|
057
|
PAGE_SIZE);
|
058
|
059
|
if (MapHandle->OrigPage == NULL)
|
060
|
{
|
061
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
062
|
leave;
|
063
|
}
|
064
|
065
|
MapHandle->Mdl = IoAllocateMdl(MapHandle->OrigPage,
|
066
|
PAGE_SIZE,
|
067
|
FALSE,
|
068
|
FALSE,
|
069
|
NULL);
|
070
|
071
|
if (MapHandle->Mdl == NULL)
|
072
|
{
|
073
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
074
|
leave;
|
075
|
}
|
076
|
077
|
//
|
078
|
// Remap
|
079
|
//
|
080
|
081
|
MapHandle->MapPage = MmMapLockedPagesSpecifyCache(MapHandle->Mdl,
|
082
|
KernelMode,
|
083
|
MmCached,
|
084
|
NULL,
|
085
|
FALSE,
|
086
|
HighPagePriority);
|
087
|
088
|
if (MapHandle->MapPage == NULL)
|
089
|
{
|
090
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
091
|
leave;
|
092
|
}
|
093
|
094
|
pte = MiGetPteAddress(MapHandle->MapPage);
|
095
|
096
|
MapHandle->MapPagePhys.QuadPart = *( PULONGLONG )pte;
|
097
|
098
|
} finally {
|
099
|
100
|
if (!NT_SUCCESS(Status))
|
101
|
{
|
102
|
if (MapHandle->Mdl != NULL)
|
103
|
{
|
104
|
IoFreeMdl(MapHandle->Mdl);
|
105
|
}
|
106
|
107
|
if (MapHandle->OrigPage != NULL)
|
108
|
{
|
109
|
ExFreePool(MapHandle->OrigPage);
|
110
|
}
|
111
|
}
|
112
|
}
|
113
|
114
|
return Status;
|
115
|
}
|
116
|
117
|
PVOID
|
118
|
MapSpecifiedPage(
|
119
|
IN PMAP_STRUCT MapHandle,
|
120
|
IN PHYSICAL_ADDRESS PhysicalAddress
|
121
|
)
|
122
|
{
|
123
|
PMMPTE pte = MiGetPteAddress(MapHandle->MapPage);
|
124
|
125
|
pte->PageFrameNumber = PhysicalAddress.QuadPart >> 12;
|
126
|
127
|
_ReadWriteBarrier();
|
128
|
129
|
__invlpg(MapHandle->MapPage);
|
130
|
131
|
return MapHandle->MapPage;
|
132
|
}
|
133
|
134
|
VOID
|
135
|
FiniMapPage(
|
136
|
IN PMAP_STRUCT MapHandle
|
137
|
)
|
138
|
{
|
139
|
PMMPTE pte = MiGetPteAddress(MapHandle->MapPage);
|
140
|
141
|
pte->PageFrameNumber = MapHandle->MapPagePhys.QuadPart >> 12;
|
142
|
143
|
MmUnmapLockedPages(MapHandle->MapPage, MapHandle->Mdl);
|
144
|
145
|
IoFreeMdl(MapHandle->Mdl);
|
146
|
147
|
ExFreePool(MapHandle->OrigPage);
|
148
|
}
|
然后下面的程序片段基于这个访问库,搜索当前Intel Core i3 CPU所关联的VMCS,顺便打印了其中的一些由New Blue Pill事先填充的数据:
01
|
{
|
02
|
……
|
03
|
04
|
PhysicalMemoryBlock = MmGetPhysicalMemoryRanges();
|
05
|
06
|
if (PhysicalMemoryBlock == NULL)
|
07
|
{
|
08
|
return STATUS_INSUFFICIENT_RESOURCES;
|
09
|
}
|
10
|
11
|
Status = InitMapPage(&MapHandle);
|
12
|
13
|
if (!NT_SUCCESS(Status))
|
14
|
{
|
15
|
ExFreePool(PhysicalMemoryBlock);
|
16
|
17
|
return Status;
|
18
|
}
|
19
|
20
|
i = 0;
|
21
|
22
|
while (PhysicalMemoryBlock[i].NumberOfBytes.QuadPart != 0)
|
23
|
{
|
24
|
PHYSICAL_ADDRESS BaseAddress = PhysicalMemoryBlock[i].BaseAddress;
|
25
|
LARGE_INTEGER NumberOfBytes = PhysicalMemoryBlock[i].NumberOfBytes;
|
26
|
27
|
DbgPrint( "BaseAddress: %I64x\n" , BaseAddress.QuadPart);
|
28
|
DbgPrint( "NumberOfBytes: %I64x\n" , NumberOfBytes.QuadPart);
|
29
|
30
|
while (NumberOfBytes.QuadPart > 0)
|
31
|
{
|
32
|
MapAddress = ( PUCHAR )MapSpecifiedPage(&MapHandle, BaseAddress);
|
33
|
34
|
if (MapAddress != NULL)
|
35
|
{
|
36
|
//
|
37
|
// 偏移依赖处理器实现,这里是Intel Core i3.
|
38
|
// 部分硬编码依赖nbp
|
39
|
//
|
40
|
41
|
if (*( PULONG )MapAddress == 0x10 && // VMCS revision identifier
|
42
|
*( PULONG )(MapAddress + 0x2D0) == 0x7F && // Guest GDTR limit
|
43
|
*( PULONG )(MapAddress + 0x2D4) == 0xFFF && // Guest IDTR limit
|
44
|
*( PULONGLONG )(MapAddress + 0x358) == __readmsr(0xc0000101)) // Host GS base
|
45
|
{
|
46
|
//
|
47
|
// Vmcs for current cpu.
|
48
|
//
|
49
|
50
|
DbgPrint( "VMCS: %I64x\n" , MapAddress);
|
51
|
DbgPrint( "VMCS Host RIP: %I64x\n" ,
|
52
|
*( PULONGLONG )(MapAddress + 0x390));
|
53
|
DbgPrint( "VMCS Host GDTR Base: %I64x\n" ,
|
54
|
*( PULONGLONG )(MapAddress + 0x368));
|
55
|
DbgPrint( "VMCS Host IDTR Base: %I64x\n" ,
|
56
|
*( PULONGLONG )(MapAddress + 0x370));
|
57
|
}
|
58
|
}
|
59
|
60
|
BaseAddress.QuadPart += PAGE_SIZE;
|
61
|
NumberOfBytes.QuadPart -= PAGE_SIZE;
|
62
|
}
|
63
|
64
|
i ++;
|
65
|
}
|
66
|
67
|
FiniMapPage(&MapHandle);
|
68
|
69
|
ExFreePool(PhysicalMemoryBlock);
|
70
|
71
|
……
|
72
|
}
|
程序debug输出如图2。
拿到这些信息,怎么制作“Red Pill”跳出被硬件虚拟化监控的状态就非常简单了,举个例子——比如首先可以通过Host CR3查找、修改Host页表映射加入我们的代码物理页(当然也可以直接利用Host中已经被映射的物理页面);随后通过修改VmxVmexitHandler(图中VMCS Host RIP所指)代码或者直接替换每个VMCS的Host RIP,在合适的VM Exit时获得Host上的运行权;最后利用获得的信息修改CPU各寄存器并转移到正确位置执行。具体代码就不贴了,有兴趣的同学可以试试。完成了这些,也就突破了nbp的限制。故以nbp的需求而言,它至少要管理完整的客户页表结构(构建Shadow Page Table或使用EPT/NPT)。
反过来,我们设计虚拟化系统或是其它安防系统,就要认真思考完备性的一些原则,才有做到滴水不漏的可能。当然了这类原则也不需要无限制扩大,例如为安全卫士设计硬件虚拟化辅助的需求,其主要是以扩充X64系统安全防护能力为目标,例如使得64位windows上的卫士软件不受PatchGuard限制,获得类似32位一样的拦截、防护能力。因此不仅无需提供物理内存防护,还要从保障性能方面考虑尽量减少不必要的#VMEXIT。
从Blue Pill、硬件虚拟化谈安全防护完备性上的一个小原则相关推荐
- 度娘计算机cpu,CPU硬件虚拟化技术和360“核晶防护引擎”(进一步强化电脑性能)...
关于CPU的硬件虚拟化技术(VT) CPU硬件虚拟化技(VT)术在两家CPU厂商的叫法不同,英特尔方面叫作 Intel-VT,AMD方面叫作 AMD-VT 简单讲,硬件虚拟化技术(以下简称VT)可以让 ...
- ARMv8架构下修改Linux内核并打开kvm硬件虚拟化支持(平台Firefly-rk3568)
前言 在做的一个项目需要使用ARMv8的硬件虚拟化支持,而购买的Firefly-3568默认的操作系统内核没有打开kvm虚拟化支持,所以尝试重新编译了一下内核开启虚拟化支持,并将遇到的问题和一些解决方 ...
- blue pill Flash 128KB的传言
有传言说,部分F103C8的Flash容量是128KB.用ST-Link Utility检查了一下,此言非虚.一款黑色的小板,Flash为官方标称的64KB:blue pill则显示确为128KB.意 ...
- virtualbox禁用硬件虚拟化_Mac版Virtualbox6.1开启嵌套虚拟化
Virtualbox从6.0版本后,支持起了Intel cpu的嵌套虚拟化.很多用Virtualbox的朋友开始陷入了茫然,为何在BIOS或EFI中开启了CPU硬件虚拟化后,Virtualbox中的v ...
- 虚拟机如何支持硬件虚拟化
虚拟机如何支持硬件虚拟化 配置:联想T440P,i7CPU,支持VT-x(硬件虚拟化技术),BIOS中已经打开虚拟化选项 系统:64位Win8 软件:VirtualBox ...
- 23. 硬件虚拟化技术分享
前段时间在梳理一些概念时,突然对虚拟化有一种新的认识,之后再去了解相关的概念或技术,能够推测某个技术是为了解决什么问题和如何实现的. 本文将对硬件虚拟化及其相关逻辑进行罗列,可能会对理解虚拟化有一些帮 ...
- 虚拟机安装kvm,bios已经开启硬件虚拟化功能,仍显示不支持硬件虚拟化
在Linux虚拟机上安装kvm,要检查硬件是否支持硬件虚拟化功能. 首先在BIOS中启动硬件虚拟化功能,发现仍然不支持 网上找了很久之后发现虚拟机设置的时候要开虚拟化引擎
- virtualbox 硬件加速配置页中已启用硬件虚拟化,但主机并不支持。需要禁用硬件虚拟化才能启动虚拟机
错误:virtualbox 硬件加速配置页中已启用硬件虚拟化,但主机并不支持.需要禁用硬件虚拟化才能启动虚拟机 解决办法: 选择[系统] 硬件加速 去掉勾选 保存 即可
- mac如何启用cpu虚拟化_如何查看自己的电脑 CPU 是否支持硬件虚拟化 - Binge-和时间做朋友...
引言 在你安装各种虚拟机之前,应该先测试一下自己的电脑 CPU 是否支持硬件虚拟化. 如果你的电脑比较老旧,可能不支持硬件虚拟化,那么将无法安装虚拟机软件. 如何查看自己 CPU 是否支持硬件虚拟化 ...
最新文章
- Failed to register Grid Infrastructure type ora.mdns.type
- numpy permutation排列组合方法
- Centos下Yum安装PHP5.5,5.6
- cli3暴露api地址 vue_手把手教你开发 Vue 组件库
- vlfeat 特征检测
- 力扣刷题心得(设计类题目)
- 焊接空间臂_焊接烟尘净化器设备哪种好
- 【论文翻译】HeteSim:异构网络中相关性度量的通用框架
- Java学习笔记--导航
- DataTable的Merge方法和添加datatable到dataset
- svn导出项目到myeclipse,运行报ClassNotFoundException
- python百分号字符串_python--003--百分号字符串拼接、format
- 【Redis 开发与运维】初识 Redis
- 启用计算机并口,电脑并口被禁用怎么办
- jsp网页视频播放器
- 目标检测中PR曲线和mAP
- Alpaca 凭什么成为 BSC 第三大协议?
- 三种代码生成炫酷代码雨(推荐)
- php货币符号怎么打,Magento修改货币符号和货币符号的位置
- 全网首发!老大众奥迪碟盒通信协议破解,可以模拟数码碟盒,外接AUX蓝牙U盘等音频设备