Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C
Tip/Trick

Creating a Process with Medium Integration Level from the Process with High Integration Level in Vista

Rate me:
Please Sign up or sign in to vote.
3.34/5 (13 votes)
26 Jan 2008CPOL2 min read 92.5K   914   30   17
This tip suggests the way of launching a process with Medium IL from the process with High IL.

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

C++
HRESULT CreateProcessWithExplorerIL(LPWSTR szProcessName, LPWSTR szCmdLine)
{
HRESULT hr=S_OK;

BOOL bRet;
HANDLE hToken;
HANDLE hNewToken;

bool bVista=false;
{ // When the function is called from IS12, GetVersionEx returns dwMajorVersion=5 on Vista!
    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)

C++
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

C++
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

C++
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

C++
if(SUCCEEDED(hr))
{
    if(dwCurIL==SECURITY_MANDATORY_MEDIUM_RID &&
    dwExplorerIL==SECURITY_MANDATORY_MEDIUM_RID)
    {
        hr=ReducePrivilegesForMediumIL(hNewToken);
    }//if(dwCurIL==...)

5. Creating new process using CreateProcessWithTokenW

C++
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);
}//if(SUCCEEDED(hr))

Epilogue

C++
                    CloseHandle(hNewToken);
                }//if (DuplicateTokenEx(...)
                else
                    hr=HRESULT_FROM_WIN32(GetLastError());
                CloseHandle(hToken);
            }//if(OpenProcessToken(...))
            else
                hr=HRESULT_FROM_WIN32(GetLastError());
            CloseHandle(hProcess);
        }//if(hProcess)
    }//if(dwCurIL==SECURITY_MANDATORY_HIGH_RID && 
    dwExplorerIL==SECURITY_MANDATORY_MEDIUM_RID)
    else if(dwCurIL==SECURITY_MANDATORY_MEDIUM_RID && 
    dwExplorerIL==SECURITY_MANDATORY_HIGH_RID)
        hr=E_ACCESSDENIED;
}//if(SUCCEEDED(hr))
}//if(bVista)

if(SUCCEEDED(hr) && !ProcInfo.dwProcessId)
{// 2K | XP | Vista & !UAC
    bRet = CreateProcess(szProcessName, szCmdLine, 
        NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcInfo);
    if(!bRet)
        hr=HRESULT_FROM_WIN32(GetLastError());
}// 2K | XP | Vista & !UAC

return hr;
}

Using the Code

Using the code is very simple. Just copy the code into your project and call CreateProcessWithExplorerIL().

C++
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Reunion Reunion
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to run Pin
isaprgmr16-Oct-12 19:30
isaprgmr16-Oct-12 19:30 
QuestionThis works well. Thank you. Pin
flobadob197521-Sep-12 1:44
flobadob197521-Sep-12 1:44 
GeneralMethod that doesn't require injection or process token changes Pin
Leo Davidson24-Feb-10 1:02
Leo Davidson24-Feb-10 1:02 
GeneralRe: Method that doesn't require injection or process token changes Pin
Gif13-Jul-11 19:55
Gif13-Jul-11 19:55 
GeneralLaunching this from a dll Pin
Yusuf Cinar5-Feb-10 8:29
Yusuf Cinar5-Feb-10 8:29 
GeneralRe: Launching this from a dll Pin
Yusuf Cinar5-Feb-10 14:09
Yusuf Cinar5-Feb-10 14:09 
NewsSIX bugs in your code !! Pin
Elmue7-Nov-09 15:37
Elmue7-Nov-09 15:37 
NewsHere the cleaned and bugfixed code Pin
Elmue8-Nov-09 5:52
Elmue8-Nov-09 5:52 
I cleaned your code and fixed the bugs.
I tested it on Windows 2000,XP,Vista and Windows 7.
Now it works seamlessly.

#include "stdafx.h"
#include "windows.h"

