如何设置Windows7标准用户的权限?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何设置Windows7标准用户的权限?相关的知识,希望对你有一定的参考价值。

为何我设置标准用户无法在登陆后打开像360安全卫士、跑跑卡丁车这样的应用程序,而且要输入管理员密码后程序才能运行,求详细设置。

  设置Windows7标准用户的权限的方法:

  1、如果是针对单个文件或者文件夹的权限设置,有两种方法:

  (1)在文件或者文件夹上右键——属性——安全中,设定用户对此文件或者文件夹的访问权限。

  (2)以管理员权限运行cmd,打开命令提示符后,使用cacls命令来设置ACL,即访问控制列表。


  2、要完全的设置一个用户的权限是标准用户权限,只能创建新帐户,也有两种方法:

  (1)打开控制面板——用户帐户和家庭安全——添加或者删除一个帐户——创建一个新帐户,在弹出的窗口中选择创建“标准用户”即可。

  (2)以管理员权限打开cmd,在命令提示符中执行:

  net user username pass /ad 添加一个用户即可,此时用户只在users用户组中,默认只有标准用户权限。如下图所示:


  说明:admin是用户名,第2个admin是admin用户的密码。/ad是net user命令的参数,表示添加一个用户。

参考技术A 针对受限用户帐户配置 Windows 7 更新时间: 2009年10月 应用到: Windows 7 备注 此博客的作者为 Steve Friedl(Unixwiz.net 技术提示),最初发布时间为 2009 年 5 月。作者感谢 Susan Bradley (Microsoft MVP) 和 Crispin Cowan 博士提供的帮助。 Windows 7 现在已发布,许多人正在试用这个新操作系统。那些从 Windows XP 跳过 Windows Vista 的用户发现了一种新体验和一个全新的安全模式:用户帐户控制 (UAC)。 UAC 随 Windows Vista 一起引入,并且因为用户不熟悉而饱受绯言困扰。尽管因为 Windows Vista 已更新而在一定程度上平静下来,但该功能似乎真的在 Windows 7 中有了进步。我非常喜欢 UAC。 但即使它的形式不完美,却是一个不错的想法,必竟它尝试使自很早以来就让 Windows 饱受困扰的管理任务和用户任务之间非常模糊的界线清晰起来。 造成这种困扰的大部分原因在于早期的消费者操作系统 Windows 95、Windows 98 和 Windows ME,这些操作系统未对这些角色加以技术上的区分:每个人都始终是管理员,软件开发人员甚至连考虑将角色分离开来都无从下手。 即使对于更为现代的 Windows NT 系统、Windows 2000 和 Windows XP,真正让您的工作以非管理用户身份完成都令人非常烦恼,大多数人都只是放弃,并以管理员帐户运行。这几乎完全是由于软件开发人员的不良习惯造成的:他们自己以管理员身份运行,而简单地编写假定每个人也都是管理员的松散代码。 Microsoft 一直在努力尝试反驳这种“每个人都是管理员”的心态,而 UAC 就是他们尝试解决这一点的折衷方法 - 如果您要以管理员身份运行,至少我们可以让您知道角色差异。这就是 UAC 试图达到的目的。 提示 此技术提示专门针对 Windows 7 体验编写,但其大部分内容也适用于 Windows Vista。此外,本文仅描述工作组或独立计算机安装;它并未解决加入域的计算机的更复杂企业环境的问题。 备注 要全部查看 Windows 7 资源、文章、演示和指南,请访问 Windows Client TechCenter 上的 Springboard Series for Windows 7。 有关本文档的可下载版本,请参见 Microsoft 下载中心中的针对受限用户帐户配置 Windows 7(可能为英文网页)( http://go.microsoft.com/fwlink/?LinkId=165707) 。 本文档内容: 用户帐户控制简介 方法1:配置新安装 方法2:转换已安装的管理员用户 禁用Administrator 帐户 选择密码 使您自己无法访问自己的计算机 用户帐户控制简介 用户帐户控制的工作原理是保护对管理权限的访问,而这涉及到 权限提升 :在尝试执行管理任务时,操作系统会自动提升到管理员权限,或请求某种许可或凭据来这样做。 Windows 7 可识别三种用户广泛分类: 内置的“Administrator”帐户 出于很多原因,此帐户是特殊帐户,并且在 Windows Vista 和 Windows 7 中默认处于禁用状态。由于此帐户会明确禁用某些重要的安全功能(例如 Internet Explorer 保护模式)以及 UAC,因此为所有内容使用 Administrator 确实是一个非常糟糕的想法。 我强烈主张禁用 Administrator 帐户! 保持此帐户禁用(意味着您不会冒险实际使用它)可帮助使您更安全! 具有管理权限的帐户 尽管用户由于具备本地 Administrators 组的成员资格而能够提升到管理员权限,但 UAC 会在关键时刻介入,提示确认您的意图。 这是 同意提示 模式,如果单击“是”,则会提升任务权限并以管理员身份运行。 要执行管理任务,请始终使用这种自定义管理员帐户,而不是内置的 Administrator 。 Windows 7 为 UAC 设置引入了滑块,从而允许更改 UAC 提示级别,包括一个用于完全禁用 UAC 的设置( 管理员批准 模式)。 标准受限用户 UNIX 和 Linux 系统从不会混淆管理任务和用户任务 - 两种任务的区分一直很明显。 而且,八十年代的新闻组中的行为是对这种角色区分观点的最好阐释。如果用户通过他的根帐户(计算机的管理员)发布消息,他将会看到警告:“不要以根用户身份运行!” 这些帐户既没有直接执行管理任务的权限,也无法仅仅通过确认来提升权限。他们需要诸如密码或智能卡等凭据。这通过向用户显示以下提示来进行请求: 这一点通俗地称为即时权限提升 模式(某人以用户的权限为基础键入密码并提升批准的任务的权限)。 我强烈主张使用受限用户帐户! 我自从 Windows XP Service Pack 2 以来就一直这样做,包括我的便携式计算机和我的主要软件开发工作站在内,都在使用受限用户帐户。有时是很令人烦恼,但它却大大减少了我的系统的受攻击面,我的 Windows 计算机从未遭受过损害,它的作用不可忽视。 开始使用 Windows 7 时,我当然希望以受限用户身份运行,但由于不知道它(在 Windows 7 或 Windows Vista 中)的工作方式,因此我实质上使我自己都无法访问自己的计算机(请参见本文档后面的使您自己无法访问自己的计算机)。 因此在弄明白之后(并重新安装了几次之后),我创建了此技术提示来帮助具有安全意识的用户执行安全的操作。 本文提供了两个过程:一个用于操作系统首次安装,另一个用于改进主要用户为自定义管理员的已安装系统。 方法1:配置新安装 因为新安装不需要解决以前的设置问题,因此最容易进行正确设置。本例使用两个 Windows 帐户: SteveAdmin :安装过程中创建的第一个帐户;它应专用于管理任务。 Steve :以标准用户形式创建的第二个帐户;此受限帐户用于日常工作。 不会以任何方式使用内置的 Administrator 帐户,该帐户将保持禁用。 按照以下步骤设置 Windows 7: 安装Windows 7,同时创建名为“SteveAdmin”的初始用户。 这应是通常的“从 DVD 安装”过程。在询问与设置用户相关的任何问题之前,初始部分会花费一段时间(并至少重新启动一次)。 出现提示时,将第一个用户命名为 SteveAdmin 。会自动将该用户创建为管理帐户。 如果选择为帐户提供密码,请确保记住该密码 - 计算机上的所有管理任务都将需要该密码。 完成Windows 7 安装 这包括配置自动更新、添加所需的驱动程序、配置网络等操作。 所有这些操作都是以管理用户 SteveAdmin 的身份完成的。 以标准用户方式创建一个名为“Steve”的新帐户 以 SteveAdmin 身份登录时,导航到控制面板: 单击“开始”图标。 单击“控制面板”。 在“用户帐户和家庭安全”下单击“添加或删除用户帐户”。 在当前帐户的列表下单击“创建一个新帐户”。 在对话框中填写新用户名“Steve”,然后单击“标准用户”。 单击“创建帐户”完成操作。 为新用户“Steve”分配密码(如果需要) 创建了帐户后,将出现当前用户的列表,标题为“选择希望更改的帐户”。单击新创建的 Steve 帐户的图标,该帐户应以标准用户形式列出。 单击“创建密码”,并输入密码(两次!)以及密码提示(如果需要)。 备注 由于您在更改您自己以外的不同用户( Steve 与 SteveAdmin )的密码,因此会出现以下不好的消息(可以忽略该消息):“如果执行该操作,Steve 将丢失网站或网络资源的所有 EFS 加密文件、个人证书和存储的密码”。由于此用户是新创建的,没有私人数据会丢失,因此我们可以忽略此消息并继续。 关闭控制面板对话框,注销并以 Steve 身份登录 此时, Steve 为标准用户。既然我们是标准用户,那么在尝试执行管理任务时,将会看到要求输入 SteveAdmin 的密码的 UAC 提示。 方法2:转换已安装的管理员用户 如果已经设置了 Windows 7,安装程序用户(此处为 Steve )已自动创建为具有管理权限,则使用此方法。尽管用户可以在技术上将该帐户重命名为 SteveAdmin 并使 Steve 的新帐户成为受限用户,但这将会对用户配置文件、桌面和其他个人配置造成严重破坏。可以复制配置文件,但更轻松的方法是创建一个新管理员帐户,并使此帐户降级。 步骤如下所示: 创建一个新 SteveAdmin 用户 以仍然是管理用户的 Steve 身份登录,并导航到控制面板以创建新用户: 单击“开始”图标,然后单击“控制面板”。 在“用户帐户和家庭安全”下单击“添加或删除用户帐户”。 在当前帐户的列表下单击“创建一个新帐户”。 在对话框中填写新用户名 SteveAdmin ,然后单击“管理员”。 单击“创建帐户”完成操作。 现在我们有一个还没有密码的新 SteveAdmin 帐户!此系统现在有两个管理员用户。 为新用户 SteveAdmin 分配密码(如果需要) 创建了帐户后,将出现当前用户的列表,标题为“选择希望更改的帐户”。单击新“SteveAdmin”用户的图标,该用户应以管理员形式列出。 单击“创建密码”,并输入密码(两次!)以及密码提示(如果需要)。 备注 由于您在更改您自己以外的不同用户( SteveAdmin 与您登录的 Steve 帐户)的密码,因此会出现以下不好的消息(可以忽略该消息):“如果执行该操作,SteveAdmin 将丢失网站或网络资源的所有 EFS 加密文件、个人证书和存储的密码”。由于此用户是新创建的,没有私人数据会丢失,因此我们可以忽略此消息并继续。 这将完成创建 SteveAdmin 帐户的过程,并在计算机上留下两个具有管理员权限的帐户。 现在不要关闭对话框!我们将从此处进入下一步。 使用户“Steve”降级 在 SteveAdmin 帐户处于良好状态的情况下,就该将原始安装用户 Steve 从管理员降级为标准用户了。由于我们仍然在控制面板中,因此可以轻松地从停止的位置开始继续操作: 单击“管理其他帐户”。 单击 Steve 帐户上的图标。 单击“更改帐户类型”。 单击“标准用户”。 单击“更改帐户类型”。 关闭控制面板对话框。 用户 Steve 下次登录时,他将具有严格的标准用户权限。 以“Steve”身份注销,然后重新开始 注销操作将会销毁仍然具有管理员权限的会话令牌,因此下一次登录将获得新的受限权限集。 以受限用户身份登录后,在尝试执行管理任务时,将会看到要求提供 SteveAdmin 用户凭据的 UAC 提示。 禁用Administrator 帐户 此时,两个过程中的其中一个过程已设置了受限用户 Steve 和适当的管理帐户 SteveAdmin ,但某些用户可能之前也启用了内置的 Administrator 帐户。 我认为这不是个好主意,并且我建议禁用该帐户。如果新安装了 Windows 7,或者 Administrator 未以可登录用户的图标的形式出现在登录页上,则不需要禁用该帐户。 如果您不确定,则检查步骤和禁用步骤几乎相同: 打开“管理用户”小程序 进行启用和禁用帐户操作的位置与创建新用户时的位置不同,因此需要导航到新位置。 单击“开始”图标。 右键单击“计算机”,然后选择“管理”。 导航到“用户”。 双击“管理员”。 确保“帐户已禁用”处于选中状态(如果已选中,则您已完成操作)。 关闭对话框。 此时,“Administrator”帐户处于禁用状态,并且无法使用它登录或批准 UAC 提升。不必更改该帐户的密码,因为禁用该帐户即会覆盖任何密码(即使是空密码)。 选择密码 奇怪的是,帐户并不总是必须有密码。由于无法通过网络访问具有空白密码的帐户,因此您可以通过这种方式大幅减小计算机的受攻击面。 但这需要您能够对计算机的物理安全性进行很好的控制。如果计算机上(或环境中)有不允许其执行管理任务的用户,则使用空白密码的主意并不好,因为这样将会允许任何人登录到计算机并执行任何操作。 此外,带到室外的便携式计算机可能也不适合于使用空白密码,因为会为物理安全性带来严重的问题。 对于大多数家庭用户,您如何选择密码方案可能并不真的很重要,但是,如果您有此方面的任何问题,请将您的方案提供给受信任的安全顾问以获得指导。 使您自己无法访问自己的计算机 如前所述,我以前未安装过 Windows Vista,因此我不知道 Administrator 帐户默认处于禁用状态。这就导致在使安装帐户 Steve 降级后出现了一件令人不快并且令人惊讶的事情。 在配置了我们的计算机后,我进入了控制面板,将 Steve 帐户降级为标准用户帐户。我已经无意中删除了唯一剩下的管理员帐户,因此在我注销并重新登录(以使帐户更改在会话中生效)后,下一个 UAC 操作提供了以下提示: 细心的读者将注意到没有地方来输入密码!说这一点令人抓狂一点都不为重。根据您的计算机的配置,可能会有使用智能卡的邀请,但在没有配置智能卡的计算机上可能没什么好办法。 尽管从技术上而言是我自己的错误,但这似乎是不好的用户体验。本回答被提问者采纳

VC++如何创建低权限的标准用户权限进程

目录

1、程序的运行权限

2、为啥需要创建一个低权限的标准用户权限进程

3、从Process Explorer工具入手,找到创建低权限进程的线索

4、Windows安全框架

4.1 安全对象

4.2 安全描述符

4.3 安全标识

4.4 访问令牌

4.5 访问控制列表及其访问控制项

4.6 线程和安全对象间的交互

4.7 完整性级别

5、创建低权限进程的代码实现


       Windows自Vista开始就引入了UAC用户账户控制的概念,对程序运行权限有着严格的控制,这给众多Windows软件开发人员带来一些负担和困扰。本文来讲述一类与进程运行权限有关的特殊问题,即如何创建低权限的标准用户权限进程。

1、程序的运行权限

       程序的运行权限主要有两种,一种是低权限的标准用户权限,一种是高权限的管理员权限。对于操作系统关键路径(比如C:\\Windows、C:\\Windows\\System32等)、操作HKEY_LOCAL_MACHINE路径下的注册表项、向系统注册控件等,都需要管理员权限才能完成,没有管理员权限会操作失败。一般情况下,安装包程序都需要管理员权限,因为安装包可能会操作系统关键路径、写注册表、向系统注册控件等。

       让程序以管理员权限运行,有多种方法:

1)右键以管理员身份运行;

2)在Visual Studio的工程属性中给进程设置requireAdministrator,指定程序以管理员权限运行;

3)可以给进程添加manifest文件,在manifest文件中设置requireAdministrator,指定程序以管理员权限运行;

4)在代码中调用API函数ShellExecuteEx,传入runas参数,以管理员权限启动目标进程。

上述4种方法的具体细节就不再赘述了,如需要了解,自行搜索一下即可。

       除上述要点之外,程序到底是以管理员权限运行,还是以标准用户运行,还和当前登录的用户类型是有关系的。用户类型主要有普通用户管理员用户超级管理员Administrator。用户类型对程序运行程序的影响如下:

1)普通用户(标准用户)登录时,程序默认是以标准用户权限运行。如果要运行设置了管理员权限运行的程序,则会UAC提权的提示框,需要输入管理员的用户和密码才能运行。

2)管理员用户登录时,要看UAC用户账户控制是否关闭:

i)UAC开关打开:如果程序没有指定以管理员权限运行,则默认是以标准用户权限运行;如果指定以管理员权限运行,则可以直接运行程序。

ii)UAC开关关闭:所有程序默认都以管理员权限运行。

3)超级管理员Administrator登录时,所有程序均以管理员权限运行。

2、为啥需要创建一个低权限的标准用户权限进程

       我们开发的软件在运行时遇到的一个问题,就和进程运行的权限有关系。我们的软件只允许运行一个进程实例,如果再次启动程序(比如通过双击桌面快捷方式去启动程序),则需要将已经运行的进程的主窗口弹出来或拉到最前面显示,不允许运行第二个进程。

       为了实现软件的单实例运行,我们会先给软件的主窗口,设置一个Property属性串,当启动本软件时,会检测软件是否已经运行,如果已经运行,则去遍历桌面上的所有窗口,通过窗口的Property属性串找到找到已运行进程的主窗口,调用ShowWindow将窗口show出来,并调用SetForegroundWindow将窗口拉到最前面显示,代码如下:

