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:
Especially, the above 'Security warning' dialog happens with the special program process which owns certain privileges written in a manifest XML file.
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;
hProcess = GetCurrentProcess();
if ( !OpenProcessToken( hProcess, READ_CONTROL | WRITE_DAC, &hToken ) )
{
printf( "OpenProcessToken() = %d\n", GetLastError() );
bError = TRUE;
goto Cleanup;
}
ZeroMemory( &ea, sizeof( EXPLICIT_ACCESS ) );
BuildExplicitAccessWithName( &ea,
"CURRENT_USER",
TOKEN_ALL_ACCESS,
GRANT_ACCESS,
0 );
if ( !GetKernelObjectSecurity( hToken,
DACL_SECURITY_INFORMATION,
pOrigSd,
0,
&dwSDLen ) )
{
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;
}
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;
}
}
if ( !GetSecurityDescriptorDacl( pOrigSd, &bDAcl, &pOldDAcl, &bDefDAcl ) )
{
printf( "GetSecurityDescriptorDacl() = %d\n", GetLastError() );
bError = TRUE;
goto Cleanup;
}
dwRet = SetEntriesInAcl( 1, &ea, pOldDAcl, &pNewDAcl );
if ( dwRet != ERROR_SUCCESS )
{
printf( "SetEntriesInAcl() = %d\n", GetLastError() );
pNewDAcl = NULL;
bError = TRUE;
goto Cleanup;
}
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;
}
}
if ( !SetSecurityDescriptorDacl( pNewSd, bDAcl, pNewDAcl, bDefDAcl ) )
{
printf( "SetSecurityDescriptorDacl() = %d\n", GetLastError() );
bError = TRUE;
goto Cleanup;
}
if ( !SetKernelObjectSecurity( hToken, DACL_SECURITY_INFORMATION, pNewSd ) )
{
printf( "SetKernelObjectSecurity() = %d\n", GetLastError() );
bError = TRUE;
goto Cleanup;
}
if ( !OpenProcessToken( hProcess, TOKEN_ALL_ACCESS, &hToken ) )
{
printf( "OpenProcessToken() = %d\n", GetLastError() );
bError = TRUE;
goto Cleanup;
}
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 );
ImpersonateLoggedOnUser( hNewToken );
if ( !CreateProcessAsUser( hNewToken,
NULL,
szProcessName,
NULL,
NULL,
FALSE,
NULL,
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