TDI Filter Driver, part II

Hello all,

I posted here a few days ago asking for help with a TDI driver that would
let me observe all TCP/IP traffic on my system. Several people pointed
out that my initial approach – trying to register an event handler with
the lower level device – would not work, and suggested I go with a TDI
filter driver instead.

(I’ve also been advised to look into WinPcap or NetMon. But WinPcap is
too limited in that it can’t see localhost traffic, and NetMon doesn’t
give me the capability to manipulate traffic – something I’d like to look
into later. Also, before someone suggests that I buy the TDI samples from
PCAUSA, let me point out that I’m just doing this to learn more about Win2K
drivers, so it’s not something I really want to sink a lot of cash into.)

Anyway, based on those suggestions plus some other random postings, I
came up with the code at the end of this message. Right now it’s just a
passthrough – it intercepts all the IRPs for \Device\Tcp, prints out a
little message with DbgPrint(), and then passes the IRP down to the lower
level device.

This seems to work, sort of, in the sense that all networking-related
functionality is still intact. However, I notice that I don’t seem to be
intercepting ALL the traffic on the system. For example, when I upload or
download a huge file from somewhere, I expect to see a lot of TDI_SEND or
TDI_RECEIVE IRPs corresponding to the increase in traffic. With my driver
loaded, I don’t see any of that. I see a TDI_SEND or TDI_RECEIVE every
now and then but for the most part, those seem to be largely missing.

As an experiment, I compared the debug output of my driver side-by-side
with output from TDIMON (from SysInternals), and the IRPs that I do
intercept seem to be correct. I just seem to be missing a bunch of them.
I realize that I’m only hooking into TCP right now, but all my test
scenarios have been TCP-based traffic so that shouldn’t be a factor.

So, does anyone have some insight into this? It may have been discussed
here before, but I wasn’t able to find much in the archives.

One other question – is there a safe way to unload this type of driver?
I’m currently doing an IoDetachDevice() and IoDeleteDevice(), but soon
after unloading I get a BSOD (I think the message that comes up is IRQL_
NOT_LESS_OR_EQUAL or something like that). If I just start the driver
once and leave it running, it seems to be fine.

Thanks in advance for any suggestions.
-chris

— START CODE

#include “ntddk.h”
#include “tdikrnl.h”
#include “tdi_drv.h”

#define DEBUG 1

typedef struct _DEVICE_EXTENSION
{
// the top of the stack before this filter was added, a.k.a. the location
// to which all Irps should be directed
PDEVICE_OBJECT TopOfStack;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

// globals
PDEVICE_OBJECT ptcpfilterdevice;

NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
int i;

#if DEBUG
DbgPrint(“\n.\n.\nBeginning driver initialization\n”);
#endif

for (i=0; i<=IRP_MJ_MAXIMUM_FUNCTION; i++)
DriverObject->MajorFunction[i] = tdi_Dispatch;
DriverObject->DriverUnload = tdi_Unload;

status = tdi_InitializeAndAttach(DriverObject);

#if DEBUG
DbgPrint(“tdi_InitializeAndAttach() return code: %08X\n”, status);
#endif

return status;
}