CString strLog;

BOOL bFind = FALSE;
HWND hPrevWnd = ::GetDesktopWindow();
hPrevWnd = ::GetWindow(hPrevWnd, GW_CHILD);
while (hPrevWnd)

    if (::GetProp(hPrevWnd, STRING_TARGET_PROGRAM_PROPERTY_NAME))
    
        bFind = TRUE;
        WriteLog(_T("[CheckShowExsitingWnd] 找到目标窗口."));

        // 窗口处于最小化或掩藏状态,则直接将其显示出来
        if (::IsIconic(hPrevWnd))
        
            WriteLog(_T("[CheckShowExsitingWnd] IsIconic返回TRUE,将窗口显示出来."));
            ::ShowWindow(hPrevWnd, SW_RESTORE);
        


        if (!::IsWindowVisible(hPrevWnd))
        
            SetLastError(0);
            ::ShowWindow(hPrevWnd, SW_RESTORE);

            // 可能会show失败,将lasterror打印出来
            strLog.Format(_T("[CheckShowExsitingWnd] IsWindowVisible返回FALSE,将窗口显示出来,LastError: %d."), GetLastError());
            WriteLog(strLog);
        

        // 再尝试show一次
        SetLastError(0);
        ShowWindow(hPrevWnd, SW_SHOWNORMAL);
        strLog.Format(_T("[CheckShowExsitingWnd] 再show一次,LastError: %d."), 
        GetLastError());
        WriteLog(strLog);

        SetForegroundWindow(hPrevWnd);
    

    hPrevWnd = ::GetWindow(hPrevWnd, GW_HWNDNEXT);

结果在部分用户的电脑上,并没有将已经运行的程序主窗口弹出来。 通过打印出来的日志发现,在调用ShowWindow时居然失败了,获取的lasterror值为5,即Access Denied访问被拒绝的错误。

       这个着实有点奇怪,这段代码很通用的,xp时代就有了,咋在win10系统上就出问题了呢。按讲是可以在一个进程中去操作另一个进程的窗口,可为啥会出现操作失败的情况呢?经过一番思考和推敲,怀疑可能是两个进程的权限不对等导致的。经研究找到了具体的原因,出问题的用户那边执行了软件最新的安装包更新了软件版本,在安装完成后会自动将软件启动起来,因为安装包是以管理员权限启动的,所以启动起来的软件也是有管理员权限的。而用户双击桌面快捷方式启动起来的新的进程是没有管理员权限的,是以标准用户权限运行的。我们的软件没有设置管理员权限,默认是以标准用户权限运行的。如果软件设置了以管理员权限运行,在系统UAC开关打开的情况下,每次启动时都会弹出UAC提示框,用户的体验会很差。

       所以导致该问题的根本原因在于,安装包启动的进程是有管理员权限的,而通过桌面快捷方式启动的新进程是以标准用户权限运行的,在低权限的进程中去操作高权限进程的窗口,会因为进程权限不对等导致操作失败,所以出现了Access Denied的问题了。

       我们对比了一款腾讯的主流软件,他们也实现了打开已运行程序的窗口的功能,他们软件并没有这样的问题,难道他们实现的机制和我们的不同?后来使用我们自己实现的一个判断某个进程是否有管理员权限的工具发现,腾讯这款软件的安装包启动起来的进程居然是以标准用户运行的,这样就不存在进程的权限不对等的问题了!在我们的认知中,以管理员权限运行的进程中启动的子进程都是有管理员权限的, 腾讯的软件怎么做到在以管理员权限运行的进程中创建一个以低权限(标准用户权限)运行的进程的呢?于是我们也想研究下这个问题,所以也就引出了本篇文章的主题。

3、从Process Explorer工具入手,找到创建低权限进程的线索

       提到Mark Russinovich,很多Windows程序员应该都比较了解。Mark Russinovich是一位世界顶级的软件开发大神,Windows系统内核大师,现任微软Azure云计算平台的首席技术主管。Mark Russinovich主持编写了最受瞩目的Windows技术权威参考图书《深入解析Windows操作系统(Windows Internals)》,深入剖析了Windows技术内幕及实现细节。Mark Russinovich还开发了Windows历史上鼎鼎大名的系统工具集Sysinternals,该工具集合中包含了多个实用工具,比如我们日常工作中常用的Process ExplorerProcess MonitorTCPView等。

       其中Process Explorer工具类似于Windows系统的任务管理器,但其功能比Windows的任务管理器要强大的多。使用Process Explorer可以查看到目标进程加载了哪些库文件以及这些库的路径,可以查看到进程中各个线程的CPU占用情况,还可以看到线程中的函数调用堆栈,可以看到进程对GPU的占用情况。正因为使用该工具可以查看到线程的CPU占用情况和线程的函数调用堆栈,我们经常使用该工具来辅助排查进程CPU占用高的问题。

      发散的有点多了,我们回到本文的主题上来。Process Explorer工具中提供了一个“Run  as Limited User”菜单项:(点击菜单栏File->Run  as Limited User...)

这个地方的Limited User,就是我们将的标准用户,所以Run  as Limited User就是以标准用户运行程序。于是想研究一下该工具是如何创建一个标准用户的进程的,经过一番搜索,找到了Mark Russinovich写过的一篇专题文章:

Running as Limited User - the Easy Wayhttps://docs.microsoft.com/en-us/archive/blogs/markrussinovich/running-as-limited-user-the-easy-way文章阐述了如何创建一个以标准用户权限运行的进程,其中一段话为:

use the CreateRestrictedToken API to create a security context, called a token, that’s a stripped-down version of its own, removing administrative privileges and group membership. After generating a token that looks like one that Windows assigns to standard users Process Explorer calls CreateProcessAsUser to launch the target process with the new token.

使用系统API函数CreateRestrictedToken来创建一个安全上下文,降低令牌(Token)的管理员权限及组成员资格,使其创建的令牌看起来像Windows赋予普通用户时一样,然后使用此令牌作为传入参数调用CreateProcessAsUser去创建新的子进程。

       此外还有一个细节:

Process Explorer queries the privileges assigned to the Users group and strips out all other privileges, including powerful ones like SeDebugPrivilege, SeLoadDriverPrivilege and SeRestorePrivilege.

Process Explorer去查询赋予用户组的特权并从当前进程权限中剔除这些权限,比如SeDebugPrivilege、SeLoadDriverPrivilege和SeRestorePrivilege等。

