使用c++给程序获取管理员权限

背景

最近开发的新项目里面的程序,在自己电脑上运行的好好的,打包出去之后,测试多次说程序有问题,打不开,后来好不容易试出来说需要管理员权限,原来测试把程序装在了c盘,而大家都知道c盘的读写是需要权限的,如果再没有权限的时候去读写,出异常就很正常了。那么如果非要这么干呢?这个问题在Windows上有两种解决方法,其中一种是非程序性方法,另一个是程序性的方法,也就是今天的主角,两个方法最后都获取了管理员权限。

修改程序开启时的运行权限

这个就是前菜了,在Windows上,我们都经常使用快捷方式,但是很少有人去设置它,其实快捷方式是有很多设置项的。
这里右键vs快捷方式,选择属性,界面如下。可以看到可以修改的项还是很多的。
在这里插入图片描述
这里我们选择高级
在这里插入图片描述
勾选用管理员身份运行,这样我们再打开程序的时候,程序就会获取管理员权限了。
问题是,这种方法只对当前的这个快捷方式生效,意味着如果我有多个打开方式,或者我把程序拷贝给其他人的时候,这个还需要设置,否则还是要出问题。

c++获取程序管理员权限

这里我们提供一个一劳永逸的好办法,而且你还可以控制是不是提示别人你需要获取管理员权限。
Talk is cheap,show me the code.
下面就是我们完成任务的函数主体

BOOL IsRunAsAdministrator()
{
    BOOL fIsRunAsAdmin = FALSE;
    DWORD dwError = ERROR_SUCCESS;
    PSID pAdministratorsGroup = NULL;

    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    if (!AllocateAndInitializeSid(
        &NtAuthority,
        2,
        SECURITY_BUILTIN_DOMAIN_RID,
        DOMAIN_ALIAS_RID_ADMINS,
        0, 0, 0, 0, 0, 0,
        &pAdministratorsGroup))
    {
        dwError = GetLastError();
        goto Cleanup;
    }

    if (!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin))
    {
        dwError = GetLastError();
        goto Cleanup;
    }

Cleanup:

    if (pAdministratorsGroup)
    {
        FreeSid(pAdministratorsGroup);
        pAdministratorsGroup = NULL;
    }

    if (ERROR_SUCCESS != dwError)
    {
        throw dwError;
    }

    return fIsRunAsAdmin;
}

void ElevateNow()
{
    BOOL bAlreadyRunningAsAdministrator = FALSE;
    try
    {
        bAlreadyRunningAsAdministrator = IsRunAsAdministrator();
    }
    catch (...)
    {
        
    }
    if (!bAlreadyRunningAsAdministrator)
    {
        WCHAR szPath[MAX_PATH];
        if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
        {


            SHELLEXECUTEINFO sei = { sizeof(sei) };

            sei.lpVerb = L"runas";
            sei.lpFile = szPath;
            sei.hwnd = NULL;
            sei.nShow = SW_SHOWDEFAULT;

            if (!ShellExecuteEx(&sei))
            {
                DWORD dwError = GetLastError();
                if (dwError == ERROR_CANCELLED)
                    //Annoys you to Elevate it LOL
                    CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ElevateNow, 0, 0, 0);
            }
        }

    }
    else
    {
        ///Code
    }
}

第一个函数主要用来查看当前程序是否已经是管理员权限运行了,主要是两个函数:

  1. AllocateAndInitializeSid,获取Windows安全标识符
  2. CheckTokenMembership,程序是否是管理员运行
    第二个函数就是我们的重点了。

我们使用GetModuleFileName获取到程序的运行路径
初始化 SHELLEXECUTEINFO sei,可以看到,我们再这个信息里面设置了软件执行的路径,runas,和SW_SHOWDEFAULT,这里SW_SHOWDEFAULT这个参数有很多其他选项,可以跳代码进去看一下,这里也贴一下,常用的就是SW_HIDE,SW_NORMAL.
#define SW_HIDE 0
#define SW_SHOWNORMAL 1
#define SW_NORMAL 1
#define SW_SHOWMINIMIZED 2
#define SW_SHOWMAXIMIZED 3
#define SW_MAXIMIZE 3
#define SW_SHOWNOACTIVATE 4
#define SW_SHOW 5
#define SW_MINIMIZE 6
#define SW_SHOWMINNOACTIVE 7
#define SW_SHOWNA 8
#define SW_RESTORE 9
#define SW_SHOWDEFAULT 10
#define SW_FORCEMINIMIZE 11
#define SW_MAX 11
最重要的就是lpVerb的值设置为runas,这里lpVerb也有很多不同的值,最常见的是"runas",“open”.“null”.更多的值可以上msdn看SHELLEXECUTEINFO,这里"runas",就是我们的提权操作。
如果提权失败,我们这里如果提权失败,会不断尝试,CreateThread,继续提权,这里其实没有太大的必要,一般一次就可以提权成功,所有最精简的代码应该可以携程这样。

void GainAdminPrivileges(CString strApp) {
    SHELLEXECUTEINFO execinfo;
    memset(&execinfo, 0, sizeof(execinfo));
    execinfo.lpFile = strApp;
    execinfo.cbSize = sizeof(execinfo);
    execinfo.lpVerb = _T("runas");
    execinfo.fMask = SEE_MASK_NO_CONSOLE;
    execinfo.nShow = SW_SHOWDEFAULT;

    ShellExecuteEx(&execinfo);
}

这里只需要传程序的路径就可以了,路径可以通过上面的GetModuleFileName获取。
然后我们可以写一个测试程序测试下

int main()
{
    /* FILE* fp = fopen("administratortest.txt", "a+");
    if (fp == NULL)
    {
        return 0;
    }

   fprintf(fp, "administratortest:%d.\n", 1);
    fclose(fp);*/

 	//获取管理员权限
    WCHAR path[MAX_PATH] = { 0 };
    GetModuleFileName(NULL, path, MAX_PATH);
    GainAdminPrivileges(path);
    
    /*FILE* fp = fopen("administratortest.txt", "a+");
    if (fp == NULL)
    {
        return 0;
    }

    fprintf(fp, "administratortest:%d.\n", 2);
    fclose(fp);*/
    return 0;
}

我们编译好之后将exe拷贝到c盘,打开上面的文件读写的版本,会发现,程序崩溃了。
我们只打开下面的文件读写的版本,程序正常运行,也生成了文件,写入了内容。

猜你喜欢

转载自blog.csdn.net/u012505629/article/details/109692159
今日推荐