Windows开发:服务程序启动有管理员权限的界面程序

这篇具有很好参考价值的文章主要介绍了Windows开发:服务程序启动有管理员权限的界面程序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

本章介绍Windows桌面开发中,服务程序如何启动有管理员权限的界面进程。

在这种情况下,以下几点需要弄清楚:

  • Windows的服务是什么

    Microsoft Windows 服务(过去称为 NT 服务)允许用户创建可在其自身的 Windows 会话中长时间运行的可执行应用程序。 这些服务可在计算机启动时自动启动,可以暂停和重启,并且不显示任何用户界面。 这些功能使服务非常适合在服务器上使用,或者需要长时间运行的功能(不会影响在同一台计算机上工作的其他用户)的情况。 还可以在与登录用户或默认计算机帐户不同的特定用户帐户的安全性上下文中运行服务。

    服务的特殊性有如下几点:

    • 服务通常在系统启动时用户登录系统之前由SCM(Service Control manager)加载,并在系统启动时自动开启的。

    • 服务属于system用户,system用户是windows操作系统下的最高权限账户,类似于Linux下的root。

      如下图,everything有一个服务,其服务进程是属于system用户的

      c windows 服务函数 调用管理员权限app,windows开发,windows,microsoft,c++,个人开发
      c windows 服务函数 调用管理员权限app,windows开发,windows,microsoft,c++,个人开发

    • 服务不显示任何用户界面

    • 服务在不同的登录用户下都可运行

  • 要在当前登录用户下启动界面程序

    服务程序是在system用户下的,如果用普通方式启动进程,那启动的进程也在system用户下,也是不能展示界面的。需要特殊操作,将进程以当前登录用户启动进程,启动程序在当前用户下可展示界面。

实操

服务程序启动有管理员权限的界面进程的过程:

  • GetCurrentUserToken获取到当前登录用户的受限token。

  • GetTokenInformation可以将受限token提升为未受限token。

    需要注意,在未开启UAC的情况下,该函数会失败。GetLastError()返回1312,它在winerror.h中定义为ERROR_NO_SUCH_LOGON_SESSION,并描述为“指定的登录会话不存在。它可能已经被终止了。”。这种情况下直接使用受限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

  • 实现文件:
#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, &current_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的第一个参数,就可以创建出具有管理员权限,并且属于当前用户的界面程序了,并且这种情况下,不需要加入窗口站。
    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。
    // 在服务程序中,可以用下面代码获取到受限的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未开时,继续使用原来的token打开
        std::cout << "GetTokenInformation error and continue open width primary token. code:" << GetLastError() << std::endl;

        isOpenOk = runProgAsCurUser(primaryToken, progPath, progArgs);
    }

    ::CloseHandle(primaryToken);

    return isOpenOk;
}

  • 调用测试
# 链接库:user32.lib Userenv.lib Wtsapi32.lib Advapi32.lib

Uitil::RunProgAsCurUserAdminPrivilege("C:\\Program Files\\Notepad++\\notepad++.exe","");

验证

构造测试环境,使notepad++写入c:/windows目录下的test.txt文件失败,但是通过服务程序拉起notepad++能够显示界面并且能写入该目录下的文件。具体流程如下:

  1. 构造使系统在windows目录下写入text.txt需要特定权限。

    • 新建管理员组的用户。

      在我的电脑-》右键“管理”打开如下界面添加新用户

      c windows 服务函数 调用管理员权限app,windows开发,windows,microsoft,c++,个人开发

      删除用户的Users分组,并添加到Administrators分组下。在新建的用户上右键“属性”,打开如下界面:

    c windows 服务函数 调用管理员权限app,windows开发,windows,microsoft,c++,个人开发

    • 将UAC打开

      1. 按“Windows + R”键,打开运行"gpedit.msc",打开组策略编辑器,在该界面上“计算机配置”-》“Windows设置”-》“安全设置”-》“本地策略”-》“安全选项”-》“用户账户控制:以管理员批准模式运行所有管理员”。设置“启动”,点击确定。
      2. 同时确认UAC的选择不是“从不通知”,如下图是开启了UAC的情况,选择为从不通知,就关闭UAC。UAC界面打开流程:按Windows+R键,打开“运行”,输入"msconfig",弹出系统设置页面,在该页面点击“工具”-》更改UAC设置-》启动后可弹出UAC修改页面。

    c windows 服务函数 调用管理员权限app,windows开发,windows,microsoft,c++,个人开发

    • 重启后,以xiaobai用户登录,然后用notepade++将不能写入c:/windows/test.txt文件。
  2. 测试

    • 在服务程序中加入以上调用代码即可测试。

    • 无服务程序可将调用写成一个命令行程序。然后使用PsExec启动命令行程序在system用户下启动测试。

      1. PsExec下载:PsExec - Windows Sysinternals | Microsoft Learn

      2. PSExec启动调试程序文章来源地址https://www.toymoban.com/news/detail-679001.html

        # PsExec64换成自己的对应目录
        # processtest 缓存自己的程序对应目录
        # -i 运行程序
        # -s 在系统帐户中运行远程进程
        D:\PSTools\PsExec64.exe -i -s D:\code\alltest\codetest\processtest\release\processtest.exe
        

