Using SoS to debug 32-bit code in a 64-bit dump with WinDbg

Update: I have since turned this into a WinDbg extension.

Let us say that you for some reason have made a 64-bit dump of a 32-bit .NET process (possibly by mistake), and you need to investigate it. It used to be that you could just load it into the 32-bit version of WinDbg and use !wow64ext.sw to switch into the 32-bit view of the process, and then SOS would work fine. However if you try this with .NET 4.5/4.5.1 then this is the result:
SOS does not support the current target architecture.

So… what can one do then? Attempting to remove some checks from windbg and sos perhaps?

It turns out that this is possible. The following instructions works with windbg from debugging tools for Windows 8.1 and Windows 10.

Continue reading

Lessons learned from working with the Windows shell namespace

Lessons learned while writing PSShellProvider:

  • Barring null-characters (well it would break things) there is absolutely no limitations on what a display name might contain – even display names for parsing. E.g. the display name for parsing for my smartphone connected as an MTP device is:
    \\?\usb#vid_04e8&pid_6860&ms_comp_mtp&android#7&102aae5b&2&0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33}

    This gives it a full display name for parsing (what I'm calling "parse path") of:

    ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_04e8&pid_6860&ms_comp_mtp&android#7&102aae5b&2&0000#{6ac27878-a6fa-4155-ba85-f98f491d4f33}

    Ugh… In this case the full path can still be parsed because it sees the first backslash as the delimiter and the IShellFolder handler for "This PC" recognizes the rest of it. But what if the display name ended with a backslash instead?

  • The Desktop folder is the root of the shell namespace, but the empty string parses into the "This PC" virtual folder. Wat?
  • The Desktop folder is the root of the shell namespace, but if you ask it what its desktop absolute parsing path is then it gives you the filesystem path to the user's Desktop folder in the filesystem, e.g.
    C:\Users\poizan\Desktop

    Parsing this display name obviously doesn't give you the virtual Desktop folder which contains This PC and other virtual folders, but the user's Desktop folder in the filesystem instead. Also by this logic the (virtual) Desktop folder is a descendant of itself.

  • The same thing as the point above happens with other special folders as well, however this isn't a problem for most of them as they have a 1-to-1 correspondence between the virtual folder and the filesystem folder. Example listing special folders from the desktop:
    DisplayName      ParseName                                DesktopAbsoluteParsePath
    -----------      ---------                                ------------------------
    This PC          ::{20D04FE0-3AEA-1069-A2D8-08002B30309D} ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}
    Recycle Bin      ::{645FF040-5081-101B-9F08-00AA002F954E} ::{645FF040-5081-101B-9F08-00AA002F954E}
    Control Panel    ::{26EE0668-A00A-44D7-9371-BEB064C98683} ::{26EE0668-A00A-44D7-9371-BEB064C98683}
    poizan           ::{59031A47-3F72-44A7-89C5-5595FE6B30EE} C:\Users\poizan
    Libraries        ::{031E4825-7B94-4DC3-B131-E946B44C8DD5} ::{031E4825-7B94-4DC3-B131-E946B44C8DD5}
    Control Panel    ::{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0} ::{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0}
    Homegroup        ::{B4FB3F98-C1EA-428D-A78A-D1F5659CBA93} ::{B4FB3F98-C1EA-428D-A78A-D1F5659CBA93}
    Network          ::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C} ::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}
    OneDrive         ::{018D5C66-4533-4307-9B53-224DE2ED1FE6} C:\Users\poizan\OneDrive
    Dropbox          ::{E31EA727-12ED-4702-820C-4B6445F28E1A} C:\Users\poizan\Dropbox

    And here from This PC:

    DisplayName       ParseName                                 DesktopAbsoluteParsePath
    -----------       ---------                                 ------------------------
    Downloads         ::{088E3905-0323-4B02-9826-5D99428E115F}  C:\Users\poizan\Downloads
    Pictures          ::{24AD3AD4-A569-4530-98E1-AB02F9417AA8}  C:\Users\poizan\Pictures
    Music             ::{3DFDF296-DBEC-4FB4-81D1-6A3438BCF4DE}  C:\Users\poizan\Music
    Desktop           ::{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}  C:\Users\poizan\Desktop
    Documents         ::{D3162B92-9365-467A-956B-92703ACA08AF}  C:\Users\poizan\Documents
    Videos            ::{F86FA3AB-70D2-4FC7-9C99-FCBF05467F3A}  C:\Users\poizan\Videos
    TVMOBiLi.dy       uuid:4f592dcd-11cb-422f-0b50-8064792d2d2d ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\uuid:4f592dcd-11cb-422f-0b50-8064792d2d2d
    Acer (C:)         C:                                        C:\
    DVD RW Drive (D:) D:                                        D:\
    BD-ROM Drive (F:) F:                                        F:\
  • The display names of files in the filesystem are always short (8.3) names given that those exists. This is true for all the variants of the display name. This seems weird as it certainly isn't how they are shown in Windows Explorer and normal shell views. I couldn't find any mention of this on google either – maybe there is some flag that one can use to change the behaviour? Or something in the manifest file (which wouldn't help in my case as I can't control PowerShell's manifest). Barring a better solution, the best way to get the real name is probably to get the file system path and then get the long path using GetLongPathNameSHGetFileInfo should be able to give us the long name directly from the pidl, but it's limited to MAX_PATH length.

Which exceptions do you get from different IO errors in .NET?

<!–

–>

Action Exception HResult Other error code
FileMode.CreateNew on existing file System.IO.IOException -2147024816 (0x80070050)
Opening a file with access incompatible with the share mode of an existing handle System.IO.IOException -2147024864 (0x80070020)
Opening a non-existing file in a directory even without read or listing access System.IO.FileNotFoundException -2147024894 (0x80070002)
Any file mode on directory System.UnauthorizedAccessException -2147024891 (0x80070005)
Opening a read-only file for writing System.UnauthorizedAccessException -2147024891 (0x80070005)
Creating a file in a directory without write access System.UnauthorizedAccessException -2147024891 (0x80070005)

isapi filters and unicode urls

If you write a custom isapi filter then you have several ways to get the url, the difference between them being not exactly well documented (actually it seems that no one has even bothered updating the documentation since IIS 6.0). So I did a couple of tests with IIS 8.0. Here is what I could see when getting a SF_NOTIFY_PREPROC_HEADERS notification. pfc is the HTTP_FILTER_CONTEXT* and pHeaders is pvNotification cast into HYYP_FILTER_PREPROC_HEADERS*.

  • pHeaders->GetHeader(pfc, "URL", buf, &size)
    This is the complete raw url (as char array) as requested by the client. For example requesting

    /☃?foo=bar

    results in buf containing

    /%E2%98%83?foo=bar
  • pfc->GetServerVariable(pfc, "URL", buf, &size)
    This contains the decoded path part of the requested url – also as a char array. This is probably encoded in the system ANSI Code Page (ACP). Characters not present in the ACP are replaced with '?'. For example requesting the above url results in buf containing

    /?

    Note that according to this the server variable URL shouldn't even be available in SF_NOTIFY_PREPROC_HEADERS. Apparently it is.

  • pfc->GetServerVariable(pfc, "UNICODE_URL", buf, &size)
    This is the same as above except that buf now contains a wchar_t array. Characters outside BMP seems to be correctly handled as a surrogate pair. So with the request from earlier buf will contain (as an UTF-16LE string):

    /☃
  • pHeaders->GetHeader(pfc, "UNICODE_URL", buf, &size)
    As one would likely expect this doesn't exists. The call returns ERROR_INVALID_PARAMETER.