From ec042434bea785a7fcbb92e12168eb4335826d6a Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 10 Jun 2024 20:29:53 +0800 Subject: [PATCH] use sihost.exe as fallback for run_as_user if no explorer.exe (#8305) * There is no relevant information, but I found that each session has a unique sihost.exe, and the user name of the process is consistent with the user name of the session, and after using the task manager to kill this process, it will automatically restart. Checking sessionUserName=siHost UserName may be unnecessary, but since there is no evidence, check it anyway. * GetFallbackUserPid is called only when explorer.exe does not exist. * ProcessHacker shows that the tokens of explorer.exe and sihost.exe are the same, I know little about it. Signed-off-by: 21pages --- src/platform/windows.cc | 146 +++++++++++++++++++++++++++++++++++++++- src/platform/windows.rs | 4 +- 2 files changed, 147 insertions(+), 3 deletions(-) diff --git a/src/platform/windows.cc b/src/platform/windows.cc index e3ef0944d..7ed76d2e4 100644 --- a/src/platform/windows.cc +++ b/src/platform/windows.cc @@ -10,6 +10,9 @@ #include #include #include +#include + +extern "C" uint32_t get_session_user_info(PWSTR bufin, uint32_t nin, uint32_t id); void flog(char const *fmt, ...) { @@ -23,6 +26,95 @@ void flog(char const *fmt, ...) fclose(h); } +static BOOL GetProcessUserName(DWORD processID, LPWSTR outUserName, DWORD inUserNameSize) +{ + BOOL ret = FALSE; + HANDLE hProcess = NULL; + HANDLE hToken = NULL; + PTOKEN_USER tokenUser = NULL; + wchar_t *userName = NULL; + wchar_t *domainName = NULL; + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processID); + if (hProcess == NULL) + { + goto cleanup; + } + if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) + { + goto cleanup; + } + DWORD tokenInfoLength = 0; + GetTokenInformation(hToken, TokenUser, NULL, 0, &tokenInfoLength); + if (tokenInfoLength == 0) + { + goto cleanup; + } + tokenUser = (PTOKEN_USER)malloc(tokenInfoLength); + if (tokenUser == NULL) + { + goto cleanup; + } + if (!GetTokenInformation(hToken, TokenUser, tokenUser, tokenInfoLength, &tokenInfoLength)) + { + goto cleanup; + } + DWORD userSize = 0; + DWORD domainSize = 0; + SID_NAME_USE snu; + LookupAccountSidW(NULL, tokenUser->User.Sid, NULL, &userSize, NULL, &domainSize, &snu); + if (userSize == 0 || domainSize == 0) + { + goto cleanup; + } + userName = (wchar_t *)malloc((userSize + 1) * sizeof(wchar_t)); + if (userName == NULL) + { + goto cleanup; + } + domainName = (wchar_t *)malloc((domainSize + 1) * sizeof(wchar_t)); + if (domainName == NULL) + { + goto cleanup; + } + if (!LookupAccountSidW(NULL, tokenUser->User.Sid, userName, &userSize, domainName, &domainSize, &snu)) + { + goto cleanup; + } + userName[userSize] = L'\0'; + domainName[domainSize] = L'\0'; + if (inUserNameSize <= userSize) + { + goto cleanup; + } + wcscpy(outUserName, userName); + + ret = TRUE; +cleanup: + if (userName) + { + free(userName); + } + if (domainName) + { + free(domainName); + } + if (tokenUser != NULL) + { + free(tokenUser); + } + if (hToken != NULL) + { + CloseHandle(hToken); + } + if (hProcess != NULL) + { + CloseHandle(hProcess); + } + + return ret; +} + // ultravnc has rdp support // https://github.com/veyon/ultravnc/blob/master/winvnc/winvnc/service.cpp // https://github.com/TigerVNC/tigervnc/blob/master/win/winvnc/VNCServerService.cxx @@ -54,6 +146,54 @@ DWORD GetLogonPid(DWORD dwSessionId, BOOL as_user) return dwLogonPid; } +static DWORD GetFallbackUserPid(DWORD dwSessionId) +{ + DWORD dwFallbackPid = 0; + const wchar_t* fallbackUserProcs[] = {L"sihost.exe"}; + const int maxUsernameLen = 256; + wchar_t sessionUsername[maxUsernameLen + 1] = {0}; + wchar_t processUsername[maxUsernameLen + 1] = {0}; + + if (get_session_user_info(sessionUsername, maxUsernameLen, dwSessionId) == 0) + { + return 0; + } + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap != INVALID_HANDLE_VALUE) + { + PROCESSENTRY32W procEntry; + procEntry.dwSize = sizeof procEntry; + + if (Process32FirstW(hSnap, &procEntry)) + do + { + for (int i = 0; i < sizeof(fallbackUserProcs) / sizeof(fallbackUserProcs[0]); i++) + { + DWORD dwProcessSessionId = 0; + if (_wcsicmp(procEntry.szExeFile, fallbackUserProcs[i]) == 0 && + ProcessIdToSessionId(procEntry.th32ProcessID, &dwProcessSessionId) && + dwProcessSessionId == dwSessionId) + { + memset(processUsername, 0, sizeof(processUsername)); + if (GetProcessUserName(procEntry.th32ProcessID, processUsername, maxUsernameLen)) { + if (_wcsicmp(sessionUsername, processUsername) == 0) + { + dwFallbackPid = procEntry.th32ProcessID; + break; + } + } + } + } + if (dwFallbackPid != 0) + { + break; + } + } while (Process32NextW(hSnap, &procEntry)); + CloseHandle(hSnap); + } + return dwFallbackPid; +} + // START the app as system extern "C" { @@ -63,6 +203,10 @@ extern "C" { BOOL bResult = FALSE; DWORD Id = GetLogonPid(dwSessionId, as_user); + if (Id == 0) + { + Id = GetFallbackUserPid(dwSessionId); + } if (pDwTokenPid) *pDwTokenPid = Id; if (HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Id)) @@ -436,7 +580,7 @@ extern "C" return nout; } - uint32_t get_session_user_info(PWSTR bufin, uint32_t nin, BOOL rdp, uint32_t id) + uint32_t get_session_user_info(PWSTR bufin, uint32_t nin, uint32_t id) { uint32_t nout = 0; PWSTR buf = NULL; diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 617bfc8eb..c3cd5567f 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -825,12 +825,12 @@ fn get_current_session_username() -> Option { fn get_session_username(session_id: u32) -> String { extern "C" { - fn get_session_user_info(path: *mut u16, n: u32, rdp: bool, session_id: u32) -> u32; + fn get_session_user_info(path: *mut u16, n: u32, session_id: u32) -> u32; } let buff_size = 256; let mut buff: Vec = Vec::with_capacity(buff_size); buff.resize(buff_size, 0); - let n = unsafe { get_session_user_info(buff.as_mut_ptr(), buff_size as _, true, session_id) }; + let n = unsafe { get_session_user_info(buff.as_mut_ptr(), buff_size as _, session_id) }; if n == 0 { return "".to_owned(); }