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:
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.
| CreateProcessWithLogonW_Demo.zip | Wednesday, 29-Dec-2010 23:45:13 CET | 4.5K |
[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