[英]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-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
包含用戶的相對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- LsaOpenPolicy
, LsaLookupNames2
, LsaClose
。內部LsaLookupNames2
使用SAM api SamLookupNamesInDomain
)。 實際上,我們需要的一切-首先獲取域SID,然后將用戶RID附加到該域。 我們可以通過LsaQueryInformationPolicy
獲取域SID, LsaQueryInformationPolicy
使用PolicyAccountDomainInformation
來獲取帳戶域(計算機)的PolicyDnsDomainInformation
始終存在,並使用PolicyDnsDomainInformation
或PolicyPrimaryDomainInformation
來獲取主域的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.