SYSTEM权限引发的系列问题

转载 2015年11月18日 15:56:02

Windows下的服务程序(S程序)都是以SYSTEM权限启动的,通过服务程序启动的程序(N程序)自然也会是SYSTEM权限的,而如果开发N的时候没有考虑到SYSTEM权限这种情况,那么有可能N就无法正常的运行于SYSTEM权限下。

场景:客户需要在服务下运行我的程序,在这样的情况下我的程序崩溃了:(为了方便调试,我给程序添加了崩溃转储功能,把DUMP文件拿回来之后用Windbg调试了一下,发现是由于在SYSTEM权限下通过环境变量获取的一些内容发生变化了,通过部分Windows API获取的内容也变了,还有注册表的部分读写也被重定向了,由于我的程序中的判断不够严格,导致了程序Crash了。

这时候我想到是服务程序在运行我的程序的时候,要先模拟成当前的登录用户,然后通过CreateProcessAsUser来启动我的程序,那样问题很好解决,我甚至不需要做任何修改即可。但是作为一个小小的开发我都没有接触到客户的机会,这其中的沟通成本太高了(可能客户的服务程序也是买来的,连源码都没有,就无从改起了),经过和XX几次交流后还得我自己改。

1. SYSTEM权限引发的问题
1. 针对HKEY_CURRENT_USER的部分注册表写操作被重定向到HKEY_USERS\.DEFAULT下面去了;
2. GetEnvironmentVariable函数获取到的环境变量都是SYSTEM用户的;
3. SHGetSpecialFolderPath函数获取到的许多路径也都是SYSTEM用户的;
4. 通过CreateProcess创建的子进程都是SYSTEM权限的;

比如通过SHGetSpecialFolderPath获取CSIDL_LOCAL_APPDATA的路径为C:\windows\system32\config\systemprofile\appdata\local;通过GetEnvironmentVariable获取TMP的路径为C:\Windows\TEMP等;

2. 判断是否是SYSTEM权限
应该是能够通过权限的API去判断的,不过这里我用了一个简单的方法,直接判断路径是否是正常的:

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

3. 模拟当前登陆用户

模拟当前登陆用户,可以解决SHGetSpecialFolderPath的问题,另外保存Token,可以通过CreateProcessAsUser来创建登录用户权限的进程。

bool MyImpersonateLoggedOnUser()
{
    HANDLE hToken = NULL;
    DWORD dwConsoleSessionId = WTSGetActiveConsoleSessionId();
    if (WTSQueryUserToken(dwConsoleSessionId, &hToken))
    {
        if (ImpersonateLoggedOnUser(hToken))
        {
            // 保存Token
            return true;
        }
    }
    return false;
}
// 使用完毕之后通过调用RevertToSelf取消模拟

4. 环境变量
比如说我要获取临时目录的路径,因为环境变量是和进程本身相关的,所以通过GetEnvironmentVariable是不行的。这里先读取注册表HKEY_USERS\.DEFAULT\Environment下的TMP/TEMP的内容,得到:

%USERPROFILE%\AppData\Local\Temp

然后通过调用GetUserProfileDirectory来获取USERPROFILE的内容,注意要传入上面的Token。

当然还有一些用户相关的环境变量是这里没有的,这时候就要枚举用户的SID,然后读取HKEY_USERS的子键来获取了。

5. 其他问题
这个时候子进程可以正常的跑起来了。但是在我的实际操作中还遇到一点点小问题。我的子进程也会调用SHGetSpecialFolderPath来获取CSIDL_LOCAL_APPDATA的路径,但是这个时候居然失败了,GetLastError返回5。

这个地方还是相当疑惑的,首先,普通的权限调用SHGetSpecialFolderPath来获取CSIDL_LOCAL_APPDATA的路径是OK的;另外通过服务调用CreateProcessAsUser启动的程序,调用SHGetSpecialFolderPath来获取CSIDL_PROGRAM_FILES也是可以的,偏偏获取CSIDL_LOCAL_APPDATA就不行了,这个地方实在是搞不明白了,网上也没找到原因。

好在Windows还提供了一个叫做SHGetFolderPath的API,是可以正常获取路径的,所以我做了一个简单的包装,这样不用改动太多的东西,而且不影响原来的稳定性。

// ============================================================================
// SHGetSpecialFolderPath形式的接口,调用失败时再尝试通过SHGetFolderPath函数实现
// ============================================================================
BOOL WINAPI SHGetSpecialFolderPathWrapper(
    HWND hwndOwner,
    LPSTR lpszPath,
    int nFolder,
    BOOL fCreate)
{
    if (SHGetSpecialFolderPath(hwndOwner, lpszPath, nFolder, fCreate))
    {
        return TRUE;
    }
    if (S_OK == SHGetFolderPath(NULL, nFolder, NULL, SHGFP_TYPE_DEFAULT, lpszPath))
    {
        return TRUE;
    }
    return FALSE;
}

MSDN中提到了SHGetSpecialFolderPath is not supported. Instead, use ShGetFolderPath. 不过也提到ShGetFolderPath是Deprecated的,推荐使用SHGetKnownFolderPath。

刚刚从上海听完xKungFoo回来,周末两天加班到深夜,实在是……好在到这里总算是把这些纠结的问题解决了:)

