簡介
本章介紹Windows桌面開發(fā)中,服務(wù)程序如何啟動有管理員權(quán)限的界面進程。
在這種情況下,以下幾點需要弄清楚:
-
Windows的服務(wù)是什么
Microsoft Windows 服務(wù)(過去稱為 NT 服務(wù))允許用戶創(chuàng)建可在其自身的 Windows 會話中長時間運行的可執(zhí)行應(yīng)用程序。 這些服務(wù)可在計算機啟動時自動啟動,可以暫停和重啟,并且不顯示任何用戶界面。 這些功能使服務(wù)非常適合在服務(wù)器上使用,或者需要長時間運行的功能(不會影響在同一臺計算機上工作的其他用戶)的情況。 還可以在與登錄用戶或默認計算機帳戶不同的特定用戶帳戶的安全性上下文中運行服務(wù)。
服務(wù)的特殊性有如下幾點:
-
服務(wù)通常在系統(tǒng)啟動時用戶登錄系統(tǒng)之前由SCM(Service Control manager)加載,并在系統(tǒng)啟動時自動開啟的。
-
服務(wù)屬于system用戶,system用戶是windows操作系統(tǒng)下的最高權(quán)限賬戶,類似于Linux下的root。
如下圖,everything有一個服務(wù),其服務(wù)進程是屬于system用戶的
-
服務(wù)不顯示任何用戶界面
-
服務(wù)在不同的登錄用戶下都可運行
-
-
要在當前登錄用戶下啟動界面程序
服務(wù)程序是在system用戶下的,如果用普通方式啟動進程,那啟動的進程也在system用戶下,也是不能展示界面的。需要特殊操作,將進程以當前登錄用戶啟動進程,啟動程序在當前用戶下可展示界面。
實操
服務(wù)程序啟動有管理員權(quán)限的界面進程的過程:
-
GetCurrentUserToken獲取到當前登錄用戶的受限token。
-
GetTokenInformation可以將受限token提升為未受限token。
需要注意,在未開啟UAC的情況下,該函數(shù)會失敗。GetLastError()返回1312,它在winerror.h中定義為ERROR_NO_SUCH_LOGON_SESSION,并描述為“指定的登錄會話不存在。它可能已經(jīng)被終止了。”。這種情況下直接使用受限token啟動即可。
-
CreateProcessAsUser以當前用戶啟動進程。
完整代碼如下:
- 頭文件定義
#ifndef UITIL_H
#define UITIL_H
#ifdef WIN32
#include <Windows.h>
#include <wingdi.h>
#include <tlhelp32.h>
#include <WinUser.h>
#include <wtsapi32.h>
#include <UserEnv.h>
#endif
#include <string>
class Uitil
{
public:
Uitil();
static std::wstring stringToWString(const std::string &string);
static HANDLE getCurrentUserToken();
static bool runProgAsCurUser(HANDLE token, const std::string &progPath, const std::string &progArgs);
static bool RunProgAsCurUserAdminPrivilege(const std::string &progPath, const std::string &progArgs);
};
#endif // UITIL_H
- 實現(xiàn)文件:
#include "uitil.h"
#include <iostream>
#include <locale>
#include <codecvt>
Uitil::Uitil()
{
}
std::wstring Uitil::stringToWString(const std::string &string)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>> cv;
return cv.from_bytes(string);
}
HANDLE Uitil::getCurrentUserToken()
{
// 查詢sessionID
PWTS_SESSION_INFO pSessionInfo = 0;
DWORD dwCount = 0;
::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);
int session_id = 0;
for (DWORD i = 0; i < dwCount; ++i)
{
WTS_SESSION_INFO si = pSessionInfo[i];
if (WTSActive == si.State)
{
session_id = si.SessionId;
break;
}
}
::WTSFreeMemory(pSessionInfo);
// 查詢token
HANDLE current_token = 0;
BOOL bRet = ::WTSQueryUserToken(session_id, ¤t_token);
if (bRet == FALSE)
{
std::cout << "WTSQueryUserToken error, code:" << GetLastError() << std::endl;
return nullptr;
}
HANDLE primaryToken = 0;
bRet = ::DuplicateTokenEx(current_token, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &primaryToken);
::CloseHandle(current_token);
if (bRet == FALSE)
{
std::cout << "DuplicateTokenEx error, code:" << GetLastError() << std::endl;
return nullptr;
}
return primaryToken;
}
bool Uitil::runProgAsCurUser(HANDLE token, const std::string &progPath, const std::string &progArgs)
{
STARTUPINFO StartupInfo = {0};
PROCESS_INFORMATION processInfo;
StartupInfo.cb = sizeof(STARTUPINFO);
auto command = std::string("\"") + progPath + "\"";
if (!progArgs.empty())
{
command += " " + progArgs;
}
void* lpEnvironment = NULL;
BOOL resultEnv = ::CreateEnvironmentBlock(&lpEnvironment, token, FALSE);
if (!resultEnv)
{
std::cout << "CreateEnvironmentBlock error, code:" << GetLastError() << std::endl;
return false;
}
std::cout << "runProgAsCurUser, command:" << command << std::endl;
// 獲取到的hUnfilteredToken就是不受限的token,以token作為CreateProcessAsUser的第一個參數(shù),就可以創(chuàng)建出具有管理員權(quán)限,并且屬于當前用戶的界面程序了,并且這種情況下,不需要加入窗口站。
BOOL result = ::CreateProcessAsUser(token, 0, Uitil::stringToWString(command).data(), NULL, NULL, FALSE, CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, lpEnvironment, 0, &StartupInfo, &processInfo);
if(!result)
{
std::cout << "CreateProcessAsUser error, code:" << GetLastError() << std::endl;
return false;
}
if(lpEnvironment != NULL)
{
::DestroyEnvironmentBlock(lpEnvironment);
}
return true;
}
bool Uitil::RunProgAsCurUserAdminPrivilege(const std::string &progPath, const std::string &progArgs)
{
// UAC開啟時,當前用戶擁有兩個token,分別是受限的token和不受限的token。explorer.exe進程的token就屬于受限的token。
// 在服務(wù)程序中,可以用下面代碼獲取到受限的token。
HANDLE primaryToken = getCurrentUserToken();
if (primaryToken == 0)
{
std::cout << "GetCurrentUserToken error." << std::endl;
return false;
}
// 由此token可以得到不受限的token
bool isOpenOk = false;
HANDLE hUnfilteredToken = NULL;
DWORD dwSize = 0;
BOOL bRet = ::GetTokenInformation(primaryToken, TokenLinkedToken, (VOID*)&hUnfilteredToken, sizeof(HANDLE), &dwSize);
if (bRet)
{
isOpenOk = runProgAsCurUser(hUnfilteredToken, progPath, progArgs);
::CloseHandle(hUnfilteredToken);
}
else
{
// UAC未開時,繼續(xù)使用原來的token打開
std::cout << "GetTokenInformation error and continue open width primary token. code:" << GetLastError() << std::endl;
isOpenOk = runProgAsCurUser(primaryToken, progPath, progArgs);
}
::CloseHandle(primaryToken);
return isOpenOk;
}
- 調(diào)用測試
# 鏈接庫:user32.lib Userenv.lib Wtsapi32.lib Advapi32.lib
Uitil::RunProgAsCurUserAdminPrivilege("C:\\Program Files\\Notepad++\\notepad++.exe","");
驗證
構(gòu)造測試環(huán)境,使notepad++寫入c:/windows目錄下的test.txt文件失敗,但是通過服務(wù)程序拉起notepad++能夠顯示界面并且能寫入該目錄下的文件。具體流程如下:
-
構(gòu)造使系統(tǒng)在windows目錄下寫入text.txt需要特定權(quán)限。
-
新建管理員組的用戶。
在我的電腦-》右鍵“管理”打開如下界面添加新用戶
刪除用戶的Users分組,并添加到Administrators分組下。在新建的用戶上右鍵“屬性”,打開如下界面:
-
將UAC打開
- 按“Windows + R”鍵,打開運行"gpedit.msc",打開組策略編輯器,在該界面上“計算機配置”-》“Windows設(shè)置”-》“安全設(shè)置”-》“本地策略”-》“安全選項”-》“用戶賬戶控制:以管理員批準模式運行所有管理員”。設(shè)置“啟動”,點擊確定。
- 同時確認UAC的選擇不是“從不通知”,如下圖是開啟了UAC的情況,選擇為從不通知,就關(guān)閉UAC。UAC界面打開流程:按Windows+R鍵,打開“運行”,輸入"msconfig",彈出系統(tǒng)設(shè)置頁面,在該頁面點擊“工具”-》更改UAC設(shè)置-》啟動后可彈出UAC修改頁面。
- 重啟后,以xiaobai用戶登錄,然后用notepade++將不能寫入c:/windows/test.txt文件。
-
-
測試
-
在服務(wù)程序中加入以上調(diào)用代碼即可測試。
-
無服務(wù)程序可將調(diào)用寫成一個命令行程序。然后使用PsExec啟動命令行程序在system用戶下啟動測試。
-
PsExec下載:PsExec - Windows Sysinternals | Microsoft Learn文章來源:http://www.zghlxwxcb.cn/news/detail-679001.html
-
PSExec啟動調(diào)試程序文章來源地址http://www.zghlxwxcb.cn/news/detail-679001.html
# PsExec64換成自己的對應(yīng)目錄 # processtest 緩存自己的程序?qū)?yīng)目錄 # -i 運行程序 # -s 在系統(tǒng)帳戶中運行遠程進程 D:\PSTools\PsExec64.exe -i -s D:\code\alltest\codetest\processtest\release\processtest.exe
-
-
到了這里,關(guān)于Windows開發(fā):服務(wù)程序啟動有管理員權(quán)限的界面程序的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!