【问题标题】:How do I return the users to the state, they were before impersonation?我如何将用户恢复到模拟之前的状态?
【发布时间】:2016-10-05 00:58:01
【问题描述】:

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

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

获取explorer.exe PID,然后使用该PID 获取OpenProcessDuplicateTokenEx

之后是代码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;
}

我使用这段代码做我的工作,现在我想将用户恢复到模拟之前的状态

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

现在我希望你帮我做这件事。

【问题讨论】:

  • 你不会通过SetThreadToken(&ThreadHandle, 0)清除线程令牌吗?
  • SetThreadToken(&ThreadHandle, 0) 如果ThreadHandle 是一个等于模拟前的令牌的令牌,这将很有用,这意味着我应该从ThreadHandle 备份,但我不知道怎么做????
  • ThreadHandle 是线程句柄,而不是令牌。如果您清除线程的令牌,它将返回使用原始进程令牌,这通常是您想要的。
  • 我会警惕从线程外部执行此操作。它打开的任何句柄都将保持有效并保留模拟访问权限。要解决这个问题,您需要关闭并重新打开,这需要线程的积极合作。
  • 我是初学者,对我来说难以理解,老师告诉我应该从令牌 backup = GetThreadToken 然后 SetThreadToken(&ThreadHandle, ExplorerToken) 得到备份,然后再 SetThreadToken backup 我不知道如何备份 :(

标签: c++ service impersonation


【解决方案1】:

获取 explorer.exe PID,然后是具有该 PID 的 OpenProcess 和 DuplicateTokenEx

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

之后是代码 CreateThread,然后是 SetThreadToken 和 ResumeThread。

另一种选择是将用户令牌传递给CreateThread()lpParameter中的线程,然后线程开始运行后可以调用ImpersonateLoggedOnUser()

我使用这段代码做我的工作,现在我想将用户恢复到模拟之前的状态

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

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

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

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

【讨论】:

  • 谢谢你的回答,你说That is useful if the thread is already impersonating one user and then needs to stop impersonating这就是我想做的!我想模拟用户,做某事然后停止模拟 -> 我应该在 SetThreadToken 之前保留句柄,然后在我完成工作之后,再次使用保留的令牌 SetThreadToken |我不知道该怎么做?
  • @biprinea 你断章取意地回答了我的那一行,你忽略了句子的后半部分。如果您只是想停止冒充 PERIOD,那么只需致电 RevertToSelf() 并继续前进。您不需要之前的令牌,除非您需要在停止模拟后再次重新模拟它。即使那样,您也可以重用您开始使用的原始令牌,除非您处于无法记住令牌的情况(在您的示例中不是这种情况),否则您不需要向线程询问它。跨度>
  • @biprinea 线程不会开始模拟,因此在您开始模拟之前没有要恢复的以前的模拟令牌。你控制你用来模拟的代币。 SetThreadToke() 仅分配/删除模拟令牌。你如此专注于SetThreadToken(),以至于你没有关注大局,或者我在回答中解释的其他事情
  • 如果我使用RevertToSelf() 我如何理解模仿停止?我想我应该在这里使用它: ResumeThread(ThreadHandle); WaitForSingleObject(线程句柄,无限); RevertToSelf();关闭句柄(线程句柄);返回 cudParams.bRetVal;
  • @biprinea RevertToSelf() 停止模拟调用线程,所以你不能在WaitForSingleObject() 之后调用它。您使用CreateThread() 创建的工作线程(实际上正在模拟的线程)必须在终止之前调用RevertToSelf()。在您的示例中这是多余的,因为线程可以简单地终止而不停止模拟,它不需要恢复。
猜你喜欢
  • 2021-04-01
  • 1970-01-01
  • 2018-12-01
  • 2021-05-22
  • 1970-01-01
  • 2012-01-09
  • 1970-01-01
  • 2022-11-02
  • 2011-10-21
相关资源
最近更新 更多