博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
System权限下获取路径以及使用用户权限启动进程
阅读量:4192 次
发布时间:2019-05-26

本文共 7194 字,大约阅读时间需要 23 分钟。

一. 背景

最近项目上踩到一个坑,即偶现升级过程中通过计划任务调起新安装包,程序安装到了错误的地方,并且桌面快捷方式等入口均没有生成,总而言之就是一个“自杀”行为。

二. 原因

通过测试发现原因:在有些情况下,通过计划任务(通过服务也是如此)调起的进程是system权限的。而在system权限下进程可能会遇到很多问题:

  1. 通过注册表或expand 环境变量等方法得到的系统目录并不是我们想要的,例如通过SHGetSpecialFolderPath获取CSIDL_LOCAL_APPDATA的路径为C:\windows\system32\config\systemprofile\appdata\local;通过GetEnvironmentVariable获取TMP的路径为C:\Windows\TEMP等;这一类的目录包括且不限于:desktop, paograms, appdata, etc..
  2. 具有system权限的进程创建的子进程也是具有system权限的,这样子进程也会遇到上面第1点的问题
  3. GetEnvironmentVariable函数获取到的环境变量都是SYSTEM用户的
  4. 对HKEY_CURRENT_USER的部分注册表的写操作将会被重定向到HKEY_USERS.DEFAULT

三. 解决过程:

1. 解决目录问题

step1.如何判断是在system下

正常的方法当然是通过通过权限相关的API来判断,当然也可以有一些小技巧来判断。例如上面说到的通过SHGetSpecialFolderPath获取CSIDL_LOCAL_APPDATA的路径,非system权限下为C:\Users\username\AppData\Local,而在system下为C:\windows\system32\config\systemprofile\appdata\local。因此,代码如下:

bool IsSystemPrivilegeImp(){    static bool isSystemPrivilege = false;    if (isSystemPrivilege)    {        return isSystemPrivilege;    }    char szPath[MAX_PATH] = {0};    if (::SHGetSpecialFolderPathA(NULL, szPath, CSIDL_APPDATA, TRUE))    {        std::string flag("config\\systemprofile");        std::string path(szPath);        if (path.find(flag) != std::string::npos)        {            isSystemPrivilege = true;        }    }    return isSystemPrivilege;}

step2. 模拟当前登陆用户

想要获取的正确的目录,需要模拟当前登陆用户,获取登录用户的token,再调用SHGetSpecialFolderPath传入此token来获取。另外通过此token,还可以通过CreateProcessAsUser来创建登录用户权限的进程,这点下文再详述。

// 若执行成功,传出获取的tokenbool ImpersonateLoggedOnUserWrapper(HANDLE& hToken){    DWORD dwConsoleSessionId = WTSGetActiveConsoleSessionId();    if (WTSQueryUserToken(dwConsoleSessionId, &hToken))    {        if (ImpersonateLoggedOnUser(hToken))        {            return true;        }    }    return false;}

step3. 获取正确的目录

通过上面获取的token传入SHGetSpecialFolderPath来获取CSIDL_LOCAL_APPDATA等正确的路径。网上资料说这个函数有时会失败,GetLastError返回5。而Windows提供的另外一个API叫做SHGetFolderPath,是可以正常获取路径的,因此我们使用后面这个API。

BOOL WINAPI SHGetSpecialFolderPathWrapper(    HWND hwnd,    LPWSTR lpszPath,    int csidl,    BOOL fCreate    ){    BOOL ret = FALSE;    do     {        if (false == IsSystemPrivilegeImp())        {            if (SHGetSpecialFolderPath(hwnd, lpszPath, csidl, fCreate))            {                ret = TRUE;                break;            }        }        HANDLE hToken = NULL;        if (false == ImpersonateLoggedOnUserWrapper(hToken))        {            break;        }        if (S_OK == SHGetFolderPath(NULL, csidl, hToken, SHGFP_TYPE_DEFAULT, lpszPath))        {            ret = TRUE;            //使用完毕之后通过调用RevertToSelf取消模拟            RevertToSelf();            break;        }    } while (0);    return ret;}

2. 如何降权启动进程

我们的安装包中会创建很多子进程处理不同任务,而由于这些程序之前在编写代码过程中从没有考虑到system权限的问题,因此改动将会比较麻烦。因此我们的思路是:

在安装包被调起后,立即判断当前是否是system权限,如果是system权限,则以普通管理员用户的权限重新把自己调起,同时本身退出。这种方法改动相对就很少了。

话不多说,上代码:

#define TokenLinkedToken 19DWORD GetActiveSessionID(){    DWORD dwSessionId = 0;    PWTS_SESSION_INFO pSessionInfo = NULL;    DWORD dwCount = 0;    WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &dwCount);    for(DWORD i = 0; i < dwCount; i++)    {        WTS_SESSION_INFO si = pSessionInfo[i];        if(WTSActive == si.State)        {            dwSessionId = si.SessionId;            break;        }    }    WTSFreeMemory(pSessionInfo);    return dwSessionId;}BOOL TriggerAppExecute(std::wstring wstrCmdLine/*, INT32& n32ExitResult*/){    DWORD dwProcesses = 0;    BOOL bResult = FALSE;    DWORD dwSid = GetActiveSessionID();    DWORD dwRet = 0;    PROCESS_INFORMATION pi;    STARTUPINFO si;    HANDLE hProcess = NULL, hPToken = NULL, hUserTokenDup = NULL;    if (!WTSQueryUserToken(dwSid, &hPToken))    {        PROCESSENTRY32 procEntry;        DWORD dwPid = 0;        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);        if (hSnap == INVALID_HANDLE_VALUE)        {            return FALSE;        }        procEntry.dwSize = sizeof(PROCESSENTRY32);        if (Process32First(hSnap, &procEntry))        {            do            {                if (_tcsicmp(procEntry.szExeFile, _T("explorer.exe")) == 0)                {                    DWORD exeSessionId = 0;                    if (ProcessIdToSessionId(procEntry.th32ProcessID, &exeSessionId) && exeSessionId == dwSid)                    {                        dwPid = procEntry.th32ProcessID;                        break;                    }                }            } while (Process32Next(hSnap, &procEntry));        }        CloseHandle(hSnap);        // explorer进程不存在        if (dwPid == 0)        {            return FALSE;        }        hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPid);        if (hProcess == NULL)        {            return FALSE;        }        if(!::OpenProcessToken(hProcess, TOKEN_ALL_ACCESS_P,&hPToken))        {            CloseHandle(hProcess);            return FALSE;        }    }    if (hPToken == NULL)        return FALSE;    TOKEN_LINKED_TOKEN admin;    bResult = GetTokenInformation(hPToken, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, &admin, sizeof(TOKEN_LINKED_TOKEN), &dwRet);    if (!bResult) // vista 以前版本不支持TokenLinkedToken    {        TOKEN_PRIVILEGES tp;        LUID luid;        if (LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))        {            tp.PrivilegeCount =1;            tp.Privileges[0].Luid =luid;            tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;        }        //复制token DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hUserTokenDup);    }    else    {        hUserTokenDup = admin.LinkedToken;    }    LPVOID pEnv =NULL;    DWORD dwCreationFlags = CREATE_PRESERVE_CODE_AUTHZ_LEVEL;    // hUserTokenDup为当前登陆用户的令牌    if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))    {        //如果传递了环境变量参数,CreateProcessAsUser的        //dwCreationFlags参数需要加上CREATE_UNICODE_ENVIRONMENT,        //这是MSDN明确说明的        dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT;    }    else    {        //环境变量创建失败仍然可以创建进程,        //但会影响到后面的进程获取环境变量内容        pEnv = NULL;    }    ZeroMemory( &si, sizeof(si) );    si.cb = sizeof(si);    si.dwFlags = STARTF_USESHOWWINDOW;    si.wShowWindow = SW_HIDE;    ZeroMemory( &pi, sizeof(pi) );    bResult = CreateProcessAsUser(        hUserTokenDup,                     // client's access token        NULL,    // file to execute        (LPTSTR) wstrCmdLine.c_str(),                 // command line        NULL,            // pointer to process SECURITY_ATTRIBUTES        NULL,               // pointer to thread SECURITY_ATTRIBUTES        FALSE,              // handles are not inheritable        dwCreationFlags,     // creation flags        pEnv,               // pointer to new environment block        NULL,               // name of current directory        &si,               // pointer to STARTUPINFO structure        &pi                // receives information about new process        );      if(pi.hProcess)    {        CloseHandle(pi.hThread);        CloseHandle(pi.hProcess);    }    if (hUserTokenDup != NULL)        CloseHandle(hUserTokenDup);    if (hProcess != NULL)        CloseHandle(hProcess);    if (hPToken != NULL)        CloseHandle(hPToken);    if (pEnv != NULL)        DestroyEnvironmentBlock(pEnv);    return TRUE;}

【参考资料: 

 
 
 
 

转载地址:http://okloi.baihongyu.com/

你可能感兴趣的文章
iPhone 12s渲染图曝光,刘海变小了!
查看>>
猎聘2020年Q4营收6.08亿元 同比增长47.6%
查看>>
数百台湾人把名字改成“鲑鱼”去吃免费日料,结果有人改不回来了
查看>>
2021款iPad Pro渲染图曝光:依旧采用双摄+激光雷达扫描仪
查看>>
华为P50首发麒麟9000L:5nm EUV工艺打造 配置有所缩减
查看>>
中兴通讯、江苏联通联合成立5G消息开放实验室
查看>>
iMac Pro全球下架 苹果或不再推出新品
查看>>
消息称荣耀7月发布年度旗舰 或用上旗舰级芯片骁龙 888
查看>>
被职场毒打的专科生
查看>>
中国移动订330万台Redmi K40系列 网友:怪不得抢不到
查看>>
全球芯片供应不足!苹果iPhone生产可能面临中断风险
查看>>
3成失眠者放下手机才能睡 说中你了吗?
查看>>
春招之下,909万应届生的去与留
查看>>
阿里云盘今日公测:无论用户是否付费,未来都不会限速
查看>>
华为P50造型没跑了,后摄造型有点吓人!
查看>>
杨笠代言电脑遭投诉抵制,网友吵翻!英特尔回应了...
查看>>
苹果因不附赠充电头被罚200万美元,网友:该,大快人心!
查看>>
饿了么回应7道菜仅1道正品事件:先行赔付 停止骑手配送资格
查看>>
京东大数据研究院:智能马桶四年销量翻10倍
查看>>
张大奕网店关联公司被行政处罚:因以不合格产品冒充合格产品
查看>>