Windows サービスプログラムからユーザプログラムを実行する方法 - その2
Windows サービスプログラムからユーザプログラムを実行する方法 の続きです。
前回は winlogon.exe のトークンを元にプロセスを起動させましたが、今回はログオンユーザのリンクトークン(自身の制限されたトークンに紐付く管理者トークン)を元に、ログオンユーザの管理者権限でプロセスを起動させる方法をご紹介します。
詳しくは以下のリンク先を参照してください。
- How to create an process with administrator privilege from service
- UAC
- Vista 以降におけるトークンの変更 〜 リンクト・トークン (フィルタード・トークン)
早速以下に私の実装例を示します.
process.hpp
#ifndef PROCESS_HPP_20121029_ #define PROCESS_HPP_20121029_ #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once #endif #include <string> #include <windows.h> namespace process { BOOL createProcessAsActiveUser(const std::wstring& app, const std::wstring& param, PROCESS_INFORMATION* pi = nullptr); } #endif
process.cpp
#include <windows.h> #include <userenv.h> #include <wtsapi32.h> #include <atlbase.h> #include "process.hpp" #pragma comment(lib, "userenv.lib") #pragma comment(lib, "wtsapi32.lib") namespace { BOOL createProcessAsUser(const std::wstring& app, const std::wstring& param, HANDLE token, DWORD creationFlags, LPVOID env, PROCESS_INFORMATION* pi = nullptr) { wchar_t arg[32768] = L""; STARTUPINFO si = { sizeof(STARTUPINFO), nullptr, L"winsta0\\default" }; wcscpy_s(arg, (param.empty() ? app.c_str() : (app + L" " + param).c_str())); if (pi) { return CreateProcessAsUser(token, nullptr, arg, nullptr, nullptr, FALSE, creationFlags, env, nullptr, &si, pi); } else { PROCESS_INFORMATION pi = {}; const BOOL retval = CreateProcessAsUser(token, nullptr, arg, nullptr, nullptr, FALSE, creationFlags, env, nullptr, &si, &pi); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return retval; } } } BOOL process::createProcessAsActiveUser(const std::wstring& app, const std::wstring& param, PROCESS_INFORMATION* pi) { const DWORD sessionId = WTSGetActiveConsoleSessionId(); CHandle userToken; BOOL retval = FALSE; if (WTSQueryUserToken(sessionId, &userToken.m_h)) { CHandle linkedToken; DWORD size = sizeof(linkedToken.m_h); if (GetTokenInformation(userToken, TokenLinkedToken, &linkedToken.m_h, size, &size)) { CHandle duplicatedToken; if (DuplicateTokenEx(linkedToken, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenPrimary, &duplicatedToken.m_h)) { DWORD creationFlags = CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS; LPVOID env = nullptr; if (CreateEnvironmentBlock(&env, duplicatedToken, TRUE)) { creationFlags |= CREATE_UNICODE_ENVIRONMENT; } else { env = nullptr; } retval = createProcessAsUser(app, param, duplicatedToken, creationFlags, env, pi); DestroyEnvironmentBlock(env); } } } return retval; }
process.cpp にログオンユーザの管理者権限でプログラムを実行する関数 createProcessAsActiveUser を実装しました.process.cpp をプロジェクトに加え(他のソースコードと一緒にコンパイル),process.hpp を include することにより createProcessAsActiveUser が使用可能になります.
以下のテストコード(テストサービス)で createProcessAsActiveUser の動作を確認できます.
test.cpp
#include <string> #include <windows.h> #include "process.hpp" namespace { wchar_t SERVICE_NAME[] = L"TestService"; wchar_t DISPLAY_NAME[] = L"テストサービス"; // このサービスプログラムの実行ファイルのパスを返します const std::wstring getAppPath(HINSTANCE instance = nullptr) { wchar_t path[MAX_PATH] = L""; GetModuleFileName(instance, path, MAX_PATH); return std::wstring(path); } // サービスを登録し,実行します void registerService() { if (SC_HANDLE scManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_CREATE_SERVICE)) { if (SC_HANDLE service = CreateService(scManager, SERVICE_NAME, DISPLAY_NAME, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, getAppPath().c_str(), nullptr, 0, nullptr, nullptr, nullptr)) { StartService(service, 0, nullptr); CloseServiceHandle(service); } CloseServiceHandle(scManager); } } // サービスを終了し,解除します void unregisterService() { if (SC_HANDLE scManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT)) { if (SC_HANDLE service = OpenService(scManager, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE)) { SERVICE_STATUS ss; QueryServiceStatus(service, &ss); if (ss.dwCurrentState == SERVICE_RUNNING) { // もし実行中なら ControlService(service, SERVICE_CONTROL_STOP, &ss); // 終了する } DeleteService(service); CloseServiceHandle(service); } CloseServiceHandle(scManager); } } HANDLE gStopEvent = nullptr; SERVICE_STATUS gServiceStatus = {}; SERVICE_STATUS_HANDLE gServiceHandle = nullptr; DWORD WINAPI handlerEx(DWORD control, DWORD, LPVOID, LPVOID) { switch (control) { case SERVICE_CONTROL_STOP: // 終了要求 gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; gServiceStatus.dwCheckPoint = 0; gServiceStatus.dwWaitHint = 2000; SetServiceStatus(gServiceHandle, &gServiceStatus); SetEvent(gStopEvent); break; case SERVICE_CONTROL_INTERROGATE: SetServiceStatus(gServiceHandle, &gServiceStatus); break; } return NO_ERROR; } VOID WINAPI serviceMain(DWORD, LPWSTR*) { gStopEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); gServiceHandle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, &handlerEx, nullptr); gServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; gServiceStatus.dwCurrentState = SERVICE_RUNNING; gServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; gServiceStatus.dwWin32ExitCode = NO_ERROR; gServiceStatus.dwServiceSpecificExitCode = 0; gServiceStatus.dwCheckPoint = 0; gServiceStatus.dwWaitHint = 0; SetServiceStatus(gServiceHandle, &gServiceStatus); { // サービス開始 process::createProcessAsActiveUser(L"C:\\Windows\\System32\\taskmgr.exe", L""); // タスクマネージャを起動 // このサービスは終了要求を待つのみ WaitForSingleObject(gStopEvent, INFINITE); } { // 終了処理 CloseHandle(gStopEvent); gServiceStatus.dwCurrentState = SERVICE_STOPPED; gServiceStatus.dwCheckPoint = 0; gServiceStatus.dwWaitHint = 0; SetServiceStatus(gServiceHandle, &gServiceStatus); } } } int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR cmdLine, int) { if (wcscmp(cmdLine, L"-register") == 0 || wcscmp(cmdLine, L"/register") == 0) { // サービス登録 registerService(); } else if (wcscmp(cmdLine, L"-unregister") == 0 || wcscmp(cmdLine, L"/unregister") == 0) { // サービス解除 unregisterService(); } else { SERVICE_TABLE_ENTRY services[] = { { SERVICE_NAME, &serviceMain }, { nullptr, nullptr } }; StartServiceCtrlDispatcher(services); } return 0; }