Habe aus Spaß an der Freude noch ein kleines Workaround verfasst, wenn man seinen Prozess wirklich mit SYSTEM Rechten laufen lassen will (Sinn sei dahingestellt), aber trotzdem Aktionen im Benutzerkontext durchführen will. Ist vielleicht ganz nützlich, wenn man ein eigentlich fertiges Programm vom Service aus starten will. Statt alles umzuschreiben, kann man dann einfach einmalig am Start die Funktion ImpersonateProcess() aufrufen. Der Hook sorgt dann von selbst dafür, dass sämtliche Threads direkt beim Start impersoniert werden. Zusätzlich wird mit
RegOverwritePredefKey der HKEY_CURRENT_USER Key gradegebogen. Benutzerprofilpfade, etc. dürften dank des angepassten Environments nicht verfälscht werden.
Delphi-Quellcode:
function QueryTokenSID(hToken: THandle): String;
var
ConvertSidToStringSidW: function(Sid: PSID;
var StringSid: PWideChar): BOOL; stdcall;
var
Buffer: PTokenUser;
ReturnLength: DWord;
StringSid: PWideChar;
begin
Result := '';
@ConvertSidToStringSidW := GetProcAddress(LoadLibrary('advapi32.dll'),
'ConvertSidToStringSidW');
if (not Assigned(ConvertSidToStringSidW)) then Exit;
if (not GetTokenInformation(hToken, TokenUser, nil, 0, ReturnLength))
and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then Exit;
GetMem(Buffer, ReturnLength);
try
if (not GetTokenInformation(hToken, TokenUser, Buffer, ReturnLength,
ReturnLength)) then Exit;
if ConvertSidToStringSidW(Buffer^.User.Sid, StringSid) then
try
Result := StringSid;
finally
LocalFree(Cardinal(StringSid));
end;
finally
FreeMem(Buffer);
end;
end;
function ImpersonateThread(hToken: THandle): Boolean;
var
hNewHKey: HKEY;
begin
Result := false;
if (not ImpersonateLoggedOnUser(hToken)) then Exit;
if (RegOpenKeyEx(HKEY_USERS, PChar(QueryTokenSID(hToken)), 0,
KEY_ALL_ACCESS, hNewHKey) = ERROR_SUCCESS) then
try
Result := RegOverridePredefKey(HKEY_CURRENT_USER, hNewHKey) =
ERROR_SUCCESS;
finally
RegCloseKey(hNewHKey);
end;
end;
type
PThreadParam = ^TThreadParam;
TThreadParam = packed record
UserToken: THandle;
Thread: Pointer;
Param: Pointer;
end;
TThreadFunction = function(lpParam: Pointer): DWord; stdcall;
var
UserToken: THandle;
var
O_CreateThread: function(lpThreadAttributes: Pointer;
dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine;
lpParameter: Pointer; dwCreationFlags: DWORD;
var lpThreadId: DWORD): THandle; stdcall = nil;
function C_CreateThread(lpThreadAttributes: Pointer;
dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine;
lpParameter: Pointer; dwCreationFlags: DWORD;
var lpThreadId: DWORD): THandle; stdcall;
function LocalThread(lpParam: PThreadParam): DWord; stdcall;
begin
ImpersonateThread(lpParam^.UserToken);
Result := TThreadFunction(lpParam^.Thread)(lpParam^.Param);
Dispose(lpParam);
end;
var
ThreadParam: PThreadParam;
begin
New(ThreadParam);
ThreadParam^.UserToken := UserToken;
ThreadParam^.Thread := lpStartAddress;
ThreadParam^.Param := lpParameter;
Result := O_CreateThread(lpThreadAttributes, dwStackSize, @LocalThread,
ThreadParam, dwCreationFlags, lpThreadId);
end;
function ImpersonateProcess(hToken: THandle): Boolean;
begin
Result := false;
if (not Assigned(O_CreateThread)) then
begin
UserToken := hToken;
Result := ImpersonateThread(hToken) and
InstallInlineHook(GetProcAddress(LoadLibrary('kernel32.dll'),
'CreateThread'), @C_CreateThread, @O_CreateThread);
end;
end;
Möglicher Aufruf wäre beispielsweise (ohne Win2000 Kompatibilität):
Delphi-Quellcode:
var
WTSGetActiveConsoleSessionId: function: DWord; stdcall;
WTSQueryUserToken: function(SessionId: ULONG;
var phToken: THandle): BOOL; stdcall;
var
hToken: THandle;
begin
@WTSGetActiveConsoleSessionId :=
GetProcAddress(LoadLibrary('kernel32.dll'), 'WTSGetActiveConsoleSessionId');
@WTSQueryUserToken :=
GetProcAddress(LoadLibrary('wtsapi32.dll'), 'WTSQueryUserToken');
if (not Assigned(WTSGetActiveConsoleSessionId)) or
(not Assigned(WTSQueryUserToken)) then Exit;
if not WTSQueryUserToken(WTSGetActiveConsoleSessionId, hToken) then
RaiseLastOSError;
ImpersonateProcess(hToken);
end;