windows服務啟動一個當前使用者的程序
阿新 • • 發佈:2018-11-08
windows服務啟動一個當前使用者的程序
首先,為什麼會使用windows服務? 大多數Windows服務是以SYSTEM使用者啟動的。SYSTEM使用者是系統中許可權最高的使用者,可以操作登錄檔中Local Machine、系統目錄,不需要UAC就能以管理員許可權啟動一個程序等等。
在windows中,每一個使用者會有一個Session ,Session 0專用於服務和其他不與使用者互動的應用程式。 第一個登入進來,可以進行互動式操作的使用者被連到Session 1上。第二個登入進行的使用者被分配給Session 2,以此類推。 因為Service在Session 0中,而我們登陸系統以後看到的桌面屬於另一個Session ,如果採取在服務程序中啟動子程序來顯示對話方塊,子對話方塊將無法在我們這個session中顯示。
PS: 如果Service 中想顯示MessageBox,需要使用API :WTSSendMessage
言歸正傳,那麼如何在Service為當前使用者登陸的session啟動一個程序呢?
我們可以使用API:CreateProcessAsUser 但是使用這個API需要一系列的準備 比如複製當前啟用session 使用者的Token,建立環境變數等等。
另一個問題來了,如何獲取當前啟用session 使用者的Token? WTSGetActiveConsoleSessionId();在這種情況下通常是失敗的。 一種常見的方法就是 開啟explorer.exe程序,取它的Token ...沒辦法,誰讓我service許可權高呢,咩哈哈哈
以上就是全文了~
首先,為什麼會使用windows服務? 大多數Windows服務是以SYSTEM使用者啟動的。SYSTEM使用者是系統中許可權最高的使用者,可以操作登錄檔中Local Machine、系統目錄,不需要UAC就能以管理員許可權啟動一個程序等等。
在windows中,每一個使用者會有一個Session ,Session 0專用於服務和其他不與使用者互動的應用程式。 第一個登入進來,可以進行互動式操作的使用者被連到Session 1上。第二個登入進行的使用者被分配給Session 2,以此類推。 因為Service在Session 0中,而我們登陸系統以後看到的桌面屬於另一個Session
PS: 如果Service 中想顯示MessageBox,需要使用API :WTSSendMessage
言歸正傳,那麼如何在Service為當前使用者登陸的session啟動一個程序呢?
我們可以使用API:CreateProcessAsUser 但是使用這個API需要一系列的準備 比如複製當前啟用session 使用者的Token,建立環境變數等等。
這樣就可以在service中啟動一個當前使用者 許可權的程序了。BOOL MyCreateProcessAsUser(const HANDLE& hCurrentToken,LPCTSTR strPath,LPTSTR lpCmdLine) { HANDLE hTokenDup = NULL; if (!DuplicateTokenEx(hCurrentToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup)) { #ifdef _DEBUG int iError = GetLastError(); CString strLog; strLog.Format(_T("DuplicateTokenEx fail,Error code:%d"), iError); OutputDebugString(strLog); #endif return FALSE; } DWORD dwSessionID = WTSGetActiveConsoleSessionId(); if (!SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionID, sizeof(DWORD))) { SafeCloseHandle(hTokenDup); return FALSE; } STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(STARTUPINFO)); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); si.cb = sizeof(STARTUPINFO); si.lpDesktop = L"WinSta0\\Default"; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = HIDE_WINDOW; LPVOID pEnv = NULL; DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT; if (!CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE)) { SafeCloseHandle(hTokenDup); return FALSE; } if (!CreateProcessAsUser(hTokenDup, strPath, lpCmdLine, NULL, NULL, FALSE, dwCreationFlags, pEnv, NULL, &si, &pi)) { SafeCloseHandle(hTokenDup); if (pEnv != NULL) DestroyEnvironmentBlock(pEnv); return FALSE; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); CloseHandle(hTokenDup); if (pEnv != NULL) DestroyEnvironmentBlock(pEnv); return TRUE; }
另一個問題來了,如何獲取當前啟用session 使用者的Token? WTSGetActiveConsoleSessionId();在這種情況下通常是失敗的。 一種常見的方法就是 開啟explorer.exe程序,取它的Token ...沒辦法,誰讓我service許可權高呢,咩哈哈哈
BOOL GetCurrentLogonUserToken(HANDLE& hToken)
{
DWORD dwCurSessionId = WTSGetActiveConsoleSessionId();
if (TRUE == WTSQueryUserToken(dwCurSessionId, &hToken))
return TRUE;
DWORD err = GetLastError();
BOOL bRet = FALSE;
HANDLE hProcessSnap = NULL;
PROCESSENTRY32 pe32;
DWORD dwSessionId = -1;
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
pe32.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hProcessSnap, &pe32))
{
do
{
if (!_tcsicmp(pe32.szExeFile, _T("explorer.exe")))
{
::ProcessIdToSessionId(pe32.th32ProcessID, &dwSessionId);
if (dwSessionId != dwCurSessionId)
continue;
//{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
DWORD err = GetLastError();
bRet = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken);
if (bRet == 0)
{
}
CloseHandle(hProcessSnap);
return TRUE;
//}
}
} while (Process32Next(hProcessSnap, &pe32));
bRet = TRUE;
}
else
{
bRet = FALSE;
}
CloseHandle(hProcessSnap);
return bRet;
}
以上就是全文了~