我在之前的一篇文章里(小小地實現一個 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.
運行結果: