c ++ - Hvordan starter en proces som en normal bruger?

Indlæg af Hanne Mølgaard Plasc

Problem



Der er en launcher - et program i C ++, som du skal køre som administrator. Lanceringsprogrammet lancerer også en anden proces på administratorens vegne, og tredjepartsprogrammer (AutoHotkey), der kører med rettighederne til en normal bruger, kan derfor ikke få adgang til det. Den anden proces kræver ikke administratorrettigheder, så jeg vil gerne implementere lanceringen med rettigheder for en normal bruger. Hvordan gør man det?


I øjeblikket kører jeg processen ved hjælp af boost::process::system.

Bedste reference


Raymond Chen har en blogartikel om dette emne:


Hvordan kan jeg starte en uoprettet proces fra min forhøjede proces og omvendt? [18]



  At gå fra en uudnyttet proces til en forhøjet proces er let. Du kan køre en proces med højde ved at sende runas verbet til ShellExecute eller ShellExecuteEx.

  
   Gå den anden vej er vanskeligere . For en ting er det meget svært at munge dit symbol for at fjerne højden naturen ordentligt. Og for en anden ting, selvom du kunne gøre det, er det ikke den rigtige ting at gøre, fordi den ueleverede bruger kan være forskellig fra den forhøjede bruger.

  
  ...

  
   Løsningen her er at gå tilbage til Explorer og bede Explorer om at starte programmet for dig . Da Explorer kører som den oprindelige ueleverede bruger, vil programmet ... køre som [[brugeren]].



Hans artikel indeholder følgende kode for at starte en uoprettet proces, og en detaljeret forklaring på, hvad koden rent faktisk gør (den FindDesktopFolderView -funktion, der er nævnt nedenfor, er defineret i hans manipulering af stillingerne på ikonerne for skrivebordsblogger): [19]


#define STRICT
#include <windows.h>
#include <shldisp.h>
#include <shlobj.h>
#include <exdisp.h>
#include <atlbase.h>
#include <stdlib.h>

// FindDesktopFolderView incorporated by reference

void GetDesktopAutomationObject(REFIID riid, void **ppv)
{
    CComPtr<IShellView> spsv;
    FindDesktopFolderView(IID\_PPV\_ARGS(&spsv));
    CComPtr<IDispatch> spdispView;
    spsv->GetItemObject(SVGIO\_BACKGROUND, IID\_PPV\_ARGS(&spdispView));
    spdispView->QueryInterface(riid, ppv);
}

void ShellExecuteFromExplorer(
    PCWSTR pszFile,
    PCWSTR pszParameters = nullptr,
    PCWSTR pszDirectory = nullptr,
    PCWSTR pszOperation = nullptr,
    int nShowCmd = SW\_SHOWNORMAL)
{
    CComPtr<IShellFolderViewDual> spFolderView;
    GetDesktopAutomationObject(IID\_PPV\_ARGS(&spFolderView));
    CComPtr<IDispatch> spdispShell;
    spFolderView->get\_Application(&spdispShell);
    CComQIPtr<IShellDispatch2>(spdispShell)
        ->ShellExecute(CComBSTR(pszFile),
                        CComVariant(pszParameters ? pszParameters : L""),
                        CComVariant(pszDirectory ? pszDirectory : L""),
                        CComVariant(pszOperation ? pszOperation : L""),
                        CComVariant(nShowCmd));
}

int \_\_cdecl wmain(int argc, wchar\_t **argv)
{
    if (argc < 2) return 0;

    CCoInitialize init;
    ShellExecuteFromExplorer(
        argv[1],
        argc >= 3 ? argv[2] : L"",
        argc >= 4 ? argv[3] : L"",
        argc >= 5 ? argv[4] : L"",
        argc >= 6 ? \_wtoi(argv[5]) : SW\_SHOWNORMAL);

    return 0;
}

Andre referencer 1


hvis vi kører som admin (mere konkret har S-1-5-32-544 'Administratorer' gruppe aktiveret i token) kan vi åbne lokal system proces token (det giver alle nødvendige for at få adgang til ' Administratorer ').
så vi kan gøre det næste:



  • få selvstændig terminal session id

  • aktiver debug privilegier i token, for at kunne åbne enhver proces med
    PROCESS\_QUERY\_LIMITED\_INFORMATION

  • opregne kørende processer i systemet

  • Åbn proces med PROCESS\_QUERY\_LIMITED\_INFORMATION

  • Åbn procestoken med TOKEN\_QUERY|TOKEN\_DUPLICATE

  • forespørgsels token privilegier - har det
    SE\_ASSIGNPRIMARYTOKEN\_PRIVILEGE og SE\_INCREASE\_QUOTA\_PRIVILEGE -
    det har brug for opkald CreateProcessAsUser og SE\_TCB\_PRIVILEGE det
    kræve for WTSQueryUserToken

  • hvis token har alle disse rettigheder (LocalSystem token har)
    duplikér dette symbol til TokenImpersonation type.

  • hvis det er nødvendigt, aktiver nogle af disse 3 privilegier i token

  • efterligner dette symbol



og nu kan vi [20] [21]



  • ring til WTSQueryUserToken med selvsession id - for ikke at blive forhøjet
    brugertoken

  • og endelig CreateProcessAsUser med dette token

  • nulstil efterligning



kode:


static volatile UCHAR guz;

ULONG RunNonElevated(\_In\_ ULONG SessionId,
                     \_In\_ HANDLE hToken, 
                     \_In\_opt\_ LPCWSTR lpApplicationName,
                     \_Inout\_opt\_ LPWSTR lpCommandLine,
                     \_In\_opt\_ LPSECURITY\_ATTRIBUTES lpProcessAttributes,
                     \_In\_opt\_ LPSECURITY\_ATTRIBUTES lpThreadAttributes,
                     \_In\_ BOOL bInheritHandles,
                     \_In\_ DWORD dwCreationFlags,
                     \_In\_opt\_ LPVOID lpEnvironment,
                     \_In\_opt\_ LPCWSTR lpCurrentDirectory,
                     \_In\_ LPSTARTUPINFOW lpStartupInfo,
                     \_Out\_ LPPROCESS\_INFORMATION lpProcessInformation)
{
    ULONG err;

    PVOID stack = alloca(guz);

    ULONG cb = 0, rcb = FIELD\_OFFSET(TOKEN\_PRIVILEGES, Privileges[SE\_MAX\_WELL\_KNOWN\_PRIVILEGE]);

    union {
        PVOID buf;
        ::PTOKEN\_PRIVILEGES ptp;
    };

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

        if (GetTokenInformation(hToken, ::TokenPrivileges, buf, cb, &rcb))
        {
            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)
                        {
                            err = NOERROR;

                            if (DuplicateTokenEx(hToken, 
                                TOKEN\_ADJUST\_PRIVILEGES|TOKEN\_IMPERSONATE, 
                                0, ::SecurityImpersonation, ::TokenImpersonation, 
                                &hToken))
                            {
                                if (fAdjust)
                                {
                                    AdjustTokenPrivileges(hToken, FALSE, ptp, rcb, NULL, NULL);
                                    err = GetLastError();
                                }

                                if (err == NOERROR)
                                {
                                    if (SetThreadToken(0, hToken))
                                    {
                                        HANDLE hUserToken;
                                        if (WTSQueryUserToken(SessionId, &hUserToken))
                                        {
                                            if (!CreateProcessAsUserW(hUserToken, 
                                                lpApplicationName,
                                                lpCommandLine,
                                                lpProcessAttributes,
                                                lpThreadAttributes,
                                                bInheritHandles,
                                                dwCreationFlags,
                                                lpEnvironment,
                                                lpCurrentDirectory,
                                                lpStartupInfo,
                                                lpProcessInformation))
                                            {
                                                err = GetLastError();
                                            }

                                            CloseHandle(hUserToken);
                                        }
                                        else
                                        {
                                            err = GetLastError();
                                        }
                                        SetThreadToken(0, 0);
                                    }
                                    else
                                    {
                                        err = GetLastError();
                                    }
                                }

                                CloseHandle(hToken);
                            }
                            else
                            {
                                err = GetLastError();
                            }

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

            return ERROR\_NOT\_FOUND;
        }

    } while ((err = GetLastError()) == ERROR\_INSUFFICIENT\_BUFFER);

    return err;
}

