Windows访问权限控制

memetao 于 2024-09-03 发布

前言

在做windows桌面程序画面采集的时候经常会遇到”GPU打满, 普通进程获取不到GPU的调度机会, Dxgi\D3D11相关的API会发生上百ms的阻塞”。D3DK给了用户调整GPU调度优先级的API,但是在部分windows(不是少列)上调整优先级并不能生效,本文基于此背景探索一下windows权限控制的原理。

OpenProcess

A进程

CreateWindow();
while (GetMessageW(&msg, nullptr, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessageW(&msg);
    // using namespace std::chrono_literals;
    // std::this_thread::sleep_for(3000ms);
    LOG_INFO("Alive %s", (mlib::Timestamp::now() - t0).to_str().c_str());
}

B进程

::OpenProcess(Flag, false, process_a);

以普通权限启动A\B: | Flag | Ret | | ——————————— | — | | PROCESS_QUERY_LIMITED_INFORMATION | Y | | PROCESS_QUERY_INFORMATION | Y | | PROCESS_CREATE_THREAD | Y | | PROCESS_ALL_ACCESS | Y |

管理员启动A, 普通权限启动B:

Flag Ret
PROCESS_QUERY_LIMITED_INFORMATION Y
PROCESS_QUERY_INFORMATION N
PROCESS_CREATE_THREAD N
PROCESS_ALL_ACCESS N

管理员启动A, 普通权限启动B后提权:


Flag Ret
PROCESS_QUERY_LIMITED_INFORMATION Y
PROCESS_QUERY_INFORMATION N
PROCESS_CREATE_THREAD N
PROCESS_ALL_ACCESS N

访问控制模型

访问控制模型有两个主要的组成部分,访问令牌(Access Token)和安全描述符(Security Descriptor),它们分别是访问者和被访问者拥有的东西。通过访问令牌和安全描述符的内容,Windows可以确定持有令牌的访问者能否访问持有安全描述符的对象。

用户登录时,系统将创建一个访问令牌,用户通过使用令牌的副本去创建和访问进程。访问令牌包含安全标识符,包含:

当进程尝试访问安全对象或执行需要特权的系统管理任务时,系统将使用此令牌来标识关联的用户是否拥有相应的权限。

安全对象

具有安全描述符的对象就是安全对象。window上的安全对象有:

创建安全对象后,系统会为其分配安全描述符,该描述符包含由其创建者指定的安全信息(未指定则为默认值)。应用程序可以使用函数来检索和设置现有对象的安全性信息。安全描述符标识指出对象的所有者,并且还可以包含以下访问控制列表:

ACL包含访问控制项(access control entries)(ACEs)的列表。每个ACE指定一组访问权限,并包含一个SID,用于标识其权限被允许、拒绝或审核的受托者。受托者可以是用户帐户、组帐户或登录会话。

Windows访问控制流程:

当一个线程尝试去访问一个对象时,系统会检查线程持有的令牌以及被访问对象的安全描述符中的DACL。 如果安全描述符中不存在DACL,则系统会允许线程进行访问。如果存在DACL,系统会顺序遍历DACL中的每个ACE(ACE是ACL中一个一个的条目),检查ACE中的SID在线程的令牌中是否存在。以访问者中的User SID或Group SID作为关键字查询被访问对象中的DACL。顺序是:先查询类型为DENY的ACE,若命中且权限符合则访问拒绝;未命中再在ALLOWED类型的ACE中查询,若命中且类型符合则可以访问;以上两步后还没命中那么访问拒绝。

### 访问令牌(Access Token)

内容 说明
用户身份 你是谁(SID)
所属的组 你是哪个部门(组SID)
权限清单 你可以干什么(关机/调试等)
完整性级别 你是实习生/正式员工/经理(低/中/高)
默认DACL 给你新建的资源的默认权限设置

与特定的windows账户关联,账户环境下启动的所有进程都会获得该令牌的副本,进程中的线程默认获得这个令牌,用于描述”安全上下文”。 令牌中的信息包括与进程或线程关联的用户帐户的标识和特权信息。

当线程与安全对象进行交互或尝试执行需要特权的系统任务时,系统使用访问令牌来标识用户。访问令牌包含以下信息:

产生AccessToken的过程:

Access Token分两种:

主令牌是由windows内核创建并分配给进程的默认访问令牌,每一个进程有一个主令牌,它描述了与当前进程相关的用户帐户的安全上下文。同时,一个线程可以模拟一个客户端帐户,允许此线程与安全对象交互时用客户端的安全上下文。一个正模拟客户端的线程拥有一个主令牌和一个模拟令牌。

安全描述符(Security Descriptors,SD)

如果访问令牌是「你是谁」的身份证明,那安全描述符就是“门禁系统”

安全描述符是与被访问对象关联的,它含有这个对象所有者的SID,以及一个访问控制列表(ACL,Access Control List),访问控制列表又包括了DACL(Discretionary Access Control List)和SACL(System Access Control List)以及一组控制位,用于限定安全描述符含义。

安全描述符可以包括以下安全信息:

组成部分 含义
Owner SID 谁是这个对象的所有者(拥有者)
Group SID (很少用)所属组
DACL(Discretionary ACL) 允许/拒绝谁干什么 的访问控制列表
SACL(System ACL) 用于审计,谁访问了你(通常与日志有关)

访问控制列表

ACL包含两个东西:

DACL:自主访问控制列表(DACL)是安全描述符中最重要的,它里面包含零个或多个访问控制项(ACE,Access Control Entry),每个访问控制项的内容描述了允许或拒绝特定账户对这个对象执行特定操作。 | ACE类型 | SID(谁) | 权限 | | ——- | ——— | ——– | | 允许 | 张三 | 读、写 | | 拒绝 | 李四 | 所有操作 |

📌 注意:DACL 是“白名单”机制

📋 DACL 的匹配过程

系统会从 DACL 中依次遍历 ACE:

SACL:系统访问控制列表(SACL) 主要是用于系统审计的,它的内容指定了当特定账户对这个对象执行特定操作时,记录到系统日志中。

UAC

🧠 初衷

为了解决Windows XP 时代”管理员默认运行一切”的安全问题,微软在Vista开始引入UAC(User Account Control)。 即使你是“管理员”,启动程序时也默认 以标准用户身份运行,需要你手动 同意提升权限。

👑 Admin SID 不等于高权限

当你是管理员账户时:

🧱 filtered token 特点:

当程序需要权限提升时,调用:

ShellExecute(NULL, "runas", "app.exe", NULL, NULL, SW_SHOW);

📌 注意:

🔒 UAC 等级分四种: | 等级 | 含义 | | —————- | —————————— | | Always notify | 改动系统前必须提示 | | Default | 提升程序才提示 | | Only notify apps | 改设置不提示,非交互程序不提示 | | Never notify | 不建议,UAC基本形同虚设 |

参考链接