运行沙盒
运行Windows Sandbox应用程序会触发执行流程,我们将在此不再赘述。我们只是提到该流程导致CmService通过RPC调用执行vmcompute!HcsRpc_CreateSystem。另一个关键服务vmcompute.exe,可运行和协调主机上的所有计算系统(容器)。
在我们的例子中,CreateSystem命令还接收描述所需计算机的下一个配置JSON:
注意:为了便于阅读,JSON被省略了,你可以在附录A中访问完整的JSON。
此JSON是在CmService!Container::Manager::Hcs::Details::GenerateCreateComputeSystemJson创建的。我们没有设法跟踪任何有助于构建该配置的文件。
在开始分析JSON中有趣的字段之前,首先请你阅读Palo Alto Networks的这篇文章,该文介绍了容器的内部结构,以及Job和Silo对象之间的关系。
第一个有趣的配置标签是RunInSilo,该标签触发vmcompute中的代码流,该代码流将导致我们进入下一个堆栈跟踪:
从堆栈中,我们可以了解到,每当计算系统接收到筒仓配置时,它就会通过container!WcCreateContainer调用来创建和配置容器。作为其配置的一部分,它还通过FLTLIB!FilterSendMessage与wcifs.sys驱动程序进行通信,我们将在短期内解释此驱动程序及其目的。
第二个有趣的功能是VirtualSmb标签,用于为我们前面提到的已安装基础层路径创建相应的共享,后面我们很快也会再讲到这一点。
容器隔离(Container Isolation)
正如我们在堆栈跟踪中所看到的,容器的创建包括使用wcifs.sys驱动程序Windows容器隔离FS筛选器驱动程序打开 \WcifsPort端口上的筛选器通信通道,这是用户模式代码与过滤器驱动程序进行通信的常用方法。
这个微型过滤器驱动程序在容器文件系统虚拟化的实现中具有重要的作用。该驱动程序在用户和主机中均担当此角色。
文件系统过滤器驱动程序通常非常复杂,这也不例外。幸运的是,Google Project Zero的James Forshaw最近写了一篇很棒的文章,解释了Windows FS筛选器驱动程序的低级设计,这有助于我们了解案例的逻辑。
我们可以将驱动程序逻辑分为两部分:
驱动程序配置:配置取决于驱动程序是在客户机系统上还是在主机系统上运行。
处理操作回调,例如WcPreCreate,WcPostCreate,WcPreRead和WcPostRead。这些回调包含主要逻辑、数据操作和适当的重定向。
我们将解释该驱动程序用来了解沙盒生态系统的一些方法。
初始配置
用户配置
如上所述,主机和用户都使用此驱动程序,但是方式不同。
用户通过注册表接收一组用于其初始配置的参数。其中一些参数位于HKLM\SYSTEM\CurrentControlSet\Control 和HKLM\SYSTEM\CurrentControlSet\Control\BootContainer,如下所示:
HKLM\SYSTEM\CurrentControlSet\Control 配置值
HKLM\SYSTEM\CurrentControlSet\Control\BootContainer配置值
你可能会注意到IO_REPARSE_TAG_WCI_1(代码0x90001018),我们之前在“真实的” VHDx文件中看到过。该标签以及IO_REPARSE_TAG_WCI_LINK_1(我们在BaseLayer.vhdx中将其视为重新分析标签)一起被硬编码到wcifs!WcSetBootConfiguration方法中:
WcSetBootConfiguration中的硬编码重解析标签值
用户配置的第二个更重要的部分是在wcifs!WcSetupVsmbUnionContext中,在这里它建立了一个称为联合上下文的虚拟化层。在后台,驱动程序将自定义数据存储在多个上下文对象上,并使用适当的NT API(FltGetInstanceContext,PsGetSiloContext和FltGetFileContext)访问它们。这些自定义对象包含AVL树和哈希表,以有效地查找虚拟化层。
WcSetupVsmbUnionContext方法具有两个更有趣的构件。一个是属于该层的vSMB路径,另一个是HOST_LAYER_ID GUID,我们先前在解析的MFT和描述虚拟机的JSON中看到过:
WcSetupVsmbUnionContext中的硬编码vSMB路径
HOST_LAYER_ID的硬编码GUID
当我们深入研究时,我们看到有迹象表明使用虚拟SMB方法在用户和主机之间共享文件。很快,我们将看到vSMB是实现基础层和映射文件夹共享的主要方法。
主机配置
对于主机系统,主要配置发生在父计算进程vmcompute,启动容器创建并将自定义消息发送到\ WcifsPort时。这将触发wcifs!WcPortMessage,这是发送到该特定端口的任何消息的回调例程。
以下是该服务发送给筛选器驱动程序的消息的部分重构:
ContextData字段还包含联合应映射的设备路径
操作回调
在注册过程中,筛选器驱动程序为其要拦截的每个操作提供一组回调。过滤器管理器在每个文件操作之前/之后调用这些回调,如下所示。
微型过滤器架构
在不花太多精力研究技术细节的情况下,驱动程序定义并处理了两个自定义的重解析标签:
IO_REPARSE_TAG_WCI_1:这是主要标签,指示磁盘上的文件实例是虚拟的,并且可以在其内部结构中找到真实路径,此“转换”的示例用法如下:
1.用户将文件从其本地路径C:\Windows\system32\kernel32.dll转换为vSMB路径\Device\vmsmb\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\os\Windows\System32\kernel32.dll。
2.主机从基础层设备路径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通过将CreateFileW调用到存储提供程序设备来创建多个虚拟共享,并发送IOCTL 0x240328。这样的调用的示例路径可能看起来如下所示: \??\STORVSP\VSMB\??\C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer\Files。
创建这些共享的方法是 vmcompute!ComputeService::Storage::OpenVsmbRootShare。我们可以在下一个堆栈跟踪中看到它的流程:
另外,当我们使用WSB文件配置将主机文件夹映射到用户时,将调用相同的方法。例如,映射Sysinternals文件夹将导致对驱动程序的下一个调用:\??\STORVSP\VSMB\??\C:\Users\hyperv-root\Desktop\SysinternalsSuite。
通过(v)SMB访问文件
创建这些共享后,我们可以通过创建的别名在用户中访问它们。我们可以使用type命令来打印主机的内核32.dll,下一个路径是\\.\vmsmb\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\os\Windows\System32\kernel32.dll:
访问vSMB共享
为了提供vSMB文件,作为VM工作进程一部分的vmusrv模块创建了工作线程。此模块是用户模式vSMB服务器,它通过vmusrv!VSmbpWorkerRecvLoop例程直接从VMBus请求数据包,然后继续处理数据包。
服务创建文件操作
每当vmusrv收到“创建SMB”请求时,它都会向存储提供程序驱动程序发起一个新请求。这样的调用可能看起来像这样:
与存储提供者的通信是通过代码为0x240320的IOCTL完成的,而引用的句柄是在初始化阶段打开的vSMB路径:
引用了IOCTL的句柄
如果我们仔细观察storvsp!VspVsmbCommonRelativeCreate,我们会看到每次执行后都会调用nt!IoCreateFileEx。该调用包含所需文件的相对路径以及一个附加的RootDirectory字段,该字段表示已安装的基础层VHDx中的\ Files文件夹:
通过storvsp.sys.执行IoCrateFileEx
服务读/写操作
读/写操作由 vmusrv!CFSObject::Read/vmusrv!CFSObject::Write中的工作线程执行。如果文件足够小,则线程仅在句柄上执行ReadFile / WriteFile。否则,它将文件映射到内存,并通过VMBus上的RDMA有效地传输文件。在vmusrv!SrvConnectionExecuteRdmaTransfer上执行此传输,而RDMA通信是使用IOCTL 0x3EC0D3或0x3EC08C与RootVMBus设备(主机VMBus设备名)完成的。
与\ Device \ RootVmBus \ rdma \ 494通信以进行读/写操作
用户到主机(Guest-to-Host)流程
基于本文中解释Storvsc.sys / Storvsp.sys关系的一些见解,我们可以将所有先前的技术块组合到下一个文件访问流。
文件访问流程
1.我们使用命令类型打开并打印kernel32.dll文件的内容,这是一个系统文件,因此沙盒不拥有其副本,而是使用主机的副本。
2.由于用户系统不知道该文件不存在,因此它将通过文件系统驱动程序堆栈(直到存储驱动程序堆栈)执行正常的文件访问。
3.Hyper-V存储使用方Storvsc.sys是一个微型端口驱动程序,这意味着它充当用户的虚拟存储,它通过VMBus接收并转发SCSI请求。
4.存储提供程序Storvsp.sys具有工作线程,该线程在storvsp!VspPvtKmclProcessingComplete上通过VMBus侦听新消息。
5.提供程序解析VMBus请求,并将其传递给vhdparser!NVhdParserExecuteScsiRequestDisk,后者执行VHD解析器驱动程序vhdmp.sys。
6.最终,vhdmp.sys通过过滤器管理器访问sandbox.vhdx的物理实例,并执行读/写操作。在本例中,它将读取用户文件系统过滤器管理器请求的数据,该数据将返回到过滤器管理器以进行进一步分析。
7.如前所述,返回的条目使用WCI 重新解析标签和主机层GUID进行了标记。当wcifs.sys在文件上执行其创建后(post-create)操作时,它将查找该设备的联合上下文,并将文件对象替换为下一个:\Device\vmsmb\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\os\Windows\System32\kernel32.dll。
8.\ Device \ vmsmb设备被创建为SMB共享,因此筛选器管理器像访问其他任何普通共享一样对其进行访问。在后台,它通过VMBus向主机执行SMB请求。
9.vSMB用户模式服务器vmusrv.dll在其工作线程方法vmusrv!SmbWorkerThread中轮询\\.\VMbus\设备以查找新消息。
10.如前所述,在创建操作中,服务器通过已安装的操作系统基础层句柄上的IOCTL与存储提供程序进行通信:\Device\STORVSP\VSMB\??\C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer\Files。
11.存储提供程序通过IoCreateFileEx执行文件请求,该请求是相对的,并且包含已安装的操作系统层的RootDirectory,这将触发过滤器管理器在已安装的操作系统层中打开文件。
12.与步骤(7)相似,返回的条目包含一个WCI reparse标签,该标签使wcifs.sys在post-create方法中更改文件对象。它将文件对象更改为其物理路径:C:\Windows\System32\kernel32.dll。
13.访问主机kernel32.dll文件,然后返回到用户。
14.对于ReadFile操作,wcifs.sys驱动程序将上下文状态保存在文件对象的顶部,以帮助其执行读/写操作。此外,辅助线程vmusrv通过直接访问文件或通过顶级VMBus上的RDMA执行读取请求。
实际过程要复杂得多,因此我们尝试着重于对虚拟化至关重要的组件。
沙盒还允许通过其配置将文件夹从主机映射到用户,这样的文件夹接收vSMB路径的唯一别名,并且访问类似于操作系统层。唯一的区别是该路径是由bindflt.sys在用户过滤器管理器中更改的。
例如,如果我们将SysinternalsSuite文件夹映射到用户Desktop文件夹,则路径
C:\Users\WDAGUtilityAccount\Desktop\SysinternalsSuite\Procmon.exe更改为\Device\vmsmb\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\db64085bcd96aab59430e21d1b386e1b37b53a7194240ce5e3c25a7636076b67\Procmon.exe,它使进程的其余部分保持不变。
自定义沙盒
本研究的目标之一是根据需要修改基础层的内容,在了解了其基础结构后,自定义沙盒的功能似乎很容易。
只需几个简单的步骤即可实现自定义过程:
1.停止创建和维护基础层的服务CmService,当服务被卸载时,它也会删除基础层安装。
2.安装基础层位于C:\ProgramData\Microsoft\Windows\Containers\BaseImages\0949cec7-8165-4167-8c7d-67cf14eeede0\BaseLayer.vhdx文件中,可以通过双击或使用diskmgmt.msc实用程序来完成。
3.对基础层进行修改,在我们的案例中,我们添加了所有FLARE安装后(post-installation)文件。
4.卸载基础层。
5.启动CmService。
6.启动沙盒的那一刻,我们实现了很棒的FLARE VM!
FLARE VM是一款免费开放的基于Windows的安全分发版,专为逆向工程师,恶意软件分析师,取证人员和渗透测试人员而设计。受到基于Linux的开放源代码启发,如Kali Linux,REMnux等,FLARE VM提供了一个完全配置的平台,包括Windows安全工具的全面集成,如调试器、反汇编器、反编译器、静态和动态分析工具、网络分析和操作、网络评估、开发、漏洞评估应用程序等等。
本文翻译自:https://research.checkpoint.com/2021/playing-in-the-windows-sandbox/如若转载,请注明原文地址