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.
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.