Click here to Skip to main content
13,191,172 members (80,835 online)
Click here to Skip to main content
Add your own
alternative version


55 bookmarked
Posted 25 May 2012

Device hooking

, 25 May 2012
Rate this:
Please Sign up or sign in to vote.
Device hooking


Network snoop is a continuation of the article with primary focus on device hooking. Here I plan to introduce device stack, device extensions, and build a driver using WDM using C++ (on Windows 7 : 64-bit).


The reader is advised to read up on device drivers by Toby Opferman, the reader is required to know how to build a driver. Also you will require WDK (Windows Driver Kit) and Dbgview (Google for it).

We will be writing code that is to be executed at ring '0' (kernel mode), processor privilege protection is provided to execute tasks greater than ring '0'. Feel free to modify the code to print using DbgPrint, the CS register. Notice the last two bits will be set to 0 indicating ring '0' code execution. Ring 0 code is required to access some portion of memory which is flagged as privileged via page table. Privileged pages can only be accessed by privileged segments (i.e., Current Privilege Level < 3), and user code runs at CPL=3 (hence cannot access them). Also to access IO, CPL <= IOPL.

Using the code

The attached code is written in C++ and must be referred to at all times.

We will start by writing a device entry function (analogous to 'main/winmain' in C/C++ user mode program):

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING RegistryPath);
// remember we are using C++, remember to provide external linkages for C//

The next thing we need to do is to create a device:

NtStatus = IoCreateDevice(pDriverObject, sizeof(PDEVICE_OBJECT), NULL, 
//allocate in-device memory: sizeof(PDEVICE_OBJECT)) 

IoCreateDevice lets you create a device and also allocate four bytes: sizeof(PDEVICE_OBJECT). This memory will belong to the context of the device we have created and is called device extension. This device extension related memory is always available with the device pointer. It is usually used to store a pointer to the memory created by calling ExAllocatePool and is ideal for passing memory across different drivers. Other drivers can access this pointer by attaching themselves by calling IoAttachDevice (more on this later) or by calling IoGetDeviceObjectPointer.

Now let's attach the driver.

RtlInitUnicodeString(&usDeviceToFilter, L"\\Device\\tcp"); 
NtStatus = IoAttachDevice(pDeviceObject_main, &usDeviceToFilter, 

As mentioned earlier, we make use of IoAttachDevice, we attach this to device\TCP since we are hooking TCP calls.

When a driver is hooked, the driver that does the hooking (the one we created and not device\TCP) will receive all calls to this device. Our driver is now on top of the device stack (until somebody hooks device\TCP). It is the responsibility of the driver to pass these requests down to the lower driver in the stack. The lowest driver is usually your port driver (a.k.a., miniports).

Requests along with parameters are passed to the device via IRP (IO request package), in a layered driver (like ours, using device stacks). An IRP exists for every device in the stack. Hence we have an IRP stack.

We must register all possible IRP functions so that there is a function for every IRP:

for(uiIndex = 0; uiIndex < IRP_MJ_MAXIMUM_FUNCTION; uiIndex++)
   pDriverObject->MajorFunction[uiIndex] = UnSupportedFunction;

UnSupportedFunction does nothing but passes the IRP to a lower:

NtStatus = IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp); }
return NtStatus; 

We must skip the current IRP processing and blindly pass it to the next driver in the stack.

To snoop network packages, we are interested only in IRP_MJ_INTERNAL_DEVICE_CONTROL. We add the following line to ensure that the appropriate function related to IO processing is called:

pDriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = IOFunction; //(refer to code)

IOFunction is called very time an IRP's current stack location major function is equal to IRP_MJ_INTERNAL_DEVICE_CONTROL.

To process any function (in our case it is IOFunction) we must acquire the current stack location

PIO_STACK_LOCATION StackIrpPointer= IoGetCurrentIrpStackLocation(Irp);

Through this we can get parameters being passed down from user mode code (refer code):

   ( PTDI_REQUEST_KERNEL_CONNECT ) (& StackIrpPointer -> Parameters );
PTA_ADDRESS TA_Address_data =( ( PTRANSPORT_ADDRESS )( TDI_connectRequest -> 
   RequestConnectionInformation -> RemoteAddress ))-> 
   Address; PTDI_ADDRESS_IP TDI_data = ( PTDI_ADDRESS_IP ) ( TA_Address_data->Address );
   { UINT Address = TDI_data -> in_addr ; UINT Port = TDI_data -> sin_port ; 
     DbgPrint (address:%x Port: %x",Address,Port) } 

I have also added code for send and recv.

For sending code we make use of MmGetSystemAddressForMdlSafe since we are using METHOD_IN_DIRECT.

For recv:

PCHAR pWriteDataBuffer = 0;
DbgPrint ( IoCopyCurrentIrpStackLocationToNext(Irp);
NtStatus = IoCallDriver((PDEVICE_OBJECT)
  DeviceObject->DeviceExtension, Irp); "inside recv function to pull out data");

We now copy the current stack location to the next and not skip it since we are calling IOSetCompletionRoutine. This will set a completion routine that gets called when an IO completes.

Let's fire it up

Drivers are installed in a similar way as services, we will use the service control manager for this.

system(sc create Driver1 binPath= "..//Driver.sys" type= kernel");
system("sc start Driver1");
MessageBoxA(0,"UnloadFilter","UnloadFilter",0);"sc stop Driver1");
system("sc stop Driver1");
system("sc delete Driver1");

Additional code: SimpleSocket is for you to test the code, simple socket is a simple server, use Telnet to connect to it while the filter driver is active, view the output in Dbgview.

Points of Interest

I do hope that the reader has learned device stack being implemented as a network snoop (filter driver).

As mentioned before, this article only aims to teach the reader device stack and not network filter, the TDI implementation is adapted from articles found on the Internet.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Asif Bahrainwala
Instructor / Trainer
India India
I have been working with computers since my eight grade, programming the ZX Spectrum. I have always had an interest in assembly language and computer theory (and is still the reason for taking tons of online courses), actively code using C/C++ on Windows (using VS) and Linux (using QT).

I also provide training on data structures, algorithms, parallel patterns library , Graphics (DX11), GPGPUs (DX11-CS,AMP) and programming for performance on x86.
Feel free to call me at 0091-9823018914 (UTC +5:30)

(All views expressed here do not reflect the views of my employer).

You may also be interested in...


Comments and Discussions

GeneralMy vote of 5 Pin
gndnet12-Jul-12 5:24
membergndnet12-Jul-12 5:24 
GeneralMy vote of 5 Pin
Mihai MOGA16-Jun-12 20:12
memberMihai MOGA16-Jun-12 20:12 
GeneralTDI is deprecated in Windows 8 Pin
Vasiliy Zverev29-May-12 8:14
memberVasiliy Zverev29-May-12 8:14 
TDI filters are deprecated in Windows 8[^] and not fully supported. For example new Metro style Internet Explorer traffic isn't captured by TDI filters. Hardware Certification Kit fails if TDI driver found[^]. The replacement is Windows Filtering Platform.
GeneralWow! Didn't know this. Pin
valdok3-Jun-12 6:34
membervaldok3-Jun-12 6:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171016.2 | Last Updated 25 May 2012
Article Copyright 2012 by Asif Bahrainwala
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid