Как получить все (не отключенные) пользовательские SID через Windows API? - PullRequest
0 голосов
/ 05 июля 2019

Я ищу способ получения всех пользовательских идентификаторов безопасности в системе с помощью Windows API.

Извлечение всех пользовательских SID может быть выполнено с помощью wmic useraccount get sid. Есть ли способ получить эту информацию через Windows API вместо этого?

Кроме того, команда wmic возвращает SID всех учетных записей, включая отключенные учетные записи - wmic useraccount get disabled,sid покажет, какие учетные записи отключены. Было бы неплохо, если бы решение могло посоветовать, как получить SID учетных записей, которые не отключены, но это не имеет решающего значения.

Ответы [ 3 ]

2 голосов
/ 05 июля 2019

Вы можете использовать функцию:

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-netuserenum и https://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, параметр bufptr будет указывать на массив структур USER_INFO_3, который содержит пользовательские RID . Член usri3_user_id содержит относительный идентификатор (RID) пользователя, а член usri3_primary_group_id содержит RID основной глобальной группы для пользователя. Используя эти значения, вам не нужно звонить LookupAccountNameW.

Эффективность повышается благодаря предложениям от RbMm в комментариях ниже.

0 голосов
/ 07 июля 2019

для перечисления учетных записей пользователей в базе данных SAM (Security Account Manager), которые мы можем использовать, или NetQueryDisplayInformation (более быстро) или NetUserEnum (если нам нужна более подробная информация о пользователе). или SAM api (самый быстрый, включая ntsam.h и ссылку с samlib.lib )

обратите внимание, что если у нас есть пользователь (RID), нам не нужно использовать LookupAccountName - в этом случае это очень неэффективно (многие тяжелые удаленные вызовы внутренние - LsaOpenPolicy, LsaLookupNames2, LsaClose внутренний LsaLookupNames2 в любом случае используйте SAM api SamLookupNamesInDomain). действительно все, что нам нужно - сначала получить домен SID , а затем добавить к нему пользователя RID . получить SID домена можно LsaQueryInformationPolicy с PolicyAccountDomainInformation для SID домена учетной записи (компьютера) - всегда существует и с PolicyDnsDomainInformation или PolicyPrimaryDomainInformation для получения SID основного домена (существует только если компьютерная часть домена)

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;
0 голосов
/ 05 июля 2019

Существует несколько способов.

Простой способ с 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;
}
...