4、Windows安全框架

       Windows的安全架构主要是建立在Windows访问控制模型ACM(Access Control Model)基础上的,而Windows访问控制模型ACM是个复杂的概念,要搞清楚,必须了解Token访问令牌,ACL访问控制列表(Access Control List),DACL选择访问控制列表(Discretionary Access Control List),ACE访问控制列表项(Access Control Entries)等与访问控制模型相关的名词含义及之间的关系。 

       ACM访问控制模型中最重要的两部分是访问令牌(Access Token)和安全描述符表(Security Descriptor)。访问令牌存在于访问主体中,安全描述符表存在于访问客体中。比如我要去意大利,我就是访问主体,意大利就是访问客体,我持有的签证就是访问令牌。

       系统中访问主体是进程客体是一切系统对象。访问令牌中有当前用户的唯一标识SID,组唯一标识SID以及一些权限标志(Privilege)。安全描述符表(SD)存在于Windows系统中的任何对象中(文件,注册表,互斥量,信号量等等)。SD中包含对象所有者的SID,组SID以及两个非常重要的数据结构选择访问控制列表(DACL)和系统访问控制列表(SACL),其中SACL涉及系统日志用的很少可以先无视。DACL中包含一个个ACE访问控制入口也是权限访问判断的核心,当一个进程访问某一对象的时候,对象会将进程的Token与自身的ACE依次比对,直到被允许或被拒绝,前面的ACE优于后面的ACE。

       下面我们就来逐一介绍这些概念。

4.1 安全对象

       有资格拥有安全描述符的对象如文件、管道、进程、进程间同步对象等。所有已命名的Windows对象都是安全的,那些未被命名的对象比如线程或进程对象也可以拥有安全描述符。

       对于大多数的安全对象,当创建该对象时可以指定它的安全描述符。当一个安全对象被创建时,系统会对其赋予一个安全描述符,安全描述符包含由其创建者指定的安全信息,或者缺省的安全信息(如果没有特意进行指定的话)。

       应用程序可以使特定的函数对已有的对象进行操作以来获取和设置安全信息。每种类型的安全对象定义了它自身的访问权限和自身映射的通用访问权限。

      关于安全对象的详细说明,可以参见微软的页面:

Securable Objectshttps://docs.microsoft.com/en-us/windows/win32/secauthz/securable-objects?redirectedfrom=MSDN

4.2 安全描述符

       在 Windows 中,每个安全对象都有一个安全描述符(Security Descriptor,SD)。它有助于控制对对象的访问,它包含所有者的信息、要审核的内容以及以何种方式授予访问权限。它也包含了真正的用于设置安全权限的 ACL访问控制列表,ACL主要包括:

1)自由访问控制列表(DACL):指明特定用户或组对该对象的访问是允许或拒绝;

2)安全访问控制列表(SACL):控制系统审计如何访问对象。

4.3 安全标识

       安全标识SID(Security Identifiers)是用来标识信任方的唯一的数值,其长度可变。而信任方就是用户,组,会话。所以基本上可以把SID理解为是一个用户名,一个组名,会话名。它们是经过安全认证,且不会重复,也就是安全可靠的。

       安全标识主要运用于如下几个方面:

1)在安全描述符中定义对象的所有者和基本组;

2)在访问控制项中定义托管的权限是允许、拒绝或是审计;

3)在访问令牌中定义用户和用户所在的组。

4.4 访问令牌

       访问令牌Token包含登录用户的信息。用来描述一个进程或线程的安全上下文的对象,令牌的信息包含关联到进程或线程的账号的标识和特权。

       当一个用户登录时,系统对用户的账号和密码进行认证,如果登录成功,系统则创建一个访问令牌,每个进程运行时都有一个访问令牌代表当前的用户,访问令牌中的安全描述符指明当前用户所属的账号和所属的组账号,令牌也包含一系列由用户或用户所在组进行维护的权限,在一个进程试图进行访问安全对象或执行系统管理员任务过程中需要权限时,系统通过这个令牌来确认关联的用户。

4.5 访问控制列表及其访问控制项

       自由访问控制列表(DACL)包含若干个访问控制项(ACEs)。约定的执行规则如下:

1)如果对象没有自由访问控制列表(DACL),则任何用户对其均有完全的访问权限;

2)如果对象拥有DACL,那么系统仅允许那些在访问控制项(ACE)显式指明的访问权限;

3)如果在访问控制列表(DACL)中不存在访问控制项(ACE),那么系统不允许任何用户对其进行访问;

4) 如果访问控制列表中的访问控制项对准许访问的用户或组数目有限,那么系统会隐式拒绝那些不在访问控制项中的其他托管的访问

       需要注意的是访问控制项的排序很重要。因为系统按照队列的方式读取访问控制项,直到访问被拒绝或允许。用户的访问拒绝ACE必须放在访问允许ACE的前头,否则当系统读到对组的访问允许ACE时,它会给当前限制的用户赋予访问的权限。系统在检测到请求访问被允许或拒绝后就不再往下检查。

       你可以通过标识允许访问的ACE来控制对对象的访问,你无需显式地拒绝一个对象的访问。

4.6 线程和安全对象间的交互

       当一个线程想要使用一个安全对象时,系统在线程执行前会进行访问审核,在访问审核中,系统将线程访问令牌中的安全信息与对象安全描述符中的安全信息进行比对。

       访问令牌中包含的安全标识(SIDs)可以指明与线程关联的用户,系统查看线程访问令牌中用户或组的SID,同时检查对象的自由访问控制列表(DACL),自由访问控制列表(DACL)中包含存储有指明对指定的用户或组的访问权限是允许或拒绝信息的访问控制项(ACE),系统检查每个访问控制项(ACE)直至出现指明针对此线程(的用户或组的SID)的访问权限是允许还是拒绝的ACE,或者到最终都没有对应的ACEs可以检查。

       系统按照序列检查每个ACE,查询ACE中的托管与定义在线程中的托管(根据托管的SID)一致的ACE,直到如下的情况出现:

1)表明访问拒绝的ACE显式拒绝在线程的访问令牌中的一个托管的任何访问权限;

2)一个或多个表明访问允许的ACEs显式地为线程访问令牌中的托管提供所有访问权限;

