Introduction
Since Windows Vista introduced process integration level (IL), it sometimes is highly desired to be specified when creating a new process. This tip suggests the way of launching a process with Medium IL from the process with High IL.
Background
There are the several reasons to specify IL explicitly when launching a new process.
- The most common task is to launch the application at the end of the installation. It’s usual for an application to be executed without elevation, but installer always runs with High IL, therefore the child process will start with the same (High) IL, unless additional enforcement is specified by a developer.
- If a part of the application is loaded as DLL into explorer.exe, it’s required for the application to execute with the same IL as explorer.exe.
- If an application consists of a number of processes which sends messages to each others, all the processes must have the same IL.
- If an application connects to the object in ROT, it must have the same IL as the server which registered the object.
There are some solutions for this problem. Kenny Kerr in his article Windows Vista for Developers – A New Series suggests to create a new token and correct the elevation level for it, however this is not a complete solution because the set of privileges will still correspond to the set of the parent process. Also, the created process doesn't refer to ROT correctly.
The most correct way is explained in the following articles:
However, this way requires to utilize an external DLL (one DLL for 32-bits caller and another one DLL for 64-bits caller).
Solution
This tip suggests a direct way of launching a process with Medium IL from the process with High IL. In Vista, the function looks up for explorer and gets a token to its process. Then, the token is duplicated, and its privileges are adjusted. Finally, it's passed to CreateProcessWithTokenW
function. In the other OS, it simply starts a new process using CreateProcess
().
This is explained step-by-step below.
Prologue
HRESULT CreateProcessWithExplorerIL(LPWSTR szProcessName, LPWSTR szCmdLine)
{
HRESULT hr=S_OK;
BOOL bRet;
HANDLE hToken;
HANDLE hNewToken;
bool bVista=false;
{ HMODULE hmodKernel32=LoadLibrary(L"Kernel32");
if(hmodKernel32 && GetProcAddress(hmodKernel32, "GetProductInfo"))
bVista=true;
if(hmodKernel32) FreeLibrary(hmodKernel32);
}
PROCESS_INFORMATION ProcInfo = {0};
STARTUPINFO StartupInfo = {0};
if(bVista)
{
1. Obtain a handle Shell Window and obtain the ID of its process (explorer.exe)
DWORD dwCurIL=SECURITY_MANDATORY_HIGH_RID;
DWORD dwExplorerID=0, dwExplorerIL=SECURITY_MANDATORY_HIGH_RID;
HWND hwndShell=::FindWindow( _T("Progman"), NULL);
if(hwndShell)
GetWindowThreadProcessId(hwndShell, &dwExplorerID);
hr=GetProcessIL(dwExplorerID, &dwExplorerIL);
if(SUCCEEDED(hr))
hr=GetProcessIL(GetCurrentProcessId(), &dwCurIL);
2. Get the token of the process
if(SUCCEEDED(hr))
{
if(dwCurIL==SECURITY_MANDATORY_HIGH_RID &&
dwExplorerIL==SECURITY_MANDATORY_MEDIUM_RID)
{
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwExplorerID);
if(hProcess)
{
if(OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hToken))
{
3. Duplicate the token
if(!DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, NULL,
SecurityImpersonation, TokenPrimary, &hNewToken))
hr=HRESULT_FROM_WIN32(GetLastError());
4. Modify the set of privileges by removing/disabling the privileges which doesn’t relay to a process with Medium IL
if(SUCCEEDED(hr))
{
if(dwCurIL==SECURITY_MANDATORY_MEDIUM_RID &&
dwExplorerIL==SECURITY_MANDATORY_MEDIUM_RID)
{
hr=ReducePrivilegesForMediumIL(hNewToken);
}
5. Creating new process using CreateProcessWithTokenW
if(SUCCEEDED(hr))
{
typedef BOOL (WINAPI *LPFN_CreateProcessWithTokenW)(
HANDLE hToken,
DWORD dwLogonFlags,
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInfo
);
LPFN_CreateProcessWithTokenW fnCreateProcessWithTokenW=NULL;
HINSTANCE hmodAdvApi32=LoadLibraryA("AdvApi32");
if(hmodAdvApi32)
fnCreateProcessWithTokenW=(LPFN_CreateProcessWithTokenW)
GetProcAddress(hmodAdvApi32, "CreateProcessWithTokenW");
if(fnCreateProcessWithTokenW)
{
bRet=fnCreateProcessWithTokenW(hNewToken, 0,
szProcessName, szCmdLine,
0, NULL, NULL, &StartupInfo, &ProcInfo);
if(!bRet)
hr=HRESULT_FROM_WIN32(GetLastError());
}
else
hr=E_UNEXPECTED;
if(hmodAdvApi32)
FreeLibrary(hmodAdvApi32);
}
Epilogue
CloseHandle(hNewToken);
} else
hr=HRESULT_FROM_WIN32(GetLastError());
CloseHandle(hToken);
} else
hr=HRESULT_FROM_WIN32(GetLastError());
CloseHandle(hProcess);
} } dwExplorerIL==SECURITY_MANDATORY_MEDIUM_RID)
else if(dwCurIL==SECURITY_MANDATORY_MEDIUM_RID &&
dwExplorerIL==SECURITY_MANDATORY_HIGH_RID)
hr=E_ACCESSDENIED;
}}
if(SUCCEEDED(hr) && !ProcInfo.dwProcessId)
{ bRet = CreateProcess(szProcessName, szCmdLine,
NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcInfo);
if(!bRet)
hr=HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
Using the Code
Using the code is very simple. Just copy the code into your project and call CreateProcessWithExplorerIL
().
CreateProcessWithExplorerIL(L"C:\\Program Files\\Microsoft Visual Studio\\Common\\
Tools\\irotview.exe", NULL);
Comments
This function cannot be used in service processes as finding shell window will fail. In this case, the ID of shell process should be obtained using another way. Moreover, there may be several explorer.exe processes.
The parent of the created process will be taskmgr.exe, but not the caller process.