以编程方式允许对注册表键的写访问

5

我需要在产品安装过程中通过编程的方式修改已知注册表键的访问描述符。 我希望它按照以下方式工作:

  1. 以管理员模式运行安装程序。
  2. 创建注册表键。
  3. 一个函数(我需要的函数)从该键查询ACL。
  4. 如果该函数发现‘Users’组已经具有写访问权限,则不应执行任何操作。
  5. 如果没有,则应添加新的允许‘Users’组写访问的权限。
  6. 将权限保存为注册表键的权限。

这个问题类似于使用.NET设置注册表键写入权限,但是我需要C++ / Win32实现。

提前致谢


你有没有粗略地浏览过MSDN?你遇到了什么问题? - dirkgently
甚至是CodeProject吗? - dirkgently
我确实在两个网站上查看了。MSDN的问题在于没有我能理解的示例。逐个查看函数并不能很好地理解。这与我学习Win32 UI开发时的情况非常相似——你查看单个消息/函数/结构,却看不到它们如何整合。CodeProject的问题在于代码示例大多是使用.NET语言编写的。你知道有什么好的C++示例吗? - Kerido
出于好奇,您想更改哪个键的 ACL?出于什么原因?通常来说,注册表键的权限应该保持不变(它们继承自 HKEY_* 的“正确”权限)。 - Matteo Italia
在我的产品KO Approach中,用户可以创建一个文件夹,将他们经常访问的项目的快捷方式放置其中。当然,这个文件夹存储在用户配置文件下。整个功能称为Approach Items。在卸载期间,我需要显示已创建的文件夹列表(由计算机的所有用户创建),以提醒可能仍存在应用程序痕迹。因此,显然,我需要一个存储所有Approach Items路径的列表。在我的情况下,它是HKLM\Software\KO Software\Approach。所以我需要使这个键可写。 - Kerido
好的,我同意这是最好的解决方案;不过,我会在HKLM\Software\KO Software\Approach\Items下为每个用户创建一个子键,只允许创建者(以及管理员)访问它们。 - Matteo Italia
4个回答

4

获取和设置键的ACL需要使用RegGetKeySecurity和RegSetKeySecurity。然后,您需要遍历ACE,并检查适用于“Users”组SID的任何条目。接下来,您将修改/删除现有条目并/或添加新条目。请注意,在普通的Win32 C中处理ACL可能很麻烦。


2

最简单的授权访问代码包含3个API调用。它为所有经过身份验证的用户和管理员提供对给定hkey的完全访问权限。

此片段不包含适当的错误处理和报告。请勿将其复制/粘贴到生产代码中。


    PSECURITY_DESCRIPTOR sd = nullptr;
    ULONG sd_size = 0;
    TCHAR* rights = TEXT( "D:" )                // Discretionary ACL
                  TEXT( "(A;OICI;GA;;;AU)" )    // Allow full control to all authenticated users
                  TEXT( "(A;OICI;GA;;;BA)" );   // Allow full control to administrators

    ConvertStringSecurityDescriptorToSecurityDescriptor( rights, SDDL_REVISION_1, &sd, &sd_size );
    RegSetKeySecurity( hkey, DACL_SECURITY_INFORMATION, sd );
    LocalFree( sd );

检测“用户”是否具有对密钥的写访问权限可能比预期更困难。我最终编写了一个测试值到注册表中并检查写入结果的方法。

翻译后的内容为:

检测用户是否有对密钥的写入访问权限可能比预期的要更加困难。我的解决方法是将一个测试值写入注册表,然后检查写入的结果。

其中,“Original Answer”翻译成“最初的回答”。


2
只是为了扩展Mikhail Vorotilov的回答,并且还从https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl的示例代码中汲取灵感。
bool RegistryGrantAll(HKEY hKey)
{
    bool bResult = false;

    PSECURITY_DESCRIPTOR sd = nullptr;

    const TCHAR* szSD =
        TEXT("D:")                  // Discretionary ACL
        TEXT("(D;OICI;KA;;;BG)")    // Deny access to built-in guests
        TEXT("(D;OICI;KA;;;AN)")    // Deny access to anonymous logon
        TEXT("(A;OICI;KRKW;;;AU)")  // Allow KEY_READ and KEY_WRITE to authenticated users ("AU")
        TEXT("(A;OICI;KA;;;BA)");   // Allow KEY_ALL_ACCESS to administrators ("BA" = Built-in Administrators)

    if (ConvertStringSecurityDescriptorToSecurityDescriptor((LPCTSTR)szSD, SDDL_REVISION_1, &sd, 0))
    {
        auto result = RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, sd);
        if (ERROR_SUCCESS == result)
            bResult = true;
        else
            SetLastError(result);

        // Free the memory allocated for the SECURITY_DESCRIPTOR.
        LocalFree(sd);
    }

    return bResult;
}

如果该函数返回false,则调用GetLastError()获取有关失败原因的更多信息。
代码可以在VS2019上编译并似乎可以正常工作。 我还没有添加代码来检查hKey是否是有效的注册表句柄。
编辑:在进行测试后,我已经对此进行了几次编辑。很抱歉所有的编辑。最终结果比我起初开始时更接近Mikhail的答案。
更多信息链接:

https://learn.microsoft.com/en-us/windows/win32/secbp/creating-a-dacl

https://learn.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptorw

https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings


0
嗨,希望OP仍然对答案感兴趣。这是添加ACEs到ACLs的工作代码,可以用于向注册表或文件系统DACLs添加ACEs。我还没有尝试过其他东西。正如您所注意到的,不需要使用糟糕的RegGetKeySecurity或手动组合ACL。甚至不需要RegOpenKeyEx。有关更多信息,请查看this MS doc.更新:当然需要管理员权限才能执行。
// sk - alloced string / path to needed key
// common look: MACHINE\\Software\\... where MACHINE == HKEY_LOCAL_MACHINE
// google for more address abbrevations
PSECURITY_DESCRIPTOR pSD = 0;
EXPLICIT_ACCESS ea;
PACL pOldDACL = 0, pNewDACL = 0;
if (ERROR_SUCCESS == GetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, &pOldDACL, 0, &pSD)) {
    memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
    ea.grfAccessPermissions = KEY_ALL_ACCESS;
    ea.grfAccessMode = GRANT_ACCESS;
    ea.grfInheritance = NO_INHERITANCE;
    ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
    ea.Trustee.ptstrName = <USERNAME HERE>; //DOMAIN\\USERNAME
    if (ERROR_SUCCESS == SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL)) {
        if (ERROR_SUCCESS == SetNamedSecurityInfo(sk, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, 0, 0, pNewDACL, 0)) {
            if (pSD != 0) LocalFree((HLOCAL)pSD);
            if (pNewDACL != 0) LocalFree((HLOCAL)pNewDACL);
            SAFE_FREE(sk);
            // WE'RE GOOD!
            return ... ;
        } else {
            if (pSD) LocalFree((HLOCAL)pSD);
            if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
            SAFE_FREE(sk);
            // SetNamedSecurityInfo failed
            return  ... ;
        }
    } else {
        if (pSD) LocalFree((HLOCAL)pSD);
        if (pNewDACL) LocalFree((HLOCAL)pNewDACL);
        SAFE_FREE(sk);
        // SetEntriesInAcl failed
        return ... ;
    }
} else {
    if (pSD) LocalFree((HLOCAL)pSD);
    SAFE_FREE(sk);
    // GetNamedSecurityInfo failed
    return ... ;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接