views:

3914

answers:

5

We need to change some settings to the HKEY_LOCAL_MACHINE at runtime.

Is it possible to prompt for uac elevation if needed at runtime, or do I have to launch a second elevated process to do 'the dirty work'?

+6  A: 

You can't "elevate" an existing process. Elevated processes under UAC have a different token with a different LUID, different mandatory integrity level, and different group membership. This level of change can't be done within a running process - and it would be a security problem if that could happen.

You need to launch a second process elevated that would do the work or by creating a COM object that runs in an elevated dllhost.

http://msdn.microsoft.com/en-us/library/bb756922.aspx gives an example "RunAsAdmin" function and a "CoCreateInstanceAsAdmin" function.

EDIT: I just saw "Delphi" in your title. Everything I listed is obviously native, but if Delphi provides access to ShellExecute-like functionality you should be able to adapt the code from the link.

Michael
I will look into it. Delphi is native and provides full access to the win32 api, including ShellExecute( ). Tanks.
Vegar
+12  A: 

There is a good article with Delphi source about how to play nice with UAC and Delphi available at the following as a PDF. It includes a few critical code snippets that will greatly help.

skamradt
+1  A: 

+1 for Pascal Fonteneau's PDF.
I used it last week in conjunction with this partial article to create a COM object that runs elevated from within a non-elevated application to write keys/values to the registry.
BTW, I think you mean HKEY_LOCAL_MACHINE not HKEY_CURRENT_MACHINE ;-)

Conor Boyd
A: 

I saw good article in the famous Jedi Lib too

Hugues Van Landeghem
The Jedi example for "elevating parts of an application" depend on a COM object, and calling into that. The downside of using a COM object is that you have to write a COM object, and even worse: register it on the user's computer. It is easier to pass yourself instructions on the command line, or in shared memory, or though a named pipe.
Ian Boyd
Using the command line to pass instructions is rather problematic if you have to use credentials or rather large amount of data.A named pipe should not be used since it can be connected from nearly everywhere on the computer. Instead use the pipe handle and send it to the new process (it can inherit the handles by CreateProcess). Be careful with shared memory because it may open a vulnerability (mainly buffer overflow). The elevated process must carefully check the input.
ChristianWimmer
+3  A: 

i would relaunch yourself as elevated, passing command line parameters indicating what elevated thing you want to do. You can then jump right to the appropriate form, or just save your HKLM stuff.

function RunAsAdmin(hWnd: HWND; filename: string; Parameters: string): Boolean;
{
    See Step 3: Redesign for UAC Compatibility (UAC)
    http://msdn.microsoft.com/en-us/library/bb756922.aspx
}
var
    sei: TShellExecuteInfo;
begin
    ZeroMemory(@sei, SizeOf(sei));
    sei.cbSize := SizeOf(TShellExecuteInfo);
    sei.Wnd := hwnd;
    sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
    sei.lpVerb := PChar('runas');
    sei.lpFile := PChar(Filename); // PAnsiChar;
    if parameters <> '' then
     sei.lpParameters := PChar(parameters); // PAnsiChar;
    sei.nShow := SW_SHOWNORMAL; //Integer;

    Result := ShellExecuteEx(@sei);
end;

The other Microsoft suggested solution is to create an COM object out of process (using the specially created CoCreateInstanceAsAdmin function). i don't like this idea because you have to write and register a COM object.


Note: There is no "CoCreateInstanceAsAdmin" API call. It's just some code floating around. Here's the Dephi version i stumbled around for. It is apparently based on the trick of prefixing a class guid string with the "Elevation:Administrator!new:" prefix when normally hidden code internally calls CoGetObject:

function CoGetObject(pszName: PWideChar; pBindOptions: PBindOpts3; 
      const iid: TIID; ppv: PPointer): HResult; stdcall; external 'ole32.dll';

procedure CoCreateInstanceAsAdmin(const Handle: HWND; 
      const ClassID, IID: TGuid; PInterface: PPointer);
var
   BindOpts: TBindOpts3;
   MonikerName: WideString;
   Res: HRESULT;
begin
   ZeroMemory(@BindOpts, Sizeof(TBindOpts3));
   BindOpts.cbStruct := Sizeof(TBindOpts3);
   BindOpts.hwnd := Handle;
   BindOpts.dwClassContext := CLSCTX_LOCAL_SERVER;

   MonikerName := 'Elevation:Administrator!new:' + GUIDToString(ClassID);

   Res := CoGetObject(PWideChar(MonikerName), @BindOpts, IID, PInterface);
   if Failed(Res) then 
      raise Exception.Create(SysErrorMessage(Res));
end;


One other question: How do you handle someone running as standard user in Windows XP?

Ian Boyd