AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Programm von Dienst starten lassen (Jetzt aber wirklich mal)

Programm von Dienst starten lassen (Jetzt aber wirklich mal)

Ein Thema von CodeX · begonnen am 26. Feb 2008 · letzter Beitrag vom 5. Aug 2014
Antwort Antwort
Seite 3 von 5     123 45   
Dezipaitor

Registriert seit: 14. Apr 2003
Ort: Stuttgart
1.701 Beiträge
 
Delphi 7 Professional
 
#21

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 23. Mai 2008, 23:07
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).
Christian
Windows, Tokens, Access Control List, Dateisicherheit, Desktop, Vista Elevation?
Goto: JEDI API LIB & Windows Security Code Library (JWSCL)
  Mit Zitat antworten Zitat
CodeX

Registriert seit: 30. Okt 2004
471 Beiträge
 
Delphi 12 Athens
 
#22

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 24. Mai 2008, 00:45
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!
  Mit Zitat antworten Zitat
Benutzerbild von Remko
Remko

Registriert seit: 10. Okt 2006
Ort: 's-Hertogenbosch, Die Niederlande
222 Beiträge
 
RAD-Studio 2010 Arc
 
#23

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 24. Mai 2008, 10:27
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.
  Mit Zitat antworten Zitat
CodeX

Registriert seit: 30. Okt 2004
471 Beiträge
 
Delphi 12 Athens
 
#24

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 25. Mai 2008, 18:06
Sure, this is my final code (so far ):

Delphi-Quellcode:
      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);
Getestet mit 2000, XP und Vista und für gut befunden.
"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.
  Mit Zitat antworten Zitat
Dezipaitor

Registriert seit: 14. Apr 2003
Ort: Stuttgart
1.701 Beiträge
 
Delphi 7 Professional
 
#25

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 25. Mai 2008, 22:25
  • Du hast 3 Tokeninstanzen, die du nicht wieder löscht.
  • Wenn irgendwas nach ServiceToken := ... fehlschlägt, dann werden diese Instanzen nicht gelöscht. Also einzeln mit try/finally für nach jedem Create eins erstellen.
    Delphi-Quellcode:
    Create1
    try
      create2
      try
      finally
        Free2
      end;
    finally
      Free1;
    end;
    Übrigens:
    JwsclComUtils implementiert AutoDestructZeiger.

    Delphi-Quellcode:
    Token2 := TJwSecurityToken....
    TJwAutoPointer.Wrap(Token2);
    In diesem Fall wird das Objekt Token2 gelöscht, wenn man beim "End;" ankommt.

    Es geht auch mehrmals. Alle mit Wrap umschlossenen Objekte werden zerstört. Egal welcher Variablenname.
    Delphi-Quellcode:
    Token2 := TJwSecurityToken....
    TJwAutoPointer.Wrap(Token2);
    Token2 := TJwSecurityToken....
    TJwAutoPointer.Wrap(Token2);

    Man kann soetwas auch für eine Instanzvariable machen, wenn man das COM Objekt speichert.
    Delphi-Quellcode:
     TXY = class
       myWrappedToken : IJwAutoPointer;
       Token2 := TJwSecurityToken;

       procedure Do;
     end;

    procedure TXY.Do;
    begin
      Token2 := TJwSecurityToken....
      myWrappedToken := TJwAutoPointer.Wrap(Token2);
    end;
    Sobald die Klasseninstanz zerstört wird, wird das Token gelöscht.



  • Jedes Create aus der JWSCL kann eine Exception liefern. Man sollte sie nicht ignorieren. Es ist zum eigenen Schutz, da die JWSCL alle Fehlercodes der SecurityAPI in Exceptions umwandelt, wird verhindert, dass Code danach ausgeführt wird, der so garnicht zum Laufen kommen sollte.
  • WtsGetActiveConsoleSessionID liefert dasselbe wie UserToken.TokenSessionId. Du brauchst also garkein UserToken erstellen. Für Win2000 kannst du auch einfach 0 verwenden.
  • Diese Zeile
    true, //__in BOOL bInheritHandles, funktioniert nicht unbedingt unter XP. Unter Vista jedoch schon. Sie funkz dann nicht, wenn die Session eine andere ist, als die des Prozesses in dem CreateProcess... aufgerufen wird. Handles können nicht über Sessiongrenzen hinweg vererbt werden. Also false setzen.
  • UNICODE <-> ANSICODE
    Delphi-Quellcode:
    CreateProcessAsUser( { TODO : UNICODE VERSION? }
          CopiedToken.TokenHandle,
          TJwPChar(App),
    Wenn du das mit UNICODE kompilierst, dann funkz es nicht, weil TJwPChar dann ein PWideChar sein wird. Dasselbe mit CreateProcessAsUserW (wenn du es änderst). Also entweder machst du ein IFDEF UNICODE um CreateProcessAsUserA/W oder du verwendest nicht TJwPchar sonder PAnsiChar.
  •     JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, true); Warum true hier? Willst du Env-Strings von SYSTEM mit drin haben?
Christian
Windows, Tokens, Access Control List, Dateisicherheit, Desktop, Vista Elevation?
Goto: JEDI API LIB & Windows Security Code Library (JWSCL)
  Mit Zitat antworten Zitat
CodeX

Registriert seit: 30. Okt 2004
471 Beiträge
 
Delphi 12 Athens
 
#26

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 1. Jun 2008, 20:30
Zitat von Dezipaitor:
WtsGetActiveConsoleSessionID liefert dasselbe wie UserToken.TokenSessionId. Du brauchst also garkein UserToken erstellen. Für Win2000 kannst du auch einfach 0 verwenden.
Das stimmt, aber ich brauche das UserToken für
JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, false); sowieso. Da komme ich ja nicht drumherum, wenn ich die Benutzerumgebung mitgeben will.


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!
  Mit Zitat antworten Zitat
Dezipaitor

Registriert seit: 14. Apr 2003
Ort: Stuttgart
1.701 Beiträge
 
Delphi 7 Professional
 
#27

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 1. Jun 2008, 20:50
Entweder wrappen oder try/finally.
Christian
Windows, Tokens, Access Control List, Dateisicherheit, Desktop, Vista Elevation?
Goto: JEDI API LIB & Windows Security Code Library (JWSCL)
  Mit Zitat antworten Zitat
Dezipaitor

Registriert seit: 14. Apr 2003
Ort: Stuttgart
1.701 Beiträge
 
Delphi 7 Professional
 
#28

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 21. Jun 2008, 16:13
Ja was ist denn? Funktioniert es oder nicht?
Christian
Windows, Tokens, Access Control List, Dateisicherheit, Desktop, Vista Elevation?
Goto: JEDI API LIB & Windows Security Code Library (JWSCL)
  Mit Zitat antworten Zitat
CodeX

Registriert seit: 30. Okt 2004
471 Beiträge
 
Delphi 12 Athens
 
#29

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 21. Jun 2008, 20:27
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;
  Mit Zitat antworten Zitat
Dezipaitor

Registriert seit: 14. Apr 2003
Ort: Stuttgart
1.701 Beiträge
 
Delphi 7 Professional
 
#30

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 21. Jun 2008, 23:44
Ja das sieht doch super aus. Kann ich das für den JEDI Blog verwenden als Post?
Christian
Windows, Tokens, Access Control List, Dateisicherheit, Desktop, Vista Elevation?
Goto: JEDI API LIB & Windows Security Code Library (JWSCL)
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:23 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz