簡體   English   中英

如何通過Windows API獲取所有(非禁用)用戶SID?

[英]How to get all (non-disabled) user SIDs via Windows API?

我正在尋找一種通過Windows API檢索系統上所有用戶SID的方法。

可以通過wmic useraccount get sid檢索所有用戶SID。 是否可以通過Windows API獲取此信息?

另外, wmic命令返回所有帳戶的SID,包括禁用的帳戶wmic useraccount get disabled,sid將顯示哪些帳戶被禁用。 如果有解決方案可以建議如何檢索未禁用帳戶的SID,那將是一個額外的好處,但這並不重要。

您可以使用以下功能:

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=3bufptr參數將指向USER_INFO_3結構的數組,其中包含用戶RID 成員usri3_user_id包含用戶的相對ID(RID),成員usri3_primary_group_id包含用戶的主要全局組的RID。 使用這些值,您無需調用LookupAccountNameW

使用以下評論中的RbMm的建議可以提高效率。

有幾種方法。

一個簡單的方法是使用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;
}

對於枚舉SAM(安全帳戶管理器)數據庫中的用戶帳戶,我們可以使用或NetQueryDisplayInformation (更快)或NetUserEnum (如果需要更多詳細的用戶信息)。 或SAM api(最快,包括ntsam.h並與samlib.lib鏈接)

請注意,如果我們有用戶(RID),則不需要使用LookupAccountName-在這種情況下這效率不高(很多繁重的遠程調用internal- LsaOpenPolicyLsaLookupNames2LsaClose 。內部LsaLookupNames2使用SAM api SamLookupNamesInDomain )。 實際上,我們需要的一切-首先獲取域SID,然后將用戶RID附加到該域。 我們可以通過LsaQueryInformationPolicy獲取域SID, LsaQueryInformationPolicy使用PolicyAccountDomainInformation來獲取帳戶域(計算機)的PolicyDnsDomainInformation始終存在,並使用PolicyDnsDomainInformationPolicyPrimaryDomainInformation來獲取主域的SID (僅當Domain的計算機部分存在時才存在)

void PrintUsersInDomain(PUNICODE_STRING ServerName, PSID DomainSid)
{
    PWSTR szServerName = 0;

    if (ServerName)
    {
        if (ULONG Length = ServerName->Length)
        {
            szServerName = ServerName->Buffer;
            // if not null terminated
            if (Length + sizeof(WCHAR) < ServerName->MaximumLength || *(PWSTR)((PBYTE)szServerName + Length))
            {
                szServerName = (PWSTR)alloca(Length + sizeof(WCHAR));
                memcpy(szServerName, ServerName->Buffer, Length);
                *(PWSTR)((PBYTE)szServerName + Length) = 0;
            }
        }
    }

    UCHAR SubAuthorityCount = *GetSidSubAuthorityCount(DomainSid);
    ULONG DestinationSidLength = GetSidLengthRequired(SubAuthorityCount + 1);

    PSID UserSid = alloca(DestinationSidLength);
    CopySid(DestinationSidLength, UserSid, DomainSid);
    ++*GetSidSubAuthorityCount(UserSid);
    PULONG pRid = GetSidSubAuthority(UserSid, SubAuthorityCount);

    PVOID Buffer;
    ULONG Index = 0, ReturnedEntryCount;

    NET_API_STATUS status;

    do 
    {
        switch (status = NetQueryDisplayInformation(szServerName, 1, Index, 
            64, MAX_PREFERRED_LENGTH, &ReturnedEntryCount, &Buffer))
        {
        case NOERROR:
        case ERROR_MORE_DATA:
            if (ReturnedEntryCount)
            {
                PNET_DISPLAY_USER pndu = (PNET_DISPLAY_USER)Buffer;

                do 
                {
                    //if (!(pndu->usri1_flags & UF_ACCOUNTDISABLE))
                    {
                        *pRid = pndu->usri1_user_id;

                        PWSTR szSid;
                        if (ConvertSidToStringSidW(UserSid, &szSid))
                        {
                            DbgPrint("\t[%08x] %S %S\n", pndu->usri1_flags, pndu->usri1_name, szSid);
                            LocalFree(szSid);
                        }
                    }

                    Index = pndu->usri1_next_index;

                } while (pndu++, --ReturnedEntryCount);
            }

            NetApiBufferFree(Buffer);
        }
    } while (status == ERROR_MORE_DATA);
}

void PrintUsersInDomain_fast(PUNICODE_STRING ServerName, PSID DomainSid)
{
    SAM_HANDLE ServerHandle, DomainHandle = 0;

    //SAM_SERVER_ENUMERATE_DOMAINS|SAM_SERVER_LOOKUP_DOMAIN
    NTSTATUS status = SamConnect(ServerName, &ServerHandle, SAM_SERVER_LOOKUP_DOMAIN, 0);

    DbgPrint("SamConnect(%wZ) = %x\n", ServerName, status);

    if (0 <= status)
    {
        status = SamOpenDomain(ServerHandle, DOMAIN_READ|DOMAIN_EXECUTE, DomainSid, &DomainHandle);

        SamCloseHandle(ServerHandle);
    }

    if (0 <= status)
    {
        UCHAR SubAuthorityCount = *GetSidSubAuthorityCount(DomainSid);
        ULONG DestinationSidLength = GetSidLengthRequired(SubAuthorityCount + 1);

        PSID UserSid = alloca(DestinationSidLength);
        CopySid(DestinationSidLength, UserSid, DomainSid);
        ++*GetSidSubAuthorityCount(UserSid);
        PULONG pRid = GetSidSubAuthority(UserSid, SubAuthorityCount);

        PVOID Buffer;

        ULONG Index = 0, TotalAvailable, TotalReturned, ReturnedEntryCount;

        do 
        {
            if (0 <= (status = SamQueryDisplayInformation(DomainHandle,
                DomainDisplayUser,
                Index,
                2,
                0x10000,
                &TotalAvailable,
                &TotalReturned,
                &ReturnedEntryCount,
                &Buffer)))
            {
                if (ReturnedEntryCount)
                {
                    PSAM_DISPLAY_USER psdu = (PSAM_DISPLAY_USER)Buffer;
                    do 
                    {
                        //if (!(psdu->AccountControl & USER_ACCOUNT_DISABLED))
                        {
                            *pRid = psdu->Rid;

                            PWSTR szSid;
                            if (ConvertSidToStringSidW(UserSid, &szSid))
                            {
                                DbgPrint("\t[%08x] %wZ %S\n", psdu->AccountControl, &psdu->AccountName, szSid);
                                LocalFree(szSid);
                            }
                        }

                        Index = psdu->Index;

                    } while (psdu++, --ReturnedEntryCount);
                }
                SamFreeMemory(Buffer);
            }
        } while (status == STATUS_MORE_ENTRIES);

        SamCloseHandle(DomainHandle);
    }
}

void PrintUsers()
{
    LSA_HANDLE PolicyHandle;

    LSA_OBJECT_ATTRIBUTES ObjectAttributes = { sizeof(ObjectAttributes) };
    NTSTATUS status;
    if (0 <= (status = LsaOpenPolicy(0, &ObjectAttributes, POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle)))
    {
        union {
            PVOID buf;
            PPOLICY_DNS_DOMAIN_INFO pddi;
            PPOLICY_ACCOUNT_DOMAIN_INFO padi;
        };

        if (0 <= LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation, &buf))
        {
            DbgPrint("DomainName=<%wZ>\n", &padi->DomainName);
            if (padi->DomainSid) 
            {
                PrintUsersInDomain_fast(&padi->DomainName, padi->DomainSid);
                PrintUsersInDomain(&padi->DomainName, padi->DomainSid);
            }
            LsaFreeMemory(buf);
        }

        if (0 <= LsaQueryInformationPolicy(PolicyHandle, PolicyDnsDomainInformation, &buf))
        {
            DbgPrint("DomainName=<%wZ>\n", &pddi->Name);
            if (pddi->Sid) 
            {
                PrintUsersInDomain_fast(&pddi->Name, pddi->Sid);
                PrintUsersInDomain(&pddi->Name, pddi->Sid);
            }
            LsaFreeMemory(buf);
        }
        LsaClose(PolicyHandle); 
    }
}

typedef struct SAM_DISPLAY_USER {
    ULONG Index;
    ULONG Rid;
    ULONG AccountControl; /* User account control bits */
    UNICODE_STRING AccountName;
    UNICODE_STRING AdminComment;
    UNICODE_STRING FullName;
} *PSAM_DISPLAY_USER;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM