[关闭]
@sambodhi 2017-02-20T08:18:09.000000Z 字数 6485 阅读 1212

基于虚拟化的安全性 - 第2篇:内核通信

AMOSSYS Security团队的Adrien Chevalier撰写了一系列文章,讲述了基于虚拟化的安全。

作者在文章中使用了CC协议,InfoQ翻译本文。

本文是第二篇文章,涉及基于虚拟化的安全和设备保护功能。在第一篇中,我们介绍了从Windows引导加载程序到VTL0启动的系统引导过程。在这篇文章中,我们将解释VTL0和VTL1之间如何进行内核通信。当它们使用超级调用进行通信时,我们将首先描述Hyper-V的超级调用实现,然后内核如何使用它们进行通信。为了完成这一描述,我们将列出这项工作中我们发现的所有不同的超级调用和安全服务调用。

Hyper-V超级调用

VTL0和VTL1之间的内核通信使用Hyper-V超级调用。使用VMCALL指令执行这些超级调用,其中在RCX寄存器中具有超级调用号,并且RDX指向包含参数的Guest物理页(Guest Physical Page,GPA)。如果设置了0x10000 RCX位,超级调用是一个“FAST”超级调用,参数(和返回值)存储在XMM寄存器中。为了执行调用,Windows使用“hypercall trampoline”,这是一个小的fastcall例程,用来执行VMCALLRET

该例程存储在“超级调用页面”中。此页面包含5个trampoline,由Hyper-V在启动时提供给winload.efi,这将在VTL0和VTL1地址空间中复制它。这五个trampoline之间的主要区别是,第一个只是一个VMCALL/RET,但是接下来的四个(它们被连续定义)将RCX存储到RAX,并且在RCX中强制使用固定值。第二个和第三个将RCX强制为0x11,下一个强制为0x12

这四个trampoline实际上由不同的VTL使用。每个内核可以使用专用的超级调用来“询问”Hyper-V的0xD0002虚拟处理器寄存器值(可以使用其标识符来查询或设置的内部Hyper-V值),其将返回两个偏移量。这些偏移量与超级页面相关,并且将被内核用来调用正确的trampoline。实际上,VTL1和VTL0使用0x11trampoline来相互通信,并且VTL1使用0x12trampoline来完成其初始化。

超级调用页面的内容可以表示为:

  1. VMCALL <-- first trampoline
  2. RET
  3. MOV RAX, RCX <-- second one (enforces 0x11)
  4. MOV RCX, 0x11
  5. VMCALL
  6. RET
  7. MOV RAX, RCX <-- third (0x11)
  8. MOV RCX, 0x11
  9. VMCALL
  10. RET
  11. MOV RAX, RCX <-- fourth (0x12)
  12. MOV RCX, 0x12
  13. VMCALL
  14. RET
  15. MOV RAX, RCX <-- fifth (0x12)
  16. MOV RCX, 0x12
  17. VMCALL
  18. RET
  19. db 0x00
  20. db 0x00
  21. db 0x00
  22. db 0x00
  23. db 0x00
  24. db 0x00
  25. ...
  26. ...
  27. db 0x00

因此,我们有五个 trampoline,偏移量为0x000x040x0F0x1D0x28。注意,可以使用WinDbg轻松地在Windows崩溃转储中获取其内容,或在Hyper-V二进制文件(hvix64.exe/hvax64.exe,适用于Intel/AMD)的内部代码找到。

备注:几个超级调用可以指定RCX最高有效位DWORD的12个最低有效位中的数据大小。这些大小不是以字节为单位的数据长度,而是与当前调用相关,且可能表示入口计数等。

对于一个超级调用的例子,VTL1的ShvlProtectContiguousPageshypercall(12)参数是遵循此方案的结构体:

  1. typedef struct _param {
  2. ULONGLONG infinite; // always 0xFFFFFFFFFFFFFFFF
  3. ULONG protection_asked; // 0xD EXECUTE, 0xF WRITE
  4. ULONG zero_value; // always 0
  5. ULONGLONG pfns[]; // entries count is set in the hypercall number
  6. } param;

