通過令牌(Token)獲取登錄用戶信息


我在之前的一篇文章里(小小地實現一個 whoami)寫到查詢當前進程的登錄用戶信息,但這個代碼對於用 LogonUser 這種函數獲取的令牌的 SID(Logon SID)是無效的(可以參考 MSDN 的 LookupAccountSid function 這篇文章的 Remarks 部分)。因此這里需要另外的方法來獲取我們所需的信息。

思路:對於登錄令牌,如果用之前的方法,LookuoAccountSid 函數會設置 LastError = ERROR_NONE_MAPPED,因此我們不能繼續使用 TokenUser 參數了。我在 MSDN 的 GetTokenInformation function 這篇文章里查找相關信息后發現,我們可以使用 TokenAccessInformation 這個參數來獲取令牌的訪問信息,這里面就包含了我們所需的 SID。

代碼(在 Delphi XE2 上編譯通過):

program GetTokenIdent;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Windows;

type
  SID_HASH_ENTRY = ULONG_PTR;

  _SID_AND_ATTRIBUTES_HASH = record
     SidCount: DWORD;
     SidAttr: ^SID_AND_ATTRIBUTES;
     Hash: array [0..31] of SID_HASH_ENTRY;
  end;
  SID_AND_ATTRIBUTES_HASH = _SID_AND_ATTRIBUTES_HASH;
  PSID_AND_ATTRIBUTES_HASH = ^SID_AND_ATTRIBUTES_HASH;

function GetTokenIdentity(hToken: THandle): String;
var
  UserName, UserDomain: String;
  cbName, cbDomainName: ULONG;
  ReturnLength: DWORD;
  Buff: array of Byte;
  tai: PTokenAccessInformation;
  saah: PSID_AND_ATTRIBUTES_HASH;
  saa: PSIDAndAttributes;
  peUse: SID_NAME_USE;
  i: Integer;
label
  Cleanup;
begin
  Result := '';

  // 獲取令牌信息
  if not GetTokenInformation(hToken, TokenAccessInformation, nil, 0, ReturnLength) then
    if GetLastError = ERROR_INSUFFICIENT_BUFFER then
    begin
      SetLength(Buff, ReturnLength);
      GetTokenInformation(hToken, TokenAccessInformation, @Buff[0], ReturnLength, ReturnLength);
      tai := @Buff[0];
    end
    else
      Exit
  else
    Exit;

  // 由於 Delphi XE2 里 TOKEN_ACCESS_INFORMATION 的結構有誤,因此我必須對其進行轉換
  // TOKEN_ACCESS_INFORMATION 的 SidHash 結構包含了用戶 SID 和組 SID
  saah := PSID_AND_ATTRIBUTES_HASH(tai.SidHash);
  saa := PSIDAndAttributes(saah.SidAttr);

  for i := 0 to saah.SidCount - 1 do
  begin
    cbName := 0;
    cbDomainName := 0;
    if not LookupAccountSid(nil, saa.Sid, nil, cbName, nil, cbDomainName, peUse) then
      if GetLastError = ERROR_INSUFFICIENT_BUFFER then
      begin
        SetLength(UserName, cbName);
        SetLength(UserDomain, cbDomainName);
        if LookupAccountSid(nil, saa.Sid, @UserName[1], cbName, @UserDomain[1], cbDomainName, peUse) then
        begin
          SetLength(UserName, cbName);
          SetLength(UserDomain, cbDomainName);
          Result := UserDomain + '\' + UserName;
          // 如果 peUse 的類型為 SidTypeUser 則表明這就是我們所需的用戶信息了
          if peUse = SidTypeUser then Break;
        end;
      end;
    Inc(saa);
  end;

  SetLength(Buff, 0);
end;

var
  hToken: THandle;

begin
  try
    LogonUser('TestUser', '.', '123456',LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT, hToken);
    Writeln(GetTokenIdentity(hToken));
    CloseHandle(hToken);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

運行結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM