AI实践哪家强?来 AICon, 解锁技术前沿,探寻产业新机! 了解详情
写点什么

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

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

关注

评论

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

关于Java 编译Servlet或者自定义Tag,引入包的问题

谷鱼

Java

刷爆朋友圈的字节跳动编码题,今天把解析思路分享下!

Java架构师迁哥

高难度对话读书笔记—认知篇2

wo是一棵草

java安全编码指南之:可见性和原子性

程序那些事

Java java安全编码 java编码指南 java安全编码指南

如何设计Go语言中的channel

soolaugust

channel goroutines Go 语言

Dolphinscheduler系统架构设计

dll

Apache DolphinScheduler

架构师训练营第二周作业

尹斌

保留时序数据波动细节的一种采样算法

小清新同学

监控 时序数据库

2B还是2C,这真是个问题

MavenTalker

SaaS

自己动手写SQL执行引擎

无毁的湖光

Java MySQL 数据库 Linux 算法

架构师训练营第 1 期第 2周作业

owl

极客大学架构师训练营

难得干货,揭秘支付宝的2维码扫码技术优化实践之路

JackJiang

支付宝

从大数据的角度来谈谈运维监控这件事儿

小清新同学

运维 监控

传销资金盘挂靠区块链热点 肃清整顿热潮拉开帷幕

CECBC

区块链 金融

程序执行太慢?快来学习SIMD加速技术,这个案例下的加速效果我也没想到(附带动手实验)

Optimize-Lab

优化代码 优化技巧 开源社区 simd Go 语言

架构师训练营第 2 周作业

netspecial

极客大学架构师训练营

RN运行项目报错:Unable to resolve module `./debugger-ui/debuggerWorker.js` from ``

凌宇之蓝

ios android React Native

不一样的面向对象(二)

书旅

php 面向对象

监控应用,应该监控什么?

小清新同学

云计算 运维 监控

缓存解决方案-技术专题-Caffeine Cache

码界西柚

收藏+下载!Flink 社区最全学习渠道汇总

Apache Flink

flink

Python 自动化测试全攻略:五种自动化测试模型实战详解

葡萄城技术团队

自动化测试

MySQL varchar类型最大值,原来一直都理解错了

架构精进之路

MySQL varchar

架构师训练营第二周学习总结

尹斌

虚拟卡兑换架构设计

孙志平

如何快速制造OOM

Since

JVM OOM

架构师训练营第 1 期第二周课后练习题

Leo乐

极客大学架构师训练营

架构师训练营第 1 期第 2 周学习总结

owl

极客大学架构师训练营

Go中的HTTP请求之——HTTP1.1请求流程分析

Gopher指北

HTTP Go web Go 语言

什么才是“应用拓扑”?

小清新同学

运维 监控

项目实战,动态增删form表单

麦洛

jquery 克隆

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