首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用CreateProcessAsUser和CreateEnvironmentBlock创建进程后不设置Clientname

使用CreateProcessAsUser和CreateEnvironmentBlock创建进程后不设置Clientname
EN

Stack Overflow用户
提问于 2017-08-29 13:07:57
回答 2查看 1.2K关注 0票数 2

我编写了一个运行在本地系统帐户下的C#服务。当用户登录终端服务器时,我使用它生成一个进程。该服务实现OnSessionChange方法,并使用相应的SessionID接收SessionChangeDescription消息。

我使用这个SessionID从WTSQueryUserToken用户那里获得一个访问令牌。我将此令牌转换为主令牌,并将其传递给CreateEnvironmentBlock,以检索指向用户环境变量的指针。经过进一步的准备,我调用了CreateProcessAsUser函数,以最终生成我的进程,作为最近在他的winsta0\default桌面上登录的用户。

当我使用ProcessExplorer研究流程时,我发现流程上下文中没有CLIENTNAME环境变量。然而,应用程序需要这个变量。

我想知道我做错了什么。或者我遗漏了什么。应该加载用户配置文件,因为当用户登录时,我会做出反应。

有可能是时间问题吗?或者CLIENTNAME var是否以任何其他方式应用于进程?

下面是如何调用CreateEnvironmentBlock函数:

代码语言:javascript
运行
复制
    private static IntPtr GetEnvironmentFromToken(IntPtr token)
    {
        // Get a pointer to the environment variables from the specified user access token
        IntPtr newEnvironment = IntPtr.Zero;
        if (!WinApi.CreateEnvironmentBlock(ref newEnvironment, token, false))
        {
            newEnvironment = IntPtr.Zero;
        }

        return newEnvironment;
    }

如果您需要更多的信息或代码示例,请随时询问。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-08-30 02:15:51

环境变量不仅依赖于用户SID,还依赖于SessionId,因为每个会话都有一些变量。

我们可以在注册表中查看HKEY_USERS\<SID>\Volatile Environment用户环境变量。这里有SessionId子键。在子密钥-每个会话变量下

因此,CreateEnvironmentBlock必须执行下一步-从令牌获取用户SID,打开HKEY_USERS\<SID>\Volatile Environment键,并查询它的值。

然后必须从令牌SessionId 通过GetTokenInformation(hToken, TokenSessionId, )查询,并查询Volatile Environment\SessionId子密钥。

但是系统错误地使用了来自当前进程的SessionId,而不是获取令牌。下一个代码在系统dll中:

代码语言:javascript
运行
复制
WCHAR buf[MAX_PATH];
StringCchPrintfW(buf, RTL_NUMBER_OF(buf), 
    L"%s\\%d", L"Volatile Environment", RtlGetCurrentPeb()->SessionId);

,gs:60h// rax -> PEB

2 c0h这是SessionId在PEB中的偏移量。

当从服务执行应用程序时,PEB中的CLIENTNAME为0,因此CLIENTNAMESESSIONNAME没有添加到环境块中。

这是常见的系统错误。为了进行测试,您可以运行两个cmd.exe -一个不是提升的(从explorer.exe运行)和一个作为admin运行(从svchost.exe -k netsvcs运行),然后在两个set命令中运行-它显示环境字符串。您可以注意到,在不提升的cmd.exe中,存在字符串SESSIONNAME=Console (如果从rdp运行,则为SESSIONNAME=RDP-Tcp#N ),如果您使用rdp,则存在CLIENTNAME=DESKTOP-xxx。但是在高架(以管理员身份运行) cmd.exe中-没有这个字符串。这是因为CreateEnvironmentBlock从具有SessionId == 0的svchost.exe -k netsvcs调用。

要解决这个问题,可以采用两种方法:

容易,但不正确:

代码语言:javascript
运行
复制
        _PEB* peb = RtlGetCurrentPeb();
        DWORD _SessionId = peb->SessionId, SessionId, rcb;

        if (GetTokenInformation(hToken, TokenSessionId, &SessionId, sizeof(SessionId), &rcb))
        {
            peb->SessionId = SessionId;
        }

        PVOID Environment;
        BOOL fOk = CreateEnvironmentBlock(&Environment, hToken, FALSE);

        peb->SessionId = _SessionId;

这里的想法-临时取代SessionId在PEB从令牌到SessionId。这将是工作。如果并发中的另一个线程将在PEB中使用SessionId怎么办?

另一种方式,相对大的代码,但正确的-自己走通过SessionId子键和扩展环境块.

代码语言:javascript
运行
复制
void AddSessionEnv(HANDLE hToken, PVOID Environment, PVOID* pNewEnvironment)
{
    SIZE_T cb = 1, len;
    PWSTR sz = (PWSTR)Environment;
    while (*sz)
    {
        len = wcslen(sz) + 1;
        sz += len;
        cb += len;
    }

    DWORD SessionId, rcb;
    if (GetTokenInformation(hToken, TokenSessionId, &SessionId, sizeof(SessionId), &rcb))
    {
        PROFILEINFO pi = { sizeof(pi), PI_NOUI, L"*" };
        if (LoadUserProfileW(hToken, &pi))
        {
            WCHAR SubKey[48];
            swprintf(SubKey, L"Volatile Environment\\%d", SessionId);
            HKEY hKey;

            if (ERROR_SUCCESS == RegOpenKeyExW((HKEY)pi.hProfile, SubKey, 0, KEY_READ, &hKey))
            {
                cb *= sizeof(WCHAR);

                ULONG cbNeed = 0x200, cbAllocated;
                PVOID NewEnvironment;
                do 
                {
                    if (NewEnvironment = LocalAlloc(0, cb + (cbAllocated = cbNeed)))
                    {
                        cbNeed = AddSessionEnv(hKey, (PWSTR)NewEnvironment, cbAllocated);

                        if (cbNeed && cbAllocated >= cbNeed)
                        {
                            memcpy((PBYTE)NewEnvironment + cbNeed, Environment, cb);
                            *pNewEnvironment = NewEnvironment;
                            break;
                        }

                        LocalFree(NewEnvironment);
                    }

                } while (cbNeed);

                RegCloseKey(hKey);
            }
            UnloadUserProfile(hToken, pi.hProfile);
        }
    }
}

static volatile UCHAR guz;

ULONG AddSessionEnv(HANDLE hKey, PWSTR sz, ULONG Length)
{
    LONG status;

    PVOID stack = alloca(guz);

    ULONG TotalLength = 0, DataLength, Index = 0, cb = 0, rcb = sizeof(KEY_VALUE_FULL_INFORMATION) + 256;

    union {
        PVOID buf;
        PKEY_VALUE_FULL_INFORMATION pkvfi;
    };

    do 
    {
        do 
        {
            if (cb < rcb) cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);

            if (0 <= (status = ZwEnumerateValueKey(hKey, Index, KeyValueFullInformation, buf, cb, &rcb)) && 
                pkvfi->Type == REG_SZ &&
                (DataLength = pkvfi->DataLength) &&
                !(DataLength & (sizeof(WCHAR) - 1)))
            {
                static const UNICODE_STRING CharSet = { 2 * sizeof(WCHAR), 2 * sizeof(WCHAR), L"="};

                USHORT NonInclusivePrefixLength;
                UNICODE_STRING Name = { (USHORT)pkvfi->NameLength, Name.Length, pkvfi->Name };

                // not add strings which containing 0 or `=` symbol or emply
                if (Name.Length && RtlFindCharInUnicodeString(0, &Name, &CharSet, &NonInclusivePrefixLength) == STATUS_NOT_FOUND)
                {
                    UNICODE_STRING Value = { 
                        (USHORT)DataLength, 
                        Value.Length, 
                        (PWSTR)RtlOffsetToPointer(pkvfi, pkvfi->DataOffset) 
                    };

                    PWSTR szEnd = (PWSTR)RtlOffsetToPointer(Value.Buffer, DataLength - sizeof(WCHAR));

                    if (!*szEnd) Value.Length -= sizeof(WCHAR);

                    // not add empty strings or containing 0 or `=` symbol
                    if (Value.Length && RtlFindCharInUnicodeString(0, &Value, &CharSet, &NonInclusivePrefixLength) == STATUS_NOT_FOUND)
                    {
                        ULONG cbNeed = Name.Length + 2 * sizeof(WCHAR) + Value.Length;

                        if (Length >= cbNeed)
                        {
                            sz += 1 + swprintf(sz, L"%wZ=%wZ", &Name, &Value), Length -= cbNeed;
                        }
                        else
                        {
                            Length = 0;
                        }

                        TotalLength += cbNeed;
                    }
                }
            }

        } while (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL );

        Index++;

    } while (status != STATUS_NO_MORE_ENTRIES);

    return TotalLength;
}

并将此代码用作:

代码语言:javascript
运行
复制
        PVOID Environment, NewEnvironment = 0;

        if (CreateEnvironmentBlock(&Environment, hToken, FALSE))
        {
            AddSessionEnv(hToken, Environment, &NewEnvironment);

            CreateProcessAsUserW(hToken, *, CREATE_UNICODE_ENVIRONMENT, 
                NewEnvironment ? NewEnvironment : Environment, *);

            if (NewEnvironment)
            {
                LocalFree(NewEnvironment);
            }
            DestroyEnvironmentBlock(Environment);
        }

为了舒适起见,我使用的RtlFindCharInUnicodeString的定义

代码语言:javascript
运行
复制
enum {
    RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END = 1,
    RTL_FIND_CHAR_IN_UNICODE_STRING_COMPLEMENT_CHAR_SET = 2,
    RTL_FIND_CHAR_IN_UNICODE_STRING_CASE_INSENSITIVE = 4
};

NTSYSAPI
NTSTATUS
NTAPI
RtlFindCharInUnicodeString(
                                 ULONG Flags,
                                 PCUNICODE_STRING StringToSearch,
                                 PCUNICODE_STRING CharSet,
                                 USHORT *NonInclusivePrefixLength
                                 );
票数 2
EN

Stack Overflow用户

发布于 2017-08-29 23:29:19

经过一些试验后,CreateEnvironmentBlock似乎只在进程在与令牌关联的同一个远程桌面会话中运行时才设置CLIENTNAME。冒充并没有产生任何影响。这可以说是Windows中的一个bug。

要解决这个问题,您可以自己将CLIENTNAME添加到环境块中,也可以在用户会话中启动一个进程来代表您调用CreateEnvironmentBlock函数。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45940033

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档