ULONG RunNonElevated(\_In\_opt\_ LPCWSTR lpApplicationName,
                     \_Inout\_opt\_ LPWSTR lpCommandLine,
                     \_In\_opt\_ LPSECURITY\_ATTRIBUTES lpProcessAttributes,
                     \_In\_opt\_ LPSECURITY\_ATTRIBUTES lpThreadAttributes,
                     \_In\_ BOOL bInheritHandles,
                     \_In\_ DWORD dwCreationFlags,
                     \_In\_opt\_ LPVOID lpEnvironment,
                     \_In\_opt\_ LPCWSTR lpCurrentDirectory,
                     \_In\_ LPSTARTUPINFOW lpStartupInfo,
                     \_Out\_ LPPROCESS\_INFORMATION lpProcessInformation
                     )
{

    ULONG SessionId;
    if (!ProcessIdToSessionId(GetCurrentProcessId(), &SessionId))
    {
        return GetLastError();
    }

    BOOLEAN b;
    RtlAdjustPrivilege(SE\_DEBUG\_PRIVILEGE, TRUE, FALSE, b);

    ULONG err = NOERROR;

    // much more effective of course use NtQuerySystemInformation(SystemProcessesAndThreadsInformation) here
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS\_SNAPPROCESS, 0), hToken;

    if (hSnapshot != INVALID\_HANDLE\_VALUE)
    {
        PROCESSENTRY32W pe = { sizeof(pe) };

        if (Process32FirstW(hSnapshot, &pe))
        {
            err = ERROR\_NOT\_FOUND;

            do 
            {
                if (pe.th32ProcessID && pe.th32ParentProcessID)
                {
                    if (HANDLE hProcess = OpenProcess(PROCESS\_QUERY\_LIMITED\_INFORMATION, FALSE, pe.th32ProcessID))
                    {
                        if (OpenProcessToken(hProcess, TOKEN\_QUERY|TOKEN\_DUPLICATE, &hToken))
                        {
                            err = RunNonElevated(
                                SessionId,
                                hToken, 
                                lpApplicationName,
                                lpCommandLine,
                                lpProcessAttributes,
                                lpThreadAttributes,
                                bInheritHandles,
                                dwCreationFlags,
                                lpEnvironment,
                                lpCurrentDirectory,
                                lpStartupInfo,
                                lpProcessInformation);

                            CloseHandle(hToken);
                        }
                        else
                        {
                            err = GetLastError();
                        }
                        CloseHandle(hProcess);
                    }
                    else
                    {
                        err = GetLastError();
                    }
                }
            } while (err && Process32NextW(hSnapshot, &pe));
        }
        else
        {
            err = GetLastError();
        }
        CloseHandle(hSnapshot);
    }

    return err;
}


og test:


void test()
{
    STARTUPINFO si = { sizeof(si)};
    PROCESS\_INFORMATION pi;
    WCHAR ApplicationName[MAX\_PATH];
    if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL\_NUMBER\_OF(ApplicationName)))
    {
        if (!RunNonElevated(ApplicationName, L"cmd /k whoami.exe /priv /groups",0,0,0,0,0,0,&si, &pi))
        {
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
    }
}


med dette kan vi ikke kun køre ikke forhøjet proces, men hvis det er nødvendigt, start det med dublet håndtag