我如何 return 将用户设置为模拟前的状态?

How do I return the users to the state, they were before impersonation?

我想在我的项目中使用模拟以在服务中正确地运行它。

我看到一些代码做这样的事情:

获取 explorer.exe PID,然后 OpenProcess 使用该 PID 和 DuplicateTokenEx

之后,代码 CreateThread 然后是 SetThreadTokenResumeThread

这是代码:

HANDLE ExplorerToken(VOID) 
{
    PROCESSENTRY32W procEntry;
    HANDLE  Snap = NULL; 
    HANDLE  Process = NULL;
    HANDLE  PToken = NULL;
    LPCWSTR TProce = L"explorer.exe";
    DWORD   TargetSID = -1;
    DWORD   SId = -1;
    DWORD   TargetPID = -1;
    BOOL    WellDone = TRUE;
    HANDLE ExplorerToken = NULL;
        if(ExplorerToken != NULL)
    {
        return ExplorerToken;
    }

    SId = WTSGetActiveConsoleSessionId();
    procEntry.dwSize = sizeof(PROCESSENTRY32W);
    Snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (!Process32FirstW(Snap, &procEntry))
    {
        WellDone = FALSE;
    }
    do
    {
        if (lstrcmpiW(procEntry.szExeFile, TProce) == 0)
        {
            if (ProcessIdToSessionId(procEntry.th32ProcessID, &TargetSID) && TargetSID == SId)
            {
                TargetPID = procEntry.th32ProcessID;
                break;
            }
        }
    } while (Process32NextW(Snap, &procEntry));


    if(TargetPID == -1)
    {
        WellDone = FALSE;
    }
    Process = OpenProcess(MAXIMUM_ALLOWED, FALSE, TargetPID);

    if(!OpenProcessToken(Process, MAXIMUM_ALLOWED, &PToken))
    {
        WellDone = FALSE;
    }

    if(!DuplicateTokenEx(PToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenImpersonation, &ExplorerToken))
    {
        WellDone = FALSE;
    }

    if(WellDone == FALSE)
    {
        return NULL;
    }
    else
    {
        return ExplorerToken;
    }
}

VOID CcCryptUnprotectData(LPVOID data)
{
    PCUD_PARAMS pCudParams;
    pCudParams = (PCUD_PARAMS)data;

    pCudParams->bRetVal = pCudParams->fp_CryptUnprotectData(
        pCudParams->pDataIn,
        pCudParams->ppszDataDescr,
        pCudParams->pOptionalEntropy,
        pCudParams->pvReserved,
        pCudParams->pPromptStruct,
        pCudParams->dwFlags,
        pCudParams->pDataOut);

}

BOOL _CryptUnprotectData(__in  DATA_BLOB *pDataIn, __out_opt  LPWSTR *ppszDataDescr, __in_opt   DATA_BLOB *pOptionalEntropy,
    __in  PVOID pvReserved, __in_opt   CRYPTPROTECT_PROMPTSTRUCT *pPromptStruct, __in DWORD dwFlags, __out DATA_BLOB *pDataOut )
{
    HANDLE ThreadHandle;
    CUD_PARAMS cudParams;
    DWORD   ThreadIdArray   = 0;
    HMODULE hmCrypt32 = NULL;
    HANDLE ExplorerToken;

    cudParams.pDataIn = pDataIn;
    cudParams.ppszDataDescr = ppszDataDescr;
    cudParams.pOptionalEntropy = pOptionalEntropy;
    cudParams.pvReserved = pvReserved;
    cudParams.pPromptStruct = pPromptStruct;
    cudParams.dwFlags = dwFlags;
    cudParams.pDataOut = pDataOut;

    cudParams.bRetVal = FALSE;

    if(MyCryptUnprotectData_OutlookPassword == NULL)
    {
        if(hmCrypt32 == NULL)
        {
            hmCrypt32 = LoadLibraryW(L"Crypt32.dll");
        }
        MyCryptUnprotectData_OutlookPassword = (BOOL (WINAPI* )(DATA_BLOB *pDataIn, LPWSTR *ppszDataDescr, DATA_BLOB *pOptionalEntropy, PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT *pPromptStruct,  DWORD dwFlags, DATA_BLOB *pDataOut))GetProcAddress(hmCrypt32, "CryptUnprotectData");
    }

    cudParams.fp_CryptUnprotectData = MyCryptUnprotectData_OutlookPassword;

    ExplorerToken = ExplorerToken();

    if(ExplorerToken == NULL)
    {
        return FALSE;
    }

    ThreadHandle =  CreateThread(NULL,
        0,                                                   // use default stack size  
        (LPTHREAD_START_ROUTINE)CcCryptUnprotectData,       // thread function name
        (LPVOID)&cudParams,                                  // argument to thread function 
        CREATE_SUSPENDED,               
        &ThreadIdArray); 

    if(ThreadHandle == NULL)
    {
        return FALSE;
    }

    if(!SetThreadToken(&ThreadHandle, ExplorerToken))
    {
        return FALSE;
    }

    ResumeThread(ThreadHandle);

    WaitForSingleObject(ThreadHandle, INFINITE);

    return cudParams.bRetVal;
}

我使用此代码并完成我的工作,现在我想return用户进入模拟前的状态

我知道我应该在 SetThreadToken 之前保留句柄,然后在我完成工作后,再次 SetThreadToken 使用保留的令牌。

现在我要你帮我做。

Get explorer.exe PID, then OpenProcess with that PID and DuplicateTokenEx

这是一种非常古老的 (Win9x) 获取会话用户令牌的方法。由于您已经在使用 WTS API,请改用 WTSQueryUserToken(),将您要模拟的会话 ID 传递给它。

after that, the code CreateThread and then SetThreadToken and ResumeThread.

另一种方案是将用户令牌传递给CreateThread()lpParameter中的线程,然后线程启动运行后就可以调用ImpersonateLoggedOnUser()

I use this code and do my work, now I want to return the users to the state, they were before impersonation

如果线程需要在终止前停止模拟,它应该调用 RevertToSelf()。如果模拟应该持续到线程终止,则无需手动停止模拟。

I know I should keep the handle before SetThreadToken and then after I've done my work, again SetThreadToken with the kept token.

如果线程已经模拟一个用户然后需要停止模拟,或者模拟另一个用户,然后需要再次模拟第一个用户,这将很有用.但是在你的例子中你并没有这样做。

现在,话虽如此,您的 _CryptUnprotectData() 函数正在泄漏线程对象,因为它不会在 WaitForSingleObject() 完成后调用 CloseHandle()。但更重要的是,创建新线程然后使调用线程阻塞直到新线程终止是浪费开销。调用线程也可以直接完成工作。在您的示例中,_CryptUnprotectData() 可以完全省略 CreateThread() 并直接调用 fp_CryptUnprotectData(),并调用 ImpersonateLoggedOnUser()/RevertToSelf()。您不需要为了使用模拟而创建新线程。