为了告诉Hyper-Vpfns参数的大小,RCX的高位DWORD必须包含其元素数量。对于只有一个入口和FAST超级调用而言,RCX值因此必须为0x10010000C

安全内核的超级调用

两个VTL能够执行多个超级调用,以便与Hyper-V进行通信。它们可以执行相同的超级调用,但Hyper-V将拒绝一些来自VTL0调用的超级调用。两个VTL还使用一个专用的超级调用来彼此通信。总结见图1

图1:Hypercalls类别

让我们首先描述“VTL1到Hyper-V”的超级调用(绿色)。然后我们将描述0x11的超级调用。

VTL1可以使用三种超级trampoline:

后两者使用0xD0002虚拟寄存器得到(通过ShvlpGetVpRegisterreturn返回值的24个最低有效位,每个偏移量为12位的长度)。这两个偏移指向0x110x12trampoline。

顺便说一句,VTL0 NTOS内核使用同一进程获得其HvlpVsmVtlCallCodeVa值(用于VTL0到VTL1通信的超级trampoline),但获得是相反的结果。正是因为Hyper-V会根据VTL所寄宿VM的不同而返回不一样的值,所以我们认为任何VM使用trampolines访问同样的超级调用页面时,都会请求一遍虚拟寄存器的值。

下表是每个trampoline可能的VTL1超级调用:

ShvlpVtlCall

Hypercall number Caller - usage
0x12 ShvlInitSystem – end of VTL1 initialization?

ShvlpVtlReturn (VTL0 returns/calls)

Hypercall number Caller - usage
0x11 SkCallNormalMode/SkpPrepareForReturnToNormalMode – returns to NTOS / calls NTOS返回NTOS /调用NTOS

ShvlpHypercallCodePage (HyperV)

Remark: We wrote an (H) when realizing an hypothesis.

Hypercall number Caller - usage
0x2 ShvlFlushEntireTb, etc. - Translation buffer flushs
0x3 Translation buffer flushs
0xB SkpgPatchGuardCallbackRoutine – (H) HyperGuard delayed routines registering
0x15
0xC ShvlProtectContiguousPages – Memory protection modification
0xD ShvlInitSystem – Called just before ShvlEnableVpVtl, seems to send several settings to HV
0xF ShvlEnableVpVtl – Sends settings to HV, and notably the ShvlpVtl1Entry function pointer
0x50 ShvlGetVpRegister – Gets a virtual processor (VP) register
0x51 ShvlSetVpRegister – Sets a virtual processor register
0x52 SkpgTranslateVa – (H) VTL0 memory access by HyperGuard
0x86 ShvlPrepareForHibernate
0x87 ShvlNotifyRootCrashDump
0x94 BugCheck
0x8E LiveDumpCollect
0x97 SkpGetPageList
0xAE ShvlSetGpaPageAttributes – 1607 build: changes a GPA attributes, seems to only been used on VTL1 memory

VTL0到VTL1的转换

几乎所有NTOS“Vsl”前缀函数最终以VslpEnterIumSecureMode结尾,带有安全服务调用号Secure Service Call Number,SSCN)。此函数调用HvlSwitchToVsmVtl1,它使用HvlpVsmVtlCallVa超级调用 trampoline(常规hyper-V超级调用使用HvcallCodeVatrampoline)。然后将SSCN复制到RAX中,并将RCX值设置为0x11

Hyper-V将0x11超级调用分派到securekernel.exe函数SkpReturnFromNormalMode中,然后调用IumInvokeSecureService(实际上我们不确定IumInvokeSecureService是否被直接调用,我们认为必须首先调用SkpReturnFromNormalMode,以使IumInvokeSecureService在安全服务调用完成后返回到VTL0)。IumInvokeSecureService主要是一个大的switch/case块,它处理所有的SSCN

最后,调用SkCallNormalMode,以SkpPrepareForReturnToNormalMode为结尾。实际上,安全内核的NTOS调用可以被认为是对VTL0的“伪返回”,因为它们也包含在0x11超级调用中。

