bool ConsoleHandler::StartShellProcess ( const wstring& strCustomShell, const wstring& strInitialDir, const wstring& strUser, const wstring& strPassword, const wstring& strInitialCmd, const wstring& strConsoleTitle, DWORD dwStartupRows, DWORD dwStartupColumns, bool bDebugFlag ) { wstring strUsername(strUser); wstring strDomain; // shared_ptr<void> userProfileKey; // shared_ptr<void> userEnvironment; // shared_ptr<void> userToken; if (strUsername.length() > 0) { size_t pos = strUsername.find(L'\\'); if (pos != wstring::npos) { strDomain = strUsername.substr(0, pos); strUsername = strUsername.substr(pos+1); } /* // logon user HANDLE hUserToken = NULL; ::LogonUser( strUsername.c_str(), strDomain.length() > 0 ? strDomain.c_str() : NULL, strPassword.c_str(), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hUserToken); userToken.reset(hUserToken, ::CloseHandle); */ /* ::ImpersonateLoggedOnUser(userToken.get()); wchar_t szComspec[MAX_PATH]; ::GetEnvironmentVariable(L"COMSPEC", szComspec, MAX_PATH); */ /* // load user's profile // seems to be necessary on WinXP for environment strings' expainsion to work properly PROFILEINFO userProfile; ::ZeroMemory(&userProfile, sizeof(PROFILEINFO)); userProfile.dwSize = sizeof(PROFILEINFO); userProfile.lpUserName = const_cast<wchar_t*>(strUser.c_str()); ::LoadUserProfile(userToken.get(), &userProfile); userProfileKey.reset(userProfile.hProfile, bind<BOOL>(::UnloadUserProfile, userToken.get(), _1)); */ /* // load user's environment void* pEnvironment = NULL; ::CreateEnvironmentBlock(&pEnvironment, userToken.get(), FALSE); userEnvironment.reset(pEnvironment, ::DestroyEnvironmentBlock); ::RevertToSelf(); */ } wstring strShellCmdLine(strCustomShell); if (strShellCmdLine.length() == 0) { wchar_t szComspec[MAX_PATH]; ::ZeroMemory(szComspec, MAX_PATH*sizeof(wchar_t)); if (strUsername.length() > 0) { /* // resolve comspec when running as another user wchar_t* pszComspec = reinterpret_cast<wchar_t*>(userEnvironment.get()); while ((pszComspec[0] != L'\x00') && (_wcsnicmp(pszComspec, L"comspec", 7) != 0)) pszComspec += wcslen(pszComspec)+1; if (pszComspec[0] != L'\x00') { strShellCmdLine = (pszComspec + 8); } */ if (strShellCmdLine.length() == 0) strShellCmdLine = L"cmd.exe"; } else { if (::GetEnvironmentVariable(L"COMSPEC", szComspec, MAX_PATH) > 0) { strShellCmdLine = szComspec; } if (strShellCmdLine.length() == 0) strShellCmdLine = L"cmd.exe"; } } if (strInitialCmd.length() > 0) { strShellCmdLine += L" "; strShellCmdLine += strInitialCmd; } wstring strStartupTitle(strConsoleTitle); if (strStartupTitle.length() == 0) { strStartupTitle = L"Console2 command window"; // strStartupTitle = str(wformat(L"Console2 command window 0x%08X") % this); } // wstring strStartupDir((strUsername.length() > 0) ? Helpers::ExpandEnvironmentStringsForUser(userToken, strInitialDir) : Helpers::ExpandEnvironmentStrings(strInitialDir)); wstring strStartupDir((strUsername.length() > 0) ? strInitialDir : Helpers::ExpandEnvironmentStrings(strInitialDir)); if (strStartupDir.length() > 0) { if ((*(strStartupDir.end() - 1) == L'\"') && (*strStartupDir.begin() != L'\"')) { // startup dir name ends with ", but doesn't start with ", the user passed // something like "C:\" as the parameter, it got parsed to C:", remove the trailing " // // This is a common mistake, thus the check... strStartupDir = strStartupDir.substr(0, strStartupDir.length()-1); } // startup dir doesn't end with \, add it if (*(strStartupDir.end() - 1) != L'\\') strStartupDir += L'\\'; // check if startup directory exists DWORD dwDirAttributes = ::GetFileAttributes(strStartupDir.c_str()); if ((dwDirAttributes == INVALID_FILE_ATTRIBUTES) || (dwDirAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // no directory, use Console.exe directory strStartupDir = Helpers::GetModulePath(NULL); } } // setup the startup info struct STARTUPINFO si; ::ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.lpTitle = const_cast<wchar_t*>(strStartupTitle.c_str()); if (g_settingsHandler->GetConsoleSettings().bStartHidden) { // Starting Windows console window hidden causes problems with // some GUI apps started from Console that use SW_SHOWDEFAULT to // initially show their main window (i.e. the window inherits our // SW_HIDE flag and remains invisible :-) si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; } else { // To avoid invisible GUI windows, default settings will create // a Windows console window far offscreen and hide the window // after it has been created. // // This approach can flash console window's taskbar button and // taskbar button can sometimes remain visible, but nothing is perfect :) si.dwFlags = STARTF_USEPOSITION; si.dwX = 0x7FFF; si.dwY = 0x7FFF; } PROCESS_INFORMATION pi; // we must use CREATE_UNICODE_ENVIRONMENT here, since s_environmentBlock contains Unicode strings DWORD dwStartupFlags = CREATE_NEW_CONSOLE|CREATE_SUSPENDED|CREATE_UNICODE_ENVIRONMENT; // TODO: not supported yet //if (bDebugFlag) dwStartupFlags |= DEBUG_PROCESS; if (strUsername.length() > 0) { if (!::CreateProcessWithLogonW( strUsername.c_str(), strDomain.length() > 0 ? strDomain.c_str() : NULL, strPassword.c_str(), LOGON_WITH_PROFILE, NULL, // const_cast<wchar_t*>(Helpers::ExpandEnvironmentStringsForUser(userToken, strShellCmdLine).c_str()), const_cast<wchar_t*>(strShellCmdLine.c_str()), dwStartupFlags, // userEnvironment.get(), NULL, (strStartupDir.length() > 0) ? const_cast<wchar_t*>(strStartupDir.c_str()) : NULL, &si, &pi)) { throw ConsoleException(str(wformat(Helpers::LoadStringW(IDS_ERR_CANT_START_SHELL_AS_USER)) % strShellCmdLine % strUser)); } } else { if (!::CreateProcess( NULL, const_cast<wchar_t*>(Helpers::ExpandEnvironmentStrings(strShellCmdLine).c_str()), NULL, NULL, FALSE, dwStartupFlags, s_environmentBlock.get(), (strStartupDir.length() > 0) ? const_cast<wchar_t*>(strStartupDir.c_str()) : NULL, &si, &pi)) { throw ConsoleException(str(wformat(Helpers::LoadString(IDS_ERR_CANT_START_SHELL)) % strShellCmdLine)); } } // create shared memory objects CreateSharedObjects(pi.dwProcessId, strUser); CreateWatchdog(); // write startup params m_consoleParams->dwConsoleMainThreadId = pi.dwThreadId; m_consoleParams->dwParentProcessId = ::GetCurrentProcessId(); m_consoleParams->dwNotificationTimeout = g_settingsHandler->GetConsoleSettings().dwChangeRefreshInterval; m_consoleParams->dwRefreshInterval = g_settingsHandler->GetConsoleSettings().dwRefreshInterval; m_consoleParams->dwRows = dwStartupRows; m_consoleParams->dwColumns = dwStartupColumns; m_consoleParams->dwBufferRows = g_settingsHandler->GetConsoleSettings().dwBufferRows; m_consoleParams->dwBufferColumns = g_settingsHandler->GetConsoleSettings().dwBufferColumns; m_hConsoleProcess = shared_ptr<void>(pi.hProcess, ::CloseHandle); // inject our hook DLL into console process if (!InjectHookDLL(pi)) return false; // resume the console process ::ResumeThread(pi.hThread); ::CloseHandle(pi.hThread); // wait for hook DLL to set console handle if (::WaitForSingleObject(m_consoleParams.GetReqEvent(), 10000) == WAIT_TIMEOUT) return false; ::ShowWindow(m_consoleParams->hwndConsoleWindow, SW_HIDE); return true; }
bool ConsoleHandler::StartShellProcess(const wstring& strCustomShell, const wstring& strInitialDir, const wstring& strInitialCmd, const wstring& strConsoleTitle, DWORD dwStartupRows, DWORD dwStartupColumns, bool bDebugFlag) { wstring strShellCmdLine(strCustomShell); if (strShellCmdLine.length() == 0) { wchar_t szComspec[MAX_PATH]; ::ZeroMemory(szComspec, MAX_PATH*sizeof(wchar_t)); if (::GetEnvironmentVariable(L"COMSPEC", szComspec, MAX_PATH) > 0) { strShellCmdLine = szComspec; } else { strShellCmdLine = L"cmd.exe"; } } if (strInitialCmd.length() > 0) { strShellCmdLine += L" "; strShellCmdLine += strInitialCmd; } wstring strStartupTitle(strConsoleTitle); if (strStartupTitle.length() == 0) { strStartupTitle = L"Console2 command window"; // strStartupTitle = str(wformat(L"Console2 command window 0x%08X") % this); } wstring strStartupDir(strInitialDir); if (strStartupDir.length()) { // check if startup directory exists DWORD dwDirAttributes = ::GetFileAttributes(strStartupDir.c_str()); if ((dwDirAttributes == INVALID_FILE_ATTRIBUTES) || (dwDirAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // no directory, use Console.exe directory strStartupDir = Helpers::GetModulePath(NULL); } } // setup the startup info struct STARTUPINFO si; ::ZeroMemory(&si, sizeof(STARTUPINFO)); si.dwFlags = STARTF_USESHOWWINDOW; si.cb = sizeof(STARTUPINFO); si.wShowWindow = SW_HIDE; si.lpTitle = const_cast<wchar_t*>(strStartupTitle.c_str()); PROCESS_INFORMATION pi; DWORD dwStartupFlags = CREATE_NEW_CONSOLE|CREATE_SUSPENDED; // TODO: not supported yet //if (bDebugFlag) dwStartupFlags |= DEBUG_PROCESS; if (!::CreateProcess( NULL, const_cast<wchar_t*>(Helpers::ExpandEnvironmentStrings(strShellCmdLine).c_str()), NULL, NULL, FALSE, dwStartupFlags, NULL, (strStartupDir.length() > 0) ? const_cast<wchar_t*>(Helpers::ExpandEnvironmentStrings(strStartupDir).c_str()) : NULL, &si, &pi)) { return false; } // create shared memory objects CreateSharedObjects(pi.dwProcessId); // write startup params m_consoleParams->dwConsoleMainThreadId = pi.dwThreadId; m_consoleParams->dwParentProcessId = ::GetCurrentProcessId(); m_consoleParams->dwNotificationTimeout = g_settingsHandler->GetConsoleSettings().dwChangeRefreshInterval; m_consoleParams->dwRefreshInterval = g_settingsHandler->GetConsoleSettings().dwRefreshInterval; m_consoleParams->dwRows = dwStartupRows; m_consoleParams->dwColumns = dwStartupColumns; m_consoleParams->dwBufferRows = g_settingsHandler->GetConsoleSettings().dwBufferRows; m_consoleParams->dwBufferColumns = g_settingsHandler->GetConsoleSettings().dwBufferColumns; m_hConsoleProcess = shared_ptr<void>(pi.hProcess, ::CloseHandle); // inject our hook DLL into console process if (!InjectHookDLL()) return false; // resume the console process ::ResumeThread(pi.hThread); ::CloseHandle(pi.hThread); // wait for hook DLL to set console handle if (::WaitForSingleObject(m_consoleParams.GetReqEvent(), 10000) == WAIT_TIMEOUT) return false; return true; }
void ConsoleHandler::StartShellProcess ( const wstring& strTitle, const wstring& strShell, const wstring& strInitialDir, const UserCredentials& userCredentials, const wstring& strInitialCmd, const wstring& strConsoleTitle, DWORD dwStartupRows, DWORD dwStartupColumns ) { PROCESS_INFORMATION pi = {0, 0, 0, 0}; bool runAsAdministrator = userCredentials.runAsAdministrator; if (runAsAdministrator) { try { if (Helpers::CheckOSVersion(6, 0)) { if( Helpers::IsElevated() ) { // process already running in elevated mode or UAC disabled runAsAdministrator = false; } } else { // UAC doesn't exist in current OS runAsAdministrator = false; } } catch(std::exception& err) { throw ConsoleException(boost::str(boost::wformat(Helpers::LoadString(IDS_ERR_CANT_GET_ELEVATION_TYPE)) % err.what())); } } SharedMemory<DWORD> pid; if (runAsAdministrator) { std::wstring strSyncName = (SharedMemNames::formatAdmin % ::GetCurrentProcessId()).str(); pid.Create(strSyncName, 1, syncObjBoth, L""); RunAsAdministrator( strSyncName, strTitle, strInitialDir, strInitialCmd, pi ); // wait for PID of shell launched in admin ConsoleZ if (::WaitForSingleObject(pid.GetReqEvent(), 10000) == WAIT_TIMEOUT) throw ConsoleException(boost::str(boost::wformat(Helpers::LoadString(IDS_ERR_DLL_INJECTION_FAILED)) % L"timeout")); pi.dwProcessId = *pid.Get(); pi.hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pi.dwProcessId); if( pi.hProcess == NULL ) { Win32Exception err(::GetLastError()); throw ConsoleException(boost::str(boost::wformat(Helpers::LoadString(IDS_ERR_DLL_INJECTION_FAILED)) % err.what())); } } else { CreateShellProcess( strShell, strInitialDir, userCredentials, strInitialCmd, strConsoleTitle, pi ); } // create shared memory objects try { CreateSharedObjects(pi.dwProcessId, userCredentials.netOnly? L"" : userCredentials.strAccountName); CreateWatchdog(); } catch(Win32Exception& err) { throw ConsoleException(boost::str(boost::wformat(Helpers::LoadString(IDS_ERR_CREATE_SHARED_OBJECTS_FAILED)) % err.what())); } // write startup params m_consoleParams->dwParentProcessId = ::GetCurrentProcessId(); m_consoleParams->dwNotificationTimeout = g_settingsHandler->GetConsoleSettings().dwChangeRefreshInterval; m_consoleParams->dwRefreshInterval = g_settingsHandler->GetConsoleSettings().dwRefreshInterval; m_consoleParams->dwRows = dwStartupRows; m_consoleParams->dwColumns = dwStartupColumns; m_consoleParams->dwBufferRows = g_settingsHandler->GetConsoleSettings().dwBufferRows; m_consoleParams->dwBufferColumns = g_settingsHandler->GetConsoleSettings().dwBufferColumns; m_hConsoleProcess = std::shared_ptr<void>(pi.hProcess, ::CloseHandle); m_dwConsolePid = pi.dwProcessId; if (runAsAdministrator) { ::SetEvent(pid.GetRespEvent()); } else { // inject our hook DLL into console process if (!InjectHookDLL(pi)) throw ConsoleException(boost::str(boost::wformat(Helpers::LoadString(IDS_ERR_DLL_INJECTION_FAILED)) % L"?")); // resume the console process ::ResumeThread(pi.hThread); ::CloseHandle(pi.hThread); } try { m_consoleMsgPipe.WaitConnect(); } catch(Win32Exception& err) { throw ConsoleException(boost::str(boost::wformat(Helpers::LoadString(IDS_ERR_DLL_INJECTION_FAILED)) % err.what())); } // wait for hook DLL to set console handle if (::WaitForSingleObject(m_consoleParams.GetReqEvent(), 10000) == WAIT_TIMEOUT) throw ConsoleException(boost::str(boost::wformat(Helpers::LoadString(IDS_ERR_DLL_INJECTION_FAILED)) % L"timeout")); ShowWindow(SW_HIDE); }