Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Running Process without Security Warning in Internet Explorer 7

0.00/5 (No votes)
4 Nov 2008 3  
How to prevent a security warning in Internet Explorer such as An identified program wants to access to your computer, or A website wants to open web content using this program on your computer.

Introduction 

Since Internet Explorer 7 has provided the best protection against malicious attacks as MSDN said, I believe that anybody will meet the same situation - when an extension for Internet Explorer tries to start another process, Internet Explorer will bring up a prompt like the below two example screenshots:

Security warning 1

Especially, the above 'Security warning' dialog happens with the special program process which owns certain privileges written in a manifest XML file.

Security warning 2

In general, if the program process does not have the privileges written in a manifest XML file, the above 'UAC control' dialog pops up.

As you guessed, I shall talk about UAC, Internet Explorer protected mode problem. Actually, I don't want to further explain the Internet Explorer protected mode here. 
In the past, Michael Dunn has further given more description in his article A Developer's Survival Guide to IE Protected Mode.

If you have already read MSDN and Mike's article about Internet Explorer protected mode, you would like to try Starting Processes from Protected Mode' by MSDN solution. Unfortunately, I have tried to use the approach, then tried to perform CreateProcess or ShellExecute(Ex), but it was a failure.

Under the Hood

For solving this trouble as above mentioned, you would first need to understand ACL(Access Control List) knowledge. If you don't intend to touch with this aspect, you can skip the below brief representation, then directly enjoy the below code.

The principle is to create a new high privilege token which refers to the Internet Explorer token. The new high privilege token will own all access permissions, then we pass the high privilege token into CreateProcessAsUser as parameter. Thus, this process will be run on the security context of the user represented by the high privilege token.

You see more details in the below entire code that I have wrapped as a function - CreateSystemProcess. The parameter szProcessName is the executable file path as the new process to be started.

#include <Accctrl.h>
#include <Aclapi.h>

BOOL CreateSystemProcess( LPTSTR szProcessName)
{
	HANDLE hProcess;
	HANDLE hToken, hNewToken;
	DWORD dwPid;

	PACL pOldDAcl = NULL;
	PACL pNewDAcl = NULL;
	BOOL bDAcl;
	BOOL bDefDAcl;
	DWORD dwRet;

	PACL pSacl = NULL;
	PSID pSidOwner = NULL;
	PSID pSidPrimary = NULL;
	DWORD dwAclSize = 0;
	DWORD dwSaclSize = 0;
	DWORD dwSidOwnLen = 0;
	DWORD dwSidPrimLen = 0;

	DWORD dwSDLen;
	EXPLICIT_ACCESS ea;
	PSECURITY_DESCRIPTOR pOrigSd = NULL;
	PSECURITY_DESCRIPTOR pNewSd = NULL;

	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	CString strUserName;

	BOOL bError;

	// Get the current process - IEExplore.exe
	hProcess = GetCurrentProcess();

	// Open IE process token and specify the access types to IE token
	if ( !OpenProcessToken( hProcess, READ_CONTROL | WRITE_DAC, &hToken ) )
	{
		printf( "OpenProcessToken() = %d\n", GetLastError() );

		bError = TRUE;
		goto Cleanup;
	}

	// Create a new access control information that includes all access permissions.
	ZeroMemory( &ea, sizeof( EXPLICIT_ACCESS ) );
	BuildExplicitAccessWithName( &ea,
		"CURRENT_USER", // Note: if you specified other trustee name,
			       // it would fail at subsequent code
		TOKEN_ALL_ACCESS,
		GRANT_ACCESS,
		0 );

	if ( !GetKernelObjectSecurity( hToken,
		DACL_SECURITY_INFORMATION,
		pOrigSd,
		0,
		&dwSDLen ) )
	{
		// We first get the length of original
		// security descriptor to IE token
		if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
		{
			pOrigSd = ( PSECURITY_DESCRIPTOR )
				HeapAlloc( GetProcessHeap(),
				HEAP_ZERO_MEMORY,
				dwSDLen );
			if ( pOrigSd == NULL )
			{
				printf( "Allocate pSd memory to failed!\n" );


				bError = TRUE;
				goto Cleanup;
			}

			// Again, we now get the original
			// security descriptor to IE token
			if ( !GetKernelObjectSecurity( hToken,
				DACL_SECURITY_INFORMATION,
				pOrigSd,
				dwSDLen,
				&dwSDLen ) )
			{
				printf( "GetKernelObjectSecurity() = %d\n",
							GetLastError() );
				bError = TRUE;
				goto Cleanup;
			}
		}
		else
		{
			printf( "GetKernelObjectSecurity() = %d\n", GetLastError() );
			bError = TRUE;
			goto Cleanup;
		}
	}

	// Getting ACL of original security descriptor
	if ( !GetSecurityDescriptorDacl( pOrigSd, &bDAcl, &pOldDAcl, &bDefDAcl ) )
	{
		printf( "GetSecurityDescriptorDacl() = %d\n", GetLastError() );

		bError = TRUE;
		goto Cleanup;
	}

	// Using the created access control information - EXPLICIT_ACCESS,
	// and the original ACL to generate a new ACL
	dwRet = SetEntriesInAcl( 1, &ea, pOldDAcl, &pNewDAcl );
	if ( dwRet != ERROR_SUCCESS )
	{
		printf( "SetEntriesInAcl() = %d\n", GetLastError() );
		pNewDAcl = NULL;

		bError = TRUE;
		goto Cleanup;
	}

	// Create a new security descriptor that refers
	// to original security descriptor.
	if ( !MakeAbsoluteSD( pOrigSd,
		pNewSd,
		&dwSDLen,
		pOldDAcl,
		&dwAclSize,
		pSacl,
		&dwSaclSize,
		pSidOwner,
		&dwSidOwnLen,
		pSidPrimary,
		&dwSidPrimLen ) )
	{
		if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
		{
			pOldDAcl = ( PACL ) HeapAlloc( GetProcessHeap(),
				HEAP_ZERO_MEMORY,
				dwAclSize );
			pSacl = ( PACL ) HeapAlloc( GetProcessHeap(),
				HEAP_ZERO_MEMORY,
				dwSaclSize );
			pSidOwner = ( PSID ) HeapAlloc( GetProcessHeap(),
				HEAP_ZERO_MEMORY,
				dwSidOwnLen );
			pSidPrimary = ( PSID ) HeapAlloc( GetProcessHeap(),
				HEAP_ZERO_MEMORY,
				dwSidPrimLen );
			pNewSd = ( PSECURITY_DESCRIPTOR )
				HeapAlloc( GetProcessHeap(),
				HEAP_ZERO_MEMORY,
				dwSDLen );

			if ( pOldDAcl == NULL ||
				pSacl == NULL ||
				pSidOwner == NULL ||
				pSidPrimary == NULL ||
				pNewSd == NULL )
			{
				printf( "Allocate SID or ACL to failed!\n" );

				bError = TRUE;
				goto Cleanup;
			}

			if ( !MakeAbsoluteSD( pOrigSd,
				pNewSd,
				&dwSDLen,
				pOldDAcl,
				&dwAclSize,
				pSacl,
				&dwSaclSize,
				pSidOwner,
				&dwSidOwnLen,
				pSidPrimary,
				&dwSidPrimLen ) )
			{
				printf( "MakeAbsoluteSD() = %d\n", GetLastError() );

				bError = TRUE;
				goto Cleanup;
			}
		}
		else
		{
			printf( "MakeAbsoluteSD() = %d\n", GetLastError() );

			bError = TRUE;
			goto Cleanup;
		}
	}

	// Well, we have owned a new security descriptor & a new ACL,
	// all we have to do is fetch the new ACL into the new security descriptor!
	if ( !SetSecurityDescriptorDacl( pNewSd, bDAcl, pNewDAcl, bDefDAcl ) )
	{
		printf( "SetSecurityDescriptorDacl() = %d\n", GetLastError() );

		bError = TRUE;
		goto Cleanup;
	}

	//
	// Injects the new security descriptor into IE token
	//
	if ( !SetKernelObjectSecurity( hToken, DACL_SECURITY_INFORMATION, pNewSd ) )
	{
		printf( "SetKernelObjectSecurity() = %d\n", GetLastError() );

		bError = TRUE;
		goto Cleanup;
	}

	//
	// When we open IE process again, the hToken has all access permissions.
	//
	if ( !OpenProcessToken( hProcess, TOKEN_ALL_ACCESS, &hToken ) )
	{
		printf( "OpenProcessToken() = %d\n", GetLastError() );

		bError = TRUE;
		goto Cleanup;
	}

	//
	// Then, make a duplicate from IE token
	//
	if ( !DuplicateTokenEx( hToken,
		TOKEN_ALL_ACCESS,
		NULL,
		SecurityImpersonation,
		TokenPrimary,
		&hNewToken ) )
	{
		printf( "DuplicateTokenEx() = %d\n", GetLastError() );

		bError = TRUE;
		goto Cleanup;
	}


	ZeroMemory( &si, sizeof( STARTUPINFO ) );
	si.cb = sizeof( STARTUPINFO );

	// Now, we impersonate the security context of a
	// logged-on user using the token.
	// Note: if you didn't, the below CreateProcessAsUser
	// will report 1314 no permission error.
	ImpersonateLoggedOnUser( hNewToken );


	// Finally, we use the token to create new process.
	if ( !CreateProcessAsUser( hNewToken,
		NULL,
		szProcessName,
		NULL,
		NULL,
		FALSE,
		NULL, //NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
		NULL,
		NULL,
		&si,
		p ) )
	{
		strMsg.Format( "CreateProcessAsUser() = %d\n", GetLastError() );

		bError = TRUE;
		goto Cleanup;
	}

	bError = FALSE;

Cleanup:
	if ( pOrigSd )
	{
		HeapFree( GetProcessHeap(), 0, pOrigSd );
	}
	if ( pNewSd )
	{
		HeapFree( GetProcessHeap(), 0, pNewSd );
	}
	if ( pSidPrimary )
	{
		HeapFree( GetProcessHeap(), 0, pSidPrimary );
	}
	if ( pSidOwner )
	{
		HeapFree( GetProcessHeap(), 0, pSidOwner );
	}
	if ( pSacl )
	{
		HeapFree( GetProcessHeap(), 0, pSacl );
	}
	if ( pOldDAcl )
	{
		HeapFree( GetProcessHeap(), 0, pOldDAcl );
	}

	if (!bError)
	{
	    CloseHandle( pi.hProcess );
	    CloseHandle( pi.hThread );
	    CloseHandle( hToken );
	    CloseHandle( hNewToken );
	    CloseHandle( hProcess );
	}

	if ( bError )
	{
	    return FALSE;
	}

	return TRUE;
}

Truly, the entire code given above is somewhere from the internet. I have only modified a portion of it for this purpose. However, if you want to create a high privilege token, it's still useful. This means that you can pass the created high privilege token to any API function which has a similar hToken parameter.

Note to Use the Code 

You never need to make a manifest XML file embedded into your EXE file. If you did that, at least, you should delete the similar section as below:

<v3:trustInfo xmlns:v3="urn:schemas-microsoft-com:asm.v3">
  <v3:security>
    <v3:requestedPrivileges>
      <v3:requestedExecutionLevel level="requireAdministrator" />
    </v3:requestedPrivileges>
  </v3:security>
</v3:trustInfo>
</assembly>

I thought that the reason is Vista will be always thinking that the process works on permission mode specified by manifest information. So even if we have created a high privilege token for CreateProcessAsUser, it never works well in Internet Explorer 7. 
On the other hand, I'm not sure whether the upcoming Internet Explorer 8 accepts the trick function. Hopefully, Internet Explorer 8 never takes out the style. :)

Feedback

I tried the wrapped function - CreateSystemProcess and it's working fine on my Vista Ultimate version, and it's applied to administrator and general user mode.
If the code doesn't work on your Vista, please contact me at chenxinyi1978@hotmail.com, I'll be pleased to solve the new problem as soon as possible.

History

  • 11/5/2008: Article created

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here