2014/06/09更新
关于第五点的子进程的问题,恰如风行同学所说的,是因为调用CreateProcessAsUser创建子进程时没有设置环境变量导致的,所以可能有的环境变量获取不到,或者说,错误的获取到了SYSTEM账户的环境变量(这些都和注册表内容相关了)。在调用CreateProcessAsUser时,需要创建环境变量,示例代码如下:

// hToken为当前登陆用户的令牌
LPVOID lpEnvBlock = NULL;
BOOL bEnv = CreateEnvironmentBlock(&lpEnvBlock, hToken, FALSE);
DWORD dwFlags = CREATE_NEW_CONSOLE;
if (bEnv)
{
    dwFlags |= CREATE_UNICODE_ENVIRONMENT;
}
// 环境变量创建失败仍然可以创建进程,但会影响到后面的进程获取环境变量内容
bRet = CreateProcessAsUser(
    hToken,
    NULL,
    szCmdLine,
    NULL,
    NULL,
    FALSE,
    dwFlags,
    bEnv ? lpEnvBlock : NULL,
    NULL,
    &si,
    &pi);
// 使用完毕需要释放环境变量的空间
if (bEnv)
{
    DestroyEnvironmentBlock(lpEnvBlock);
}

CreateEnvironmentBlock失败的时候,要不要创建子进程就看自己怎么决定了,我还是要让子进程创建起来,所以调用了CreateProcessAsUser。需要注意的是,如果传递了环境变量参数,CreateProcessAsUser的dwCreationFlags参数需要加上CREATE_UNICODE_ENVIRONMENT,这是MSDN明确说明的。


注:

“获取CSIDL_LOCAL_APPDATA居然获取到了system账户的目录(C:\windows\system32\config\systemprofile\appdata\local)”

这是因为没有创建子进程的环境变量。权限什么的由token决定,保存在内核空间,环境变量保存在用户空间的另一个地方,两者不相干。 (来自http://bbs.csdn.net/topics/200018497)

用CreateEnvironmentBlock创建子进程的环境变量,记得CreateProcessAsUser的dwCreateFlags加上CREATE_UNICODE_ENVIRONMENT属性

相关文章推荐

WINDOWS下system用户至高无上的权限

WINDOWS下system用户至高无上的权限 大家知道,SYSTEM是至高无上的超级管理员帐户。默认情况下,我们无法直接在登录对话框上以SYSTEM帐户的身份登录到Windows桌面环境。实际上S...
  • haiross
  • haiross
  • 2013年12月31日 17:12
  • 6744

Win7-x64实现以System权限执行命令

前段时间发现硬盘上某个分区中有一文件夹,好像是系统更新的时候留下的,以Administrator登陆的当前账户也无法将其删除,恐怕权限不够啊。。。 这可如何是好,放在那确实碍眼,想方设法也要把它清除掉...
  • net_syc
  • net_syc
  • 2014年08月08日 00:46
  • 2141

SHGetSpecialFolderPath 一个很有用的API

 SHGetSpecialFolderPath 一个很有用的APIPB取系统文件夹,有很多API,如GetSystemDirectoryA可以得到SYSTEM32系统文件夹,GetTempPathA得...

SHGetSpecialFolderPath 一个很有用的API

SHGetSpecialFolderPath 一个很有用的API2007-05-15 21:38PB取系统文件夹,有很多API,如GetSystemDirectoryA可以得到SYSTEM32系统文件...

如何获得system权限

大家知道,SYSTEM是至高无上的超级管理员帐户。默认情况下,我们无法直接在登录对话框上以SYSTEM帐户的身份登录到Windows桌面环境。实际上SYSTEM帐户早就已经“盘踞”在系统中了。想想也是...

以Windows系统服务得到活动用户的用户名、UserProfile与环境变量

在之前的一篇博文中(http://blog.csdn.net/nirendao/article/details/51194003),介绍了如何使用QT写一个Windows下的Service. 这篇文章...

C:\windows\system32\config\systemprofile\下创建的desktop 与服务有关

【IT168 专稿】甲型H1N1流行,人人谈隔离而色变。好在我们今天谈的不是H1N1的隔离,而是系统服务的Session 0的隔离。   隔离,是为了更好的保护。但是,众所周知的,隔离也会给我们的生...

System权限下进程遇到的问题以及如何降权启动进程

一. 背景最近项目上踩到一个坑,即偶现升级过程中通过计划任务调起新安装包,程序安装到了错误的地方,并且桌面快捷方式等入口均没有生成,总而言之就是一个“自杀”行为。二. 原因通过测试发现原因:在有些情况...
  • yockie
  • yockie
  • 2015年06月10日 20:23
  • 3984

Windows系统下获取SYSTEM权限设置的方法

默认情况下,我们无法直接在登录对话框上以SYSTEM帐户的身份登录到Windows桌面环境。实际上SYSTEM帐户早就已经“盘踞”在系统中了。想想也是,连负责用户验证的Winlogon、Lsass等进...

Windows 系统操作系列API-(1)-SHGetFolderPath

VC获取特殊目录SHGetFolderPath
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:SYSTEM权限引发的系列问题
举报原因:
原因补充:

(最多只允许输入30个字)