I want to use impersonation in my project to run it in Services correctly.
I see some code that do something like this:
Get explorer.exe
PID, then OpenProcess
with that PID and DuplicateTokenEx
after that, the code CreateThread
and then SetThreadToken
and ResumeThread
This is the code:
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;
}
I use this code and do my work, now I want to return the users to the state, they were before impersonation
I know I should keep the handle before SetThreadToken
and then after I've done my work, again SetThreadToken
with the kept token.
Now I want you to help me to do that.
Get explorer.exe PID, then OpenProcess with that PID and DuplicateTokenEx
That is a VERY OLD (Win9x) approach to getting a session's user token. Since you are already using the WTS API, use WTSQueryUserToken()
instead, passing it the session ID that you want to impersonate.
after that, the code CreateThread and then SetThreadToken and ResumeThread.
Another option is to pass the user token to the thread in the lpParameter
of CreateThread()
, and then the thread can call ImpersonateLoggedOnUser()
after it starts running.
I use this code and do my work, now I want to return the users to the state, they were before impersonation
If a thread needs to stop impersonating before it terminates, it should call RevertToSelf()
. If the impersonation should last until the thread terminates, there is no need to stop impersonating manually.
I know I should keep the handle before SetThreadToken and then after I've done my work, again SetThreadToken with the kept token.
That is useful if the thread is already impersonating one user and then needs to stop impersonating, or to impersonate a different user, and then later needs to impersonate the first user again. But you are not doing that in your example.
Now, with that said, your _CryptUnprotectData()
function is leaking the thread object, as it is not calling CloseHandle()
after WaitForSingleObject()
is done. But more importantly, it is wasted overhead to create a new thread and then make the calling thread block until the new thread is terminated. The calling thread may as well just do the work directly. In your example, _CryptUnprotectData()
can omit CreateThread()
altogether and just call fp_CryptUnprotectData()
directly, wrapped with calls to ImpersonateLoggedOnUser()
/RevertToSelf()
. You do not need to create a new thread just to use impersonation.