Delphi-PRAXiS
Seite 1 von 2  1 2      

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)

CodeX 25. Feb 2008 23:34


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?
Hier und hier gibt es jasehr interessante Informationen und Ansätze, aber allein damit komme ich nicht weiter.

Ich hoffe sehr, dass wir auf eine allgemein verwendbare Lösung kommen können...

Dezipaitor 26. Feb 2008 02:09

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:

Zitat von CodeX
- Der normale Benutzer startet das Programm, dieses merkt, dass es nicht genügend Rechte hat, benachrichtigt den Dienst (über Pipes) und beendet sich wieder.

Ein böses Programm könnte dein Programm ersetzen oder einfach nur den Dienst über die Pipe benachrichtigen, dass es ein (böses) Programm gestartet haben will. Die einzige Möglichkeit das zu verhindern, wäre dein Programm zu signieren und den Dienst das überprüfen zu lassen.
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:

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

Und schon sind wir an der Stelle angelangt, an der du ein Programm startest, welches nur Adminrechte benötigt aber nicht gleich SYSTEM-Rechte. SYSTEM ist doch ein bisschen härter. Also nutzen wir doch nur Adminrechte. Wie?

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:

Hier und hier gibt es jasehr interessante Informationen und Ansätze, aber allein damit komme ich nicht weiter.
Ich hoffe sehr, dass wir auf eine allgemein verwendbare Lösung kommen können...
Allgemein kann man da nichts verwenden. Es ist schon so spezifisch alleine, dass man, wenn man es allgemein Verwendbar machen würde ein riesiges Sicherheitsproblem beschwören würde. Ohne das obige Wissen würde man bis dahin aber erst garnicht kommen. Es würde nur durch pures Glück überhaupt funktionieren. Jedoch allein die obige Aufzählung beschreibt ja noch nichteinmal annähernd, wie man es implementiert.

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

CodeX 26. Feb 2008 02:44

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

Zitat von Dezipaitor
Die einzige Möglichkeit das zu verhindern, wäre dein Programm zu signieren und den Dienst das überprüfen zu lassen.

Na, das sollte doch machbar sein. Damit ist meine Idee doch gar nicht so weit hergeholt.

Zitat:

Zitat von Dezipaitor
Und schon sind wir an der Stelle angelangt, an der du ein Programm startest, welches nur Adminrechte benötigt aber nicht gleich SYSTEM-Rechte. SYSTEM ist doch ein bisschen härter. Also nutzen wir doch nur Adminrechte. Wie?

Richtig, Admin-Rechte reichen. Ich meinte auch nur, dass die System-Rechte, die ja dann wohl automatisch mitvererbt werden, auch ihre Arbeit tun würden. ;)

Zitat:

Zitat von Dezipaitor
Der Benutzer ist kein Administrator. Aber vielleicht hat er das Passwort?

Nein, davon kann ich nicht ausgehen. D.h. für diese Lösung müsste das Passwort vom Administrator verschlüsselt gespeichert werden. Aber da es spätestens im Speicher wieder entschlüsselt werden muss, ist das ja nun nicht die sicherste Lösung.
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:

Zitat von Dezipaitor
Tja es ist machbar. Ich habe es schon gemacht. Und ein anderer auch.

Tja, genau das würde mich interessieren. Kannst Du mir hier bitte aushelfen?

Dezipaitor 26. Feb 2008 11:13

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

Zitat von CodeX
Nein, davon kann ich nicht ausgehen. D.h. für diese Lösung müsste das Passwort vom Administrator verschlüsselt gespeichert werden. Aber da es spätestens im Speicher wieder entschlüsselt werden muss, ist das ja nun nicht die sicherste Lösung.
Entsprechend würde ich wirklich gerne eine Lösung mit Verwendung eines Services finden.

Es ist aus sicherheitstechnischen Gründen untersagt, mit SYSTEM Rechten zu arbeiten. Zudem würde die gestartete Anwendung plötzlich mit dem dem SYSTEM Profil und Umgebungsvariablen arbeiten müssen. Da sind einige Sachen anders. Daher funktionieren einige Anwendungen nicht damit.
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:

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.
Ab Windows2000 sollte es funktionieren. Für Vista müsste man noch meine andere Komponente einbauen. Windows NT <= 4 wird nicht unterstützt.

Zitat:

Tja, genau das würde mich interessieren. Kannst Du mir hier bitte aushelfen?
Lade dir die JWSCL runter und mach ein update mit Subversion (z.B. Tortoise SVN). Nur so bekommst du die inoffiziellen Teile des Paketes, darunter auch XP Elevation. Ich kann dir leider nicht alles Schritt für Schritt erklären, aber sonst helfe ich dir gerne.
Versuch dich dort einzuarbeiten, wenn es Probleme gibt, dann schreibe einfach. Wäre super wenn XP Elevation mehr Unterstützung bekommen würde.

CodeX 20. Mai 2008 23:48

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:
[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'
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?

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?

Dezipaitor 21. Mai 2008 11:14

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.

Remko 21. Mai 2008 16:10

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

[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'
These are all functions defined in JwaWinsta, maybe you need to update and/or rebuild Jwa?

SirThornberry 21. Mai 2008 16:22

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.

Dezipaitor 21. Mai 2008 19:02

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.

CodeX 21. Mai 2008 19:56

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.

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?

Dezipaitor 23. Mai 2008 22:07

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

CodeX 23. Mai 2008 23:45

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:

Remko 24. Mai 2008 09:27

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.

CodeX 25. Mai 2008 17:06

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
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. ;)

Dezipaitor 25. Mai 2008 21:25

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
  • 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
    Delphi-Quellcode:
    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.
  • Delphi-Quellcode:
        JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, true);
    Warum true hier? Willst du Env-Strings von SYSTEM mit drin haben?

CodeX 1. Jun 2008 19:30

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

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
Delphi-Quellcode:
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!

Dezipaitor 1. Jun 2008 19:50

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Entweder wrappen oder try/finally.

Dezipaitor 21. Jun 2008 15:13

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Ja was ist denn? Funktioniert es oder nicht?

CodeX 21. Jun 2008 19:27

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;

Dezipaitor 21. Jun 2008 22:44

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?

CodeX 21. Jun 2008 22:51

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Klar doch. :)

Olli 21. Jun 2008 23:01

Re: Programm von Dienst starten lassen (Jetzt aber wirklich
 
Laß es ihn doch selber posten ;)

Dezipaitor 21. Jun 2008 23:04

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 : http://blog.delphi-jedi.net/

CodeX 22. Jun 2008 18:59

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.

Dezipaitor 22. Jun 2010 16:30

AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
 
statt "is2000"
Delphi-Quellcode:
if TJwWindowsVersion.IsWindows2000(false)then
vorher noch
Delphi-Quellcode:
uses ..., JwsclVersion;

himitsu 22. Jun 2010 18:59

AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
 
Zitat:

Zitat von Dezipaitor (Beitrag 1030897)
Delphi-Quellcode:
if TJwWindowsVersion.IsWindows2000(false)then

So langsam kann man sich soeine Abfrage auch sparen.

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.

Dezipaitor 23. Jun 2010 11:07

AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
 
Jo, aber der Code läuft noch mit JWSCL unter D6.

kuba 28. Nov 2011 21:16

AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
 
Zitat:

Zitat von Dezipaitor (Beitrag 1031013)
Jo, aber der Code läuft noch mit JWSCL unter D6.

hier ganz ohne JEDI:

Delphi-Quellcode:

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;
Habe diesen Code umgebaut, funktioniert ab Windows 2000, ohne Admin Rechte ....

kuba

squetk 28. Nov 2011 23:14

AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
 
Wo kann man die Funktion GetShellProcName finden?

Sir Rufo 28. Nov 2011 23:36

AW: Programm von Dienst starten lassen (Jetzt aber wirklich mal)
 
Zitat:

Zitat von squetk (Beitrag 1138345)
Wo kann man die Funktion GetShellProcName finden?

Über die Suchfunktion?


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:44 Uhr.
Seite 1 von 2  1 2      

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