HANDLE MSWindowsWatchdog::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security) { HANDLE sourceToken; BOOL tokenRet = OpenProcessToken( process, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, &sourceToken); if (!tokenRet) { LOG((CLOG_ERR "could not open token, process handle: %d", process)); throw XArch(new XArchEvalWindows()); } LOG((CLOG_DEBUG "got token %i, duplicating", sourceToken)); HANDLE newToken; BOOL duplicateRet = DuplicateTokenEx( sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security, SecurityImpersonation, TokenPrimary, &newToken); if (!duplicateRet) { LOG((CLOG_ERR "could not duplicate token %i", sourceToken)); throw XArch(new XArchEvalWindows()); } LOG((CLOG_DEBUG "duplicated, new token: %i", newToken)); return newToken; }
String WinINetRequest::send() { if (m_used) { throw XArch("class is one time use."); } m_used = true; openSession(); connect(); openRequest(); String headers("Content-Type: text/html"); if (!HttpSendRequest(m_request, headers.c_str(), (DWORD)headers.length(), NULL, NULL)) { throw XArch(new XArchEvalWindows()); } std::stringstream result; CHAR buffer[1025]; DWORD read = 0; while (InternetReadFile(m_request, buffer, sizeof(buffer) - 1, &read) && (read != 0)) { buffer[read] = 0; result << buffer; read = 0; } return result.str(); }
void MSWindowsWatchdog::shutdownExistingProcesses() { // first we need to take a snapshot of the running processes HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { LOG((CLOG_ERR "could not get process snapshot")); throw XArch(new XArchEvalWindows); } PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); // get the first process, and if we can't do that then it's // unlikely we can go any further BOOL gotEntry = Process32First(snapshot, &entry); if (!gotEntry) { LOG((CLOG_ERR "could not get first process entry")); throw XArch(new XArchEvalWindows); } // now just iterate until we can find winlogon.exe pid DWORD pid = 0; while (gotEntry) { // make sure we're not checking the system process if (entry.th32ProcessID != 0) { if (_stricmp(entry.szExeFile, "synergyc.exe") == 0 || _stricmp(entry.szExeFile, "synergys.exe") == 0) { HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID); shutdownProcess(handle, entry.th32ProcessID, 10); } } // now move on to the next entry (if we're not at the end) gotEntry = Process32Next(snapshot, &entry); if (!gotEntry) { DWORD err = GetLastError(); if (err != ERROR_NO_MORE_FILES) { // only worry about error if it's not the end of the snapshot LOG((CLOG_ERR "could not get subsiquent process entry")); throw XArch(new XArchEvalWindows); } } } CloseHandle(snapshot); m_processRunning = false; }
void ArchPluginWindows::load() { String dir = getPluginsDir(); LOG((CLOG_DEBUG "plugins dir: %s", dir.c_str())); String pattern = String(dir).append("\\*.dll"); std::vector<String> plugins; getFilenames(pattern, plugins); std::vector<String>::iterator it; for (it = plugins.begin(); it != plugins.end(); ++it) { LOG((CLOG_DEBUG "loading plugin: %s", (*it).c_str())); String path = String(dir).append("\\").append(*it); HINSTANCE library = LoadLibrary(path.c_str()); if (library == NULL) { LOG((CLOG_ERR "failed to load plugin: %s %d", (*it).c_str(), GetLastError())); throw XArch(new XArchEvalWindows); } void* lib = reinterpret_cast<void*>(library); String filename = synergy::string::removeFileExt(*it); m_pluginTable.insert(std::make_pair(filename, lib)); LOG((CLOG_DEBUG "loaded plugin: %s", (*it).c_str())); } }
CString CArchPluginWindows::getModuleDir() { TCHAR c_modulePath[MAX_PATH]; if (GetModuleFileName(NULL, c_modulePath, MAX_PATH) == 0) { throw XArch(new XArchEvalWindows); } CString modulePath(c_modulePath); size_t lastSlash = modulePath.find_last_of("\\"); if (lastSlash != CString::npos) { return modulePath.substr(0, lastSlash); } throw XArch("could not get module path."); }
CAppUtilWindows::CAppUtilWindows() : m_exitMode(kExitModeNormal) { if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE) { throw XArch(new XArchEvalWindows()); } }
void ArchSystemWindows::setting(const std::string& valueName, const std::string& valueString) const { HKEY key = ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_settingsKeyNames); if (key == NULL) throw XArch(std::string("could not access registry key: ") + valueName); ArchMiscWindows::setValue(key, valueName.c_str(), valueString.c_str()); }
void MSWindowsWatchdog::startProcess() { if (m_command.empty()) { throw XMSWindowsWatchdogError("cannot start process, command is empty"); } m_commandChanged = false; if (m_processRunning) { LOG((CLOG_DEBUG "closing existing process to make way for new one")); shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20); m_processRunning = false; } m_session.updateActiveSession(); SECURITY_ATTRIBUTES sa; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); getActiveDesktop(&sa); ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); HANDLE userToken = getUserToken(&sa); m_autoElevated = false; // patch by Jack Zhou and Henry Tung // set UIAccess to fix Windows 8 GUI interaction // http://synergy-project.org/spit/issues/details/3338/#c70 DWORD uiAccess = 1; SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD)); BOOL createRet = doStartProcess(m_command, userToken, &sa); if (!createRet) { LOG((CLOG_ERR "could not launch")); DWORD exitCode = 0; GetExitCodeProcess(m_processInfo.hProcess, &exitCode); LOG((CLOG_ERR "exit code: %d", exitCode)); throw XArch(new XArchEvalWindows); } else { // wait for program to fail. ARCH->sleep(1); if (!isProcessActive()) { throw XMSWindowsWatchdogError("process immediately stopped"); } m_processRunning = true; m_processFailures = 0; LOG((CLOG_DEBUG "started process, session=%i, command=%s", m_session.getActiveSessionId(), m_command.c_str())); } }
HANDLE MSWindowsSession::getUserToken(LPSECURITY_ATTRIBUTES security) { HANDLE sourceToken; if (!WTSQueryUserToken(m_activeSessionId, &sourceToken)) { LOG((CLOG_ERR "could not get token from session %d", m_activeSessionId)); throw XArch(new XArchEvalWindows); } HANDLE newToken; if (!DuplicateTokenEx( sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security, SecurityImpersonation, TokenPrimary, &newToken)) { LOG((CLOG_ERR "could not duplicate token")); throw XArch(new XArchEvalWindows); } LOG((CLOG_DEBUG "duplicated, new token: %i", newToken)); return newToken; }
void CArchPluginWindows::load(const CString& dllFilename) { LOG((CLOG_DEBUG "loading plugin: %s", dllFilename.c_str())); CString path = CString(getPluginsDir()).append("\\").append(dllFilename); HINSTANCE library = LoadLibrary(path.c_str()); if (library == NULL) throw XArch(new XArchEvalWindows); initFunc initPlugin = (initFunc)GetProcAddress(library, "init"); initPlugin(&sendEvent, &log); }
void WinINetRequest::connect() { m_connect = InternetConnect( m_session, m_url.m_host.c_str(), m_url.m_port, NULL, NULL, INTERNET_SERVICE_HTTP, NULL, NULL); if (m_connect == NULL) { throw XArch(new XArchEvalWindows()); } }
void WinINetRequest::openRequest() { m_request = HttpOpenRequest( m_connect, "GET", m_url.m_path.c_str(), HTTP_VERSION, NULL, NULL, m_url.m_flags, NULL); if (m_request == NULL) { throw XArch(new XArchEvalWindows()); } }
BOOL MSWindowsSession::nextProcessEntry(HANDLE snapshot, LPPROCESSENTRY32 entry) { BOOL gotEntry = Process32Next(snapshot, entry); if (!gotEntry) { DWORD err = GetLastError(); if (err != ERROR_NO_MORE_FILES) { // only worry about error if it's not the end of the snapshot LOG((CLOG_ERR "could not get next process entry")); throw XArch(new XArchEvalWindows()); } } return gotEntry; }
void WinINetRequest::openSession() { std::stringstream userAgent; userAgent << "Synergy "; userAgent << kVersion; m_session = InternetOpen( userAgent.str().c_str(), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL); if (m_session == NULL) { throw XArch(new XArchEvalWindows()); } }
String ArchInternetWindows::urlEncode(const String& url) { TCHAR buffer[1024]; DWORD bufferSize = sizeof(buffer); if (UrlEscape(url.c_str(), buffer, &bufferSize, URL_ESCAPE_UNSAFE) != S_OK) { throw XArch(new XArchEvalWindows()); } String result(buffer); // the win32 url encoding funcitons are pretty useless (to us) and only // escape "unsafe" chars, but not + or =, so we need to replace these // manually (and probably many other chars). synergy::string::findReplaceAll(result, "+", "%2B"); synergy::string::findReplaceAll(result, "=", "%3D"); return result; }
BOOL MSWindowsWatchdog::doStartProcess(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa) { // clear, as we're reusing process info struct ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION)); STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.lpDesktop = "winsta0\\Default"; // TODO: maybe this should be \winlogon if we have logonui.exe? si.hStdError = m_stdOutWrite; si.hStdOutput = m_stdOutWrite; si.dwFlags |= STARTF_USESTDHANDLES; LPVOID environment; BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE); if (!blockRet) { LOG((CLOG_ERR "could not create environment block")); throw XArch(new XArchEvalWindows); } DWORD creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; // re-launch in current active user session LOG((CLOG_INFO "starting new process")); BOOL createRet = CreateProcessAsUser( userToken, NULL, LPSTR(command.c_str()), sa, NULL, TRUE, creationFlags, environment, NULL, &si, &m_processInfo); DestroyEnvironmentBlock(environment); CloseHandle(userToken); return createRet; }
COSXScreen::COSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCursor) : CPlatformScreen(events), m_events(events), MouseButtonEventMap(NumButtonIDs), m_isPrimary(isPrimary), m_isOnScreen(m_isPrimary), m_cursorPosValid(false), m_cursorHidden(false), m_dragNumButtonsDown(0), m_dragTimer(NULL), m_keyState(NULL), m_sequenceNumber(0), m_screensaver(NULL), m_screensaverNotify(false), m_ownClipboard(false), m_clipboardTimer(NULL), m_hiddenWindow(NULL), m_userInputWindow(NULL), m_switchEventHandlerRef(0), m_pmMutex(new CMutex), m_pmWatchThread(NULL), m_pmThreadReady(new CCondVar<bool>(m_pmMutex, false)), m_activeModifierHotKey(0), m_activeModifierHotKeyMask(0), m_lastSingleClick(0), m_lastDoubleClick(0), m_lastSingleClickXCursor(0), m_lastSingleClickYCursor(0), m_autoShowHideCursor(autoShowHideCursor), m_eventTapRLSR(nullptr), m_eventTapPort(nullptr), m_pmRootPort(0) { try { m_displayID = CGMainDisplayID(); updateScreenShape(m_displayID, 0); m_screensaver = new COSXScreenSaver(m_events, getEventTarget()); m_keyState = new COSXKeyState(m_events); // TODO: http://stackoverflow.com/questions/2950124/enable-access-for-assistive-device-programmatically if (m_isPrimary && !AXAPIEnabled()) throw XArch("system setting not enabled: \"Enable access for assistive devices\""); // install display manager notification handler #if defined(MAC_OS_X_VERSION_10_5) CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallback, this); #else m_displayManagerNotificationUPP = NewDMExtendedNotificationUPP(displayManagerCallback); OSStatus err = GetCurrentProcess(&m_PSN); err = DMRegisterExtendedNotifyProc(m_displayManagerNotificationUPP, this, 0, &m_PSN); #endif // install fast user switching event handler EventTypeSpec switchEventTypes[2]; switchEventTypes[0].eventClass = kEventClassSystem; switchEventTypes[0].eventKind = kEventSystemUserSessionDeactivated; switchEventTypes[1].eventClass = kEventClassSystem; switchEventTypes[1].eventKind = kEventSystemUserSessionActivated; EventHandlerUPP switchEventHandler = NewEventHandlerUPP(userSwitchCallback); InstallApplicationEventHandler(switchEventHandler, 2, switchEventTypes, this, &m_switchEventHandlerRef); DisposeEventHandlerUPP(switchEventHandler); constructMouseButtonEventMap(); // watch for requests to sleep m_events->adoptHandler(m_events->forCOSXScreen().confirmSleep(), getEventTarget(), new TMethodEventJob<COSXScreen>(this, &COSXScreen::handleConfirmSleep)); // create thread for monitoring system power state. *m_pmThreadReady = false; LOG((CLOG_DEBUG "starting watchSystemPowerThread")); m_pmWatchThread = new CThread(new TMethodJob<COSXScreen> (this, &COSXScreen::watchSystemPowerThread)); } catch (...) { m_events->removeHandler(m_events->forCOSXScreen().confirmSleep(), getEventTarget()); if (m_switchEventHandlerRef != 0) { RemoveEventHandler(m_switchEventHandlerRef); } #if defined(MAC_OS_X_VERSION_10_5) CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this); #else if (m_displayManagerNotificationUPP != NULL) { DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, NULL, &m_PSN, 0); } if (m_hiddenWindow) { ReleaseWindow(m_hiddenWindow); m_hiddenWindow = NULL; } if (m_userInputWindow) { ReleaseWindow(m_userInputWindow); m_userInputWindow = NULL; } #endif delete m_keyState; delete m_screensaver; throw; } // install event handlers m_events->adoptHandler(CEvent::kSystem, m_events->getSystemTarget(), new TMethodEventJob<COSXScreen>(this, &COSXScreen::handleSystemEvent)); // install the platform event queue m_events->adoptBuffer(new COSXEventQueueBuffer(m_events)); }
void CMSWindowsRelauncher::mainLoop(void*) { SendSas sendSasFunc = NULL; HINSTANCE sasLib = LoadLibrary("sas.dll"); if (sasLib) { LOG((CLOG_DEBUG "found sas.dll")); sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS"); } DWORD sessionId = -1; bool launched = false; SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) { throw XArch(new XArchEvalWindows()); } PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); while (m_running) { HANDLE sendSasEvent = 0; if (sasLib && sendSasFunc) { // can't we just create one event? seems weird creating a new // event every second... sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS"); } DWORD newSessionId = getSessionId(); // only enter here when id changes, and the session isn't -1, which // may mean that there is no active session. if (((newSessionId != sessionId) && (newSessionId != -1)) || m_commandChanged) { m_commandChanged = false; if (launched) { LOG((CLOG_DEBUG "closing existing process to make way for new one")); shutdownProcess(pi, 10); launched = false; } // ok, this is now the active session (forget the old one if any) sessionId = newSessionId; SECURITY_ATTRIBUTES sa; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); // get the token for the user in active session, which is the // one receiving input from mouse and keyboard. HANDLE userToken = getCurrentUserToken(sessionId, &sa); if (userToken != 0) { LOG((CLOG_DEBUG "got user token to launch new process")); std::string cmd = command(); if (cmd == "") { LOG((CLOG_WARN "nothing to launch, no command specified.")); continue; } // in case reusing process info struct ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.lpDesktop = "winsta0\\default"; si.hStdError = m_stdOutWrite; si.hStdOutput = m_stdOutWrite; si.dwFlags |= STARTF_USESTDHANDLES; LPVOID environment; BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE); if (!blockRet) { LOG((CLOG_ERR "could not create environment block (error: %i)", GetLastError())); continue; } else { DWORD creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; // re-launch in current active user session BOOL createRet = CreateProcessAsUser( userToken, NULL, LPSTR(cmd.c_str()), &sa, NULL, TRUE, creationFlags, environment, NULL, &si, &pi); DestroyEnvironmentBlock(environment); CloseHandle(userToken); if (!createRet) { LOG((CLOG_ERR "could not launch (error: %i)", GetLastError())); continue; } else { LOG((CLOG_DEBUG "launched in session %i (cmd: %s)", sessionId, cmd.c_str())); launched = true; } } } } if (sendSasEvent) { // use SendSAS event to wait for next session. if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0 && sendSasFunc) { LOG((CLOG_DEBUG "calling SendSAS")); sendSasFunc(FALSE); } CloseHandle(sendSasEvent); } else { // check for session change every second. ARCH->sleep(1); } } if (launched) { LOG((CLOG_DEBUG "terminated running process on exit")); shutdownProcess(pi, 10); } }
void MSWindowsWatchdog::mainLoop(void*) { shutdownExistingProcesses(); SendSas sendSasFunc = NULL; HINSTANCE sasLib = LoadLibrary("sas.dll"); if (sasLib) { LOG((CLOG_DEBUG "found sas.dll")); sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS"); } SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) { throw XArch(new XArchEvalWindows()); } ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION)); while (m_monitoring) { try { if (m_processRunning && getCommand().empty()) { LOG((CLOG_INFO "process started but command is empty, shutting down")); shutdownExistingProcesses(); m_processRunning = false; continue; } if (m_processFailures != 0) { // increasing backoff period, maximum of 10 seconds. int timeout = (m_processFailures * 2) < 10 ? (m_processFailures * 2) : 10; LOG((CLOG_INFO "backing off, wait=%ds, failures=%d", timeout, m_processFailures)); ARCH->sleep(timeout); } if (!getCommand().empty() && ((m_processFailures != 0) || m_session.hasChanged() || m_commandChanged)) { startProcess(); } if (m_processRunning && !isProcessActive()) { m_processFailures++; m_processRunning = false; LOG((CLOG_WARN "detected application not running, pid=%d", m_processInfo.dwProcessId)); } if (sendSasFunc != NULL) { HANDLE sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS"); if (sendSasEvent != NULL) { // use SendSAS event to wait for next session (timeout 1 second). if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0) { LOG((CLOG_DEBUG "calling SendSAS")); sendSasFunc(FALSE); } CloseHandle(sendSasEvent); continue; } } // if the sas event failed, wait by sleeping. ARCH->sleep(1); } catch (std::exception& e) { LOG((CLOG_ERR "failed to launch, error: %s", e.what())); m_processFailures++; m_processRunning = false; continue; } catch (...) { LOG((CLOG_ERR "failed to launch, unknown error.")); m_processFailures++; m_processRunning = false; continue; } } if (m_processRunning) { LOG((CLOG_DEBUG "terminated running process on exit")); shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20); } LOG((CLOG_DEBUG "watchdog main thread finished")); }
void CMSWindowsRelauncher::mainLoop(void*) { shutdownExistingProcesses(); SendSas sendSasFunc = NULL; HINSTANCE sasLib = LoadLibrary("sas.dll"); if (sasLib) { LOG((CLOG_DEBUG "found sas.dll")); sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS"); } DWORD sessionId = -1; bool launched = false; SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) { throw XArch(new XArchEvalWindows()); } PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); int failures = 0; while (m_running) { HANDLE sendSasEvent = 0; if (sasLib && sendSasFunc) { // can't we just create one event? seems weird creating a new // event every second... sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS"); } DWORD newSessionId = getSessionId(); bool running = false; if (launched) { DWORD exitCode; GetExitCodeProcess(pi.hProcess, &exitCode); running = (exitCode == STILL_ACTIVE); if (!running) { failures++; LOG((CLOG_INFO "detected application not running, pid=%d, failures=%d", pi.dwProcessId, failures)); // increasing backoff period, maximum of 10 seconds. int timeout = (failures * 2) < 10 ? (failures * 2) : 10; LOG((CLOG_DEBUG "waiting, backoff period is %d seconds", timeout)); ARCH->sleep(timeout); // double check, in case process started after we waited. GetExitCodeProcess(pi.hProcess, &exitCode); running = (exitCode == STILL_ACTIVE); } else { // reset failures when running. failures = 0; } } // only enter here when id changes, and the session isn't -1, which // may mean that there is no active session. bool sessionChanged = ((newSessionId != sessionId) && (newSessionId != -1)); // relaunch if it was running but has stopped unexpectedly. bool stoppedRunning = (launched && !running); if (stoppedRunning || sessionChanged || m_commandChanged) { m_commandChanged = false; if (launched) { LOG((CLOG_DEBUG "closing existing process to make way for new one")); shutdownProcess(pi.hProcess, pi.dwProcessId, 20); launched = false; } // ok, this is now the active session (forget the old one if any) sessionId = newSessionId; SECURITY_ATTRIBUTES sa; ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); // get the token for the user in active session, which is the // one receiving input from mouse and keyboard. HANDLE userToken = getCurrentUserToken(sessionId, &sa); if (userToken != 0) { LOG((CLOG_DEBUG "got user token to launch new process")); std::string cmd = command(); if (cmd == "") { // this appears on first launch when the user hasn't configured // anything yet, so don't show it as a warning, only show it as // debug to devs to let them know why nothing happened. LOG((CLOG_DEBUG "nothing to launch, no command specified.")); continue; } // in case reusing process info struct ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.lpDesktop = "winsta0\\default"; si.hStdError = m_stdOutWrite; si.hStdOutput = m_stdOutWrite; si.dwFlags |= STARTF_USESTDHANDLES; LPVOID environment; BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE); if (!blockRet) { LOG((CLOG_ERR "could not create environment block (error: %i)", GetLastError())); continue; } else { DWORD creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; // re-launch in current active user session LOG((CLOG_INFO "starting new process")); BOOL createRet = CreateProcessAsUser( userToken, NULL, LPSTR(cmd.c_str()), &sa, NULL, TRUE, creationFlags, environment, NULL, &si, &pi); DestroyEnvironmentBlock(environment); CloseHandle(userToken); if (!createRet) { LOG((CLOG_ERR "could not launch (error: %i)", GetLastError())); continue; } else { LOG((CLOG_DEBUG "launched in session %i (cmd: %s)", sessionId, cmd.c_str())); launched = true; } } } } if (sendSasEvent) { // use SendSAS event to wait for next session. if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0 && sendSasFunc) { LOG((CLOG_DEBUG "calling SendSAS")); sendSasFunc(FALSE); } CloseHandle(sendSasEvent); } else { // check for session change every second. ARCH->sleep(1); } } if (launched) { LOG((CLOG_DEBUG "terminated running process on exit")); shutdownProcess(pi.hProcess, pi.dwProcessId, 20); } LOG((CLOG_DEBUG "relauncher main thread finished")); }
bool MSWindowsSession::isProcessInSession(const char* name, PHANDLE process = NULL) { // first we need to take a snapshot of the running processes HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { LOG((CLOG_ERR "could not get process snapshot")); throw XArch(new XArchEvalWindows()); } PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); // get the first process, and if we can't do that then it's // unlikely we can go any further BOOL gotEntry = Process32First(snapshot, &entry); if (!gotEntry) { LOG((CLOG_ERR "could not get first process entry")); throw XArch(new XArchEvalWindows()); } // used to record process names for debug info std::list<std::string> nameList; // now just iterate until we can find winlogon.exe pid DWORD pid = 0; while(gotEntry) { // make sure we're not checking the system process if (entry.th32ProcessID != 0) { DWORD processSessionId; BOOL pidToSidRet = ProcessIdToSessionId( entry.th32ProcessID, &processSessionId); if (!pidToSidRet) { // if we can not acquire session associated with a specified process, // simply ignore it LOG((CLOG_ERR "could not get session id for process id %i", entry.th32ProcessID)); gotEntry = nextProcessEntry(snapshot, &entry); continue; } else { // only pay attention to processes in the active session if (processSessionId == m_activeSessionId) { // store the names so we can record them for debug nameList.push_back(entry.szExeFile); if (_stricmp(entry.szExeFile, name) == 0) { pid = entry.th32ProcessID; } } } } // now move on to the next entry (if we're not at the end) gotEntry = nextProcessEntry(snapshot, &entry); } std::string nameListJoin; for(std::list<std::string>::iterator it = nameList.begin(); it != nameList.end(); it++) { nameListJoin.append(*it); nameListJoin.append(", "); } LOG((CLOG_DEBUG "processes in session %d: %s", m_activeSessionId, nameListJoin.c_str())); CloseHandle(snapshot); if (pid) { if (process != NULL) { // now get the process, which we'll use to get the process token. LOG((CLOG_DEBUG "found %s in session %i", name, m_activeSessionId)); *process = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid); } return true; } else { LOG((CLOG_DEBUG "did not find %s in session %i", name, m_activeSessionId)); return false; } }