旧应用的 AppContainer

AppContainer 环境是一种限制性的进程执行环境,可用于旧应用以提供资源安全性。 AppContainer 应用的进程及其子进程在轻量级应用容器中运行,其中它们只能访问专门授予它们的资源。 使用文件系统和注册表虚拟化对它们进行隔离。 因此,在 AppContainer 中实现的应用无法受到黑客攻击,不会允许在有限分配的资源之外执行恶意操作。

对于打包和未打包的应用,AppContainer 都代表着良好、安全的工程实践。

提示

AppContainer 最初名为 LowBox(在 Windows 8 发布之前)。 可以在 API 名称(如 NtCreateLowBoxToken)中看到该旧名称。

封装应用

可以找一个使用 MSIX 打包的应用,并轻松地将其配置为在 AppContainer 环境中运行。 通用 Windows 平台 (UWP) 应用自动成为 AppContainer 应用。 但是,还可以将使用 MSIX 打包的桌面应用配置为 AppContainer 应用。 如果使用 MSIX 打包,则使用 AppContainer 就特别容易。 有关详细信息、方案和配置示例,请参阅 MSIX AppContainer 应用

未打包的应用

未打包的应用也可以在应用容器中运行。 要在应用容器中创建进程,需要 AppContainer 定义(或配置文件)。 这就是为什么在 AppContainer 中使用打包的应用更加容易。 为用户注册某个包时,部署堆栈会为你调用某些 Win32 API,以便创建必要的 AppContainer 配置文件(例如 CreateAppContainerProfile)。 当你为用户注销包时,部署堆栈会执行移除 AppContainer 配置文件(DeleteAppContainerProfile)的工作。 如果不打包应用,则你必须自己调用这些 Win32 API 来完成相同的任务,但它可能很复杂。

大多数使用低完整性级别的未打包应用现在会使用 AppContainer 作为提供受限执行环境的更佳方法。

在应用容器中运行的解包进程调用 CreateProcess 时,子进程通常会继承父级的令牌。 该令牌包括完整性级别 (IL) 和应用容器信息。 最好不要考虑具有 elevated/medium/low/appContainer 值的单轴。 相反,是否在应用容器中是第二个且正交的属性。 也就是说,如果你应用容器中,则完整性级别 (IL) 始终为

使用 AppContainer 环境的好处

AppContainer 环境的主要目标是尽可能多地将应用状态与系统状态分开,同时保持与其他应用的兼容性。 Windows 通过检测和重定向它在运行时对文件系统和注册表所做的某些更改(称为虚拟化)来完成此目标。 AppContainer 应用写入到其自身的虚拟注册表和应用程序数据文件夹,卸载或重置应用时会删除该数据。 其他应用无法访问 AppContainer 应用的虚拟注册表或虚拟文件系统。

因此,AppContainer 环境提供了安全的应用沙盒。 隔离应用,使其在没有特定权限时无法访问硬件、文件、注册表、其他应用、网络连接和网络资源。 可以在不公开其他资源的前提下面向单个资源。 此外,用户标识通过唯一标识进行保护,该标识是用户和应用的串联,资源的授予使用最低特权模型。 这可进一步防止应用模拟用户访问其他资源。

测试在应用容器中运行的示例代码

在 C# 或 C++ 项目中,可以使用下面的相应代码示例之一来确定进程是否在应用容器中运行。 对于每个示例,在代码运行后,如果 isAppContainer 的值不为零(或 true),则进程在应用容器中运行。

C# (P/Invoke)

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetCurrentProcess();

[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool OpenProcessToken(
    IntPtr ProcessHandle,
    UInt32 DesiredAccess,
    out IntPtr TokenHandle);

[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetTokenInformation(
    IntPtr TokenHandle,
    uint TokenInformationClass,
    out uint TokenInformation,
    uint TokenInformationLength,
    out uint ReturnLength);

UInt32 TOKEN_QUERY = 0x0008;
IntPtr tokenHandle;

if (!OpenProcessToken(
    GetCurrentProcess(),
    TOKEN_QUERY,
    out tokenHandle))
{
    // Handle the error.
}

uint isAppContainer;
uint TokenIsAppContainer = 29;
uint tokenInformationLength = sizeof(uint);

if (!GetTokenInformation(
    tokenHandle,
    TokenIsAppContainer,
    out isAppContainer,
    tokenInformationLength,
    out tokenInformationLength))
{
    // Handle the error.
}

C++ (WIL)

此示例使用 Windows 实现库 (WIL)。 安装 WIL 的一种便捷方法是转到 Visual Studio,单击“项目”>“管理 NuGet 包...”>“浏览”,在搜索框中键入或粘贴 Microsoft.Windows.ImplementationLibrary,选择搜索结果中的项,然后单击“安装”以安装该项目的包。

#include <wil\token_helpers.h>
...
bool isAppContainer = wil::get_token_is_app_container();

函数 wil::get_token_is_app_container_nothrowwil::get_token_is_app_container_failfast 提供了替代性的错误处理策略。 有关详细信息,请参阅 wil\token_helpers.h

C++ (canonical)

#include <windows.h>
...
HANDLE tokenHandle{};
DWORD isAppContainer{};
DWORD tokenInformationLength{ sizeof(DWORD) };

if (!::OpenProcessToken(
    GetCurrentProcess(),
    TOKEN_QUERY,
    &tokenHandle))
{
    // Handle the error.
}

if (!::GetTokenInformation(
    tokenHandle,
    TOKEN_INFORMATION_CLASS::TokenIsAppContainer,
    &isAppContainer,
    tokenInformationLength,
    &tokenInformationLength
))
{
    // Handle the error.
}

本部分内容

有关将 AppContainer 用于旧应用的详细信息,请参阅以下主题。

主题 说明
AppContainer 隔离 隔离是 AppContainer 执行环境的主要目标。 通过将应用与不需要的资源和其他应用隔离,恶意操作的机会可以降到最小。 基于最低特权授予访问权限可防止应用和用户访问超出其权限的资源。 控制对资源的访问可保护进程、设备和网络。
实现 AppContainer AppContainer 的实现方法是将新信息添加到进程令牌、更改 SeAccessCheck(),使得默认情况下,所有旧的未经修改的访问控制列表 (ACL) 对象都会阻止来自 AppContainer 进程的访问请求,并针对应提供给 AppContainers 的对象重新进行 ACL 操作。