You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Win32 API获取的文件DACL条目与文件属性信息不匹配?

Windows文件权限解析问题排查与解决

1. 获取文件安全信息的代码

ULONG result = GetSecurityInfo(Hfile
        , SE_FILE_OBJECT
        , OWNER_SECURITY_INFORMATION |GROUP_SECURITY_INFORMATION| DACL_SECURITY_INFORMATION 
        , &sidowner
        , &sidgroup
        , &pdacl
        , NULL
        , &psd);

2. 提取访问控制条目(ACE)的代码

BOOL b = GetAce(pdacl, i, (LPVOID*)&ace); 
if (((ACCESS_ALLOWED_ACE*)ace)->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) {
    sid = (PSID)&((ACCESS_ALLOWED_ACE*)ace)->SidStart;
    LookupAccountSid(NULL, sid, oname, &namelen, doname, &domainnamelen, &peUse);
    wcout << "domianName/AccoutName : " << doname << "/" << oname << endl;
    mask = ((ACCESS_ALLOWED_ACE*)ace)->Mask;
    cout << "Allowed" << endl;
}

3. API输出结果

domianName/AccoutName : BUILTIN/Administrators
Allowed
 

    DELETE
     FILE_GENERIC_READ
     FILE_GENERIC_WRITE
     FILE_GENERIC_EXECUTE
     READ_CONTROL
     WRITE_DAC
     WRITE_OWNER
     SYNCHRONIZE
    
    domianName/AccoutName : BUILTIN/Administrators
    Allowed
     DELETE
     FILE_GENERIC_READ
     FILE_GENERIC_WRITE
     FILE_GENERIC_EXECUTE
     READ_CONTROL
     WRITE_DAC
     WRITE_OWNER
     SYNCHRONIZE
    
    domianName/AccoutName : BUILTIN/Administrators
    Allowed
     DELETE
     FILE_GENERIC_READ
     FILE_GENERIC_WRITE
     FILE_GENERIC_EXECUTE
     READ_CONTROL
     SYNCHRONIZE
    
    domianName/AccoutName : BUILTIN/Users
    Allowed
     FILE_GENERIC_READ
     FILE_GENERIC_WRITE
     FILE_GENERIC_EXECUTE
     READ_CONTROL
     SYNCHRONIZE

4. CACLS命令输出结果

C:\Users\Administrator>cacls e:/hello.txt
e:\hello.txt BUILTIN\Administrators:F
             NT AUTHORITY\SYSTEM:F
             NT AUTHORITY\Authenticated Users:C
             BUILTIN\Users:R

存在的问题

  • Administrators出现多条重复的ACE条目
  • 未找到NT AUTHORITY\SYSTEM和NT AUTHORITY\Authenticated Users的ACE
  • 文件属性中Users仅拥有读取权限,但API输出显示其拥有写入权限

问题原因与解决方案

1. 重复ACE条目

重复条目来自继承权限与显式权限的叠加。Windows文件系统中,文件会继承父目录的ACE,同时可添加显式ACE。需通过ACE的AceFlags字段判断是否为继承条目(INHERITED_ACE标志),过滤重复项:

// 在处理ACE时添加判断
if ((allowedAce->Header.AceFlags & INHERITED_ACE) != 0) {
    // 跳过继承条目或标记为继承
    continue;
}

2. 缺失SYSTEM和Authenticated Users的ACE

代码仅处理了ACCESS_ALLOWED_ACE_TYPE类型的ACE,需遍历所有ACE类型,同时确保循环覆盖pdacl->AceCount所有索引:

for (DWORD i = 0; i < pdacl->AceCount; i++) {
    BOOL b = GetAce(pdacl, i, (LPVOID*)&ace);
    if (!b) continue;

    switch(ace->Header.AceType) {
        case ACCESS_ALLOWED_ACE_TYPE:
            // 处理允许ACE
            break;
        case ACCESS_DENIED_ACE_TYPE:
            // 处理拒绝ACE
            break;
        // 可扩展处理其他ACE类型,如审核类ACE
    }
}

3. Users权限显示不符

API返回的Mask是原始权限位,FILE_GENERIC_WRITE是通用权限宏,需用MapGenericMask映射为具体文件权限,同时需考虑拒绝ACE的优先级(拒绝ACE会覆盖允许ACE):

// 映射通用权限到具体权限
FILE_GENERIC_MAPPING fileMapping = {
    FILE_GENERIC_READ,
    FILE_GENERIC_WRITE,
    FILE_GENERIC_EXECUTE,
    FILE_ALL_ACCESS
};
DWORD specificMask = mask;
MapGenericMask(&specificMask, &fileMapping);

// 检查实际有效权限,比如写入权限
if (specificMask & FILE_WRITE_DATA) {
    // 实际拥有写入权限
} else {
    // 无写入权限
}

另外,LookupAccountSid需正确初始化缓冲区大小,避免因缓冲区不足导致的解析错误:

WCHAR oname[256], doname[256];
DWORD namelen = _countof(oname);
DWORD domainnamelen = _countof(doname);
SID_NAME_USE peUse;

// 第一次调用获取缓冲区大小(若需要)
if (!LookupAccountSid(NULL, sid, oname, &namelen, doname, &domainnamelen, &peUse)) {
    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
        // 分配足够大的缓冲区,此处简化处理
        namelen = 256;
        domainnamelen = 256;
        LookupAccountSid(NULL, sid, oname, &namelen, doname, &domainnamelen, &peUse);
    }
}

内容的提问来源于stack exchange,提问作者Brighton Joel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 01:24:28