我们已经从下面的阵列中确认了来自VTL0的所有可能的SSCN。对于每一个,我们指出了被调用的函数,它们的名字通常是不言自明的。相应的参数必须通过逆向VTL0调用者或VTL1调用源来确定。

SSCN Called function
1 SkmmInitializeUserSharedData / SkInitSystem
2 SkeStartProcessor
3 SkpsRegisterSystemDll
4 InterlockedCompareExchange(IumSystemProcessRegistered) / PsIumSystemProcess manipulation
5 SkmmCreateProcessAddressSpace/SkobCreateHandle
6 SkeInitializeProcess
7 IumCreateThread
8 SkiTerminateAllThreads
9 IumTerminateThread
10 SkeRundownProcess
11 SkpsIsProcessDebuggingEnabled / SkmmDisableProcessMemoryProtection
12 Unknown
13 & 14 SkmmMapMdl / IumpGetSetContext
15 SkeReferenceProcessByHandle / SkmmMapDataTransfer / SkpEncryptWithTrustletKey
16 SkeReferenceProcessByHandle / Unknown
17 SkRetrieveMailbox
18 SkIstTrustletRunning
19 SkmmCreateSecureAllocation
20 SkmmMapDataTransfer / SkmiFillSecureAllocation
21 SkmmConvertSecureAllocationToCatalog
22 SkmmCreateSecureImageSection
23 SkmmFinializeSecureImageHash
24 SkmmFinishSecureImageValidation
25 SkmmPrepareImageRelocations
26 SkmmRelocateImage
27 SkobCloseHandleEx
28 SkmmValidateDynamicCodePages
29 SkmmTransferImageVersionResource
30 EntropyProvideData / BCryptGenRandom
31 SkpEncryptHiberData / SkpSetHiberCrashState
32 SkpSetHiberCrashState / SkpgHibernateActive = 0 / SkFinalizePageEncryption
33 SkmmConfigureDynamicMemory
34 IumConnectSwInterrupt
35 Unknown = 0x3000
36 SkLiveDumpStart
37 SkpLiveDumpContext / Unknown
38 SkLiveDumpSetupBuffer
39 SkLiveDumpFinalize
40 SkpLiveDumpFreeContext / SkpReleaseLiveDumpLock
41 SkNotifyPowerState
42 IumDispatchQueryProfileInformation
192 SkeReferenceProcessByHandle
193 SkmmValidateSecureImagePages
208 SkmmInitSystem / IumpInitializeSystem
209 SkpWorkItemList / Unknown
210 SkmiReleaseUnprivilegedPagesInRange / SmiReserveNtAddressRange
211 SkmmApplyDynamicRelocations
212 SkEtwEnableCallback
224 SkiAttachProcess / SkmiFlushAddressRange
225 SkmmFastFlushRangeList
226 SkmmSlowFlushRangeList
227 SkmmRemoveProtectedPage
228 SkmmCopyProtectedPage
229 SkmmMakeProtectedPageWritable
230 SkmmMakeProtectedPageExecutable
231 (H) Gets *Skmi *flags
232 SkhalEfiInvokeRuntimeService
233 SkLiveDumpCOllect
234 SkmmRegisterFailureLog
235 SkPrepareForHibernate
236 SkPrepareForCrashDump
237 SkhalpEfiRuntimeInitialize / SkhalpReportBugCheckInProgress
238 & 240 Returns an error code
241 SkKsrCall
Otherwise SkeBugCheckEx(0x121, 0xFFFFFFFFC000001C, , 0, 0)

正如你所见,几个被调用的函数是未知的。这是因为它们没有执行明显的调用,我们没有花时间去继续分析。

结论

本文描述了基于虚拟化的安全VTL0-VTL1如何进行内核通信。

如果你想要更多的Hyper-V相关信息,你也可以阅读这两篇文章:

我们计划将发表第三篇关于VBS的文章,将重点介绍HVCI内部,特别是*W^X*VTL0内核保护。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注