Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Service launching exe in security context of logged in user

90 views
Skip to first unread message

Steve McGrath

unread,
Oct 7, 2004, 5:18:54 PM10/7/04
to
Does anyone have a code sample of using a service to run an exe in the
security context of the interactive desktop user on XP without needing the
users password? A lot of Google links seem to suggest getting a handle to
windows explorer to get some sort of security token to impersonate but I'm
lost.
Can anyone please help???
Thanks,
Steve


Sasan Adami

unread,
Oct 8, 2004, 2:51:58 PM10/8/04
to
Steve McGrath wrote:
>
> Does anyone have a code sample of using a service to run an exe in the
> security context of the interactive desktop user on XP without needing the
> users password? A lot of Google links seem to suggest getting a handle to
> windows explorer to get some sort of security token to impersonate but I'm
> lost.

Hi Steve,

Assuming:
1) your service is running under the LOCAL SYSTEM account (so there's
sufficient rights, read the notes on MSDN for CreateProcessAsUser)
2) there is an interactive user logged on to the system (you might want
to check this too)
here are the steps to do this:
- Get the processid of explorer.exe
- Call OpenProcessToken() with the processid and then DuplicateTokenEx()
to get the right process token
- Call ImpersonateLoggedOnUser() with this token
- Now call CreateProcessAsUser() to launch the exe. Use
'WinSta0\Default' for StartupInfo.lpDesktop parameter.
This works when there's 1 interactive user logged on. I haven tested
what happens on a Terminal Server or XP with fast user switching.

hth,
Sasan.

Steve McGrath

unread,
Oct 8, 2004, 5:43:07 PM10/8/04
to
Hi Sasan,

Do you have working code sample for this. I've searched everywhere on the
net and all I can find are suggestions but no working sample. What you've
described below is beyond my skill level :-(

Thanks,
Steve

"Sasan Adami" <s.a...@no-spam.gmx.net> wrote in message
news:4166E1CE...@no-spam.gmx.net...

Sasan Adami

unread,
Oct 9, 2004, 5:26:12 AM10/9/04
to
Steve McGrath wrote:
>
> Hi Sasan,
>
> Do you have working code sample for this. I've searched everywhere on the
> net and all I can find are suggestions but no working sample. What you've
> described below is beyond my skill level :-(

Hi Steve,

I've posted a unit in the attachments newsgroup.
Note that it needs project Jedi/Marcel van Brakel's api header
translations to compile. These are available here:
http://members.chello.nl/m.vanbrakel2/

Sasan.

Steve McGrath

unread,
Oct 9, 2004, 7:30:58 PM10/9/04
to
Hi Sasan,

Many thanks for taking the time to help and I can now get my service to
launch an exe as the desktop user. Yours is the only sample I have *ever*
come across which shows how this can be done. I wouldn't have figured it out
in a million years!
Once again, many thanks,

Steve

"Sasan Adami" <s.a...@no-spam.gmx.net> wrote in message

news:4167AEB4...@no-spam.gmx.net...

Scott Price

unread,
Oct 11, 2004, 6:42:18 AM10/11/04
to
Sasan Adami wrote:

Very interesting approach and read Susan. Thank you for posting an
example.


Regards,


Scott :)

Colin Wilson

unread,
Oct 11, 2004, 10:28:57 AM10/11/04
to
Steve McGrath wrote:

> Do you have working code sample for this.

You can use the code below. People keep complaining that I forgot to
upload this to my website, so posting it here will kill two birds with
one stone!

It impersonates the logged on user, then loads their profile - so that
things like HKEY_CURRENT_USER work correctly in the context of the user
being impersonated.

You use it like this, from within your service's thread...

procedure TMyServiceThread.Execute;
var
impersonator : TImpersonator;
begin
impersonator := TImpersonator.CreateLoggedOn;
try

... Do whatever you like in the context of the logged on user ...

finally
impersonator.Free // Revert to the SYSTEM account
end
end;

--
Colin - using XanaNews HTTP Transport
e-mail :co...@wilsonc.demon.co.uk
web: http://www.wilsonc.demon.co.uk/delphi.htm

Posted with XanaNews 1.16.4.6

--- Here's the code... ----

unit unitImpersonator;

interface

uses Windows, Classes, SysUtils;

type

TProfileInfo = record
dwSize : DWORD;
dwFlags : DWORD;
lpUserName : PChar;
lpProfilePath : PChar;
lpDefaultPath : PChar;
lpServerName : PChar;
lpPolicyPath : PChar;
hProfile : HKEY;
end;


TImpersonator = class
private
fTokenHandle : THandle;
fImpersonating: boolean;
fProfileLoaded : boolean;
fProfileInfo : TProfileInfo;
procedure Impersonate;
function GetImpersonating: boolean;
function GetHKCURootKey: HKEY;
public
constructor Create (const domain, user, password : string);
constructor CreateLoggedOn; // Impersonate the currently logged on
user.

destructor Destroy; override;

property Impersonating : boolean read GetImpersonating;
property HKCURootKey : HKEY read GetHKCURootKey;
end;

const
PI_NOUI = 1; // Prevents displaying of messages
PI_APPLYPOLICY = 2; // Apply NT4 style policy

function LoadUserProfile (hToken : THandle; var profileInfo :
TProfileInfo) : BOOL; stdcall;
function UnloadUserProfile (hToken, HKEY : THandle) : BOOL; stdcall;
function GetCurrentUserName : string;
function OpenProcessHandle (const process : string) : THandle;

implementation

uses psapi;

function LoadUserProfile (hToken : THandle; var profileInfo :
TProfileInfo) : BOOL; external 'userenv.dll' name 'LoadUserProfileA';
function UnLoadUserProfile (hToken, HKEY : THandle) : BOOL; external
'userenv.dll';

function OpenProcessHandle (const process : string) : THandle;
var
buffer, pid : PDWORD;
bufLen, cbNeeded : DWORD;
hp : THandle;
fileName : array [0..256] of char;
i : Integer;
begin
result := 0;
bufLen := 65536;
GetMem (buffer, bufLen);
try
if EnumProcesses (buffer, bufLen, cbNeeded) then
begin
pid := buffer;
for i := 0 to cbNeeded div sizeof (DWORD) - 1 do
begin
hp := OpenProcess (PROCESS_VM_READ or
PROCESS_QUERY_INFORMATION, False, pid^);
if hp <> 0 then
try
if (GetModuleBaseName (hp, 0, fileName, sizeof (fileName)) >
0) and
(CompareText (fileName, process) = 0) then
begin
result := hp;
break
end
finally
if result = 0 then
CloseHandle (hp)
end;

Inc (pid)
end
end
finally
FreeMem (buffer)
end
end;

function GetExplorerProcessToken : THandle;
var
explorerProcessHandle : THandle;
begin
explorerProcessHandle := OpenProcessHandle ('explorer.exe');
if explorerProcesshandle <> 0 then
try
if not OpenProcessToken (explorerProcessHandle, TOKEN_QUERY or
TOKEN_IMPERSONATE or TOKEN_DUPLICATE, result) then
RaiseLastOSError;
finally
CloseHandle (explorerProcessHandle)
end
else
result := INVALID_HANDLE_VALUE;
end;

function GetCurrentUserName : string;
var
unLen : DWORD;
begin
unLen := 512;
SetLength (result, unLen);
GetUserName (PChar (result), unLen);
result := PChar (result);
end;

{ TImpersonator }

constructor TImpersonator.Create(const domain, user, password: string);
begin
if LogonUser (PChar (user), PChar (domain), PChar (password),
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, fTokenHandle) then
Impersonate;
end;

procedure TImpersonator.Impersonate;
var
userName : string;
begin
fImpersonating := ImpersonateLoggedOnUser (fTokenHandle);
if fImpersonating then
begin
userName := GetCurrentUserName;

ZeroMemory (@fProfileInfo, sizeof (fProfileInfo));
fProfileInfo.dwSize := sizeof (fProfileInfo);
fProfileInfo.lpUserName := PChar (userName);
fProfileInfo.dwFlags := PI_APPLYPOLICY;
fprofileLoaded := LoadUserProfile (fTokenHandle, fProfileInfo);
end
end;

constructor TImpersonator.CreateLoggedOn;
begin
fTokenHandle := GetExplorerProcessToken;

if fTokenHandle <> INVALID_HANDLE_VALUE then
Impersonate;
end;

destructor TImpersonator.Destroy;
begin
if fProfileLoaded then
UnloadUserProfile (fTokenHandle, fProfileInfo.hProfile);

if fImpersonating then
RevertToSelf;

CloseHandle (fTokenHandle);
end;

function TImpersonator.GetImpersonating: boolean;
begin
result := fImpersonating and fProfileLoaded;
end;

function TImpersonator.GetHKCURootKey: HKEY;
begin
result := fProfileInfo.hProfile
end;

end.

Scott Price

unread,
Oct 12, 2004, 4:30:59 AM10/12/04
to
> You can use the code below. People keep complaining that I forgot to
> upload this to my website, so posting it here will kill two birds with
> one stone!
>
> It impersonates the logged on user, then loads their profile - so that
> things like HKEY_CURRENT_USER work correctly in the context of the
> user being impersonated.
>
> You use it like this, from within your service's thread...
>
> procedure TMyServiceThread.Execute;
> var
> impersonator : TImpersonator;
> begin
> impersonator := TImpersonator.CreateLoggedOn;
> try
>
> ... Do whatever you like in the context of the logged on user ...
>
> finally
> impersonator.Free // Revert to the SYSTEM account
> end
> end;

Thanks Colin, I will have a look at that as well :)


Scott :)

0 new messages