PJF (http://blog.sina.com.cn/u/1455428611)
I haven't written a blog for many years. I'll pick up some leftovers and stir fry them. There's no nutrition. Students can have a look at them. In addition, the writing is really bad. The students who read need to be patient.
This blog talks about a small principle required for the completeness of a security protection system: the complete isolation of the protection system from the resources used by the protected objects, including that the resources relied on by the protection system itself cannot depend on the protected objects. It sounds like a simple point in principle, which is often complicated in implementation. For example, the operating system isolates the application, the virtual machine monitor isolates the client, all aspects need to be considered, and a large number of processor / chipset hardware support is also needed. It is necessary to follow many such small principles when analyzing and designing the system.
It seems that in 2006, Joanna team launched a rootkit sample based on hardware virtualization on blackhat, named BlueHill, and in 2007, an improved new version of newblue hill was released. This sample does not have any real malicious behavior. It is just the most famous prototype system supported by hardware virtualization. Let's take this topic as an example and introduce the necessity of this principle from the opposite direction.
After new BlueHill is loaded as a driver, it settles down, uses hardware virtualization support to put the original operating system into the client to run, and it itself exists as a virtual machine monitor. Its purpose is to show a rootkit using hardware virtualization technology. Like some programs (such as some debuggers) that use hardware virtualization support, it is incomplete. We can simply detect it and break away from its control. It is easy for students who have read the source code of newblue pill to recall that many resources required for the work of the virtual machine monitor are not effectively protected, and the client software can be accessed and modified at will. One of the most important is the physical memory resources. BlueHill's memory protection is only virtual memory hiding. See Joanna's diagram (Figure 1) for the principle. The virtual machine monitor has its own private page table. At the same time, the mapping of the page table used by the client operating system to itself is processed. Therefore, the software in the client can't directly access the virtual memory page of the virtual machine monitor.
Figure 1. New Blue Hill memory protection
How effective is this virtual memory protection? Because the physical memory resources of the client and the virtual machine monitor are not effectively isolated and do not conform to the principle, the effect can only be said to be better than nothing. The software in the client can easily access the physical memory of the virtual machine monitor, tamper with the code data of the virtual machine monitor, and even make a complete breakthrough to make the original operating system of the client run back to the host environment (such as vmxroot). Before reading the following content, you need to be familiar with some intel64 architecture and some knowledge of windows kernel.
First, a very simple physical memory access library (working in win7 x64 system) is designed. The principle is to modify the PTE corresponding to the non pagedpool page allocated in advance, so that the linear address can be used to map the physical memory page specified by us in turn. Note that the x64 system generally obtains a linear address in a large page by such allocation, so it needs to remap to get the PTE corresponding to the 4K page that can be modified; in addition, it should be noted that this mapping scheme is demonstration and very rough, and should not be used to map the physical space that has been mapped with the type of noncached, such as the IO mapping address space of peripheral devices.
typedef struct _MAP_STRUCT {
PVOIDOrigPage;
PVOIDMapPage;
PMDLMdl;
PHYSICAL_ADDRESS MapPagePhys;
} MAP_STRUCT, *PMAP_STRUCT;
#define VIRTUAL_ADDRESS_BITS 48
#define VIRTUAL_ADDRESS_MASK ((((ULONG_PTR)1)<< VIRTUAL_ADDRESS_BITS) -1)
#define PTE_BASE 0xFFFFF68000000000UI64
#define PTI_SHIFT 12
#define PDI_SHIFT 21
#define PPI_SHIFT 30
#define PXI_SHIFT 39
#define PTE_SHIFT 3
#define _HARDWARE_PTE_WORKING_SET_BITS 11
typedef struct _MMPTE {
ULONGLONGValid : 1;
ULONGLONGWritable :1; // changed for MPversion
ULONGLONGOwner : 1;
ULONGLONGWriteThrough : 1;
ULONGLONGCacheDisable : 1;
ULONGLONGAccessed : 1;
ULONGLONGDirty : 1;
ULONGLONGLargePage : 1;
ULONGLONGGlobal : 1;
ULONGLONGCopyOnWrite : 1; // softwarefield
ULONGLONGPrototype : 1; // software field
ULONGLONGWrite :1; // software field - MPchange
ULONGLONGPageFrameNumber : 28;
ULONG64reserved1 : 24 - (_HARDWARE_PTE_WORKING_SET_BITS+1);
ULONGLONGSoftwareWsIndex : _HARDWARE_PTE_WORKING_SET_BITS;
ULONG64NoExecute : 1;
} MMPTE, *PMMPTE;
#define MiGetPteAddress(va) \
((PMMPTE)(((((ULONG_PTR)(va) &VIRTUAL_ADDRESS_MASK) >> PTI_SHIFT)<< PTE_SHIFT) + PTE_BASE))
NTSTATUS
InitMapPage (
OUTPMAP_STRUCT MapHandle
)
{
NTSTATUSStatus = STATUS_SUCCESS;
PMMPTEpte;
RtlZeroMemory(MapHandle, sizeof(*MapHandle));
Try {
MapHandle->OrigPage =ExAllocatePool(NonPagedPool,
PAGE_SIZE);
if(MapHandle->OrigPage == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
leave; } MapHandle->Mdl =IoAllocateMdl(MapHandle->OrigPage, PAGE_SIZE, FALSE, FALSE, NULL); if (MapHandle->Mdl== NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; leave; } // // Remap // MapHandle->MapPage =MmMapLockedPagesSpecifyCache(MapHandle->Mdl, KernelMode, MmCached, NULL, FALSE, HighPagePriority); if(MapHandle->MapPage == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; leave; } pte =MiGetPteAddress(MapHandle->MapPage); MapHandle->MapPagePhys.QuadPart =*(PULONGLONG)pte; }finally { if (!NT_SUCCESS(Status)) { if (MapHandle->Mdl!= NULL) { IoFreeMdl(MapHandle->Mdl); } if(MapHandle->OrigPage != NULL) { ExFreePool(MapHandle->OrigPage); } } } return Status; } PVOID MapSpecifiedPage( INPMAP_STRUCT MapHandle, INPHYSICAL_ADDRESS PhysicalAddress ) { PMMPTE pte =MiGetPteAddress(MapHandle->MapPage); pte->PageFrameNumber = PhysicalAddress.QuadPart>> 12; _ReadWriteBarrier(); __invlpg(MapHandle->MapPage); returnMapHandle->MapPage; } VOID FiniMapPage( INPMAP_STRUCT MapHandle ) { PMMPTE pte =MiGetPteAddress(MapHandle->MapPage); pte->PageFrameNumber =MapHandle->MapPagePhys.QuadPart>> 12; MmUnmapLockedPages(MapHandle->MapPage,MapHandle->Mdl); IoFreeMdl(MapHandle->Mdl); ExFreePool(MapHandle->OrigPage); } 然后下面的程序片段基于这个访问库,搜索当前Intel Core i3CPU所关联的VMCS,顺便打印了其中的一些由NewBlue Pill事先填充的数据: { …… PhysicalMemoryBlock = MmGetPhysicalMemoryRanges(); if (PhysicalMemoryBlock == NULL) { returnSTATUS_INSUFFICIENT_RESOURCES; } Status =InitMapPage(&MapHandle); if (!NT_SUCCESS(Status)) { ExFreePool(PhysicalMemoryBlock); return Status; } i =0; while(PhysicalMemoryBlock[i].NumberOfBytes.QuadPart != 0) { PHYSICAL_ADDRESS BaseAddress =PhysicalMemoryBlock[i].BaseAddress; LARGE_INTEGER NumberOfBytes =PhysicalMemoryBlock[i].NumberOfBytes; DbgPrint("BaseAddress: %I64x\n",BaseAddress.QuadPart); DbgPrint("NumberOfBytes:%I64x\n", NumberOfBytes.QuadPart); while (NumberOfBytes.QuadPart> 0) { MapAddress = (PUCHAR)MapSpecifiedPage(&MapHandle,BaseAddress); if (MapAddress != NULL) { // // 偏移依赖处理器实现,这里是Intel Core i3. // 部分硬编码依赖nbp // if (*(PULONG)MapAddress == 0x10&& // VMCS revisionidentifier *(PULONG)(MapAddress + 0x2D0) == 0x7F&& // Guest GDTRlimit *(PULONG)(MapAddress + 0x2D4) == 0xFFF&& // Guest IDTR limit *(PULONGLONG)(MapAddress + 0x358) == __readmsr(0xc0000101))// Host GS base { // // Vmcs for currentcpu. // DbgPrint("VMCS:%I64x\n", MapAddress); DbgPrint("VMCS Host RIP:%I64x\n", *(PULONGLONG)(MapAddress + 0x390));
DbgPrint("VMCS Host GDTR Base:%I64x\n",
*(PULONGLONG)(MapAddress + 0x368));
DbgPrint("VMCS Host IDTR Base:%I64x\n",
*(PULONGLONG)(MapAddress + 0x370));
}
}
BaseAddress.QuadPart += PAGE_SIZE;
NumberOfBytes.QuadPart -= PAGE_SIZE;
}
i ++;
}
FiniMapPage(&MapHandle);
ExFreePool(PhysicalMemoryBlock);
...
}
The program debug output is shown in Figure 2.
Figure 2. Search current CPU VMCs
To get this information, it's very simple to make "red hill" jump out of the state monitored by hardware virtualization. For example, you can first find and modify the host page table mapping to add our code physical page (or directly use the mapped physical page in the host); then modify vmxvmexithandler (vmcshost in the figure) RIP) code or directly replace the hostrip of each VMCs to obtain the running right on the host when the vmexit is appropriate; at last, use the information obtained to modify the CPU registers and transfer to the correct location for execution. The specific code will not be posted, interested students can try. After that, the limitation of NBP is broken. Therefore, in terms of the requirements of NBP, it should at least manage the complete customer page table structure (build shadowpage table or use EPT / NPT).
In turn, when we design a virtualization system or other security system, we need to seriously consider some principles of completeness, so that we can be water tight. Of course, this kind of principle does not need unlimited expansion, such as the need to design hardware virtualization assistance for security guards, which is mainly aimed at expanding the security protection ability of x64 system. For example, it makes the guard software on 64 bit windows not limited by patchguard, and obtains the same interception and protection ability as 32-bit. Therefore, it is not only unnecessary to provide physical memory protection, but also to minimize unnecessary vmexit from the aspect of ensuring performance.