3)所有ACEs已经比对审核完但至少有一个请求访问权限没有显式允许,这种情况下该访问权限则被隐式拒绝。

       一个访问控制列表(ACL)可以有多个的访问控制项(ACE)针对令牌的(同一个)SIDs,这种情况下每个ACE授予的访问权限是可以进行累积叠加,比如,如果一个ACE对一个组允许读的访问权限,而另一个ACE对该组内的一个用户允许写的访问权限,那么该用户对于当前对象就拥有了读和写的访问权限。

       如上图所示,对于线程A,尽管在ACE@3中允许写权限,但因为在ACE@1中已经显式拒绝“Andrew”用户的访问权限,所以该安全对象对于线程A是不可访问的;对于线程B,在ACE@2中显式指明A组用户可以有写的权限,并且在ACE@3中允许任何用户读和执行的权限,所以线程B对这个安全对象拥有读、写以及执行的权限。

       以上内容参考了微软的页面:

Interaction Between Threads and Securable Objectshttps://docs.microsoft.com/en-us/windows/win32/secauthz/interaction-between-threads-and-securable-objects?redirectedfrom=MSDN

4.7 完整性级别

       Windows完整性机制是对Windows安全架构的扩展,该完整性机制通过添加完整性等级到安全访问令牌和添加强制性标记访问控制项到安全描述符中的系统访问控制列表(SACL)。进程在安全访问令牌中定义完整性等级,IE在保护模式下的完整性等级为低,从开始菜单运行的应用程序的等级为中等,需要管理员权限并以管理员权限运行的应用程序的等级为高。

       保护模式能够有效地减少IE进程附带的攻击行为如篡改和摧毁数据、安装恶意程序;相比其他程序,连接网络的程序更容易遭受网络的攻击因为它们更可能从未知源地址下载未受信任的内容,通过降低完整性等级或限制对其的允许权限,可以减少篡改系统或污染用户数据文件的风险。

       在系统访问控制列表(SACL)中有一个称为强制标识的访问控制项(ACE),该控制项的安全描述符定义完整性等级或允许访问当前对象需要达到的等级,安全对象如果没有该控制项则默认拥有中等的完整性等级;即使用户在自由访问控制列表(DACL)中已经明确授予相应的写权限,低等级的进程也不能获取比其高等级的安全对象的写权限,完整性等级检验在用户访问权限审查之前完成。

        所有的文件和注册表键在缺省下的完整性等级为中,而由低等完整性进程创建的安全对象,系统会自动地赋予其低等完整性强制标志,同样,由低等完整性进程创建的子进程也是在低完整性等级下运行。

完整性访问等级(IL)

系统权限

安全标识

System

System

S-1-16-16384

High

Administrative

S-1-16-12288

可安装文件到Program Files文件夹、往敏感的注册表中如HKEY_LOCAL_MACHINE写数据

Medium

User

S-1-16-8192

创建和修改用户文档中的文件、往特定用户的注册表如HKEY_CURRENT_USER中写数据

Low

Untrusted

S-1-16-4096

仅能往低等级位置写数据如临时网络文件夹和注册表

HKEY_CURRENT_USER\\Software\\LowRegistry

       低完整性进程可以往用户存档文件下写文件,通常为%USER PROFILE%\\AppData\\LocalLow,可以通过调用SHGetKnownFolderPath 函数并传入FOLDERID_LocalAppDataLow参数来获取完整的路径名称:

SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, NULL, szPath, ARRAYSIZE(szPath));

      同样低完整性进程可以往指定的注册表下创建和修改子键,该注册表路径通常为HKEY_CURRENT_USER\\Software\\AppDataLow

       以上内容参考了微软的相关页面:

Windows Integrity Mechanism Designhttps://docs.microsoft.com/en-us/previous-versions/dotnet/articles/bb625963(v=msdn.10)?redirectedfrom=MSDN

5、创建低权限进程的代码实现

        创建低权限(标准用户权限)进程的步骤如下:

1)创建新的普通用户组和系统管理员的安全描述符标识;

2)获取当前进程的令牌,并根据令牌句柄获取当前进程所拥有的特权;

3)通过已创建的普通用户组的安全描述符标识获取普通用户组所拥有的特权;

4)给当前进程令牌中的管理员安全描述符添加Deny-only属性,以此达到避免新创建的进程以管理员作为其所有者;

5)从当前进程拥有的特权中剔除普通用户组所没有的特权;

6)从新的受限令牌中复制为模拟令牌;

7)将模拟令牌的完整性特权设为低级,以限制新创建的进程对普通文档、可执行程序的写、执行等访问权限;

        具体的代码如下:

