jdevelop
jdevelop

Reputation: 12296

Restarting explorer.exe process for all logged users on Windows to enforce SRP rules

I need to intercept certain signal in a windows service, and then find all explorer.exe processes and restart them.

So far it works fine if only one user is logged into the system and the service is in "foreground" mode, e.g - started as process for the current user. Explorer is killed and restarted just fine. However if there are multiple users - the explorer is killed but started only for the current user.

Moreover, as a service - there is a different user that owns the service itself, and while killing explorer works fine - it can't be started most of the time, and if starts - it is started for the service user only.

With some digging in Win32 API it seems possible to use something like CreateProcessWithLogonW() - but that requires knowing user's credentials.

Is there any way to restart / send some signal to explorer so it will know that it needs to restart / re-read SRP rules?


UPD: just for the reference if someone is looking for the hints:

Upvotes: 0

Views: 1126

Answers (1)

RbMm
RbMm

Reputation: 33754

if you run from service you in most case (if service not special restricted) will have SE_TCB_NAME privilege. as result you can call WTSQueryUserToken for obtain the primary access token of the logged-on user specified by the session ID. the explorer run exactly with this token. and you can use CreateProcessAsUserW for start explorer again with this token (again i assume your service not restricted and have SE_ASSIGNPRIMARYTOKEN_NAME privilege.). so you can enumerate all user terminal sessions with WTSEnumerateSessionsW function, got token for every session, found explorer in session and restart it.

but here question how found explorer process(es). of course we can do this by short file name. enumerate all processes. but i think more reliable way will be call GetShellWindow and then GetWindowThreadProcessId. but we can not do this direct from service. this need do exactly from user session(s). so possible solution - first launch self exe in every terminal session, with special command line as marker, and then already from session call GetShellWindow and restart explorer with simply CreateProcessW call

WCHAR ExeName[MAX_PATH];
PROCESS_INFORMATION pi;
STARTUPINFO si = { sizeof(si) };

static const WCHAR tagCmdLine[] = L"|";

if (wcscmp(GetCommandLineW(), tagCmdLine))
{
    GetModuleFileNameW(0, ExeName, _countof(ExeName));

    if (GetLastError() == NOERROR)
    {
        ULONG Count;
        PWTS_SESSION_INFO pSessionInfo;
        if (WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &Count))
        {
            if (Count)
            {
                pSessionInfo += Count;
                do 
                {
                    HANDLE hToken;
                    if (WTSQueryUserToken((--pSessionInfo)->SessionId, &hToken))
                    {
                        PVOID lpEnvironment;
                        if (CreateEnvironmentBlock(&lpEnvironment, hToken, FALSE))
                        {
                            if (CreateProcessAsUserW(hToken, ExeName, const_cast<PWSTR>(tagCmdLine), 
                                0, 0, 0, CREATE_UNICODE_ENVIRONMENT, lpEnvironment, 0, &si, &pi))
                            {
                                CloseHandle(pi.hThread);
                                CloseHandle(pi.hProcess);
                            }
                            DestroyEnvironmentBlock(lpEnvironment);
                        }
                        CloseHandle(hToken);
                    }
                } while (--Count);
            }

            WTSFreeMemory(pSessionInfo);
        }
    }
}
else
{
    if (HWND hwnd = GetShellWindow())
    {
        ULONG dwProcessId, dwThreadId;

        if (dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId))
        {
            if (HANDLE hProcess = OpenProcess(PROCESS_TERMINATE|SYNCHRONIZE|
                PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcessId))
            {
                ULONG cch = _countof(ExeName);
                if (QueryFullProcessImageNameW(hProcess, 0, ExeName, &cch))
                {
                    PostThreadMessageW(dwThreadId, WM_QUIT, 0, 0);

                    if (WaitForSingleObject(hProcess, 1000) == WAIT_OBJECT_0 ||
                        TerminateProcess(hProcess, 0) || 
                        RtlGetLastNtStatus() == STATUS_PROCESS_IS_TERMINATING)
                    {
                        if (CreateProcessW(ExeName, 0, 0, 0, 0, 0, 0, 0, &si, &pi))
                        {
                            CloseHandle(pi.hThread);
                            CloseHandle(pi.hProcess);
                        }
                    }
                }
                CloseHandle(hProcess);
            }
        }
    }

    ExitProcess(0);

Upvotes: 1

Related Questions