Prozess unter anderen Benutzerrechten ausführen


Im ersten Teil dieser Serie habe ich gezeigt, wie man innerhalb seines eigenen Prozesses andere Benutzerrechte erlangen kann. Dies mal geht es darum, wie man einen weiteren Prozess unter einem anderen Benutzer starten kann.

Die Windows-API stellt zu diesem Zweck die API-Funktion CreateProcessWithLogonW zur Verfügung. Mit ihr kann man einen Prozess unter einem anderen Benutzer starten. Dazu vier kurze Bemerkungen:

  1. Wie der Name schon vermuten läßt, CreateProcessWithLogonW, liegt sie nur in der Unicode Variante vor.
  2. Mit ihr ist es möglich auch das entsprechende Profil des Benutzers zu laden (LOGON_WITH_PROFILE).
  3. Es ist nicht nötig sich erst mit der API-Funktion LogonUser [1] eine gültiges Token zu holen. Setzt allerdings voraus, dass der Dienst Sekundäre Anmeldung ("Ermöglicht das Starten von Prozessen unter Verwendung alternativer Anmeldeinformationen.") gestartet sein muss.
  4. Sie steht erst ab Windows 2000 zur Verfügung

Wichtig sind hier insbesondere die Punkte drei und vier!

Damit wir nun diese Funktion unter Delphi nutzen können, sind erst ein paar Vorbereitungen notwendig. Als aller erstes müssen wir ein paar nötige Konstanten deklarieren:

const
  LOGON_WITH_PROFILE = $00000001;
  LOGON_NETCREDENTIALS_ONLY = $00000002;
  LOGON_ZERO_PASSWORD_BUFFER = DWORD($80000000);

  CREATE_DEFAULT_ERROR_MODE = $04000000;
  CREATE_NEW_CONSOLE = $00000010;
  CREATE_NEW_PROCESS_GROUP = $00000200;
  CREATE_SEPARATE_WOW_VDM = $00000800;
  CREATE_SUSPENDED  = $00000004;
  CREATE_UNICODE_ENVIRONMENT = $00000400;

Eine genaue Erklärung, was welche Konstante bedeutet und was sie bewirkt, findet man im MSDN.

Als nächstes müssen wir uns noch eine Struktur deklarieren: TStartupInfoW. Diese Struktur ist zwar schon in der Unit Windows.pas deklariert, allerdings nur die Ansi-Variante. Wir müssen aber bedingt dadurch, dass CreateProcessWithLogonW nur in der Unicode-Variante vorliegt, mit WideStrings arbeiten:

type
  TStartupInfoW = record
    cb: DWORD;
    lpReserved: LPWSTR;
    lpDesktop: LPWSTR;
    lpTitle: LPWSTR;
    dwX: DWORD;
    dwY: DWORD;
    dwXSize: DWORD;
    dwYSize: DWORD;
    dwXCountChars: DWORD;
    dwYCountChars: DWORD;
    dwFillAttribute: DWORD;
    dwFlags: DWORD;
    wShowWindow: WORD;
    cbReserved2: WORD;
    lpReserved2: LPBYTE;
    hStdInput: THANDLE;
    hStdOutput: THANDLE;
    hStdError: THANDLE;
  end;
  PStartupInfoW = ^TStartupInfoW;

Und zu guter letzt die Funktion selber:

function CreateProcessWithLogonW(lpUsername, lpDomain, lpPassword: LPWSTR; dwLogonFlags: dword;
  lpApplicationName, lpCommandLine: LPWSTR; dwCreationFlags: dword; lpEnvironment: pointer;
  lpCurrentDirectory: LPWSTR; lpStartupInfo: PStartUpInfoW;
  lpProcessInfo: PProcessInformation): boolean; stdcall; external 'advapi32.dll';

Um den Aufruf etwas zu vereinfachen, habe ich einen kleinen Wrapper für die Funktion geschrieben:

function CreateProcessAsLogon(const User, PW, Application, CmdLine: WideString): DWORD;
var
  si                : TStartupInfoW;
  pif               : TProcessInformation;
  s                 : WideString;
begin
  ZeroMemory(@si, sizeof(si));
  si.cb := sizeof(si);
  si.dwFlags := STARTF_USESHOWWINDOW;
  si.wShowWindow := 1;

  if CmdLine = '' then
    s := Application
  else
    s := Application + ' "' + CmdLine + '"';

  SetLastError(0);
  CreateProcessWithLogonW(PWideChar(User), nil, PWideChar(PW), 0, nil, PWideChar(s), 
    CREATE_DEFAULT_ERROR_MODE, nil, nil, @si, @pif);
  Result := GetLastError;
end;

Aufruf dann wie folgt:

procedure TForm1.Button1Click(Sender: TObject);
var
  WinDir            : WideString;
  User              : WideString;
  PW                : WideString;
  err: DWORD;
begin
  WinDir := GetWinDir;
  User := WideString(Edit1.Text);
  PW := WideString(Edit2.Text);
  err := CreateProcessAsLogon(User, PW, WinDir + '\notepad.exe', 'c:\boot.ini');
  if err <> 0 then
    ShowMessage(SysErrorMessage(err));
end;

Der Einfachheit halber habe ich beim Beispielaufruf die Prüfung, ob der Dienst Sekundäre Anmeldung gestartet ist, weggelassen. Natürlich sollte man dies vorher tun. Denn obwohl dieser Dienst standardmäßig gestartet ist, sollte man sich darauf nicht verlassen. Desweiteren habe ich darauf verzichtet, die Funktion dynamisch einzubinden, was man natürlich tun sollte, wenn das Programm auch auf Betriebssystem kleiner Windows 2000 zum Einsatz kommen soll.

Downloads


CreateProcessWithLogonW_Demo.zip Wednesday, 29-Dec-2010 23:45:13 CET 4.5K

Links

[1] http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/logonuser.asp
[2] http://www.michael-puff.de/Artikel/2006/files/CreateProcessWithLogonW_Demo.zip


2010-12-29T23:44:39 +0100, mail+homepage[at]michael-puff.de