void CreateRestrictedProcess()

    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;

    TCHAR szCmdLine[CMDLINE_SIZE] = 0;
    HANDLE hToken = NULL;
    HANDLE hNewToken = NULL;
    HANDLE hNewExToken = NULL;

    CHAR szIntegritySid[20] = "S-1-16-4096";
    PSID pIntegritySid = NULL;
    PSID pUserGroupSID = NULL;
    PSID pAdminSID = NULL;

    TOKEN_MANDATORY_LABEL tml = 0;

    PROCESS_INFORMATION pi;
    STARTUPINFO si;

    BOOL bSuc = FALSE;
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    GetStartupInfo(&si);
    DWORD fdwCreate = 0;

    __try
    

        if (!OpenProcessToken(GetCurrentProcess(),
            //MAXIMUM_ALLOWED,
            TOKEN_DUPLICATE |
            TOKEN_ADJUST_DEFAULT |
            TOKEN_QUERY |
            TOKEN_ASSIGN_PRIMARY,
            &hToken))
        
            char szMsg[DEFAULT_MSG_SIZE] = 0;
            Dbg("OpenProcessToken failed, GLE = %u.", GetLastError());
            __leave;
        

        Dbg("Using RestrictedTokens way !!!");
        DWORD dwSize = 0;
        DWORD dwTokenInfoLength = 0;
        SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
        SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
        if(!AllocateAndInitializeSid(
            &SIDAuthNT,
            0x2,
            SECURITY_BUILTIN_DOMAIN_RID/*0×20*/,
            DOMAIN_ALIAS_RID_USERS,
            0, 0, 0, 0, 0, 0,
            &pUserGroupSID))
        
            Dbg("AllocateAndInitializeSid for UserGroup Error %u", GetLastError());
            __leave;
        

        // Create a SID for the BUILTIN\\Administrators group.
        if(! AllocateAndInitializeSid( &SIDAuth, 2,
            SECURITY_BUILTIN_DOMAIN_RID,
            DOMAIN_ALIAS_RID_ADMINS,
            0, 0, 0, 0, 0, 0,
            &pAdminSID) )
        
            Dbg("AllocateAndInitializeSid for AdminGroup Error %u", GetLastError());
            __leave;
        

        SID_AND_ATTRIBUTES SidToDisable[1] = 0;
        SidToDisable[0].Sid = pAdminSID;
        SidToDisable[0].Attributes = 0;

        PTOKEN_PRIVILEGES pTokenPrivileges = NULL;
        PTOKEN_PRIVILEGES pTokenPrivilegesToDel = NULL;
        if(!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize))
        
            if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            
                pTokenPrivileges = (PTOKEN_PRIVILEGES)LocalAlloc(0, dwSize);
                pTokenPrivilegesToDel = (PTOKEN_PRIVILEGES)LocalAlloc(0, dwSize);
                if(pTokenPrivileges != NULL && pTokenPrivilegesToDel != NULL)
                
                    if(!GetTokenInformation(hToken, TokenPrivileges, pTokenPrivileges, dwSize, &dwSize))
                    
                        Dbg("GetTokenInformation about TokenPrivileges failed GTE = %u.", GetLastError());
                        __leave;
                    
                
                else
                
                    char szMsg[DEFAULT_MSG_SIZE] = 0;
                    Dbg("LocalAlloc for pTokenPrivileges failed GTE = %u.", GetLastError());
                    __leave;
                
            
        

        LUID_AND_ATTRIBUTES *pTokenLUID = pTokenPrivileges->Privileges;
        Dbg("CurrentToken's TokenPrivileges Count: %u", pTokenPrivileges->PrivilegeCount);
        DWORD dwLuidCount = 0;
        PLUID pPrivilegeLuid = NULL;
        if(!CTWProcHelper::GetPrivilegeLUIDWithSID(pUserGroupSID, &pPrivilegeLuid, &dwLuidCount))
        
            Dbg("GetPrivilegeLUIDWithSID failed GTE = %u.", GetLastError());
            if(pPrivilegeLuid)
            
                //HeapFree(GetProcessHeap(), 0, pPrivilegeLuid);
                LocalFree(pPrivilegeLuid);
                pPrivilegeLuid = NULL;
            
            __leave;
        
        Dbg("UserGroup's TokenPrivileges Count: %u", dwLuidCount);

        DWORD dwDelPrivilegeCount = 0;
        for(DWORD dwIdx=0; dwIdx<(pTokenPrivileges->PrivilegeCount); dwIdx++)
        
            BOOL bFound = FALSE;
            DWORD dwJdx = 0;
            for(; dwJdx<dwLuidCount; dwJdx++)
            
                //if(memcmp(&(pTokenLUID[dwIdx].Luid), &(pPrivilegeLuid[dwJdx]), sizeof(LUID)) == 0)
                if((pTokenLUID[dwIdx].Luid.HighPart == pPrivilegeLuid[dwJdx].HighPart)
                    &&
                    (pTokenLUID[dwIdx].Luid.LowPart == pPrivilegeLuid[dwJdx].LowPart))
                
                    bFound = TRUE;
                    break;
                
            
            if(!bFound)
            
                char szPrivilegeName[MAX_PATH] = 0;
                DWORD dwNameSize = MAX_PATH;
                if(!LookupPrivilegeName(NULL, &(pTokenLUID[dwIdx].Luid), szPrivilegeName, &dwNameSize))
                
                    Dbg("LookupPrivilegeName failed GTE = %u.", GetLastError());
                    //Dbg("NoFound[%u]: i=%u, j=%u", dwDelPrivilegeCount, dwIdx, dwJdx);
                
                //else
                //
                //    Dbg("NoFound[%u]: i=%u, j=%u -> %s", dwDelPrivilegeCount, dwIdx, dwJdx, szPrivilegeName);
                //
                pTokenPrivilegesToDel->Privileges[dwDelPrivilegeCount++].Luid = pTokenLUID[dwIdx].Luid;
            
        
        pTokenPrivilegesToDel->PrivilegeCount = dwDelPrivilegeCount;
        Dbg("TokenPrivileges to delete Count: %u", dwDelPrivilegeCount);
        if(pPrivilegeLuid)
        
            //HeapFree(GetProcessHeap(), 0, pPrivilegeLuid);
            LocalFree(pPrivilegeLuid);
            pPrivilegeLuid = NULL;
        

        if(!CreateRestrictedToken(hToken,
            0,
            1, SidToDisable,
            //0, NULL,
            dwDelPrivilegeCount, pTokenPrivilegesToDel->Privileges,
            0, NULL,
            &hNewToken
            ))
        
            char szMsg[DEFAULT_MSG_SIZE] = 0;
            Dbg("CreateRestrictedToken failed GTE = %u.", GetLastError());
            __leave;
        

        // Duplicate the primary token of the current process.
        if (!DuplicateTokenEx(hNewToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation,
            TokenPrimary, &hNewExToken))
        
            Dbg("DuplicateTokenEx failed GTE = %u.", GetLastError());
            hNewExToken = NULL;
            //__leave;
        
        else
        
            if (ConvertStringSidToSid(szIntegritySid, &pIntegritySid))
            
                tml.Label.Attributes = SE_GROUP_INTEGRITY;
                tml.Label.Sid = pIntegritySid;

                // Set the process integrity level
                if (!SetTokenInformation(hNewExToken, TokenIntegrityLevel, &tml,
                    sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(pIntegritySid)))
                
                    Dbg("SetTokenInformation failed GTE = %u.", GetLastError());
                    //__leave;
                
                else
                
                    CloseHandle(hNewToken);
                    hNewToken = hNewExToken;
                    hNewExToken = NULL;
                    Dbg("Assign Low Mandatory Level to New Token which used to CreateProcessAsUser.");
                
            

        

        if(!(bSuc = CreateProcessAsUser(hNewToken, NULL,
            szCmdLine,     // command line
            NULL,          // TODO: process security attributes
            NULL,          // TODO: primary thread security attributes
            TRUE,         // handles are inherited ??
            fdwCreate,     // creation flags
            NULL,          // use parent's environment
            NULL,          // use parent's current directory
            &si,           // STARTUPINFO pointer
            &pi)))         // receives PROCESS_INFORMATION
        
            Dbg("CreateProcessAsUser failed GTE = %u.", GetLastError());
            __leave;
        

        if(pTokenPrivileges)
        
            LocalFree(pTokenPrivileges);
        
        if(pTokenPrivilegesToDel)
        
            LocalFree(pTokenPrivilegesToDel);
        
    
    __finally
    
        if(pIntegritySid)
        
            LocalFree(pIntegritySid);
        
        if(pUserGroupSID)
        
            LocalFree(pUserGroupSID);
        
        if(pAdminSID)
        
            LocalFree(pAdminSID);
        
        //
        // Close the access token.
        //
        if (hToken)
        
            CloseHandle(hToken);
        
        if(hNewToken)
        
            CloseHandle(hNewToken);
        
        if(hNewExToken)
        
            CloseHandle(hNewExToken);
        
        if(!bSuc)
        
            Dbg("Retry to Create process in normal way.");
            //Create process.
            bSuc = CreateProcess(NULL,
                szCmdLine,     // command line
                NULL,          // TODO: process security attributes
                NULL,          // TODO: primary thread security attributes
                TRUE,          // handles are inherited ??
                fdwCreate,     // creation flags
                NULL,          // use parent's environment
                NULL,          // use parent's current directory
                &si,           // STARTUPINFO pointer
                &pi);          // receives PROCESS_INFORMATION
        
    

