От: | Danchik | ||
Дата: | 11.08.06 14:31 | ||
Оценка: |
This code fragment determines whether the current process is running interactively or as a service (services running in a real user account have a SID with SECURITY_SERVICE_RID in their token; console processes have a SID with SECURITY_INTERACTIVE_RID; services running as BUILTIN\SYSTEM [LocalSystem] have neither). If the process is a LocalSystem service, the fragment checks for interactive access to the desktop, too.
Kudos to Rohan Philips for sharing this!
type
TDServiceUtils = class
protected
class function InternalIsService(out IsService, IsInteractive : Boolean): Integer;
public
....
class function IsService: Boolean;
end;
implementation
class function TDServiceUtils.IsService: Boolean;
var
aIsService : Boolean;
aIsInteractive : Boolean;
begin
aIsService := False;
Result := (InternalIsService (aIsService, aIsInteractive) <> 0) or aIsService;
end;
class function TDServiceUtils.InternalIsService(out IsService, IsInteractive : Boolean): Integer;
var
hProcessToken: THandle;
groupLength: Cardinal;
groupInfo: PTokenGroups;
siaNt: SID_IDENTIFIER_AUTHORITY;
pInteractiveSid: PSID;
pServiceSid: PSID;
ndx: Cardinal;
sanda: SID_AND_ATTRIBUTES;
aSid: PSID;
aKey: HKEY;
aStrKey: string;
aServiceName: string;
dwType: Cardinal;
dwSize: Cardinal;
begin
hProcessToken := 0;
groupLength := 50;
groupInfo := nil;
siaNt := SECURITY_NT_AUTHORITY;
pInteractiveSid := nil;
pServiceSid := nil;
Result := NO_ERROR;
try
// open the token
if not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, hProcessToken) then
begin
Result := GetLastError;
Exit;
end;
// allocate a buffer of default size
groupInfo := AllocMem (groupLength);
// try to get the info
if not GetTokenInformation(hProcessToken, TokenGroups,
groupInfo, groupLength, groupLength) then
begin
// if buffer was too small, allocate to proper size, otherwise error
if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
begin
Result := GetLastError;
Exit;
end;
FreeMem(groupInfo);
groupInfo := AllocMem(groupLength);
if not GetTokenInformation(hProcessToken, TokenGroups,
groupInfo, groupLength, groupLength) then
begin
Result := GetLastError;
Exit;
end;
end;
//
// We now know the groups associated with this token. We want
// to look to see if the interactive group is active in the
// token, and if so, we know that this is an interactive process.
//
// We also look for the "service" SID, and if it's present,
// we know we're a service.
//
// The service SID will be present iff the service is running in a
// user account (and was invoked by the service controller).
//
// create comparison sids
if not AllocateAndInitializeSid(siaNt, 1, SECURITY_INTERACTIVE_RID,
0, 0, 0, 0, 0, 0, 0, pInteractiveSid) then
begin
Result := GetLastError;
Exit;
end;
if not AllocateAndInitializeSid(siaNt, 1, SECURITY_SERVICE_RID,
0, 0, 0, 0, 0, 0, 0, pServiceSid) then
begin
Result := GetLastError;
Exit;
end;
// reset flags
IsInteractive := False;
IsService := False;
// try to match sids
ndx := 0;
while ndx < groupInfo.GroupCount do
begin
sanda := groupInfo.Groups[ndx];
aSid := sanda.Sid;
//
// Check to see if the group we're looking at is one of
// the two groups we're interested in.
//
if EqualSid(aSid, pInteractiveSid) then
begin
//
// This process has the Interactive SID in its
// token. This means that the process is running as
// a console process
//
IsInteractive := True;
IsService := False;
Break;
end else
if EqualSid(aSid, pServiceSid) then
begin
//
// This process has the Service SID in its
// token. This means that the process is running as
// a service running in a user account ( not local system ).
//
IsService := True;
IsInteractive := False;
Break;
end;
Inc (ndx);
end;
if not (IsService or IsInteractive) then
begin
//
// Neither Interactive or Service was present in the current
// users token, This implies that the process is running as
// a service, most likely running as LocalSystem.
//
IsService := True;
// determine if the local system service is interactive. We
// do this y looking in the service's registry and checking
// the Type value for the SERVICE_INTERACTIVE_PROCESS bit
aKey := 0;
aStrKey := Format ('SYSTEM\CurrentControlSet\Services\%s', [aServiceName]);
if RegOpenKeyEx(HKEY_LOCAL_MACHINE,
PChar (aStrKey),
0,
KEY_READ,
aKey) = ERROR_SUCCESS then
begin
dwType := 0;
dwSize := sizeof(DWORD);
if RegQueryValueEx(aKey,
PChar ('Type'),
nil,
nil,
@dwType,
@dwSize) = ERROR_SUCCESS then
begin
if (dwType and SERVICE_INTERACTIVE_PROCESS) <> 0 then
IsInteractive := True;
end;
RegCloseKey(aKey);
end;
end;
finally
if pServiceSid <> nil then
FreeSid (pServiceSid);
if pInteractiveSid <> nil then
FreeSid (pInteractiveSid);
if groupInfo <> nil then
FreeMem (groupInfo);
if hProcessToken <> 0 then
CloseHandle (hProcessToken);
end;
end;