NTSTATUS tdi_InitializeAndAttach(
IN PDRIVER_OBJECT DriverObject
)
{
NTSTATUS status;
PDEVICE_EXTENSION pdev_ext;
UNICODE_STRING u_name;

RtlInitUnicodeString(&u_name, L"\Device\Tcp");

// note: need same device type and characteristics as the thing we’re
// filtering.

// create the filter device
status = IoCreateDevice(DriverObject,
sizeof(DEVICE_EXTENSION),
NULL, // no device name
FILE_DEVICE_NETWORK, // same as \Device\Tcp
FILE_DEVICE_SECURE_OPEN, // same as \Device\Tcp
FALSE, // non-exclusive
&ptcpfilterdevice);
#if DEBUG
DbgPrint(“IoCreateDevice() return code: %08X\n”, status);
DbgPrint(" ptcpfilterdevice: %08X\n", ptcpfilterdevice);
#endif
if (status) {
DbgPrint(“IoCreateDevice() failed\n”);
return STATUS_INSUFFICIENT_RESOURCES;
}

RtlZeroMemory(ptcpfilterdevice->DeviceExtension, sizeof(DEVICE_EXTENSION));
pdev_ext = (PDEVICE_EXTENSION) ptcpfilterdevice->DeviceExtension;

// attach to the Tcp chain and put a pointer to \Device\Tcp in the device
// extension structure
status = IoAttachDevice(ptcpfilterdevice, &u_name, &pdev_ext->TopOfStack);
#if DEBUG
DbgPrint(“IoAttachDevice() return code: %08X\n”, status);
DbgPrint(" pdev_ext->TopOfStack = %08X\n", pdev_ext->TopOfStack);
#endif
if (status) {
DbgPrint(“IoAttachDevice() failed\n”);
IoDeleteDevice(ptcpfilterdevice);
return STATUS_INSUFFICIENT_RESOURCES;
}

return STATUS_SUCCESS;
}

VOID tdi_Unload(
IN PDRIVER_OBJECT DriverObject
)
{

#if DEBUG
DbgPrint(“Unloading driver\n”);
#endif

// detach from the Tcp chain
IoDetachDevice(((PDEVICE_EXTENSION)
ptcpfilterdevice->DeviceExtension)->TopOfStack);

// delete the filter device
IoDeleteDevice(ptcpfilterdevice);
}

NTSTATUS tdi_Dispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PIO_STACK_LOCATION pirp_stack;

pirp_stack = IoGetCurrentIrpStackLocation(Irp);
#if DEBUG
if (pirp_stack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL)
DbgPrint(“tdi_Dispatch(): Passing %s->%s (major %02X, minor %02X)\n”,
XlatedIrpMajor[pirp_stack->MajorFunction],
XlatedMJInternal[pirp_stack->MinorFunction],
pirp_stack->MajorFunction, pirp_stack->MinorFunction);
else
DbgPrint(“tdi_Dispatch(): Passing %s (major %02X, minor %02X)\n”,
XlatedIrpMajor[pirp_stack->MajorFunction],
pirp_stack->MajorFunction, pirp_stack->MinorFunction);
#endif

//
// Pass the IRP to the target without touching the IRP
//
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(((PDEVICE_EXTENSION)
DeviceObject->DeviceExtension)->TopOfStack, Irp);
}

— END CODE


You are currently subscribed to ntdev as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com

  1. If you want to intercept all data, you should also intercept callbacks,
    that
    protocol driver(tcpip) registers … (see help for TDI_SET_EVENT_HANDLER)
    if you dont intercept these callbacks you will be able to get OUTGOING data
    and very very little of INCOMING data …

  2. your driver crashes, because there are some open connections … If you
    close all connections and stop the driver It wont crash …

----- Original Message -----
From: “Chris Eng”
To: “NT Developers Interest List”
Sent: Saturday, January 26, 2002 9:37 AM
Subject: [ntdev] TDI Filter Driver, part II

> Hello all,
>
> I posted here a few days ago asking for help with a TDI driver that would
> let me observe all TCP/IP traffic on my system. Several people pointed
> out that my initial approach – trying to register an event handler with
> the lower level device – would not work, and suggested I go with a TDI
> filter driver instead.
>
> (I’ve also been advised to look into WinPcap or NetMon. But WinPcap is
> too limited in that it can’t see localhost traffic, and NetMon doesn’t
> give me the capability to manipulate traffic – something I’d like to look
> into later. Also, before someone suggests that I buy the TDI samples from
> PCAUSA, let me point out that I’m just doing this to learn more about
Win2K
> drivers, so it’s not something I really want to sink a lot of cash into.)
>
> Anyway, based on those suggestions plus some other random postings, I
> came up with the code at the end of this message. Right now it’s just a
> passthrough – it intercepts all the IRPs for \Device\Tcp, prints out a
> little message with DbgPrint(), and then passes the IRP down to the lower
> level device.
>
> This seems to work, sort of, in the sense that all networking-related
> functionality is still intact. However, I notice that I don’t seem to be
> intercepting ALL the traffic on the system. For example, when I upload or
> download a huge file from somewhere, I expect to see a lot of TDI_SEND or
> TDI_RECEIVE IRPs corresponding to the increase in traffic. With my driver
> loaded, I don’t see any of that. I see a TDI_SEND or TDI_RECEIVE every
> now and then but for the most part, those seem to be largely missing.
>
> As an experiment, I compared the debug output of my driver side-by-side
> with output from TDIMON (from SysInternals), and the IRPs that I do
> intercept seem to be correct. I just seem to be missing a bunch of them.
> I realize that I’m only hooking into TCP right now, but all my test
> scenarios have been TCP-based traffic so that shouldn’t be a factor.
>
> So, does anyone have some insight into this? It may have been discussed
> here before, but I wasn’t able to find much in the archives.
>
> One other question – is there a safe way to unload this type of driver?
> I’m currently doing an IoDetachDevice() and IoDeleteDevice(), but soon
> after unloading I get a BSOD (I think the message that comes up is IRQL_
> NOT_LESS_OR_EQUAL or something like that). If I just start the driver
> once and leave it running, it seems to be fine.
>
> Thanks in advance for any suggestions.
> -chris
>
>
>
> — START CODE
>
> #include “ntddk.h”
> #include “tdikrnl.h”
> #include “tdi_drv.h”
>
> #define DEBUG 1
>
> typedef struct _DEVICE_EXTENSION
> {
> // the top of the stack before this filter was added, a.k.a. the
location
> // to which all Irps should be directed
> PDEVICE_OBJECT TopOfStack;
>
> } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
>
> // globals
> PDEVICE_OBJECT ptcpfilterdevice;
>
>
> NTSTATUS DriverEntry(
> IN PDRIVER_OBJECT DriverObject,
> IN PUNICODE_STRING RegistryPath
> )
> {
> NTSTATUS status;
> int i;
>
> #if DEBUG
> DbgPrint(“\n.\n.\nBeginning driver initialization\n”);
> #endif
>
> for (i=0; i<=IRP_MJ_MAXIMUM_FUNCTION; i++)
> DriverObject->MajorFunction[i] = tdi_Dispatch;
> DriverObject->DriverUnload = tdi_Unload;
>
> status = tdi_InitializeAndAttach(DriverObject);
>
> #if DEBUG
> DbgPrint(“tdi_InitializeAndAttach() return code: %08X\n”, status);
> #endif
>
> return status;
> }
>
>
> NTSTATUS tdi_InitializeAndAttach(
> IN PDRIVER_OBJECT DriverObject
> )
> {
> NTSTATUS status;
> PDEVICE_EXTENSION pdev_ext;
> UNICODE_STRING u_name;
>
> RtlInitUnicodeString(&u_name, L"\Device\Tcp");
>
> // note: need same device type and characteristics as the thing we’re
> // filtering.
>
> // create the filter device
> status = IoCreateDevice(DriverObject,
> sizeof(DEVICE_EXTENSION),
> NULL, // no device name
> FILE_DEVICE_NETWORK, // same as
\Device\Tcp
> FILE_DEVICE_SECURE_OPEN, // same as
\Device\Tcp
> FALSE, // non-exclusive
> &ptcpfilterdevice);
> #if DEBUG
> DbgPrint(“IoCreateDevice() return code: %08X\n”, status);
> DbgPrint(" ptcpfilterdevice: %08X\n", ptcpfilterdevice);
> #endif
> if (status) {
> DbgPrint(“IoCreateDevice() failed\n”);
> return STATUS_INSUFFICIENT_RESOURCES;
> }
>
> RtlZeroMemory(ptcpfilterdevice->DeviceExtension,
sizeof(DEVICE_EXTENSION));
> pdev_ext = (PDEVICE_EXTENSION) ptcpfilterdevice->DeviceExtension;
>
> // attach to the Tcp chain and put a pointer to \Device\Tcp in the
device
> // extension structure
> status = IoAttachDevice(ptcpfilterdevice, &u_name,
&pdev_ext->TopOfStack);
> #if DEBUG
> DbgPrint(“IoAttachDevice() return code: %08X\n”, status);
> DbgPrint(" pdev_ext->TopOfStack = %08X\n", pdev_ext->TopOfStack);
> #endif
> if (status) {
> DbgPrint(“IoAttachDevice() failed\n”);
> IoDeleteDevice(ptcpfilterdevice);
> return STATUS_INSUFFICIENT_RESOURCES;
> }
>
> return STATUS_SUCCESS;
> }
>
>
> VOID tdi_Unload(
> IN PDRIVER_OBJECT DriverObject
> )
> {
>
> #if DEBUG
> DbgPrint(“Unloading driver\n”);
> #endif
>
> // detach from the Tcp chain
> IoDetachDevice(((PDEVICE_EXTENSION)
> ptcpfilterdevice->DeviceExtension)->TopOfStack);
>
> // delete the filter device
> IoDeleteDevice(ptcpfilterdevice);
> }
>
>
> NTSTATUS tdi_Dispatch(
> IN PDEVICE_OBJECT DeviceObject,
> IN PIRP Irp
> )
> {
> PIO_STACK_LOCATION pirp_stack;
>
> pirp_stack = IoGetCurrentIrpStackLocation(Irp);
> #if DEBUG
> if (pirp_stack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL)
> DbgPrint(“tdi_Dispatch(): Passing %s->%s (major %02X, minor
%02X)\n”,
> XlatedIrpMajor[pirp_stack->MajorFunction],
> XlatedMJInternal[pirp_stack->MinorFunction],
> pirp_stack->MajorFunction, pirp_stack->MinorFunction);
> else
> DbgPrint(“tdi_Dispatch(): Passing %s (major %02X, minor %02X)\n”,
> XlatedIrpMajor[pirp_stack->MajorFunction],
> pirp_stack->MajorFunction, pirp_stack->MinorFunction);
> #endif
>
> //
> // Pass the IRP to the target without touching the IRP
> //
> IoSkipCurrentIrpStackLocation(Irp);
> return IoCallDriver(((PDEVICE_EXTENSION)
> DeviceObject->DeviceExtension)->TopOfStack, Irp);
> }
>
> — END CODE
>
>
> —
> You are currently subscribed to ntdev as: xxxxx@yandex.ru
> To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com
>


