写点什么

基于虚拟化的安全性 - 第 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:424741
用户头像

发布了 376 篇内容, 共 200.3 次阅读, 收获喜欢 949 次。

关注

评论

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

适合程序员表白的情话【保你脱单】

Java 程序员 后端

遇到Java内存溢出(OOM)时,这样排查

Java 程序员 后端

dart系列之:dart语言中的异常

程序那些事

flutter dart 程序那些事 11月日更

拥有一台服务器后,我竟然这么酷?

老表

Python Linux web开发 云服务器 跟老表学云服务器

送给你一份程序员大佬整理的 HTTP 基础知识大图

Java 程序员 后端

适合程序员表白的情话【保你脱单】(1)

Java 程序员 后端

闭关2个月肝完Java7大核心知识(分布式+JVM+Java基础+算法

Java 程序员 后端

阿里P6程序员的Java之路-或许起点不高,但我从未停止我的脚步

Java 程序员 后端

阿里P7告诉你SpringBoot如何防止重复提交?

Java 程序员 后端

阿里P8大牛整理的300页图解网络知识+计算机底层操作系统

Java 程序员 后端

透过根源从而探究红黑树的本质,究竟二叉树是什么神仙鬼怪?

Java 程序员 后端

逼着面试官问了我ArrayList和LinkedList的区别,他对我彻底服了

Java 程序员 后端

重走JAVA之路(五):面试又被问线程池原理?教你如何反击

Java 程序员 后端

一个简单的UserCase,带你认识openLooKeng的行级权限控制

LooK

大数据 openLooKeng

Python爬虫120例之第20例,1637、一路商机网全站加盟数据采集

梦想橡皮擦

11月日更

阿里P8大佬终于把自己珍藏多年581页JavaJDK9学习笔记分享出来了

Java 程序员 后端

阿里Java架构师春招面试高频600题:集合+JVM+Redis+并发

Java 程序员 后端

重走JAVA之路(五):面试又被问线程池原理?教你如何反击(1)

Java 程序员 后端

阿里P8十年摸爬滚打,告诉你上古程序猿为何反对使用Redis?

Java 程序员 后端

安装企业级的dokuwiki文档系统

小鲍侃java

11月日更

阿里P8大牛透出,入职阿里必备12套Java面试题,适合从校招到社招

Java 程序员 后端

Vue进阶(幺捌叁):IE9兼容性问题-数据初始化问题

No Silver Bullet

Vue 11月日更

金三银四助力面试-手把手轻松读懂HashMap源码

Java 程序员 后端

闲下来的学习时光

Java 程序员 后端

阿里-美团-字节面试官必问的Mysql锁机制,你真的明白吗

Java 程序员 后端

远景能源java后台开发实习面试题

Java 程序员 后端

通过AOP和自定义注解实现请求日志收集功能

Java 程序员 后端

我终于知道,中国互联网是怎么弯道超车,干翻美国了!

博文视点Broadview

阿里2020首发一百多道Java高级岗面试题(含答案)

Java 程序员 后端

华为全球技术服务引领行业数智创新,共赢未来

【Flutter 专题】11 图解 ListView 的多种绑定方式

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 11月日更

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