{"id":441,"date":"2010-01-20T18:48:34","date_gmt":"2010-01-20T18:48:34","guid":{"rendered":"https:\/\/driverentry.com.br\/en\/2010\/01\/20\/writing-filters\/"},"modified":"2026-07-31T03:49:27","modified_gmt":"2026-07-31T03:49:27","slug":"writing-filters","status":"publish","type":"post","link":"https:\/\/driverentry.com.br\/en\/2010\/01\/20\/writing-filters\/","title":{"rendered":"Writing Filters"},"content":{"rendered":"<div style=\"text-align: right;\"><a href=\"https:\/\/driverentry.com.br\/2010\/01\/20\/escrevendo-filtros\/\"><img loading=\"lazy\" decoding=\"async\" title=\"Em Portugues\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/br.gif\" alt=\"\" width=\"21\" height=\"19\" \/><\/a><\/div>\n<p>Let&#8217;s play with something more interesting now. Obviously we will still take small steps so as not to get lost in so many details. Today I will talk about driver filters. The Windows <span style=\"font-style: italic;\">IoManager<\/span> lets us add functionality to certain drivers without having to replace them. A classic example would be a file-encryption driver. You don&#8217;t need to write a new <span style=\"font-style: italic;\">file system<\/span> driver to have such functionality. You can simply write a filter that would sit between the <span style=\"font-style: italic;\">file system<\/span> driver and the rest of the system.<\/p>\n<p>In the figure below we can observe the flow of <a href=\"https:\/\/driverentry.com.br\/en\/2007\/02\/12\/cool-but-what-is-an-irp\/\">IRPs<\/a> that go from the <span style=\"font-style: italic;\">IoManager<\/span> to a certain driver; a filter is installed on top of the existing driver and starts receiving the IRPs in place of the original driver. With this, the filter has the opportunity to change the parameters of the received IRPs, or log the original driver&#8217;s activity, or duplicate system requests to that driver, or even deny the original driver&#8217;s service.<\/p>\n<div style=\"text-align: center;\"><img decoding=\"async\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/NewFilterDev.png\" alt=\"\" \/><\/div>\n<p>In a write operation, a filter could encrypt the data before sending it to the original driver, and similarly, in a read operation the filter could decrypt data before delivering it to the system.<\/p>\n<p>It still won&#8217;t be this time that we build a <span style=\"font-style: italic;\">file system<\/span> encryption filter. Real-time file-encryption filters are among the most complex drivers to write. Let&#8217;s choose a simpler driver, not to say a really silly one, to apply the basic concepts demonstrated here.<\/p>\n<p>Speaking of a silly driver, let&#8217;s use the driver from <a href=\"https:\/\/driverentry.com.br\/en\/2009\/07\/07\/strings-in-the-kernel\/\">this post<\/a> that we already saw here. This driver simply stores a list of strings sent by an application in write operations. Such strings are returned to the application in read operations.<\/p>\n<h3>Writing the DriverEntry<\/h3>\n<p>As we saw in this <a href=\"https:\/\/driverentry.com.br\/en\/2007\/06\/18\/we-want-samples\/\">other post<\/a>, one of the things the <span style=\"font-style: italic;\">DriverEntry()<\/span> function does in an ordinary driver is to set the <span style=\"font-style: italic;\">Dispatch Routines<\/span> that the driver will service for a certain device. To do this you must fill in the <span style=\"font-style: italic;\">Major Functions<\/span> array that is in the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd852039.aspx\">DRIVER_OBJECT<\/a> structure.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Sets the driver's dispatch routines.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    pDriverObj->MajorFunction[IRP_MJ_CREATE] = OnCreate;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    pDriverObj->MajorFunction[IRP_MJ_CLEANUP] = OnCleanup;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    pDriverObj->MajorFunction[IRP_MJ_CLOSE] = OnClose;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    pDriverObj->MajorFunction[IRP_MJ_READ] = OnRead;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    pDriverObj->MajorFunction[IRP_MJ_WRITE] = OnWrite;<\/pre>\n<\/div>\n<p>In the case of our example filter, we just want to monitor the activity of the driver we are attached to; this way we will always have to forward any received IRPs to the driver below. A simple and common way to do this is to set all the dispatch routines to a single function. That function is responsible for simply logging the received request and passing it along.<\/p>\n<p>If we take a look at the definition of IRP_MJ_CREATE and its friends, we will see the following excerpt from the wdm.h file.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">\/\/<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">\/\/ Define the major function codes for IRPs.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">\/\/<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_CREATE                   <span style=\"color: purple;\">0x00<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_CREATE_NAMED_PIPE        <span style=\"color: purple;\">0x01<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_CLOSE                    <span style=\"color: purple;\">0x02<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_READ                     <span style=\"color: purple;\">0x03<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_WRITE                    <span style=\"color: purple;\">0x04<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_QUERY_INFORMATION        <span style=\"color: purple;\">0x05<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_SET_INFORMATION          <span style=\"color: purple;\">0x06<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_QUERY_EA                 <span style=\"color: purple;\">0x07<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_SET_EA                   <span style=\"color: purple;\">0x08<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_FLUSH_BUFFERS            <span style=\"color: purple;\">0x09<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_QUERY_VOLUME_INFORMATION <span style=\"color: purple;\">0x0a<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_SET_VOLUME_INFORMATION   <span style=\"color: purple;\">0x0b<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_DIRECTORY_CONTROL        <span style=\"color: purple;\">0x0c<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_FILE_SYSTEM_CONTROL      <span style=\"color: purple;\">0x0d<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_DEVICE_CONTROL           <span style=\"color: purple;\">0x0e<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_INTERNAL_DEVICE_CONTROL  <span style=\"color: purple;\">0x0f<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_SHUTDOWN                 <span style=\"color: purple;\">0x10<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_LOCK_CONTROL             <span style=\"color: purple;\">0x11<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_CLEANUP                  <span style=\"color: purple;\">0x12<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_CREATE_MAILSLOT          <span style=\"color: purple;\">0x13<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_QUERY_SECURITY           <span style=\"color: purple;\">0x14<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_SET_SECURITY             <span style=\"color: purple;\">0x15<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_POWER                    <span style=\"color: purple;\">0x16<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_SYSTEM_CONTROL           <span style=\"color: purple;\">0x17<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_DEVICE_CHANGE            <span style=\"color: purple;\">0x18<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_QUERY_QUOTA              <span style=\"color: purple;\">0x19<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_SET_QUOTA                <span style=\"color: purple;\">0x1a<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_PNP                      <span style=\"color: purple;\">0x1b<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_PNP_POWER                IRP_MJ_PNP      <span style=\"color: green;\">\/\/ Obsolete....<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">#define<\/span> IRP_MJ_MAXIMUM_FUNCTION         <span style=\"color: purple;\">0x1b<\/span><\/pre>\n<\/div>\n<p>Note that there is a special definition, <span style=\"font-weight: bold;\">IRP_MJ_MAXIMUM_FUNCTION<\/span>, which indicates the maximum index of the <span style=\"font-style: italic;\">dispatch routines<\/span> table. We will use a simple loop to make all the routines in our table point to a single routine that we will name <span style=\"font-style: italic;\">OnForwardDispatch<\/span>.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Sets all the driver's dispatch routines to<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/      one that forwards the IRP to the original driver.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: blue;\">for<\/span> (i = <span style=\"color: purple;\">0<\/span>; i &lt;= IRP_MJ_MAXIMUM_FUNCTION; i++)<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        pDriverObj->MajorFunction[i] = OnForwardDispatch;<\/pre>\n<\/div>\n<p>We will see the implementation of this routine later. The next step we will take here is to locate the device we are going to attach to. To do this, we will use the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms801459.aspx\">IoGetDeviceObjectPointer()<\/a> routine. It basically receives the device name and returns a reference to it.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">NTSTATUS <\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">  IoGetDeviceObjectPointer(<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    IN PUNICODE_STRING  ObjectName,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    IN ACCESS_MASK  DesiredAccess,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    OUT PFILE_OBJECT  *FileObject,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    OUT PDEVICE_OBJECT  *DeviceObject<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    );<\/pre>\n<\/div>\n<p>Notice that this routine has two output parameters. Besides the pointer to the <span style=\"font-style: italic;\">device object<\/span> we also receive a pointer to a <span style=\"font-style: italic;\">file object<\/span>. I have seen some people get confused about these two parameters, so I will give this some emphasis.<\/p>\n<p>The pointer to the <span style=\"font-style: italic;\">file object<\/span> represents a connection created between your driver and the device you opened. As we saw in <a href=\"https:\/\/driverentry.com.br\/en\/2007\/08\/29\/using-fileobject-and-fscontext\/\">this post<\/a>, a <span style=\"font-style: italic;\">file object<\/span> is created to represent connections between user-mode applications and your driver. Applications use that <span style=\"font-style: italic;\">file object<\/span> through the <span style=\"font-style: italic;\">handle<\/span> obtained in the call to the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa363858(VS.85).aspx\">CreateFile()<\/a> routine. Here we have something similar, but only the Kernel was involved. This means that if you wanted to, you could launch IRPs to the device requesting operations as an application would, but we won&#8217;t see that today; we still have a filter to finish.<\/p>\n<p>The big confusion regarding these two parameters is about the references between the objects. <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms801459.aspx\">In the documentation<\/a> we see that the caller of this routine must release the reference it gained when the device is no longer used. We do this simply by using the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms802945.aspx\">ObDereferenceObject()<\/a> routine.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">VOID <\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">  ObDereferenceObject(<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    IN PVOID  Object<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    );<\/pre>\n<\/div>\n<blockquote>\n<p><span style=\"font-weight: bold; font-style: italic;\">&#8220;I&#8217;ve got it! Since we are obtaining a reference to a device object, then I should pass the device object pointer. Right?&#8221;<\/span><\/p>\n<\/blockquote>\n<p>Er&#8230; Actually no. The <span style=\"font-style: italic;\">file object<\/span> is an indirect reference to the <span style=\"font-style: italic;\">device object<\/span>. According to the figure below, if we imagine that the reference counters only concern our references, when we call <span style=\"font-style: italic;\">ObDereferenceObject()<\/span> for the <span style=\"font-style: italic;\">file object<\/span>, its reference count would drop to zero and a new call to <span style=\"font-style: italic;\">ObDeferenceObject()<\/span> would be made indirectly for the <span style=\"font-style: italic;\">device object<\/span>, causing its reference count to also drop to zero, destroying the object.<\/p>\n<div style=\"text-align: center;\"><img decoding=\"async\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/DeviceReferences.png\" alt=\"\" \/><\/div>\n<p>After obtaining the pointer to the target device, we will have to create our own device, which will receive the IRPs in place of the original device. To do this we will still use the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa490468.aspx\">IoCreateDevice()<\/a> routine as we did with drivers, but with some differences.<\/p>\n<p>The first difference is that your device normally has no name. It is possible to create named filters, but that can create a security flaw. This happens because when a name is looked up in the <span style=\"font-style: italic;\">Object Manager<\/span>, its security rules are evaluated. When we create a named filter, we create the possibility of the same object being obtained by a different name that may have less restrictive security rules. But that is another subject.<\/p>\n<p>When we create a device, we need to inform the size of the <span style=\"font-style: italic;\">device extension<\/span>.<\/p>\n<blockquote>\n<p><span style=\"font-weight: bold; font-style: italic;\">&#8220;What is a device extension?&#8221;<\/span><\/p>\n<\/blockquote>\n<p><span style=\"font-style: italic;\">Device extension<\/span> is simply a space of memory that is associated with the <span style=\"font-style: italic;\">device object<\/span>. Such space normally holds information concerning the device. The address of the device we are attached to normally sits in the <span style=\"font-style: italic;\">device extension<\/span>. This way, we can define that our <span style=\"font-style: italic;\">device extension<\/span> must contain the following structure.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">\/\/-f--&gt; Our device extension will contain only the address<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">\/\/      of the device we are attached to.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">typedef<\/span> <span style=\"color: blue;\">struct<\/span> _DEVICE_EXTENSION<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">{<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    PDEVICE_OBJECT  pNextDeviceObj;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">} DEVICE_EXTENSION, *PDEVICE_EXTENSION;<\/pre>\n<\/div>\n<p>After creating our <span style=\"font-style: italic;\">device object<\/span>, we configure the I\/O method by copying the <span style=\"font-weight: bold;\">DO_BUFFERED_IO<\/span> and <span style=\"font-weight: bold;\">DO_DIRECT_IO<\/span> bits. If you don&#8217;t remember these bits, take a look at <a href=\"https:\/\/driverentry.com.br\/en\/2008\/10\/01\/buffered-direct-or-neither\/\">this post<\/a>. The filter must propagate the original driver&#8217;s choice, and the driver has the commitment not to change the method during its lifetime.<\/p>\n<p>Now we are ready to attach to the chosen device, and we will do this using the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms795450.aspx\">IoAttachDeviceToDeviceStackSafe()<\/a> routine to make our device enter the device stack.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">NTSTATUS<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">  IoAttachDeviceToDeviceStackSafe(<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    IN PDEVICE_OBJECT  SourceDevice,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    IN PDEVICE_OBJECT  TargetDevice,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    IN OUT PDEVICE_OBJECT  *AttachedToDeviceObject <\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    );<\/pre>\n<\/div>\n<p>With this call we will obtain the pointer to the device we will be attached to; that device will be the next device that will receive the IRP after you pass it along.<\/p>\n<blockquote>\n<p><span style=\"font-weight: bold; font-style: italic;\">&#8220;But Fernando, don&#8217;t we already have the pointer to the target device?&#8221;<\/span><\/p>\n<\/blockquote>\n<p>Very well, when you obtain the pointer to a device, you theoretically don&#8217;t know whether there are filters already attached on top of it. The pointer you receive in this routine may not be the pointer to the target device. In the figure below we can understand how this relationship happens.<\/p>\n<div style=\"text-align: center;\"><img decoding=\"async\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/NextDevice.png\" alt=\"\" \/><\/div>\n<blockquote>\n<p><span style=\"font-weight: bold; font-style: italic;\">&#8220;Fernando, is there an unsafe version of this routine?&#8221;<\/span><\/p>\n<\/blockquote>\n<p>There actually is, <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa490466.aspx\">IoAttachDeviceToDeviceStack()<\/a>.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">PDEVICE_OBJECT <\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">  IoAttachDeviceToDeviceStack(<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    IN PDEVICE_OBJECT  SourceDevice,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    IN PDEVICE_OBJECT  TargetDevice<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    );<\/pre>\n<\/div>\n<p>It is considered unsafe because of a small time window that can cause a race condition. Notice that the difference between these routines is the way of obtaining the address of the next device. In the original version, this address is obtained by the function&#8217;s return value. If we put this function to run in slow motion we will see the following steps.<\/p>\n<p><br class=\"spacer_\" \/><\/p>\n<ol>\n<li>Your device is attached to the device stack.<\/li>\n<li>The address of the next device is returned by the function.<\/li>\n<li>Your driver receives this address on the function&#8217;s return and updates the device extension.<\/li>\n<li>IRPs start arriving and your driver forwards them to the next device in the list.<\/li>\n<\/ol>\n<p><br class=\"spacer_\" \/><\/p>\n<p>Everything looks beautiful and even gives the impression that everything will work very well in any situation, but a driver developer is a critter trained to hunt for race conditions. Take another look at the sequence, but now in super slow motion. With this super-slow-motion camera we can now observe the steps that can occur between steps 2 and 3.<\/p>\n<p><br class=\"spacer_\" \/><\/p>\n<ol>\n<li>Your device is attached to the device list.<\/li>\n<li>The address of the next device is returned by the function.\n<ol>\n<li>Your thread is interrupted and an IRP launched by an application running in parallel begins its journey through this device stack.<\/li>\n<li>Your device, which is already attached, receives the IRP and tries to forward it to the device below.<\/li>\n<li>Oops! Our <span style=\"font-style: italic;\">device extension<\/span> has not yet been updated with that address.<\/li>\n<li>Your driver remembers when it was a child and everything it had lived through until then.<\/li>\n<li>It decides to fully join that dance and send the IRP to a device whose pointer is still NULL, causing a BSOD.<\/li>\n<\/ol>\n<\/li>\n<li>&#8220;Jeremias, I am a man. Something you are not, and I don&#8217;t shoot people in the back&#8230;&#8221;<\/li>\n<\/ol>\n<p><br class=\"spacer_\" \/><\/p>\n<p>Anyway, understand that even if you use the return value of the <span style=\"font-style: italic;\">IoAttachDeviceToDeviceStack(<\/span>) routine directly to update your device extension, there is still a time window in which your device will be attached but the value has not yet been updated in the device extension. This is because a routine&#8217;s return value comes through a register. Taking the value of that register and updating a variable still gives chances for bad luck.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; ----==== DO NOT COPY THIS ====----<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/      Here we still have a time window between the device being<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/      attached and the value of pNextDeviceObj being updated.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    pDeviceExt->pNextDeviceObj = IoAttachDeviceToDeviceStack(pMyDeviceObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                                                             pTargetDeviceObj);<\/pre>\n<\/div>\n<p>The <span style=\"font-style: italic;\">IoAttachDeviceToDeviceStackSafe(<\/span>) routine makes the system interrupt the flow of IRPs in this stack until the variable pointed to by the output pointer is updated. For this reason, the address passed to this routine must be the final address of the variable where this value will be stored, which in our case is <span style=\"font-style: italic;\">pDeviceExt-&gt;pNextDeviceObj<\/span> without going through intermediate variables.<\/p>\n<p><img decoding=\"async\" style=\"float: right; margin: 0px 10px;\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/homer_doh.png\" border=\"0\" alt=\"\" \/><\/p>\n<p>These details are important and will make you understand that using the safe version of this routine is no guarantee that everything will go right. Imagine that using the safe version you receive the address of the next device in a local variable and then update your device extension. This is one of those typical cases where you need to replace that component that sits between the keyboard and the chair.<\/p>\n<p>Think it&#8217;s overkill? Try reading chapter 5 of the book <a href=\"http:\/\/www.amazon.com\/Programming-Microsoft-Windows-Driver-Model\/dp\/0735618038\/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1264044523&amp;sr=1-1\">&#8220;Programming the Microsoft Windows Driver Model&#8221;<\/a> where Walter Oney talks about how to deal with IRP cancellation.<\/p>\n<p>After attaching our device we can already release the reference obtained by <span style=\"font-style: italic;\">IoGetDeviceObjectPointer()<\/span> using the <span style=\"font-style: italic;\">ObDereferenceObject()<\/span> routine, since the <span style=\"font-style: italic;\">IoAttachDeviceToDeviceStackSafe()<\/span> routine already guaranteed the reference until this connection is undone.<\/p>\n<p>Very well. For those who couldn&#8217;t understand almost anything I said, here is the source code of the implementation of our example <span style=\"font-style: italic;\">DriverEntry()<\/span>. You know how a programmer&#8217;s mind is, sometimes an <span style=\"font-style: italic;\">if<\/span> is worth more than a thousand pages of explanation.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">\/****<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">***     DriverEntry<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">**<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">**      Entry point of the driver.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">**      Welcome to hell.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">*\/<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: blue;\">extern<\/span> <span style=\"color: #a31515;\">\"C\"<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">NTSTATUS<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">DriverEntry(IN PDRIVER_OBJECT  pDriverObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">            IN PUNICODE_STRING pusRegistryPath)<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">{<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    NTSTATUS            nts;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    PDEVICE_OBJECT      pMyDeviceObj;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: blue;\">int<\/span>                 i;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    UNICODE_STRING      usDeviceName;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    PDEVICE_OBJECT      pTargetDeviceObj;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    PFILE_OBJECT        pFileObj;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    PDEVICE_EXTENSION   pDeviceExt;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; Sets the driver's unload routine.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    pDriverObj->DriverUnload = OnDriverUnload;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Sets all the driver's dispatch routines to<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/      one that forwards the IRP to the original driver.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: blue;\">for<\/span> (i = <span style=\"color: purple;\">0<\/span>; i &lt;= IRP_MJ_MAXIMUM_FUNCTION; i++)<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        pDriverObj->MajorFunction[i] = OnForwardDispatch;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; We initialize the string with the name of the device<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/      we want to attach to.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    RtlInitUnicodeString(&amp;usDeviceName,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                         L<span style=\"color: #a31515;\">\"\\\\Device\\\\StringList\"<\/span>);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; We get the pointer to the target device<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    nts = IoGetDeviceObjectPointer(&amp;usDeviceName,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                                   FILE_READ_DATA,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                                   &amp;pFileObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                                   &amp;pTargetDeviceObj);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: blue;\">if<\/span> (!NT_SUCCESS(nts))<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">        <span style=\"color: blue;\">return<\/span> nts;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; We create our device object<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    nts = IoCreateDevice(pDriverObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                         <span style=\"color: blue;\">sizeof<\/span>(DEVICE_EXTENSION),<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                         NULL,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                         pTargetDeviceObj->DeviceType,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                         pTargetDeviceObj->Characteristics,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                         FALSE,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                         &amp;pMyDeviceObj);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: blue;\">if<\/span> (!NT_SUCCESS(nts))<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    {<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">        <span style=\"color: green;\">\/\/-f--&gt; Oops!<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        ObDereferenceObject(pFileObj);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">        <span style=\"color: blue;\">return<\/span> nts;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    }<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; Gets our DEVICE_EXTENSION<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    pDeviceExt = (PDEVICE_EXTENSION)pMyDeviceObj->DeviceExtension;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Uses the same IO method as the original driver<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    pMyDeviceObj->Flags |= pTargetDeviceObj->Flags &amp; (DO_BUFFERED_IO | DO_DIRECT_IO);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; Here our driver enters the device stack<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    nts = IoAttachDeviceToDeviceStackSafe(pMyDeviceObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                                          pTargetDeviceObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                                          &amp;pDeviceExt->pNextDeviceObj);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: blue;\">if<\/span> (!NT_SUCCESS(nts))<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    {<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        <span style=\"color: green;\">\/\/-f--&gt; Oops!<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">        IoDeleteDevice(pMyDeviceObj);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">        <span style=\"color: green;\">\/\/-f--&gt; There is no missing return here.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    }<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Whether we are attached or not, we must release<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/      the reference obtained from the target device.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    ObDereferenceObject(pFileObj);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: blue;\">return<\/span> nts;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">}<\/pre>\n<\/div>\n<h3>Writing the OnDriverUnload<\/h3>\n<p>Here is where the party ends; before our driver is unloaded by the system, we will have to remove our device from the device stack and destroy it. (Evil laughter)<\/p>\n<p>The code here is simple and requires no explanation if you are able to read the comments contained in it.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">\/****<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">***     OnDriverUnload<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">**<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">**      Driver's unload routine.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">*\/<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">VOID<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">OnDriverUnload(IN PDRIVER_OBJECT  pDriverObj)<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">{<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    PDEVICE_OBJECT      pMyDeviceObj;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    PDEVICE_EXTENSION   pDeviceExt;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; Our device is in the list of devices created by this driver<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/      so let's get it just like this:<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    pMyDeviceObj = pDriverObj->DeviceObject;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; Here we get the device extension.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    pDeviceExt = (PDEVICE_EXTENSION)pMyDeviceObj->DeviceExtension;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Here we remove our device from the stack, passing the address of the<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/      next device to the routine below.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    IoDetachDevice(pDeviceExt->pNextDeviceObj);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Now we can destroy our device (evil laughter)<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    IoDeleteDevice(pMyDeviceObj);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">}<\/pre>\n<\/div>\n<blockquote>\n<p><span style=\"font-weight: bold; font-style: italic;\">&#8220;Fernando, is our driver forced to unload when the original driver is unloaded?&#8221;<\/span><\/p>\n<\/blockquote>\n<p>That is the <span style=\"font-weight: bold;\">WDM<\/span> philosophy: drivers are loaded automatically when their devices are detected and unloaded when their devices are disabled or removed. Filters follow the same rules and are loaded\/unloaded based on these events.<\/p>\n<p>But that is not what happens here. The example drivers I use on this blog are of the <span style=\"font-weight: bold;\">Legacy<\/span> model, not WDM. In the Legacy model, drivers are started following their load order in the registry, and it has nothing to do with the detection of your device. Filters need to start after the original drivers, and this is also controlled by their load order. <a href=\"https:\/\/driverentry.com.br\/en\/2009\/12\/17\/boot-drivers-on-windows\/\">This post<\/a> talks about the load order of legacy drivers.<\/p>\n<blockquote>\n<p><span style=\"font-weight: bold; font-style: italic;\">&#8220;OK! You talked and talked and didn&#8217;t answer my question. What happens if I request the unload of the original driver while there is a filter attached on top of it?&#8221;<\/span><\/p>\n<\/blockquote>\n<p>The unloading of the drivers that form a stack must occur in reverse order to their loading. In this case the filter must be unloaded before the original driver, unstacking the devices from top to bottom. If the original driver receives an unload request while there are still references to its devices, whether by a filter or by an application, the unload is postponed until its references are undone. Until then, the attempt to obtain new references to a device that received the unload request will be denied.<\/p>\n<h3>Writing Dispatch Routines<\/h3>\n<p>A filter&#8217;s <span style=\"font-style: italic;\">dispatch routines<\/span> are also different from a driver&#8217;s <span style=\"font-style: italic;\">dispatch routine<\/span>s. Although they can complete an IRP by calling <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa490590.aspx\">IoCompleteRequest()<\/a>, they normally pass the request along using the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa490633.aspx\">IoCallDriver()<\/a> routine. I will talk more about the behavior of a filter&#8217;s <span style=\"font-style: italic;\">dispatch routines<\/span> in future posts. In this example filter we will just log the activity and pass the request to the next driver.<\/p>\n<p>When we talk about passing a request along we are actually talking about passing IRPs along. Reading this <a href=\"https:\/\/driverentry.com.br\/en\/2007\/02\/12\/cool-but-what-is-an-irp\/\">other post<\/a> is essential for what we are going to do in the implementation of our <span style=\"font-style: italic;\">dispatch routines<\/span>.<\/p>\n<p>To finish this post while still in this life, here is the code for the implementation of our <span style=\"font-style: italic;\">dispatch routine<\/span>. After reading the post about IRPs that I just recommended, reading the comments in this code should be enough to understand everything that was done here, or not.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">\/****<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">***     OnForwardDispatch<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">**<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">**      Our dispatch routine simply logs the received IRP<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">**      and forwards the request onward.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\"><span style=\"color: green;\">**<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\"><span style=\"color: green;\">*\/<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">NTSTATUS<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">OnForwardDispatch(IN PDEVICE_OBJECT    pDeviceObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">                  IN PIRP              pIrp)<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">{<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    PDEVICE_EXTENSION   pDeviceExt;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    PIO_STACK_LOCATION  pStack;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Gets pointer to the device extension<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    pDeviceExt = (PDEVICE_EXTENSION)pDeviceObj->DeviceExtension;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/-f--&gt; Gets the current stack location<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    pStack = IoGetCurrentIrpStackLocation(pIrp);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Prints the name of the IRP's major function<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    ASSERT(pStack->MajorFunction &lt;= IRP_MJ_MAXIMUM_FUNCTION);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    DbgPrint(<span style=\"color: #a31515;\">\"[StringFilter] : %s\\n\"<\/span>, g_szMajorNames[pStack->MajorFunction]);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Since we are not modifying anything in the stack location,<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: green;\">\/\/      let's leave it for the next device to use.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    IoSkipCurrentIrpStackLocation(pIrp);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">&nbsp;<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">    <span style=\"color: green;\">\/\/-f--&gt; Forwards the IRP to the next device.<\/span><\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">    <span style=\"color: blue;\">return<\/span> IoCallDriver(pDeviceExt->pNextDeviceObj,<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">                        pIrp);<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">}<\/pre>\n<\/div>\n<p>If you didn&#8217;t understand anything, don&#8217;t forget to send me an e-mail explaining your doubts (without personal offenses). This will help me understand your difficulties and improve my explanations.<\/p>\n<h3>Testing the Filter<\/h3>\n<p>This is the easy part of the post. To test the filter we will first have to compile, install and start the driver from <a href=\"https:\/\/driverentry.com.br\/en\/2009\/07\/07\/strings-in-the-kernel\/\">this post<\/a>. If you still don&#8217;t know how to do that, this <a href=\"https:\/\/driverentry.com.br\/en\/2006\/09\/11\/getting-started\/\">other post<\/a> can give you a starting point. After that, compile, install and start the filter.<\/p>\n<p>Once installed, we can use the test application to exercise the driver. We will be able to follow the filter&#8217;s activity by observing its debug messages, which can be seen in the Kernel debugger or simply by using <a href=\"http:\/\/www.osronline.com\/article.cfm?article=99\">this application<\/a>, which does away with the need for a debugger to see a driver&#8217;s debug messages.<\/p>\n<div style=\"text-align: center;\"><img decoding=\"async\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/Filtering.png\" alt=\"\" \/><\/div>\n<blockquote>\n<p><span style=\"font-weight: bold; font-style: italic;\">&#8220;Fernando, I ran a test here and saw that when starting the filter it logs an <span style=\"font-style: italic;\">IRP_MJ_CLOSE<\/span> event even before starting the test application. What did I do wrong?&#8221;<\/span><\/p>\n<\/blockquote>\n<div style=\"text-align: center;\"><img decoding=\"async\" src=\"https:\/\/driverentry.com.br\/en\/wp-content\/uploads\/2026\/07\/LogClose.png\" alt=\"\" \/><\/div>\n<p>There is nothing wrong. This happens because of the sequence of steps followed in the filter&#8217;s <span style=\"font-style: italic;\">DriverEntry()<\/span> routine. Initially the driver calls the <span style=\"font-style: italic;\">IoGetDeviceObjectPointer()<\/span> routine; this makes the <span style=\"font-style: italic;\">IoManager<\/span> send an <span style=\"font-style: italic;\">IRP_MJ_CREATE<\/span> request to the original driver. After that we attach to the device stack and finally call the <span style=\"font-style: italic;\">ObDereferenceObject()<\/span> routine, which will finalize the only reference to the <span style=\"font-style: italic;\">file object<\/span> we received, sending an <span style=\"font-style: italic;\">IRP_MJ_CLOSE<\/span> request to the driver below. Since we are already attached to it, we are able to see our own activity on the original driver. This can be observed by the call stack we will have if there is a <span style=\"font-style: italic;\">breakpoint<\/span> in our <span style=\"font-style: italic;\">dispatch routine<\/span> when we release the <span style=\"font-style: italic;\">file object<\/span> reference at the end of <span style=\"font-style: italic;\">DriverEntry()<\/span>.<\/p>\n<div style=\"font-family: Courier New; font-size: 9pt; color: black; background: #A4F64C; border: 1px outset; padding: 0 0 0 5px;\">\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">kd> k<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">ChildEBP RetAddr  <\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA; color: red;\">f8af9bcc 804ee129 StringFilter!OnForwardDispatch<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">f8af9bdc 80578f6a nt!IopfCallDriver+0x31<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">f8af9c14 805b0b18 nt!IopDeleteFile+0x132<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">f8af9c30 80522bd1 nt!ObpRemoveObjectRoutine+0xe0<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">f8af9c54 f8c80663 nt!ObfDereferenceObject+0x5f<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF; color: red;\">f8af9c7c 805767ff StringFilter!DriverEntry+0xf3<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">f8af9d4c 8057690f nt!IopLoadDriver+0x66d<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">f8af9d74 80534c12 nt!IopLoadUnloadDriver+0x45<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">f8af9dac 805c61ee nt!ExpWorkerThread+0x100<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FFFFFF;\">f8af9ddc 80541de2 nt!PspSystemThreadStartup+0x34<\/pre>\n<pre style=\"margin: 0px; padding: 0 0 0 5px; background: #FAFAFA;\">00000000 00000000 nt!KiThreadStartup+0x16<\/pre>\n<\/div>\n<p>As usual, the filter source that was implemented in this post is available for download. Our filter does almost nothing, but it will already serve as a base for future posts that will give it more functionality, explaining how such functionalities are implemented.<\/p>\n<p>See you!<\/p>\n<p class=\"download\"><a href=\"http:\/\/www.driverentry.com.br\/samples\/StringFilter.zip\">StringFilter.zip<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s play with something more interesting now. Obviously we will still take small steps so as not to get lost in so many details. Today I will talk about driver filters. The Windows IoManager lets us add functionality to certain drivers without having to replace them. A classic example would be a file-encryption driver. You [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-441","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/posts\/441","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/comments?post=441"}],"version-history":[{"count":1,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/posts\/441\/revisions"}],"predecessor-version":[{"id":442,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/posts\/441\/revisions\/442"}],"wp:attachment":[{"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/media?parent=441"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/categories?post=441"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/driverentry.com.br\/en\/wp-json\/wp\/v2\/tags?post=441"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}