从Blue Pill、硬件虚拟化谈安全防护完备性上的一个小原则
来源:互联网 发布:淘宝怎么搜二手 编辑:程序博客网 时间:2024/04/30 11:03
原文地址http://blogs.360.cn/360safe/2013/09/30/from-bluepill-vmx-to-a-simple-defence-principle/
多年没写博客了,就先捡点剩菜剩饭炒炒,没啥营养,同学们可以随便看看。另外就是文笔真心烂,阅读的同学们需要点耐心。
这篇博客说说一个安全防护系统完备性上所需的一个小原则:防护系统与被防护对象所用资源的完全隔离,包括防护系统自身工作所依赖的资源不能依赖被防护对象。听起来是原理很简单的一个点,实现中往往很复杂。例如操作系统隔离应用程序、虚拟机监视器隔离客户机,需要考虑方方面面、还需要处理器/芯片组硬件的大量支持。分析、设计系统时遵循许多这类小原则是必要的。
记得好像是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、硬件虚拟化谈安全防护完备性上的一个小原则
- 从Blue Pill、硬件虚拟化谈安全防护完备性上的一个小原则
- Blue pill
- Blue Pill的编译和安装
- 云计算虚拟化环境下的安全防护
- The Hottest Pill – Blue Pill
- Blue Pill源代码分析(1)
- Blue Pill源代码分析(2)
- Blue Pill源代码分析(3)
- Blue Pill源代码分析(4)
- 实验三十五 Windows Server 2012 RDS桌面虚拟化之六VDI虚拟桌面的用户管理和安全防护
- new blue pill在Intel系列CPU上无法卸载问题
- 从六大方面防护你的网站安全
- 细数iOS上的那些安全防护
- 细数iOS上的那些安全防护
- 细数iOS上的那些安全防护
- 细数iOS上的那些安全防护
- 细数iOS上的那些安全防护
- vs2013设置winpcap开发环境
- 图的搜索算法
- matlab 同个坐标下的两个函数图像的比较
- HDU2084
- android 手机不能发短信
- 从Blue Pill、硬件虚拟化谈安全防护完备性上的一个小原则
- C++编程规范指47.以同样的顺序定义和初始化成员变量
- Matlab科学图表美化——漫画式图表
- Mysql数据库5.6.10 error 2003 hy000
- JS之时间显示问题
- Interview---一道有趣的推理题
- 资源记录(网络资源)
- linux 管道
- 错过,你才知道我的好