我在之前的一篇文章里(小小地实现一个 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.
运行结果: