Click here to Skip to main content
15,897,518 members
Articles / Desktop Programming / WTL

A Developer's Survival Guide to IE Protected Mode

Rate me:
Please Sign up or sign in to vote.
4.91/5 (51 votes)
20 May 200719 min read 433.3K   1.9K   125  
Busted features? APIs failing? Use this guide to get your IE plugin up and running again in protected mode!
// BandDialog.cpp : Implementation of CBandDialog

#include "stdafx.h"
#include "resource.h"
#include "BandDialog.h"

/////////////////////////////////////////////////////////////////////////////
// Message handlers

BOOL CBandDialog::OnInitDialog ( HWND hwndFocus, LPARAM lParam )
{
    DlgResize_Init ( false, false );
    DoDataExchange();

    m_uRegisteredMsg1 = RegisterWindowMessage ( REGISTERED_MSG1_NAME );
    m_uRegisteredMsg2 = RegisterWindowMessage ( REGISTERED_MSG2_NAME );

    m_cLog.InsertColumn ( 0, _T(""), LVCFMT_LEFT, 0, 0 );

    // Print a log message that shows whether PM is on.
HRESULT hr;
BOOL bProtectedMode;

    hr = IEIsProtectedModeProcess ( &bProtectedMode );

    if ( SUCCEEDED(hr) )
        {
        if ( bProtectedMode )
            Log("IE is running in protected mode");
        else
            Log("IE is not running in protected mode");
        }
    else
        Log("IEIsProtectedModeProcess() failed, error: %s", _E(hr));

    return TRUE;    // Let the system set the focus
}


/////////////////////////////////////////////////////////////////////////////
// Command handlers

void CBandDialog::OnOpenMutex1 ( UINT uCode, int nID, HWND hwndCtrl )
{
CHandle hMutex;

    // Try to open mutex #1. If DemoApp.exe is running, the OpenMutex()
    // call will fail if IE is running in protected mode, because the mutex
    // is at medium integrity and this code is running at low integrity, so
    // it can't access the mutex which is at a higher integrity level.
    Log("Opening Mutex1 (this will fail in protected mode)");

    hMutex.Attach ( OpenMutex ( MUTEX_ALL_ACCESS, false, MUTEX1_NAME ) );

    if ( !hMutex )
        Log("OpenMutex() failed, error: %s", _E(GetLastError()));
    else
        Log("OpenMutex() succeeded, the EXE is running");
}

void CBandDialog::OnOpenMutex2 ( UINT uCode, int nID, HWND hwndCtrl )
{
CHandle hMutex;

    // Try to open mutex #2. If DemoApp.exe is running, the OpenMutex()
    // call will succeed even in protected mode, because the EXE sets the
    // mutex's integrity level to low..
    Log("Opening Mutex2");

    hMutex.Attach ( OpenMutex ( MUTEX_ALL_ACCESS, false, MUTEX2_NAME ) );

    if ( !hMutex )
        Log("OpenMutex() failed, error: %s", _E(GetLastError()));
    else
        Log("OpenMutex() succeeded, the EXE is running");
}

void CBandDialog::OnRunExe1 ( UINT uCode, int nID, HWND hwndCtrl )
{
// The magic number that the EXE will look for to check that the data came from us.
#ifdef UNICODE
int sig = 'CP!W';
#else
int sig = 'CP!A';
#endif

    // Build a string with the current time.
SYSTEMTIME st = {0};
TCHAR szTime[128] = {0};
CString sData;
DWORD cbyData;

    GetLocalTime ( &st );
    GetTimeFormat ( LOCALE_USER_DEFAULT, 0, &st, NULL, szTime, _countof(szTime) );
    sData.Format ( _T("Running EXE at %s"), szTime );
    cbyData = (1 + sData.GetLength()) * sizeof(TCHAR) + sizeof(sig);

    // Put that string into shared memory.
CHandle hMapping;
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };

    sa.bInheritHandle = TRUE;

    hMapping.Attach ( CreateFileMapping ( INVALID_HANDLE_VALUE, &sa,
                                          PAGE_READWRITE, 0, cbyData, NULL ) );

    if ( !hMapping )
        {
        Log("CreateFileMapping() failed, error: %s", _E(GetLastError()));
        return;
        }

void* pv = MapViewOfFile ( hMapping, FILE_MAP_WRITE, 0, 0, 0 );
BYTE* pbyData = (BYTE*) pv;

    if ( NULL == pv )
        {
        Log("MapViewOfFile() failed, error: %s", _E(GetLastError()));
        return;
        }

    memcpy ( pbyData, &sig, sizeof(sig) );
    pbyData += sizeof(sig);
    memcpy ( pbyData, (LPCTSTR) sData, cbyData-sizeof(sig) );

    UnmapViewOfFile ( pv );

    // Run the EXE and pass it the shared memory handle. In protected mode,
    // the process is actually started by the broker process IEUser.exe, which
    // means that DemoApp.exe can't inherit the handle from us.
TCHAR szExeDir[MAX_PATH] = {0};
CString sCommandLine;
BOOL bSuccess;
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {0};

    GetModuleFileName ( _Module.GetModuleInstance(), szExeDir, _countof(szExeDir) );
    PathRemoveFileSpec ( szExeDir );
    PathAddBackslash ( szExeDir );
    sCommandLine.Format ( _T("\"%sDemoApp.exe\" /h:%p"), szExeDir,
                          (DWORD_PTR)(HANDLE) hMapping );

    Log("Running command line: %s", (LPCTSTR) sCommandLine);

    bSuccess = CreateProcess ( NULL, sCommandLine.GetBuffer(0), NULL, NULL, TRUE,
                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi );

    if ( bSuccess )
        {
        Log("CreateProcess() succeeded");
        CloseHandle ( pi.hProcess );
        CloseHandle ( pi.hThread );
        }
    else
        Log("CreateProcess() failed, error: %s", _E(GetLastError()));
}

void CBandDialog::OnRunExe2 ( UINT uCode, int nID, HWND hwndCtrl )
{
// The magic number that the EXE will look for to check that the data came from us.
#ifdef UNICODE
int sig = 'CP!W';
#else
int sig = 'CP!A';
#endif

    // Build a string with the current time.
SYSTEMTIME st = {0};
TCHAR szTime[128] = {0};
CString sData;
DWORD cbyData;

    GetLocalTime ( &st );
    GetTimeFormat ( LOCALE_USER_DEFAULT, 0, &st, NULL, szTime, _countof(szTime) );
    sData.Format ( _T("Running EXE at %s"), szTime );
    cbyData = (1 + sData.GetLength()) * sizeof(TCHAR) + sizeof(sig);

    // Get a GUID that we'll use as the name of the shared memory object.
GUID guid = {0};
WCHAR wszGuid[64] = {0};
CString sMappingName;
HRESULT hr;

    hr = CoCreateGuid ( &guid );

    if ( FAILED(hr) )
        {
        Log("CoCreateGuid() failed, error: %s", _E(hr));
        return;
        }

    if ( 0 == StringFromGUID2 ( guid, wszGuid, _countof(wszGuid) ))
        {
        Log("StringFromGUID2() failed, error: %s", _E(GetLastError()));
        return;
        }

    sMappingName = wszGuid;

    // Put that string into shared memory.
CHandle hMapping;

    hMapping.Attach ( CreateFileMapping ( INVALID_HANDLE_VALUE, NULL,
                                          PAGE_READWRITE, 0, cbyData, sMappingName ) );

    if ( !hMapping )
        {
        Log("CreateFileMapping() failed, error: %s", _E(GetLastError()));
        return;
        }

void* pv = MapViewOfFile ( hMapping, FILE_MAP_WRITE, 0, 0, 0 );
BYTE* pbyData = (BYTE*) pv;

    if ( NULL == pv )
        {
        Log("MapViewOfFile() failed, error: %s", _E(GetLastError()));
        return;
        }

    memcpy ( pbyData, &sig, sizeof(sig) );
    pbyData += sizeof(sig);
    memcpy ( pbyData, (LPCTSTR) sData, cbyData-sizeof(sig) );

    UnmapViewOfFile ( pv );

    // Run the EXE and pass it the name of the file mapping object.
    // This works even in protected mode, because DemoApp.exe can access
    // the object via its name.
TCHAR szExeDir[MAX_PATH] = {0};
CString sCommandLine;
BOOL bSuccess;
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {0};

    GetModuleFileName ( _Module.GetModuleInstance(), szExeDir, _countof(szExeDir) );
    PathRemoveFileSpec ( szExeDir );
    PathAddBackslash ( szExeDir );
    sCommandLine.Format ( _T("\"%sDemoApp.exe\" /n:%s"), szExeDir, (LPCTSTR) sMappingName );

    Log("Running command line: %s", (LPCTSTR) sCommandLine);

    bSuccess = CreateProcess ( NULL, sCommandLine.GetBuffer(0), NULL, NULL, FALSE,
                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi );

    if ( bSuccess )
        {
        Log("CreateProcess() succeeded");

        // Note that we have to wait for the EXE to go idle, so the file mapping
        // object stays around until the EXE has had a chance to access it.
        WaitForInputIdle ( pi.hProcess, 10000 );

        CloseHandle ( pi.hProcess );
        CloseHandle ( pi.hThread );
        }
    else
        Log("CreateProcess() failed, error: %s", _E(GetLastError()));
}

void CBandDialog::OnSendMessage1 ( UINT uCode, int nID, HWND hwndCtrl )
{
HWND hwndDlg = FindWindow ( _T("#32770"), _T("DemoApp Main Dialog") );

    if ( NULL == hwndDlg )
        Log("Didn't find the DemoApp window");
    else
        {
        // Send the EXE registered message #1. This will fail in protected
        // mode because we are prevented from sending a message to a window
        // in a process that's at a higher integrity level.
        LRESULT lRes = ::SendMessage ( hwndDlg, m_uRegisteredMsg1, 0, 0 );
        Log("Sent registered message #1, expecting retval %ld, actual retval: %ld", MSG1_RETVAL, lRes);
        }
}

void CBandDialog::OnSendMessage2 ( UINT uCode, int nID, HWND hwndCtrl )
{
HWND hwndDlg = FindWindow ( _T("#32770"), _T("DemoApp Main Dialog") );

    if ( NULL == hwndDlg )
        Log("Didn't find the DemoApp window");
    else
        {
        // Send the EXE registered message #2. This will work in protected
        // mode because DemoApp.exe explicitly allows this message to be
        // sent from lower integrity levels by calling ChangeWindowMessageFilter().
        LRESULT lRes = ::SendMessage ( hwndDlg, m_uRegisteredMsg2, 0, 0 );
        Log("Sent registered message #2, expecting retval %ld, actual retval: %ld", MSG2_RETVAL, lRes);
        }
}

void CBandDialog::OnClearLog ( UINT uCode, int nID, HWND hwndCtrl )
{
    m_cLog.DeleteAllItems();
}

void CBandDialog::OnSaveLog ( UINT uCode, int nID, HWND hwndCtrl )
{
HRESULT hr;
HANDLE hState;
LPWSTR pwszSelectedFilename = NULL;
const DWORD dwSaveFlags = OFN_ENABLESIZING|OFN_HIDEREADONLY|OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT;

    // Call IEShowSaveFileDialog() to get a filename from the user.
    hr = IEShowSaveFileDialog ( m_hWnd, L"Saved log.txt", NULL, L"Text files|*.txt|All files|*.*|",
                                L"txt", 1, dwSaveFlags, &pwszSelectedFilename, &hState );

    if ( S_OK == hr )
        {
        LPWSTR pwszCacheDir = NULL;
        TCHAR szTempFile[MAX_PATH] = {0};

        // Get the path to the IE cache dir, which is a dir that we're allowed
        // to write to in protected mode.
        hr = IEGetWriteableFolderPath ( FOLDERID_InternetCache, &pwszCacheDir );

        if ( SUCCEEDED(hr) )
            {
            // Get a temp file name in that dir.
            GetTempFileName ( CW2CT(pwszCacheDir), _T("bob"), 0, szTempFile );
            CoTaskMemFree ( pwszCacheDir );

            // Write our data to that temp file.
            hr = WriteLogFile ( szTempFile );
            }

        if ( SUCCEEDED(hr) )
            {
            // If we wrote the file successfully, have IE save that data to
            // the file that the user chose.
            hr = IESaveFile ( hState, T2CW(szTempFile) );

            // Clean up our temp file.
            DeleteFile ( szTempFile );
            }
        else
            {
            // We couldn't complete the save operation, so cancel it.
            IECancelSaveFile ( hState );
            }

        if ( FAILED(hr) )
            {
            CString sMsg;
            sMsg.Format ( _T("Error saving file: %s"), _E(hr));
            ::MessageBox ( GetTopLevelParent(), sMsg, _T("IE PM Demo Extension"), MB_ICONERROR );
            }
        else
            Log("Log file saved to: %ls", pwszSelectedFilename);

        CoTaskMemFree ( pwszSelectedFilename );
        }
    else
        {
        if ( S_FALSE == hr )
            Log("Save file dialog cancelled by the user");
        else
            Log("IEShowSaveFileDialog() failed, error: %s", _E(hr));
        }
}


/////////////////////////////////////////////////////////////////////////////
// Other methods

void CBandDialog::Log ( LPCSTR szFormat, ... )
{
va_list va;
CString s;
int nIdx;

    va_start ( va, szFormat );
    s.FormatV ( CA2CT(szFormat), va );

    nIdx = m_cLog.InsertItem ( m_cLog.GetItemCount(), s );
    m_cLog.EnsureVisible ( nIdx, false );
    m_cLog.SetColumnWidth ( 0, LVSCW_AUTOSIZE );
}

HRESULT CBandDialog::WriteLogFile ( LPCTSTR szFilename )
{
CHandle hFile ( CreateFile ( szFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                             FILE_ATTRIBUTE_NORMAL, NULL ) );

    if ( INVALID_HANDLE_VALUE == hFile )
        return E_FAIL;

    for ( int i = 0; i < m_cLog.GetItemCount(); i++ )
        {
        CString sLine;
        DWORD cbyToWrite, cbyWritten = 0;

        m_cLog.GetItemText ( i, 0, sLine );
        sLine += _T("\r\n");

        cbyToWrite = sLine.GetLength() * sizeof(TCHAR);

        if ( !WriteFile ( hFile, (LPCTSTR) sLine, cbyToWrite, &cbyWritten, NULL ) )
            return E_FAIL;
        }

    return S_OK;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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


Written By
Software Developer (Senior) VMware
United States United States
Michael lives in sunny Mountain View, California. He started programming with an Apple //e in 4th grade, graduated from UCLA with a math degree in 1994, and immediately landed a job as a QA engineer at Symantec, working on the Norton AntiVirus team. He pretty much taught himself Windows and MFC programming, and in 1999 he designed and coded a new interface for Norton AntiVirus 2000.
Mike has been a a developer at Napster and at his own lil' startup, Zabersoft, a development company he co-founded with offices in Los Angeles and Odense, Denmark. Mike is now a senior engineer at VMware.

He also enjoys his hobbies of playing pinball, bike riding, photography, and Domion on Friday nights (current favorite combo: Village + double Pirate Ship). He would get his own snooker table too if they weren't so darn big! He is also sad that he's forgotten the languages he's studied: French, Mandarin Chinese, and Japanese.

Mike was a VC MVP from 2005 to 2009.

Comments and Discussions