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

dbgfix: An add-in to properly launch IE during ASP.NET debugging when running VS7 under alternate credentials

0.00/5 (No votes)
16 Apr 2003 2  
This add-in launches IE properly during ASP.NET debugging when running VS7 under alternate credentials.

Introduction

Normally I log on to my machine as a normal non-admin user. In order to debug ASP.NET applications, one needs to be a member of the administrators group. So I need to launch VS.NET as local administrator. Things work out pretty good except that there is an annoying problem that, when I start debugging of the web application, Internet Explorer does not appear, so I have to launch Internet Explorer separately.

I wrote a small macro which handled the IDE events and launched Internet Explorer when debugging started. The only problem with this solution was that, while debugging there were two ASP.NET sessions running simultaneously and single stepping through the code became confusing (as happens with debugging multithreaded applications). The fact that there were two simultaneous sessions indicated that VS did launch IE. Viewing list of processes did indicate that there was an extra IE instance.

SessView

Why was IE main window not visible? Using the SessView tool by Keith Brown indicated that the invisible IE was launched in a separate non-interactive window station. Each window created in the Windows NT based operating systems belongs to a window station. Only one window station named winsta0 is interactive. Windows that belong to this window station can interact with the user. Window stations can be allocated to a process by the parent process through the CreateProcess function call. The 9th parameter of CreateProcess is of type STARTUPINFO. STARTUPINFO has a parameter called lpDesktop which specifies the name of the window station (and desktop) allocated to the process. Here is how it works:-

  1. If lpDesktop is NULL the newly created process inherits the window station of the calling process.
  2. If lpDestop is an empty string, system allocates the process a unique window station based on the logon session ID of the newly created process. The string is of the form Service-0xX-XXX$.
  3. Otherwise system uses the name of the window station provided in lpDesktop.

All evidences from SessView pointed out that lpDestop parameter passed to CreateProcess when launching IE from VS.NET, was an empty string. So a simple fix would be to intercept CreateProcess calls from within the context of the devenv process and modify this parameter.

Detours

Detours library from Microsoft research is my favorite for API interception. API interception is greatly simplified using Detours. I recommend everyone to have a look at the source and the documentation of this library. Detours works by changing the first few processor instructions at the start of the actual API, to invoke the interceptor function or the Detour. The interceptor function can call the real function with actual instructions if needed. The only problem remaining was to inject some code in the devnev process. The easiest way to do that was to write a VS add-in. The add-in can use the Detours library to intercept CreateProcess calls.

The add-in

I named the add-in dbgfix. There are two solutions and two projects included with the source code. dbgfix2003.sln is for VS.NET 2003 users and dbgfix.sln is for VS.NET 2002 users.

To use the add-in directly :-

  1. Extract the source zip files.
  2. Register the add-in by calling RegSvr32 dbgfix.dll

If you choose to build the source, the add-in gets registered in the post build step. Here is what the add-in code does in brief:-

  1. In the OnConnection event the add-in uses Detours library to hook CreateProcessW and CreateProcessA APIs. This is done only if VS.NET is running under the credentials of the primary interactive user - the user who logged on to the machine from the logon screen.
  2. In the OnDisconnection event the add-in cleans up by removing the hooks on CreateProcessW and CreateProcessA APIs.

OnConnection

Lets examine the code of the OnConnection method of the add-in.

  1. Find whether VS.NET is running as primary interactive user. For each window station, system associates a user. The SID of the user can be obtained by calling GetUserObjectInformation on the window station handle of winsta0.
    //Winsta0 is associated with the primary interactive user
    
    HWINSTA hwinsta = OpenWindowStation(TEXT("winsta0"), 
                               FALSE, WINSTA_READATTRIBUTES);
    
    //Get the sid associated with winsta0
    
    DWORD dwLength = 0;
    
    if (!GetUserObjectInformation(hwinsta, UOI_USER_SID, 
                                       NULL, 0, &dwLength))
    {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
            AtlThrowLastWin32();
    }
    
    CAutoVectorPtr<BYTE> spSid(new BYTE[dwLength]);
    
    if (spSid == NULL)
        AtlThrow(E_OUTOFMEMORY);
    
    if (!GetUserObjectInformation(hwinsta, UOI_USER_SID, 
                                spSid, dwLength, &dwLength))
        AtlThrowLastWin32();
  2. The next step is to find the logon sid of the user who launched devenv. This can be obtained from the process token.
    //Get the sid associated with the process token
    
    CAccessToken token;
    
    if (!token.GetProcessToken(TOKEN_QUERY))
        AtlThrowLastWin32();
    
    CSid sidLogon;
    
    if (!token.GetLogonSid(&sidLogon))
        AtlThrowLastWin32();
    
    m_bRunningAsPrimary = (sidLogon == *(SID*)((BYTE*)spSid));
  3. Finally if the logon sid of the user is different from the sid associated with the user of winsta0, we call into Detours library.
    if (!m_bRunningAsPrimary)
    {
        //This means that devnv is running in a different user 
    
        //account than the primary interactive user
    
        DetourFunctionWithTrampoline((PBYTE)Real_CreateProcessW, 
                                      (PBYTE)CreateProcessW_Detour);
        DetourFunctionWithTrampoline((PBYTE)Real_CreateProcessA, 
                                      (PBYTE)CreateProcessA_Detour);
    }

    The first DetourFunctionWithTrampoline call modifies the first few instructions at the address of the CreateProcessWand invoke the function CreateProcessW_Detour. The actual CreateProcess function can still be called using the Real_CreateProcessW. For a complete discussion on how Detours work, refer the Detour web site. Here is how the CreateProcessW_Detour function looks like.

    BOOL WINAPI CreateProcessW_Detour(LPCWSTR lpApplicationName, 
       LPWSTR  lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, 
       LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles, 
       DWORD dwCreationFlags, LPVOID lpEnvironment, 
       LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, 
       LPPROCESS_INFORMATION lpProcessInformation)
    {
       if (lpStartupInfo->lpDesktop != NULL)
       {
           //Check for empty string
    
           if (*lpStartupInfo->lpDesktop == 0)
               lpStartupInfo->lpDesktop = NULL;
       }
    
       return Real_CreateProcessW(lpApplicationName, lpCommandLine, 
              lpProcessAttributes, lpThreadAttributes, 
              bInheritHandles, dwCreationFlags, 
              lpEnvironment, lpCurrentDirectory, 
              lpStartupInfo, lpProcessInformation);
    }

    Basically if the lpDesktop is an empty string, it is converted to NULL before calling the real CreateProcessW. This makes the invoked process to inherit the same window station as VS.NET, essentially fixing the problem.

Conclusion

This simple add-in resolves the problem with launching of IE. Thanks to Microsoft research for providing the source of Detours library. Please note that only detours.lib and detours.h is included with the source of this article. If you are interested in the full source of Detours, you can download it from the Detours web site.

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