Running application as non-administrative user while elevated

254 Views Asked by At

How (if possible) do you - when you're running as elevated user - start a program with non-administrative rights, when you don't have logon credentials.

Scenario: A program running non-elevated executes a child process with elevated status (via ShellExecute with "runas" verb). This application needs to do something that requires administrative rights (replace the .EXE file that ShellExecute'd while it's in the ProgramFiles hierarchy), and once that's done, it should execute the original program, but without elevated status.

How do I run the original application non-elevated from an elevated program? It should simply run under the same user context as originally run, but I don't have the credentials to supply. I "just" want to strip the administrative token from the user when running the original application.

1

There are 1 best solutions below

1
HeartWare On BEST ANSWER

There were quite a few missing imports/definitions in Delphi, so I had to define them myself. But here is the finished implementation in Delphi of the C++ code from the link provided by Remy:

FUNCTION GetShellWindow : HWND; EXTERNAL 'USER32.DLL';
FUNCTION InitializeProcThreadAttributeList(lpAttributeList : PProcThreadAttributeList ; dwAttributeCount,dwFlags : DWORD ; VAR lpSize : NativeUInt) : ByteBool; stdcall; EXTERNAL 'KERNEL32.DLL';
FUNCTION UpdateProcThreadAttribute(lpAttributeList : PProcThreadAttributeList ; dwFlags : DWORD ; Attribute : NativeUInt ; lpValue : Pointer ; cbSize : NativeUInt ; lpPreviousValue : POINTER = NIL ; lpReturnSize : PSIZE_T = NIL) : ByteBool; OVERLOAD; stdcall; EXTERNAL 'KERNEL32.DLL';
FUNCTION UpdateProcThreadAttribute(lpAttributeList : PProcThreadAttributeList ; dwFlags : DWORD ; Attribute : NativeUInt ; VAR Process : THandle ; lpPreviousValue : Pointer = NIL ; lpReturnSize : PSIZE_T = NIL) : ByteBool; OVERLOAD;
  BEGIN
    Result:=UpdateProcThreadAttribute(lpAttributeList,dwFlags,Attribute,@Process,SizeOf(THandle),lpPreviousValue,lpReturnSize)
  END;
PROCEDURE DeleteProcThreadAttributeList(lpAttributeList : PProcThreadAttributeList); stdcall; EXTERNAL 'KERNEL32.DLL';
CONST PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = $0002000
CONST EXTENDED_STARTUPINFO_PRESENT = $00080000;
TYPE
  STARTUPINFOEXW        = PACKED RECORD
                            StartupInfo         : STARTUPINFOW;
                            lpAttributeList     : PProcThreadAttributeList
                          END;
  STARTUPINFOEX         = STARTUPINFOEXW;

FUNCTION TryRunUnelevated(CONST Prog : TFileName ; CONST Tail : STRING ; CONST StartupDir : STRING = '') : BOOLEAN;
  VAR
    H           : HWND;
    PID         : DWORD;
    Process     : THandle;
    Size        : SIZE_T;
    P           : PProcThreadAttributeList;
    SIEX        : STARTUPINFOEX;
    PI          : PROCESS_INFORMATION;

  BEGIN
    Result:=FALSE; H:=GetShellWindow;
    IF H=0 THEN EXIT;
    IF GetWindowThreadProcessID(H,PID)=0 THEN EXIT;
    Process:=OpenProcess(PROCESS_CREATE_PROCESS,FALSE,PID);
    IF Process=0 THEN EXIT;
    TRY
      InitializeProcThreadAttributeList(NIL,1,0,Size);
      GetMem(P,Size);
      TRY
        IF NOT InitializeProcThreadAttributeList(P,1,0,Size) THEN EXIT;
        TRY
          IF NOT UpdateProcThreadAttribute(P,0,PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,Process) THEN EXIT;
          FillChar(SIEX,SizeOf(STARTUPINFOEX),0);
          SIEX.lpAttributeList:=P;
          SIEX.StartupInfo.cb:=SizeOf(STARTUPINFOEX);
          IF NOT CreateProcess(PChar(Prog),PChar(Tail),NIL,NIL,FALSE,CREATE_NEW_CONSOLE OR EXTENDED_STARTUPINFO_PRESENT,NIL,POINTER(StartupDir),SIEX.StartupInfo,PI) THEN EXIT
        FINALLY
          DeleteProcThreadAttributeList(P)
        END;
        CloseHandle(PI.hProcess);
        CloseHandle(PI.hThread)
      FINALLY
        FreeMem(P)
      END
    FINALLY
      CloseHandle(Process)
    END;
    Result:=TRUE
  END;

PROCEDURE RunUnelevated(CONST Prog : TFileName ; CONST Tail : STRING ; CONST StartupDir : STRING = '');
  BEGIN
    IF NOT TryRunUnelevated(Prog,Tail,StartupDir) THEN RaiseLastOSError
  END;

There are two routines - one that simply returns TRUE/FALSE to signify success or failure, and one that raises an exception if it can't do it.

Edit: The re-declaration of DeleteProcThreadAttributeList is because the declaration of this routine in the standard Delphi sources is wrong - it uses TProcThreadAttributeList instead of PProcThreadAttributeList.