写点什么

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

  • 2017-03-08
  • 本文字数:4407 字

    阅读完需:约 14 分钟

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 例程,用来执行 VMCALL 和 RET。

该例程存储在“超级调用页面”中。此页面包含 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 来完成其初始化。

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

复制代码
VMCALL <-- first trampoline
RET
MOV RAX, RCX <-- second one (enforces 0x11)
MOV RCX, 0x11
VMCALL
RET
MOV RAX, RCX <-- third (0x11)
MOV RCX, 0x11
VMCALL
RET
MOV RAX, RCX <-- fourth (0x12)
MOV RCX, 0x12
VMCALL
RET
MOV RAX, RCX <-- fifth (0x12)
MOV RCX, 0x12
VMCALL
RET
db 0x00
db 0x00
db 0x00
db 0x00
db 0x00
db 0x00
...
...
db 0x00

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

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

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

复制代码
typedef struct _param {
ULONGLONG infinite; // always 0xFFFFFFFFFFFFFFFF
ULONG protection_asked; // 0xD EXECUTE, 0xF WRITE
ULONG zero_value; // always 0
ULONGLONG pfns[]; // entries count is set in the hypercall number
} param;

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

安全内核的超级调用

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

复制代码
1:Hypercalls 类别

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

VTL1 可以使用三种超级 trampoline:

  • ShvlpHypercallCodePage, 相当于 NTOS HvlpHypercallCodePage(偏移 0),并指向第一个 trampoline;
  • ShvlpVtlReturn,它将 RCX 强制为 0x11 并允许 VTL0 和 VTL1 通信;
  • ShvlpVtlCall,它将 RCX 强制为 0x12,并且仅在 VTL1 初始化期间使用。

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

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

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

ShvlpVtlCall

Hypercall number Caller - usage0x12 ShvlInitSystem – end of VTL1 initialization?

ShvlpVtlReturn (VTL0 returns/calls)

Hypercall number Caller - usage0x11 SkCallNormalMode/SkpPrepareForReturnToNormalMode – returns to NTOS / calls NTOS 返回 NTOS / 调用 NTOSShvlpHypercallCodePage (HyperV)

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 / SkInitSystem2 SkeStartProcessor3 SkpsRegisterSystemDll4 InterlockedCompareExchange(IumSystemProcessRegistered) / PsIumSystemProcess manipulation5 SkmmCreateProcessAddressSpace/SkobCreateHandle6 SkeInitializeProcess7 IumCreateThread8 SkiTerminateAllThreads9 IumTerminateThread10 SkeRundownProcess11 SkpsIsProcessDebuggingEnabled / SkmmDisableProcessMemoryProtection12 Unknown_13 & 14 SkmmMapMdl / IumpGetSetContext15 SkeReferenceProcessByHandle / SkmmMapDataTransfer / SkpEncryptWithTrustletKey16 SkeReferenceProcessByHandle / Unknown_17 SkRetrieveMailbox18 SkIstTrustletRunning19 SkmmCreateSecureAllocation20 SkmmMapDataTransfer / SkmiFillSecureAllocation21 SkmmConvertSecureAllocationToCatalog_22 SkmmCreateSecureImageSection_23 SkmmFinializeSecureImageHash_24_ SkmmFinishSecureImageValidation_25_ SkmmPrepareImageRelocations_26_ SkmmRelocateImage_27_ SkobCloseHandleEx_28_ SkmmValidateDynamicCodePages_29_ SkmmTransferImageVersionResource30 EntropyProvideData / BCryptGenRandom31 SkpEncryptHiberData / SkpSetHiberCrashState32 SkpSetHiberCrashState / SkpgHibernateActive = 0 / SkFinalizePageEncryption33 SkmmConfigureDynamicMemory34 IumConnectSwInterrupt35 Unknown = 0x300036 SkLiveDumpStart37 SkpLiveDumpContext / _Unknown_38 SkLiveDumpSetupBuffer39 SkLiveDumpFinalize40 SkpLiveDumpFreeContext / SkpReleaseLiveDumpLock41 SkNotifyPowerState42 IumDispatchQueryProfileInformation192 SkeReferenceProcessByHandle193 SkmmValidateSecureImagePages208 SkmmInitSystem / IumpInitializeSystem209 SkpWorkItemList / _Unknown_210 SkmiReleaseUnprivilegedPagesInRange / SmiReserveNtAddressRange211 SkmmApplyDynamicRelocations212 SkEtwEnableCallback224 SkiAttachProcess / SkmiFlushAddressRange225 SkmmFastFlushRangeList226 SkmmSlowFlushRangeList227 SkmmRemoveProtectedPage228 SkmmCopyProtectedPage229 SkmmMakeProtectedPageWritable230 SkmmMakeProtectedPageExecutable231 (H) Gets *Skmi *flags232 SkhalEfiInvokeRuntimeService233 SkLiveDumpCOllect234 SkmmRegisterFailureLog235 SkPrepareForHibernate236 SkPrepareForCrashDump237 SkhalpEfiRuntimeInitialize / SkhalpReportBugCheckInProgress238 & 240 Returns an error code241 SkKsrCallOtherwise SkeBugCheckEx(0x121, 0xFFFFFFFFC000001C, , 0, 0)

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

结论

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

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

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


感谢魏星对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017-03-08 16:424225
用户头像

发布了 366 篇内容, 共 167.1 次阅读, 收获喜欢 935 次。

关注

评论

发布
暂无评论
发现更多内容

程序员如何快速开发高质量的代码?

利志分享

在线Excel转CSV工具

入门小站

工具

「面向信仰编程」Draven 专访:像写代码一样,用树形的结构写文章

Shopee技术团队

Go 语言 技术播客

Java多线程归纳整理

高山觅流水

多线程

如何发挥测试策略的指导性作用

KJ Meng

DevOps 研发管理 测试 自动化测试 测试策略

在线时序流程图制作工具

入门小站

工具

轻量化项目管理思路

秋去冬来春未远

需求管理 问题管理 问题和需求区别

一文搞懂SPI通信协议

不脱发的程序猿

spi 嵌入式 通信协议

Robot OS添加开机启动服务

轻口味

android 4月月更 AOSP

LabVIEW采集鼠标、键盘数据

不脱发的程序猿

LabVIEW 获取鼠标、键盘输入数据

LabVIEW显示控件中内容过长设置自动滚动条

不脱发的程序猿

LabVIEW 设置自动滚动条

网络安全之渗透靶场实战详解

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 漏洞挖掘

瑞萨IDE:CS+ for CC新建工程配置方法

不脱发的程序猿

ide 汽车电子 CS+ for CC 瑞萨MCU

feign报400处理

Rubble

4月日更

OpenHarmony开源开发者成长计划,寻找改变世界的开源新生力!

科技汇

Scrum Patterns之理解各种团队模式

Bruce Talk

Scrum 敏捷开发 Agile Scrum Patterns

Go 语言入门很简单:时间包

宇宙之一粟

时间 Go 语言 4月月更

redis优化系列(三)解决主从配置后的常见问题

乌龟哥哥

4月月更

linux之pkill命令

入门小站

什么是区块链数字藏品,它具有什么价值?

CECBC

LabVIEW修改应用程序窗口外观

不脱发的程序猿

LabVIEW 修改应用程序窗口外观

瑞萨IDE:CS+ for CC进行BootLoader升级时开发环境配置

不脱发的程序猿

嵌入式 汽车电子 MCU 瑞萨 CS+ for CC

读《Software Engineering at Google》(15)

术子米德

架构师成长笔记

手撕《Google SRE Book》

一席

正确的文档排版方式

源字节1号

【PIMF】OpenHarmony啃论文俱乐部—在ACM Survey闲逛是什么体验

离北况归

ACM OpenHarmony Openharmony啃论文俱乐部 欧建深教练 PIMF团队

网络协议之:sctp流控制传输协议

程序那些事

Java Netty 程序那些事 4月月更

DaaS大数据服务简析

穿过生命散发芬芳

4月月更

LabVIEW设置应用程序显示标签透明

不脱发的程序猿

LabVIVE 美化应用程序

14岁懂社会-《给想要成为医生的你》读书笔记

懒时小窝

读书笔记 读书

瑞萨IDE:CS+ for CC下载、安装和基础使用

不脱发的程序猿

ide 瑞萨 汽车级MCU CS+ for CC

基于虚拟化的安全性 - 第2篇:内核通信_安全_Adrien Chevalier_InfoQ精选文章