|
Hi,
Explorer only exists when logged into a desktop session under Windows, whereas services can exist without anyone physically logged in. Also there could be multiple explorer.exe processes if this machine is a terminal server..
unit xbwWinLogon;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Windows,
TLHelp32,
SysUtils,
Classes;
const
wtsapi = 'wtsapi32.dll';
advapi32 = 'advapi32.dll';
userenvlib = 'userenv.dll';
TOKEN_ADJUST_SESSIONID = $0100;
{$EXTERNALSYM TOKEN_ADJUST_SESSIONID}
SE_DEBUG_NAME = 'SeDebugPrivilege';
{$EXTERNALSYM SE_DEBUG_NAME}
type
_TOKEN_INFORMATION_CLASS = (TokenInfoClassPad0, TokenUser, TokenGroups,
TokenPrivileges, TokenOwner, TokenPrimaryGroup, TokenDefaultDacl, TokenSource,
TokenType, TokenImpersonationLevel, TokenStatistics, TokenRestrictedSids,
TokenSessionId, TokenGroupsAndPrivileges, TokenSessionReference,
TokenSandBoxInert, TokenAuditPolicy, TokenOrigin);
{ kernel32 }
TWTSGetActiveConsoleSessionId = function : DWORD; stdcall;
TProcessIdToSessionId = function (dwProcessId: DWORD; var pSessionId: DWORD): BOOL; stdcall;
{ wtsapi }
TWTSQueryUserToken = function (SessionId: ULONG; var phToken: THANDLE): BOOL; stdcall;
{ advpai32 }
TSetTokenInformation = function (TokenHandle: THANDLE; TokenInformationClass: _TOKEN_INFORMATION_CLASS;
TokenInformation: Pointer; TokenInformationLength: DWORD): BOOL; stdcall;
TAdjustTokenPrivileges = function (TokenHandle: THANDLE; DisableAllPrivileges: BOOL;
NewState: Pointer; BufferLength: DWORD;
PreviousState: Pointer; ReturnLength: LPDWORD): BOOL; stdcall;
{ userenvlib }
TCreateEnvironmentBlock = function (lpEnvironment: Pointer; hToken: THANDLE; bInherit: BOOL): BOOL; stdcall;
var
WTSGetActiveConsoleSessionId: TWTSGetActiveConsoleSessionId=ni=nil;
ProcessIdToSessionId:TProcessIdToSessionId=nil;
WTSQueryUserToken:TWTSQueryUserToken=nil;
SetTokenInformation:TSetTokenInformation=nil;
AdjustTokenPrivileges:TAdjustTokenPrivileges=nil;
CreateEnvironmentBlock:TCreateEnvironmentBlock=nil;
function xbwSessionNumber: Integer;
function xbwActiveConsole: Integer;
function xbwGetProcessID(strProcess: String; iSessionID: Integer = -1): DWORD;
procedure xbwExecProcess(strParameters: String; strConfig: String = ''); { <<< starts command line under user process }
function xbwStartProcess(strProcess: String; bLocalSystem: Boolean = True; iSessionID: Integer = -1): Boolean;
function xbwImpersonateLoggedOnUser: Boolean;
function xbwRevertToSelf: Boolean;
implementation
uses IniFiles, SHFolder, Forms, CMCConsts;
var
LibsLoaded:integer=0;
FhUserTokenDup: THandle; { used for user impersonation }
function GetProcedureAddress(var P: Pointer; const ModuleName, ProcName: string):boolean;
var
ModuleHandle: HMODULE;
begin
if not Assigned(P) then
begin
ModuleHandle := GetModuleHandle(PChar(ModuleName));
if ModuleHandle = 0 then
ModuleHandle := LoadLibrary(PChar(ModuleName));
if ModuleHandle <> 0 then
P := Pointer(GetProcAddress(ModuleHandle, PChar(ProcName)));
Result:=Assigned(P);
end
else
Result:=True;
end;
function InitProcLibs:boolean;
begin
if LibsLoaded>0 then
Result:=True
else if LibsLoaded<0 then
Result:=False
else
begin
LibsLoaded:=-1;
if GetProcedureAddress(@WTSGetActiveConsoleSessionId, kernel32, 'WTSGetActiveConsoleSessionId') and
GetProcedureAddress(@ProcessIdToSessionId, kernel32, 'ProcessIdToSessionId') and
GetProcedureAddress(@WTSQueryUserToken, wtsapi, 'WTSQueryUserToken') and
GetProcedureAddress(@SetTokenInformation, advapi32, 'SetTokenInformation') and
GetProcedureAddress(@AdjustTokenPrivileges, advapi32, 'AdjustTokenPrivileges') and
GetProcedureAddress(@CreateEnvironmentBlock, userenvlib, 'CreateEnvironmentBlock') then
LibsLoaded:=1;
Result:=LibsLoaded=1;
end;
end;
function xbwSessionNumber: Integer;
var dwSessionID: DWord;
begin
Result := 0;
if not InitProcLibs then Exit;
ProcessIdToSessionId(GetCurrentProcessId(), dwSessionID);
Result := dwSessionID;
end;
function xbwActiveConsole: Integer;
begin
Result := 0;
if not InitProcLibs then Exit;
Result := WTSGetActiveConsoleSessionId;
end;
function xbwGetProcessID(strProcess: String; iSessionID: Integer = -1): DWORD;
var dwSessionId, winlogonSessId: DWord;
hsnap: THandle;
procEntry: TProcessEntry32;
myPID: Cardinal;
begin
Result := 0;
if not InitProcLibs then Exit;
{ check running processes and return ID of process in current session... }
if iSessionID = -1 then
dwSessionId := WTSGetActiveConsoleSessionId
else
dwSessionId := iSessionID;
hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap = INVALID_HANDLE_VALUE) then Exit;
strProcess:=UpperCase(ExtractFileName(strProcess));
myPID:= GetCurrentProcessId;
procEntry.dwSize := sizeof(TProcessEntry32);
if (not Process32First(hSnap, procEntry)) then Exit;
repeat
if (procEntry.th32ProcessID<>myPID) and ((UpperCase(procEntry.szExeFile) = strProcess) or
(UpperCase(ExtractFileName(procEntry.szExeFile)) = strProcess)) then
begin
winlogonSessId := 0;
if (ProcessIdToSessionId(procEntry.th32ProcessID, winlogonSessId) and (winlogonSessId = dwSessionId)) then
begin
Result := procEntry.th32ProcessID;
break;
end;
end;
until (not Process32Next(hSnap, procEntry));
end;
procedure xbwExecProcess(strParameters: String; strConfig: String = '');
function GetSpecialPath(csidl: Integer): String;
var i: Integer;
begin
SetLength(Result, MAX_PATH);
SHGetFolderPath(0, csidl or CSIDL_FLAG_CREATE, 0, 0, PChar(Result));
i := Pos(#0, Result);
if i > 0 then SetLength(Result, Pred(i));
end;
var strCommonAppData, strResult: String;
iLoop: Integer;
begin
{ execute command using lower process executable... }
strCommonAppData IncludeTrailingBackslash( GetSpecialPath(CSIDL_COMMON_APPDATA)TA)) + STR_PRODUCT + '\';
with TIniFile.Create(strCommonAppData + INI_SESSION) do
try
WriteString(STR_PRODUCT, INI_EXEC, strParameters);
{ collect config into INI transfer... }
if strConfig <> '' then
begin
strConfig := StringReplace(strConfig, #13, '&RC&', [rfReplaceAll]);
strConfig := StringReplace(strConfig, ',', '&CM&', [rfReplaceAll]);
with TStringList.Create do
try
StrictDelimiter := True;
Delimiter := '|';
DelimitedText := strConfig;
for iLoop := 0 to Count -1 do
WriteString(STR_PRODUCT, Names[iLoop], ValueFromIndex[iLoop]);
finally
Free;
end;
end;
{ start process as standard user... }
xbwStartProcess(ExtractFilePath(Application.ExeName) + 'CMCProcess.exe', False, xbwSessionNumber);
Sleep(1000);
{ check result... }
strResult := ReadString(STR_PRODUCT, INI_RESULT, '');
if strResult <> '' then
raise Exception.Create(strResult);
finally
Free;
end;
end;
function xbwStartProcess(strProcess: String; bLocalSystem: Boolean = True; iSessionID: Integer = -1): Boolean;
var pi: PROCESS_INFORMATION;
si: STARTUPINFO;
winlogonPid, dwSessionId: DWord;
hUserToken, hUserTokenDup, hPToken, hProcess: THANDLE;
dwCreationFlags: DWORD;
tp: TOKEN_PRIVILEGES;
lpenv: pointer;
bError: Boolean;
strClone: String;
begin
{ start process as elevated by cloning existing process, as we're running as admin... }
Result := True;
bError := False;
if not InitProcLibs then Exit;
if bLocalSystem then strClone := 'winlogon.exe' else strClone := 'explorer.exe';
winlogonPid := xbwGetProcessID(strClone, iSessionID);
try
{ get user token for winlogon and duplicate it... (this gives us admin rights) }
dwSessionId := WTSGetActiveConsoleSessionId();
WTSQueryUserToken(dwSessionId, hUserToken);
dwCreationFlags := NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE;
ZeroMemory(@si, sizeof(STARTUPINFO));
si.cb := sizeof(STARTUPINFO);
si.lpDesktop := PChar('Winsta0\Default');
ZeroMemory(@pi, sizeof(pi));
hProcess := OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid);
if (not OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY or TOKEN_DUPLICATE or
TOKEN_ASSIGN_PRIMARY or TOKEN_ADJUST_SESSIONID or TOKEN_READ or TOKEN_WRITE, hPToken)) then
bError := True;
if (not LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid)) then
bError := True;
tp.PrivilegeCount := 1;
tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, Nil, SecurityIdentification, TokenPrimary, hUserTokenDup);
{ adjust token privilege }
SetTokenInformation(hUserTokenDup, TokenSessionId, pointer(dwSessionId), sizeof(DWORD));
if (not AdjustTokenPrivileges(hUserTokenDup, FALSE, @tp, sizeof(TOKEN_PRIVILEGES), nil, nil)) then bError := True;
if (GetLastError() = ERROR_NOT_ALL_ASSIGNED) then bError := True;
lpEnv := nil;
if (CreateEnvironmentBlock(lpEnv, hUserTokenDup,TRUE)) then
dwCreationFlags := dwCreationFlags or CREATE_UNICODE_ENVIRONMENT
else
lpEnv := nil;
{ launch the process in the client's logon session... }
if not CreateProcessAsUser(hUserTokenDup, nil, PChar(strProcess), nil, nil, FALSE,
dwCreationFlags, lpEnv, PChar(ExtractFilePath(strProcess)), si, pi) then bError := True;
{ perform all the close handles tasks... }
try
CloseHandle(hProcess);
CloseHandle(hUserToken);
CloseHandle(hUserTokenDup);
CloseHandle(hPToken);
except
{}
end;
except
bError := True;
end;
Result := not bError;
end;
function xbwImpersonateLoggedOnUser: Boolean;
var pi: PROCESS_INFORMATION;
si: STARTUPINFO;
winlogonPid, dwSessionId: DWord;
hUserToken, hPToken, hProcess: THANDLE;
tp: TOKEN_PRIVILEGES;
bError: Boolean;
strClone: String;
begin
{ start process as elevated by cloning existing process, as we're running as admin... }
Result := True;
bError := False;
if not InitProcLibs then Exit;
strClone := 'explorer.exe';
winlogonPid := xbwGetProcessID(strClone);
try
{ get user token for winlogon and duplicate it... (this gives us admin rights) }
dwSessionId := WTSGetActiveConsoleSessionId();
WTSQueryUserToken(dwSessionId, hUserToken);
ZeroMemory(@si, sizeof(STARTUPINFO));
si.cb := sizeof(STARTUPINFO);
si.lpDesktop := PChar('Winsta0\Default');
ZeroMemory(@pi, sizeof(pi));
hProcess := OpenProcess(MAXIMUM_ALLOWED, FALSE, winlogonPid);
if (not OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY or TOKEN_DUPLICATE or
TOKEN_ASSIGN_PRIMARY or TOKEN_ADJUST_SESSIONID or TOKEN_READ or TOKEN_WRITE, hPToken)) then
bError := True;
if (not LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid)) then
bError := True;
tp.PrivilegeCount := 1;
tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, Nil, SecurityIdentification, TokenPrimary, FhUserTokenDup);
{ adjust token privilege }
SetTokenInformation(FhUserTokenDup, TokenSessionId, pointer(dwSessionId), sizeof(DWORD));
if (not AdjustTokenPrivileges(FhUserTokenDup, FALSE, @tp, sizeof(TOKEN_PRIVILEGES), nil, nil)) then bError := True;
if (GetLastError() = ERROR_NOT_ALL_ASSIGNED) then bError := True;
{ do the impersonation... }
ImpersonateLoggedOnUser(FhUserTokenDup);
{ perform all the close handles tasks... }
try
CloseHandle(hProcess);
CloseHandle(hUserToken);
// CloseHandle(FhUserTokenDup); { this is closed later... }
CloseHandle(hPToken);
except
{}
end;
except
bError := True;
end;
Result := not bError;
end;
function xbwRevertToSelf: Boolean;
begin
RevertToSelf;
CloseHandle(FhUserTokenDup);
end;
end.
Enjoy!
|