其中 GetPrivilegeLUIDWithSID 函数的实现如下:

BOOL GetPrivilegeLUIDWithSID(PSID pSID, PLUID *pLUID, PDWORD pDwCount)

    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    NTSTATUS ntsResult;
    LSA_HANDLE lsahPolicyHandle;

    // Object attributes are reserved, so initialize to zeros.
    ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

    // Get a handle to the Policy object.
    ntsResult = LsaOpenPolicy(
        NULL,    //Name of the target system.
        &ObjectAttributes, //Object attributes.
        POLICY_ALL_ACCESS, //Desired access permissions.
        &lsahPolicyHandle  //Receives the policy handle.
        );

    if (ntsResult != STATUS_SUCCESS)
    
        printf("OpenPolicy failed returned %lu", LsaNtStatusToWinError(ntsResult));
        return FALSE;
    

    PLSA_UNICODE_STRING UserRights = NULL;
    ULONG uRightCount;
    ntsResult = LsaEnumerateAccountRights(lsahPolicyHandle, pSID, &UserRights, &uRightCount);
    if (ntsResult != STATUS_SUCCESS)
    
        printf("LsaEnumerateAccountRights failed returned %lu", LsaNtStatusToWinError(ntsResult));
        LsaClose(lsahPolicyHandle);
        return FALSE;
    

    printf("LsaEnumerateAccountRights returned Right count: %lu", uRightCount);

    (*pDwCount) = 0;
    //pLUID = (PLUID)HeapAlloc(GetProcessHeap(), 0, uRightCount*sizeof(LUID));
    (*pLUID) = (PLUID)LocalAlloc(LPTR, uRightCount*sizeof(LUID));
    if((*pLUID) == NULL)
    
        printf("HeapAlloc for PLUID failed returned %u", GetLastError());
        LsaClose(lsahPolicyHandle);
        return FALSE;
    

    for(ULONG uIdx=0; UserRights != NULL && uIdx<uRightCount; uIdx++)
    
        int nLenOfMultiChars = WideCharToMultiByte(CP_ACP, 0, UserRights[uIdx].Buffer, UserRights[uIdx].Length,
            NULL, 0, NULL, NULL);
        PTSTR pMultiCharStr = (PTSTR)HeapAlloc(GetProcessHeap(), 0, nLenOfMultiChars*sizeof(char));
        if(pMultiCharStr != NULL)
        
            WideCharToMultiByte(CP_ACP, 0, UserRights[uIdx].Buffer, UserRights[uIdx].Length,
                pMultiCharStr, nLenOfMultiChars, NULL, NULL);
            LUID luid;
            if(!LookupPrivilegeValue(NULL, pMultiCharStr, &luid))
            
                printf("LookupPrivilegeValue about %s failed, GLE=%u.", pMultiCharStr, GetLastError());
                HeapFree(GetProcessHeap(), 0, pMultiCharStr);
                continue;
            
            (*pLUID)[(*pDwCount)++] = luid;
            HeapFree(GetProcessHeap(), 0, pMultiCharStr);
        
    
    if((ntsResult = LsaFreeMemory(UserRights)) != STATUS_SUCCESS)
    
        printf("LsaFreeMemory failed returned %lu", LsaNtStatusToWinError(ntsResult));
    
    LsaClose(lsahPolicyHandle);
    return TRUE;

 以上部分代码参考了微软的网页:

Designing Applications to Run at a Low Integrity Levelhttps://docs.microsoft.com/en-us/previous-versions/dotnet/articles/bb625960(v=msdn.10)?redirectedfrom=MSDN       新创建的进程的安全属性,可以在Process Explorer中查看,右键点击目标进程的属性,查看Security标签页,可以看到目标进程的安全信息:

       对于特权(Privilege),它是用于对一个对象或服务的访问控制,比自由访问控制更为严格,一个系统管理员通过使用特权控制那些用户可以操纵系统资源,一个应用程序在修改系统层级的资源需要使用到特权,比如修改系统时间和关闭系统。

以上是关于如何设置Windows7标准用户的权限?的主要内容,如果未能解决你的问题,请参考以下文章

Windows7 文件夹权限设置?

如何提升windows7的权限

如何提升windows7的权限

怎样提升Windows7的用户权限?

windows7有几种类型账户?权限分别是啥

电脑的用户权限设置在哪里