Delphi-PRAXiS
Seite 2 von 5     12 34     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Programm von Dienst starten lassen (Jetzt aber wirklich mal) (https://www.delphipraxis.net/109191-programm-von-dienst-starten-lassen-jetzt-aber-wirklich-mal.html)

Dezipaitor 21. Mai 2008 20:14

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Zitat:

Zitat von CodeX
@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...

Du willst also das Token vom Benutzer kopiere und nur Adminrechte einfügen? Nunja. Das müsste gehen. Man kann nur kein komplett neues Token erstellen, weil dafür eine Logon ID notwendig ist, die man von einen Logonprovider bekommt. Ka wie das geht. Im Moment macht das nur LsaLogonUser. Man kann aber eine vorhandene ID nutzen, die z.b. ein Benutzer beim Einloggen bekommen hat. Mein CreateToken Beispiel in JWSCL macht das.


Zitat:

Zitat von CodeX
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.

Das geht auch mit dem normalen Benutzertoken. Dazu brauchst du nur die SessionID und WTSQueryUserToken

CodeX 22. Mai 2008 04:20

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Zitat:

Zitat von Dezipaitor
Du willst also das Token vom Benutzer kopiere und nur Adminrechte einfügen? Nunja. Das müsste gehen.

Klingt gut. Die Frage ist nur: Wie?


Zitat:

Zitat von Dezipaitor
Das geht auch mit dem normalen Benutzertoken. Dazu brauchst du nur die SessionID und WTSQueryUserToken

Und wie komme ich an die SessionID?

Prinzipiell würde ich das ja so machen:
Delphi-Quellcode:
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;
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?


Huch, doch schon so spät. Du siehst, mir lässt das keine Ruhe. Ich gehe jetzt erstmal schlafen...

Remko 22. Mai 2008 08:16

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

Dezipaitor 22. Mai 2008 16:14

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.

CodeX 22. Mai 2008 16:38

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Zitat:

Zitat von Remko
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...

Yes, you're right this is shorter, but it gives me the environment of SYSTEM and not of the logged in user.

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

OK, good that you remind me, because I do need 2000 compatibility. Unfortunatelly I still can't get Jwscl to work so I can't test WTSQueryUserTokenEx at the moment.


Zitat:

Zitat von Dezipaitor
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.

Ich würde ja sehr gerne die Jwscl benutzen, aber ich bekomme das leider nicht zum Laufen bei mir. Ich mache jetzt mal eine frische Installation von Jwa und Jwscl dann mal sehen...

CodeX 22. Mai 2008 19:29

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:
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;
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?

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

Dezipaitor 22. Mai 2008 20:19

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Zitat:

Zitat von CodeX
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?

Wenn du den neuen Prozess nicht als den Benutzer ausführen willst, dann nutze CreateProcess. Wenn dein aktueller Prozess SYSTEM ist, dann wird der neue auch SYSTEM.

Zitat:

Zitat von CodeX
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?

Guck mal hier :bounce2:
http://blog.delphi-jedi.net/2008/04/...in-full-glory/

CodeX 22. Mai 2008 22:44

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:
CreateEnvironmentBlock(@pEnv, 0, true);
sollte deshalb geändert werden.

Das hier funktioniert als Ersatz:
Delphi-Quellcode:
//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);
Damit läuft das Programm als SYSTEM Anwendung aber mit der Umgebung des aktuellen Benutzers. :D


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?

Dezipaitor 23. Mai 2008 10:10

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

CodeX 23. Mai 2008 19:06

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Zitat:

Zitat von Dezipaitor
CreateWTSQueryUserTokenEx funktioniert nur unter Windows 2000. Unter XP kann es funkzen, muss aber nicht. Unter Vista funktioniert es nicht!

Okay, gut zu wissen. Wobei CreateWTSQueryUserTokenEx bei mir (wie in meinem auskommentierten Beispiel) nur mit WtsGetActiveConsoleSessionID Sinn gemacht hat. Da letzteres aber erst ab XP existiert, habe ich das eben doch nicht verwendet.
Aber letztlich tut
Delphi-Quellcode:
UserToken := TJwSecurityToken.CreateCompatibilityQueryUserToken(MAXIMUM_ALLOWED, 'explorer.exe');
ja vollkommen das, was es soll. Nachteile sehe ich keine.


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?


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:43 Uhr.
Seite 2 von 5     12 34     Letzte »    

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