如何通过Windows API获取所有(非禁用)用户SID?

时间:2019-07-05 08:15:59

标签: c++ c windows winapi sid

我正在寻找一种通过Windows API检索系统上所有用户SID的方法。

可以通过wmic useraccount get sid来检索所有用户SID。是否可以通过Windows API获取此信息?

此外,wmic命令返回所有帐户的SID,包括禁用的帐户-wmic useraccount get disabled,sid将显示哪些帐户已禁用。如果有解决方案可以建议如何检索未禁用的帐户的SID,那将是一个好处,但这并不重要。

3 个答案:

答案 0 :(得分:2)

您可以使用以下功能:

NET_API_STATUS NET_API_FUNCTION NetUserEnum(
  LPCWSTR servername,
  DWORD   level,
  DWORD   filter,
  LPBYTE  *bufptr,
  DWORD   prefmaxlen,
  LPDWORD entriesread,
  LPDWORD totalentries,
  PDWORD  resume_handle
);

servername = NULL枚举本地计算机帐户,然后使用:

BOOL LookupAccountNameW(
  LPCWSTR       lpSystemName,
  LPCWSTR       lpAccountName,
  PSID          Sid,
  LPDWORD       cbSid,
  LPWSTR        ReferencedDomainName,
  LPDWORD       cchReferencedDomainName,
  PSID_NAME_USE peUse
);

检索 SID

有关详细信息和示例,请参考https://docs.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netuserenumhttps://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-lookupaccountnamew

在功能NetUserEnum中,设置参数level=1将返回有关用户帐户的详细信息,而bufptr参数将指向USER_INFO_1结构的数组。

检查结构usri1_flags的成员USER_INFO_1并使用掩码UF_ACCOUNTDISABLE给出帐户的状态。

在RbMm注释之后,请注意,在功能NetUserEnum中指定参数level=3的{​​{1}}参数将指向bufptr结构的数组,其中包含用户 RID 。 成员USER_INFO_3包含用户的相对ID(RID),成员usri3_user_id包含用户的主要全局组的RID。使用这些值,您无需调用usri3_primary_group_id

RbMm在下面的评论中提出了一些建议,从而提高了效率。

答案 1 :(得分:0)

有几种方法。

一个简单的方法就是使用NetQueryDisplayInformation

测试示例(Windows 10,VS 2015)=>

NET_API_STATUS NetStatus;
DWORD dwIndex = 0;
DWORD dwEntriesRequested = 0xFFFFFFFF;
DWORD dwPreferredMaximumLength = 0xFFFFFFFF;
DWORD dwReturnedEntryCount;
PVOID pNDU = NULL;
do {
    NetStatus = NetQueryDisplayInformation(NULL, 1, dwIndex, dwEntriesRequested, dwPreferredMaximumLength, &dwReturnedEntryCount, &pNDU);
    if (NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA)
        break;
    for (int i = 0; i < dwReturnedEntryCount; i++)
    {
        PNET_DISPLAY_USER NetDisplayUser = (PNET_DISPLAY_USER)(((LPBYTE)pNDU) + sizeof(NET_DISPLAY_USER) * i);              
        PSID pSID = ConvertNameToSID(NetDisplayUser->usri1_name);
        LPWSTR pszSid = NULL;
        ConvertSidToStringSid(pSID, &pszSid);
        BOOL bIsAccountDisabled = ((NetDisplayUser->usri1_flags & UF_ACCOUNTDISABLE) != 0) ? TRUE : FALSE;
        WCHAR wsBuffer[MAX_PATH];           
        wsprintf(wsBuffer, L"%4.4ld %-20.20ws SID : %ws - Disabled : %ws - Comment : %ws\n",
            NetDisplayUser->usri1_next_index,
            NetDisplayUser->usri1_name,
            pszSid,
            (bIsAccountDisabled ? L"True" : L"False"),
            NetDisplayUser->usri1_comment
        );
        LocalFree(pSID);

        OutputDebugString(wsBuffer);
        dwIndex = NetDisplayUser->usri1_next_index;                 
    }
    NetApiBufferFree(pNDU);
} while (NetStatus == ERROR_MORE_DATA);

PSID ConvertNameToSID(LPTSTR lpszName)
{
    WCHAR wszDomainName[256];
    DWORD dwSizeDomain = sizeof(wszDomainName) / sizeof(TCHAR);
    DWORD dwSizeSid = 0;
    SID_NAME_USE sidName;
    LookupAccountName(NULL, lpszName, NULL, &dwSizeSid, wszDomainName, &dwSizeDomain, &sidName);
    PSID pSid;
    pSid = (PSID)LocalAlloc(LPTR, dwSizeSid);
    LookupAccountName(NULL, lpszName, pSid, &dwSizeSid, wszDomainName, &dwSizeDomain, &sidName);
    return pSid;
}

答案 2 :(得分:0)

对于枚举SAM(安全帐户管理器)数据库中的用户帐户,我们可以使用或NetQueryDisplayInformation(更快)或NetUserEnum(如果我们需要更多详细的用户信息)。或SAM api(最快,包括ntsam.h并与 samlib.lib 链接)

请注意,如果我们拥有用户(RID),则不需要使用 LookupAccountName -在这种情况下,这效率非常低(内部有许多繁重的远程调用-LsaOpenPolicy,{{1} },LsaLookupNames2。内部LsaClose仍要使用SAM api SamLookupNamesInDomain)。 实际上,我们需要的一切-首先获取域 SID ,然后将用户 RID 附加到该域。我们可以通过LsaLookupNames2来获取域SID,其中LsaQueryInformationPolicy代表帐户域(计算机)的SID-始终存在,并使用PolicyAccountDomainInformationPolicyDnsDomainInformation来获取 SID (仅在域的计算机部分存在)

PolicyPrimaryDomainInformation