![]() |
Service: UAC Elevated Child Prozess im Benutzerkontext starten
Hallo zusammen,
nachdem ich nun fast 4 Stunden herumprobiert habe, poste ich hier mal eine Lösung, mit der man aus einem Dienst heraus einen Prozess mit UAC Elevation starten kann. Ist der momentan eingeloggte Benutzer kein Administrator, wird der Prozess mit eingeschränkten Rechten gestartet. Das Besondere an dieser Lösung ist, dass der neu erzeugte Prozess auch wirklich im Kontext des Benutzers und nicht als SYSTEM läuft.
Delphi-Quellcode:
Lauffähig sollte das ganze ab Windows 2000 sein.
function OpenShellProcessToken(ProcessName: String;
var hToken: THandle): Boolean; var hSnapshot, hProcess: THandle; Process: TProcessEntry32; begin Result := false; hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot <> 0) and (hSnapshot <> INVALID_HANDLE_VALUE) then try FillChar(Process, SizeOf(Process), #0); Process.dwSize := SizeOf(Process); if Process32First(hSnapshot, Process) then repeat if (AnsiLowerCase(Process.szExeFile) = AnsiLowerCase(ProcessName)) then begin hProcess := OpenProcess(PROCESS_ALL_ACCESS, false, Process.th32ProcessID); if (hProcess <> 0) and (hProcess <> INVALID_HANDLE_VALUE) then try Result := OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, hToken); finally CloseHandle(hProcess); end; Break; end; until (not Process32Next(hSnapshot, Process)); finally CloseHandle(hSnapshot); end; end; function CreateProcessElevated(lpApplicationName: PChar; lpCommandLine: PChar; lpCurrentDirectory: PChar; var ProcessInfo: TProcessInformation): Boolean; var WTSGetActiveConsoleSessionId: function: DWord; stdcall; WTSQueryUserToken: function(SessionId: ULONG; var phToken: THandle): BOOL; stdcall; CreateEnvironmentBlock: function(lpEnvironment: PPointer; hToken: THandle; bInherit: BOOL): BOOL; stdcall; DestroyEnvironmentBlock: function(lpEnvironment: LPVOID): BOOL; stdcall; var hUserToken, hLinkedToken, hElevatedToken: THandle; ReturnLength, ElevationType: DWord; Environment: Pointer; StartupInfo: TStartupInfo; begin Result := false; @CreateEnvironmentBlock := GetProcAddress(LoadLibrary('userenv.dll'), 'CreateEnvironmentBlock'); @DestroyEnvironmentBlock := GetProcAddress(LoadLibrary('userenv.dll'), 'DestroyEnvironmentBlock'); if (not Assigned(CreateEnvironmentBlock)) or (not Assigned(DestroyEnvironmentBlock)) then Exit; @WTSGetActiveConsoleSessionId := GetProcAddress(LoadLibrary('kernel32.dll'), 'WTSGetActiveConsoleSessionId'); @WTSQueryUserToken := GetProcAddress(LoadLibrary('wtsapi32.dll'), 'WTSQueryUserToken'); if (Assigned(WTSGetActiveConsoleSessionId) and Assigned(WTSQueryUserToken)) then begin Result := WTSQueryUserToken(WTSGetActiveConsoleSessionId, hUserToken); end else begin Result := OpenShellProcessToken('explorer.exe', hUserToken); end; if Result then try if GetTokenInformation(hUserToken, TokenElevationType, @ElevationType, SizeOf(ElevationType), ReturnLength) then begin if (ElevationType = 3) then begin if GetTokenInformation(hUserToken, TokenLinkedToken, @hLinkedToken, SizeOf(hLinkedToken), ReturnLength) then try Result := DuplicateTokenEx(hLinkedToken, MAXIMUM_ALLOWED, nil, SecurityImpersonation, TokenPrimary, hElevatedToken); finally CloseHandle(hLinkedToken); end; end else begin hElevatedToken := hUserToken; end; try if CreateEnvironmentBlock(@Environment, hElevatedToken, false) then try FillChar(StartupInfo, SizeOf(StartupInfo), #0); StartupInfo.cb := SizeOf(StartupInfo); Result := CreateProcessAsUser(hElevatedToken, lpApplicationName, lpCommandLine, nil, nil, false, CREATE_NEW_CONSOLE or CREATE_DEFAULT_ERROR_MODE or CREATE_UNICODE_ENVIRONMENT, Environment, lpCurrentDirectory, StartupInfo, ProcessInfo); finally DestroyEnvironmentBlock(Environment); end; finally CloseHandle(hElevatedToken); end; end; finally CloseHandle(hUserToken); end; end; Viele Grüße Zacherl |
AW: Service: UAC Elevated Child Prozess im Benutzerkontext starten
Zitat:
Aber egal, Hauptsache der Code funktioniert. Mich würde natürlich trotzdem gerne interessieren, bei welcher Zeile jetzt dem System gesagt wurde, dass der Prozess Admin-Rechte bekommt. Und was mich auch brennend interessiert ist, was passiert, wenn eine Anwendung mit Admin-Manifest ohne Admin-Rechte gestartet wurde. Übrigens: Da ist ein größerer Fehler drin: Laut ![]() |
AW: Service: UAC Elevated Child Prozess im Benutzerkontext starten
Zitat:
Ich konvertiere das Token dann in ein Primary Token namens ElevatedToken. Danach kann ich das "alte" Token ja ohne weiteres schließen :) Das Neue bleibt ja erhalten. Zitat:
Zitat:
![]() Wobei ich sehe grade, dass ich den Typen als DWord deklariert habe. Ist das korrekt für ein C++ Enum oder muss da besser Byte hin? |
AW: Service: UAC Elevated Child Prozess im Benutzerkontext starten
Zitat:
Zitat:
![]() ADD: Es scheint dabei aber dem Compiler überlassen zu sein, wie groß der Typ dann wirklich ist (siehe ![]() Bernhard |
AW: Service: UAC Elevated Child Prozess im Benutzerkontext starten
Danke dir fürs nachschlagen, dann werd ichs einfach mal so lassen. Habe zwar bisher nur unter Win7 64 bit getestet, aber da funktionierts schonmal wunderbar :)
|
AW: Service: UAC Elevated Child Prozess im Benutzerkontext starten
Glückwunsch für diesen Akt. Ich will deine Freude nicht schmälern, aber den Code hättest du sicher auch mit etwas Suche und Geduld hier in irgendeiner Form gefunden. Trotzdem ist die daraus gewonnene Erkenntnis und Erfahrung unersetzlich und wird dir bei ähnlichen Problemen helfen.
Wenn du eine Anwendung mit Manifest, aber ohne Admintoken in CreateProcessAsUser starten willst, wird CPAU fehlschlagen und die Fehlermeldung ERROR_ELEVATION_REQUIRED (740) über GetLastError zurückmelden. Dann muss man ShellExecute (nicht im Dienst, sondern über extra Prozess!) verwenden. Jedes Tokenn besitzt ein Feld LinkedToken, das ein weiteres Token enthält. Wenn UAC aktiv ist, dann bekommt man darüber das Admintoken. Das Admintoken hat auch so ein Feld und man bekommt darüber das eingeschränkte Token. Es ist also ein Ringschluss. Nun ist es aber dennoch möglich auch als normaler Benutzer das Admintoken über dieses Feld zu laden, jedoch verweigert CPAU und ImpersonateLoggedOnUser die Nutzung. Mehr Info siehe dazu in der Hilfe zur letztgenannten Funktion. Enums haben tatsächlich keine exakte Größe, wenn man allgemein spricht. Man kann aber Compilern normalerweise irgendwie mitteilen, wie groß mindestens ein Enum sein soll, z.B. in Delphi mit $MINENUMSIZE (oder ähnlich). MS C++ Compiler kann es auch und das Windows SDK nutzt es auch. Am sichersten kannst du es sehen, indem du den Datentyp in C mit sizeof auf dessen Größe prüfst. |
AW: Service: UAC Elevated Child Prozess im Benutzerkontext starten
Zitat:
Zitat:
KUBA |
Alle Zeitangaben in WEZ +1. Es ist jetzt 18:30 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz