Thursday, January 23, 2014

Getting Started with Windows File System Filters - DriverEntry

Now that we have everything in place (meaning the INF file with the Microsoft allocated altitude), we're ready to start coding. Minifilters are regular WDM drivers (well, they implement a subset of the features; no power manager or plug and play) and so their main() function is DriverEntry(). A minifilter's DriverEntry() is very simple and typically only registers the minifilter with FltMgr and starts filtering. Of course, a minifilter can choose a different flow, where it starts filtering (or even registers with FltMgr) at a later point. In my experience these are pretty rare so I'll just discuss the usual case.
So, as explained before, there are two steps to "starting" a minifilter:
  1. Registering it with FltMgr - This is done by calling FltRegisterFilter() and it is during this time where the FLT_FILTER structure is allocated by FltMgr. When the function returns the system can see the filter but filtering hasn't started yet, no callbacks are being called.
  2. Starting filtering - This is done in the call to FltStartFiltering() and a minifilter is expected to be ready to process callbacks even before the function returns. This separation is useful for cases where the filter needs to set up some additional things before it's ready to do any work, things that might require some time (for example it might need to create some threads).
The only potentially difficult thing here is dealing with the many fields of the FLT_REGISTRATION structure. It has its own documentation page, but i'd like to quickly go over the fields:
  • Size - this is pretty obvious, just sizeof(FLT_REGISTRATION).
  • Version - this has changed a couple of times. Mainly exists for future compatibility. Look in fltkernel.h if you'd like to see what the versions look like.
  • Flags - The values here are documented and pretty easy to understand. Keep in mind that support for NPFS and MSFS is only available for Win8 and newer.
  • ContextRegistration - Now we're getting into the tricky bits. This is an array of structures that define which types of contexts the filter will use. This concept of the array of structures might feel a bit strange initially, but it works out great for things that are really sparse. For example there are 7 types of context and most applications only use one or two.
  • OperationRegistration - This is also an array of the IRP_MJ operations you want to filter. I generally start with one or two operations (depending on the style of filter) and just keep adding stuff here.
  • FilterUnloadCallback - Callback for unloading the filter. If NULL the filter can't be unloaded.
  • InstanceSetupCallback - Callback for when an instance gets created.
  • InstanceQueryTeardownCallback - Callback to check whether a manual detach should be allowed.
  • InstanceTeardownStartCallback - Callback to indicate that the instance will start tearing down.
  • InstanceTeardownCompleteCallback - Callback to indicate that the instance has finished tearing down.
  • GenerateFileNameCallback - Name provider callback for generating names.
  • NormalizeNameComponentCallback - Name provider callback for normalizing names.
  • NormalizeContextCleanupCallback - Name provider callback for normalization context cleanup.
  • TransactionNotificationCallback - Callback for transaction management.
  • NormalizeNameComponentExCallback - Another normalization callback for with better transaction support.
  • SectionNotificationCallback - A callback for notifications for sections created by the filter.
As you can see this list is a pretty random set of things that the filter might need. So let's look at the minimal set of things that are required to make a filter:
  • ContextRegistration - for anything other than very trivial filters you'll need some contexts, so this will probably have something.
  • OperationRegistration - same here, you can only do very limited stuff (like watching volumes appear and go away) without any operations to monitor. You'll have at least one callback defined here.
  • FilterUnloadCallback - you can probably skip this initially but you won't be able to unload the filter. Doesn't matter much if you use a VM for debugging but in general I'd advise creating this callback, even if it doesn't do anything.
  • InstanceXxxCallback - you can probably initially skip all these. In my experience it'll be pretty clear when you need to use them.
  • XxxNameXxxCallback - unless you're a name provider you can skip all these. You probably shouldn't make your first filter a name provider anyway.
  • TransactionNotificationCallback - very likely you can skip this initially.
  • SectionNotificationCallback - same here, most likely not necessary.
So basically once you build this structure and call FltRegisterFilter() and FltStartFiltering() from DriverEntry() you should be able to play with your filter. If you don't want to do all the typing take a look at the nullFilter WDK sample. It doesn't really do anything (no operations are defined) but it should load and unload just fine. Easy way if you want a simple file system filter template to build on top of.



Thursday, January 16, 2014

Getting Started with Windows File System Filters - What's That INF File For ?

In a nutshell, INF files are files that contain parameters for drivers. There is an MSDN page for INF files in general and then there is a file system filter specific MSDN page. I won't go over the information there, which is very comprehensive, I'd rather focus a bit on how I work with them and what sort of decisions a file system designer might need to make.

In terms of how often does one use the INF files, I use the INF file every time I need to install a new version of my minifilter. My workflow is to rebuild the SYS file and copy it to a location shared with the VM, and then I need the INF to load it. Sometimes I load it by right-clicking the INF file and then clicking on Install:
This is fine when I just want to play with a filter but when actively working on it (when I'm building it 40 times a day) I use a script to load it. It's usually a one-line script but it can grow if I need to initialize other things as well:
rundll32.exe setupapi.dll,InstallHinfSection DefaultInstall 128 nullfilter.inf
The magic value "128" is documented on the page for the InstallHinfSection Function. I never need to reboot for development versions of my drivers so 128 works for me. I remove any bits that might require a reboot and if I need to run other services and such I just add more lines to my installation script for that.
Now, for most filters I've ever needed to work on I just take one of the INF files for any of the file system filter samples (nullfilter is the one I use as a template most often, for more advanced usage see simrep and minispy) and I change a couple of things:
  • The driver name. This is pretty much a straight find&replace, but make sure you preserve the case.
  • The LoadOrderGroup. This is the big one. Each file system filter developer needs to pick the right LoadOrderGroup for their filter, depending on what they plan do to. All the various load orders are listed on the MSDN page Load Order Groups for File System Filter Drivers. For an in-depth look at how loading a filter works and how to more closely control the load order see my previous post on Controlling the Load Order of File System Filters. However, at the time of writing the INF all one needs to worry about is the proper LoadOrderGroup.
  • The Class and ClassGuid. This is closely related to the LoadOrderGroup, basically just copy the name and the GUID from the MSDN page File System Filter Driver Classes and Class GUIDs.
  • The altitude. You need to get this from Microsoft, they allocate unique ones for each filter. If you follow the steps on the page Minifilter Altitude Request you should receive your altitude in an email. This might take a couple of weeks or more (AFAIK it's a manual process and each request is reviewed to make sure that the LoadOrderGroup for the filter type makes sense (based on the brief description in the submission form; thus it helps to explain what the filter does fairly clearly, to expedite the process)). But again, I don't think there are any guarantees when it comes to the time, so you want to do this early in the development process and not wait till the last minute. However, when starting development you'll need an altitude to load your filter so what I normally do is just pick an unused one in the right LoadOrderGroup from the page Allocated Altitudes while I wait for my request to be processed. Please keep in mind that this page isn't updated all the often so it's possible that the altitude you picked has already been allocated to someone else. But in general when you get the new altitude it's simply a matter of replacing it in the INF file and you should be good to go. Please make sure to use altitudes that you picked (not allocated by Microsoft) ONLY for development and debugging, you NEVER should to release a filter or share it with customers without a proper altitude from Microsoft.
  • The InstanceFlags. The only documentation I've been able to find for these is in a presentation on Loading and Unloading Minifilters. In short, 1 means 'don't allow automatic attachments' and 2 means 'don't allow manual attachments'. I mostly use 1 during development anyway since I prefer to attach my filter manually to specific drives (generally by adding more lines to the script that loads the driver).
Since I mentioned altitudes here and the fact that you must register your filter with Microsoft and get an altitude from them, there is another process that requires a similar registration with Microsoft. I'm talking about the process of requiring a reparse point. Basically, if you call FSCTL_SET_REPARSE_POINT in your product (assuming you don't set any of the Microsoft reparse points, which are undocumented anyway as far as I know) you need a reparse point tag. There is a page that describes that process, Reparse Point Tag Request and the only other thing you'll need when applying for one of those is a GUID, which you must generate and then use together with the Reparse Point Tag (the one you get back from Microsoft) every time you call FSCTL_SET_REPARSE_POINT. (NOTE: this is only necessary for a very small set of filters and it's not related with the INF file at all, I'm just mentioning it here because it also requires a tag from Microsoft, similar to altitudes, so it's easy to do them both at the same time (if you are one of those filters)).

Anyway, hopefully this page and the links to MSDN documentation will get you going with your INF so that you can finally start working on the actual filter.



Thursday, December 19, 2013

Getting Started with Windows File System Filters - Hooking Up WinDbg to the VM

Sorry I missed last Thursday, I've been traveling and I didn't get around to post anything.
This post is focused to hooking up WinDBG to a VM. There are multiple ways to do attach debuggers to physical machines, each with it's specific pluses and minuses (please note, however, that this list changes often since Microsoft keeps improving the tools, which is awesome). Also, please note that in most cases you'll use two machines, a target (the machine you want to debug) and a host (the debugger host). If I remember correctly there used to be a way to attach WinDBG to the kernel it was running on, which was useful to inspect various kernel structures, but I don't think that's supported anymore, and anyway with VMs this is easy to do and much more flexible. Anyway, here are the main ways to attach to machines (that I'm aware of at the time of writing this):
  • serial cable connection. This is pretty universal, works on all Windows versions and it's been tested for a very long time. However, not many machine have serial ports anymore, and serial is really slow. But it's really the solution that's most likely to work for all cases.
  • USB connection. I've never actually used this, I think I tried once but gave up for some reason I can't remember (and I was left with the impression that it's quite finicky). This is also only available on Vista and newer.
  • 1394 (or firewire). I've used this a lot, it's really fast and pretty easy to set up. The downside is that it's expensive because it needs specialized hardware (like firewire cards and cables). This is available since XP and I highly recommend it if you need to work with real hardware and a VM won't work for you. 
  • network. I've not used this at all since it's pretty new (Win8+) but it should be pretty awesome since you don't have to be physically connected to the machine you're trying to debug (which limits the usefulness of the other methods, since if you work in a larger group you'll often have to debug things on machines that are not in your office so you'll have to figure out a way to connect to them). 
In general you really want a fast debugger connection because you might end up moving massive amounts of data across from the target to the host (for example, you might decide to capture a dump instead of keep using the machine, so if you'll have to capture all physical memory you'll want the 4, 8 or who knows, maybe even 128 GB to move quickly. I mean, over serial this can last weeks...
However, we're not talking about physical machines here so you should be able to just read the memory of the VM from the host, right ? Well, as it turns out, you can. There is a tool called VirtualKD, that actually does this and it has a couple of features that really help with driver development. There is a tutorial on how to install VirtualKD so please follow the steps there. Once you have everything working (the way I test that it's working is to boot up the VM and then breaking in the debugger. At that point the VM should freeze (please note that it might grab your mouse and you'll need to press some key combination (Ctrl+Alt for VMWare) to release it). Then I return to WinDBG and press F5 to resume running the VM) it's time to build and install a minifilter. This is what WinDBG should look like once it's connected properly and paused and resumed the VM:
One other thing we should set up is some method of copying files between the VM and host. I do this by setting up the Shared Folders feature to always share a certain folder between the host and the VM. Then all I need to do is copy the files I need to that folder on the host and I can access them from the VM (for this machine I used a folder D:\shared). After everything is set up, it's time to start the sequence of steps that will deploy the minifilter on the VM (which will become familiar to you pretty quickly):
  1. So now let's build a minifilter. Using the steps described in the previous post, let's try to build the passThrough minifilter. Try to build the checked version for the platform you've set up your VM to use (basically, for my Win7 x86 VM I'll start the "x86 Checked Build Environment" under the Windows 7 folder and build there). Once it's built I'd like to add a breakpoint in DriverEntry() (which is basically the main() function for drivers) to break in the debugger once the filter loads. So please just add a breakpoint (a call to DbgBreakPoint();) somewhere in DriverEntry() and rebuild the driver (bcz again)).
  2. Now we need to copy two files to the VM. Those two files are the actual driver (objchk_win7_x86\i386\passThrough.sys in my case) and the inf file (passThrough.inf). Put these two in the same directory and copy them to the VM through the shared folder.
  3. And finally we need to install the minifilter on the VM. So now we're switching to the VM. Open the shared folder, find the passThrough folder, right-click on passThrough.inf and select Install (which will require administrator privileges).
  4. Then we need to start an elevated CMD window from where we'll run "fltmc load passThrough".
  5. If everything is fine and the minifilter is loading then we'll trigger the breakpoint, which will freeze the VM. So we'll need to switch back to the debugger and unfreeze it (F5 again or use "g" command to allow execution to continue).
  6. After that we should see passthrough loaded and attached to all the volumes in your VM.
So this is it for today. In the next post I'll talk about some of my configurations and some optimizations I use to make this whole process faster, but that's not going to be until next year, so I wish you Happy Holidays! and a Happy New Year! in the meantime :).

Thursday, December 5, 2013

Getting Started with Windows File System Filters - Installing the Tools

Hello everyone,

It's been a while since I last posted anything. I took a break from file systems (and Windows, for that matter) but now I'm ready to get back. Since I'll be spending some time getting reacquainted with the whole thing I figured it's a good time to start a blog series for absolute beginners to this subject. However, you're expected to know C and understand synchronization (you should know what a mutexes and semaphores are).
So with that, the first step is to try to get the environment set up. We'll need the following tools:
  1. Windows Driver Kit - I'll be installing the 7.1.0 WDK, which should be good for Windows 7, Windows Vista, Windows XP, Windows Server 2008 R2, Windows Server 2008, and Windows Server 2003. The drivers we build should also run on Windows 8 and newer but the 7.1 WDK doesn't include the additional features (new APIs and such) that are available there. I'll cover setting up the newer WDK in a different post. For now, just get the WDK from MSDN.
  2. A Virtual Machine software - Personally I'm a big fan of VMware Workstation, but it's not free. I've used VirtualBox in the past and it's fine for what we need and it's also free so you can get it from here.
So let's get started. Install the WDK anywhere (please note that it's an ISO image so you can either mount it with any tool you like or you can just use a tool like 7zip (or most other decent archivers) to extract the files to a directory. Then just run KitSetup.exe and we're good to go.
This is what I have installed under D:\WinDDK (the red Xs are there because the kit can't find the kit I used so I can't add anything else - just ignore those):



Once the WDK installed you should be able to build a sample. So the steps are:
  1. Start a cmd prompt to build the sample: Start->Windows Driver Kits->WDK 7600.16385.1->Build Environments->Windows 7->x86 Checked Build Environment
  2. type cd src\filesys\miniFilter\nullFilter to get to the simplest minifilter sample
  3. type bcz to build it
  4. now you can go to the output directory (cd objchk_win7_x86\i386) and you should see the nullfilter.sys file, which is the null minifilter sample
At this point you should have the WDK installed properly. Please install VirtualBox on your own and create a new Win7 VM  (or, if you have VMware Workstation, you can just use that) and next week we'll configure the debugger and load the filter and look at it a bit.

Thursday, November 15, 2012

FltCreateSectionForDataScan

Thanks everyone for your suggestions. I really appreciate you took the time and sent me emails. Some of the topics you've suggested I won't be able to address for various reasons (mostly having to do with the legal implications of my doing that). However, I have a couple of topics that I might go over in the next couple of months. Incidentally, I also plan to change the frequency of my posts because of changes around my work so I'll probably update the blog whenever I get around to it. I still plan to answer questions though so feel free to leave comments and suggest topics (preferably in email). And now let's proceed to the topic at hand.

Someone suggested it would be useful to take a closer look at what FltCreateSectionForDataScan does and discuss how the same functionality could be achieved in previous OS releases. The FltCreateSectionForDataScan function is documented as being supported only in Win8+ but looking at the documentation page for the FltRegisterForDataScan function there is a mention about the FsRtlCreateSectionForDataScan function, which is documented as being available since Win 2000 SP4 and XPSP2, basically from the time FltMgr is available. A quick look in the debugger at FltCreateSectionForDataScan shows that it's really a wrapper around FsRtlCreateSectionForDataScan so I thought it might be useful to take a closer look at that:

  1. As one might expect the FsRtlCreateSectionForDataScan function begins with various parameter checks, primarily to filter out various invalid parameters. There is also a check to see if the FileObject supports sections.
  2. The next step is to set the TopLevelIrp to the value 1, which maps to FSRTL_FSP_TOP_LEVEL_IRP.
  3. Once that is done there is a call to nt!FsRtlAcquireFileExclusiveCommon. This function is not documented anywhere and I could only find some references in crash dumps. However, looking for just FsRtlAcquireFileExclusive returns more hits and we can see that FsRtlAcquireFileExclusive doesn't do much beyond calling nt!FsRtlAcquireFileExclusiveCommon. In the ntifs.h file we see a comment to the effect that FsRtlAcquireFileExclusive is called from NtCreateSection to avoid deadlocks with file systems, so we can guess that this the same thing going on here. We can also guess that whatever FsRtlCreateSectionForDataScan does is probably pretty similar to NtCreateSection.
  4. The next step is to get the file size. This is done by a call to nt!FsRtlGetFileSize, and the documentation for FsRtlGetFileSize states that "If you already own file system resources, you should call FsRtlGetFileSize instead of ZwQueryInformationFile, because attempting to acquire the file object lock would violate locking order and lead to deadlocks. The ZwQueryInformationFile function should be only if you do not already own file system resources.". This makes sense since the function has already acquired the file object lock in the previous step.
  5. There is a quick check to see if the file is empty and the function should return with STATUS_END_OF_FILE.
  6. Then, once everything is set up we have a call to nt!MmCreateSection. Calling this function directly is not supported by Microsoft and there is no documentation for how to do that. This is basically why FsRtlCreateSectionForDataScan was added. This can be inferred from the msdn page "Installing the Filter Manager rollup package for Windows XP SP2". If the function returns STATUS_FILE_LOCK_CONFLICT the call to nt!MmCreateSection is retried after delaying the thread by nt!FsRtlHalfSecond (I had no idea this existed, but I plan to use it from now on :)).
  7. Once the section is created there is a call to nt!CcZeroEndOfLastPage. I've not been able to find any documentation for this function (though it's pretty obvious what it does) except in the NT Insider article "Cache Me if You Can: Using the NT Cache Manager".
  8. Once this is done the function releases the locks so the rest of the function is done outside the critical region (that was set up around step 2).
  9. The only interesting thing happening in the rest of the function is the creation of the handle for the section in the current process handle table. The rest of it just deals with cleanup and the return path from the function.

One thing to note is that FsRtlCreateSectionForDataScan just creates the section. There are A LOT of FltMgr functions related to these data sections and based on the names most of them are dealing with conflict resolution. I'm not sure what that's about yet and I haven't actually used them anywhere but if you plan to use this API directly in downlevel OSes then you should probably expect some sort of conflicts. Good luck!

Thursday, October 4, 2012

Suggestions

Hi everyone. It seems lately I'm having a hard time coming up with good topics, so I wanted to ask you to send me emails with topics you might be interested in. These could be things you already know but you think might be beneficial to have posted somewhere to the rest of the community or things you're not sure about. However, please keep the requests specific. Things like "minifilter interaction with the memory manager" are simply too vague to be helful. You could ask about specific functions or behaviors or structure members (but again, please keep it specific). You can find my email address on my contact information on this blog.

Thanks,
Alex.

Thursday, September 27, 2012

A Helpful Debugging Technique: Debug Stream Contexts

In this post I'd like to discuss a debugging technique that I found pretty useful. I'm a big fan of keeping lots of information around in debug builds and ASSERTing all over the place. I try to be very careful about what information I keep so that I don't change the actual flow of execution for the code. For example, I tend to try to not add references when I copy some pointers because I don't want to alter when freeing the memory happens for the objects I'm tracking (but this approach requires extra care when asserting since I don't want to use pointers to free memory).
One particularly useful thing I like is to keep track of all the files I've processed in my filter and remember various decisions I made about them and keep back-pointers to things that would otherwise be impossible to find (for example when I base my decision on some parameters in the FLT_CALLBACK_DATA I might take a snapshot of those parameters; or I might keep a stack trace for certain operations and so on). The problem that arises with this approach is, of course, where to store this information. In some cases it's feasible to use some of the structures I'm already using (StreamContext or StreamHandleContext) and just add extra members in debug builds. However, there are other times where this becomes a significant pain and it would alter the filter quite a bit (for example, if the logic of the filter is to check for presence of a context to decide on what to do with a file then adding a context in which I only populate the debugging information for files I don't intend to process would change that logic). Also, certain contexts might store data that isn't fixed in size (like a file name or path) which I typically set to be the last thing in the context so adding more debugging information (which might itself be dynamic in size) would make things very complicated.
The ideal approach would be to set more than one context on an object, but FltMgr's functions don’t really allow that. Well, here is where the some FsRtl functions really come in handy. There is support built in the file system library (FsRtl code) for contexts (which I've discussed in more detail in my post on Contexts in legacy filters and FSRTL_ADVANCED_FCB_HEADER) and the library supports setting multiple contexts for the same stream, with different keys. Moreover, the contexts have two keys and there are pretty cool searching semantics that allow searching on only one key. The functions that deal with this are FsRtlInitPerStreamContext, FsRtlInsertPerStreamContext and FsRtlLookupPerStreamContext. They’re pretty easy to use and there is even a page on MSDN on Creating and Using Per-Stream Context Structures.
Here is some code that shows how to use these functions:
typedef struct _MY_DEBUG_CONTEXT {

    //
    // this is the header for the perStream context structure. 
    //

    FSRTL_PER_STREAM_CONTEXT PerStreamContext;

    // 
    // this is the information we want to track.
    //
    
    BOOLEAN Flag;

} MY_DEBUG_CONTEXT, *PMY_DEBUG_CONTEXT;



VOID
FreeDebugContext(
    __in PVOID ContextToFree
    )
{

    //
    // here we would release and uninitialize any other data.. 
    //

    //
    // and then we free the actual context.
    //
    
    ExFreePoolWithTag( ContextToFree, 'CgbD' );
}


NTSTATUS
SetDebugContext(
    __in PFILE_OBJECT FileObject,
    __in PVOID Key1,
    __in_opt PVOID Key2,
    __in BOOLEAN Flag
    )
{
    PMY_DEBUG_CONTEXT context = NULL;
    NTSTATUS status = STATUS_SUCCESS;

    __try {

        //
        // allocate and initialize the context.
        //

        context = ExAllocatePoolWithTag( PagedPool, 
                                         sizeof(MY_DEBUG_CONTEXT),
                                         'CgbD' );

        if (context == NULL) {

            status = STATUS_INSUFFICIENT_RESOURCES;
            __leave;    
        }

        FsRtlInitPerStreamContext( &context->PerStreamContext,
                                   Key1,
                                   Key2,
                                   FreeDebugContext );

        //
        // initialize all the members that we want to track.
        //

        context->Flag = Flag;

        //
        // try to set the context.
        //

        status = FsRtlInsertPerStreamContext( FsRtlGetPerStreamContextPointer(FileObject),
                                              &context->PerStreamContext );

        NT_ASSERT(status == STATUS_SUCCESS);

    } __finally {

        if (!NT_SUCCESS(status)) {

            if (context != NULL) {
            
                ExFreePoolWithTag( context, 'CgbD' );
            }
        }
    }

    return status;
}


BOOLEAN
GetDebugContextFlag(
    __in PFILE_OBJECT FileObject,
    __in PVOID Key1,
    __in_opt PVOID Key2
    )
{
    PMY_DEBUG_CONTEXT context = NULL;
    
    context = (PMY_DEBUG_CONTEXT)FsRtlLookupPerStreamContext( FsRtlGetPerStreamContextPointer(FileObject),
                                                              Key1,
                                                              Key2 );

    if ((context != NULL) &&
        (context->Flag)) {

        return TRUE;
    }

    return FALSE;
}
Finally, one more thing to show is how easy it is to find this structure in the debugger:
0: kd> dt @@(tempFileObject) nt!_FILE_OBJECT FsContext  <- get the FsContext
   +0x00c FsContext : 0xa24d05f8 Void
0: kd> dt 0xa24d05f8 nt!_FSRTL_ADVANCED_FCB_HEADER FilterContexts. <- look at the FilterContexts  in the FsContext
   +0x02c FilterContexts  :  [ 0xa25a1108 - 0x936e93e4 ]
      +0x000 Flink           : 0xa25a1108 _LIST_ENTRY [ 0x936e93e4 - 0xa24d0624 ]
      +0x004 Blink           : 0x936e93e4 _LIST_ENTRY [ 0xa24d0624 - 0xa25a1108 ]
0: kd> dl 0xa25a1108 <- look at all the entries in the list
a25a1108  936e93e4 a24d0624 9441eb40 92e3cb40
936e93e4  a24d0624 a25a1108 93437438 a24d05f8
a24d0624  a25a1108 936e93e4 00000000 a24d05f4
0: kd> !pool a25a1108 2  <- check the pool type for the first one
Pool page a25a1108 region is Paged pool
*a25a1100 size:   20 previous size:  100  (Allocated) *DbgC  <- this is our structure
  Owning component : Unknown (update pooltag.txt)
0: kd> !pool 936e93e4  2   <- for fun, let's see what the other context is 
Pool page 936e93e4 region is Nonpaged pool
*936e93d8 size:   68 previous size:    8  (Allocated) *FMsl   
  Pooltag FMsl : STREAM_LIST_CTRL structure, Binary : fltmgr.sys  <- obviously FltMgr's context
0: kd> dt a25a1108 _MY_DEBUG_CONTEXT  <- display the debugging context
PassThrough!_MY_DEBUG_CONTEXT
   +0x000 PerStreamContext : _FSRTL_PER_STREAM_CONTEXT
   +0x014 Flag             : 0x1 ''