You are currently subscribed to ntdev as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com

bryan,

i appreciate the suggestion and i will definitely get around to messing with
NDIS at
some point. however, one thing i want to avoid is binding to a specific adapter
because i want to see traffic on localhost as well as traffic on various
ethernet
adapters.

i’m really interested to get some feedback on the specific TDI issues i
described
in my original post – there must be a good way to fix the problem, since TDIMON
seems to be able to capture everything. i just don’t know why mine doesn’t!

-chris

----- Original Message -----
From: “Bryan Burgin”
To:
Sent: Saturday, January 26, 2002 1:57 AM
Subject: RE: [ntdev] TDI Filter Driver, part II

> You could try writing a NDIS intermediate driver that sits between TCP/IP and
the adapter miniport. See the PASSTHRU sample in the XP DDK.
>
> An intermediate driver has a miniport upper edge and a protocol lower edge.
Where TCPIP’s protocol edge normally binds to the miniport, you would sit in
between. TCPIP would bind to your IM driver’s miniport edge, there’s an
internal binding inside the driver. And then your driver’s lower protocol edge
binds to the original adapter/miniport. Basically, you just snap in between;
all packets would flow through you.
>
> The NDIS library is not IRP based, but is call-back based. You would register
your protocol and miniport via NdisRegisterProtocol and NdisMRegisterMiniport.
Both of these calls inform NDIS of all your handler functions. NDIS then calls
approprate handlers based on what’s going on.
>
> I think you’ll learn a lot about the internals of the system by going this
route.
>
> Best of luck.
>
> Bryan S. Burgin
> xxxxx@microsoft.com
>
> -----Original Message-----
> From: Chris Eng [mailto:xxxxx@connick.com]
> Sent: Fri 1/25/2002 10:37 PM
> To: NT Developers Interest List
> Cc:
> Subject: [ntdev] TDI Filter Driver, part II
>
>
>
> Hello all,
>
> I posted here a few days ago asking for help with a TDI driver that would
> let me observe all TCP/IP traffic on my system. Several people pointed
> out that my initial approach – trying to register an event handler with
> the lower level device – would not work, and suggested I go with a TDI
> filter driver instead.
>
> (I’ve also been advised to look into WinPcap or NetMon. But WinPcap is
> too limited in that it can’t see localhost traffic, and NetMon doesn’t
> give me the capability to manipulate traffic – something I’d like to look
> into later. Also, before someone suggests that I buy the TDI samples from
> PCAUSA, let me point out that I’m just doing this to learn more about Win2K
> drivers, so it’s not something I really want to sink a lot of cash into.)
>
> Anyway, based on those suggestions plus some other random postings, I
> came up with the code at the end of this message. Right now it’s just a
> passthrough – it intercepts all the IRPs for \Device\Tcp, prints out a
> little message with DbgPrint(), and then passes the IRP down to the lower
> level device.
>
> This seems to work, sort of, in the sense that all networking-related
> functionality is still intact. However, I notice that I don’t seem to be
> intercepting ALL the traffic on the system. For example, when I upload or
> download a huge file from somewhere, I expect to see a lot of TDI_SEND or
> TDI_RECEIVE IRPs corresponding to the increase in traffic. With my driver
> loaded, I don’t see any of that. I see a TDI_SEND or TDI_RECEIVE every
> now and then but for the most part, those seem to be largely missing.
>
> As an experiment, I compared the debug output of my driver side-by-side
> with output from TDIMON (from SysInternals), and the IRPs that I do
> intercept seem to be correct. I just seem to be missing a bunch of them.
> I realize that I’m only hooking into TCP right now, but all my test
> scenarios have been TCP-based traffic so that shouldn’t be a factor.
>
> So, does anyone have some insight into this? It may have been discussed
> here before, but I wasn’t able to find much in the archives.
>
> One other question – is there a safe way to unload this type of driver?
> I’m currently doing an IoDetachDevice() and IoDeleteDevice(), but soon
> after unloading I get a BSOD (I think the message that comes up is IRQL_
> NOT_LESS_OR_EQUAL or something like that). If I just start the driver
> once and leave it running, it seems to be fine.
>
> Thanks in advance for any suggestions.
> -chris
>
>
>
> — START CODE
>
> #include “ntddk.h”
> #include “tdikrnl.h”
> #include “tdi_drv.h”
>
> #define DEBUG 1
>
> typedef struct _DEVICE_EXTENSION
> {
> // the top of the stack before this filter was added, a.k.a. the location
> // to which all Irps should be directed
> PDEVICE_OBJECT TopOfStack;
>
> } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
>
> // globals
> PDEVICE_OBJECT ptcpfilterdevice;
>
>
> NTSTATUS DriverEntry(
> IN PDRIVER_OBJECT DriverObject,
> IN PUNICODE_STRING RegistryPath
> )
> {
> NTSTATUS status;
> int i;
>
> #if DEBUG
> DbgPrint(“\n.\n.\nBeginning driver initialization\n”);
> #endif
>
> for (i=0; i<=IRP_MJ_MAXIMUM_FUNCTION; i++)
> DriverObject->MajorFunction[i] = tdi_Dispatch;
> DriverObject->DriverUnload = tdi_Unload;
>
> status = tdi_InitializeAndAttach(DriverObject);
>
> #if DEBUG
> DbgPrint(“tdi_InitializeAndAttach() return code: %08X\n”, status);
> #endif
>
> return status;
> }
>
>
> NTSTATUS tdi_InitializeAndAttach(
> IN PDRIVER_OBJECT DriverObject
> )
> {
> NTSTATUS status;
> PDEVICE_EXTENSION pdev_ext;
> UNICODE_STRING u_name;
>
> RtlInitUnicodeString(&u_name, L"\Device\Tcp");
>
> // note: need same device type and characteristics as the thing we’re
> // filtering.
>
> // create the filter device
> status = IoCreateDevice(DriverObject,
> sizeof(DEVICE_EXTENSION),
> NULL, // no device name
> FILE_DEVICE_NETWORK, // same as \Device\Tcp
> FILE_DEVICE_SECURE_OPEN, // same as \Device\Tcp
> FALSE, // non-exclusive
> &ptcpfilterdevice);
> #if DEBUG
> DbgPrint(“IoCreateDevice() return code: %08X\n”, status);
> DbgPrint(" ptcpfilterdevice: %08X\n", ptcpfilterdevice);
> #endif
> if (status) {
> DbgPrint(“IoCreateDevice() failed\n”);
> return STATUS_INSUFFICIENT_RESOURCES;
> }
>
> RtlZeroMemory(ptcpfilterdevice->DeviceExtension,
sizeof(DEVICE_EXTENSION));
> pdev_ext = (PDEVICE_EXTENSION) ptcpfilterdevice->DeviceExtension;
>
> // attach to the Tcp chain and put a pointer to \Device\Tcp in the device
> // extension structure
> status = IoAttachDevice(ptcpfilterdevice, &u_name, &pdev_ext->TopOfStack);
> #if DEBUG
> DbgPrint(“IoAttachDevice() return code: %08X\n”, status);
> DbgPrint(" pdev_ext->TopOfStack = %08X\n", pdev_ext->TopOfStack);
> #endif
> if (status) {
> DbgPrint(“IoAttachDevice() failed\n”);
> IoDeleteDevice(ptcpfilterdevice);
> return STATUS_INSUFFICIENT_RESOURCES;
> }
>
> return STATUS_SUCCESS;
> }
>
>
> VOID tdi_Unload(
> IN PDRIVER_OBJECT DriverObject
> )
> {
>
> #if DEBUG
> DbgPrint(“Unloading driver\n”);
> #endif
>
> // detach from the Tcp chain
> IoDetachDevice(((PDEVICE_EXTENSION)
> ptcpfilterdevice->DeviceExtension)->TopOfStack);
>
> // delete the filter device
> IoDeleteDevice(ptcpfilterdevice);
> }
>
>
> NTSTATUS tdi_Dispatch(
> IN PDEVICE_OBJECT DeviceObject,
> IN PIRP Irp
> )
> {
> PIO_STACK_LOCATION pirp_stack;
>
> pirp_stack = IoGetCurrentIrpStackLocation(Irp);
> #if DEBUG
> if (pirp_stack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL)
> DbgPrint(“tdi_Dispatch(): Passing %s->%s (major %02X, minor %02X)\n”,
> XlatedIrpMajor[pirp_stack->MajorFunction],
> XlatedMJInternal[pirp_stack->MinorFunction],
> pirp_stack->MajorFunction, pirp_stack->MinorFunction);
> else
> DbgPrint(“tdi_Dispatch(): Passing %s (major %02X, minor %02X)\n”,
> XlatedIrpMajor[pirp_stack->MajorFunction],
> pirp_stack->MajorFunction, pirp_stack->MinorFunction);
> #endif
>
> //
> // Pass the IRP to the target without touching the IRP
> //
> IoSkipCurrentIrpStackLocation(Irp);
> return IoCallDriver(((PDEVICE_EXTENSION)
> DeviceObject->DeviceExtension)->TopOfStack, Irp);
> }
>
> — END CODE
>
>
> —
> You are currently subscribed to ntdev as: xxxxx@microsoft.com
> To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com
>
>
>


You are currently subscribed to ntdev as: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com