等待由IShellDispatch2.ShellExecute启动的进程

3
我正在使用IShellDispatch2.ShellExecute来从我的提升的进程下以标准用户身份运行一个进程,如Raymond Chen的文章中所述。与ShellExecuteEx不同,此方法不返回有关进程的任何信息。
我需要知道启动的进程何时完成,并且我可能需要其退出代码。是否有一种方法可以获取此进程的句柄(除了快照之外的其他方式)?最初的回答。

为什么不能使用 ShellExecuteEx? - battlmonstr
@battlmonstr - 用另一个非提升的标记运行进程 - RbMm
如果您知道用户详细信息,可能可以调用“runas”命令以降级运行。此外,这似乎是相关的:https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/20a2d22b-6de6-449a-82f8-6b17e6ccd5be/how-to-createprocess-not-as-administrator - battlmonstr
1
如果您具有调试权限,则可以获取系统进程令牌,模拟它,然后使用WTSQueryUserToken获取您的会话标记或者通过进程枚举中的LogonSID找到它,并最终使用CreateProcessAsUserW - RbMm
@zett42 谢谢,这种方法可能会很有用。 - lpVoid
显示剩余3条评论
2个回答

0

你不能这样做,因为shell没有公开ShellExecuteEx方法,即使有,返回的进程句柄也无法在你的进程中有效。

我能想到的最不hacky的解决方案是创建一个小助手应用程序,充当shell和你想要启动的真实应用程序之间的中间人。这个中间人应用程序可以调用ShellExecuteEx,并在子进程退出时向你的真实应用程序发送消息。


当然可以不使用辅助进程来创建所有内容,但是代码会变得有点冗长。 - RbMm
@RbMm 这真的取决于情况,搜索特定进程名称/路径的进程列表,在某个时间戳之后启动仍可能会产生误报。中间人进程(甚至可以在您的主.EXE中实现)保证不会产生误报。 - Anders
不,我有一种解决方案,它不依赖于进程名称/路径/时间,并且不会产生误报。 - RbMm
我们需要使用CreateProcessAsUserW。但是这里有两个问题 - 获取用户令牌和权限(用于调用CreateProcessAsUserW,并可能获取用户令牌,取决于方式)。如果我们最初拥有调试特权,则可以获取所需的特权:枚举进程,打开它的令牌,查看是否包含所需的特权,复制并模拟用户。要获取用户令牌,我们可以使用WTSQueryUserToken(从同一终端会话)或在具有相同LogonId SID的进程中再次搜索令牌。 - RbMm

0

您可以使用CreateProcessAsUserW以非提升用户令牌启动进程。但有几个问题存在——如果hToken是调用者的主标记的受限版本,则需要SE_INCREASE_QUOTA_NAMESE_ASSIGNPRIMARYTOKEN_NAME特权来调用此API。

第二个问题是如何获取用户令牌?您可以使用WTSQueryUserToken,但是要调用此API,您需要拥有SE_TCB_NAME特权。

因此,您需要具备/获取3项特权SE_ASSIGNPRIMARYTOKEN_NAMESE_TCB_NAMESE_INCREASE_QUOTA_NAME。一般情况下,LocalSystem进程都具有这些权限。如果我们具有SE_DEBUG_PRIVILEGE,则可以打开其中的一些进程。

所以一般来说我们需要做以下步骤:

  • 获取自身的SessionId(用于调用WTSQueryUserToken
  • 在进程或线程令牌中启用SE_DEBUG_PRIVILEGE(提升的令牌通常具有此特权)
  • 搜索具有所需特权的令牌的进程
  • 使用此令牌进行模拟
  • 调用WTSQueryUserToken
  • 调用CreateProcessAsUserW

----- 代码: ---------------

inline ULONG BOOL_TO_ERROR(BOOL f)
{
    return f ? NOERROR : GetLastError();
}

// use in _alloca(guz) because _alloca(0) work incorrect
// return 0 pointer instead allocates a zero-length item
volatile UCHAR guz;

ULONG takePrivileges(HANDLE hToken, ::PTOKEN_PRIVILEGES ptp, ULONG cb, BOOL& bContinue)
{
    if (ULONG PrivilegeCount = ptp->PrivilegeCount)
    {
        int n = 3;
        BOOL fAdjust = FALSE;

        ::PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges;

        do 
        {
            switch (Privileges->Luid.LowPart)
            {
            case SE_ASSIGNPRIMARYTOKEN_PRIVILEGE:
            case SE_INCREASE_QUOTA_PRIVILEGE:
            case SE_TCB_PRIVILEGE:

                if (!(Privileges->Attributes & SE_PRIVILEGE_ENABLED))
                {
                    Privileges->Attributes |= SE_PRIVILEGE_ENABLED;
                    fAdjust = TRUE;
                }

                if (!--n)
                {
                    bContinue = FALSE;

                    ULONG dwError = BOOL_TO_ERROR(DuplicateTokenEx(hToken, 
                        TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE, 
                        0, ::SecurityImpersonation, ::TokenImpersonation, 
                        &hToken));

                    if (dwError == NOERROR)
                    {
                        if (fAdjust)
                        {
                            AdjustTokenPrivileges(hToken, FALSE, ptp, cb, NULL, NULL);
                            dwError = GetLastError();
                        }

                        if (dwError == NOERROR)
                        {
                            dwError = BOOL_TO_ERROR(SetThreadToken(0, hToken));
                        }

                        CloseHandle(hToken);
                    }

                    return dwError;
                }
            }
        } while (Privileges++, --PrivilegeCount);
    }

    return ERROR_NOT_FOUND;
}

ULONG GetPrivileges()
{
    ULONG dwError;

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (hSnapshot != INVALID_HANDLE_VALUE)
    {
        dwError = ERROR_NOT_FOUND;

        PROCESSENTRY32W pe = { sizeof(pe) };

        if (Process32FirstW(hSnapshot, &pe))
        {
            ULONG cb = 0, rcb = 0x100;

            PVOID stack = alloca(guz);

            union {
                PVOID buf;
                ::PTOKEN_PRIVILEGES ptp;
            };

            BOOL bContinue = TRUE;

            do 
            {
                if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
                {
                    HANDLE hToken;

                    if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
                    {
                        do 
                        {
                            if (cb < rcb)
                            {
                                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
                            }

                            if (GetTokenInformation(hToken, ::TokenPrivileges, buf, cb, &rcb))
                            {
                                dwError = takePrivileges(hToken, ptp, rcb, bContinue);
                                break;
                            }

                        } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);

                        CloseHandle(hToken);
                    }

                    CloseHandle(hProcess);
                }
            } while (bContinue && Process32NextW(hSnapshot, &pe));
        }

        CloseHandle(hSnapshot);
    }
    else
    {
        dwError = GetLastError();
    }

    return dwError;
}

ULONG RunNotElevated(PCWSTR lpApplicationName, PWSTR lpCommandLine, PCWSTR lpCurrentDirectory)
{
    HANDLE hToken, hDupToken = 0;

    ULONG SessionId;

    ULONG dwError = BOOL_TO_ERROR(ProcessIdToSessionId(GetCurrentProcessId(), &SessionId));

    if (NOERROR == dwError && 
        (NOERROR == (dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY|TOKEN_DUPLICATE, &hToken)))))
    {
        dwError = BOOL_TO_ERROR(DuplicateTokenEx(hToken, 
            TOKEN_IMPERSONATE|TOKEN_ADJUST_PRIVILEGES, 0,
            ::SecurityImpersonation, ::TokenImpersonation, &hDupToken));

        CloseHandle(hToken);

        if (dwError == NOERROR)
        {
            // get SE_DEBUG_PRIVILEGE

            static ::TOKEN_PRIVILEGES tp = { 1, { { {SE_DEBUG_PRIVILEGE }, SE_PRIVILEGE_ENABLED } } };

            AdjustTokenPrivileges(hDupToken, FALSE, &tp, 0, 0, 0);

            if ((dwError = GetLastError()) == NOERROR)
            {
                dwError = BOOL_TO_ERROR(SetThreadToken(0, hDupToken));
            }

            CloseHandle(hDupToken);

            if (dwError == NOERROR)
            {
                if (NOERROR == (dwError = GetPrivileges()))
                {
                    STARTUPINFOW si = { sizeof(si) };
                    PROCESS_INFORMATION pi;

                    PVOID lpEnvironment;

                    if (WTSQueryUserToken(SessionId, &hToken))
                    {
                        dwError = BOOL_TO_ERROR(CreateEnvironmentBlock(&lpEnvironment, hToken, FALSE));

                        if (dwError == NOERROR)
                        {
                            dwError = BOOL_TO_ERROR(CreateProcessAsUserW(
                                hToken, lpApplicationName, lpCommandLine, 
                                NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT,
                                lpEnvironment, lpCurrentDirectory, &si, &pi));

                            DestroyEnvironmentBlock(lpEnvironment);
                        }
                        CloseHandle(hToken);

                        if (dwError == NOERROR)
                        {
                            CloseHandle(pi.hThread);
                            CloseHandle(pi.hProcess);
                        }
                    }
                }

                SetThreadToken(0, 0);
            }
        }
    }

    return dwError;
}

void test_r()
{
    WCHAR cmd[MAX_PATH];
    if (GetEnvironmentVariable(L"comspec", cmd, RTL_NUMBER_OF(cmd)))
    {
        RunNotElevated1(cmd, L"cmd /k whoami /all",0);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接