|
|
/******************************************************************************
Copyright (c) 2001 Microsoft Corporation
Module Name: erwait.cpp
Revision History: derekm 02/28/2001 created
******************************************************************************/
/////////////////////////////////////////////////////////////////////////////
// tracing
#ifdef THIS_FILE
#undef THIS_FILE
#endif
static char __szTraceSourceFile[] = __FILE__; #define THIS_FILE __szTraceSourceFile
#include "stdafx.h"
#include <stdio.h>
#include <pfrcfg.h>
// this is a private NT function that doesn't appear to be defined anywhere
// public, so I'm including it here
extern "C" { HANDLE GetCurrentUserTokenW( WCHAR Winsta[], DWORD DesiredAccess); }
//////////////////////////////////////////////////////////////////////////////
// utility functions
// ***************************************************************************
BOOL CreateQueueDir(void) { SECURITY_DESCRIPTOR sd; SECURITY_ATTRIBUTES sa; WCHAR wszDir[MAX_PATH], *pwszDir = NULL; DWORD dw, cchNeed, cch; BOOL fRet = FALSE;
USE_TRACING("CreateQueueDir");
ZeroMemory(&sa, sizeof(sa)); ZeroMemory(&sd, sizeof(sd));
#define DIR_ACCESS_ALL GENERIC_ALL | DELETE | READ_CONTROL | SYNCHRONIZE | SPECIFIC_RIGHTS_ALL
if (AllocSD(&sd, DIR_ACCESS_ALL, DIR_ACCESS_ALL, 0) == FALSE) { DBG_MSG("AllocSD dies"); goto done; }
sa.nLength = sizeof(sa); sa.bInheritHandle = FALSE; sa.lpSecurityDescriptor = &sd;
cch = GetSystemWindowsDirectoryW(wszDir, sizeofSTRW(wszDir)); if (cch == 0) goto done;
cchNeed = cch + sizeofSTRW(c_wszQSubdir) + 2; if (cchNeed > sizeofSTRW(wszDir)) { __try { pwszDir = (WCHAR *)_alloca(cchNeed * sizeof(WCHAR)); } __except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { pwszDir = NULL; } if (pwszDir == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
if (cch > sizeofSTRW(wszDir)) cch = GetSystemWindowsDirectoryW(pwszDir, cchNeed); else StringCchCopyW(pwszDir, cchNeed, wszDir); } else { pwszDir = wszDir; cchNeed = sizeofSTRW(wszDir); }
// if the length of the system directory is 3, then %windir% is in the form
// of X:\, so clip off the backslash so we don't have to special case
// it below...
if (cch == 3) *(pwszDir + 2) = L'\0';
StringCchCatW(pwszDir, cchNeed, L"\\PCHealth");
if (CreateDirectoryW(pwszDir, NULL) == FALSE && GetLastError() != ERROR_ALREADY_EXISTS) goto done; StringCchCatW(pwszDir, cchNeed, L"\\ErrorRep");
if (CreateDirectoryW(pwszDir, NULL) == FALSE && GetLastError() != ERROR_ALREADY_EXISTS) goto done;
StringCchCatW(pwszDir, cchNeed, L"\\UserDumps");
if (CreateDirectoryW(pwszDir, &sa) == FALSE && GetLastError() != ERROR_ALREADY_EXISTS) goto done;
// if the directory is created, then we need to write the security
// descriptor to it cuz we want to make sure that no inappropriate
// people can access it.
if (GetLastError() != ERROR_ALREADY_EXISTS) { SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY; PACL pacl = NULL; PSID psidLS = NULL; BOOL fDef, fACL;
if (GetSecurityDescriptorDacl(&sd, &fACL, &pacl, &fDef) == FALSE) goto done;
if (AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidLS) == FALSE) goto done; dw = SetNamedSecurityInfoW(pwszDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, psidLS, NULL, pacl, NULL); FreeSid(psidLS); if (dw != ERROR_SUCCESS) { SetLastError(dw); goto done; } }
fRet = TRUE;
done: return fRet; }
// ***************************************************************************
HMODULE LoadERDll(void) { HMODULE hmod = NULL; WCHAR wszMod[MAX_PATH], *pwszMod; DWORD cch, cchNeed;
cch = GetSystemDirectoryW(wszMod, sizeofSTRW(wszMod)); if (cch == 0) goto done;
// the '14' is for the
cchNeed = cch + 14;
if (cchNeed > sizeofSTRW(wszMod)) { __try { pwszMod = (WCHAR *)_alloca(cchNeed * sizeof(WCHAR)); } __except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { pwszMod = NULL; } if (pwszMod == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
if (cch > sizeofSTRW(wszMod)) cch = GetSystemDirectoryW(pwszMod, cchNeed); else StringCchCopyW(pwszMod, cchNeed, wszMod); } else { pwszMod = wszMod; cchNeed = sizeofSTRW(wszMod); }
// if the length of the system directory is 3, then %windir% is in the form
// of X:\, so clip off the backslash so we don't have to special case
// it below...
if (cch == 3) *(pwszMod + 2) = L'\0';
StringCchCatW(pwszMod, cchNeed, L"\\faultrep.dll");
hmod = LoadLibraryExW(wszMod, NULL, 0);
done: return hmod; }
// **************************************************************************
BOOL FindAdminSession(DWORD *pdwSession, HANDLE *phToken) { USE_TRACING("FindAdminSession");
WINSTATIONUSERTOKEN wsut; LOGONIDW *rgSesn = NULL; DWORD i, cSesn, cb, dw; BOOL fRet = FALSE; HRESULT hr = NOERROR;
ZeroMemory(&wsut, sizeof(wsut));
VALIDATEPARM(hr, (pdwSession == NULL || phToken == NULL)); if (FAILED(hr)) { SetLastError(ERROR_INVALID_PARAMETER); goto done; }
*pdwSession = (DWORD)-1; *phToken = NULL;
fRet = WinStationEnumerateW(SERVERNAME_CURRENT, &rgSesn, &cSesn); if (fRet == FALSE) goto done; wsut.ProcessId = LongToHandle(GetCurrentProcessId()); wsut.ThreadId = LongToHandle(GetCurrentThreadId());
for(i = 0; i < cSesn; i++) { if (rgSesn[i].State != State_Active) continue;
fRet = WinStationQueryInformationW(SERVERNAME_CURRENT, rgSesn[i].SessionId, WinStationUserToken, &wsut, sizeof(wsut), &cb); if (fRet == FALSE) continue;
if (wsut.UserToken != NULL) { if (IsUserAnAdmin(wsut.UserToken)) break; CloseHandle(wsut.UserToken); wsut.UserToken = NULL; } }
if (i < cSesn) { fRet = TRUE; *pdwSession = rgSesn[i].SessionId; *phToken = wsut.UserToken; } else { fRet = FALSE; } done: dw = GetLastError(); if (rgSesn != NULL) WinStationFreeMemory(rgSesn); SetLastError(dw); return fRet; }
// ***************************************************************************
BOOL GetInteractiveUsersToken(HANDLE *phTokenUser) { HWINSTA hwinsta = NULL; DWORD cbNeed; BOOL fRet = FALSE; PSID psid = NULL; HRESULT hr = NOERROR;
USE_TRACING("GetInteractiveUsersToken");
VALIDATEPARM(hr, (phTokenUser == NULL)); if (FAILED(hr)) { SetLastError(ERROR_INVALID_PARAMETER); goto done; }
*phTokenUser = NULL;
hwinsta = OpenWindowStationW(L"WinSta0", FALSE, MAXIMUM_ALLOWED); if (hwinsta == NULL) goto done;
// if this function returns 0 for cbNeed, there is no one logged in. Also,
// it should never return TRUE with these parameters. If it does,
// something's wrong...
fRet = GetUserObjectInformationW(hwinsta, UOI_USER_SID, NULL, 0, &cbNeed); if (fRet || cbNeed == 0) { fRet = FALSE; goto done; }
*phTokenUser = GetCurrentUserTokenW(L"WinSta0", TOKEN_ALL_ACCESS); fRet = (*phTokenUser != NULL); done: if (hwinsta != NULL) CloseWindowStation(hwinsta); return fRet; }
// ***************************************************************************
BOOL ValidateUserAccessToProcess(HANDLE hPipe, HANDLE hProcRemote) { PSECURITY_DESCRIPTOR psd = NULL; GENERIC_MAPPING gm; HRESULT hr = NOERROR; PRIVILEGE_SET *pPS; ACCESS_MASK amReq; HANDLE hTokenImp = NULL; DWORD dwErr = 0, cbPS, dwGranted; PACL pDACL = NULL; PACL pSACL = NULL; PSID psidOwner = NULL; PSID psidGroup = NULL; BYTE rgBuf[sizeof(PRIVILEGE_SET) + 3 * sizeof(LUID_AND_ATTRIBUTES)]; BOOL fRet = FALSE, fStatus = FALSE;
USE_TRACING("ValidateUserAccessToProcess");
VALIDATEPARM(hr, (hPipe == NULL || hProcRemote == NULL)); if (FAILED(hr)) { SetLastError(ERROR_INVALID_PARAMETER); goto done; }
ZeroMemory(&gm, sizeof(gm)); gm.GenericAll = GENERIC_ALL; gm.GenericExecute = GENERIC_EXECUTE; gm.GenericRead = GENERIC_READ; gm.GenericWrite = GENERIC_WRITE; pPS = (PRIVILEGE_SET *)rgBuf; cbPS = sizeof(rgBuf); // get the SD for the remote process
dwErr = GetSecurityInfo(hProcRemote, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, &psidOwner, &psidGroup, &pDACL, &pSACL, &psd); if (dwErr != ERROR_SUCCESS) { SetLastError(dwErr); goto done; }
// get the client's token
fRet = ImpersonateNamedPipeClient(hPipe); if (fRet == FALSE) goto done;
fRet = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hTokenImp);
// don't need to be the other user anymore, so go back to being LocalSystem
RevertToSelf(); if (fRet == FALSE) goto done; amReq = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; fRet = AccessCheck(psd, hTokenImp, amReq, &gm, pPS, &cbPS, &dwGranted, &fStatus); if (fRet == FALSE) goto done;
if (fStatus == FALSE || (dwGranted & amReq) != amReq) { fRet = FALSE; SetLastError(ERROR_ACCESS_DENIED); goto done; }
fRet = TRUE;
done: if (hTokenImp != NULL) CloseHandle(hTokenImp); if (psd != NULL) LocalFree(psd);
return fRet; }
//////////////////////////////////////////////////////////////////////////////
// remote execution functions
// ***************************************************************************
BOOL ProcessFaultRequest(HANDLE hPipe, PBYTE pBuf, DWORD *pcbBuf) { SPCHExecServFaultRequest *pesreq = (SPCHExecServFaultRequest *)pBuf; SPCHExecServFaultReply esrep, *pesrep; OSVERSIONINFOEXW osvi; SFaultRepManifest frm; EFaultRepRetVal frrv = frrvErrNoDW; HMODULE hmodFR = NULL; LPWSTR wszTempDir = NULL, wszDumpFileName = NULL; HANDLE hprocRemote = NULL; HANDLE hTokenUser = NULL; LPVOID pvEnv = NULL; WCHAR *pwszDump = NULL; WCHAR wszTemp[MAX_PATH]; DWORD cb, dw, dwSessionId; WCHAR wch = L'\0', *pwch, *pwszFile; BOOL fRet = FALSE, fUseManifest = FALSE; BOOL fQueue = FALSE, fTS = FALSE; HRESULT hr = NOERROR; BOOL fLeaveTempDir = FALSE;
USE_TRACING("ProcessFaultRequest");
ZeroMemory(&esrep, sizeof(esrep)); ZeroMemory(&frm, sizeof(frm));
pwszDump = &wch;
SetLastError(ERROR_INVALID_PARAMETER); esrep.cb = sizeof(esrep); esrep.ess = essErr;
// validate parameters
VALIDATEPARM(hr, (pesreq->cbTotal > *pcbBuf || pesreq->cbTotal < sizeof(SPCHExecServFaultRequest) || pesreq->cbESR != sizeof(SPCHExecServFaultRequest) || pesreq->thidFault == 0 || pesreq->wszExe == 0 || pesreq->wszExe >= *pcbBuf)); if (FAILED(hr)) { SetLastError(ERROR_INVALID_PARAMETER); goto done; }
// check and make sure that there is a NULL terminator between the
// start of the string & the end of the buffer
pwszFile = (LPWSTR)(pBuf + *pcbBuf); frm.wszExe = (LPWSTR)(pBuf + (DWORD)pesreq->wszExe); for (pwch = frm.wszExe; pwch < pwszFile && *pwch != L'\0'; pwch++); VALIDATEPARM(hr, (pwch >= pwszFile)); if (FAILED(hr)) { SetLastError(ERROR_INVALID_PARAMETER); goto done; }
// need the filename for comparison later...
for (pwszFile = pwch; pwszFile > frm.wszExe && *pwszFile != L'\\'; pwszFile--); if (*pwszFile == L'\\') pwszFile++;
frm.pidReqProcess = pesreq->pidReqProcess; frm.pvFaultAddr = pesreq->pvFaultAddr; frm.thidFault = pesreq->thidFault; frm.fIs64bit = pesreq->fIs64bit; frm.pEP = pesreq->pEP;
ZeroMemory(&osvi, sizeof(osvi)); osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionExW((LPOSVERSIONINFOW)&osvi); if ((osvi.wSuiteMask & (VER_SUITE_TERMINAL | VER_SUITE_SINGLEUSERTS)) != 0) fTS = TRUE;
// if it's one of these processes, then we need to ALWAYS go queued.
if (_wcsicmp(pwszFile, L"lsass.exe") == 0 || _wcsicmp(pwszFile, L"winlogon.exe") == 0 || _wcsicmp(pwszFile, L"csrss.exe") == 0 || _wcsicmp(pwszFile, L"smss.exe") == 0) { fQueue = TRUE; fUseManifest = FALSE; }
if (fQueue == FALSE) { // need the session id for the process
fRet = ProcessIdToSessionId(pesreq->pidReqProcess, &dwSessionId); if (fTS && fRet) { WINSTATIONINFORMATIONW wsi; WINSTATIONUSERTOKEN wsut; ZeroMemory(&wsi, sizeof(wsi)); fRet = WinStationQueryInformationW(SERVERNAME_CURRENT, dwSessionId, WinStationInformation, &wsi, sizeof(wsi), &cb); if (fRet == FALSE) goto doneTSTokenFetch;
// so the session isn't active. Determine what our session is cuz
// if the faulting process and the ERSvc are in the same session,
// then our session is obviously not active either. So it's not
// useful to obtain the user token for it.
if (wsi.ConnectState != State_Active) { fRet = FALSE; goto doneTokenFetch; }
// get the token of the user we want to pop up the display to.
// If this fn returns FALSE, assume there is no one logged
// into our session & just go to delayed fault reporting
ZeroMemory(&wsut, sizeof(wsut)); wsut.ProcessId = LongToHandle(GetCurrentProcessId()); wsut.ThreadId = LongToHandle(GetCurrentThreadId()); fRet = WinStationQueryInformationW(SERVERNAME_CURRENT, dwSessionId, WinStationUserToken, &wsut, sizeof(wsut), &cb); if (fRet == FALSE) goto doneTSTokenFetch;
hTokenUser = wsut.UserToken; } else { fRet = FALSE; } doneTSTokenFetch: if (fRet == FALSE) fRet = GetInteractiveUsersToken(&hTokenUser);
// if the above call succeeded, check if the user is an admin or not...
// If he is, we can just display DW directly. Otherwise, we go into
// delayed reporting mode.
if (fRet && hTokenUser != NULL) fUseManifest = IsUserAnAdmin(hTokenUser); }
doneTokenFetch: // if we are going to go into manifest mode, then check to see if we really
// need to go into Q mode
if (fQueue == FALSE) { EEnDis eedReport; HKEY hkey = NULL; // first check the policy key
dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRPCfgPolicy, 0, KEY_READ, &hkey); if (dw == ERROR_SUCCESS) { // ok, if that succeeded then check and see if the DoReport value
// is here...
cb = sizeof(eedReport); dw = RegQueryValueExW(hkey, c_wszRVDoReport, 0, NULL, (LPBYTE)&eedReport, &cb); if (dw == ERROR_SUCCESS /*&& eedReport != eedDisabled*/) { cb = sizeof(fQueue); dw = RegQueryValueExW(hkey, c_wszRVForceQueue, 0, NULL, (LPBYTE)&fQueue, &cb);
// if it's not a valid value, then pretend we got an error
if (dw == ERROR_SUCCESS && fQueue != TRUE && fQueue != FALSE) dw = ERROR_INVALID_PARAMETER; } else { RegCloseKey(hkey); hkey = NULL; } }
// if we didn't find a policy key or we were not able to read the
// 'DoReport' value from it, then try the CPL key
if (dw != ERROR_SUCCESS && hkey == NULL) { dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRPCfg, 0, KEY_READ, &hkey); if (dw == ERROR_SUCCESS) { cb = sizeof(eedReport); dw = RegQueryValueExW(hkey, c_wszRVDoReport, 0, NULL, (LPBYTE)&eedReport, &cb); if (dw == ERROR_SUCCESS /*&& eedReport != eedDisabled*/) { cb = sizeof(fQueue); dw = RegQueryValueExW(hkey, c_wszRVForceQueue, 0, NULL, (LPBYTE)&fQueue, &cb);
// if it's not a valid value, then pretend we got an error
if (dw == ERROR_SUCCESS && fQueue != TRUE && fQueue != FALSE) dw = ERROR_INVALID_PARAMETER; } } }
// ok, if we still haven't got an ERROR_SUCCESS value back, then
// determine what the default should be.
if (dw != ERROR_SUCCESS) fQueue = (osvi.wProductType == VER_NT_SERVER);
if (hkey != NULL) RegCloseKey(hkey);
if (fQueue) fUseManifest = FALSE; }
if (SetupIsInProgress()) { // force this to Q mode
fQueue = TRUE; fUseManifest = FALSE; }
// ok, so if we're not in forced queue mode & we're not in manifest mode
// then go hunt and see if any other session on the machine has an admin
// logged into it, assuming we're on a TS machine
if (fQueue == FALSE && fUseManifest == FALSE && fTS) { if (hTokenUser != NULL) { CloseHandle(hTokenUser); hTokenUser = NULL; } fUseManifest = FindAdminSession(&dwSessionId, &hTokenUser); }
hmodFR = LoadERDll(); if (hmodFR == NULL) goto done;
// need a handle to the process to verify that the user has access to it.
hprocRemote = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | READ_CONTROL | ACCESS_SYSTEM_SECURITY, FALSE, pesreq->pidReqProcess); if (hprocRemote == NULL) goto done;
fRet = ValidateUserAccessToProcess(hPipe, hprocRemote); if (fRet == FALSE) goto done;
if (fUseManifest) { PROCESS_INFORMATION pi; pfn_REPORTFAULTDWM pfn; DWORD cch;
ZeroMemory(&pi, sizeof(pi));
pfn = (pfn_REPORTFAULTDWM)GetProcAddress(hmodFR, "ReportFaultDWM"); if (pfn == NULL) goto done;
// need to figure out the temp path for the logged on user...
wszTemp[0] = L'\0'; fRet = ExpandEnvironmentStringsForUserW(hTokenUser, L"%TMP%", wszTemp, sizeofSTRW(wszTemp)); if (fRet == FALSE || wszTemp[0] == L'\0') { fRet = ExpandEnvironmentStringsForUserW(hTokenUser, L"%TEMP%", wszTemp, sizeofSTRW(wszTemp)); if (fRet == FALSE || wszTemp[0] == L'\0') { fRet = ExpandEnvironmentStringsForUserW(hTokenUser, L"%USERPROFILE%", wszTemp, sizeofSTRW(wszTemp)); if (fRet == FALSE || wszTemp[0] == L'\0') GetTempPathW(sizeofSTRW(wszTemp), wszTemp); } }
// determine what the name of the dump file will be
cch = wcslen(pwszFile) + sizeofSTRW(c_wszDumpSuffix) + 1;
__try { wszDumpFileName = (WCHAR *)_alloca(cch * sizeof(WCHAR)); } __except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { wszDumpFileName = NULL; } if (wszDumpFileName == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; } StringCchCopyW(wszDumpFileName, cch, pwszFile); StringCchCatW(wszDumpFileName, cch, c_wszDumpSuffix);
// get a dir where we'll put all our temp files
if (CreateTempDirAndFile(wszTemp, NULL, &wszTempDir) == 0) goto done;
// need an environment block for the target user to properly launch DW
if (CreateEnvironmentBlock(&pvEnv, hTokenUser, FALSE) == FALSE) pvEnv = NULL;
// do the real work.
frrv = (*pfn)(&frm, wszTempDir, hTokenUser, pvEnv, &pi, wszDumpFileName);
// we only need to duplicate the hProcess back to the requesting process
// cuz it doesn't use any of the other values in the PROCESSINFORMATION
// structure.
if (pi.hThread != NULL) CloseHandle(pi.hThread);
// if we don't get this error code back, then we didn't launch the
// process, or it died for some reason. Now we will try to queue it
if (frrv != frrvOk) { fUseManifest = FALSE; goto try_queue; }
if (hprocRemote != NULL) { fRet = DuplicateHandle(GetCurrentProcess(), pi.hProcess, hprocRemote, &esrep.hProcess, 0, FALSE, DUPLICATE_SAME_ACCESS); if (fRet == FALSE) esrep.hProcess = NULL; }
if (pi.hProcess != NULL) CloseHandle(pi.hProcess); esrep.ess = essOk; // The temp directory will be handed off in the reply
// so do not clean it up.
fLeaveTempDir = TRUE; }
// since the user is NOT an admin, we have to queue it for later viewing
// the next time an admin logs on
else { pfn_REPORTFAULTTOQ pfn; try_queue: pfn = (pfn_REPORTFAULTTOQ)GetProcAddress(hmodFR, "ReportFaultToQueue"); if (pfn == NULL) goto done;
if (CreateQueueDir() == FALSE) goto done;
frrv = (*pfn)(&frm);
// want to be local system again...
dw = GetLastError(); RevertToSelf(); SetLastError(dw); // if (frrv != frrvOk)
// goto done;
esrep.ess = essOkQueued; }
SetLastError(0); fRet = TRUE;
done: esrep.dwErr = GetLastError();
// build the reply packet with the handle valid in the context
// of the requesting process
pesrep = (SPCHExecServFaultReply *)pBuf; RtlCopyMemory(pesrep, &esrep, sizeof(esrep)); *pcbBuf = sizeof(esrep) + sizeof(esrep) % sizeof(WCHAR); if (fUseManifest) { pBuf += *pcbBuf;
if (wszTempDir != NULL) { cb = (wcslen(wszTempDir) + 1) * sizeof(WCHAR); RtlCopyMemory(pBuf, wszTempDir, cb); } else { cb = sizeof(WCHAR); *pBuf = L'\0'; } *pcbBuf += cb; pesrep->wszDir = (UINT64)pBuf - (UINT64)pesrep;
pBuf += cb; if (wszDumpFileName != NULL) { cb = (wcslen(wszDumpFileName) + 1) * sizeof(WCHAR); RtlCopyMemory(pBuf, wszDumpFileName, cb); } else { cb = sizeof(WCHAR); *pBuf = L'\0'; } *pcbBuf += cb; pesrep->wszDumpName = (UINT64)pBuf - (UINT64)pesrep; }
if (wszTempDir != NULL) { if (!fLeaveTempDir) DeleteTempDirAndFile(wszTempDir, FALSE); MyFree(wszTempDir); } if (pvEnv != NULL) DestroyEnvironmentBlock(pvEnv); if (hprocRemote != NULL) CloseHandle(hprocRemote); if (hTokenUser != NULL) CloseHandle(hTokenUser); if (hmodFR != NULL) FreeLibrary(hmodFR);
ErrorTrace(1, "return %d", esrep.ess); return fRet; }
// ***************************************************************************
// Note that the pipe that this thread services is secured such that only
// local system has access to it. Thus, we do not need to impersonate the
// pipe client in it.
BOOL ProcessHangRequest(HANDLE hPipe, PBYTE pBuf, DWORD *pcbBuf) { SPCHExecServHangRequest *pesreq = (SPCHExecServHangRequest *)pBuf; SPCHExecServHangReply esrep; PROCESS_INFORMATION pi; WINSTATIONUSERTOKEN wsut; OSVERSIONINFOEXW osvi; STARTUPINFOW si; HANDLE hprocRemote = NULL, hTokenUser = NULL; LPVOID pvEnv = NULL; LPWSTR wszEventName; DWORD cbWrote, dwErr, cch, cchNeed; WCHAR wszSysDir[MAX_PATH], *pwszSysDir = NULL; WCHAR *pwszCmdLine = NULL, *pwszEnd = NULL; WCHAR *pwch; BOOL fRet = FALSE; HRESULT hr;
USE_TRACING("ProcessHangRequest");
ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&wsut, sizeof(wsut)); ZeroMemory(&esrep, sizeof(esrep)); SetLastError(ERROR_INVALID_PARAMETER); esrep.cb = sizeof(esrep); esrep.ess = essErr;
// validate parameters
VALIDATEPARM(hr, (*pcbBuf < sizeof(SPCHExecServHangRequest) || pesreq->cbESR != sizeof(SPCHExecServHangRequest) || pesreq->wszEventName == 0 || pesreq->wszEventName >= *pcbBuf || pesreq->dwpidHung == 0 || pesreq->dwtidHung == 0));
if (FAILED(hr)) { SetLastError(ERROR_INVALID_PARAMETER); goto done; }
// check and make sure that there is a NULL terminator between the
// start of the string & the end of the buffer
pwszEnd = (LPWSTR)(pBuf + *pcbBuf); wszEventName = (LPWSTR)(pBuf + (DWORD)pesreq->wszEventName); for (pwch = wszEventName; pwch < pwszEnd && *pwch != L'\0'; pwch++); if (pwch >= pwszEnd) { SetLastError(ERROR_INVALID_PARAMETER); DBG_MSG("Bad event name"); goto done; }
// Get the handle to remote process
hprocRemote = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION, FALSE, pesreq->pidReqProcess); if (hprocRemote == NULL) { DBG_MSG("Can't get handle to process"); goto done; }
ZeroMemory(&osvi, sizeof(osvi)); osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionExW((LPOSVERSIONINFOW)&osvi); if ((osvi.wSuiteMask & (VER_SUITE_TERMINAL | VER_SUITE_SINGLEUSERTS)) != 0) { WINSTATIONINFORMATIONW wsi; DWORD cb; ZeroMemory(&wsi, sizeof(wsi)); fRet = WinStationQueryInformationW(SERVERNAME_CURRENT, pesreq->ulSessionId, WinStationInformation, &wsi, sizeof(wsi), &cb); if (fRet == FALSE) { DBG_MSG("WinStationQI failed"); goto doneTSTokenFetch; }
// if the session where the hang was terminated isn't active (which
// would be an odd state to be in- perhaps it is closing down?)
// then just bail cuz we don't want to put up UI in it
if (wsi.ConnectState != State_Active) { DBG_MSG("No Active Session found!"); SetLastError(0); goto done; } // fetch the token associated with the sessions user
wsut.ProcessId = LongToHandle(GetCurrentProcessId()); wsut.ThreadId = LongToHandle(GetCurrentThreadId()); fRet = WinStationQueryInformationW(SERVERNAME_CURRENT, pesreq->ulSessionId, WinStationUserToken, &wsut, sizeof(wsut), &cbWrote); if (fRet) { if (wsut.UserToken != NULL) hTokenUser = wsut.UserToken; else { DBG_MSG("no token found"); fRet = FALSE; } } } else { DBG_MSG("WTS not found"); fRet = FALSE; } doneTSTokenFetch: if (fRet == FALSE) { DWORD dwERSvcSession = (DWORD)-1;
// make sure the hung app is in our session before using this API and
// if it's not, just bail.
fRet = ProcessIdToSessionId(GetCurrentProcessId(), &dwERSvcSession); if (fRet == FALSE) { DBG_MSG("Failed in ProcessIdToSessionId"); goto done; } if (dwERSvcSession != pesreq->ulSessionId) { DBG_MSG("Session IDs do not match"); goto done; }
fRet = GetInteractiveUsersToken(&hTokenUser); if (fRet == FALSE) { DBG_MSG("Failure in GetInteractiveUsersToken"); goto done; } }
// create the default environment for the user token- note that we
// have to set the CREATE_UNICODE_ENVIRONMENT flag...
fRet = CreateEnvironmentBlock(&pvEnv, hTokenUser, FALSE); if (fRet == FALSE) { DBG_MSG("CreateEnvironmentBlock failed"); pvEnv = NULL; }
// note that we do not allow inheritance of handles cuz they would be
// inherited from this process and not the real parent, making it sorta
// pointless.
ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); #ifdef _WIN64
if (pesreq->fIs64bit == FALSE) cch = GetSystemWow64DirectoryW(wszSysDir, sizeofSTRW(wszSysDir)); else #endif
cch = GetSystemDirectoryW(wszSysDir, sizeofSTRW(wszSysDir)); if (cch == 0) { DBG_MSG("GetSystemDirectoryW failed"); goto done; }
cchNeed = cch + sizeofSTRW(c_wszQSubdir) + 2; if (cchNeed > sizeofSTRW(wszSysDir)) { __try { pwszSysDir = (WCHAR *)_alloca(cchNeed * sizeof(WCHAR)); } __except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { pwszSysDir = NULL; } if (pwszSysDir == NULL) { SetLastError(ERROR_OUTOFMEMORY); DBG_MSG("out of memory"); goto done; }
if (cch > sizeofSTRW(wszSysDir)) { #ifdef _WIN64
if (pesreq->fIs64bit == FALSE) cch = GetSystemWow64DirectoryW(pwszSysDir, cchNeed); else #endif
cch = GetSystemDirectoryW(pwszSysDir, cchNeed); } else { StringCchCopyW(pwszSysDir, cchNeed, wszSysDir); } } else { pwszSysDir = wszSysDir; }
// if the length of the system directory is 3, then %windir% is in the form
// of X:\, so clip off the backslash so we don't have to special case
// it below...
if (cch == 3) *(pwszSysDir + 2) = L'\0';
// compute the size of the buffer to hold the command line. 14 is for
// the max # of characters in a DWORD
cchNeed = 12 + cch + wcslen((LPWSTR)wszEventName) + sizeofSTRW(c_wszDWMCmdLine64);
__try { pwszCmdLine = (WCHAR *)_alloca(cchNeed * sizeof(WCHAR)); } __except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { pwszCmdLine = NULL; } if (pwszCmdLine == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
#ifdef _WIN64
StringCchPrintfW(pwszCmdLine, cchNeed, c_wszDWMCmdLine64, pwszSysDir, pesreq->dwpidHung, ((pesreq->fIs64bit) ? L'6' : L' '), pesreq->dwtidHung, wszEventName); #else
StringCchPrintfW(pwszCmdLine, cchNeed, c_wszDWMCmdLine32, pwszSysDir, pesreq->dwpidHung, pesreq->dwtidHung, wszEventName); #endif
ErrorTrace(0, "Launching hang report \'%S\'", pwszCmdLine);
TESTBOOL(hr, CreateProcessAsUserW(hTokenUser, NULL, pwszCmdLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT | NORMAL_PRIORITY_CLASS, pvEnv, pwszSysDir, &si, &pi));
if (FAILED(hr)) { DBG_MSG("CreateProcessAsUser failed"); fRet = FALSE; goto done; }
// duplicate the process & thread handles back into the remote process
fRet = DuplicateHandle(GetCurrentProcess(), pi.hProcess, hprocRemote, &esrep.hProcess, 0, FALSE, DUPLICATE_SAME_ACCESS); if (fRet == FALSE) esrep.hProcess = NULL;
// get rid of any errors we might have encountered
SetLastError(0); fRet = TRUE;
esrep.ess = essOk;
done: esrep.dwErr = GetLastError();
// build the reply packet with the handle valid in the context
// of the requesting process
RtlCopyMemory(pBuf, &esrep, sizeof(esrep)); *pcbBuf = sizeof(esrep);
// close our versions of the handles. The requestors references
// are now the main ones
if (hTokenUser != NULL) CloseHandle(hTokenUser); if (pvEnv != NULL) DestroyEnvironmentBlock(pvEnv); if (pi.hProcess != NULL) CloseHandle(pi.hProcess); if (pi.hThread != NULL) CloseHandle(pi.hThread); if (hprocRemote != NULL) CloseHandle(hprocRemote);
return fRet; }
|