![]() |
Programm von Dienst starten lassen (Jetzt aber wirklich mal)
Ich habe ein paar Beiträge zu ähnlichen Themen gefunden, jedoch sind diese bestenfalls auf Lösungsansätze hinausgelaufen.
Da ich das immer noch nicht hinbekommen habe, bitte ich hier um Hilfe. Mein Szenario: - Programm benötigt zwingend erhöhte Rechte (Adminrechte), da es diverse Systemeinstellungen ändern können soll - Der Administrator des Systems kann natürlich Programme und Dienste installieren - Nun soll ein normaler Benutzer das Programm verwenden können Meine Idee: - Administrator installiert einen Dienst - Der normale Benutzer startet das Programm, dieses merkt, dass es nicht genügend Rechte hat, benachrichtigt den Dienst (über Pipes) und beendet sich wieder. Der Dienst startet nun das Programm, sodass dieses über die geerbten erhöhten Rechte des Dienstes verfügt, und kann normal verwendet werden. Ist das machbar? Wenn ja, wie genau? ![]() ![]() Ich hoffe sehr, dass wir auf eine allgemein verwendbare Lösung kommen können... |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Tja es ist machbar. Ich habe es schon gemacht. Und ein anderer auch.
Allerdings was du vorschlägst, ist die Vordertüre ins Haus einfach offenstehen zu lassen und zudem noch die Schlüssel zum Tresor auf der Türschwelle. Zitat:
Es gäbe noch die Möglichkeit den Dienst das Programm mit SHARE_DENY_WRITE offenzuhalten. Aber es gibt Mittel und Wege dies während des Bootvorgangs zu umgehen. Zitat:
Der Benutzer ist kein Administrator. Aber vielleicht hat er das Passwort? Ein Aufruf zu LogonUser mit dem Passwort erzeugt das Admintoken. (nur nicht in Vista) Damit kann man mit CreateProcessAsUser einen Prozess mit Adminrechten starten. Aber hier gibt es schon wieder ein Problem: Wo wird das Passwort eingegeben? In einem Dialog natürlich. BÖSE. Der Dialog sollte niemals auf dem Benutzerdesktop angezeigt werden, da jedes andere Programm dies abhöhren könnte (Wenn ein systemweiter Keylogger installiert ist, dann ist eh alles zu spät). Also sollte dieser Dialog in einem eigenen Desktop dargestellt werden. Das dumme dabei ist: Ein Dienst kann das nicht (ohne beträchtlichen Aufwand). Er muss ein Prozess mit normalen Benutzerrechten erzeugen, der den Desktop erzeugt, das Passwort abfragt und es dem Dienst übermittelt. Und das die ganze Kommunikation geht nichtmal mit Pipse, wie Experte Apollonius sicher bezeugen kann (Er hat es ja so gemacht). Nun bleibt noch die Frage, welcher Benutzer will denn den Prozess gestartet haben? Ist der Benutzer vielleicht über RemoteDesktop eingeloggt? Hat der Benutzer ein FastSwitch gemacht und sitzt als anderer Benutzer Y vor dem PC? Sind mehrere angemeldet? Läuft der Benutzer in Session 0 oder noch 1 rum? 0,1,2,3,4,5,6... ? In welche Session soll der Prozess gespawnt werden? Zu allem überfluss muss der Prozess auch noch GUI darstellen können. D.h. er benötigt Rechte in der Windowstation und Desktop. Die LogonSessionID in den Tokengroups ist ideal dazu. Leider funkz das nicht mit LogonUser. Wir benötigen "The function from hell" : LsaLogonUser. Hab ich schon LoadUserProfile erwähnt? Es lädt das Benutzerprofil. Und man sollte es auch wieder entladen, wenn der Prozess fertig ist und seine Kinder. Zitat:
In der JWSCL ist ein Projekt mit dem Namen XP Elevation drin. Es ist noch nicht fertig - leider. Ein Benutzer ohne Adminrechte kann dadurch ein Prozess mit Adminrechten starten. Hat all das was oben drin ist (Mit Ausnahme von Vista-spezifischem, die ich extra gelagert habe). Zudem wird der neue Desktop mit den Benutzerhingergrund bemalt. Echt super Feature :D |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Zitat:
Zitat:
Entsprechend würde ich wirklich gerne eine Lösung mit Verwendung eines Services finden. Das angesprochene "XP Elevation" zielt auf die Lösung mit (der sicheren Methode) der Passworteingabe ab, oder? Und ich würde zudem gerne alle OS ab Win2000 abdecken. Also inklusive Vista. Zitat:
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Aber man kann natürlich den Benutzer für den zu startenden Prozess mit der Administratorgruppe ausstatten. Das ist ja auch das Hauptziel von XP Elevation. Der zu startende Prozess bekommt unter dem aktuellen Benutzer Administratorrechte, ohne dass der Benutzer Administrator sein muss. Dazu bleiben die Profilpfade erhalten. Das einzige was sein muss, ist dass der Benutzer sein Passwort eingeben muss. Aber selbst das könnte man z.B. so machen, dass es nur einmal notwendig ist und nach jedem Dienststart. Das Passwort wird dazu nicht gesichert, sonder das Token selbst. Die Abfrage ala UAC muss jedoch bestehen bleiben, da sonst jedes beliebige Programm ein anderes ohne Benutzerwissen starten kann - mit Adminrechten! Natürlich ist es möglich ein eigenes Token zu erstellen. Dann ist garkeine Passworteingabe mehr notwendig. Dies ist aber nicht unbedingt der Königsweg, da es doch noch einige Fallstricke enthalten könne. Zitat:
Zitat:
Versuch dich dort einzuarbeiten, wenn es Probleme gibt, dann schreibe einfach. Wäre super wenn XP Elevation mehr Unterstützung bekommen würde. |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Hallo Dezipaitor,
musste mich damals von diesem Problem trennen, weil ich mich um andere dringende Dinge kümmern musste. Nun ist das wieder aktuell für mich, weil ich das definitiv immer noch benötige. 1. XP Elevation Ich habe XP Elevation in der aktuellen Version geholt und kriege das aber nach wie vor nicht zum Laufen. Du hast gesagt, Du arbeitest da immer noch dran. Kann es sein, dass es deshalb noch nicht vollständig ist? Zumindest erhalte ich solche Meldungen:
Delphi-Quellcode:
Diese werden aber erst beim Kompilieren angezeigt, sind also noch einige mehr. Sind diese Teile einfach noch nicht veröffentlicht oder habe ich vergessen noch was einzubinden?
[Pascal Fehler] JwsclVersion.pas(854): E2003 Undefinierter Bezeichner: 'IsTerminalServiceRunning'
[Pascal Fehler] JwsclSid.pas(1349): E2003 Undefinierter Bezeichner: 'CachedGetUserFromSid' [Pascal Fehler] JwsclTerminalServer.pas(2129): E2003 Undefinierter Bezeichner: 'TWinStationShadowInformation' [Pascal Fehler] JwsclTerminalServer.pas(2442): E2003 Undefinierter Bezeichner: 'WinStationNameFromLogonIdW' Ich habe mich in den letzten Tagen durch Dutzende von Artikeln zu dem Thema durchgewälzt. Mir ist dabei aufgefallen, dass es sehr wohl noch einige andere Menschen gibt, die gern genau das gleiche machen würden wie ich. Die Ansätze, die als Antworten gegeben wurden, waren unglaublich vielfältig, leider aber keins davon auf Anhieb funktionsfähig. Ich habe viel ausprobiert (2000, XP und Vista laufen bei mir seit Tagen als VM parallel mit ;)) und habe immer wieder eine Erkenntnis dazugewonnen. Nur leider habe ich immer noch keine Lösung. 2. Niedrigere Rechte vergeben Nach meinen jetzigen Recherchen ist es wohl nicht möglich, dass ein Programm ein anderes Programm mit niedrigeren Rechten als den eigenen ausführt. Also bspw. ein Programm mit Adminrechten führt ein Programm mit normalen Benutzerrechten aus. Oder ein Programm mit Systemrechten führt ein Programm mit Adminrechten aus. Geht das denn wirklich nicht? Dass ein Programm nicht zur Laufzeit seine Rechte ändern kann, verstehe ich. Aber dass ein Programm ein anderes nicht einfach so mit geringeren Rechten starten kann, leuchtet mir nicht ein. Es ist doch rein Sicherheitstechnisch absolut nichts dagegen einzuwenden!? 3. Systemrechte mit Benutzerumgebung Das einzige, was ich mir jetzt nach so viel unmöglichen Möglichkeiten noch vorstellen kann, ist das der Service das Programm mit den eigenen Rechten (System) ausführt und als lpEnvironment irgendwie die Umgebung des eingeloggten Benutzers übergibt. Das lässt sich ja hoffentlich auslesen (zB über die Umgebung des Explorers). Ich meine, dass das möglich sein sollte, habe es aber noch nicht hinbekommen. Hast Du (oder natürlich gerne jemand anderes auch!) hier eine Lösung? |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
1. XPElevation
Ich vermute mal, du hast nicht die Entwicklerquellcodeversion. Die ist immer Vorraussetzung, wenn man die internen Beispiele kompilieren will. Zudem ist XPElevation im Branch und nicht im Trunk das aktuelleste. Da arbeite ich noch dran. 2. Man kann immer ein anderers Token nehmen und dies mit CreateRestrictedToken nochmals einschränken. Und als SYSTEM kann man ein eigenes Token erstellen mit LsaLogonUser oder gar CreateToken. XPElevation nutzt LsaLogonUser, d.h. man brauchte in Password. Es kann aber (verschlüsselt) gespeichert werden. 3. Die Benutzerumgebung bringt nichts zur Sicherheit. |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
warum eigentlich so kompliziert?
Letzendlich soll ja nicht ein ganzes Programm mit Adminrechten laufen sondern nur einzelne Funktionen. Im Web wird es ja auch nicht anders gemacht. Sucht man nach etwas im Internet schickt man den Suchbegriff an einen Server und der liefert einem nur das Ergebnis zurück - also auch nicht so das man Zugang zur gesamten Datenbank bekommt. Genau so kann man es ja auch bei einem Service machen. Der Service läuft die ganze Zeit im Hintergrund und nimmt bestimmte Anfragen entgegen und behandelt diese. Das Ergebnis schickt er dann zurück. Und schon hat man nicht mehr das Problem das die Anwendung mehr machen kann als sie soll. |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Wenn das Programm bereits besteht und es eben Adminrechte benötigt, dann macht man es so. Andernfalls müsste man ne Client-Serverbeziehung von vorne herstellen.
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
@SirThornberry
Das Programm besteht eben schon länger und da ist es um ein vielfaches komplizierter, das auszulagern. Zumal würde ich gerne die Option behalten, das Programm (sofern mit Adminrechten gestartet) ohne Service zu benutzen. @Remko I'll have a look. Thanks. @Dezipaitor zu 2. Das andere Token bekomme ich aber wirklich nur von einem bestimmten Administratoraccount auf dem Rechner, oder? Es wäre eben schön, wenn das auch anders gehen könnte und der User nicht gezwungen wäre, irgendein Passwort einzugeben. Kann ich denn als SYSTEM ein Admin-Token einfach so erstellen und das nicht von einem bestehenden Account kopieren? Dann bräuchte man eben keine Login-Daten... zu 3. Ne, nichts zur Sicherheit, aber damit das Programm normal funktionieren kann und z.B. auf Current_User zugreifen kann und auf dessen Desktop dargestellt werden kann. Sonst besteht doch auch das Problem, dass die GUI gar nicht sichtbar ist. |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Zitat:
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Zitat:
Prinzipiell würde ich das ja so machen:
Delphi-Quellcode:
Aber WTSGetActiveConsoleSessionId liefert mir ja nur die SessionID von SYSTEM, bringt also nichts. Wie muss der Code statt dessen lauten, damit ich die Umgebung dann bei CreateProcess mit übergeben kann?
function GetEnvironment(): LPVOID;
var LToken: THandle; LDupToken: THandle; LSessionID: DWORD; begin Result := nil; LSessionID := WTSGetActiveConsoleSessionId; //<- da muss wohl was anderes hin... WTSQueryUserToken(LSessionID, @LToken); DuplicateTokenEx(LToken, TOKEN_ASSIGN_PRIMARY or TOKEN_ALL_ACCESS, nil, SecurityIdentification, TokenPrimary, LDupToken); if (not CreateEnvironmentBlock(Result, LDupToken, True)) then Result := nil; end; function RunProcess(FileName: string): Longword; var StartupInfo: TStartupInfo; ProcessInfo: TProcessInformation; begin FillChar(StartupInfo, SizeOf(StartupInfo), #0); StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_FORCEONFEEDBACK; StartupInfo.wShowWindow := SW_SHOW; if not CreateProcess(nil, @Filename[1], nil, nil, False, NORMAL_PRIORITY_CLASS or CREATE_NEW_PROCESS_GROUP, //<- stimmen die Flags so? GetEnvironment, //<- hier die Umgebung des normalen Benutzers übergeben nil, StartupInfo, ProcessInfo) then Result := WAIT_FAILED; end; Huch, doch schon so spät. Du siehst, mir lässt das keine Ruhe. Ich gehe jetzt erstmal schlafen... |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
WTSQueryUserToken returns a user's primary token so you don't need to duplicate it. Just past the tokenhandle to CreateProcessAsUser and be sure to pass nil for the desktop param of the startupinfo structure. Something like this:
Delphi-Quellcode:
Notes: WTSQueryUserToken needs Windows XP/2003 or higher, if you need windows 2000 compatibility check Jwscl as it contains a WTSQueryUserTokenEx function that also works on Windows 2000.
// Obtain the primary user token
WTSQueryUserToken(SessionId, hToken); if hToken <> 0 then begin // Fill buffer with zeroes ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); si.lpDesktop := nil; if CreateProcessAsUser(hToken, nil, Cmd, nil, nil, False, 0, nil, nil, si, pi) then ... |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Willst du das wirklich alles nur mit der WinAPI machen? Das geht natürlich. Plane aber mal noch 3-4Wochen Zusatzaufwand dafür ein. Ich selbst würde da nur noch die JWSCL benutzen.
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Delphi-Quellcode:
function GetEnvironment(): LPVOID;
var SessionID : DWORD; hToken : THandle; begin Result := nil; WTSQueryUserToken(SessionID, hToken); if (not CreateEnvironmentBlock(Result, hToken, True)) then Result := nil; end; Zitat:
Zitat:
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Ok, nach einer vollständigen und peniblen Neuinstallation funktioniert Jwa und Jwscl jetzt wohl. Scheint was durcheinander gekommen zu sein mit der alten und der aktuellen Version. Nun gut, das geht ja jetzt schon mal. Dann versuche ich jetzt mal da drauf aufzubauen.
Das habe ich jetzt zusammengebasteln:
Delphi-Quellcode:
1. Ich verwende CreateProcess und nicht CreateProcessAsUser, weil ich ja gar nicht brauche, dass das Programm als anderer Benutzer ausgeführt wird. Ist das dann so OK?
var
UserToken: TJwSecurityToken; ConsoleUser : TJwSecurityId; StartupInfo: TStartupInfo; ProcessInfo: TProcessInformation; UserEnvironment : LPVOID; begin UserToken := TJwSecurityToken.CreateWTSQueryUserTokenEx(nil, WtsGetActiveConsoleSessionID); try ZeroMemory(@StartupInfo, SizeOf(StartupInfo)); StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.lpDesktop := nil; StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_FORCEONFEEDBACK; StartupInfo.wShowWindow := SW_SHOW; CreateEnvironmentBlock(UserEnvironment, UserToken.TokenHandle, True); //<- so richtig? CreateProcess(nil, PChar('c:\test.exe'), nil, nil, False, NORMAL_PRIORITY_CLASS or CREATE_NEW_PROCESS_GROUP, UserEnvironment, //<- EnvironmentBlock nil, StartupInfo, ProcessInfo); finally FreeAndNil(UserToken); end; end; 2. Die Umgebung (UserEnvironment) wird nicht richtig verarbeitet. Wenn ich nil statt UserEnvironment übergebe, dann startet das Programm als SYSTEM hat aber auch dessen Umgebungsvariablen. Wenn ich aber versuche, den EnvironmentBlock zu erstellen (wie im Code dargestellt), dann startet das Programm gar nicht mehr. Wo liegt der Fehler? Bzw. wie geht das richtig? 3. Selbst wenn das dann starten würde, bin ich immer noch der Meinung, dass bei Verwendung von WtsGetActiveConsoleSessionID ich die Umgebung von SYSTEM erhalte und nicht vom User. Wie dann? edit: Hm, habe das jetzt mit UserToken.GetTokenUserName überprüft und es ist der Benutzername. Sollte dann also wohl doch so funktionieren... |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Zitat:
![]() |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Du hast wohl alles schon mal gemacht, oder? :)
Habe ein wenig mit Deiner Funktion herumexperimentiert. Hier meine Ergebnisse: Wenn ich die Funktion genau so übernehme, dann wird die Umgebung von "Default User" genommen. Zum Testen schaue ich immer über SHGetSpecialFolderLocation nach, welcher Bentzerordner aus dem Programm heraus angezeigt wird. Nehme ich bei CreateEnvironmentBlock als zweiten Parameter 0, erhalte ich C:\Dokumente und Einstellungen\Default User\Anwendungsdaten Mit der nachfolgenden Änderung kommt das gewünschte C:\Dokumente und Einstellungen\MyUser\Anwendungsdaten Ich denke
Delphi-Quellcode:
sollte deshalb geändert werden.
CreateEnvironmentBlock(@pEnv, 0, true);
Das hier funktioniert als Ersatz:
Delphi-Quellcode:
Damit läuft das Programm als SYSTEM Anwendung aber mit der Umgebung des aktuellen Benutzers. :D
//UserToken := TJwSecurityToken.CreateWTSQueryUserTokenEx(nil, WtsGetActiveConsoleSessionID); //nur ab XP :(
UserToken := TJwSecurityToken.CreateCompatibilityQueryUserToken(MAXIMUM_ALLOWED, 'explorer.exe'); //auch für 2000! :) JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, true); Bleibt noch eine Frage: Für Vista lässt sich da nichts mehr anpassen, oder? Da wird das Programm nämlich auf dem speziellen Desktop gestartet und entsprechend nicht normal angezeigt. Ja, ich weiß, das ist eine gewollte sicherheitsbedingte Änderung gewesen, aber vielleicht lässt sich da ja trotzdem noch was machen? |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
CreateWTSQueryUserTokenEx funktioniert nur unter Windows 2000. Unter XP kann es funkzen, muss aber nicht. Unter Vista funktioniert es nicht!
Damit dieser Desktopwechsel unter Vista nicht auftritt, musst man die TokenSessionID (Property) auf die jeweilige ID des Users setzen und dann damit das Programm starten.
Delphi-Quellcode:
var T,t2 : TJwSecurityToken;
S : TStartupInfo; P : TProcessInformation; begin T := TJwSecurityToken.CreateTokenEffective(MAXIMUM_ALLOWED); T2 := TJwSecurityToken.CreateDuplicateExistingToken(T.TokenHandle, MAXIMUM_ALLOWED); // T2.TokenSessionId := 2; ZeroMemory(@S, Sizeof(S)); S.cb := sizeof(S); CreateProcessAsUser(T2.TokenHandle,'C:\Windows\system32\cmd.exe',nil,nil,nil,false, 0, nil, nil, S,P); ... |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Aber letztlich tut
Delphi-Quellcode:
ja vollkommen das, was es soll. Nachteile sehe ich keine.
UserToken := TJwSecurityToken.CreateCompatibilityQueryUserToken(MAXIMUM_ALLOWED, 'explorer.exe');
Dein Vista-Beispiel verstehe ich allerdings nicht. Ich schreibe doch da nicht wirklich eine 2 als TokenSessionId hinein? Jedenfalls funktioniert das Codeschnippsel bei mir auch nicht. Ich vermute, es liegt an der 2, weiß aber nicht, was ich da sonst übergeben soll. Klärst Du mich da bitte auf? |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Eine Session (oder Sitzung) ist ein abgetrennter Bereich, der eigene WindowStations und Desktops besitzt (und einiges andere).
Jeder Benutzer, der sich per Winlogon einloggt, wird eine neue SessionID zugewiesen. In Windows 2000 gibt es nur die 0. In XP bekommt der erste Benutzer die 0, alle anderen, 1 oder größer. Dienste tummeln sich immer in Session 0 und sind daher mit dem ersten eingeloggten Benutzer in einem Boot. Daher können Dienst auf den Benutzerdesktop zugreifen und CreateCompatibilityQueryUserToken findet Explorer.exe In Vista jedoch, hat der erste Benutzer die Session ID #1 und die Dienste #0. Da jede Session ihren eigenen Desktop hat (vereinfacht ausgedrückt) und Explorer.exe nur für normale Benutzer gestartet wird, gibt es Probleme. Die Session ID #2, dich ich angegeben habe, habe ich mit der Absicht angegeben, dass man einen Fehler bekommst. Nur in diesem Fall macht man sich Gedanken drüber. (Hatte vorhin wenig Zeit was zu schreiben). |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Vielen Dank für die Erläuterung! Damit wird so einiges klarer.
Hole mir die TokenSessionID jetzt dynamisch vom aktuellen Benutzer und erstelle für diesen auch gleich den passenden Umgebungsblock. Scheint alles zu funktionieren. :) Intern werde ich also drei verschiedene Blöcke für 2000, XP und Vista basteln, damit das jeweils korrekt funktioniert. Gehe ich richtig in der Annahme, dass sich 2003 wie XP verhält? Und 2008 wie Vista? Stehen mir momentan leider nicht zum Testen zur Verfügung... Christian, ich möchte noch loswerden, dass ich ganz großen Respekt vor Deinem Wissen und Deiner Leistung habe. Allein wenn ich sehe, was alles in der JWSCL steckt, kann ich nur staunen. Wenn man aber noch bedenkt, dass da jemand dahinter steckt, der das alles versteht und systematisch aufgebaut hat, kann man nur noch gratulieren. Dann nimmst Du Dir aber noch die Zeit, mir kleinem Programmierer, bei meinen Problemen zu helfen. Einfach klasse. Vielen Dank für alles! :hello: |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Great that you got it all working! Maybe you can post your final code for future reference?
Just adding some notes, also future reference: Vista SP1 and Server 2008 share the same kernel and terminal server. I don't see why WTSQueryUserTokenEx should fail on vista (it uses undocumented api from JwaWinsta but actually it does exactly the same as WTSQueryUserToken internally). Don't forget this note about WTSGetActiveConsoleSession: Return Value The session identifier of the session that is attached to the physical console. If there is no session attached to the physical console, (for example, if the physical console session is in the process of being attached or detached), this function returns 0xFFFFFFFF. On Windows 2000 with Terminal Server it's safe to assume session 0 is the console session. If you launch a process as user system you always get the environment of Default User because system doesn't have one, same goes for CURRENT_USER in the registry. |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Sure, this is my final code (so far ;)):
Delphi-Quellcode:
Getestet mit 2000, XP und Vista und für gut befunden. :)
ZeroMemory(@StartupInfo, sizeof(StartupInfo));
StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.lpDesktop := 'winsta0\default'; pCmdLine := TJwPChar('"'+App+'" ' + Parameters); pCurDir := Nil; if Length(CurDir) > 0 then pCurDir := TJwPChar(CurDir); //get the token from the service system session ServiceToken := TJwSecurityToken.CreateTokenEffective(MAXIMUM_ALLOWED); //copy the token to be able to change the TokenSessionId //Info: Win2000: Only 0 // WinXP: Service=0, 1.User=0, 2.User=1, 3.User=2, ... // WinVista: Service=0, 1.User=1, 2.User=2, 3.User=3, ... CopiedToken := TJwSecurityToken.CreateDuplicateExistingToken(ServiceToken.TokenHandle, MAXIMUM_ALLOWED); //get the token of the logged in user if is2000 then UserToken := TJwSecurityToken.CreateCompatibilityQueryUserToken(MAXIMUM_ALLOWED, 'explorer.exe') else //XP, 2003, Vista, 2008 UserToken := TJwSecurityToken.CreateWTSQueryUserTokenEx(nil, WtsGetActiveConsoleSessionID); //give the copied token the same sessionid as the logged in user CopiedToken.TokenSessionId := UserToken.TokenSessionId; //create the environment block using the logged in user JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, true); try if not CreateProcessAsUser( { TODO : UNICODE VERSION? } CopiedToken.TokenHandle, TJwPChar(App), //__in_opt LPCTSTR lpApplicationName, pCmdLine, //__inout_opt LPTSTR lpCommandLine, nil, //__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, nil, //__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, true, //__in BOOL bInheritHandles, CREATE_NEW_CONSOLE or CREATE_DEFAULT_ERROR_MODE or CREATE_UNICODE_ENVIRONMENT, //__in DWORD dwCreationFlags, pEnv, //__in_opt LPVOID lpEnvironment, pCurDir, //__in_opt LPCTSTR lpCurrentDirectory, StartupInfo, //__in LPSTARTUPINFO lpStartupInfo, ProcInfo //__out LPPROCESS_INFORMATION lpProcessInformation ) then raiseLastOsError; finally DestroyEnvironmentBlock(pEnv); end; CloseHandle(ProcInfo.hProcess); CloseHandle(ProcInfo.hThread); "is2000" ist eine Prozedur, die prüft, ob das aktuelle Betriebssystem Win2000 ist. Müsste man sich also noch dazubasteln. Einige der Schritte (z.B. SessionID kopieren, CreateProcessAsUser statt CreateProcess, ...) sind nicht für alle Betriebssysteme notwendig, schaden aber auch nicht. Ich fand es sinnvoller, den Code übersichtlich zu lassen, als wirklich unterschiedliche Blöcke für jedes Betriebssystem zu erstellen. Was ich noch nicht so recht weiß, ist, ob es wirklich notwendig ist, die Unicode Version von CreateProcessAsUser (mit entsprechenden Parametern) zu verwenden. Sehe aber auch keine Nachteile, weshalb ich das wohl noch machen werde. Anmerkungen und Verbesserungsvorschläge werden natürlich gern angenommen. ;) |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Delphi-Quellcode:
sowieso. Da komme ich ja nicht drumherum, wenn ich die Benutzerumgebung mitgeben will.
JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, false);
Wenn ich TJwAutoPointer.Wrap(...) verwende, brauche ich dann die try-finally Blöcke trotzdem noch oder reicht das tatsächlich einfach nach jedem Erzeugen das Token einmal zu wrappen? Deine anderen Hinweise habe ich soweit befolgt und den Code angepasst. Danke! |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Entweder wrappen oder try/finally.
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Ja was ist denn? Funktioniert es oder nicht?
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Ja, klar. Hatte das ja schon bei meinem letzen Posting geschrieben. Habe eben noch die kleineren Anpassungen, die Du geschrieben hast, eingebaut.
So sieht das ganze in der letzten Version aus:
Delphi-Quellcode:
procedure StartApp(const App, Parameters, CurDir : TJwString);
var StartupInfo : TStartupInfoA; ProcInfo : TProcessInformation; pEnv : Pointer; pCurDir, pCmdLine : TJwPChar; ServiceToken, CopiedToken, UserToken : TJwSecurityToken; begin ZeroMemory(@StartupInfo, sizeof(StartupInfo)); StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.lpDesktop := 'winsta0\default'; pCmdLine := TJwPChar('"'+App+'" ' + Parameters); pCurDir := Nil; if Length(CurDir) > 0 then pCurDir := TJwPChar(CurDir); //get the token from the service system session ServiceToken := TJwSecurityToken.CreateTokenEffective(MAXIMUM_ALLOWED); TJwAutoPointer.Wrap(ServiceToken); //copy the token to be able to change the TokenSessionId //Info: Win2000: Only 0 // WinXP: Service=0, 1.User=0, 2.User=1, 3.User=2, ... // WinVista: Service=0, 1.User=1, 2.User=2, 3.User=3, ... CopiedToken := TJwSecurityToken.CreateDuplicateExistingToken(ServiceToken.TokenHandle, MAXIMUM_ALLOWED); TJwAutoPointer.Wrap(CopiedToken); //get the token of the logged in user if is2000 then UserToken := TJwSecurityToken.CreateCompatibilityQueryUserToken(MAXIMUM_ALLOWED, 'explorer.exe') else //XP, 2003, Vista, 2008 UserToken := TJwSecurityToken.CreateWTSQueryUserTokenEx(nil, WtsGetActiveConsoleSessionID); TJwAutoPointer.Wrap(UserToken); //give the copied token the same sessionid as the logged in user CopiedToken.TokenSessionId := UserToken.TokenSessionId; //create the environment block using the logged in user JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, false); try if not CreateProcessAsUser( CopiedToken.TokenHandle, TJwPChar(App), //__in_opt LPCTSTR lpApplicationName, pCmdLine, //__inout_opt LPTSTR lpCommandLine, nil, //__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, nil, //__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, false, //__in BOOL bInheritHandles, CREATE_NEW_CONSOLE or CREATE_DEFAULT_ERROR_MODE or CREATE_UNICODE_ENVIRONMENT, //__in DWORD dwCreationFlags, pEnv, //__in_opt LPVOID lpEnvironment, pCurDir, //__in_opt LPCTSTR lpCurrentDirectory, StartupInfo, //__in LPSTARTUPINFO lpStartupInfo, ProcInfo //__out LPPROCESS_INFORMATION lpProcessInformation ) then raiseLastOsError; finally DestroyEnvironmentBlock(pEnv); end; CloseHandle(ProcInfo.hProcess); CloseHandle(ProcInfo.hThread); end; |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Ja das sieht doch super aus. Kann ich das für den JEDI Blog verwenden als Post?
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Klar doch. :)
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Laß es ihn doch selber posten ;)
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Wäre mein nächster Vorschlag gewesen.
Hast du Lust einen Artikel über deinen Code zu posten auf : ![]() |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Ja, kann ich machen. Dann brauch ich einen Login, oder?
Kannst mir die Login-Daten per PN schicken. Ich versuche, den Artikel noch heute zu verfassen, da ich ab morgen eine Woche unterwegs bin und keine Zeit haben werde. Sollte das heute nicht mehr klappen, dann mache ich das in einer Woche. |
AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
statt "is2000"
Delphi-Quellcode:
vorher noch
if TJwWindowsVersion.IsWindows2000(false)then
Delphi-Quellcode:
uses ..., JwsclVersion;
|
AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
Zitat:
Delphi 2010 setzt ja schon mindestens ein WinNT (das hat eh keiner und's nächste der NT-Reihe war schon die 2000) oder gar ein XP voraus. |
AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
Jo, aber der Code läuft noch mit JWSCL unter D6.
|
AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
Zitat:
Delphi-Quellcode:
Habe 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 CreateUserProcess(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 : THandle; ReturnLength, Environment: Pointer; StartupInfo: {$IFDEF UNICODE}TStartupInfoW{$ELSE}TStartupInfoA{$ENDIF}; 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(GetShellProcName, hUserToken); end; if Result then try if CreateEnvironmentBlock(@Environment, hUserToken, false) then try ZeroMemory(@StartupInfo, sizeof(StartupInfo)); StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.lpDesktop := 'winsta0\default'; StartupInfo.dwFlags := STARTF_USESHOWWINDOW; StartupInfo.wShowWindow := SW_SHOWDEFAULT; Result := CreateProcessAsUser (hUserToken, 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(hUserToken); end; end; ![]() kuba |
AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
Wo kann man die Funktion GetShellProcName finden?
|
AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:56 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