Home > C/C++, Delphi, Programming, Tech > Launch your application in Vista under the local system account without the UAC popup

Launch your application in Vista under the local system account without the UAC popup

I know this isn’t real new, hoowever, this is still pretty cool and occasionally necessary. As expected, this works on Win7 as well. This is useful within a service application in order to execute an application with elevated credentials on a standard user’s active desktop.

Someone even took the code (written in C++) and converted it to my preferred native programming language, Delphi.

Click ‘Read More’ to view the Delphi code.

unit main;

interface

uses
  Windows,
  JwaWinNT,
  JwaWinBase,
  JwaWtsApi32,
  JwaWinSvc,
  JwaWinType,
  JwaNtStatus,
  PsAPI,
  TlHelp32,
  Messages,
  SysUtils,
  Classes,
  Graphics,
  Controls,
  SvcMgr,
  Dialogs;

type
  TService1 = class(TService)
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;

var
  Service1: TService1;

const
  SE_DEBUG_NAME = 'SeDebugPrivilege';

function CreateEnvironmentBlock(var lpEnvironment: Pointer;
  hToken: THandle;
  bInherit: BOOL): BOOL; stdcall; external 'userenv';

function DestroyEnvironmentBlock(pEnvironment: Pointer): BOOL; stdcall; external 'userenv';

implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure Err(const Msg: string);
var
  FEventLogger: TEventLogger;
begin

  FEventLogger := TEventLogger.Create('service_name');
  try
    FEventLogger.LogMessage(Msg, EVENTLOG_ERROR_TYPE, 0, 0);
  finally
    FreeAndNil(FEventLogger);
  end;
end;

function startapp: integer;
var
  pi: PROCESS_INFORMATION;
  si: STARTUPINFO;
  bresult: boolean;
  dwSessionId, winlogonPid: DWORD;
  hUserToken, hUserTokenDup, hPToken, hProcess, hsnap: THANDLE;
  dwCreationFlags: DWORD;
  procEntry: TlHelp32.TProcessEntry32;
  winlogonSessId: DWORD;
  tp: TOKEN_PRIVILEGES;
  abcd, abc, dup: integer;
  lpenv: pointer;
  iResultOfCreateProcessAsUser: integer;

begin
  Result := 0;
  bresult := false;

  //TOKEN_ADJUST_SESSIONID := 256;

  // Log the client on to the local computer.
  dwSessionId := WTSGetActiveConsoleSessionId();
  hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

  if (hSnap = INVALID_HANDLE_VALUE) then
  begin
    result := 1;
    exit;
  end;

  procEntry.dwSize := sizeof(TPROCESSENTRY32);

  if (not Process32First(hSnap, procEntry)) then
  begin
    result := 1;
    exit;
  end;

  repeat
    if (comparetext(procEntry.szExeFile, 'winlogon.exe') = 0) then
    begin
      // We found a winlogon process...
       // make sure it's running in the console session

      winlogonSessId := 0;
      if (ProcessIdToSessionId(procEntry.th32ProcessID, winlogonSessId)
        and (winlogonSessId = dwSessionId)) then
      begin
        winlogonPid := procEntry.th32ProcessID;
        break;
      end;
    end;

  until (not Process32Next(hSnap, procEntry));

  ////////////////////////////////////////////////////////////////////////

  WTSQueryUserToken(dwSessionId, hUserToken);
  dwCreationFlags := NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE;
  ZeroMemory(@si, sizeof(STARTUPINFO));
  si.cb := sizeof(STARTUPINFO);
  si.lpDesktop := 'winsta0\default';
  ZeroMemory(@pi, sizeof(pi));
  hProcess := OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid);

  if (not OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY
    or TOKEN_DUPLICATE or TOKEN_ASSIGN_PRIMARY or TOKEN_ADJUST_SESSIONID
    or TOKEN_READ or TOKEN_WRITE, hPToken)) then
  begin
    abcd := GetLastError();
    err('Process token open Error: ' + inttostr(GetLastError()));
  end;

  if (not LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid)) then
  begin
    err('Lookup Privilege value Error: ' + inttostr(GetLastError()));
  end;
  tp.PrivilegeCount := 1;
  tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;

  DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, nil,
    SecurityIdentification, TokenPrimary, hUserTokenDup);
  dup := GetLastError();

  // Adjust Token privilege

  SetTokenInformation(hUserTokenDup,
    TokenSessionId, pointer(dwSessionId), sizeof(DWORD));

  if (not AdjustTokenPrivileges(hUserTokenDup, FALSE, @tp, sizeof(TOKEN_PRIVILEGES),
    nil, nil)) then
  begin
    abc := GetLastError();
    err('Adjust Privilege value Error: ' + inttostr(GetLastError()));
  end;

  if (GetLastError() = ERROR_NOT_ALL_ASSIGNED) then
  begin
    err('Token does not have the provilege');
  end;

  lpEnv := nil;

  if (CreateEnvironmentBlock(lpEnv, hUserTokenDup, TRUE)) then
  begin
    dwCreationFlags := dwCreationFlags or CREATE_UNICODE_ENVIRONMENT;
  end
  else
    lpEnv := nil;

  // Launch the process in the client's logon session.

  bResult := CreateProcessAsUser(
    hUserTokenDup, // client's access token
    'D:\panorama\panorama.exe', // file to execute
    nil, // command line
    nil, // pointer to process SECURITY_ATTRIBUTES
    nil, // pointer to thread SECURITY_ATTRIBUTES
    FALSE, // handles are not inheritable
    dwCreationFlags, // creation flags
    lpEnv, // pointer to new environment block
    'D:\panorama', // name of current directory
    si, // pointer to STARTUPINFO structure
    pi // receives information about new process
    );

  // End impersonation of client.
  //GetLastError Shud be 0

  iResultOfCreateProcessAsUser := GetLastError();

  //Perform All the Close Handles tasks
  CloseHandle(hProcess);
  CloseHandle(hUserToken);
  CloseHandle(hUserTokenDup);
  CloseHandle(hPToken);
end;

end.
Advertisements
Categories: C/C++, Delphi, Programming, Tech
  1. Federico
    August 16, 2009 at 4:19 AM

    Hello, this works great but executes the program as SYSTEM in the session of the logged on user.
    What if I need my system service to start the program in the currently logged user session with the user privileges? I mean, without elevation and nothing…
    Is that doable? Can you help pointing me the right direction?
    Thanks in advance.

    • Mick
      August 17, 2009 at 9:36 AM

      You have a couple of options. The easiest (in my estimation) is to write an application that runs in the user’s session either in the system tray, or completely hidden. Then build your service to communicate with this other application via:

      – named pipes (http://stackoverflow.com/questions/512366/how-do-i-send-a-string-from-one-instance-of-my-delphi-program-to-another)
      – shared memory
      – or possibly windows messages

      If you’d like to have the service do it correctly, it’s a bit more tricky. First, your service would need to determine which logged on user (if multiple are logged on) to run the app for. Next it would need to duplicate their token (copy from a process you know that user owns…such as explorer.exe), then create the process using their token.

      Here is the basic idea (in C++):
      1. Get the explorer.exe process ( result is Process ID)
      2. Open Process ( result is Process handle hToken )
      3. Open Process Token with hToken ( Result is phToken)

      then

      . // Impersonate the Logger on user token
      ImpersonateLoggedOnUser(phToken);

      // Creates new access token that duplicates the existing one
      DuplicateTokenEx(phToken, NULL, NULL, (SECURITY_IMPERSONATION_LEVEL)NULL, TokenPrimary, &hToken);

      and then start the process

      // Start the child process.
      if(!CreateProcessAsUser(hToken, // User Token
      NULL, // No module name (use command line)
      strCommand, // Command line
      NULL, // Process handle not inheritable
      NULL, // Thread handle not inheritable
      FALSE, // Set handle inheritance to FALSE
      dwCreationFlags, // No creation flags
      pEnv, // Use parent’s environment block
      NULL, // Use parent’s starting directory
      &si, // Pointer to STARTUPINFO structure
      &pi) // Pointer to PROCESS_INFORMATION structure
      ) ;

      CloseHandle(hToken);
      CloseHandle(phToken);

      RevertToSelf();

      // WaitForSingleObject(pi.hProcess, -1) ;

      // Close process and thread handles.
      CloseHandle(pi.hProcess);
      CloseHandle(pi.hThread);

      //================================

      Here is a much cleaner solution, written in Delphi:

      http://blog.delphi-jedi.net/2008/04/15/how-to-get-the-users-token-from-a-service-2/

  2. Federico
    August 17, 2009 at 9:39 AM

    Thanks, I’ll keep investigating the second option. It’s much more neat… I don’t want to run a process in the tray or so, I’d prefer to launch the app directly from within my system service.
    Thank you!!

  3. cli
    January 27, 2012 at 4:01 AM

    if (CreateEnvironmentBlock(@lpEnv, hUserTokenDup, TRUE)) then
    —need @ before lpEnv –

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s