#ifndef SECURITY_MANDATORY_HIGH_RID
	#define SECURITY_MANDATORY_UNTRUSTED_RID            (0x00000000L)
	#define SECURITY_MANDATORY_LOW_RID                  (0x00001000L)
	#define SECURITY_MANDATORY_MEDIUM_RID               (0x00002000L)
	#define SECURITY_MANDATORY_HIGH_RID                 (0x00003000L)
	#define SECURITY_MANDATORY_SYSTEM_RID               (0x00004000L)
	#define SECURITY_MANDATORY_PROTECTED_PROCESS_RID    (0x00005000L)
#endif

#ifndef TokenIntegrityLevel
	#define TokenIntegrityLevel ((TOKEN_INFORMATION_CLASS)25)
#endif

#ifndef TOKEN_MANDATORY_LABEL
	typedef struct  
	{
		SID_AND_ATTRIBUTES Label;
	} TOKEN_MANDATORY_LABEL;
#endif

typedef BOOL (WINAPI *defCreateProcessWithTokenW)
		(HANDLE,DWORD,LPCWSTR,LPWSTR,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);


// Writes Integration Level of the process with the given ID into pu32_ProcessIL
// returns Win32 API error or 0 if succeeded
DWORD GetProcessIL(DWORD u32_PID, DWORD* pu32_ProcessIL)
{
	*pu32_ProcessIL = 0;
	
	HANDLE h_Process   = 0;
	HANDLE h_Token     = 0;
	DWORD  u32_Size    = 0;
	BYTE*  pu8_Count   = 0;
	DWORD* pu32_ProcIL = 0;
	TOKEN_MANDATORY_LABEL* pk_Label = 0;

	h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_PID);
	if (!h_Process)
		goto _CleanUp;

	if (!OpenProcessToken(h_Process, TOKEN_QUERY, &h_Token))
		goto _CleanUp;
				
	if (!GetTokenInformation(h_Token, TokenIntegrityLevel, NULL, 0, &u32_Size) &&
		 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
		goto _CleanUp;
						
	pk_Label = (TOKEN_MANDATORY_LABEL*) HeapAlloc(GetProcessHeap(), 0, u32_Size);
	if (!pk_Label)
		goto _CleanUp;

	if (!GetTokenInformation(h_Token, TokenIntegrityLevel, pk_Label, u32_Size, &u32_Size))
		goto _CleanUp;

	pu8_Count = GetSidSubAuthorityCount(pk_Label->Label.Sid);
	if (!pu8_Count)
		goto _CleanUp;
					
	pu32_ProcIL = GetSidSubAuthority(pk_Label->Label.Sid, *pu8_Count-1);
	if (!pu32_ProcIL)
		goto _CleanUp;

	*pu32_ProcessIL = *pu32_ProcIL;
	SetLastError(ERROR_SUCCESS);

	_CleanUp:
	DWORD u32_Error = GetLastError();
	if (pk_Label)  HeapFree(GetProcessHeap(), 0, pk_Label);
	if (h_Token)   CloseHandle(h_Token);
	if (h_Process) CloseHandle(h_Process);
	return u32_Error;
}

// Creates a new process u16_Path with the integration level of the Explorer process (MEDIUM IL)
// If you need this function in a service you must replace FindWindow() with another API to find Explorer process
// The parent process of the new process will be svchost.exe if this EXE was run "As Administrator"
// returns Win32 API error or 0 if succeeded
DWORD CreateProcessMediumIL(WCHAR* u16_Path, WCHAR* u16_CmdLine)
{
	HANDLE h_Process = 0;
	HANDLE h_Token   = 0;
	HANDLE h_Token2  = 0;
	PROCESS_INFORMATION k_ProcInfo    = {0};
	STARTUPINFOW        k_StartupInfo = {0};

	BOOL b_UseToken = FALSE;

	// Detect Windows Vista, 2008, Windows 7 and higher
	if (GetProcAddress(GetModuleHandleA("Kernel32"), "GetProductInfo"))
	{
		DWORD u32_CurIL;
		DWORD u32_Err = GetProcessIL(GetCurrentProcessId(), &u32_CurIL);
		if (u32_Err)
			return u32_Err;

		if (u32_CurIL > SECURITY_MANDATORY_MEDIUM_RID)
			b_UseToken = TRUE;
	}

	// Create the process normally (before Windows Vista or if current process runs with a medium IL)
	if (!b_UseToken)
	{
		if (!CreateProcessW(u16_Path, u16_CmdLine, 0, 0, FALSE, 0, 0, 0, &k_StartupInfo, &k_ProcInfo))
			return GetLastError();

		CloseHandle(k_ProcInfo.hThread);
		CloseHandle(k_ProcInfo.hProcess); 
		return ERROR_SUCCESS;
	}

	defCreateProcessWithTokenW f_CreateProcessWithTokenW = 
		(defCreateProcessWithTokenW) GetProcAddress(GetModuleHandleA("Advapi32"), "CreateProcessWithTokenW");

	if (!f_CreateProcessWithTokenW) // This will never happen on Vista!
		return ERROR_INVALID_FUNCTION; 
	
	HWND h_Progman = ::GetShellWindow();

	DWORD u32_ExplorerPID = 0;		
	GetWindowThreadProcessId(h_Progman, &u32_ExplorerPID);

	// ATTENTION:
	// If UAC is turned OFF all processes run with SECURITY_MANDATORY_HIGH_RID, also Explorer!
	// But this does not matter because to start the new process without UAC no elevation is required.
	h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_ExplorerPID);
	if (!h_Process)
		goto _CleanUp;

	if (!OpenProcessToken(h_Process, TOKEN_DUPLICATE, &h_Token))
		goto _CleanUp;

	if (!DuplicateTokenEx(h_Token, TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &h_Token2))
		goto _CleanUp;

	if (!f_CreateProcessWithTokenW(h_Token2, 0, u16_Path, u16_CmdLine, 0, 0, 0, &k_StartupInfo, &k_ProcInfo))
		goto _CleanUp;

	SetLastError(ERROR_SUCCESS);

	_CleanUp:
	DWORD u32_Error = GetLastError();
	if (h_Token)   CloseHandle(h_Token);
	if (h_Token2)  CloseHandle(h_Token2);
	if (h_Process) CloseHandle(h_Process);
	CloseHandle(k_ProcInfo.hThread);
	CloseHandle(k_ProcInfo.hProcess); 
	return u32_Error;
}

int main(int argc, char* argv[])
{
	DWORD u32_Err = CreateProcessMediumIL(L"C:\\Windows\\System32\\Calc.exe", NULL);

	printf("CreateProcessMediumIL() exited with error %d\r\n", u32_Err);
	Sleep(2000);
	return 0;
}

GeneralRe: Here the cleaned and bugfixed code Pin
Brunhilde8-Nov-09 7:07
Brunhilde8-Nov-09 7:07 
GeneralRe: Here the cleaned and bugfixed code Pin
flamierd15-Feb-11 5:40
flamierd15-Feb-11 5:40 
GeneralRe: Here the cleaned and bugfixed code - still errors (leaked handles) Pin
cpp_nerd27-May-11 5:41
cpp_nerd27-May-11 5:41 
GeneralRe: Closing handles Pin
Elmue27-May-11 17:13
Elmue27-May-11 17:13 
NewsRe: Here the cleaned and bugfixed code Pin
ExoticmE7-Nov-11 21:56
ExoticmE7-Nov-11 21:56 
GeneralVista don't allow AppInit_Dlls to inject Pin
sunil_20084-Sep-09 19:46
sunil_20084-Sep-09 19:46 
QuestionGood job, can this be done even better? Pin
Ellery Pierce13-Aug-08 18:23
professionalEllery Pierce13-Aug-08 18:23 
GeneralThank you very much Pin
JuanT25-Jul-08 0:40
JuanT25-Jul-08 0:40 
GeneralGood solution Pin
FaxedHead28-May-08 20:52
FaxedHead28-May-08 20:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.