到了这里,关于Windows开发:服务程序启动有管理员权限的界面程序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • windows server 2016由于没有远程桌面授权服务器可以提供许可证,远程会话被中断。请跟服务器管理员联系...

    选择 开始 ,单击 运行 ,在打开框中输入 ServerManager ,单击 确定 。 进入 服务器管理器 页面, 选择管理 删除角色和功能 。 进入删除功能和角色页面,选择 服务器角色 。 在 角 色 框中,取消勾选 远程桌面服务 ,其它配置默认,单击 下一步 。 重启Windows实例或重启服务器

    2024年02月16日
    浏览(71)
  • Windows远程连接桌面报错“由于没有远程桌面授权服务器可以提供许可证,远程会话连接已断开。请跟服务器管理员联系。”

      1、win键+R打开运行器,使用mstsc -admin命令强制远程 2、输入远程服务器ip、用户名和密码,忽略证书问题的提示框,即可连接进入远程桌面    3、在远程桌面中,打开远程服务器的运行框,使用regedit命令打开注册表  进入计算机HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlTerm

    2024年02月16日
    浏览(56)
  • Windows Server 2008 远程连接桌面报错“由于没有远程桌面授权服务器可以提供许可证,远程会话连接已断开。请跟服务器管理员联系。”

    一、首先可以临时强制远程到服务器 win键+R打开运行框 二、在远程桌面中,打开远程服务器的运行框,使用regedit命令打开注册表 1、进入计算机HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlTerminal ServerRCMGracePeriod 2、右击“GracePeriod”,点击权限,再点击高级 3、选择Administrator用

    2024年02月04日
    浏览(75)
  • 设置软件以管理员权限开机自启动,MemReduct为例

    目录 一.MemReduct 1.简介 2.下载方式 二.问题描述 三.具体操作 1.在windows搜索框搜索\\\"任务计划程序\\\" 并打开  2.在右方操作栏点击创建任务  3.常规选项  4.操作选项  5.触发器选项 6.条件选项 7.启动 支持中文,是一个很方便的内存清理软件,占用空间小,效果好,支持定时清理 通过设

    2024年02月08日
    浏览(58)
  • C#实现软件开机自启动(不需要管理员权限)

    目录 原理简介 使用方法 完整代码 本文参考C#/WPF/WinForm/程序实现软件开机自动启动的两种常用方法,将里面中的第一种方法做了封装成 AutoStart 类,使用时直接两三行代码就可以搞定。 自启动的原理是 将软件的快捷方式创建到计算机的自动启动目录下(不需要管理员权限)

    2024年02月11日
    浏览(55)
  • linux环境minio安装启动,管理员登录,nginx代理

    一.下载minio 官网下载:  MinIO | Code and downloads to create high performance object storage  直接点击下载或者用wget https://dl.min.io/server/minio/release/linux-amd64/minio 最后都是得到一个文件minio(大概100M) 二.启动minio 1.创建文件夹,比如 2.将第一步得到的minio放到上面目录下 3.给该文件夹赋权限  

    2023年04月24日
    浏览(66)
  • 【Windows】Windows LAPS:本地管理员密码解决方案

    微软已经在2023年4月的安全更新中,为Windows 10(20H2、21H2、22H2)、Windows 11(21H2、22H2)、Windows Server 2019和Windows Server 2022提供了称为Windows LAPS的新功能。Windows 本地管理员密码解决方案 (Windows LAPS) 是一项 Windows 功能,可自动管理和备份已加入 Azure Active Directory 或已加入 Window

    2024年02月03日
    浏览(44)
  • Windows Update被管理员禁用怎么解决

    大家好,我是破解窝,相信大家的电脑都经历过这种情况 如下图: 想更新,但是提示说你的 *组织已关闭自动更新 ,但是又找不到开启的方法,这是怎么回事? 电脑开启自动更新会更新到更高版本,但是Microsoft怕一些用户不喜欢更新之后的版本,在Windows11中如果你没有开启

    2024年02月06日
    浏览(42)
  • windows脚本获取管理员权限修改host

    很多时候我们常常需要通过管理员权限执行脚本,脚本可能涉及到一些受保护信息的访问,我们写个简单的脚本来更改host文件,host文件就是需要管理员权限才能访问的启动脚本时先检查是否有管理员权限,如果没有就调用授权脚本进行管理员授权打开,给用户弹出需要管理

    2024年02月14日
    浏览(45)
  • Windows管理员权限打开cmd使用cd切换路径失败

    问题描述 :在安装MySQL时需要使用到管理员权限打开cmd来进行服务的安装,MySQL的目录放在了D盘下,在使用到cd来进行路径切换时,出现了路径切换失败。 如:C:WINDOWSsystem32 cd D:UtilMySqlmysql-5.6.44-winx64bin 原因 :在管理员权限打开cmd时,使用 cd 路径 的方式切换路径时,只能

    2024年02月16日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包