Delphi - 如何检查任何Windows用户是否为管理员?

10

我需要检查本地机器上有哪些类型的帐户。

我已经找到了如何为当前登录用户执行此操作:

function IsWindowsAdmin: Boolean;
var
   hAccessToken: THandle;
   ptgGroups: PTokenGroups;
   dwInfoBufferSize: DWORD;
   psidAdministrators: PSID;
   g: Integer;
   bSuccess: BOOL;
begin
   Result:= False;
   bSuccess:= OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, hAccessToken);
   if not bSuccess then
   begin
     if GetLastError = ERROR_NO_TOKEN then
       bSuccess:= OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, hAccessToken);
   end;

   if bSuccess then
   begin
     GetMem(ptgGroups, 1024);
     bSuccess:= GetTokenInformation(hAccessToken, TokenGroups, ptgGroups, 1024, dwInfoBufferSize);
     CloseHandle(hAccessToken);
     if bSuccess then
     begin
       AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2,
                                SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
                                0, 0, 0, 0, 0, 0, psidAdministrators);
       for g:= 0 to ptgGroups.GroupCount - 1 do
         if EqualSid(psidAdministrators, ptgGroups.Groups[g].Sid) then
         begin
           Result:= True;
           Break;
         end;
       FreeSid(psidAdministrators);
     end;
     FreeMem(ptgGroups);
   end;
end;

我还找到了如何获取本地计算机上的所有用户。有没有可能在不登录每个用户的情况下检查任何用户是否是管理员或受限帐户?


请在这里查看:https://stackoverflow.com/q/7295326/2292722 - Tom Brunberg
@TomBrunberg:“…无需为每个用户登录”-请注意,您链接的问题的所有答案都需要登录到每个用户,因为它们依赖于用户令牌来查询成员资料。 - Remy Lebeau
哎呀,我的错 @Remy。那么,实际上有什么解决方案吗? - Tom Brunberg
2
非常快速的搜索建议使用 NetUserEnumUSER_INFO_2。它不起作用吗? - Sertac Akyuz
好的,谢谢。我会尝试一下。同时也许我找到了一个解决方法。我确定问题解决后会更新帖子的。 - Georgi Bonchev
1个回答

9
以下是我对这个主题的看法,感谢Sertac Akyuz提供有关使用 NetUserEnum 的提示,并使用请求和结构USER_INFO_2

我们可以请求以下权限级别的用户。

const
  USER_PRIV_GUEST = 0;
  USER_PRIV_USER  = 1;
  USER_PRIV_ADMIN = 2;
  USER_PRIV_ANY   = 3; // own invention

type
  TPrivLevel = USER_PRIV_GUEST..USER_PRIV_ANY;

我们需要一些声明

const
  // some consts
  NERR_Success = 0;
  MAX_PREFERRED_LENGTH = $FFFFFFFF;

type
  NetApiStatus = DWORD;

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa371337(v=vs.85).aspx
  TUserInfo2 = record
    usri2_name: LPWSTR ;
    usri2_password: LPWSTR ;
    usri2_password_age: DWORD  ;
    usri2_priv: DWORD  ;
    usri2_home_dir: LPWSTR ;
    usri2_comment: LPWSTR ;
    usri2_flags: DWORD  ;
    usri2_script_path: LPWSTR ;
    usri2_auth_flags: DWORD  ;
    usri2_full_name: LPWSTR ;
    usri2_usr_comment: LPWSTR ;
    usri2_parms: LPWSTR ;
    usri2_workstations: LPWSTR ;
    usri2_last_logon: DWORD  ;
    usri2_last_logoff: DWORD  ;
    usri2_acct_expires: DWORD  ;
    usri2_max_storage: DWORD  ;
    usri2_units_per_week: DWORD  ;
    usri2_logon_hours: PBYTE  ;
    usri2_bad_pw_count: DWORD  ;
    usri2_num_logons: DWORD  ;
    usri2_logon_server: LPWSTR ;
    usri2_country_code: DWORD  ;
    usri2_code_page: DWORD  ;
  end;
  PUSER_INFO_2 = ^TUserInfo2;
  LPUSER_INFO_2 = ^TUserInfo2;


// https://msdn.microsoft.com/en-us/library/windows/desktop/aa370304(v=vs.85).aspx
function NetApiBufferFree (Buffer: Pointer): NetApiStatus ;
                     stdcall; external 'netapi32.dll';

// https://msdn.microsoft.com/en-us/library/windows/desktop/aa370652%28v=vs.85%29.aspx
function NetUserEnum(
  servername: LPCWSTR;
  level: DWORD;
  filter: DWORD;
  var bufptr: pointer;
  prefmaxlen: DWORD;
  var entriesread: DWORD;
  var totalentries: DWORD;
  resume_handle: LPDWORD
): NetApiStatus; stdcall; external 'netapi32.dll';

PrivLevel是您要查询用户的特权级别,users是一个TStringList,其中包含将要被填充的用户名。

function GetUsers(PrivLevel: TPrivLevel; users: TStrings): integer;
var
  i: integer;
  NetApiStatus: DWORD;
  bufptr: pointer;
  recptr: PUSER_INFO_2;
  EntriesRead,
  TotalEntries,
  HResume: DWORD;
begin
  HResume := 0;

  repeat
    NetApiStatus := NetUserEnum(
      nil,                         // local
      2,                           // USER_INFO_2
      0,                           // no special filter
      bufptr,
      MAX_PREFERRED_LENGTH,
      EntriesRead,
      TotalEntries,
      @HResume
    );

    if (NetApiStatus = NERR_Success) or (NetApiStatus = ERROR_MORE_DATA) then
    begin
      recptr := bufptr;
      for i := 0 to EntriesRead-1 do
      begin
        if (PrivLevel = USER_PRIV_ANY) or (recptr^.usri2_priv = PrivLevel) then
          users.Add(recptr^.usri2_name);
        inc(recptr);
      end;
      NetApiBufferFree(bufptr);
    end;

  until NetApiStatus <> ERROR_MORE_DATA;
  Result := NetApiStatus;
end;

使用示例

procedure TForm1.Button1Click(Sender: TObject);
var
  res: integer;
begin
  res := GetUsers(USER_PRIV_ADMIN, Memo.Lines);
  if res <> 0 then Memo.Lines.Add('Error getting users! Error code '+IntToStr(res));
end;

编辑

我将GetUsers()更改为函数,并从对NetUserEnum()的调用中返回成功/错误代码。

根据文档,可能的返回值如下:

NERR_Success = 0
ERROR_ACCESS_DENIED  =    5; // $0005
ERROR_INVALID_LEVEL  =  124; // $007C
ERROR_MORE_DATA      =  234; // $00EA
NERR_BufTooSmall     = 2123; // $084B
NERR_InvalidComputer = 2351; // $092F

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接