介绍
两年前,微软作为Insiders build 18305的一部分发布了一项新功能- Windows Sandbox。
该沙箱具有一些有用的规格:
- Windows 10(Pro/Enterprise)的集成部分。
- 在 Hyper-V 虚拟化上运行。
- 原始且可抛弃 – 每次运行时都干净地开始并且没有持久状态。
- 可通过具有专用格式(WSB 格式)的配置文件进行配置。您可以配置网络、vGPU、映射文件夹、用户登录时运行的自动脚本以及许多其他选项。
- 该部署基于 Windows Containers 技术。
微软实现了一个重大的技术里程碑。由此产生的沙盒兼具了两方面的优点:一方面,沙盒基于 Hyper-V 技术,这意味着它继承了 Hyper-V 严格的虚拟化安全性。另一方面,沙盒包含多项功能,允许与主机共享资源以减少 CPU 和内存消耗。
其中一个有趣的特征特别重要,我们将在这里详细说明。
动态生成的图像
客户磁盘和文件系统是动态创建的,并使用主机文件系统中的文件实现。
图 1 – 动态生成的图像(来自 Microsoft 官方文档)。
出于多种原因,我们决定深入研究这项技术。
- 缺乏关于其内部技术细节的文档,无论是官方文档还是社区文档。虽然它结合了两种广泛记录的技术(Windows 容器和 Hyper-V),但我们仍然不知道它们是如何协同工作的。例如,技术博客提到了Windows 容器技术,但在官方文档中,Windows 容器的创建和管理是使用 Windows 的Docker实用程序完成的,而 Windows Sandbox 中并未使用该实用程序。
- 不幸的是,除了调整 WSB 文件之外,微软不允许对沙盒进行任何自定义。这意味着我们无法安装任何需要重新启动的程序,也无法为沙盒创建自己的基础映像。
在本文中,我们将分解动态图像功能的多个组件、执行流程、驱动程序支持和实现设计。我们展示了其中涉及的几种内部技术,例如 NTFS 自定义重新解析标记、VHDx 分层、用于正确隔离的容器配置、虚拟存储驱动程序、VMBus 上的 vSMB 等。我们还创建了一个自定义FLARE VM沙盒用于恶意软件分析,其启动时间仅为 10 秒。
通用组件
Hyper-V 及其模块的复杂生态系统已经得到了广泛的研究。发现了几个漏洞,例如下一个 VmSwitch RCE,它可能导致完全的客户机到主机逃逸。几年前,微软推出了 Windows 容器(主要用于服务器),该功能允许在 Windows 上本地运行 Docker,以简化软件部署。
这两项技术也以两个组件的形式引入了 Windows 10 端点平台:WDAG(Windows Defender 应用程序防护)以及最近的Windows Sandbox。最近,WDAG 和另一项令人兴奋的 Office 隔离功能合并为MDAG – Microsoft Defender 应用程序防护。
在POC2018大会上,张云海做了一个演讲,深入介绍了 WDAG 的架构和内部原理。正如我们所展示的,Windows Sandbox 的底层实现采用了相同的技术。
沙箱可以分为三个组件:两个服务——CmService.dll
和vmcompute.exe
——以及创建的工作进程vmwp.exe
。
图 2 – Windows Sandbox 通用组件
准备沙箱
每个基于 Hyper-V 的虚拟机背后都有一个VHDx文件,即机器使用的虚拟磁盘。为了了解磁盘是如何创建的,我们查看了一个正在运行的沙箱的工作文件夹:%PROGRAMDATA%\Microsoft\Windows\Containers
。令人惊讶的是,我们发现了 8 个以上的 VHDx 文件。
图 3 – 工作文件夹结构。
我们可以通过下一个路径中的动态大小来跟踪主 VHDx 文件 –Sandboxes\29af2772-55f9-4540-970f-9a7a9a6387e4\sandbox.vhdx
其中 GUID 是在每次沙盒运行时随机生成的。
当我们手动挂载 VHDx 文件时,我们发现它的大部分文件系统都丢失了(这种现象在前面提到的张的 WDAG 研究中也能看到)。
图 4 – 安装的沙盒 VHDx。
我们可以立即看到文件夹图标上的“ X ”符号。如果我们打开文件资源管理器中的“属性”列,我们可以看到两个不寻常的 NTFS 属性。这些解释如下:
O——离线
L——重新解析点
重新解析点是 NTFS 的一个扩展,允许它创建指向另一条路径的“链接”。它还在其他功能中发挥作用,例如卷安装。在我们的例子中,使用此功能是有意义的,因为大多数文件并非“物理”存在于 VHDx 文件中。
为了了解重新解析指向的位置以及其中的内容,我们深入研究了 NTFS 结构。
解析MFT记录
主文件表( MFT) 存储从 NTFS 分区检索文件所需的信息。一个文件可能有一个或多个 MFT 记录,并且可以包含一个或多个属性。我们可以运行流行的取证工具Volatility,并可以mftparser
选择解析底层文件系统中的所有 MFT 记录。这可以使用以下命令行完成:
volatility.exe -f sandbox.vhdx mftparser --output=body -D output --output-file=sandbox.body
当我们kernel32.dll
在输出中搜索(示例系统文件)记录时,我们遇到以下文本:
0| [ MFT FILE_NAME ] Windows\System32\kernel32. dll (偏移量:0x3538c00 )| 1251 |---a---S--o----| 0 | 0 | 764456 | 1604310972 | 1596874670 | 1603021550 | 1596874670
0 | [ MFT STD_INFO ] Windows\System32\kernel32. dll (偏移量:0x3538c00 )| 1251 |---a---Sr-o----| 0 | 0 | 764456 | 1606900209 | 1596874670 | 1603021550 | 1596874670
我们可以看到与之前类似的重新解析(“ S ”)和离线(“ o0x3538c00
”)属性,但 Volatility 没有提供任何其他信息。我们可以使用 MFT 记录的偏移量来启动我们自己的手动解析。
我们使用下一个 NTFS 文档来执行解析过程。我们没有提供 MFT 格式的完整规范,但简单地说,MFT 记录包含可变数量的属性,每个属性都有自己的标头和有效负载。我们正在寻找$REPARSE_POINT
由序数 标识的属性0xC0
。
图 5 – MFT 属性头结构。
图 6 –$REPARSE_POINT
属性有效载荷结构。
我们对上面列出的结构进行解析,得到以下数据:
$REPARSE_POINT 属性
--------------- 属性标题 ---------------
C0 00 00 00 -类型($REPARSE_POINT )
78 00 00 00 - 长度
00—— 非居民旗帜
00—— 名称长度
00 00 - 名称偏移量
00 00—— 旗帜
03 00 - 属性ID (a )
5C 00 00 00 -属性的长度
18 00 - 属性偏移量
00—— 索引标志
00—— 填充
---------------- 属性数据 ----------------
18 10 00 90 - 重新解析标签
54 00 - 重新解析数据长度
00 00—— 填充
----------------- 重新解析数据 -----------------
01 00 00 00-版本?
00 00 00 00——保留?
77 F6 64 82 B0 40 A5 4C BF 9A 94 4A C2 DA 80 87 - 参考 GUID
3A 00 - 路径字符串大小
57 00 69 00 6E 00 64 00 6F 00 77 00 73 00 5C 00
53 00 79 00 73 00 74 00 65 00 6D 00 33 00 32 00
5C 00 6B 00 65 00 72 00 6E 00 65 00 6C 00 33 00
32 00 2E 00 64 00 6C 00 6C 00 - 路径字符串
一些重要说明:
- 我们没有找到有关微软重新解析数据结构的任何公开文档,但进行逆向工程并不太难。
- 重新解析标签在此处
0x90001018
定义如下:IO_REPARSE_TAG_WCI_1
“由 Windows 容器隔离过滤器使用。仅供服务器端解释,无意义。” |
- 在这项研究中对 Windows 模块进行逆向工程时,我们多次发现引用的 GUID
77 F6 64 82 B0 40 A5 4C BF 9A 94 4A C2 DA 80 87
是硬编码值。此值表示对主机基础层的引用,我们稍后会讨论它。 - 重新解析数据中的路径显示了我们的示例文件的相对路径:
Windows\System32\kernel32.dll
根据以上信息,我们可以得出结论,文件是由底层文件系统“链接”的(可能是链接到指定的 FS 过滤器),但许多问题仍然没有答案:VHDx 是如何构建的,其他 VHDx 的用途是什么,以及哪个组件负责链接到主机文件。
VHDx 分层
如果我们在沙箱创建期间跟踪Procmon日志,我们会注意到一系列 VHDx 访问尝试:
图 7 – VHDx 分层引线。
虽然第一个是我们之前解析过的“真实” VHDx,但后面还有 3 个其他 VHDx 访问。我们怀疑 Microsoft 对虚拟磁盘模板使用了某种分层。
通过使用二进制编辑器检查 VHDx 文件,可以轻松验证我们的理论:
图 8 – parent_linkage
010 编辑器中的标签。
VHDx 格式的父定位器可以使用多种方法给出:绝对路径、相对路径和卷路径。文档可在此处找到。
有了这些知识,我们可以构建下一层:
Sandboxes\<new_sandbox_guid>\sandbox.vhdx
– “真正的” VHDx。Sandboxes\<constant_guid_per_installation>\sandbox.vhdx
– 每个沙箱安装创建一次。BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\Snapshot\SnapshotSandbox.vhdx
– 可能与基础层快照相关。PortableBaseLayer\SystemTemplateBase.vhdx
– 基础模板。
当我们浏览这些虚拟磁盘时,我们注意到文件仍然丢失;一些系统文件夹是空的,以及用户/程序文件和其他各种文件的文件夹。
使用 Procmon 让我们了解到缺少另一个重要层:操作系统基础层。
操作系统基础层
操作系统基础层主文件位于下一个路径的沙盒工作文件夹中:BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer.vhdx
。通过 Procmon 查看安装过程,我们可以看到下一个.wim
(Windows 映像格式)文件C:\Windows\Containers\serviced\WindowsDefenderApplicationGuard.wim
被提取到PortableBaseLayer
同名文件夹中,并被复制并重命名到上面的基础层文件中。这又显示了 WDAG 和 Windows Sandbox 之间的另一个相似之处。
当我们浏览BaseLayer.vhdx
磁盘时,我们可以看到创建的沙箱的完整结构,但系统文件仍然“物理”缺失。像kernel32.dll
我们之前所做的那样解析 MFT 记录会产生相同的$REPARSE_POINT
属性,但标签不同:0xA0001027
。IO_REPARSE_TAG_WCI_LINK_1
记住这个标签以备后用。
图 9 – 基础层用户文件夹。
此外,当我们运行mountvol
命令时,我们会看到基础层 VHDx 被挂载到其所在的同一目录中:
图 10 – 已安装的操作系统基础层。
负责安装该卷的服务以及我们到目前为止提到的所有功能都是容器管理服务 CmService.dll
。
此服务运行一个名为的可执行文件cmimageworker.exe
,并使用下一个命令行参数之一expandpbl/deploy/clean
来执行这些操作。
图 11 —CmService
基础层创建。
computestorage!HcsSetupBaseOSLayer
我们可以在 中观察到对 的调用cmimageworker.exe
,以及在 中基础层的实际创建部分computestorage.dll
。
图 12 –cmimageworker!Container::Manager::Hcs::ProcessImage
启动基础层创建。
图 13 – 中基础层创建的一部分computestorage!OsImageUtilities::ProcessOsLayer
。
微软就沙盒发表了以下声明:
Windows 的一部分– 此功能所需的一切都随 Windows 10 Pro 和 Enterprise 一起提供。无需下载 VHD! |
到目前为止,我们了解了有关该功能的关键实现细节。让我们继续看看容器是如何执行的。
运行沙盒
运行 Windows Sandbox 应用程序会触发执行流程,我们在此不再赘述。我们只提到该流程通过 RPC 调用导致CmService
执行。另一项关键服务运行并协调主机上的所有计算系统(容器)。vmcompute!HcsRpc_CreateSystem
vmcompute.exe
在我们的例子中,CreateSystem命令还接收描述所需机器的下一个配置 JSON:
注意:为了便于阅读,JSON 已被截断。您可以在附录 A中查看完整的 JSON 。
{
“所有者” :“马德里” ,
...
“虚拟机” :{
...
“设备” :{
“Scsi” :{
“基本的” :{
“附件” :{
"0" :{
“类型” :“虚拟磁盘” ,
“路径” :“C:\\ProgramData\\Microsoft\\Windows\\Containers\\Sandboxes\\025b00c8-849a-4e00-bcb2-c2b8ec698bab\\sandbox.vhdx” ,
...
}
}
}
} ,
...
“虚拟Smb” :{
“分享” :[{
“名称” :“os” ,
“路径” :“C:\\ProgramData\\Microsoft\\Windows\\Containers\\BaseImages\\0949cec7-8165-4167-8c7d-67cf14eeede0\\BaseLayer\\Files” ,
...
}] ,
` } ,
...
} ,
...
“运行在Silo” :{
“SiloBaseOsPath” :“C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer\Files” ,
“通知SiloJob创建” :true ,
"文件系统层" : [{
“ID” :“8264f677-40b0-4ca5-bf9a-944ac2da8087” ,
"路径" :“C:\\” ,
“路径类型” :“绝对路径”
}]
} ,
...
} ,
...
}
此 JSON 是在 处创建的CmService!Container::Manager::Hcs::Details::GenerateCreateComputeSystemJson
。我们未能追踪任何有助于构建该配置的文件。
在开始分析 JSON 中有趣的字段之前,我们想提一下 Palo Alto Networks 的这篇文章。该文章解释了容器内部结构以及Job和Silo对象之间的关系。
第一个有趣的配置标签是RunInSilo
。此标签触发代码流,vmcompute
引导我们进入下一个堆栈跟踪:
3: kd > k
# Child-SP ReAddr 调用站点
00 ffff9a00`8da57648 fffff806`85d2b7fb wcifs!WcPortMessage
01 ffff9a00`8da57650 fffff806`85d63499 FLTMGR!FltpFilterMessage+ 0xdb
... (减少)
0b 0000004d`4218dbf0 00007ffa`08c5363d FLTLIB!FilterSendMessage+ 0x31
0c 0000004d`4218dc40 00007ffa`08c48686 wc_storage!WciSetupFilter+ 0x195
0d 0000004d`4218dcf0 00007ffa` 22e06496 wc_storage!WcAttachFilterEx+ 0x156
0e 0000004d`4218dee0 00007ffa`22de5a66 容器!容器::FilesystemProvider::Setup+ 0x15e
0f 0000004d`4218dfc0 00007ffa`22ded4ad 容器!container_runtime::CreateContainerObject+ 0x106
10 0000004d` 4218e010 00007ffa`22decf3c 容器!容器::CreateContainer+ 0x10d
11 0000004d` 4218e4 a0 00007ff6`fcf0bc7f 容器!WcCreateContainer+ 0x1c
12 0000004d` 4218e4 d0 00007ff6`fcf0c5c4 vmcompute!计算服务::JobUtilities::ConvertJobObjectToContainer+ 0xcb
13 0000004d` 4218e590 00007ff6`fce8573f vmcompute!ComputeService::JobUtilities::CreateSiloForIsolatedWorkerProcess+ 0x4dc
14 0000004d` 4218e8 c0 00007ff6`fce875c5 vmcompute!ComputeService::Management::Details::PrepareJobForWorkerProcess+ 0x17b
15 0000004d` 4218e9 a0 00007ff6`fcee6cbb vmcompute!计算服务::管理::详细信息::ConstructVmWorker+ 0xfd5
... (减少)
从堆栈中,我们可以了解到,每当计算系统收到 Silo 配置时,它都会通过调用创建并配置一个容器container!WcCreateContainer
。作为其配置的一部分,它还wcifs.sys
通过 与驱动程序进行通信FLTLIB!FilterSendMessage
。我们简要介绍一下这个驱动程序及其用途。
第二个有趣的特性是VirtualSmb
用于为之前提到的已安装基础层路径创建相应共享的标签。我们稍后也会讨论这一点。
容器隔离
从堆栈跟踪中我们可以看到,容器创建包括\WcifsPort
使用wcifs.sys
驱动程序Windows Container Isolation FS Filter Driver打开端口上的过滤器通信通道。这是用户模式代码与过滤器驱动程序通信的 常用方法。
此微过滤驱动程序在容器文件系统虚拟化的实现中起着重要作用。此驱动程序在客户机和主机中都充当此角色。
文件系统过滤驱动程序通常非常复杂,这个也不例外。幸运的是, Google Project Zero 的James Forshaw最近写了一篇很棒的文章,解释了 Windows FS 过滤驱动程序的低级设计,这有助于我们理解我们案例中的逻辑。
我们可以将驱动逻辑分为两部分:
- 驱动程序配置——配置取决于驱动程序是在客户机还是主机系统上运行。
- 处理操作回调,例如
WcPreCreate
、WcPostCreate
、WcPreRead
和WcPostRead
。这些回调包含主要逻辑、数据操作和适当的重定向。
我们将解释该驱动程序用来了解沙盒生态系统的一些方法。
初始配置
来宾配置
正如我们之前所说,主机和客户机都使用该驱动程序,但方式不同。
客户机通过注册表接收一组参数,用于其初始配置。其中一些参数位于,HKLM\SYSTEM\CurrentControlSet\Control
如下HKLM\SYSTEM\CurrentControlSet\Control\BootContainer
所示:
图 14 –HKLM\SYSTEM\CurrentControlSet\Control
配置值。
图 15 –HKLM\SYSTEM\CurrentControlSet\Control\BootContainer
配置值。
您可能会注意到我们之前在“真实” VHDx 文件中看到的IO_REPARSE_TAG_WCI_1
(代码)。此标记与我们在中看到的重新解析标记一起被硬编码到方法中:0x90001018
IO_REPARSE_TAG_WCI_LINK_1
BaseLayer.vhdx
wcifs!WcSetBootConfiguration
图 16 – 中的硬编码重新解析标签值WcSetBootConfiguration
。
客户机配置的第二个更重要的部分是wcifs!WcSetupVsmbUnionContext
,它在其中设置了一个称为Union ContextFltGetInstanceContext
的虚拟化层。在后台,驱动程序将自定义数据存储在多个上下文对象上,并使用适当的 NT API( 、PsGetSiloContext
和 )访问它们FltGetFileContext
。这些自定义对象包含 AVL 树和哈希表,以高效查找虚拟化层。
该WcSetupVsmbUnionContext
方法还有两个有趣的工件。一个是属于层的 vSMB 路径,另一个是HOST_LAYER_ID
我们之前在解析的 MFT 和描述虚拟机的 JSON 中看到的 GUID:
图 17 – 中的硬编码 vSMB 路径WcSetupVsmbUnionContext
。
图 18 – 的硬编码 GUID HOST_LAYER_ID
。
随着我们深入研究,我们发现有迹象表明虚拟 SMB方法用于在客户机和主机之间共享文件。很快我们就会发现 vSMB 是基础层实现和映射文件夹共享的主要方法。
主机配置
对于主机系统,主要配置发生在父计算进程vmcompute
启动容器创建并向发送自定义消息时\WcifsPort
。这会触发wcifs!WcPortMessage
发送到该特定端口的任何消息的回调例程。
下面是服务向过滤驱动发送的消息的部分重构:
WcifsPortMsg结构
{
DWORD 消息代码;
DWORD 消息大小;
WcifsPortMsgSetUnion 消息;
} ;
WcifsPortMsgSetUnion结构
{
DWORD 消息版本或代码;
DWORD 消息大小;
DWORD 数字联合;
wchar_t 实例名称[ 50 ] ;
DWORD 实例名称长度;
DWORD 重新解析标签;
DWORD 重新解析标签链接;
DWORD 不确定;
处理工作;
BYTE 上下文数据[ 1 ] ;
} ;
该ContextData
字段还包含联合应映射的设备路径。
操作回调
在注册期间,过滤驱动程序会为其想要拦截的每项操作提供一组回调。过滤管理器会在每项文件操作之前/之后调用这些回调,如下所示。
图 19 –迷你过滤器架构,由James Forshaw提供。
无需过多探讨技术细节,驱动程序定义并处理两个自定义重新解析标签:
- IO_REPARSE_TAG_WCI_1 – 这是指示磁盘上的文件实例是虚拟的,真实路径可以在其内部结构中找到的主要标记。此“转换”的示例用法如下:
- 客户机将文件从其本机路径转换
C:\Windows\system32\kernel32.dll
为 vSMB 路径\Device\vmsmb\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\os\Windows\System32\kernel32.dll
。 - 主机将文件从基础层设备路径转换
C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer\Files\Windows\System32\en-US\apphelp.dll.mui
为真实路径C:\Windows\System32\en-US\apphelp.dll.mui
。
这种转换非常有趣,因为它主要发生在基础层中包含此重新解析标记的空系统文件夹中(例如文件en-US
夹)。
- 客户机将文件从其本机路径转换
- IO_REPARSE_TAG_WCI_LINK_1 – 据我们所知,此标签仅在主机上使用,并将系统文件从基础层设备路径链接
C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer\Files\Windows\System32\kernel32.dll
到真实路径C:\Windows\System32\kernel32.dll
。与上一点相比,此示例 DLL 文件条目确实存在于基础层中,并且具有此重新解析标签。
发现 vSMB 是操作系统基础层共享的主要方法,这令人颇感意外。既然我们知道它是生态系统中一种至关重要的通信方法,那么下一步自然就是进一步深入挖掘。
(v)SMB 文件共享
在沙盒安装过程中,我们注意到通过调用存储提供程序设备vmcompute
创建了多个虚拟共享,并发送了 IOCTL 。此类调用的示例路径可能如下所示:。CreateFileW
0x240328
\??\STORVSP\VSMB\??\C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer\Files
创建这些共享的方法是vmcompute!ComputeService::Storage::OpenVsmbRootShare
。我们可以在下一个堆栈跟踪中看到它的流程:
3: kd > k
# Child-SP ReAddr 调用站点
00 ffff9a00`8d48a178 fffff806`85fd6af8 storvsp!VspFileCreate
01 (内联函数)--------`-------- Wdf01000!FxFileObjectFileCreate::Invoke+ 0x29 [ minkernel\wdf\framework\shared\inc\private\common\ FxFileObjectCallbacks.hpp @ 58 ]
... (减少)
11 0000004d`4210d690 00007ff6`fcf33700 KERNELBASE!CreateFileW+ 0x66
12 0000004d`4210d6f0 00007ff6`fceb8180 vmcompute!计算服务::存储::OpenVsmbRootShare+ 0x3ac
13 0000004d`4210d850 00007ff6`fceba0fc vmcompute!计算服务::虚拟机::详细信息::配置VSMB+ 0x598
14 0000004d`4210da30 00007ff6`fceba908 vmcompute!计算服务::虚拟机::详细信息::初始化设备设置+ 0x918
15 0000004d`4210eb90 00007ff6`fce86abd vmcompute!计算服务::虚拟机::创建虚拟机配置+ 0x68
16 0000004d`4210ebe0 00007ff6`fcee6cbb vmcompute!计算服务::管理::详细信息::ConstructVmWorker+ 0x4cd
... (减少)
此外,当我们使用 WSB 文件配置将主机文件夹映射到客户机时,也会调用相同的方法。例如,映射Sysinternals文件夹会导致对驱动程序的下一个调用:\??\STORVSP\VSMB\??\C:\Users\hyperv-root\Desktop\SysinternalsSuite
。
通过 (v)SMB 访问文件
创建这些共享后,我们可以通过创建的别名在客户机中访问它们。我们可以使用该type
命令打印kernel32.dll
主机的以下路径\\.\vmsmb\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\os\Windows\System32\kernel32.dll
:
图 20 – 访问 vSMB 共享。
为了提供 vSMB 文件,vmusrv
作为 VM 工作进程一部分的模块会创建一个工作线程。该模块是一个用户模式 vSMB 服务器,它在例程中直接从 VMBus 请求数据包vmusrv!VSmbpWorkerRecvLoop
,然后继续处理这些数据包。
服务创建文件操作
每当vmusrv
收到创建SMB 请求时,它都会向存储提供程序驱动程序发起新请求。这样的调用可能如下所示:
2:kd >;k
# Child-SP ReAddr 调用站点
... (减少)
0c ffff9a00`8d9522e0 fffff806`892c4741 storvsp!VspVsmbCommonRelativeCreate+ 0x369
0d ffff9a00`8d952510 fffff806`892c3b7e storvsp!VspVsmbHandleRelativeCreateFileRequest+ 0x321
0e ffff9a00`8d952790 fffff806`892c0f85 storvsp!VspVsmbDispatchIoControlForProcess+ 0x11e
0f ffff9a00`8d9527e0 fffff806` 8100e522 storvsp!VspFastIoDeviceControl+ 0x175
... (减少)
13 000000ae`9c0ff298 00007ffa`110c0c0a ntdll!NtDeviceIoControlFile+ 0x14
14 000000ae`9c0ff2a0 00007ffa`110c0456 vmusrv!CShare::OpenFileRelativeToShareRootInternal+ 0x306
15 000000ae`9c0ff3e0 00007ffa`110b9381 vmusrv!CShare::OpenFileRelativeToShareRoot+ 0x356
16 000000ae`9c0ff510 00007ffa`110b4451 vmusrv!CFSObject::CreateFileW+ 0x185
17 000000ae`9c0ff690 00007ffa`1109a568 vmusrv!CShare::创建+ 0x91
18 000000ae`9c0ff740 00007ffa`1109d74d vmusrv!ProviderCallback_Create+ 0x30
19 000000ae`9c0ff780 00007ffa`1109c299 vmusrv!SrvCreateFile+ 0x331
1a 000000ae`9c0ff860 00007ffa`1109c6f0 vmusrv!Smb2ExecuteCreateReal+ 0x111
1b 000000ae`9c0ff940 00007ffa`110a08da vmusrv!Smb2ExecuteCreate+ 0x30
1c 000000ae`9c0ff970 00007ffa` 11098907 vmusrv!Smb2ExecuteProviderCallback+ 0x7e
1d 000000ae`9c0ff9d0 00007ffa` 11088311 vmusrv!Smb2PacketProcessing+ 0x97
1e 000000ae`9c0ffa40 00007ffa` 11087225 vmusrv!Smb2PacketProcessingCallback+ 0x11
... (减少)
与存储提供程序的通信是通过代码中的IOCTL完成的0x240320
,而引用的句柄是在初始化阶段打开的vSMB路径:
图 21 – 引用 IOCTL 的句柄。
如果我们仔细观察storvsp!VspVsmbCommonRelativeCreate
,就会发现每次执行后都会调用nt!IoCreateFileEx
。此调用包含所需文件的相对路径,以及一个附加RootDirectory
字段,该字段表示\Files
已安装的基础层 VHDx 中的文件夹:
IoCrateFileEx
图22 – 执行storvsp.sys
。
提供读/写操作
中的工作线程执行读/写操作vmusrv!CFSObject::Read/vmusrv!CFSObject::Write
。如果文件足够小,线程只需ReadFile/WriteFile
在句柄上执行。否则,它将文件映射到内存,并通过VMBus 上的RDMAvmusrv!SrvConnectionExecuteRdmaTransfer
高效传输。此传输在 执行,而 RDMA 通信是使用 IOCTL或 与RootVMBus
设备(主机 VMBus 设备名称)进行的。0x3EC0D3
0x3EC08C
2:kd >;k
... (减少)
06 ffffad0e`3bee7650 fffff800`36225b62 vmbusr!RootIoctlRdmaFileIoHandleMappingComplete+ 0x10f
07 ffffad0e`3bee7690 fffff800`361fee21 vmbusr!RootIoctlRdmaFileIo+ 0xf2
08 ffffad0e`3bee76f0 fffff800`339da977 vmbusr!RootIoctlDeviceControlPreprocess+ 0x191
... (减少)
12 00000009 `ae27f7e8 00007ffe`281ce773 ntdll!NtDeviceIoControlFile+ 0x14
13 00000009 `ae27f7f0 00007ffe`281dcbd2 vmusrv!SrvConnectionExecuteRdmaTransfer+ 0x24f
14 00000009 `ae27f940 00007ffe`281d4874 vmusrv!CFile::ReadFileRdma+ 0xc2
15 00000009 `ae27f9c0 00007ffe`281c218e vmusrv!CFSObject::Read+ 0x94
16 00000009 `ae27fa00 00007ffe`281c08da vmusrv!Smb2ExecuteRead+ 0x1be
17 00000009 `ae27fa60 00007ffe`281b8907 vmusrv!Smb2ExecuteProviderCallback+ 0x7e
18 00000009 `ae27fac0 00007ffe`281a6a4e vmusrv!Smb2PacketProcessing+ 0x97
19 00000009 `ae27fb30 00007ffe`3bba6fd4 vmusrv!SmbWorkerThread+ 0xce
... (减少)
图 23\Device\RootVmBus\rdma\494
–读/写操作的通信。
访客到主人的流程
根据本文解释关系的一些见解,我们Storvsc.sys/Storvsp.sys
可以将所有先前的技术块结合到下一个文件访问流中。
图 24 – 文件访问流程。
- 我们用命令
type
打开并打印文件内容kernel32.dll
。这是一个系统文件,因此沙盒不拥有它的副本,而是使用主机的副本。 - 客户机不知道该文件不存在,因此它通过文件系统驱动程序堆栈直到存储驱动程序堆栈执行正常的文件访问。
- Hyper-V 存储消费者
Storvsc.sys
是一个微型端口驱动程序,这意味着它充当来宾的虚拟存储。它通过 VMBus 接收和转发 SCSI 请求。 - 存储提供程序
Storvsp.sys
有一个工作线程,正在通过 VMBus 监听新消息storvsp!VspPvtKmclProcessingComplete
。 - 提供程序解析 VMBus 请求,并将其传递给
vhdparser!NVhdParserExecuteScsiRequestDisk
执行vhdmp.sys
VHD 解析器驱动程序。 - 最后,通过过滤管理器
vhdmp.sys
访问的物理实例sandbox.vhdx
,并执行读/写操作。在这种情况下,它读取客户文件系统过滤管理器请求的数据。该数据返回给过滤管理器进行进一步分析。 - 如前所述,返回的条目带有 WCI 重解析标记和主机层 GUID。在
wcifs.sys
对文件执行创建后操作时,它会查找该设备的联合上下文,并用下一个文件对象替换该文件对象:\Device\vmsmb\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\os\Windows\System32\kernel32.dll
- 该
\Device\vmsmb
设备被创建为 SMB 共享,因此筛选器管理器可以像访问任何其他普通共享一样访问它。在后台,它通过 VMBus 向主机执行 SMB 请求。 - vSMB 用户模式服务器在其工作线程方法中
vmusrv.dll
轮询\\.\VMbus\
设备以获取新消息vmusrv!SmbWorkerThread
。 - 正如我们之前所展示的,在创建操作中,服务器通过已安装的 OS 基础层的句柄上的 IOCTL 与存储提供程序进行通信:
\Device\STORVSP\VSMB\??\C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer\Files
- 存储提供程序通过 执行文件请求
IoCreateFileEx
。该请求是相对的,并且包含RootDirectory
已安装操作系统层的 。这会触发筛选器管理器在已安装操作系统层中打开文件。 - 与步骤(7)类似,返回的条目包含 WCI 重解析标记,这会导致
wcifs.sys
在 post-create 方法中更改文件对象。它将文件对象更改为其物理路径:C:\Windows\System32\kernel32.dll
- 访问主机
kernel32.dll
文件,并返回给客户机。 - 对于
ReadFile
操作,wcifs.sys
驱动程序会在文件对象顶部保存上下文状态,以帮助其执行读/写操作。此外,工作线程vmusrv
要么直接访问文件,要么通过 VMBus 顶部的 RDMA 执行读取请求。
实际过程要复杂得多,因此我们试图专注于虚拟化的关键组件。
沙盒还允许通过其配置将文件夹从主机映射到客户机。此类文件夹会收到 vSMB 路径的唯一别名,并且访问方式与 OS 层类似。唯一的区别是,路径在客户机过滤器管理器中由 更改bindflt.sys
。
例如,如果我们将SysinternalsSuite文件夹映射到客户机桌面文件夹,则路径C:\Users\WDAGUtilityAccount\Desktop\SysinternalsSuite\Procmon.exe
将更改为\Device\vmsmb\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\db64085bcd96aab59430e21d1b386e1b37b53a7194240ce5e3c25a7636076b67\Procmon.exe
,而其余过程保持不变。
玩沙盒
这项研究的目标之一是根据我们的需求修改基础层内容。现在我们了解了生态系统,这似乎相当容易。
修改有几个简单的步骤:
- 停止
CmService
,创建并维护基础层的服务。当服务卸载时,也会移除基础层的挂载。 - 安装基础层(它在
C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer.vhdx
文件中)。这可以通过双击或使用diskmgmt.msc
实用程序来完成。 - 对基础层进行修改。在我们的例子中,我们添加了所有 FLARE 安装后文件。
- 卸下基础层。
- 开始
CmService
。
当我们启动沙箱时,我们就有了超棒的 FLARE VM!
图 25 – Windows Sandbox 上的 FLARE VM。
概括
当我们开始研究 Windows Sandbox 时,我们并不知道这样一个“简单”的操作会归结为一个复杂的流程,其中包含多项 Microsoft 内部未记录的技术,例如 vSMB 和容器隔离。
链接
Hyper-V VmSwitch RCE 漏洞
https://www.youtube.com/watch?v=025r8_TrV8I
Windows 沙盒
Windows Sandbox - Microsoft Community Hub
Windows 沙盒 WSB 配置
Windows Sandbox configuration - Windows Security | Microsoft Learn
Windows 容器
- About Windows containers | Microsoft Learn
- What I Learned from Reverse Engineering Windows Containers
NTFS 属性
SOLVED: All NTFS Attributes Defined – Up & Running Inc – Tech How To's
重新解析点
Reparse Points - Win32 apps | Microsoft Learn
NTFS 文档
http://dubeyko.com/development/FileSystems/NTFS/ntfsdoc.pdf
NTFS 重新解析标签
[MS-FSCC]: Reparse Tags | Microsoft Learn
VHDx 父级定位器
[MS-VHDX]: VHDX Parent Locator | Microsoft Learn
FS 过滤驱动 – 用户态与内核态之间的通信
寻找 Windows Mini-Filter 驱动程序中的错误
https://googleprojectzero.blogspot.com/2021/01/hunting-for-bugs-in-windows-mini-filter.html
Hyper-V Storvsp.sys-Strovsc.sys 流程
https://www.linkedin.com/pulse/hyper-v-architecture-internals-pravin-gatale/
微软对 RDMA 的解释
Host network requirements for Azure Stack HCI - Azure Stack HCI | Microsoft Learn