Click here to Skip to main content
Click here to Skip to main content

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

By , 26 Jan 2008
 

Introduction

Since Windows Vista introduced process integration level (IL), it sometimes is highly desired to be specified when creating a new process. This article 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 in the end of 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 if 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 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:

Riding the Vista UAC elevator, up and down

In-depth analysis of Vista UAC and the creation of CreateProcess...Elevated() APIs

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 article suggest direct way of launching a process with Medium IL from the process with High IL. In Vista the function looks up for explorer and get 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 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;
{ // 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)

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);
                    }//if(dwCurIL==...)

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

Epilogue

                    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 a 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 due to 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)

About the Author

Volirvag Yexela
Software Developer
Reunion Reunion
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: Here the cleaned and bugfixed codememberflamierd15 Feb '11 - 5:40 
Hey, super coder genius...
 
_T("Progman").
 
If you are going to correct code at least make sure it is compatible with the current set of compilers/OS, such as 2008 and XP.
 
Why you have to be the ultimate jerk is unknown, but good luck with that.
What you say?

GeneralRe: Here the cleaned and bugfixed code - still errors (leaked handles)memberpatzw27 May '11 - 5:41 
Many thanks for the effort the original author put in the code and also thanks for these bug fixes!
 
Though the code is still not 100% bug-free because you did not close the unused handles of the newly created process. See my fix below:
 
	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;
	}
 
And another one:
 
	if (!f_CreateProcessWithTokenW(h_Token2, 0, u16_Path, u16_CmdLine, 0, 0, 0, &k_StartupInfo, &k_ProcInfo))
		goto _CleanUp;
 
	::CloseHandle( k_ProcInfo.hThread );
	::CloseHandle( k_ProcInfo.hProcess ); 
	SetLastError(ERROR_SUCCESS);
 
Even better would be to extend the function declaration of CreateProcessMediumIL() to accept all parameters of the original CreateProcess() / CreateProcessWithTokenW() and forward them unmodified. So we would still have the flexibility to use the process handles, if we want to wait for the process, for example.
GeneralRe: Closing handlesmemberElmue27 May '11 - 17:13 
Hello
 
Thanks for the hint.
I added your changes.
 
> ...So we would still have the flexibility to use the process handles, if we want to wait for the process, for example.
 
Obviously.
But if you don't need it (like me) its just blowing up the code...
and you generate a new problem: The caller must not forget to close the handles.
 

In the real live I use a C++ class CHandle that stores my handles and it's destructor cares about closing them.
 
Elmü
NewsRe: Here the cleaned and bugfixed codememberMember 82097377 Nov '11 - 21:56 
Thanks for the solution!
I’ve ported it to Delphi.
GeneralVista don't allow AppInit_Dlls to injectmembersunil_20084 Sep '09 - 19:46 
Hi Friends,
 
Vista for Internet Explorer, doesn't allow to inject any DLL present in HKLM\SOFTWARE\MICROSOFT\Windows NT\CurrentVersion\Windows\AppInit_Dlls.
 
I've also set the entry LoadAppInit_Dlls to 1.
 
When IE start, it stop working.
 
Please, help me to solve the problem.
 
Thanks,
Sunil.
QuestionGood job, can this be done even better?memberEllery Pierce13 Aug '08 - 18:23 
So far this solution is working for me, the only place I think it might not work is on a DRM'd Vista Media PC. I'll check that out later.
 
Can't this be done without impersonating explorer? Can't a token just be adjusted to have the same qualities as the explorer process?
 
It looks like the explorer process has the following attributes:
 
Administrator group is set as "deny only"
Mandatory Label is set to Medium (S-1-16-8192)
Most privileges are disabled.
 
I've tried creating a copy of my token and setting these values but there must be something else in the explorer token that is different because it still isn't working unless I just copy the explorer token. Are there any good tools to fully analyze the properties of a token?
 
Weee!!!

GeneralThank you very muchmemberJuanT25 Jul '08 - 0:40 
This will help me to survive in the move to Vista! Wink | ;)
GeneralGood solutionmemberFaxedHead28 May '08 - 20:52 
This is a tricky problem to get around, and you have provided a creative solution using token duplication. Well done and thanks.

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 26 Jan 2008
Article Copyright 2008 by Volirvag Yexela
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid